Application/Python

[Python] 파이썬 프로젝트 구조, 한 파일에서 졸업하기

devsalix 2026. 6. 5. 11:40
728x90

파이썬 프로젝트 구조, 한 파일에서 졸업하기

파이썬 코드를 짜다 보면 어느 순간 한 파일이 500줄, 800줄을 넘어가요. 스크롤을 한참 내려야 함수가 보이고, 어디서 뭘 고쳤는지 본인도 헷갈리기 시작하죠.

저도 옛날에 만든 main.py 하나 열어봤다가 1000줄 넘는 거 보고 한숨 쉰 적 있거든요. 분명 제가 짠 코드인데 남이 짠 것 같은 그 느낌, 진짜 묘해요.

지난 글에서는 pandas로 데이터프레임 다루는 법까지 익히셨잖아요. 거기서 한 발만 더 나가면 코드량이 금방 불어나요. 오늘은 이걸 어떻게 폴더 단위로 쪼개는지, 즉 파이썬 프로젝트 구조를 어떻게 잡는지 얘기해볼게요.


왜 python 폴더 구조가 필요할까요

부엌 한번 생각해보세요. 그릇이랑 식재료랑 조미료를 한 서랍에 다 쑤셔 넣어두면 어떻게 될까요. 라면 하나 끓이려고 30분 동안 서랍 뒤지게 되잖아요.

근데 그릇 칸, 식재료 칸, 조미료 칸을 나눠두면 어디 뭐가 있는지 바로 알죠.

책장도 똑같아요. 소설이랑 만화책이랑 전공서를 한 칸에 다 꽂아두면 찾는 데 한세월이지만, 장르별로 나눠두면 손이 바로 가잖아요.

코드도 마찬가지예요. API 호출하는 코드, 데이터 가공하는 코드, 자주 쓰는 함수들을 한 파일에 다 때려넣으면 나중에 수정할 때 지옥이 펼쳐집니다. 역할별로 파일을 나눠두면 "아 이 부분은 api.py에 있겠다" 하고 바로 찾아갈 수 있어요.

거창한 이론 같은 거 몰라도 됩니다. 그냥 역할별로 나눈다. 이거 하나만 기억하세요.


파이썬 실무에서 쓰는 기본 폴더 구조

회사마다 다르고 프로젝트마다 다르긴 한데, 개인 프로젝트나 소규모 작업이라면 보통 이런 모양으로 시작합니다.

weather_app/
├── README.md           # 이 프로젝트가 뭐 하는 건지 설명
├── requirements.txt    # 필요한 라이브러리 목록
├── .gitignore          # 깃에 안 올릴 파일 목록
├── .env                # 비밀번호/API 키 (절대 깃에 X)
├── main.py             # 실행 진입점
├── src/                # 실제 코드가 모여있는 곳
│   ├── __init__.py
│   ├── api.py          # 외부 API 호출 담당
│   ├── utils.py        # 자주 쓰는 헬퍼 함수
│   └── config.py       # 설정값 모음
└── data/               # csv 같은 데이터 파일

 

처음 보시면 좀 부담스러우실 수 있는데요. 사실 처음부터 다 만들 필요는 없어요. main.py 하나랑 src/ 폴더 하나만 있어도 시작은 됩니다. 나머지는 필요할 때 하나씩 추가하시면 돼요.


main.py 진입점과 src 폴더 나누기

가장 중요한 약속이 하나 있어요. main.py는 실행 시작점이라는 거예요. 터미널에서 python main.py 치면 여기부터 돌아가는 거죠.

그래서 main.py에는 로직을 길게 쓰지 않아요. 그냥 "어떤 함수들을 어떤 순서로 부른다" 정도로만 짧게 둡니다.

# main.py — 프로그램 시작점
from src.api import fetch_weather         # 날씨 가져오는 함수 불러오기
from src.utils import format_temperature  # 출력 정리하는 함수 불러오기

def run():
    data = fetch_weather("Seoul")         # 서울 날씨 가져오기
    print(format_temperature(data))       # 보기 좋게 출력

if __name__ == "__main__":
    run()                                 # 이 파일을 직접 실행하면 run() 호출

 

실제 로직은 src/ 안에 모듈로 분리해둡니다.

# src/api.py — 외부 API 호출 담당
import requests
import os

API_KEY = os.getenv("WEATHER_API_KEY")    # .env에서 키 불러옴

def fetch_weather(city):
    # 날씨 API 호출
    url = f"https://api.example.com/weather?city={city}&key={API_KEY}"
    response = requests.get(url)          # GET 요청 보내기
    return response.json()                # JSON 응답을 딕셔너리로 변환

 

# src/utils.py — 자주 쓰는 헬퍼
def format_temperature(data):
    # 보기 좋은 문자열로 변환
    temp = data["temp"]                   # 응답에서 기온 값 꺼내기
    return f"현재 기온: {temp}°C"

 

