FastAPI - MiniProject - 2

김기훈·2025년 10월 25일

부트캠프 프로젝트

목록 보기
4/39

구조

fastapi_mini_project/
├── app/
│   ├── api/              # FastAPI 라우터 (엔드포인트)
│   ├── core/             # 공통 설정, 보안, 의존성 (Config, JWT, Password 등)
│   ├── models/           # Tortoise ORM 모델 (DB 테이블 정의)
│   ├── schemas/          # Pydantic 스키마 (요청/응답 데이터 검증)
│   ├── services/         # 비즈니스 로직 (회원가입, 로그인, CRUD 처리)
│   ├── repositories/     # DB 접근 로직 (CRUD 메서드)
│   ├── db/               # DB 연결 및 마이그레이션 설정
│   ├── scraping/         # 스크래핑 기능 (명언/질문 수집용)
│   ├── main.py           # FastAPI 진입점 (앱 실행 및 라우터 등록)
│   └── __init__.py
├── tests/                # 테스트 코드
├── .env / .env.dev       # 환경변수
├── pyproject.toml        # Poetry 설정
└── README.md             # 프로젝트 설명
폴더주요 역할예시 파일
api/실제로 클라이언트 요청이 들어오는 엔드포인트 정의auth.py, diary.py
core/앱의 핵심 설정 (DB URL, JWT, 암호화 등)config.py, security.py, dependencies.py
models/데이터베이스 테이블 정의 (Tortoise ORM 모델)user.py, diary.py
schemas/요청(Request) / 응답(Response) 검증용 모델 (Pydantic)user.py
services/핵심 비즈니스 로직 (회원가입, 로그인 등)auth_service.py
repositories/DB 조작 담당 (CRUD 기능 구현)user_repo.py
db/DB 연결 및 마이그레이션 관련base.py, session.py
scraping/외부 웹 데이터 수집 기능quote_scraper.py
tests/pytest 등으로 테스트 실행test_auth.py
main.pyFastAPI 앱 실행, 라우터 등록, DB 초기화

상세 구조

fastapi_mini_project/
├── app/
│   ├── api/
│   │   └── v1/
│   │       ├── auth.py
│   │       ├── diary.py
│   │       ├── quote.py
│   │       ├── question.py
│   │       └── __init__.py
│   │
│   ├── core/
│   │   ├── config.py
│   │   ├── security.py
│   │   ├── dependencies.py   
│   │   └── __init__.py
│   │
│   ├── models/
│   │   ├── user.py
│   │   ├── diary.py
│   │   ├── quote.py
│   │   ├── question.py
│   │   └── __init__.py
│   │
│   ├── schemas/
│   │   ├── user.py
│   │   ├── diary.py
│   │   ├── quote.py
│   │   ├── question.py
│   │   └── __init__.py
│   │
│   ├── services/
│   │   ├── auth_service.py
│   │   ├── diary_service.py
│   │   ├── quote_service.py
│   │   ├── question_service.py
│   │   └── __init__.py
│   │
│   ├── repositories/
│   │   ├── user_repo.py
│   │   ├── diary_repo.py
│   │   ├── quote_repo.py
│   │   ├── question_repo.py
│   │   └── __init__.py
│   │
│   ├── db/
│   │   ├── base.py
│   │   ├── session.py
│   │   └── migrations/
│   │
│   ├── scraping/
│   │   ├── quote_scraper.py
│   │   ├── question_scraper.py
│   │   └── __init__.py
│   │
│   ├── main.py
│   └── __init__.py
│
├── tests/
│   ├── test_auth.py
│   ├── test_diary.py
│   ├── test_quote.py
│   ├── test_question.py
│
├── .env
├── .env.dev
├── pyproject.toml
├── poetry.lock
└── README.md

프로젝트하면서 공부가 된 부분

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")

  • "요청의 HTTP 헤더에서 토큰을 자동으로 추출해주는 의존성"
  • FastAPI는 이걸 통해 Authorization: Bearer <토큰> 형식의 헤더를 자동으로 감지하고,
    • 해당 토큰을 함수에 전달
  • 흐름
    • 사용자가 로그인 요청을 보냄 -> /auth/login에서 아이디와 비밀번호를 검증
      • JWT 토큰(Access Token)을 발급받음
    • 이후 API 요청 시, 사용자는 헤더에 다음처럼 토큰을 포함해서 요청을 보냄
      • Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6...
    • oauth2_scheme이 작동해서, FastAPI가 이 헤더를 자동으로 인식하고 토큰만 추출
@router.post("/logout")
async def logout(token: str = Depends(oauth2_scheme)):
    return await AuthService.logout(token)

# 요청 헤더의 Bearer ... 토큰이 token 변수로 들어오게 됨

### 요약
GET /diary
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6...

이렇게 클라이언트가 요청을 보내면 Depends(oauth2_scheme) 덕분에
"eyJhbGciOi..." 부분만 token 인자로 자동으로 들어감
  • tokenUrl="/auth/login"의 의미
    • “토큰을 발급받는 엔드포인트 경로” 를 명시적으로 알려주는 설정값
    • 즉, Swagger 문서(/docs)에서 "Authorize" 버튼을 눌렀을 때
      • FastAPI가 어디로 로그인 요청을 보내야 할지 이 값을 보고 알아냄

JWT

# auth_service.py
@staticmethod
    async def login(username: str, password: str):
        user = await UserRepository.get_by_username(username)
        if not user or not verify_password(password, user.password_hash):
            raise HTTPException(status_code=401, detail="Invalid credentials")

        access_token = create_access_token(data={"sub": user.username})
        return {"access_token": access_token, "token_type": "bearer"}

# dependencies.py
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
username = payload.get("sub")


"sub"은 “subject”의 약자 = 토큰이 발급된 사용자
즉 로그인할 때 넣어준 {"sub": user.username} 값이 들어있음.
  • jwt.decode()를 통해 SECRET_KEY와 ALGORITHM으로 디코딩하면
    • 원래 들어있던 데이터(payload)를 볼 수 있음

.env 연결 확인

  • 연결정보
    • psql -h 000.000.00.000 -U todolist_admin -d todolist
DB_USER=todolist_admin
DB_PASSWORD= (비밀번호를 여기 입력)
DB_HOST=000.000.00.000
DB_PORT=5432
DB_NAME=todolist
DB_SCHEME=asyncpg

SECRET_KEY="mysecret"
ALGORITHM="HS256"
  • 서버 켜져있나 확인하기
    • ps aux | grep postgres

profile
안녕하세요.

0개의 댓글