Python 모듈(Module) 완벽 가이드: import와 패키지 마스터하기

“다른 파일의 함수를 가져다 쓰고 싶은데, 어떻게 해야 하나요?”

프로그램이 커질수록 코드를 여러 파일로 나누어 관리해야 합니다. Python의 모듈 시스템은 코드를 재사용하고 구조화하는 강력한 방법입니다. 이 글에서는 모듈의 개념부터 실전 활용법까지 완벽하게 알려드립니다.

모듈이란?

모듈의 개념

  • *모듈(Module)**은 Python 코드를 담고 있는 파일입니다. 함수, 클래스, 변수 등을 담아 다른 프로그램에서 재사용할 수 있습니다.

간단히 말해, .py 확장자를 가진 Python 파일 = 모듈입니다.

왜 모듈을 사용할까?

# ❌ 모듈 없이 - 모든 코드가 한 파일에
# main.py (5000줄)
def calculate_area(width, height):
    return width * height

def calculate_volume(width, height, depth):
    return width * height * depth

def save_to_file(data, filename):
    with open(filename, 'w') as f:
        f.write(data)

def read_from_file(filename):
    with open(filename, 'r') as f:
        return f.read()

# ... 수백 개의 함수들 ...

# ✅ 모듈 사용 - 기능별로 분리
# geometry.py
def calculate_area(width, height):
    return width * height

def calculate_volume(width, height, depth):
    return width * height * depth

# file_handler.py
def save_to_file(data, filename):
    with open(filename, 'w') as f:
        f.write(data)

def read_from_file(filename):
    with open(filename, 'r') as f:
        return f.read()

# main.py
import geometry
import file_handler

area = geometry.calculate_area(10, 5)
file_handler.save_to_file(str(area), 'result.txt')

모듈 사용의 장점

  • 코드 재사용성 증가
  • 유지보수 편리
  • 네임스페이스 분리
  • 협업 용이

모듈 만들기

기본 모듈 생성

파일을 만들기만 하면 모듈이 됩니다!

calculator.py (모듈 파일)

"""
계산기 모듈
간단한 사칙연산 기능을 제공합니다.
"""

def add(a, b):
    """덧셈"""
    return a + b

def subtract(a, b):
    """뺄셈"""
    return a - b

def multiply(a, b):
    """곱셈"""
    return a * b

def divide(a, b):
    """나눗셈"""
    if b == 0:
        raise ValueError("0으로 나눌 수 없습니다")
    return a / b

# 모듈 레벨 변수
PI = 3.14159
VERSION = "1.0.0"

main.py (모듈 사용)

import calculator

result1 = calculator.add(10, 5)
result2 = calculator.multiply(3, 4)

print(result1)  # 15
print(result2)  # 12
print(calculator.PI)  # 3.14159

클래스가 포함된 모듈

person.py

"""사람 정보를 관리하는 모듈"""

class Person:
    """사람 클래스"""

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        return f"안녕하세요, 저는 {self.name}이고 {self.age}살입니다."

class Student(Person):
    """학생 클래스"""

    def __init__(self, name, age, student_id):
        super().__init__(name, age)
        self.student_id = student_id

    def study(self, subject):
        return f"{self.name}이(가) {subject}를 공부합니다."

# 모듈 레벨 함수
def create_person(name, age):
    """Person 인스턴스를 생성하는 팩토리 함수"""
    return Person(name, age)

main.py

import person

# 클래스 사용
p = person.Person("김철수", 25)
print(p.introduce())

s = person.Student("이영희", 20, "2025001")
print(s.study("Python"))

# 팩토리 함수 사용
p2 = person.create_person("박민수", 30)
print(p2.introduce())

import 기본 문법

1. 기본 import

import math

result = math.sqrt(16)  # 4.0
print(math.pi)          # 3.141592653589793

2. 여러 모듈 import

import math
import random
import datetime

# 또는 한 줄로
import math, random, datetime  # 가능하지만 권장하지 않음

3. as로 별칭 사용

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 긴 모듈명을 짧게
import very_long_module_name as vlmn

arr = np.array([1, 2, 3])

4. from으로 특정 요소만 import

from math import sqrt, pi, cos

# 모듈명 없이 바로 사용
result = sqrt(16)  # math.sqrt(16) 대신
print(pi)          # math.pi 대신

5. from import with as

from datetime import datetime as dt

now = dt.now()  # datetime.datetime.now() 대신

6. 모든 것 import (주의!)

from math import *

# 모든 함수를 직접 사용 가능
result = sqrt(16)
print(pi)

# ⚠️ 권장하지 않음: 네임스페이스 오염

import 고급 활용

상대 경로 import

프로젝트 구조가 복잡할 때 상대 경로로 import합니다.

