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 |