파이썬 __init__.py, 비어있는 파일 그 이상
많은 파이썬 개발자가 __init__.py 파일을 "이 디렉토리를 패키지로 인식하게 만드는" 용도의 빈 파일로만 사용합니다. 하지만 이 파일은 단순한 마커(marker)가 아닙니다.
__init__.py의 핵심은 "패키지가 import 될 때 가장 먼저 실행되는 스크립트" 라는 점입니다.
이 특성을 활용하면 지저분한 코드를 정리하고, 패키지의 API를 명확하게 설계하며, 초기화 로직을 관리하는 등 매우 강력한 작업을 수행할 수 있습니다.
1. 지저분한 Import 경로를 깔끔하게 (API 단순화)
__init__.py의 가장 유용하고 일반적인 활용법입니다. 하위 모듈의 클래스나 함수를 패키지 레벨로 '끌어올려' 사용자가 더 쉽게 import 할 수 있도록 만듭니다.
문제점:
Django의 views.py가 1,000줄이 넘어 기능별로 파일을 분리했다고 가정해 봅시다.
payments/
├── views/
│ ├── __init__.py
│ ├── user_views.py # UserProfileView, ...
│ ├── payment_views.py # PaymentView, ...
│ └── webhooks.py # PaypalWebhookView, ...
└── urls.py
urls.py에서 이 뷰들을 사용하려면 매우 번거롭습니다.
# payments/urls.py (나쁜 예시)
from .views import user_views, payment_views, webhooks
urlpatterns = [
path('profile/', user_views.UserProfileView.as_view()),
path('toss/', payment_views.PaymentView.as_view()),
path('webhook/', webhooks.PaypalWebhookView.as_view()),
]
urls.py가 views 디렉토리의 내부 파일 구조를 모두 알아야만 합니다.
해결책 (init.py 활용):
views/init.py 파일에서 필요한 뷰들을 미리 import 해줍니다.
# payments/views/__init__.py
# 각 파일에서 필요한 클래스/함수를 패키지 레벨로 끌어올립니다.
from .user_views import UserProfileView
from .payment_views import PaymentView
from .webhooks import PaypalWebhookView
이제 views 패키지를 사용하는 urls.py는 views 패키지가 UserProfileView를 직접 가지고 있는 것처럼 보입니다.
# payments/urls.py (개선된 예시)
# 'views'는 이제 파일이 아닌 패키지(디렉토리)입니다.
from . import views
urlpatterns = [
# __init__.py 덕분에 내부 구조를 몰라도 됩니다.
path('profile/', views.UserProfileView.as_view()),
path('toss/', views.PaymentView.as_view()),
path('webhook/', views.PaypalWebhookView.as_view()),
]
views.py가 파일이든, 여러 파일을 가진 디렉토리이든, urls.py 코드는 동일하게 유지됩니다. 이것이 좋은 추상화입니다.
2. 패키지의 '공식 API' 명시 (__all__)
from my_package import * (와일드카드 임포트)는 편리하지만, 의도치 않은 변수나 모듈(예: import os)까지 현재 네임스페이스로 가져와 코드를 오염시킬 수 있습니다.
__all__은 import *를 했을 때 "오직 이 목록에 있는 것들만 가져가라"고 허용 목록(whitelist)을 정의합니다.
# payments/views/__init__.py
# 내부 사용을 위한 모듈
import logging
from .internal_utils import _helper_function
# 외부에 공개할 뷰
from .user_views import UserProfileView
from .payment_views import PaymentView
# __all__ 정의:
# "import * 를 사용하면 UserProfileView와 PaymentView만 가져갈 수 있습니다."
__all__ = [
'UserProfileView',
'PaymentView'
]
이제 from payments.views import *를 실행해도, logging 모듈이나 내부용 함수인 _helper_function은 임포트되지 않아 네임스페이스를 안전하게 보호할 수 있습니다.
3. 패키지 초기화 및 메타데이터 정의
패키지가 처음 로드될 때 단 한 번 실행되어야 하는 코드를 넣기에 완벽한 장소입니다.
- 버전 정보 제공:
# my_library/__init__.py
__version__ = "1.2.0"
__author__ = "whitedec"
이제 사용자는 import my_library; print(my_library.__version__)로 버전을 쉽게 확인할 수 있습니다.
- 전역 설정(Setup) 실행:
Django가 import django 될 때 내부적으로 django.setup()을 호출하거나, Celery가 앱을 초기화하는 코드가 init.py에 포함되는 것이 대표적인 예시입니다.
# my_project/__init__.py (Celery 예시)
# Django가 시작될 때 Celery 앱이 함께 로드되도록 보장
from .celery import app as celery_app
__all__ = ('celery_app',)
※ 주의: 내부 모듈 간의 순환 참조
__init__.py를 '안내 데스크'처럼 활용할 때 주의할 점이 있습니다.
views/webhooks.py가 views/payment_commons.py의 CancelView를 사용해야 한다고 가정해 봅시다.
- 나쁜 방법 (순환 참조 위험):
webhooks.py에서 from . import CancelView (즉, init.py)를 통해 가져오려고 시도하는 것.
- 좋은 방법 (직접 임포트):
webhooks.py에서 from .payment_commons import CancelView처럼 필요한 모듈에서 직접 가져오는 것.
규칙:
- 외부 사용자 (예:
urls.py): '안내 데스크'(__init__.py)를 통해 API를 사용합니다. - 내부 모듈 (예:
webhooks.py): '안내 데스크'를 거치지 않고, 동료 모듈(payment_commons.py)에서 직접 기능을 가져옵니다.
결론
__init__.py는 단순한 빈 파일이 아닌, 패키지의 '안내 데스크', '공식 API 문서', '초기 설정 스크립트' 역할을 모두 수행할 수 있는 강력한 도구입니다. 이 파일을 잘 활용하면 프로젝트의 구조를 훨씬 더 깔끔하고 유지보수하기 쉽게 만들 수 있습니다.
댓글이 없습니다.