모듈과 패키지: 코드를 파일과 디렉토리로 조직화하기

Modules and Packages in Python

파이썬에서 모듈과 패키지는 코드를 파일과 디렉토리 단위로 조직화하는 핵심 도구이다. 이 글에서는 모듈의 정의와 import 방식, 패키지 구조, 표준 라이브러리와 pip 사용법, 그리고 가상환경까지 체계적으로 다룬다.

Engineering
Python
저자

Kwangmin Kim

공개

2023년 07월 02일

1 개요

이 글은 파이썬의 모듈(Module)패키지(Package) 시스템을 체계적으로 다룬다. 다음과 같은 흐름으로 구성되어 있다:

  1. 모듈과 패키지: 코드를 파일과 디렉토리로 조직화하는 방법
  2. 라이브러리 생태계: 표준 라이브러리, pip, 가상환경

2 모듈과 패키지

2.1 모듈이란?

  • 모듈은 Python 정의와 문장을 담고 있는 .py 파일이다.
  • 모듈은 함수, 변수, 클래스 등 Python으로 작성할 수 있는 모든 것을 포함할 수 있다.
  • 참고:
    • 스크립트(Script)는 직접 실행하기 위해 작성한 .py 파일이고
    • 모듈(Module)은 다른 파일에서 import해서 사용하기 위해 작성한 .py 파일이다.
# calc.py - 이 파일 자체가 calc라는 모듈이 된다
def add(a, b):
    return a + b

def sub(a, b):
    return a - b

모듈이 제공하는 핵심 가치는 재사용성네임스페이스 분리이다.

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 네임스페이스의 add

Python의 네임스페이스 종류 (LEGB 규칙)

범위 설명 예시
Local 함수 내부 함수 안의 변수
Enclosing 중첩 함수의 바깥 함수 클로저
Global 모듈 전체 모듈 최상단 변수
Built-in Python 내장 len, print, range

이름을 찾을 때 Python은 L → E → G → B 순서로 탐색하고, 먼저 찾은 것을 사용한다.

2.2 모듈 가져오기: import의 세 가지 방식

2.2.1 import 모듈명

import calc

# 모듈명.함수명() 형태로 사용
print(calc.add(3, 4))   # 7

어떤 함수가 어느 모듈에서 왔는지 코드를 읽는 사람이 즉시 알 수 있다. 대규모 프로젝트에서 가장 안전한 방식이다.

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 * (권장하지 않음)

from calc import *   # calc의 모든 것을 가져옴

어떤 이름들이 어디서 들어왔는지 추적하기 어렵고, 예상치 못한 이름 충돌이 발생할 수 있다.

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__"으로 표시한다.
  • import calc로 불러오면 calc.py는 부품으로 사용되는 것이지 시작점이 아니다.
    • Python이 이를 __name__ = "calc"(파일명)으로 표시한다.

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으로 설치한다:
# 기본 설치
python -m pip install pandas

# 특정 버전 설치
python -m pip install pandas==2.0.0

# 여러 패키지 동시 설치
python -m pip install numpy pandas scikit-learn

3.4 requirements.txt

프로젝트가 의존하는 라이브러리와 그 버전을 명시적으로 기록한 파일이다:

# requirements.txt
numpy==1.24.0
pandas==2.0.0
scikit-learn==1.3.0
# 현재 환경의 설치 목록을 requirements.txt로 저장
python -m pip freeze > requirements.txt

# requirements.txt에 명시된 라이브러리 전체 설치
python -m pip install -r requirements.txt

3.5 가상환경

프로젝트별로 완전히 독립된 Python 환경을 만들어서 각 환경에 서로 다른 버전의 라이브러리를 설치할 수 있게 한다:

# 가상환경 생성
python -m venv myproject_env

# 가상환경 활성화 (Windows)
myproject_env\Scripts\activate

# 가상환경 활성화 (macOS/Linux)
source myproject_env/bin/activate

# 활성화 후 라이브러리 설치 (가상환경 안에만 설치됨)
pip install pandas numpy

# 가상환경 비활성화
deactivate

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 정리

이 글에서 다룬 내용을 연결해서 보면 하나의 흐름으로 이어진다:

개념 역할
함수 반복되는 코드를 재사용 가능한 단위로 묶는다
모듈 관련 함수들을 파일 단위로 조직화한다
패키지 관련 모듈들을 디렉토리 계층으로 묶는다
라이브러리 특정 목적을 위한 패키지들의 완결된 집합이다
가상환경 프로젝트별로 독립된 라이브러리 환경을 보장한다

이 계층 구조 전체가 하나의 목표를 향한다: 작성한 코드를 안전하게 재사용하고, 팀원과 공유하고, 다른 환경에서도 동일하게 실행되도록 만드는 것이다.

Subscribe

Enjoy this blog? Get notified of new posts by email: