AI로 러닝(Learn) 내일을 향해 러닝(Running)

원당컴퓨터학원에서 배우는 AI, 세상을 향해 달리다

데이터베이스

[Firebase] 2장 사용자 인증(Authentication) 실습

파아란기쁨1 2025. 8. 23. 09:06
반응형

🏰 우리 웹사이트를 특별한 성으로 만들기

우리가 만들 웹사이트를 마법의 성이라고 생각해보세요! 이 성에는:

  • 🚪 입구 (로그인)
  • 📝 회원가입 데스크
  • 🏃‍♂️ 출구 (로그아웃)
  • 👮‍♂️ 경비원 (상태 감지)

이 모든 걸 Firebase가 도와줄 거예요!

🎯 먼저 Firebase 콘솔에서 인증 설정하기

1단계: Firebase 콘솔 접속

  1. Firebase 콘솔에 들어가세요
  2. 여러분의 프로젝트를 클릭하세요

2단계: Authentication 활성화

  1. 왼쪽 메뉴에서 "Authentication" 클릭
  2. "시작하기" 버튼 클릭
  3. "Sign-in method" 탭 클릭
  4. "이메일/비밀번호" 를 클릭하고 "사용 설정" 체크
  5. "저장" 버튼 클릭

이제 우리 성에 마법의 문이 설치되었어요! ✨

🏗️ 완전한 인증 시스템 만들기

Firebase 사용자 인증 시스템
대화형 아티팩트 
 

🎯 코드 설명 - 각 기능별로 쉽게 이해하기

📝 회원가입 (signupUser 함수)

 
 
javascript
function signupUser() {
    // 1️⃣ 사용자가 입력한 정보 가져오기
    const email = document.getElementById('signupEmail').value;
    const password = document.getElementById('signupPassword').value;
    
    // 2️⃣ Firebase에게 "새 마법사 만들어줘!" 요청
    auth.createUserWithEmailAndPassword(email, password)
        .then(() => {
            // 3️⃣ 성공하면 축하 메시지!
            console.log("🎉 새 마법사 탄생!");
        })
        .catch((error) => {
            // 4️⃣ 실패하면 친절하게 알려주기
            console.log("😢 마법사 되기 실패:", error);
        });
}

🚪 로그인 (loginUser 함수)

 
 
javascript
function loginUser() {
    // 1️⃣ 이메일과 비밀번호 받기
    const email = document.getElementById('loginEmail').value;
    const password = document.getElementById('loginPassword').value;
    
    // 2️⃣ Firebase에게 "이 사람 맞나?" 확인 요청
    auth.signInWithEmailAndPassword(email, password)
        .then(() => {
            // 3️⃣ 맞다면 성 안으로 들어가기!
            console.log("🎉 성 입장 성공!");
        });
}

🏃‍♂️ 로그아웃 (logoutUser 함수)

 
 
javascript
function logoutUser() {
    // Firebase에게 "나 이제 나갈래!" 알리기
    auth.signOut()
        .then(() => {
            console.log("👋 안전하게 성을 나왔어요!");
        });
}

👮‍♂️ 상태 감지 (onAuthStateChanged)

가장 마법 같은 기능이에요! Firebase가 자동으로 경비원 역할을 해줘요:

 
 
javascript
auth.onAuthStateChanged((user) => {
    if (user) {
        // 로그인된 마법사다!
        console.log("✅ 등록된 마법사 확인!");
        showUserScreen(user); // 특별한 화면 보여주기
    } else {
        // 그냥 손님이다
        console.log("❌ 손님입니다");
        showAuthScreen(); // 로그인 화면 보여주기
    }
});

🎮 실제로 해보기!

1단계: 설정 넣기

위 코드에서 이 부분을 여러분의 실제 Firebase 설정으로 바꿔주세요:

 
 
javascript
const firebaseConfig = {
    apiKey: "your-api-key-here",  // 여기에 실제 API 키
    authDomain: "your-project.firebaseapp.com",  // 실제 도메인
    projectId: "your-project-id",  // 실제 프로젝트 ID
    // ... 나머지도 실제 값으로
};

