오늘도 예상보다 긴 디버깅 시간이 필요했다. 원인은 단순했지만 그걸 확실하게 짚고 넘어가는 데 시간이 꽤 걸렸다. FastAPI를 Docker로 띄우면서 502 Bad Gateway 오류를 마주했고 하나씩 원인을 추적하며 개선해보았다.
증상: Nginx를 통해 접근하면 502 Bad Gateway
FastAPI는 /api prefix를 붙인 상태로 정상 작동해야 했고 Ghost CMS도 같은 nginx 프록시 아래 붙어 있는 구조였다. Ghost는 잘 뜨는데 FastAPI는 계속 502 오류 발생. 가장 먼저 했던 건 docker compose logs로 백엔드 로그를 확인하는 것이었다.
원인 분석: Jinja2Templates인데 jinja2가 없다
백엔드 로그에는 이렇게 뜨고 있었다.
AssertionError: jinja2 must be installed to use Jinja2TemplatesFastAPI 내부에서 Jinja2Templates를 호출하고 있었지만 requirements.txt 컨테이너 안에는 jinja2 패키지가 설치되어 있지 않았다. 강의 자료에 해당 부분이 빠져있었는데.. 다른 데에 이유가 있을 거라 여기며 다른 시도들을 했다. 특히 main.py에도 import Jinja2Templates가 있었기 때문에 해당 이유라고 짐작을 못했다. 코드에 모듈이 임포트되어 있다고 해도 완벽하지 않을 수 있다는 점을 깨닫게 된 계기가 되었다.
해결: requirements.txt에 jinja2 추가 → 재빌드
문제와 에러 메시지가 명확했기에 해결은 간단했다.
# requirements.txt
fastapi
uvicorn
jinja2
httpx
python-jose
jinja2추가 후 도커로 다시 올렸다. FastAPI 컨테이너도 정상 기동되고 웹페이지도 제대로 나와주었다. nginx 로그에서도 더 이상 connect() failed가 찍히지 않았다.
Jinja2는 Python 기반의 템플릿 엔진. 주로 HTML 파일 안에서 동적인 데이터를 삽입하고 렌더링할 때 사용. Flask, FastAPI 등의 웹 프레임워크에서 자주 쓰임
Jinja2의 역할
| 역할 | 설명 | 예시 |
|---|---|---|
| 1. HTML과 Python 데이터 연결 | 백엔드에서 넘긴 데이터를 HTML에 동적으로 표현 | {{ username }} 으로 사용자 이름 출력 |
| 2. 조건문/반복문 처리 | HTML 안에서 if, for 문을 사용 | {% if user %} ... {% endif %} |
| 3. 템플릿 상속 | 공통 레이아웃을 만들어 페이지마다 재사용 | base.html을 상속해서 home.html 작성 |
| 4. 필터 및 함수 제공 | 날짜 포맷, 대소문자 변환 등 데이터 전처리를 지원 | {{ today.strftime('%Y-%m-%d') }}, {{ name|upper }} |
| 5. 보안 처리 (자동 이스케이프) | HTML 삽입 공격(XSS)을 방지하기 위해 자동으로 문자열을 이스케이프 처리 | <script> 태그 차단 |
마무리하며
- Import했어도 완벽하게 설치된 건 아닐 수 있다. 컨테이너 환경에서는 dependencies가 명시되지 않으면 제대로 작동하지 않는다.
- Docker에서 발생하는 502는 Nginx 보다 "백엔드가 죽어서" 생기는 경우가 많다.
docker logs를 가장 먼저 확인할 필요가 있다.
502 덕분에 로그도, 컨테이너도, FastAPI 앱도 조금 더 친해진 기분!