📘 Aura_AI Day 10
FastAPI 백엔드 분리 & 프론트엔드 연동 (노베이스 A~Z)
이 글은 **“FastAPI가 뭔지도 모르던 상태”에서
Day 10 작업을 처음부터 끝까지 이해하고,
직접 같은 구조를 다시 만들 수 있게” 쓰였습니다.
Part 1. Day 10의 정체부터 이해하기
0️⃣ Day 10은 “무엇을 만드는 날”인가요?
Day 10은 기능 개발 날이 아닙니다.
아키텍처 전환일입니다.
Day 1 ~ Day 9까지의 상태
- Next.js App Router
- Auth.js 인증
- Zustand 전역 상태
- 회원가입 / 로그인 완료
- UI 중심 개발
👉 이 단계에서는 프론트 혼자서도 충분했습니다.
Day 10부터 바뀌는 점
이제 앞으로 만들 것들:
- 날씨 데이터 가공
- 사용자 조건 기반 추천
- GPT 프롬프트 조합
- 대화 기록 요약
이건 공통점이 있습니다.
❗ UI가 아니라 “데이터 처리 로직” 중심
그래서 Day 10의 목표는 단 하나입니다.
프론트엔드와 독립된 “진짜 백엔드”를 하나 만들고
둘이 네트워크로 대화하게 만들기
1️⃣ 왜 Next.js API Route로 계속 안 갔나요?
❓ 흔한 질문
“이미 Next.js API Route 쓰고 있었는데
굳이 FastAPI를 또 만들 필요 있나요?”
✅ 결론
지금 시점에서는 FastAPI로 분리하는 게 맞습니다.
이유 1. 기술의 역할이 다르기 때문
영역적합한 기술
| UI / 화면 / 상태 | Next.js |
| 인증 / 라우팅 | Next.js |
| 데이터 가공 / AI | Python |
| GPT / 추천 로직 | Python |
👉 프론트가 잘하는 것과, 백엔드가 잘하는 걸 나눈 것
이유 2. GPT / 데이터 생태계
Python 쪽에는:
- Pandas
- LangChain
- OpenAI SDK
- 수많은 데이터 처리 라이브러리
👉 JS로도 가능하지만 비효율적
🎤 면접용 한 문장 (중요)
“Aura_AI의 핵심 로직은 GPT 프롬프트와 데이터 가공이기 때문에,
프론트엔드는 UI에 집중하고 백엔드는 Python 기반으로 분리했습니다.”
2️⃣ “프론트 안에 백엔드 폴더를 만든다”는 게 이상한가요?
❌ 아닙니다.
오히려 실무에서도 가장 흔한 초기 구조입니다.
이유
- 하나의 레포로 관리
- 배포 전까지 관리 편함
- 나중에 백엔드만 분리 가능
👉 지금 이 타이밍이 정확히 맞음
3️⃣ Day 10에서 “일부러” 안 한 것들
이게 중요합니다.
오늘 안 한 것 ❌
- DB 연결
- GPT 호출
- 복잡한 추천 로직
- JWT / 쿠키 인증
오늘 한 것 ✅
- FastAPI 서버 실행
- CORS 문제 해결
- 프론트 → 백엔드 요청
- Header 기반 유저 식별
- 요청/응답 구조 고정
👉 “길부터 닦은 날”
4️⃣ 전체 그림 먼저 보기 (아주 중요)
Day 10 이후의 데이터 흐름은 이겁니다.
[Next.js UI]
↓ fetch
(Header: x-user-id)
↓
[FastAPI]
↓
(가공 / 처리)
↓
[Response JSON]
아직:
- GPT ❌
- DB ❌
하지만:
- 통신은 완성 ✅
5️⃣ 개발 환경 준비 (노베이스 기준)
5-1. Python 확인
python --version
권장: Python 3.10 이상
5-2. backend 폴더 생성
mkdir backend
cd backend
이 시점부터:
- Next.js = 프론트
- backend 폴더 = 백엔드
5-3. 패키지 설치
pip install fastapi uvicorn python-dotenv
각각 왜 필요한가요?
- fastapi
- API 서버 프레임워크
- 라우터, 요청/응답 구조 제공
- uvicorn
- FastAPI를 실행시켜주는 서버
- uvicorn main:app --reload
- python-dotenv
- .env 파일 읽기
- GPT Key / DB 대비
6️⃣ Day 10 백엔드 폴더 구조 (완성형)
backend/
├─ main.py # 서버 시작점
├─ routers/ # API 엔드포인트
│ ├─ health.py
│ ├─ users.py
│ ├─ chats.py
│ └─ __init__.py
├─ schemas/ # 데이터 구조 정의
│ ├─ chat.py
│ └─ __init__.py
이 구조의 핵심 원칙
“요청을 받는 곳”과
“데이터 모양을 정의하는 곳”을 분리
이걸 안 하면:
- 파일 하나에 코드 500줄
- 수정할 때마다 사고 남
7️⃣ main.py의 역할 (아직 코드 X, 개념만)
main.py는 백엔드의 index.ts 같은 존재입니다.
여기서 하는 일:
- FastAPI 서버 생성
- CORS 설정
- 라우터 연결
👉 비즈니스 로직 없음
📘 Aura_AI Day 10
FastAPI 백엔드 분리 & 프론트엔드 연동
Part 2. 서버 뼈대 만들기 + 라우터 이해하기 (진짜 A~Z)
8️⃣ FastAPI 서버의 시작점: main.py
중요한 인식부터 잡고 갑니다
main.py는
👉 “이 서버가 어떤 규칙으로 동작할지”를 정의하는 파일입니다.
여기에는:
- 비즈니스 로직 ❌
- 데이터 처리 ❌
대신:
- 서버 생성
- 보안 규칙
- 라우터 연결
만 있습니다.
8-1. main.py 전체 코드 (완성본)
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from routers.health import router as health_router
from routers.users import router as users_router
from routers.chats import router as chats_router
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(health_router)
app.include_router(users_router)
app.include_router(chats_router)
이제 한 줄씩 해부합니다.
8-2. FastAPI()는 뭔가요?
app = FastAPI()
- FastAPI 서버 인스턴스를 생성합니다
- Express의 const app = express() 같은 개념
👉 이 app이 서버 그 자체
8-3. CORS는 왜 필요한가요? (진짜 중요)
from fastapi.middleware.cors import CORSMiddleware
❓ CORS가 뭐예요?
“다른 출처(origin)의 요청을 허용할지 말지”를 정하는 보안 규칙
현재 상황
구분주소
| 프론트 | http://localhost:3000 |
| 백엔드 | http://localhost:8000 |
👉 출처가 다름
그래서 CORS 설정 없으면:
- 브라우저가 요청 자체를 차단
- 서버 로그도 안 남음
- 초보자 멘붕 포인트 1위
8-4. CORS 설정 코드 해부
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
각각의 의미
- allow_origins
- 이 주소에서 오는 요청만 허용
- 실무에서는 환경별로 분리
- allow_methods=["*"]
- GET / POST / PUT / DELETE 전부 허용
- allow_headers=["*"]
- x-user-id 같은 커스텀 헤더 허용 (이게 중요)
👉 이 줄이 없으면 Day 10은 절대 성공 못함
9️⃣ 라우터란 무엇인가?
❓ 라우터(router)가 뭐예요?
URL과 함수를 연결하는 묶음
예:
- /health → health_check()
- /users/me → get_my_info()
왜 파일로 나누나요?
❌ 한 파일에 전부 쓰면:
- 파일 1000줄
- 수정 시 충돌
✅ 기능별로 나누면:
- 유지보수 쉬움
- 팀 협업 가능
9-1. 라우터 import 방식이 중요한 이유
from routers.health import router as health_router
이게 중요한 이유는 당신이 실제로 겪은 에러 때문입니다.
❌ 잘못된 방식 (에러 유발)
from routers import health
app.include_router(health.router) # 에러 가능
✅ 안전한 방식
from routers.health import router as health_router
👉 Python 패키지 로딩 순서 문제 방지
9-2. 라우터 등록
app.include_router(health_router)
의미:
- health.py 안에 정의된 모든 API를 서버에 등록
🔟 첫 번째 라우터: health.py
백엔드 만들 때 가장 먼저 만드는 파일
10-1. routers/health.py
from fastapi import APIRouter
router = APIRouter()
@router.get("/health")
def health_check():
return { "status": "ok" }
10-2. 왜 이게 필요하나요?
- 서버가 켜졌는지 확인
- 배포 후 헬스 체크
- 로드밸런서가 쓰는 API
👉 실무 필수
테스트 방법
uvicorn main:app --reload
브라우저:
http://localhost:8000/health
결과:
{ "status": "ok" }
🔟-1. 여기까지 성공하면?
이제 당신은:
- FastAPI 서버 실행 가능
- CORS 이해함
- 라우터 구조 이해함
👉 절반 성공
1️⃣1️⃣ 유저 라우터: users.py
Day 10의 핵심 개념 중 하나
11-1. 왜 JWT 안 쓰고 Header 쓰나요?
Day 10의 목표는:
“통신 구조 검증”
- JWT ❌
- 쿠키 ❌
- OAuth ❌
👉 그래서 가장 단순한 방식 선택
11-2. routers/users.py 전체 코드
from fastapi import APIRouter, Header, HTTPException
router = APIRouter(prefix="/users")
@router.get("/me")
def get_my_info(x_user_id: str | None = Header(default=None)):
if not x_user_id:
raise HTTPException(status_code=401, detail="User not authenticated")
return {
"id": x_user_id,
"name": "Dummy User",
"email": "dummy@example.com"
}
11-3. 코드 완전 해부
Header(...)
x_user_id: str | None = Header(default=None)
의미:
- HTTP Header에서 x-user-id 값을 읽음
- 없으면 None
인증 실패 처리
if not x_user_id:
raise HTTPException(status_code=401)
👉 “로그인 안 된 요청”의 기본 패턴
프론트에서 보내는 헤더
fetch("http://localhost:8000/users/me", {
headers: {
"x-user-id": user.id
}
});
🎤 면접용 포인트 (여기서)
“JWT를 바로 쓰지 않고 Header 기반으로 유저를 식별해
프론트와 백엔드 통신 구조를 먼저 검증했습니다.”
✅ Part 2 요약
- FastAPI 서버 구조 완성
- CORS 정확히 이해
- 라우터 분리 이유 이해
- Header 기반 인증의 의미 이해
📘 Aura_AI Day 10
FastAPI 백엔드 분리 & 프론트엔드 연동
Part 3. Pydantic 스키마 + 채팅 API + 프론트 연결 (핵심)
1️⃣2️⃣ schemas 폴더는 왜 필요한가?
노베이스가 가장 헷갈리는 지점부터 짚고 갑니다
❓ 질문
“라우터에서 바로 request.body 쓰면 되는데
왜 굳이 schemas 폴더를 만드나요?”
✅ 결론부터
👉 데이터 구조를 ‘문서 + 검증 + 계약’으로 만들기 위해서
❌ schemas 없이 가면?
@router.post("/start")
def start_chat(body: dict):
message = body["message"]
문제점:
- message 없으면 런타임 에러
- 타입 검증 ❌
- API 문서 자동 생성 ❌
✅ schemas를 쓰면?
- 요청 데이터 자동 검증
- 타입 강제
- Swagger 문서 자동 생성
- GPT 연동 시 안정성 ↑
👉 실무에서는 무조건 schemas 분리
1️⃣3️⃣ schemas/chat.py
13-1. 전체 코드
from pydantic import BaseModel
class ChatRequest(BaseModel):
message: str
class ChatResponse(BaseModel):
reply: str
13-2. BaseModel이 뭐예요?
FastAPI의 핵심 중 핵심
BaseModel은:
- 타입 검증
- 데이터 파싱
- 자동 문서화
를 담당합니다.
13-3. 이 코드에서 일어나는 일
{
"message": "오늘 날씨 어때?"
}
- message가 문자열 ❌ → 요청 거부
- message 없음 ❌ → 422 에러
👉 백엔드가 스스로 방어
1️⃣4️⃣ 채팅 라우터: routers/chats.py
이제 진짜 “서비스 같은 API”가 등장합니다
14-1. 전체 코드 (완성본)
from fastapi import APIRouter, Header, HTTPException
from schemas.chat import ChatRequest, ChatResponse
router = APIRouter(prefix="/chats")
@router.post("/start", response_model=ChatResponse)
def start_chat(
body: ChatRequest,
x_user_id: str | None = Header(default=None)
):
if not x_user_id:
raise HTTPException(status_code=401, detail="Unauthorized")
return ChatResponse(
reply=f"({x_user_id}) 님의 메시지: {body.message}"
)
1️⃣4️⃣-2. 이 함수의 구조를 그림으로 보면
POST /chats/start
│
├─ Header: x-user-id
├─ Body: { message }
│
├─ 인증 체크
└─ 응답: { reply }
1️⃣4️⃣-3. response_model은 왜 쓰나요?
@router.post("/start", response_model=ChatResponse)
의미:
- 응답은 반드시 ChatResponse 형태
- 다른 값 반환하면 자동 필터링
👉 API 계약 보장
❓ 없어도 되지 않나요?
기술적으로는 ❌
실무적으로는 ⛔
response_model 없는 API = 문서 없는 계약서
1️⃣5️⃣ FastAPI 자동 문서 (Swagger)
서버 실행 후 접속:
http://localhost:8000/docs
여기서 확인 가능:
- 모든 API
- 요청/응답 구조
- 직접 테스트
👉 면접에서 자랑해도 되는 포인트
1️⃣6️⃣ 프론트엔드 연동 구조
16-1. 흐름 요약
Zustand user.id
↓
HTTP Header (x-user-id)
↓
FastAPI
↓
Response
16-2. 실제 프론트 요청 코드
await fetch("http://localhost:8000/chats/start", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-user-id": user.id,
},
body: JSON.stringify({
message: inputMessage,
}),
});
16-3. 왜 Header에 user id를 넣었나요?
- URL ❌ (노출)
- Body ❌ (의미상 안 맞음)
- Header ✅ (인증/식별 용도)
👉 HTTP 철학에 맞는 선택
🎤 면접용 Q&A (추가)
Q. 왜 Next.js API Route를 안 쓰고 FastAPI로 분리했나요?
A.
Aura_AI의 핵심 로직은
날씨 데이터 처리, GPT 프롬프트 엔지니어링 등
Python 생태계가 훨씬 강점인 영역입니다.프론트엔드는 UI에,
백엔드는 데이터 처리에 집중하도록
역할을 분리했습니다.
Q. 왜 JWT 안 쓰고 Header 기반 인증을 썼나요?
A.
Day 10의 목표는
인증 구현이 아니라 통신 구조 검증이었기 때문에
가장 단순한 식별 방식으로 먼저 설계를 검증했습니다.
1️⃣7️⃣ Day 10에서 일부러 안 한 것들 (중요)
❌ JWT
❌ DB
❌ GPT API
❌ OAuth
👉 이유
구조를 먼저 안정화시키기 위해
1️⃣8️⃣ Day 11로 이어지는 설계 포인트
지금 구조는 그대로 두고:
- x-user-id → JWT
- dummy reply → GPT 응답
- in-memory → DB
로 갈아끼우기만 하면 됨
👉 이게 바로 확장 가능한 설계
✅ Day 10 최종 정리
오늘 얻은 것
- FastAPI 서버 분리
- CORS 완벽 이해
- Router / Schema 분리
- Header 기반 인증 구조
- 프론트 ↔ 백엔드 통신
한 줄 요약 (진짜)
“FastAPI로 독립 백엔드를 분리하고,
프론트와 안정적인 통신 구조를 완성한 날”
'챗봇 공부 노트' 카테고리의 다른 글
| [12편] 모달 선택값 전역 저장 및 채팅 context 연동 (0) | 2026.01.30 |
|---|---|
| [11편] API 구조 & 데이터 계약(Contract) 고정 (0) | 2026.01.26 |
| [9편] Zustand로 인증 상태 통합 (0) | 2026.01.25 |
| [8편] OAuth vs 자체 회원가입 — 서비스에 맞는 인증 전략 (0) | 2026.01.23 |
| [7편] Google OAuth 인증 체계 구축 (1) | 2026.01.21 |