
이번 시간에는 파이썬 코드가 길어지고 복잡해질 때, 이를 효율적으로 관리하고 재사용하기 위해 필수적으로 알아야 하는
'모듈(Module)'과 '패키지(Package)'에 대해 알아봅시다.
파이썬 프로그래밍을 시작하고 코드가 100줄, 1000줄 길어지다 보면 하나의 파일(*.py)에 모든 로직을 담는 것이 불가능해집니다.
코드의 가독성이 떨어지고, 유지보수가 힘들어지며, 여러 명의 개발자가 함께 협업하기도 어렵기 때문입니다.
이러한 문제를 해결하기 위해 파이썬은 코드를 기능별로 쪼개어 관리할 수 있는 모듈(Module)과 패키지(Package)라는 훌륭한 시스템을 제공합니다.
오늘은 이 두 가지의 개념과 활용법, 그리고 파이썬 스크립트에서 항상 보게 되는 __name__의 비밀까지 완벽하게 파헤쳐 보겠습니다.
1. 개요: 파이썬 프로그램의 구성 요소
파이썬 프로그램은 크게 세 가지 요소로 구성됩니다.
1) 모듈 (Module)
- 정의: 파이썬 코드가 작성된 하나의 파일(.py)입니다.
- 실행: 터미널에서 python 파일명.py 형태로 실행합니다.
- 통상적으로 로컬 PC의 디스크에 저장되어 구동되지만, 클라우드 환경(예: AWS S3)에 업로드된 모듈을 로드하여 대규모 데이터 처리(ETL) 등에 활용하기도 합니다.
2) 패키지 (Package)
- 정의: 여러 개의 모듈(.py)이나 하위 패키지들을 하나로 묶어놓은 폴더(디렉토리)입니다.
- 목적: 관련된 기능들을 체계적으로 분류하여 모아두는 공간입니다.
💡 추가 지식: __init__.py 파일의 역할 과거 파이썬(3.3 이전)에서는 특정 폴더가 패키지로 인식되려면 폴더 안에 반드시 __init__.py 파일이 있어야만 했습니다. 지금은 없어도 되지만(네임스페이스 패키지), 하위 버전 호환성 및 패키지 초기화 코드를 작성하기 위해 여전히 관습적으로 생성하는 것이 좋습니다. 패키지 자체가 임포트 될 때 __init__.py의 내용이 가장 먼저 실행됩니다.
3) 리소스 (Resource)
- 모듈과 패키지가 아닌 모든 외부 자원을 의미합니다. (예: 이미지, 오디오 파일, 데이터베이스, 환경변수 파일 등)
- 모듈은 파일 I/O 등을 통해 리소스에 접근하며, 외부 요인의 영향을 받으므로 반드시 예외 처리를 수반해야 합니다.
2. 실전 실습: 패키지와 모듈 구조 만들기
내가 만든 유용한 기능을 다른 사람이 가져다 쓰게 하거나, 반대로 남이 만든 모듈을 내가 쓰기 위해서는 모듈화가 필수입니다.
아래 파이썬 코드를 실행하여 가상의 패키지 구조를 직접 만들어 보겠습니다.
(직접 노가다로 만드는 것보다 개발자답게 코드로 디렉토리와 파일을 만들어보아요 ㅎㅎ)
import os
# 디렉토리 생성 (a 패키지 안에 b 패키지 생성)
os.makedirs('a/b', exist_ok=True)
# a 패키지를 대변하는 초기화 모듈
with open('a/__init__.py', 'w', encoding='utf-8') as f:
f.write("PI = 3.14\n")
# b 패키지를 대변하는 초기화 모듈
with open('a/b/__init__.py', 'w', encoding='utf-8') as f:
f.write("PI2 = 3.144\n")
# 일반 모듈 작성 (변수, 함수, 클래스 포함)
mod_content = """# 변수
PI3 = 3.1456789
# 함수
def add(x, y):
return x + y
# 클래스
class A:
pass
"""
with open('a/b/mod.py', 'w', encoding='utf-8') as f:
f.write(mod_content)
[생성된 구조]
- / (현재 디렉토리 - 엔트리 포인트)
- a/ (패키지)
- __init__.py (변수 PI 포함)
- b/ (하위 패키지)
- __init__.py (변수 PI2 포함)
- mod.py (변수 PI3, 함수 add, 클래스 A 포함)
- a/ (패키지)
3. 모듈 가져오기 (Import)
외부 모듈을 가져올 때는 현재 코드가 실행되는 진입점(엔트리 포인트)을 기준(Root)으로 경로를 탐색합니다.
가져오는 방식은 크게 두 가지로 나뉩니다.
1) from ~ import ~ 방식
가장 자주 쓰이는 방식입니다. 특정 패키지나 모듈 안에서 내가 필요한 기능만 쏙 빼서 가져옵니다.
출처를 매번 적을 필요 없이 마치 내 코드에 원래 있던 것처럼 바로 사용할 수 있어 코드가 깔끔해집니다.
# a/b/mod.py 안에서 특정 변수, 함수, 클래스만 가져오기
from a.b.mod import PI3, add, A
print(PI3) # 3.1456789
print(add(1, 2)) # 3
obj = A()
# 패키지 자체를 지칭할 때는 그 안의 __init__.py를 의미합니다.
from a.b import PI2
print(PI2) # 3.144
# 패키지 안의 모든 것(*)을 가져올 수도 있습니다. (메모리 낭비가 있을 수 있어 권장하지는 않음)
from a import *
print(PI) # 3.14
2) import ~ 방식
모듈이나 패키지를 통째로 가져옵니다. 사용할 때마다 도트(.) 연산자를 통해 출처를 명확히 밝혀야 합니다.
이름이 겹치는 것을 방지할 때 유용합니다.
# 모듈 통째로 가져오기
import a.b.mod
# 사용 시 출처를 모두 적어주어야 합니다.
print(a.b.mod.PI3)
# 너무 길다면 'as' 키워드로 별칭(Alias)을 부여할 수 있습니다.
import a.b.mod as m
print(m.PI3)
import a.b as my
print(my.PI2)
(관습적 별칭 예시: import numpy as np, import pandas as pd)
4. 파이썬의 마법 변수: __name__
파이썬 스크립트를 열어보면 파일 맨 밑에 if __name__ == '__main__': 이라는 코드를 자주 보게 됩니다.
__name__은 파이썬이 내부적으로 사용하는 특수 전역 변수로, 현재 모듈이 어떻게 실행되고 있는지에 따라 값이 바뀝니다.
Case 1: 파일을 직접 실행할 때 (엔트리 포인트)
해당 .py 파일을 직접 실행(Run)하면, __name__ 변수 안에는 "__main__" 이라는 문자열이 담깁니다. 즉, "내가 지금 프로그램의 메인(주인공)이다!"라고 선언하는 것입니다.
Case 2: 다른 파일에서 import로 불려 올 때
내가 만든 파일을 다른 사람이 import 해서 사용할 경우, __name__ 변수에는 해당 파일의 원래 이름(모듈명)이 담깁니다.
🚀 왜 중요할까요? (테스트 코드 보호)
이 성질을 이용하면, 모듈을 만든 원작자가 파일 구조를 테스트하기 위해 작성한 코드를 타인이 import 했을 때 실행되지 않도록 막을 수 있습니다.
# support.py 라는 파일을 작성해 봅니다.
mod_content = """print(f'현재 __name__의 값 => {__name__}')
# 아래 코드는 파일을 '직접 실행'할 때만 작동합니다.
if __name__ == '__main__':
print('해당 모듈 원개발자의 테스트 코드입니다.')
print('다른 파일에서 import 하면 이 부분은 무시됩니다!')
"""
with open('support.py', 'w', encoding='utf-8') as f:
f.write(mod_content)
[결과 비교]
- 터미널에서 python support.py로 직접 실행할 때:
- 출력: 현재 __name__의 값 => __main__
- 출력: 해당 모듈 원개발자의 테스트 코드입니다. (if문 내부 실행됨)
- 다른 파일에서 import support로 가져올 때:
- 출력: 현재 __name__의 값 => support
- if문 안의 테스트 코드는 실행되지 않고 깔끔하게 무시됩니다!
💡 추가 지식: 주피터 노트북과 import의 특징 모듈을 한 번 import 하면, 파이썬은 속도를 위해 이를 메모리에 캐싱(저장)합니다. 따라서 외부 .py 파일을 수정하더라도 주피터 노트북 셀을 다시 실행한다고 해서 수정된 내용이 즉각 반영되지 않습니다. 이때는 커널을 재시작하거나, importlib.reload(모듈명)을 사용하여 모듈을 강제로 다시 읽어와야 합니다.
오늘의 한 마디
최근 친구를 만났는데 그 친구와 나눈 이야기가 생각나서 작성합니다.
누군가를 만날 때는 그 사람이 그 사람인 것을 인정하고 만나야 한다고 생각합니다. 그 사람을 나의 입맛에 맞게 변화시키려는 노력을 최대한 하지 않고 그냥 그 사람이 그런 사람인 것을 인정하고 만나야한다고 생각합니다.
'코딩 개발 > Python' 카테고리의 다른 글
| Python - 데이터수집처리 (feat. 네이버 finance 환율) 1 (0) | 2026.07.24 |
|---|---|
| Python - 데이터 수집, 처리, 적재 (ETL) (feat. NAVER Developer 어플리케이션 사용 법, Google Colab에서 API 사용) (1) | 2026.07.23 |
| Python - 함수 지향적 프로그래밍(feat. 숫자 맞추기 게임) (1) | 2026.07.21 |
| Python - 함수의 종류와 활용법 (0) | 2026.07.20 |
| Python - 함수 (Function) (0) | 2026.07.17 |