본문 바로가기
PulseBoard

Firebase Cloud Functions index.js 완전 해설

by 밤새는 탐험가89 2025. 12. 25.
728x90
SMALL

– Kakao 로그인 → Firebase Custom Token 변환 서버 이해하기

 

0️⃣ 이 글에서 다루는 전제

  • Node.js 경험 ❌
  • Firebase Cloud Functions 처음
  • Kakao 로그인 + Firebase Auth 연동이 목표
  • 이 파일 하나(index.js)가 서버의 전부

 

1️⃣ 이 파일(index.js)이 “무엇을 하는 파일인가?”

먼저 한 문장으로 요약하면 이거다.

index.js는
“카카오 로그인 결과를
Firebase가 신뢰할 수 있는 인증 토큰으로 변환해주는 서버 코드”다.

 

조금 더 풀면:

  • iOS 앱에서 카카오 accessToken을 받아서
  • 카카오 서버에 진짜 유효한 토큰인지 물어보고
  • 문제가 없으면
  • Firebase Custom Token을 만들어서
  • 다시 iOS 앱에 돌려준다

👉 이 파일은 로그인 서버가 아니라
👉 **인증 토큰 변환기(adapter)**다.

 

2️⃣ 전체 코드 먼저 보기 (완성본)

📌 이 코드는 functions/index.js에 그대로 들어간다.

 

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const axios = require("axios");

admin.initializeApp();

exports.kakaoCustomToken = functions.https.onRequest(async (req, res) => {
  try {
    // 1. iOS에서 전달한 Kakao accessToken
    const { kakaoAccessToken } = req.body;

    if (!kakaoAccessToken) {
      return res.status(400).json({ error: "Missing kakaoAccessToken" });
    }

    // 2. Kakao API 호출 → 토큰 검증 + 사용자 정보 조회
    const kakaoResponse = await axios.get(
      "https://kapi.kakao.com/v2/user/me",
      {
        headers: {
          Authorization: `Bearer ${kakaoAccessToken}`,
        },
      }
    );

    // 3. Kakao userId 추출
    const kakaoUserId = kakaoResponse.data.id;
    if (!kakaoUserId) {
      return res.status(401).json({ error: "Invalid Kakao token" });
    }

    // 4. Firebase에서 사용할 uid 생성
    const uid = `kakao:${kakaoUserId}`;

    // 5. Firebase Custom Token 생성
    const customToken = await admin.auth().createCustomToken(uid, {
      provider: "KAKAO",
    });

    // 6. iOS 앱으로 Custom Token 반환
    return res.json({ customToken });
  } catch (error) {
    console.error(error);
    return res.status(500).json({ error: "Internal Server Error" });
  }
});

 

3️⃣ Node.js를 전혀 모른다면, 이것부터 이해하면 된다

3-1. require는 뭐야? 

const axios = require("axios");

 

 

  • Swift의 import와 같은 개념
  • “이 파일에서 axios라는 라이브러리를 쓰겠다”

 

3-2. 이 파일은 “서버”다 

functions.https.onRequest((req, res) => { ... })

 

 

이 한 줄이 의미하는 건:

  • HTTP 요청을 받는 서버 함수
  • iOS에서 URL로 호출 가능
  • req = 요청 (Request)
  • res = 응답 (Response)

Swift로 비유하면:

func handleRequest(request) -> Response

 

3-3. async / await는 왜 쓰나?

exports.kakaoCustomToken = async (req, res) => { ... }

 

 

  • 네트워크 요청(Kakao API)은 시간이 걸린다
  • await는 “이 작업이 끝날 때까지 기다려라”는 뜻

Swift의:

async / await

 

와 개념적으로 거의 동일하다.

 

4️⃣ 코드 한 줄씩, “왜 이렇게 썼는지” 설명

 

① Firebase & 라이브러리 초기화 

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const axios = require("axios");

admin.initializeApp();

 

왜 필요한가?

  • firebase-functions
    → Cloud Functions에서 서버 함수를 만들기 위해 필요
  • firebase-admin
    Custom Token을 만들 수 있는 유일한 SDK
    → 클라이언트(iOS)에서는 절대 만들 수 없음
    (Firebase 공식 문서 근거)
  • axios
    → Node.js에서 HTTP API를 호출하기 위한 라이브러리
    → Kakao API(/v2/user/me) 호출용
    (카카오 공식/커뮤니티 예제 다수에서 사용)

 