실행하면 이런 결과가 나와요.

현재 기온: 27°C

 

이렇게 나눠두면 나중에 "API 응답 형식이 바뀌었네" 할 때 api.py만 열어서 고치면 돼요. 출력 모양만 바꾸고 싶으면 utils.py만 보면 되고요.


init.py의 정체

src/ 폴더 안에 있는 그 빈 파일, 뭔가 싶으시죠. 저도 처음에 "이 안에 뭘 써야 하지?" 하고 한참 고민했거든요.

결론부터 말씀드리면, 빈 파일이어도 괜찮습니다. 진짜로 한 글자도 안 써도 돼요.

__init__.py는 그냥 "이 폴더는 파이썬 패키지예요"라고 표시해주는 푯말 같은 거예요. 안에 아무것도 없어도 파이썬이 그걸 보고 "아 여긴 패키지구나" 하고 인식합니다.

파이썬 3.3 이후로는 사실 없어도 동작은 해요. 근데 헷갈리니까 그냥 만들어두는 걸 추천합니다. 우클릭해서 새 파일 만들고 이름만 __init__.py로 저장하면 끝이에요.


import 경로는 절대 임포트로

src/ 폴더 안에서 다른 파일을 불러올 때, 이렇게 쓰는 걸 권장해요.

from src.utils import format_temperature  # 절대 경로로 명확하게

 

상대 임포트(from .utils import ...)도 가능하긴 한데, 처음엔 헷갈리니까 절대 임포트로 통일하세요. 경로가 한눈에 보여서 디버깅도 편하거든요.


requirements.txt와 .gitignore 정리

requirements.txt는 이전 글에서도 짚었지만, 이 프로젝트에 필요한 라이브러리 목록이에요. 다른 사람(혹은 미래의 내가 다른 컴퓨터에서) 이 코드를 실행할 때 pip install -r requirements.txt 한 줄로 환경을 맞출 수 있어요.

.gitignore는 깃에 안 올릴 파일 목록입니다. 기본 템플릿은 이 정도면 충분해요.

# 가상환경
venv/
.venv/

# 파이썬 캐시
__pycache__/
*.pyc

# 비밀 정보
.env

# IDE 설정
.vscode/
.idea/

 

__pycache__/는 파이썬이 자동으로 만드는 캐시 폴더인데, 깃에 올릴 이유가 전혀 없어요. venv/는 가상환경 폴더, 이것도 무겁고 사람마다 달라서 절대 공유하면 안 됩니다. .env는 다음에 바로 얘기할 비밀키 파일이고요.


비밀키는 .env로 분리하세요

이거 진짜 중요해요. API 키나 비밀번호를 코드에 직접 적어두면 안 됩니다.

.env라는 파일을 따로 만들어서 거기에 적어두고, 코드에서는 python-dotenv 라이브러리로 불러오는 식으로 작업하세요.

WEATHER_API_KEY=abc123def456

 

그리고 .gitignore.env 꼭 추가하세요. 이거 빼먹고 깃허브 공개 저장소에 푸시하면, 5분도 안 돼서 봇이 키를 긁어가서 도용합니다. 농담 아니고 실제로 자주 일어나는 사고예요.


자주 하는 실수 모음

ModuleNotFoundError: No module named 'src'

처음에 다들 한 번씩 만나는 에러예요. src/api.py 안에 들어가서 python api.py로 직접 실행했을 때 자주 납니다. 같은 폴더 안인데 왜 못 찾냐 싶으시죠.

이유는 실행 위치가 잘못된 거예요. 항상 프로젝트 루트(즉 main.py가 있는 폴더)에서 python main.py로 실행해야 합니다.

비밀키 깃 사고

"한 번만 테스트하려고 그냥 코드에 키 박았어요" → 깃 푸시 → 사고. 진짜 흔합니다. 처음부터 .env 습관 들이세요.

모듈을 너무 잘게 쪼개기

반대로 함수 하나마다 파일 만드는 분들도 있어요. 그러면 오히려 어디 뭐가 있는지 더 헷갈립니다. "이 파일은 뭐 담당이다"가 한 문장으로 설명되면 OK인 정도로 묶으세요.


여기까지 읽으시면 "그래서 어쩌라고요" 싶으실 수도 있어요. 이론만 봐서는 감이 잘 안 오는 게 정상입니다. 저도 그랬거든요.

그래서 다음 글이 이 시리즈의 마지막인데, 오늘 배운 이 폴더 구조를 그대로 써서 진짜로 돌아가는 날씨 앱을 하나 만들어볼 거예요. 32편까지 왔으니 거의 다 온 거죠.


 


제 글이 도움이 되셨다면 댓글 & 공감 부탁드려요 😀

 

 
728x90