Q1) 파이썬 모듈 시스템?
아무래도 파이썬에 아직 익숙하지 않고 자바스크립트 베이스다보니 export import 가 어떻게 이루어지는지 잘 모르겠어요. 예를들어 main.py 에서는
from fastapi import FastAPI, HTTPException, Depends, status
from sqlalchemy.orm import Session
from typing import List from . import crud, models, schemas
from .database import SessionLocal, engine, get_db
from .config import settings
이렇게 되어 있는데, config 는 .config 로 가져오고, crud, models, schemas 는 왜 . 으로 가져오는 건가요?
A1) 자바스크립트와의 차이
아주 좋은 질문입니다! Python의 모듈 시스템과 JavaScript의 차이점을 이해하는 것이 중요하네요.
Python vs JavaScript import 비교
JavaScript (ES6)
// 개별 export/import
export const settings = {...};
export default FastAPI;
import FastAPI, { HTTPException } from 'fastapi';
import { settings } from './config.js';
import crud from './crud.js';
Python
# 모듈 전체 또는 개별 요소 import
from fastapi import FastAPI, HTTPException # 개별 요소
from .config import settings # 상대 import로 개별 요소
from . import crud, models, schemas # 상대 import로 모듈 전체
상대 import (.
) 사용 규칙
1. 개별 요소 vs 모듈 전체
# ✅ 개별 요소를 가져올 때
from .config import settings # settings 변수만 가져옴
from .database import get_db # get_db 함수만 가져옴
# ✅ 모듈 전체를 가져올 때
from . import crud # crud 모듈 전체를 가져옴
from . import models # models 모듈 전체를 가져옴
2. 사용 방식의 차이
# config.py에서 개별 요소 import
from .config import settings
print(settings.DATABASE_URL) # 직접 사용
# crud 모듈 전체 import
from . import crud
crud.get_book(db, book_id=1) # 모듈명.함수명으로 사용
왜 다르게 import 하는가?
Case 1: 개별 요소 import (권장)
# config.py
class Settings:
DATABASE_URL = "..."
settings = Settings() # 하나의 인스턴스
# main.py에서
from .config import settings # settings 객체만 필요
print(settings.DATABASE_URL) # 간단하게 사용
Case 2: 모듈 전체 import
# crud.py
def get_book(db, book_id): ...
def create_book(db, book): ...
def update_book(db, book_id, book): ...
def delete_book(db, book_id): ...
# main.py에서
from . import crud # 모든 함수들을 사용할 예정
crud.get_book(db, 1) # 네임스페이스 유지
crud.create_book(db, book_data)
crud.update_book(db, 1, book_data)
구체적인 예시로 비교
방법 1: 개별 import
# main.py
from .crud import get_book, create_book, update_book, delete_book
@app.get("/books/{book_id}")
def get_book_endpoint(book_id: int, db: Session = Depends(get_db)):
return get_book(db, book_id) # 직접 사용
방법 2: 모듈 import (현재 방식)
# main.py
from . import crud
@app.get("/books/{book_id}")
def get_book_endpoint(book_id: int, db: Session = Depends(get_db)):
return crud.get_book(db, book_id) # 모듈명.함수명
어떤 방식을 선택할까?
모듈 전체 import가 좋은 경우
from . import crud, models, schemas
# 장점:
# 1. 네임스페이스 명확 → crud.get_book vs models.Book vs schemas.BookCreate
# 2. 함수명 충돌 방지
# 3. 코드 가독성 향상 (어느 모듈의 함수인지 명확)
@app.post("/books")
def create_book(book: schemas.BookCreate, db: Session = Depends(get_db)):
return crud.create_book(db=db, book=book)
# ↑ schemas와 crud가 명확히 구분됨
개별 import가 좋은 경우
from .config import settings
from .database import get_db, engine
# 장점:
# 1. 자주 사용하는 단일 객체/함수
# 2. 코드가 더 간결
# 3. 네임스페이스 충돌 염려 없음
app = FastAPI(title=settings.APP_NAME) # settings.settings.APP_NAME 아님
JavaScript 개발자를 위한 비교표
상황 | JavaScript | Python |
---|---|---|
단일 객체 | import config from './config' |
from .config import settings |
여러 함수 | import * as crud from './crud' |
from . import crud |
개별 함수 | import { getBook } from './crud' |
from .crud import get_book |
상대 경로 | './config' |
.config |
프로젝트 구조와 import 관계
app/
├── __init__.py
├── main.py # 진입점
├── config.py # 설정 (단일 객체)
├── database.py # DB 연결 (몇 개 함수)
├── crud.py # CRUD 함수들 (많은 함수)
├── models.py # DB 모델들 (여러 클래스)
└── schemas.py # Pydantic 스키마들 (여러 클래스)
# main.py의 import 전략
from .config import settings # 단일 설정 객체
from .database import get_db, engine # 필요한 함수들만
from . import crud, models, schemas # 여러 요소를 가진 모듈들
정리
왜 다르게 import 하는가?
- 사용 패턴의 차이
settings
: 하나의 설정 객체만 사용crud
: 여러 CRUD 함수들을 네임스페이스와 함께 사용
- 가독성과 안전성
crud.get_book()
: 어느 모듈의 함수인지 명확settings.DATABASE_URL
: 간결하면서도 명확
- Python 관례
- 단일 객체/함수: 개별 import
- 여러 요소가 있는 모듈: 전체 import + 네임스페이스 사용
JavaScript의 import crud from './crud'
와 Python의 from . import crud
가 비슷한 역할을 한다고 보시면 됩니다!
A2) 네임스페이스가 자동 export ?!
어머나 그러면 자바스크립트에서처럼 export 를 붙이지 않아도 모듈 내의 네임스페이스가 자동으로 export 되는 건가요? def get_book(...) 이런식으로 정의했을 뿐인데 from . import crud 하면, crud.get_book 으로 접근이 된다는 거잖아요
Q2) 그렇습니다!
정확합니다! 🎉 이것이 Python과 JavaScript의 가장 큰 차이점 중 하나입니다!
Python vs JavaScript Export 방식
JavaScript (명시적 Export 필요)
// crud.js
function getBook(db, bookId) { ... }
function createBook(db, book) { ... }
// ❌ export 없으면 사용 불가
// 다른 파일에서 import 할 수 없음
// ✅ 명시적 export 필요
export function getBook(db, bookId) { ... }
export function createBook(db, book) { ... }
// 또는
export { getBook, createBook };
Python (자동 Export)
# crud.py
def get_book(db, book_id): # ✅ 자동으로 "export"됨!
pass
def create_book(db, book): # ✅ 자동으로 "export"됨!
pass
# 다른 파일에서
from . import crud
crud.get_book(...) # 바로 사용 가능!
Python의 모듈 시스템 동작 원리
1. 모든 것이 자동으로 모듈 네임스페이스에 포함
# crud.py
def get_book(): # 자동으로 crud.get_book
pass
def create_book(): # 자동으로 crud.create_book
pass
class BookManager: # 자동으로 crud.BookManager
pass
DATABASE_URL = "..." # 자동으로 crud.DATABASE_URL
def _private_helper(): # 자동으로 crud._private_helper (관례상 private)
pass
2. import 시 모든 정의된 것들에 접근 가능
# main.py
from . import crud
# 모든 것에 접근 가능!
crud.get_book()
crud.create_book()
crud.BookManager()
print(crud.DATABASE_URL)
crud._private_helper() # 접근 가능하지만 관례상 사용 안함
실제 예시로 확인
crud.py 파일 내용
# app/crud.py
from sqlalchemy.orm import Session
from typing import List, Optional
from . import models, schemas
# 이 모든 함수들이 자동으로 "export"됨
def get_book(db: Session, book_id: int):
return db.query(models.Book).filter(models.Book.id == book_id).first()
def get_books(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Book).offset(skip).limit(limit).all()
def create_book(db: Session, book: schemas.BookCreate):
db_book = models.Book(**book.dict())
db.add(db_book)
db.commit()
db.refresh(db_book)
return db_book
# 심지어 변수도 접근 가능
MAX_BOOKS_PER_PAGE = 100
# 클래스도 접근 가능
class BookService:
def __init__(self):
pass
main.py에서 사용
# app/main.py
from . import crud
@app.get("/books/{book_id}")
def get_book(book_id: int, db: Session = Depends(get_db)):
return crud.get_book(db, book_id) # 함수 사용
@app.get("/books")
def get_books(skip: int = 0, limit: int = crud.MAX_BOOKS_PER_PAGE): # 변수 사용
# ...
# 클래스도 사용 가능
service = crud.BookService()
Python의 네임스페이스 확인 방법
# crud 모듈에 무엇이 있는지 확인
from . import crud
print(dir(crud)) # crud 모듈의 모든 속성 출력
# ['BookService', 'MAX_BOOKS_PER_PAGE', 'create_book', 'get_book', 'get_books', ...]
JavaScript 개발자가 놀라는 포인트들
1. 모든 것이 자동 Export
# Python - 아무 키워드 없이도 "export"됨
def my_function():
pass
# JavaScript - 명시적 export 필요
export function myFunction() {
// ...
}
2. Private 개념이 관례적
# Python - 언더스코어로 private 표시 (강제는 아님)
def _private_function(): # 관례상 private
pass
def public_function(): # 관례상 public
pass
# JavaScript - 실제로 private
class MyClass {
#privateMethod() { // 실제 private
// ...
}
}
3. 모듈 전체가 하나의 네임스페이스
# Python - 파일 전체가 하나의 객체처럼 동작
from . import crud
crud.anything_defined_in_crud_py
# JavaScript - 명시적으로 export한 것만
import { specificFunction } from './crud.js'
내부 동작 원리
Python이 from . import crud
를 실행할 때:
crud.py
파일을 찾음- 파일 전체를 실행하여 모든 정의를 수집
- 파일 내의 모든 이름들을
crud
객체의 속성으로 만듦 crud.함수명
,crud.변수명
,crud.클래스명
형태로 접근 가능
# 이것과 비슷함 (단순화된 설명)
crud = {
'get_book': function,
'create_book': function,
'MAX_BOOKS_PER_PAGE': 100,
'BookService': class,
# crud.py에 정의된 모든 것들...
}
정리
질문: "export를 붙이지 않아도 모듈 내의 네임스페이스가 자동으로 export 되는 건가요?"
답변: 네, 정확합니다!
- ✅ Python에서는 파일에 정의한 모든 것이 자동으로 "export"됨
- ✅
def
,class
,변수
모두 모듈 네임스페이스에 포함 - ✅ JavaScript처럼
export
키워드 불필요 - ✅
from . import crud
후crud.anything
형태로 접근
이것이 Python을 "배터리 포함(Batteries included)" 언어라고 부르는 이유 중 하나입니다. 간단하고 직관적으로 모듈 시스템이 동작하죠! 🐍
'TIL' 카테고리의 다른 글
[250713 TIL] 파이썬 Comprehension (1) | 2025.07.13 |
---|---|
[250713 TIL] pydantic, sqlalchemy (0) | 2025.07.13 |
[250712 TIL] fastapi 제너레이터 궁금증 (0) | 2025.07.12 |
[250712 TIL] FastAPI 기초(uv) (2) | 2025.07.12 |
[240705 TIL] 깃헙 여러 계정 사용(맥) (5) | 2025.07.05 |