② HTTP 엔드포인트 생성 

exports.kakaoCustomToken = functions.https.onRequest(...)

 

이 한 줄로:

  • Firebase가 서버를 직접 띄워줌
  • 우리가 포트/서버 설정할 필요 ❌
  • 함수 이름이 곧 API endpoint가 됨

예:

https://us-central1-프로젝트ID.cloudfunctions.net/kakaoCustomToken

 

👉 Express의 app.post() 역할을 Firebase가 대신해준다.

 

③ iOS에서 accessToken 받기 

const { kakaoAccessToken } = req.body;

 

 

  • iOS 앱에서 JSON body로 전달
  • 예:
{
  "kakaoAccessToken": "eyJhbGciOi..."
}

 

 

왜 body로 받나?

  • URL 쿼리보다 보안적으로 낫다
  • REST API 표준 패턴

 

④ accessToken이 없으면 바로 종료  

if (!kakaoAccessToken) {
  return res.status(400).json({ error: "Missing kakaoAccessToken" });
}

 

왜 이렇게 빨리 종료하나?

  • 서버는 불필요한 작업을 빨리 중단하는 게 좋다
  • 잘못된 요청은 400 Bad Request가 REST 표준

 

⑤ Kakao API 호출 (핵심) 

const kakaoResponse = await axios.get(
  "https://kapi.kakao.com/v2/user/me",
  {
    headers: {
      Authorization: `Bearer ${kakaoAccessToken}`,
    },
  }
);

 

이 코드의 의미

  • 카카오 서버에 직접 묻는다:
  • “이 토큰, 진짜 네가 발급한 거 맞아?”
  • 응답이 온다면:
    • 토큰 유효
    • 사용자 정보 포함

 

⑥ Kakao userId 추출 

const kakaoUserId = kakaoResponse.data.id;

 

  • id는 카카오 계정의 고유 식별자
  • 이메일은 바뀔 수 있어도 id는 바뀌지 않음

👉 Firebase UID로 쓰기에 가장 적합

 

⑦ Firebase UID 설계  

const uid = `kakao:${kakaoUserId}`;

 

왜 이렇게 만들었나?

  • provider prefix를 붙이면:
    • google / apple / naver 확장 쉬움
    • UID 충돌 방지
    • 나중에 디버깅 쉬움

이건 Firebase Custom Auth에서 실무적으로 가장 많이 쓰이는 패턴

 

⑧ Custom Token 생성

const customToken = await admin.auth().createCustomToken(uid, {
  provider: "KAKAO",
});

 

이게 핵심 중의 핵심

  • Firebase는 이 토큰만 신뢰한다
  • 이 토큰으로만:
Auth.auth().signIn(withCustomToken: customToken)

 

⑨ iOS로 응답 반환

return res.json({ customToken });

 

  • 서버의 역할은 여기서 끝
  • 로그인 행위는 iOS가 담당

👉 서버는 “인증 판단자”,
👉 클라이언트는 “로그인 실행자”

 

5️⃣ 이 파일에서 구현한 책임 정리 

이 index.js는 딱 이것만 한다:

  1. Kakao 토큰을 받는다
  2. Kakao 서버에 검증을 요청한다
  3. Firebase용 토큰으로 변환한다
  4. 결과를 반환한다

❌ 세션 관리 안 함
❌ 사용자 프로필 저장 안 함
❌ DB 접근 안 함

→ 책임을 최소화했기 때문에 안전하고 확장 가능

 

6️⃣ 로직 실행 순서 한 번에 보기

iOS 앱
 └─ Kakao SDK 로그인
     ↓ accessToken
Firebase Functions (index.js)
 ├─ 토큰 존재 확인
 ├─ Kakao API (/v2/user/me) 호출
 ├─ userId 추출
 ├─ Firebase Custom Token 생성
 └─ customToken 반환
iOS 앱
 └─ signInWithCustomToken

 


 

 

Firebase Cloud Functions index.js 구현 시 참고한 코드와 출처 정리

