콘텐츠로 이동

04. 내 컴퓨터 안의 가상 서버 (Emulator & TDD)

"배포는 짜릿하지만 위험합니다. 내 컴퓨터에서 먼저 완벽하게 검토하세요."

실제 서버에 코드를 올렸을 때 에러가 나면 수정하기 어렵고 비용도 발생할 수 있습니다. Firebase 에뮬레이터를 통해 클라우드와 똑같은 환경을 내 컴퓨터에 만들고, 테스트 코드로 품질을 보장하는 법을 배웁니다.


1. Local Emulator: 완벽한 시뮬레이션

Firebase 에뮬레이터는 구글 클라우드 서버를 내 컴퓨터에 그대로 복제해 놓은 것과 같습니다.

🛠️ 에뮬레이터 환경 통합

프론트엔드와 백엔드 코드가 로컬 에뮬레이터를 바라보도록 설정합니다.

  1. 프론트엔드(React): localhost:8080(DB), localhost:9099(Auth)에 연결.
  2. 백엔드(FastAPI): 환경 변수 FIRESTORE_EMULATOR_HOSTGCLOUD_PROJECT를 설정하여 로컬 DB에 접속.
// frontend/src/firebase.ts
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
  console.log("🔧 Using Firebase Emulators");
  connectAuthEmulator(auth, "http://127.0.0.1:9099");
  connectFirestoreEmulator(db, "127.0.0.1", 8080);
}

⚠️ 데이터 오염 주의

환경 변수 설정 없이 스크립트를 실행하면 실제 운영(Production) DB에 데이터가 들어갈 수 있습니다. 에뮬레이터 UI(localhost:4000)에 데이터가 보이지 않는다면 설정을 다시 확인하세요.


2. Test Driven: AI를 활용한 테스트 자동화

"코드가 잘 돌아가겠지?"라는 추측 대신, 테스트 코드로 증명해야 합니다. AI는 복잡한 테스트 케이스를 짜는 데 아주 훌륭한 조수입니다.

🧪 TDD 개발 워크플로우 (3단계 원칙)

기본적으로 필요한 백엔드 기능(Functions)을 중심으로 테스트 코드를 작성하여 TDD 기반을 구축합니다. 나중에 프론트엔드 TDD와 연동하여 전체 개발 과정에 TDD를 활용하게 됩니다.

참고: 프론트엔드 테스트는?

이 챕터에서는 백엔드 로직의 무결성을 검증하는 데 집중합니다. React 컴포넌트나 UI 상호작용에 대한 테스트(Frontend TDD)는 6장 Capstone: AI Agent에서 다루며, 최종적으로 두 테스트가 합쳐져 완벽한 CI/CD 파이프라인을 구성합니다.

모든 백엔드 테스트는 [1. 초기화(Initialize) → 2. 시딩(Seed) → 3. 테스트(Test)]의 3단계 구성을 따릅니다.

  1. 초기화 (Initialize): 에뮬레이터 환경에서 이전 테스트의 잔류 데이터를 배제하고 깨끗한 상태로 시작합니다.
  2. 시딩 (Seed): seed.py 등을 실행하여 테스트에 필요한 기초 데이터를 주입합니다.
  3. 테스트 (Test): 준비된 환경에서 실제 비즈니스 로직(Happy/Sad Path)을 검증합니다.
  4. 멱등성 (Idempotency): 위 과정을 통해 테스트는 언제 어디서 실행해도 같은 결과를 내야 합니다.
  5. 환경 분리: 반드시 Firebase 에뮬레이터 환경에서 실행하여 운영 데이터 오염을 방지합니다.

📂 테스트 디렉토리 구조

└── functions/tests/     # ✅ 테스트 코드 모음
    ├── conftest.py      # 설정 및 공유 Fixture (에뮬레이터 설정, 자동 시딩 등)
    ├── test_api.py      # 엔드포인트(API) 테스트 (requests 활용)
    └── test_logic.py    # 순수 비즈니스 로직 테스트 (DAL 직접 호출)

🚀 테스트 실행

cd functions
./venv/bin/pytest tests/

🤖 AI에게 테스트 요청하는 방법 (Design First)

무작정 테스트 코드를 작성해달라고 하면 AI도 헷갈려합니다. 먼저 prd.md에 정의된 기능 명세를 바탕으로 함수의 뼈대(Scaffolding)를 만들고, 그 다음에 테스트를 요청하세요.

  1. Step 1. 기능 명세 기반 함수 껍데기 생성

    "prd.md를 참고해서 [기능명]에 필요한 백엔드 함수와 데이터 입출력 양식만 먼저 만들어줘. (함수 내용은 pass로 비워둬도 좋아)"

  2. Step 2. 테스트 코드 생성

    "방금 만든 함수들을 검증할 수 있는 pytest 코드를 작성해줘."

통합 프롬프트 예시

prd.md의 [기능명] 명세를 검증하고 싶어. 다음 단계로 개발해줘.

  1. 먼저 functions/main.py에 필요한 빈 함수(Empty Function)와 입출력 모델을 정의해줘.
  2. 그 다음, 이 함수들이 정상 작동하는지 검증하는 pytest 코드를 tests/ 폴더에 작성해줘.
  3. 테스트 실행 전 seed.py를 활용해 DB를 초기화하는 Fixture도 잊지 마.

3. Debugging: 데이터 흐름 추적하기

에뮬레이터의 꽃은 Emulator Suite UI입니다.

  1. Firestore Emulator: 데이터가 실시간으로 쌓이고 변하는 모습을 눈으로 확인합니다.
  2. Logs: FastAPI에서 출력하는 print나 에러 로그를 한곳에서 모아봅니다.
  3. Authentication: 가짜 계정을 무제한으로 만들어 로그인 테스트를 진행합니다.

4. [Check] 배포 전 최종 점검 (Pre-flight Check)

테스트가 모두 통과했다면, 배포 직전 마지막 관문을 통과해야 합니다.

  • Linting: 코드 스타일이 깔끔한가? (flake8, eslint)
  • Environment Variables: 실제 서버용 API Key와 로컬용 주소가 섞이지 않았는가?
  • Build Test: npm run build를 했을 때 에러 없이 완료되는가?

5. 핵심 정리

용어 설명 비유
Emulator Suite Firebase의 모든 기능을 로컬에서 돌려주는 도구 가상 비행 시뮬레이터
TDD (테스트 주도 개발) 테스트 코드를 먼저 짜거나 병행하며 개발하는 방식 설계도대로 지어졌는지 검측하며 짓기
Pytest Python 코드를 테스트하기 위한 쉽고 강력한 프레임워크 자동 검사 장치
Mocking 실제 DB나 API 대신 가짜 데이터를 사용하는 기술 연습용 샌드백

준비 완료! 이제 우리 시스템은 '검증된 시스템'이 되었습니다. 다음 05장에서는 내가 수동으로 배포하지 않아도, 코드를 올리기만 하면 비서가 알아서 배포해 주는 24시간 자동 배포 시스템(CI/CD)을 구축해 보겠습니다!


실습 과제: 에뮬레이터를 켠 상태에서 AI가 짜준 테스트 코드를 실행(pytest)해 보고, 모든 테스트가 Green (Pass)이 뜨는 짜릿함을 경험해 보세요!