본문 바로가기

운영체제 및 플랫폼/클라우드 플랫폼 (Cloud Platforms)

[Firebase] React + Spring Boot Firebase Authentication 연동

728x90
반응형

프로젝트 로그인 기능 구현을 위해 Firebase Authentication을 연동하였다.

이 글은 특정 프로젝트 코드가 아닌, React + Spring Boot 환경에서 Firebase Authentication을 사용할 때의 공통적인 구성 방법과 전체 흐름을 정리한 내용이다.

나중에 다시 보더라도 "어떤 파일을 만들고, 어떤 순서로 연결했는지" 빠르게 기억할 수 있도록 작성하였다.

구성 환경은 아래와 같다.

Frontend : React (Vite)
Backend : Spring Boot
Authentication : Firebase Authentication
SSO : Google / Apple Login
Phone Authentication : SMS 인증

1. 전체 인증 흐름 이해하기

Firebase Authentication을 사용하면 Google, Apple, SMS 인증 등을 Firebase에서 처리할 수 있다.

전체 흐름은 아래와 같다.

React
 ↓
Firebase Authentication
 ↓
Firebase ID Token 발급
 ↓
Spring Boot 전달
 ↓
Firebase ID Token 검증
 ↓
사용자 정보 확인
 ↓
서비스 JWT 발급
 ↓
API 인증 처리

역할을 구분하면 다음과 같다.

Firebase
→ 로그인 인증 담당

Spring Boot
→ 서비스 권한 및 API 인증 담당

즉, Firebase는 "사용자가 누구인지" 확인하고, Spring Boot는 "서비스를 사용할 권한이 있는지" 관리한다.


2. Firebase 프로젝트 생성

Firebase Console에서 프로젝트를 생성한다.

Firebase Console
 ↓
프로젝트 생성
 ↓
Authentication 활성화
 ↓
로그인 공급자 설정

필요한 로그인 방식을 활성화한다.

설정이 완료되면 웹 앱을 등록하고 Firebase 설정 정보를 확인할 수 있다.


3. React 프로젝트에 Firebase 설치

React 프로젝트 루트에서 Firebase SDK를 설치한다.

npm install firebase

설치 후 Firebase 관련 기능을 사용할 수 있다.


4. 환경 변수 설정

프로젝트 루트에 .env 파일을 생성한다.

project
 ├ src
 ├ public
 └ .env

Firebase Console에서 확인한 값을 입력한다.

VITE_FIREBASE_API_KEY=YOUR_API_KEY
VITE_FIREBASE_AUTH_DOMAIN=YOUR_AUTH_DOMAIN
VITE_FIREBASE_PROJECT_ID=YOUR_PROJECT_ID
VITE_FIREBASE_STORAGE_BUCKET=YOUR_STORAGE_BUCKET
VITE_FIREBASE_MESSAGING_SENDER_ID=YOUR_SENDER_ID
VITE_FIREBASE_APP_ID=YOUR_APP_ID

환경 변수로 분리하면 개발/운영 환경을 쉽게 관리할 수 있다.

firebaseConfig 안에 있는 내용을 넣으면 됨.


5. Firebase 초기화

파일 생성

src/firebase.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID,
};

const app = initializeApp(firebaseConfig);

export const auth = getAuth(app);

이 파일은 Firebase 연결 및 Authentication 객체 생성을 담당한다.


6. 로그인 구현

파일 생성

src/pages/Login.jsx
import { GoogleAuthProvider, OAuthProvider, signInWithPopup } from "firebase/auth";
import { auth } from "../firebase";

async function login(provider) {
  // ① 어떤 로그인 창을 띄울지 선택
  const authProvider =
    provider === "GOOGLE"
      ? new GoogleAuthProvider()
      : new OAuthProvider("apple.com");

  // ① Firebase 로그인 팝업 → 사용자가 구글/애플 계정으로 인증
  const { user } = await signInWithPopup(auth, authProvider);

  // ② 여권(ID Token) 발급받기
  const idToken = await user.getIdToken();

  // ③ 여권을 백엔드에 제출하고, 사원증(서비스 JWT)을 받아온다
  const res = await fetch("/api/auth/firebase", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ idToken }),
  });

  const { accessToken } = await res.json();

  // ⑥ 이제부터 우리 서비스의 출입증은 이 JWT. 안전한 곳에 보관.
  localStorage.setItem("accessToken", accessToken);
}

export default function Login() {
  return (
    <>
      <button onClick={() => login("GOOGLE")}>Google 로그인</button>
      <button onClick={() => login("APPLE")}>Apple 로그인</button>
    </>
  );
}

 

여기서 멈추고 확인: 콘솔에 idToken을 찍어보세요. eyJ...로 시작하는 긴 문자열이 나오면 프론트는 끝입니다. 여권을 받은 상태.


7.Spring Boot — 토큰 확인하고 JWT 주기

 
gradle
implementation 'com.google.firebase:firebase-admin:9.2.0'

다운받은 키를 여기 두기

src/main/resources/firebase-service-account.json

 

앱 켜질 때 Firebase 초기화

@Configuration
public class FirebaseConfig {
    @PostConstruct
    public void init() throws IOException {
        var stream = new ClassPathResource("firebase-service-account.json").getInputStream();
        FirebaseApp.initializeApp(FirebaseOptions.builder()
                .setCredentials(GoogleCredentials.fromStream(stream))
                .build());
    }
}

 

로그인 받는 API

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @PostMapping("/firebase")
    public Map<String, String> login(@RequestBody Map<String, String> body) throws Exception {
        // 1) Firebase가 준 증명서가 진짜인지 확인
        FirebaseToken decoded = FirebaseAuth.getInstance()
                .verifyIdToken(body.get("idToken"));

        String uid = decoded.getUid();
        String email = decoded.getEmail();
        // (필요하면 여기서 DB에 회원 저장)

        // 2) 우리 서비스용 JWT 만들어서 돌려줌
        String token = createJwt(uid, email);
        return Map.of("accessToken", token);
    }
}

verifyIdToken이 핵심. 위조·만료된 토큰이면 여기서 알아서 예외가 남. JWT 만드는 부분(createJwt)은 jjwt 같은 라이브러리로 따로 만들면 됨.


4. 이후 요청

이제 Firebase는 끝. 모든 API에 우리 JWT만 붙이면 됨

fetch("/api/내데이터", {
  headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
});

파일 정리

frontend/src/
  firebase.js        ← 연결
  pages/Login.jsx    ← 로그인 → 토큰 받기

backend/
  FirebaseConfig.java   ← 초기화
  AuthController.java    ← 토큰 확인 → JWT 발급
  resources/firebase-service-account.json  ← 키 (깃에 올리지 말기!)

한 줄 요약

Firebase로 "누구인지" 확인 → 백엔드가 "우리 출입증(JWT)" 발급 → 이후엔 JWT만 사용.

반응형