[g6] __init__.py에 코드를 정의하는 방법에 대한 이점
- 이 포스트는 `api/v1/` 디렉토리 이하의 `__init__.py` 파일들이 어떤 용도로 사용되고 있는지, 각 코드의 동작 목적, 이러한 패턴을 사용하는 이유, 그리고 이점에 대해 정리합니다.
1. 목차
1) [개요]
2) [파일별 분석](#파일별-분석)
- [api/v1/auth/__init__.py](#apiv1auth__init__py)
- [api/v1/models/__init__.py](#apiv1models__init__py)
- [api/v1/routers/__init__.py](#apiv1routers__init__py)
3) [이 패턴의 이점](#이-패턴의-이점)
4) [사용 예시](#사용-예시)
2. __init__.py에 정의하는 이점
2.1. Import 경로 단축 (Public API 노출)
- # 만약 별도 파일(oauth2.py)에 정의했다면
- from api.v1.auth.oauth2 import oauth2_scheme
- # __init__.py에 정의하면
- from api.v1.auth import oauth2_scheme
2.2. 싱글톤 인스턴스 보장
- # __init__.py가 처음 import될 때 딱 한 번만 실행됨
- oauth2_scheme = OAuth2PasswordBearer(tokenUrl=TOKEN_URL, ...)
- 모든 모듈이 동일한 인스턴스를 공유
- 여러 번 import해도 새 인스턴스 생성 안 됨
- FastAPI의 의존성 시스템과 잘 맞음
2.3. 패키지의 공개 인터페이스 명시
- api/v1/auth/
├── __init__.py → 외부에 노출할 것: oauth2_scheme, oauth2_optional
└── jwt.py → 내부 구현 세부사항: JWT 클래스
| 구분 | 위치 | 의미 |
| Public API | __init__.py | "이것들을 사용하세요" |
| Internal | 하위 .py 파일 | "내부 구현, 직접 접근 비권장" |
2.4. 순환 import 방지
- # __init__.py에 공용 객체를 정의하면
- # 다른 모듈들이 서로를 import하지 않고
- # __init__.py만 import하면 됨
- # dependencies/member.py
- from api.v1.auth import oauth2_scheme # 깔끔
- # dependencies/current_connect.py
- from api.v1.auth import oauth2_optional # 같은 위치
2.5. 관련 설정의 응집도 향상
- # 인증 관련 설정이 한 곳에 모여있음
- TOKEN_URL = f"/api/{api_settings.API_VERSION}/token" # URL 설정
- oauth2_scheme = OAuth2PasswordBearer(tokenUrl=TOKEN_URL, ...) # 필수 인증
- oauth2_optional = OAuth2PasswordBearer(tokenUrl=TOKEN_URL, ...) # 선택 인증
- 변경이 필요할 때 한 파일만 수정하면 됨
2.6. 정리: 언제 __init__.py에 정의하는가?
| 상황 | __init__.py에 정의 | 별도 파일에 정의 |
| 패키지 전체에서 공유하는 싱글톤 객체 | V | |
| 외부 모듈에서 자주 사용하는 공용 인터페이스 | V | |
| 복잡한 클래스/로직 구현 | V (jwt.py) | |
| 내부적으로만 사용하는 헬퍼 함수 | V |
3. 파일별 분석 - api/v1/auth/__init__.py
3.1. api/v1/auth/__init__.py
from fastapi.security import OAuth2PasswordBearer
from api.settings import api_settings
TOKEN_URL = f"/api/{api_settings.API_VERSION}/token"
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl=TOKEN_URL,
description="""로그인을 통해 Access Token 발급 후, API 요청 시 Authorization 헤더를 추가합니다.
> Authorization: Bearer {Access Token}"""
)
oauth2_optional = OAuth2PasswordBearer(
tokenUrl=TOKEN_URL,
auto_error=False,
scheme_name="OAuth2PasswordBearer(Optional)",
description="""Access Token발급 여부와 관계없이 API 요청이 가능합니다.
> Access Token이 발급되었을 경우: Authorization: Bearer {Access Token}
> Access Token이 발급되지 않았을 경우: 비회원으로 인식"""
)
3.2. 용도 및 동작
- 목적 : OAuth2 인증 스키마 객체를 정의하고 애플리케이션 전체에서 공유
- oauth2_scheme : 필수 인증이 요구되는 API 엔드포인트에서 사용. 토큰이 없으면 401 에러 반환
- oauth2_optional : 선택적 인증을 지원하는 API 엔드포인트에서 사용. 토큰이 없어도 비회원으로 처리
3.3. 왜 이 방식을 사용하는가?
1. 싱글톤 패턴 구현: `OAuth2PasswordBearer` 인스턴스를 패키지 레벨에서 한 번만 생성하여 메모리 효율성 확보
2. 일관된 설정 보장: 모든 인증 관련 모듈에서 동일한 `tokenUrl`과 설정을 사용
3. Import 편의성: `from api.v1.auth import oauth2_scheme` 형태로 간결하게 import 가능
3.4. 실제 사용처
- `api/v1/dependencies/member.py` - 회원 인증 의존성
- `api/v1/dependencies/current_connect.py` - 현재 접속자 처리
- `lib/slowapi/create_post_limit/limiter.py` - API 요청 제한
4. 파일별 분석 - api/v1/models/__init__.py
4.1. api/v1/models/__init__.py
from datetime import datetime
from enum import Enum
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
from core.database import DBConnect
from core.models import Base, DB_TABLE_PREFIX
class MemberRefreshToken(Base):
"""회원 Refresh Token 테이블 모델"""
__tablename__ = DB_TABLE_PREFIX + "member_refresh_token"
id = Column(Integer, primary_key=True, index=True)
mb_id = Column(String(20), index=True, nullable=False, default="")
refresh_token = Column(String(255), unique=True)
expires_at = Column(DateTime, default=datetime.now)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now)
# 테이블 자동 생성
MemberRefreshToken.__table__.create(bind=DBConnect().engine, checkfirst=True)
class Tags(Enum):
"""API 태그를 정의합니다."""
AUTH = "인증"
BOARD = "게시판"
CAPTCHA = "캡차"
# ... (생략)
4.2. 용도 및 동작
- MemberRefreshToken JWT Refresh Token을 저장하는 데이터베이스 모델
- Tags (Enum) API 문서화(Swagger/OpenAPI)에서 사용되는 태그 정의
- 테이블 자동 생성 패키지 import 시점에 테이블이 존재하지 않으면 자동 생성
4.3. 왜 이 방식을 사용하는가?
1. 지연 초기화(Lazy Initialization) 방지
- 애플리케이션 시작 시점에 필수 DB 테이블을 즉시 생성
- `checkfirst=True`로 중복 생성 방지
2. 중앙 집중화된 모델 관리
- API 관련 모델들을 한 곳에서 정의하여 관리 용이
- 다른 모듈에서 `from api.v1.models import MemberRefreshToken` 형태로 import
3. 열거형(Enum)을 통한 타입 안정성
- API 태그를 문자열 대신 Enum으로 정의하여 오타 방지
- IDE 자동완성 지원
4.4. 실제 사용처
- `api/v1/routers/auth.py` - 토큰 발급/갱신 로직
- `api/v1/routers/member.py` - 회원 관련 API
- `api/v1/dependencies/auth.py` - 인증 의존성
- `api/v1/routers/__init__.py` - 라우터 태그 설정
5. 파일별 분석 - api/v1/routers/__init__.py
5.1. api/v1/routers/__init__.py
from fastapi import APIRouter, Depends
from lib.dependency.dependencies import check_use_api
from api.v1.dependencies.current_connect import set_current_connect
from api.v1.models import Tags
from api.v1.routers import (
auth, autosave, board, board_good, board_new, captcha, config, content,
current_connect, faq, member, memo, menu, newwin, point, poll, popular,
qa, scrap, search, visit, group
)
# API 버전 1 라우터를 정의합니다.
router = APIRouter(prefix="/api/v1",
dependencies=[Depends(check_use_api),
Depends(set_current_connect)])
router.include_router(auth.router, tags=[Tags.AUTH])
router.include_router(board.router, prefix="/boards", tags=[Tags.BOARD])
router.include_router(captcha.router, tags=[Tags.CAPTCHA])
# ... (나머지 라우터들)
5.2. 용도 및 동작
- 중앙 라우터 : 모든 v1 API 라우터를 하나의 진입점으로 통합
- 공통 의존성 : `check_use_api`, `set_current_connect`를 모든 API 엔드포인트에 적용
- URL 프리픽스 : `/api/v1` 프리픽스를 자동으로 모든 하위 라우터에 적용
5.3. 왜 이 방식을 사용하는가?
1. 단일 진입점 패턴 (Single Entry Point)
# main.py에서 단 한 줄로 모든 API v1 라우터 등록
from api.v1.routers import router as api_router
app.include_router(api_router)
2. 공통 의존성 일괄 적용
- 모든 v1 API에 공통으로 적용되어야 하는 로직을 한 곳에서 관리
- `check_use_api`: API 사용 가능 여부 확인
- `set_current_connect`: 현재 접속자 정보 설정
3. API 버전 관리 용이
- v2 API 추가 시 `api/v2/routers/__init__.py` 생성으로 쉽게 확장
- 버전별 라우터 분리로 하위 호환성 유지 가능
4. 일관된 태그 적용
- `Tags` Enum을 사용하여 Swagger UI에서 API 그룹핑
- 문서화 일관성 보장
5.4. 실제 사용처
- `main.py` - FastAPI 애플리케이션에 라우터 등록
'Study > fastapi' 카테고리의 다른 글
| [udemy] Learning Pydantic: Advanced Data Validation In Python - 학습후기 (0) | 2025.03.25 |
|---|---|
| fastapi와 sqlalchemy admin인 sqladmin을 이용해 celery beat을 admin으로 관리하기 (0) | 2025.03.22 |
| sqlalchey admin을 사용하여 관리자 페이지 만들기 (0) | 2025.03.18 |
| G6의 기본적인 추상클래스 사용 방법/패턴 정리 (0) | 2025.03.10 |
| fastapi - G6 간단하게 훓어보기 - 2) API 호출 (0) | 2025.02.21 |
댓글