티스토리 뷰
순환 참조(circular import)는 두 개 이상의 모듈이 서로를 참조(혹은 의존)하는 상황을 말합니다. 즉, A 모듈이 B 모듈을 import하고, 동시에 B 모듈이 다시 A 모듈을 import하는 구조가 될 때 발생하는 문제입니다. 이런 상황은 Python의 import 시스템에서 문제가 발생할 수 있으며, 특히 Flask 같은 프로젝트에서는 app 객체를 전역으로 사용하면 규모가 커질수록 이런 문제가 발생할 확률이 높아집니다.
순환 참조의 예시
다음과 같은 두 파일을 예로 들어보겠습니다:
a.py
# a.py
from b import func_b
def func_a():
print("Function A")
func_b()
b.py
# b.py
from a import func_a
def func_b():
print("Function B")
func_a()
여기서 a.py 파일은 b.py에서 func_b를 가져오고 있고, b.py는 a.py에서 func_a를 가져오고 있습니다. 이때 이 파일들을 실행하려고 하면 Python은 모듈을 로드하는 과정에서 순환 참조가 발생하고, 결국 오류가 발생할 수 있습니다.
왜 순환 참조가 문제가 되는가?
Python의 모듈 로딩 시스템은 한 모듈을 import할 때 해당 모듈을 완전히 로드하기 전까지는 사용할 수 없습니다. 그런데 순환 참조가 발생하면 A 모듈이 B 모듈을 import하고, B 모듈이 다시 A 모듈을 import하는 과정에서 두 모듈 모두 완전히 로드되지 않아서 Python이 어느 시점에 모듈을 로드해야 할지 혼란스러워집니다. 이로 인해 ImportError나 AttributeError와 같은 오류가 발생하게 됩니다.
Flask에서의 순환 참조 문제
Flask에서는 app 객체를 여러 모듈에서 전역적으로 사용하려고 할 때 순환 참조가 발생할 수 있습니다. 예를 들어, app 객체를 routes.py에서 import한 다음, 다시 app.py에서 routes.py를 import하는 상황이 생길 수 있습니다.
Flask에서의 순환 참조 예시:
# app.py
from flask import Flask
from routes import init_routes
app = Flask(__name__)
init_routes(app)
if __name__ == "__main__":
app.run()
# routes.py
from app import app # 문제 발생: app.py에서 이미 routes.py를 불러오고 있음
def init_routes(app):
@app.route('/')
def index():
return "Hello, World!"
위의 예시에서, app.py는 routes.py를 가져오고, routes.py는 다시 app 객체를 import합니다. 이로 인해 순환 참조가 발생할 수 있습니다.
순환 참조 해결 방법
순환 참조를 방지하는 가장 일반적인 방법은 모듈의 의존성을 명확히 나누고, 지연된 import(lazy import) 방식을 사용하는 것입니다.
1. 모듈 분리
모듈을 재구성하여 순환 참조를 방지하는 방법입니다. app 객체를 별도의 초기화 모듈로 분리하고, 라우팅이나 다른 기능을 따로 관리합니다.
# app.py (Flask app 객체를 초기화하는 모듈)
from flask import Flask
def create_app():
app = Flask(__name__)
return app
# routes.py (라우트 설정)
def init_routes(app):
@app.route('/')
def index():
return "Hello, World!"
# main.py (앱 실행)
from app import create_app
from routes import init_routes
app = create_app()
init_routes(app)
if __name__ == "__main__":
app.run()
이 방식으로 분리하면 app 객체를 명확히 분리할 수 있어 순환 참조가 발생하지 않습니다.
2. 지연된 import (Lazy Import)
특정 함수나 조건 안에서 import를 실행하여 순환 참조를 피할 수 있습니다.
# routes.py
def init_routes(app):
@app.route('/')
def index():
return "Hello, World!"
# app.py
from flask import Flask
app = Flask(__name__)
def init_routes():
from routes import init_routes # 이 부분에서만 import 실행
init_routes(app)
if __name__ == "__main__":
init_routes()
app.run()
이렇게 init_routes 함수 안에서만 필요한 시점에 import를 실행하면, Python의 모듈 로딩 순환 문제를 피할 수 있습니다.
결론
순환 참조는 모듈들이 서로를 참조하면서 무한 루프에 빠지는 문제로, Python의 모듈 로딩 시스템에서 큰 문제가 될 수 있습니다. Flask와 같은 웹 프레임워크에서도 이를 방지하려면 모듈을 명확하게 분리하고, 필요한 경우 지연된 import 방식을 사용해야 합니다.
'알아두면 쓸데 있는 > 코딩 지식' 카테고리의 다른 글
유용한 Xcode 단축키 모음 (1) | 2024.09.21 |
---|---|
werkzeug(버크저그)란? (0) | 2024.09.19 |
Git에 이미 커밋한 특정 파일, 폴더 추적을 중지하려면? (0) | 2024.09.18 |
PostgreSQL(포스트그레SQL)이란? (2) | 2024.09.16 |
로그 레벨(Log Level)이란? (0) | 2024.09.16 |
- Total
- Today
- Yesterday
- useEffect
- 쉽게 풀어쓴 C언어 Express
- stdlib.h
- 소프트웨어 버전 관리
- pwa(progressive web app)
- 프로세스 강제 종료
- 시맨틱 버전(semantic versioning
- Collections
- named export vs default export
- defaultdict
- react router
- ajax (asynchronous javascript and xml)
- x.y.z (메이저.마이너.패치)
- public vs assets
- inp
- chrome extension 자동 배포
- structuredclone()
- 원시값(primitive)
- json.parse(json.stringify())
- javascript 필수 문법
- react
- jackson 라이브러리
- semver)
- core web vitals
- mermaid-cli
- styled-components
- math.h
- 중첩 함수(nested function)
- counter
- Jest
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |