SW/Python

Python : __init__.py 이해하기: 패키지 관리 필수 팁

얇은생각 2025. 3. 25. 07:30
반응형

여러분, Python 코딩하다 보면 __init__.py라는 파일을 만나곤 하죠? 처음 보면 "이게 뭐지?" 싶은데, 그 목적과 쓰임새를 알면 코드를 정말 깔끔하고 조직적으로 쓸 수 있어요. 오늘은 Python 모듈과 패키지, 그리고 그 속에서 __init__.py가 어떤 마법을 부리는지 얘기해볼게요.

 

Python : __init__.py 이해하기: 패키지 관리 필수 팁

 


 

모듈? 그게 뭔데?

일단, 패키지나 __init__.py를 얘기하기 전에 모듈부터 시작해 볼게요. 모듈은 간단히 말해서 다른 Python 파일에서 가져다가 쓸 수 있는 코드 묶음이에요.

 

모듈은 이런 거예요

Python 모듈은 .py 확장자를 가진 모든 파일이 될 수 있어요. 근데 보통 import해서 사용할 때 이걸 모듈이라고 부르는 거죠.

예를 들어 두 개의 모듈이 있다고 해볼게요:

  • string_util.py
  • math_util.py

 

각각의 모듈은 이렇게 생겼어요:

# string_util.py

def capitalize(text):
    return text.capitalize()

 

# math_util.py

def add(a, b):
    return a + b

 

모듈을 불러와서 써보자

메인 스크립트인 main.py에서 불러와서 사용해볼게요:

# main.py
from string_util import capitalize
from math_util import add

print(capitalize("hello world"))  # 결과: Hello world
print(add(2, 3))  # 결과: 5

 

이렇게 하면 코드가 깔끔하죠? 모든 기능을 한 파일에 몰아넣지 않고 필요할 때 불러와 쓰는 거예요.

 


 

그럼 패키지는 뭔가요?

프로젝트가 커지면 모듈이 점점 많아지겠죠? 여기저기 흩어져 있으면 관리가 힘들어져요. 그래서 패키지를 사용하는 거예요.

 

패키지의 의미

패키지는 모듈들을 한곳에 모아둔 디렉터리라고 생각하면 돼요. 예를 들어, utils라는 패키지를 만들어서 유틸리티 함수들을 정리해볼게요:

project_root/── utils/
    ── string_util.py
    ── math_util.py

 

예전에는 패키지로 인식되려면 __init__.py 파일이 꼭 필요했는데, 요즘 Python은 없어도 알아서 인식해 줘요.

 

패키지에서 모듈 가져오기

이제 유틸리티 모듈을 패키지에서 불러와볼게요:

# main.py
from utils.string_util import capitalize
from utils.math_util import add

print(capitalize("hello world"))
print(add(2, 3))

 

이렇게 하면 코드를 정돈된 구조로 유지할 수 있죠.

 


 

그런데 __init__.py는 왜 필요한가요?

이제 드디어 __init__.py에 대해 얘기해볼 시간이에요.

 

__init__.py가 하는 일

__init__.py는 패키지를 초기화하는 역할을 해요. 필수는 아니지만 여전히 많이 쓰이는 이유가 있죠:

  1. 초기 설정 작업: 패키지가 import될 때 미리 설정해야 하는 작업을 처리할 수 있어요.
  2. 간편한 import: 복잡한 import 구문을 단순화하는 데 도움을 줘요.

 

예제: __init__.py로 패키지 초기화하기

이런 디렉터리 구조를 가정해 볼게요:

project_root/── utils/
    ── __init__.py
    ── string_util.py
    ── math_util.py

 

__init__.py에 이런 코드를 추가해 봐요:

print("utils 패키지가 import되었습니다.")

 

그다음 main.py를 실행해 보면:

from utils.string_util import capitalize
from utils.math_util import add

print(capitalize("hello world"))
print(add(2, 3))

 

출력 결과:

utils 패키지가 import되었습니다.
Hello world
5

 

멋지죠? 이렇게 패키지가 처음 import될 때 초기 설정을 할 수 있어요.

 

import를 더 간단하게

__init__.py를 이용해서 주요 모듈을 한 번에 불러오게 설정할 수 있어요:

# __init__.py
from .string_util import capitalize
from .math_util import add

 

그럼 main.py에서 이렇게 간단하게 쓸 수 있어요:

from utils import capitalize, add

print(capitalize("hello world"))
print(add(2, 3))

 

더 간결하고 깔끔하죠?

 


import 오류? 걱정하지 마세요!

때때로 import 오류가 발생할 수 있어요. 특히 상대 import와 절대 import에서 혼동할 수 있죠.

 

상대 import란?

상대 import는 점(.)을 사용해서 모듈의 위치를 나타내요:

  • 한 점(.): 현재 패키지를 의미
  • 두 점(..): 상위 패키지를 의미

 

예를 들어, __init__.py에서:

from .math_util import add
from .string_util import capitalize

 

이렇게 설정하면 main.py에서 실행할 때는 괜찮지만, __init__.py를 직접 실행하면 오류가 발생해요:

ImportError: attempted relative import with no known parent package

 

왜 이런 오류가 생길까?

main.py에서 실행하면 Python은 프로젝트 구조를 알고 있지만, __init__.py를 직접 실행하면 구조를 몰라서 오류가 나는 거예요.

 

해결 방법

항상 메인 스크립트 (main.py)에서 실행하거나 절대 import를 사용하세요:

from utils.math_util import add
from utils.string_util import capitalize

 

절대 import는 더 안정적이지만, 프로젝트 구조를 변경할 때는 약간 불편할 수 있어요.

 


 

중첩된 패키지 사용하기

이제 패키지 안에 또 패키지를 넣어볼게요. 이런 구조를 생각해 봅시다:

project_root/── utils/
    ── __init__.py
    ── string_util.py
    ── math_util.py
    ── nested_utils/
        ── __init__.py
        ── general.py

 

중첩된 패키지에서 import하기

nested_utils.general.py에서 모듈을 import하려면:

# utils/nested_utils/__init__.py
from ..math_util import add
from ..string_util import capitalize

 

또는 이렇게 쓸 수도 있어요:

# utils/nested_utils/general.py
from ..math_util import add

def calculate_and_print(a, b):
    print("결과:", add(a, b))

 

그다음 main.py에서:

from utils.nested_utils.general import calculate_and_print

calculate_and_print(5, 10)

 

중첩된 구조도 깔끔하게 관리할 수 있죠.

 


 

마무리하면서...

Python 모듈과 패키지를 제대로 이해하면 확장성과 유지보수성이 뛰어난 프로젝트를 만들 수 있어요. 그리고 __init__.py는 이를 더 효율적으로 관리할 수 있게 도와주죠.

반응형