Study/fastapi

[g6] __init__.py에 코드를 정의하는 방법에 대한 이점

bluebamus 2025. 12. 18.

- 이 포스트는 `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 애플리케이션에 라우터 등록

 

 

 

댓글