React + FastAPI로 JWT 인증 기반 이벤트 관리 시스템 만들기
1. 개요
React와 FastAPI를 연동하여 JWT 인증 기반 로그인 시스템을 구축하고, 인증된 사용자만 이벤트를 등록할 수 있게 했습니다. 추가로 이미지를 업로드하고 다운로드하는 기능도 구현했습니다.
📁 구조
├── frontend (React + Vite)
│ ├── App.jsx
│ ├── user/Login.jsx
│ └── event/Regist.jsx, List.jsx, Detail.jsx
└── backend (FastAPI)
├── main.py
├── routes/users.py, events.py
├── models/users.py, events.py
└── uploads/
2. FastAPI - JWT 기반 로그인 구현
사용자 로그인 시 JWT 토큰을 발급하고, 이후 API 요청에는 Authorization: Bearer <token> 헤더를 통해 인증을 처리합니다.
@user_router.post("/signin")
async def sign_in(data: OAuth2PasswordRequestForm = Depends(), session = Depends(get_session)):
user = session.exec(select(User).where(User.email == data.username)).first()
if not user or not hash_password.verify_password(data.password, user.password):
raise HTTPException(status_code=401, detail="로그인 실패")
return {
"access_token": create_jwt_token(user.email, user.id),
"username": user.username
}
3. React - 로그인 요청 및 토큰 저장
로그인 성공 시 JWT 토큰을 sessionStorage에 저장합니다.
axios.post("http://localhost:8000/users/signin", { username, password })
.then((res) => {
sessionStorage.setItem("token", res.data.access_token);
navigate("/list");
});
4. 이벤트 등록 - 이미지 포함 FormData 전송
React에서 FormData를 구성해 텍스트와 파일을 함께 전송합니다.
const formData = new FormData();
formData.append("title", form.title);
formData.append("description", form.description);
formData.append("tags", form.tags);
formData.append("location", form.location);
formData.append("image", imageFile);
axios.post("http://localhost:8000/events/", formData, {
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${token}`
});
5. FastAPI - 이미지 저장 및 DB 기록
업로드된 이미지는 지정된 디렉터리에 저장하고, DB에는 파일명만 저장합니다.
file_path = FILE_DIR / image.filename
with open(file_path, "wb") as f:
f.write(await image.read())
event = Event(..., image=image.filename)
session.add(event)
6. 이미지 다운로드 구현
이벤트 ID를 기반으로 파일명을 조회하고, 파일을 다운로드 응답으로 반환합니다.
@event_router.get("/download/{event_id}")
async def download_image(event_id: int):
event = session.get(Event, event_id)
file_path = FILE_DIR / event.image
if not file_path.exists():
raise HTTPException(404, "파일 없음")
return FileResponse(path=file_path, filename=event.image)
7. 공통 레이아웃 및 로그인 여부 조건 처리
Layout 컴포넌트를 통해 로그인 여부에 따라 상단 메뉴와 라우팅을 제어합니다.
useEffect(() => {
const token = sessionStorage.getItem("token");
setIsLogin(!!token);
navigate(token ? "/list" : "/login");
}, []);
return (
<header>
{isLogin ? <a onClick={handleLogout}>로그아웃</a> : <Link to="/login">로그인</Link>}
</header>
);
전체 흐름과 핵심 포인트
전체 구현 흐름
- 1단계: FastAPI에서 사용자 인증 로직(JWT 기반) 구현
- 2단계: React에서 로그인 폼 구현 및 JWT 토큰 sessionStorage 저장
- 3단계: 이벤트 등록 기능 구현 (FormData를 통해 이미지 포함 전송)
- 4단계: FastAPI에서 이미지 파일 저장 + DB에 파일명 기록
- 5단계: React 목록 컴포넌트에서 이미지 조회 및 다운로드 처리
- 6단계: 공통 레이아웃 구성 및 로그인 여부에 따른 UI 렌더링 처리
핵심 구현 포인트
JWT 토큰을 발급하여 클라이언트 인증 상태를 유지FormData를 통해 이미지 파일과 텍스트 데이터를 함께 전송UploadFile로 받은 파일을FastAPI에서 저장하고 파일명을 DB에 기록FileResponse를 활용하여 이미지 다운로드 처리sessionStorage에 저장된 토큰을 기반으로 로그인 상태 유지React Router의 중첩 라우팅 +Outlet으로 공통 레이아웃 적용
반드시 기억해야 할 부분
- 로그인 시 저장하는 키는
token으로 통일하고, 헤더에Authorization: Bearer로 전달 - 이미지는 파일명만 DB에 저장하고, 실제 파일은 서버 경로에 따로 저장
- 다운로드 시
파일 경로 = 업로드 경로 + DB의 파일명이 정확히 일치해야 함 - 라우팅 구조는
<Route path="/" element=<Layout />>기반으로 구성 - 공통 컴포넌트에서 로그인 여부 체크 및
navigate()로 페이지 제어
'React' 카테고리의 다른 글
| React - Router (0) | 2025.05.20 |
|---|---|
| React - props, 이벤트 핸들러 (0) | 2025.05.20 |
| React - JSX - 기초 문법 (0) | 2025.05.19 |
| React - Vite를 사용한 React 앱 개발 (0) | 2025.05.19 |