my_project/
├── main.py
├── utils/
│   ├── __init__.py
│   ├── math_utils.py
│   └── string_utils.py
└── models/
    ├── __init__.py
    └── user.py

utils/math_utils.py

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

def multiply(a, b):
    return a * b

models/user.py

# 상대 경로 import
from ..utils import math_utils  # 상위 디렉토리의 utils

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def calculate_birth_year(self, current_year):
        # math_utils 사용
        return math_utils.subtract(current_year, self.age)

상대 경로 import 규칙

  • . : 현재 디렉토리
  • .. : 상위 디렉토리
  • ... : 상위의 상위 디렉토리

조건부 import

import sys

if sys.platform == 'win32':
    import winsound  # Windows 전용
    def beep():
        winsound.Beep(1000, 500)
else:
    import os
    def beep():
        os.system('echo -e "\\\\a"')  # Unix/Linux

beep()

선택적 import (try-except)

# 특정 패키지가 설치되어 있으면 사용, 없으면 대안 사용
try:
    import numpy as np
    USE_NUMPY = True
except ImportError:
    USE_NUMPY = False
    print("NumPy가 설치되지 않았습니다. 기본 기능만 사용합니다.")

def calculate_mean(numbers):
    if USE_NUMPY:
        return np.mean(numbers)
    else:
        return sum(numbers) / len(numbers)

지연 import

필요할 때만 import하여 시작 시간을 단축합니다.

def process_image(filename):
    # 함수 호출 시에만 import
    from PIL import Image

    img = Image.open(filename)
    return img.size

# PIL이 필요한 경우에만 import됨

\\init\\.py 이해하기

패키지 만들기

디렉토리를 패키지로 만들려면 __init__.py 파일이 필요합니다.

mypackage/
├── __init__.py
├── module1.py
└── module2.py

mypackage/module1.py

def function1():
    return "Module 1"

mypackage/module2.py

def function2():
    return "Module 2"

mypackage/\\init\\.py (비어있어도 됨)

# 패키지 초기화 코드
print("mypackage를 불러왔습니다")

# 편의를 위한 import
from .module1 import function1
from .module2 import function2

__version__ = "1.0.0"
__all__ = ['function1', 'function2']

사용 예시

import mypackage

# __init__.py에서 import했으므로 바로 사용 가능
result1 = mypackage.function1()
result2 = mypackage.function2()

print(mypackage.__version__)  # 1.0.0

\\all\\ 변수

from package import * 시 어떤 것을 노출할지 제어합니다.

mymodule.py

__all__ = ['public_function', 'PublicClass']

def public_function():
    return "공개 함수"

def _private_function():
    return "비공개 함수"

class PublicClass:
    pass

class _PrivateClass:
    pass

사용

from mymodule import *

public_function()  # 작동
PublicClass()      # 작동

# _private_function()  # NameError
# _PrivateClass()      # NameError

# 명시적 import는 가능
from mymodule import _private_function
_private_function()  # 작동

내장 모듈 활용

Python은 다양한 내장 모듈을 제공합니다.

1. os – 운영체제 기능

import os

# 현재 작업 디렉토리
print(os.getcwd())

# 디렉토리 생성
os.makedirs('test_folder', exist_ok=True)

# 파일 목록
files = os.listdir('.')
print(files)

# 경로 조작
path = os.path.join('folder', 'subfolder', 'file.txt')
print(path)  # folder/subfolder/file.txt

# 파일 존재 여부
if os.path.exists('myfile.txt'):
    print("파일이 존재합니다")

# 환경 변수
home = os.environ.get('HOME')

2. sys – 시스템 관련

import sys

# Python 버전
print(sys.version)

# 명령행 인자
print(sys.argv)  # ['script.py', 'arg1', 'arg2']

# 모듈 검색 경로
print(sys.path)

# 프로그램 종료
# sys.exit(0)

# 표준 출력/에러
sys.stdout.write("표준 출력\\\\n")
sys.stderr.write("에러 메시지\\\\n")

3. datetime – 날짜와 시간

from datetime import datetime, date, time, timedelta

# 현재 날짜와 시간
now = datetime.now()
print(now)  # 2025-10-11 14:30:00

# 날짜 생성
birthday = date(1995, 5, 15)

# 시간 계산
tomorrow = now + timedelta(days=1)
next_week = now + timedelta(weeks=1)

# 포맷팅
formatted = now.strftime('%Y년 %m월 %d일 %H시 %M분')
print(formatted)

# 파싱
date_str = "2025-10-11"
parsed = datetime.strptime(date_str, '%Y-%m-%d')

4. random – 난수 생성

import random

# 정수 난수
num = random.randint(1, 100)  # 1~100

# 실수 난수
float_num = random.random()  # 0.0~1.0

# 리스트에서 선택
fruits = ['사과', '바나나', '오렌지']
choice = random.choice(fruits)