2단계: 테스트해보기

  1. 파일을 브라우저에서 열기
  2. F12 눌러서 콘솔 열기
  3. 회원가입 해보기 (test@example.com / 123456)
  4. 로그아웃 하기
  5. 다시 로그인하기

3단계: 마법 관찰하기

콘솔에서 이런 메시지들을 볼 수 있어요:

 
 
🎉 Firebase 마법이 시작되었어요!
📝 새로운 마법사 등록 시작: test@example.com
👮‍♂️ 경비원이 신원을 확인했어요: [User Object]
✅ 확인됨: 등록된 마법사입니다!
🏠 사용자 화면을 표시합니다

🎯 각 기능이 어떻게 작동하는지

🔄 상태 감지의 마법

Firebase는 24시간 경비원처럼 항상 지켜보고 있어요:

  • 사용자가 로그인하면 → 즉시 알려줌 ✅
  • 사용자가 로그아웃하면 → 즉시 알려줌 ❌
  • 페이지를 새로고침해도 → 로그인 상태 유지! 🔄

🎨 UI 변경의 마법

 
 
javascript
if (user) {
    // 로그인했을 때
    document.getElementById('authScreen').classList.add('hidden');     // 로그인 화면 숨기기
    document.getElementById('userScreen').classList.remove('hidden');  // 사용자 화면 보여주기
} else {
    // 로그아웃했을 때
    document.getElementById('authScreen').classList.remove('hidden');  // 로그인 화면 보여주기  
    document.getElementById('userScreen').classList.add('hidden');     // 사용자 화면 숨기기
}

🚨 문제 해결 가이드

😢 "등록되지 않은 마법사" 에러

→ Firebase 콘솔에서 Authentication이 활성화되었는지 확인!

😅 "비밀 주문이 틀렸어요" 에러

→ 비밀번호를 정확히 입력했는지 확인!

🔧 아무것도 작동 안 함

→ F12 → Console에서 빨간 에러 메시지 확인!

🎉 축하해요!

이제 여러분은:

  • 회원가입 시스템을 만들 수 있어요
  • 로그인/로그아웃 기능을 구현할 수 있어요
  • 사용자 상태에 따라 화면을 바꿀 수 있어요
  • 실시간으로 인증 상태를 감지할 수 있어요

여러분은 이제 진짜 웹 개발 마법사가 되었어요! 🧙‍♂️✨

 

 

전체 소스코드

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>🏰 마법의 성 - Firebase 인증</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Comic Sans MS', cursive;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 800px;
            margin: 0 auto;
            text-align: center;
        }

        h1 {
            font-size: 2.5em;
            margin: 20px 0;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }

        .castle {
            font-size: 4em;
            margin: 20px;
            animation: bounce 3s ease-in-out infinite;
        }

        @keyframes bounce {
            0%, 100% { transform: translateY(0px); }
            50% { transform: translateY(-20px); }
        }

        .auth-container {
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 30px;
            margin: 20px auto;
            max-width: 400px;
            box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
        }

        .form-group {
            margin: 15px 0;
            text-align: left;
        }

        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }

        input {
            width: 100%;
            padding: 12px;
            border: none;
            border-radius: 10px;
            font-size: 1em;
            background: rgba(255, 255, 255, 0.9);
            color: #333;
        }

        input:focus {
            outline: none;
            box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
            transform: scale(1.02);
        }

        button {
            background: linear-gradient(45deg, #ff6b35, #f7931e);
            color: white;
            border: none;
            padding: 15px 25px;
            border-radius: 25px;
            font-size: 1.1em;
            cursor: pointer;
            margin: 10px 5px;
            transition: all 0.3s ease;
            font-weight: bold;
        }

        button:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0,0,0,0.2);
        }

        button:active {
            transform: translateY(0);
        }

        .btn-logout {
            background: linear-gradient(45deg, #e74c3c, #c0392b);
        }

        .user-info {
            background: rgba(76, 175, 80, 0.2);
            border: 2px solid rgba(76, 175, 80, 0.5);
            border-radius: 15px;
            padding: 20px;
            margin: 20px 0;
        }

        .status-message {
            padding: 15px;
            border-radius: 10px;
            margin: 15px 0;
            font-weight: bold;
        }

        .success {
            background: rgba(76, 175, 80, 0.3);
            border: 1px solid #4CAF50;
        }

        .error {
            background: rgba(244, 67, 54, 0.3);
            border: 1px solid #f44336;
        }

        .info {
            background: rgba(33, 150, 243, 0.3);
            border: 1px solid #2196F3;
        }

        .hidden {
            display: none;
        }

        .toggle-link {
            color: #ffd700;
            cursor: pointer;
            text-decoration: underline;
            margin-top: 15px;
            display: block;
        }

        .toggle-link:hover {
            color: #ffed4a;
        }

        .welcome-screen {
            background: rgba(76, 175, 80, 0.1);
            border-radius: 20px;
            padding: 40px;
            margin: 20px 0;
        }

        .user-avatar {
            font-size: 3em;
            margin: 20px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🏰 마법의 성에 오신 걸 환영해요!</h1>
        <div class="castle">🏰</div>
        
        <!-- 상태 메시지 표시 영역 -->
        <div id="statusMessage"></div>
        
        <!-- 로그인하지 않은 사용자용 화면 -->
        <div id="authScreen">
            <div class="auth-container">
                <!-- 로그인 폼 -->
                <div id="loginForm">
                    <h2>🚪 성문 입장 (로그인)</h2>
                    <div class="form-group">
                        <label>📧 마법사 이메일:</label>
                        <input type="email" id="loginEmail" placeholder="magic@wizard.com">
                    </div>
                    <div class="form-group">
                        <label>🔐 비밀 주문 (비밀번호):</label>
                        <input type="password" id="loginPassword" placeholder="비밀 주문을 입력하세요">
                    </div>
                    <button onclick="loginUser()">🚪 성 안으로 들어가기</button>
                    <div class="toggle-link" onclick="showSignupForm()">
                        📝 아직 마법사가 아니신가요? 여기서 등록하세요!
                    </div>
                </div>
                
                <!-- 회원가입 폼 -->
                <div id="signupForm" class="hidden">
                    <h2>📝 마법사 등록 (회원가입)</h2>
                    <div class="form-group">
                        <label>📧 마법사 이메일:</label>
                        <input type="email" id="signupEmail" placeholder="magic@wizard.com">
                    </div>
                    <div class="form-group">
                        <label>🔐 비밀 주문 만들기:</label>
                        <input type="password" id="signupPassword" placeholder="강력한 비밀 주문을 만드세요">
                    </div>
                    <div class="form-group">
                        <label>🔐 비밀 주문 확인:</label>
                        <input type="password" id="confirmPassword" placeholder="비밀 주문을 다시 입력하세요">
                    </div>
                    <button onclick="signupUser()">📝 마법사로 등록하기</button>
                    <div class="toggle-link" onclick="showLoginForm()">
                        🚪 이미 마법사이신가요? 여기서 입장하세요!
                    </div>
                </div>
            </div>
        </div>
        
        <!-- 로그인한 사용자용 화면 -->
        <div id="userScreen" class="hidden">
            <div class="welcome-screen">
                <div class="user-avatar">🧙‍♂️</div>
                <h2>🎉 환영합니다, 위대한 마법사님!</h2>
                <div class="user-info" id="userInfo">
                    <!-- 사용자 정보가 여기에 표시됩니다 -->
                </div>
                <button class="btn-logout" onclick="logoutUser()">🏃‍♂️ 성에서 나가기 (로그아웃)</button>
                <button onclick="testAuthState()">🧪 마법 상태 확인</button>
            </div>
        </div>
    </div>

    <!-- Firebase SDK -->
    <script src="https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/10.7.1/firebase-auth.js"></script>
    
    <script>
        // 🔑 Firebase 설정 (여기에 여러분의 실제 설정을 넣어주세요!)
        const firebaseConfig = {
            apiKey: "your-api-key-here",
            authDomain: "your-project.firebaseapp.com",
            projectId: "your-project-id",
            storageBucket: "your-project.appspot.com",
            messagingSenderId: "123456789",
            appId: "your-app-id-here"
        };

        // 🚀 Firebase 초기화
        let app;
        let auth;
        
        try {
            app = firebase.initializeApp(firebaseConfig);
            auth = firebase.auth();
            console.log("🎉 Firebase 마법이 시작되었어요!");
            showStatus("🎉 마법의 성이 준비되었어요!", "success");
        } catch (error) {
            console.error("😢 Firebase 초기화 실패:", error);
            showStatus("😢 마법의 성 준비 중 오류가 발생했어요. 설정을 확인해주세요!", "error");
        }

        // 📝 회원가입 함수
        function signupUser() {
            const email = document.getElementById('signupEmail').value;
            const password = document.getElementById('signupPassword').value;
            const confirmPassword = document.getElementById('confirmPassword').value;

            // 입력값 검사
            if (!email || !password) {
                showStatus("😅 이메일과 비밀번호를 모두 입력해주세요!", "error");
                return;
            }

            if (password !== confirmPassword) {
                showStatus("😅 비밀번호가 일치하지 않아요!", "error");
                return;
            }

            if (password.length < 6) {
                showStatus("😅 비밀번호는 6글자 이상이어야 해요!", "error");
                return;
            }

            console.log("📝 새로운 마법사 등록 시작:", email);
            showStatus("✨ 마법사 등록 중...", "info");

            // Firebase로 회원가입
            auth.createUserWithEmailAndPassword(email, password)
                .then((userCredential) => {
                    const user = userCredential.user;
                    console.log("🎉 마법사 등록 성공!", user);
                    showStatus("🎉 축하해요! 새로운 마법사가 되었어요!", "success");
                    clearInputs();
                })
                .catch((error) => {
                    console.error("😢 등록 실패:", error);
                    let message = "😢 마법사 등록에 실패했어요.";
                    
                    // 에러 종류별 친근한 메시지
                    if (error.code === 'auth/email-already-in-use') {
                        message = "😅 이미 등록된 이메일이에요! 로그인을 시도해보세요.";
                    } else if (error.code === 'auth/invalid-email') {
                        message = "😅 올바른 이메일 형식을 입력해주세요.";
                    } else if (error.code === 'auth/weak-password') {
                        message = "😅 더 강한 비밀번호를 만들어주세요!";
                    }
                    
                    showStatus(message, "error");
                });
        }

        // 🚪 로그인 함수
        function loginUser() {
            const email = document.getElementById('loginEmail').value;
            const password = document.getElementById('loginPassword').value;

            if (!email || !password) {
                showStatus("😅 이메일과 비밀번호를 모두 입력해주세요!", "error");
                return;
            }

            console.log("🚪 마법사 입장 시도:", email);
            showStatus("🔍 마법사 신원 확인 중...", "info");

            // Firebase로 로그인
            auth.signInWithEmailAndPassword(email, password)
                .then((userCredential) => {
                    const user = userCredential.user;
                    console.log("🎉 입장 성공!", user);
                    showStatus("🎉 환영합니다! 성 안으로 들어오세요!", "success");
                    clearInputs();
                })
                .catch((error) => {
                    console.error("😢 로그인 실패:", error);
                    let message = "😢 성 입장에 실패했어요.";
                    
                    // 에러 종류별 친근한 메시지
                    if (error.code === 'auth/user-not-found') {
                        message = "😅 등록되지 않은 마법사예요. 먼저 회원가입을 해주세요!";
                    } else if (error.code === 'auth/wrong-password') {
                        message = "😅 비밀 주문이 틀렸어요! 다시 시도해보세요.";
                    } else if (error.code === 'auth/invalid-email') {
                        message = "😅 올바른 이메일 형식을 입력해주세요.";
                    }
                    
                    showStatus(message, "error");
                });
        }

        // 🏃‍♂️ 로그아웃 함수
        function logoutUser() {
            console.log("🏃‍♂️ 마법사가 성에서 나갑니다...");
            showStatus("👋 안전하게 성에서 나가는 중...", "info");

            auth.signOut()
                .then(() => {
                    console.log("✅ 로그아웃 성공!");
                    showStatus("👋 안전하게 성을 나왔어요! 또 놀러오세요!", "success");
                })
                .catch((error) => {
                    console.error("😢 로그아웃 오류:", error);
                    showStatus("😅 성에서 나가는 중 문제가 생겼어요.", "error");
                });
        }

        // 👮‍♂️ 사용자 상태 감지 (Firebase의 마법!)
        auth.onAuthStateChanged((user) => {
            console.log("👮‍♂️ 경비원이 신원을 확인했어요:", user);
            
            if (user) {
                // 로그인된 상태
                console.log("✅ 확인됨: 등록된 마법사입니다!");
                showUserScreen(user);
            } else {
                // 로그인되지 않은 상태  
                console.log("❌ 미확인: 손님입니다.");
                showAuthScreen();
            }
        });

        // 🖥️ 로그인 화면 보여주기
        function showAuthScreen() {
            document.getElementById('authScreen').classList.remove('hidden');
            document.getElementById('userScreen').classList.add('hidden');
            console.log("🖥️ 로그인 화면을 표시합니다");
        }

        // 🏠 사용자 화면 보여주기
        function showUserScreen(user) {
            document.getElementById('authScreen').classList.add('hidden');
            document.getElementById('userScreen').classList.remove('hidden');
            
            // 사용자 정보 표시
            document.getElementById('userInfo').innerHTML = `
                <h3>🧙‍♂️ 마법사 정보</h3>
                <p><strong>📧 마법사 이메일:</strong> ${user.email}</p>
                <p><strong>🆔 마법사 ID:</strong> ${user.uid}</p>
                <p><strong>📅 등록 날짜:</strong> ${user.metadata.creationTime}</p>
                <p><strong>🔄 마지막 로그인:</strong> ${user.metadata.lastSignInTime}</p>
                <p><strong>✅ 이메일 인증:</strong> ${user.emailVerified ? '완료' : '미완료'}</p>
            `;
            
            console.log("🏠 사용자 화면을 표시합니다");
        }

        // 📝 폼 전환 함수들
        function showSignupForm() {
            document.getElementById('loginForm').classList.add('hidden');
            document.getElementById('signupForm').classList.remove('hidden');
            clearInputs();
        }

        function showLoginForm() {
            document.getElementById('signupForm').classList.add('hidden');
            document.getElementById('loginForm').classList.remove('hidden');
            clearInputs();
        }

        // 🧹 입력 필드 정리 함수
        function clearInputs() {
            const inputs = document.querySelectorAll('input');
            inputs.forEach(input => input.value = '');
        }

        // 📢 상태 메시지 표시 함수
        function showStatus(message, type) {
            const statusDiv = document.getElementById('statusMessage');
            statusDiv.innerHTML = `<div class="status-message ${type}">${message}</div>`;
            
            // 3초 후 메시지 자동 삭제
            setTimeout(() => {
                statusDiv.innerHTML = '';
            }, 3000);
        }

        // 🧪 인증 상태 테스트 함수
        function testAuthState() {
            const user = auth.currentUser;
            
            if (user) {
                console.log("🧪 현재 마법사 상태:");
                console.log("   📧 이메일:", user.email);
                console.log("   🆔 ID:", user.uid);
                console.log("   ✅ 로그인 상태: 활성");
                showStatus("🧪 마법 상태: 활성화! 모든 마법을 사용할 수 있어요!", "success");
            } else {
                console.log("🧪 현재 상태: 손님 (로그인 필요)");
                showStatus("🧪 마법 상태: 비활성화. 마법을 사용하려면 로그인하세요!", "info");
            }
        }

        // 🎮 페이지 로드 시 초기화
        window.onload = function() {
            console.log("🎮 마법의 성이 열렸어요!");
            console.log("👀 사용법:");
            console.log("   1️⃣ F12를 눌러 콘솔을 열어보세요");
            console.log("   2️⃣ 회원가입 또는 로그인을 해보세요");
            console.log("   3️⃣ 상태 변화를 관찰해보세요");
            
            showStatus("🏰 마법의 성에 오신 걸 환영해요! 마법사가 되어보세요!", "info");
        };
    </script>
</body>
</html>

 

미리 보기

반응형