1Firebase 프로젝트 설정
- Firebase Console 에서 프로젝트 생성
- Authentication 클릭 → 시작하기
- Sign-in method에서 원하는 로그인 방식 활성화:
- 이메일/비밀번호
- GitHub (OAuth 설정 필요)
- 프로젝트 설정 → 일반 → 웹 앱 추가
- Firebase 설정 정보 복사
2React 앱 연동
터미널
npm install firebase환경 변수 설정
.env.local
# .env.local
VITE_FIREBASE_API_KEY=xxx
VITE_FIREBASE_AUTH_DOMAIN=your-app.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=your-app
VITE_FIREBASE_STORAGE_BUCKET=your-app.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID=xxx
VITE_FIREBASE_APP_ID=xxxFirebase 초기화
lib/firebase.ts
// lib/firebase.ts
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);3이메일/비밀번호 인증
components/EmailAuth.tsx
// components/EmailAuth.tsx
import { useState } from 'react';
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
} from 'firebase/auth';
import { auth } from '../lib/firebase';
export function EmailAuth() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isLogin, setIsLogin] = useState(true);
const [error, setError] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
try {
if (isLogin) {
await signInWithEmailAndPassword(auth, email, password);
} else {
await createUserWithEmailAndPassword(auth, email, password);
}
} catch (err: any) {
setError(err.message);
}
};
return (
<form onSubmit={handleSubmit} className="space-y-4">
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="이메일"
className="w-full px-4 py-2 border rounded-lg"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="비밀번호 (6자 이상)"
className="w-full px-4 py-2 border rounded-lg"
required
minLength={6}
/>
{error && <p className="text-red-500 text-sm">{error}</p>}
<button
type="submit"
className="w-full py-2 bg-blue-600 text-white rounded-lg"
>
{isLogin ? '로그인' : '회원가입'}
</button>
<button
type="button"
onClick={() => setIsLogin(!isLogin)}
className="w-full text-sm text-gray-600"
>
{isLogin ? '계정이 없으신가요? 회원가입' : '이미 계정이 있으신가요? 로그인'}
</button>
</form>
);
}4Google 소셜 로그인
components/GoogleLogin.tsx
// components/GoogleLogin.tsx
import { signInWithPopup, GoogleAuthProvider } from 'firebase/auth';
import { auth } from '../lib/firebase';
const googleProvider = new GoogleAuthProvider();
export function GoogleLogin() {
const handleGoogleLogin = async () => {
try {
const result = await signInWithPopup(auth, googleProvider);
// result.user에 사용자 정보
console.log('로그인 성공:', result.user);
} catch (error: any) {
console.error('로그인 실패:', error.message);
}
};
return (
<button
onClick={handleGoogleLogin}
className="flex items-center gap-2 px-4 py-2 bg-white border rounded-lg"
>
<svg className="w-5 h-5" viewBox="0 0 24 24">
<path
fill="#4285F4"
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
/>
<path
fill="#34A853"
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
/>
<path
fill="#FBBC05"
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
/>
<path
fill="#EA4335"
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
/>
</svg>
Google로 계속하기
</button>
);
}정보
Firebase Console에서 Google 로그인을 먼저 활성화해야 합니다. GitHub, Facebook 등 다른 프로바이더도 비슷한 방식으로 추가할 수 있습니다.
5인증 상태 관리 (커스텀 훅)
onAuthStateChanged로 로그인 상태 변화를 감지합니다.
hooks/useAuth.ts
// hooks/useAuth.ts
import { useState, useEffect } from 'react';
import { User, onAuthStateChanged, signOut } from 'firebase/auth';
import { auth } from '../lib/firebase';
export function useAuth() {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setUser(user);
setLoading(false);
});
// 컴포넌트 언마운트 시 구독 해제
return () => unsubscribe();
}, []);
const logout = async () => {
await signOut(auth);
};
return {
user,
loading,
isAuthenticated: !!user,
logout,
};
}Context로 전역 상태 관리
contexts/AuthContext.tsx
// contexts/AuthContext.tsx
import { createContext, useContext, ReactNode } from 'react';
import { User } from 'firebase/auth';
import { useAuth } from '../hooks/useAuth';
interface AuthContextType {
user: User | null;
loading: boolean;
isAuthenticated: boolean;
logout: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const auth = useAuth();
return (
<AuthContext.Provider value={auth}>
{children}
</AuthContext.Provider>
);
}
export function useAuthContext() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuthContext must be used within AuthProvider');
}
return context;
}
// main.tsx에서 사용
// <AuthProvider>
// <App />
// </AuthProvider>6보호된 라우트 구현
components/ProtectedRoute.tsx
// components/ProtectedRoute.tsx
import { Navigate } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext';
interface ProtectedRouteProps {
children: React.ReactNode;
}
export function ProtectedRoute({ children }: ProtectedRouteProps) {
const { isAuthenticated, loading } = useAuthContext();
if (loading) {
return <div>로딩 중...</div>;
}
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return <>{children}</>;
}
// 사용 예시
// <Route
// path="/dashboard"
// element={
// <ProtectedRoute>
// <Dashboard />
// </ProtectedRoute>
// }
// />사용자 프로필 표시
components/UserProfile.tsx
// components/UserProfile.tsx
import { useAuthContext } from '../contexts/AuthContext';
export function UserProfile() {
const { user, logout, isAuthenticated } = useAuthContext();
if (!isAuthenticated) {
return null;
}
return (
<div className="flex items-center gap-4 p-4 bg-white rounded-lg shadow">
{user?.photoURL && (
<img
src={user.photoURL}
alt={user.displayName || '프로필'}
className="w-12 h-12 rounded-full"
/>
)}
<div>
<p className="font-bold">{user?.displayName || '사용자'}</p>
<p className="text-sm text-gray-600">{user?.email}</p>
</div>
<button
onClick={logout}
className="ml-auto px-4 py-2 bg-red-600 text-white rounded-lg"
>
로그아웃
</button>
</div>
);
}언제 Firebase Auth를 선택할까요?
다음 단계
- 전화번호 인증 - SMS 인증 추가
- 사용자 관리 - 프로필 업데이트, 비밀번호 재설정
- Firestore 연동 - 사용자별 데이터 저장