# 여러 개 선택
samples = random.sample(fruits, 2)

# 리스트 섞기
random.shuffle(fruits)

5. json – JSON 처리

import json

# 딕셔너리를 JSON 문자열로
data = {
    'name': '김철수',
    'age': 25,
    'skills': ['Python', 'JavaScript']
}

json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(json_str)

# JSON 문자열을 딕셔너리로
loaded = json.loads(json_str)

# 파일로 저장
with open('data.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# 파일에서 읽기
with open('data.json', 'r', encoding='utf-8') as f:
    loaded_data = json.load(f)

6. re – 정규표현식

import re

# 패턴 매칭
text = "이메일: user@example.com"
pattern = r'\\\\w+@\\\\w+\\\\.\\\\w+'
match = re.search(pattern, text)

if match:
    print(match.group())  # user@example.com

# 모든 매치 찾기
text = "전화번호: 010-1234-5678, 010-9876-5432"
phones = re.findall(r'\\\\d{3}-\\\\d{4}-\\\\d{4}', text)
print(phones)  # ['010-1234-5678', '010-9876-5432']

# 치환
text = "Hello World"
replaced = re.sub(r'World', 'Python', text)
print(replaced)  # Hello Python

실전 모듈 구조

유틸리티 모듈 만들기

utils/file_utils.py

"""파일 처리 유틸리티"""

import os
import json

def read_text_file(filename):
    """텍스트 파일 읽기"""
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            return f.read()
    except FileNotFoundError:
        print(f"파일을 찾을 수 없습니다: {filename}")
        return None

def write_text_file(filename, content):
    """텍스트 파일 쓰기"""
    with open(filename, 'w', encoding='utf-8') as f:
        f.write(content)

def read_json_file(filename):
    """JSON 파일 읽기"""
    with open(filename, 'r', encoding='utf-8') as f:
        return json.load(f)

def write_json_file(filename, data):
    """JSON 파일 쓰기"""
    with open(filename, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

def get_file_size(filename):
    """파일 크기 반환 (bytes)"""
    return os.path.getsize(filename)

def file_exists(filename):
    """파일 존재 여부"""
    return os.path.exists(filename)

utils/string_utils.py

"""문자열 처리 유틸리티"""

def reverse_string(text):
    """문자열 뒤집기"""
    return text[::-1]

def count_words(text):
    """단어 개수 세기"""
    return len(text.split())

def capitalize_words(text):
    """각 단어의 첫 글자를 대문자로"""
    return ' '.join(word.capitalize() for word in text.split())

def remove_whitespace(text):
    """공백 제거"""
    return ''.join(text.split())

def truncate(text, length, suffix='...'):
    """문자열 자르기"""
    if len(text) <= length:
        return text
    return text[:length] + suffix

utils/\\init\\.py

"""유틸리티 패키지"""

from .file_utils import (
    read_text_file,
    write_text_file,
    read_json_file,
    write_json_file
)

from .string_utils import (
    reverse_string,
    count_words,
    capitalize_words
)

__version__ = '1.0.0'
__all__ = [
    'read_text_file',
    'write_text_file',
    'read_json_file',
    'write_json_file',
    'reverse_string',
    'count_words',
    'capitalize_words'
]

main.py

from utils import read_json_file, capitalize_words

# 간편하게 사용
data = read_json_file('config.json')
text = capitalize_words("hello python world")
print(text)  # Hello Python World

\\name\\ == “\\main\\” 이해하기

모듈이 직접 실행되는지, import되는지 구분합니다.

calculator.py

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

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

# 이 부분은 직접 실행할 때만 동작
if __name__ == "__main__":
    print("Calculator 모듈을 직접 실행했습니다")
    print("10 + 5 =", add(10, 5))
    print("10 - 5 =", subtract(10, 5))

테스트

# 직접 실행
$ python calculator.py
Calculator 모듈을 직접 실행했습니다
10 + 5 = 15
10 - 5 = 5

# import해서 사용
import calculator

result = calculator.add(3, 4)  # if __name__ 블록은 실행 안 됨

실전 활용: 테스트 코드 작성

# math_utils.py
def factorial(n):
    """팩토리얼 계산"""
    if n <= 1:
        return 1
    return n * factorial(n - 1)

def fibonacci(n):
    """피보나치 수열"""
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# 모듈 테스트
if __name__ == "__main__":
    # 직접 실행 시 자동 테스트
    print("=== 테스트 시작 ===")

    assert factorial(5) == 120, "factorial 테스트 실패"
    assert factorial(0) == 1, "factorial 테스트 실패"
    print("✓ factorial 테스트 통과")

    assert fibonacci(10) == 55, "fibonacci 테스트 실패"
    assert fibonacci(0) == 0, "fibonacci 테스트 실패"
    print("✓ fibonacci 테스트 통과")

    print("=== 모든 테스트 통과 ===")

모듈 검색 경로

Python은 다음 순서로 모듈을 찾습니다:

  1. 현재 디렉토리
  2. PYTHONPATH 환경 변수
  3. 표준 라이브러리 디렉토리
  4. site-packages (설치된 패키지)

sys.path 확인

import sys

print("모듈 검색 경로:")
for path in sys.path:
    print(f"  - {path}")

경로 추가

import sys
import os

# 특정 디렉토리를 모듈 검색 경로에 추가
custom_path = os.path.join(os.getcwd(), 'my_modules')
sys.path.append(custom_path)

# 이제 my_modules 디렉토리의 모듈을 import 가능
import custom_module

순환 import 문제 해결

문제 상황

module_a.py

import module_b

def function_a():
    return "A" + module_b.function_b()

module_b.py

import module_a  # 순환 import!

def function_b():
    return "B" + module_a.function_a()

해결 방법 1: 지연 import

# module_a.py
def function_a():
    import module_b  # 함수 내부에서 import
    return "A" + module_b.function_b()

해결 방법 2: 구조 개선

# common.py
def shared_function():
    return "공통 기능"

# module_a.py
import common

def function_a():
    return "A" + common.shared_function()

# module_b.py
import common

def function_b():
    return "B" + common.shared_function()

타사 패키지 관리

pip로 패키지 설치

# 패키지 설치
pip install requests
pip install pandas numpy

# 특정 버전 설치
pip install django==4.2.0

# 업그레이드
pip install --upgrade requests

# 제거
pip uninstall requests

# 설치된 패키지 목록
pip list

# requirements.txt 생성
pip freeze > requirements.txt

# requirements.txt로 일괄 설치
pip install -r requirements.txt

requirements.txt 예시

requests==2.31.0
pandas==2.1.0
numpy==1.25.2
flask==3.0.0

가상환경 사용

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

# 활성화 (Windows)
myenv\\\\Scripts\\\\activate

# 활성화 (Mac/Linux)
source myenv/bin/activate

# 비활성화
deactivate

모듈 배포하기

setup.py 작성

# setup.py
from setuptools import setup, find_packages

setup(
    name='mypackage',
    version='1.0.0',
    author='Your Name',
    author_email='your@email.com',
    description='패키지 설명',
    long_description=open('README.md').read(),
    long_description_content_type='text/markdown',
    url='<https://github.com/yourusername/mypackage>',
    packages=find_packages(),
    classifiers=[
        'Programming Language :: Python :: 3',
        'License :: OSI Approved :: MIT License',
        'Operating System :: OS Independent',
    ],
    python_requires='>=3.7',
    install_requires=[
        'requests>=2.25.0',
        'numpy>=1.20.0',
    ],
)

배포

# 패키지 빌드
python setup.py sdist bdist_wheel

# PyPI에 업로드
pip install twine
twine upload dist/*

자주 하는 실수와 해결법

실수 1: 표준 라이브러리와 같은 이름

# ❌ 잘못된 예: random.py 파일 생성
# random.py
def my_function():
    pass

# main.py
import random  # 표준 라이브러리 대신 random.py를 import!
# random.randint(1, 10)  # AttributeError!

해결: 표준 라이브러리 이름 피하기

실수 2: \\init\\.py 누락

mypackage/
├── module1.py  # __init__.py가 없음!
└── module2.py

# import mypackage.module1  # ModuleNotFoundError!

해결: __init__.py 파일 추가 (비어있어도 됨)

실수 3: 상대 경로 import 오류

# ❌ 최상위 스크립트에서 상대 경로 import
# main.py
from . import utils  # ValueError!

# ✅ 절대 경로 사용
import utils

마무리하며

Python 모듈 시스템은 코드를 구조화하고 재사용하는 핵심 기능입니다.

핵심 요약

  • 모듈: Python 파일 (.py)
  • 패키지: 모듈을 담은 디렉토리 (\\init\\.py 포함)
  • import: 모듈을 가져와 사용
  • from import: 특정 요소만 가져오기
  • \\name\\: 실행 방식 구분

모듈 설계 원칙

  • 하나의 모듈은 하나의 책임
  • 명확하고 직관적인 이름
  • 문서화 (docstring) 작성
  • \_\all\\_로 공개 API 명시
  • 순환 import 방지

실무 체크리스트

  • ✅ 기능별로 모듈 분리
  • ✅ \\init\\.py로 패키지 구성
  • ✅ requirements.txt로 의존성 관리
  • ✅ 가상환경 사용
  • ✅ 테스트 코드 작성

모듈을 잘 활용하면 코드의 품질과 유지보수성이 크게 향상됩니다. 작은 프로젝트부터 모듈화를 실천해보세요!


참고 자료

댓글 남기기