1 개요
이 글은 파이썬의 모듈(Module)과 패키지(Package) 시스템을 체계적으로 다룬다. 다음과 같은 흐름으로 구성되어 있다:
- 모듈과 패키지: 코드를 파일과 디렉토리로 조직화하는 방법
- 라이브러리 생태계: 표준 라이브러리, pip, 가상환경
2 모듈과 패키지
2.1 모듈이란?
- 모듈은 Python 정의와 문장을 담고 있는
.py파일이다. - 모듈은 함수, 변수, 클래스 등 Python으로 작성할 수 있는 모든 것을 포함할 수 있다.
- 참고:
- 스크립트(Script)는 직접 실행하기 위해 작성한 .py 파일이고
- 모듈(Module)은 다른 파일에서 import해서 사용하기 위해 작성한 .py 파일이다.
- 스크립트(Script)는 직접 실행하기 위해 작성한 .py 파일이고
모듈이 제공하는 핵심 가치는 재사용성과 네임스페이스 분리이다.
2.1.1 네임스페이스(Namespace)란?
- 네임스페이스(Namespace)는 이름(변수·함수·클래스명)과 객체를 연결하는 매핑 공간이다.
- 쉽게 말해, “어떤 이름이 어느 범위에 속하는가”를 구분하는 컨테이너다.
- 모듈 없이 코드가 길어지면 이름 충돌이 발생한다:
모듈명.접두어가 곧 네임스페이스다. 같은 이름이라도 소속 공간이 다르면 독립적으로 존재한다.
# 파일 A에서
def add(a, b): return a + b
# 파일 B에서 (같은 이름)
def add(a, b): return str(a) + str(b)
# 누구의 add()인지 알 수 없음모듈이 네임스페이스를 분리해 주면:
import calc
import string_utils
calc.add(1, 2) # calc 네임스페이스의 add
string_utils.add(1, 2) # string_utils 네임스페이스의 addPython의 네임스페이스 종류 (LEGB 규칙)
| 범위 | 설명 | 예시 |
|---|---|---|
| Local | 함수 내부 | 함수 안의 변수 |
| Enclosing | 중첩 함수의 바깥 함수 | 클로저 |
| Global | 모듈 전체 | 모듈 최상단 변수 |
| Built-in | Python 내장 | len, print, range |
이름을 찾을 때 Python은 L → E → G → B 순서로 탐색하고, 먼저 찾은 것을 사용한다.
2.2 모듈 가져오기: import의 세 가지 방식
2.2.1 import 모듈명
어떤 함수가 어느 모듈에서 왔는지 코드를 읽는 사람이 즉시 알 수 있다. 대규모 프로젝트에서 가장 안전한 방식이다.
2.2.2 from 모듈명 import 객체명
from calc import add, sub, divide #calc.py 안에 있는 add(),sub() 함수를 호출
from category import divide # category.py 안에 있는 divide() 함수를 호출
# 객체명만으로 사용 가능. 즉, 함수명()만으로 호출 가능
print(add(3, 4)) # 모듈명 없이 바로 사용
print(divide(10, 2)) # 마지막으로 import한 divide()가 호출됨 (calc.py의 divide()는 무시됨)- 코드가 간결해지지만, 여러 모듈에서 같은 이름의 함수를 가져오면 이전 것을 덮어써서 버그가 생길 수 있다.
- 육안으로 명시적인 관찰이 어려운 bug는 debugging을 상당히 어렵게 만들기 때문에 가급적 다른 객체명을 사용할 것이 권장된다.
2.2.3 from 모듈명 import * (권장하지 않음)
어떤 이름들이 어디서 들어왔는지 추적하기 어렵고, 예상치 못한 이름 충돌이 발생할 수 있다.
2.3 __name__ == "__main__" 패턴
모듈을 직접 실행할 때와 import할 때의 동작을 분리하는 패턴이다:
# calc.py
def add(a, b):
return a + b
if __name__ == "__main__":
# 이 블록은 이 파일을 직접 실행할 때만 동작한다
print(add(3, 4)) # 테스트 코드__name__은 Python이 모든 모듈에 자동으로 부여하는 특수 변수이다.
- 파일을 직접 실행하면
"__main__"이 되고, import되면 모듈 이름(예:"calc")이 된다.
- 다시 말해서,
python calc.py로 직접 실행하면calc.py가 프로그램의 시작점(entry point), 즉 “메인”이다.- Python이 이를
__name__ = "__main__"으로 표시한다.
- Python이 이를
import calc로 불러오면calc.py는 부품으로 사용되는 것이지 시작점이 아니다.- Python이 이를
__name__ = "calc"(파일명)으로 표시한다.
- Python이 이를
2.4 패키지란?
- 패키지는 모듈들을 디렉토리 계층구조로 조직화하는 방법이다:
- 모듈이 많아지거나 관련된 모듈들을 그룹지어 묶어야 할 때 패키지로 만드는 것이 좋다. 구체적인 기준은 다음과 같다.
my_package/
__init__.py
module1.py
module2.py
subpackage/
__init__.py
module3.py
# 패키지 없이 — 파일이 많아지면 어느 파일이 뭔지 파악 어려움
preprocess.py
train.py
evaluate.py
visualize.py
data_loader.py
feature_engineer.py
...
# 패키지로 묶으면 — 역할 단위로 그룹화
ml_project/
data/
loader.py
feature_engineer.py
model/
train.py
evaluate.py
visualization/
visualize.py
__init__.py는 해당 디렉토리가 Python 패키지임을 알려주는 파일이다. 패키지가 import될 때 자동으로 실행된다.
3 라이브러리 생태계
3.1 모듈, 패키지, 라이브러리, 프레임워크의 계층 관계
프레임워크 (라이브러리들을 포함한 완결된 개발 환경, 제어 흐름을 프레임워크가 가짐)
└─ 라이브러리 (패키지들의 모음, 특정 목적을 위한 완결된 도구)
└─ 패키지 (모듈들을 담은 디렉토리)
└─ 모듈 (.py 파일 하나)
└─ 함수 / 클래스 / 변수
- 모듈:
numpy/linalg/decomp.py파일 하나 - 패키지:
numpy/linalg/디렉토리 (여러 모듈 묶음) - 라이브러리:
numpy전체 (특정 목적 — 수치 연산 — 을 위한 완결된 도구)- 내가
np.array()를 언제, 어디서 호출할지 전적으로 내 코드가 결정한다.
- 내가
- 프레임워크:
Django,FastAPI,PyTorch등 — 라이브러리와의 차이는 제어 흐름의 주체가 다르다- 프레임워크: 틀이 정해져 있고 내 코드를 그 틀에 끼워 넣는 것
| 구분 | 제어 흐름의 주체 | 예시 |
|---|---|---|
| 라이브러리 | 내 코드가 라이브러리를 호출 | import numpy; numpy.array(...) |
| 프레임워크 | 프레임워크가 내 코드를 호출 | Django가 내 views.py를 호출 |
3.1.1 장고 예시
# Django 프로젝트의 views.py — 내가 작성하는 파일
from django.http import JsonResponse
from .models import User
# 이 함수는 내가 만드는 것 — 이름도 내가 정할 수 있음
def get_users(request):
users = list(User.objects.values())
return JsonResponse(users, safe=False)
# Django 프로젝트의 urls.py — 내가 작성하는 파일
from django.urls import path
from . import views
urlpatterns = [
# "/users/" 로 요청이 오면 views.get_users 를 실행하라고 Django에게 등록
path('users/', views.get_users),
]사용자가 브라우저에서 /users/ 접속
→ Django가 urls.py를 보고 get_users 함수를 찾음
→ Django가 get_users(request) 호출 ← 내 코드를 프레임워크가 호출
→ 내 함수가 실행되어 결과 반환
3.1.2 PyTorch 예시
- 라이브러리이면서 동시에 프레임워크 역할도 한다.
# Library로서의 예시
import torch
import torch.nn as nn
# 내가 원할 때 원하는 순서로 호출
x = torch.tensor([1.0, 2.0, 3.0])
y = torch.sum(x)
print(y)
# Framework로서의 예시
import torch.nn as nn
# nn.Module을 상속받아 내 모델을 정의
class MyModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(10, 1)
# forward()는 내가 작성하지만, 언제 실행할지는 PyTorch가 결정
# model(x) 호출 시 PyTorch 내부에서 forward()를 자동으로 호출함
def forward(self, x):
return self.linear(x)
model = MyModel()
output = model(input_data) # ← 내가 forward()를 직접 호출하지 않음
# PyTorch가 forward()를 대신 호출
# (PyTorch가 model.forward(input_data) 호출)3.2 표준 라이브러리
- Python을 설치하면 표준 라이브러리(Standard Library)가 함께 설치된다.
- 추가 설치 없이
import만으로 바로 사용할 수 있다.
| 라이브러리 | 용도 | 예시 |
|---|---|---|
random |
난수 생성 | random.randint(1, 10) |
math |
수학 연산 | math.sqrt(25) |
datetime |
날짜/시간 | datetime.date.today() |
os |
운영체제 인터페이스 | os.getcwd() |
3.3 외부 라이브러리와 pip
- 표준 라이브러리가 커버하지 못하는 전문적인 기능은 외부 라이브러리를 통해 얻는다.
pip으로 설치한다:
3.4 requirements.txt
프로젝트가 의존하는 라이브러리와 그 버전을 명시적으로 기록한 파일이다:
3.5 가상환경
프로젝트별로 완전히 독립된 Python 환경을 만들어서 각 환경에 서로 다른 버전의 라이브러리를 설치할 수 있게 한다:
3.6 참고: Poetry
- Poetry는 패키지 관리자(Package Manager) 이지 모듈/패키지/라이브러리 개념 자체는 아니기 때문에 이 글의 범위 밖이다.
현재 글의 범위와 Poetry의 위치를 정리하면:
| 도구 | 역할 | 이 글에서 |
|---|---|---|
| Poetry | pip + venv + requirements.txt를 통합 관리 | 미포함 |
| Conda | 패키지 + 가상환경 + Python 버전까지 통합 관리 | 미포함 |
Poetry가 하는 일:
pip install pandas → poetry add pandas
pip freeze > requirements.txt → pyproject.toml 자동 관리
python -m venv myenv → eval $(poetry env activate) (가상환경 자동 생성/활성화)
pip install -r requirements.txt → poetry install
- 즉 Poetry는
pip+venv+requirements.txt를 하나의 도구로 통합한 것이다.
- 별도 글로 다루는 게 적절하다.
4 정리
이 글에서 다룬 내용을 연결해서 보면 하나의 흐름으로 이어진다:
| 개념 | 역할 |
|---|---|
| 함수 | 반복되는 코드를 재사용 가능한 단위로 묶는다 |
| 모듈 | 관련 함수들을 파일 단위로 조직화한다 |
| 패키지 | 관련 모듈들을 디렉토리 계층으로 묶는다 |
| 라이브러리 | 특정 목적을 위한 패키지들의 완결된 집합이다 |
| 가상환경 | 프로젝트별로 독립된 라이브러리 환경을 보장한다 |
이 계층 구조 전체가 하나의 목표를 향한다: 작성한 코드를 안전하게 재사용하고, 팀원과 공유하고, 다른 환경에서도 동일하게 실행되도록 만드는 것이다.