이 글에서 구현한 functions/index.js 코드는
완전히 처음부터 창작한 코드가 아니라,
공식 문서 + 실제 사용 사례 + 커뮤니티 예제를 참고해
내 목적(iOS + Kakao + Firebase Auth)에 맞게 재구성한 결과물이다.

 

아래는 실제로 참고한 대표적인 코드/자료들과,
그중에서 무엇을 가져오고 무엇을 버렸는지
에 대한 정리다.

 

1️⃣ Firebase 공식 문서 – Custom Token 생성 (가장 핵심 근거)

📌 출처

📌 원본 예제 코드 (공식)

admin.auth().createCustomToken(uid)
  .then((customToken) => {
    // Send token back to client
  });

 

📌 우리가 참고한 부분

  • firebase-admin SDK에서
  • Custom Token은 반드시 서버에서 생성해야 한다
  • 클라이언트는 signInWithCustomToken으로 로그인

📌 우리가 변형한 부분

  • 단순 예제 → Kakao userId 기반 uid 생성
  • Custom claim에 { provider: "KAKAO" } 추가
  • HTTP Function으로 감싸 iOS에서 호출 가능하게 구성

👉 이 문서가 index.js의 “존재 이유” 자체를 결정함

 

2️⃣ Kakao API 공식 문서 – 사용자 정보 조회 (/v2/user/me)

📌 출처

📌 원본 API 사용 방식

GET https://kapi.kakao.com/v2/user/me
Authorization: Bearer {ACCESS_TOKEN}

 

📌 우리가 참고한 부분

  • accessToken을 Authorization Bearer 헤더로 전달
  • /v2/user/me 응답에서 id가 고유 사용자 식별자라는 점

📌 우리가 변형한 부분

  • 웹 서버가 아닌 Firebase Functions에서 호출
  • 세션 저장 ❌
  • DB 저장 ❌
    “검증 + 식별”만 수행

👉 Kakao 로그인에서 **유일하게 변하지 않는 값은 id**라는 판단의 근거

 

3️⃣ Node.js + Kakao 로그인 커뮤니티 예제 (axios 사용 근거)

📌 출처 (네가 실제로 참고했던 글)

📌 원본 코드의 핵심 부분 (요지)

axios({
  method: "GET",
  url: "https://kapi.kakao.com/v2/user/me",
  headers: {
    Authorization: `Bearer ${accessToken}`,
  },
});

📌 우리가 참고한 부분

  • Node.js 환경에서 Kakao API 호출 시 axios 사용
  • accessToken 검증을 Kakao 서버에 위임하는 구조

📌 우리가 버린 부분

  • Express 서버 구성
  • /auth/kakao, /callback 라우팅
  • 세션(express-session)
  • 웹 페이지 렌더링

👉 “Kakao API 호출 방식만 참고하고, 서버 구조는 전부 버림”

 

4️⃣ Firebase Cloud Functions 공식 예제 – HTTPS Function 형태

📌 출처

📌 원본 예제 코드

exports.helloWorld = functions.https.onRequest((req, res) => {
  res.send("Hello from Firebase!");
});

 

📌 우리가 참고한 부분

  • functions.https.onRequest 형태
  • Express 없이도 HTTP API를 만들 수 있다는 점

📌 우리가 변형한 부분

  • 단순 응답 → 인증 로직
  • req.body 파싱
  • status code + JSON 응답

 

5️⃣ Firebase + 외부 OAuth 연동 실무 패턴 (Custom Token 전략)

📌 출처

  • Firebase GitHub / StackOverflow / 커뮤니티 전반
  • 검색 키워드:
    • firebase custom token oauth
    • firebase kakao login custom token
    • firebase external auth custom token

대표적인 패턴 요약:

Client (SNS login)
 → Server (verify token)
   → Firebase Admin (createCustomToken)
     → Client (signInWithCustomToken)

 

📌 우리가 그대로 따른 부분

  • 외부 SNS 로그인 → Custom Token 변환
  • 서버는 인증 판단만, 로그인 상태 관리는 Firebase에 위임

📌 우리가 단순화한 부분

  • 사용자 profile DB 저장 ❌
  • 신규/기존 유저 분기 ❌
    → Auth 책임만 수행

👉 실무에서 가장 많이 쓰이는 “얇은 인증 서버” 패턴

728x90
LIST