신규 프로젝트나 운영 업무를 인계받다 보면, 아무런 가이드 문서 없이 돌아가고 있는 서버 하나만 덩그러니 놓여 있는 상황을 맞닥뜨리곤 한다. 이번 분석의 시작이 그랬다....
시스템 구성도나 서비스 명세서 하나 없이, 리눅스 서버 터미널과 한 판 씨름하며 소스 코드를 하나씩 까보고 프로세스를 역추적하여 Flask + Gunicorn + Nginx로 이어지는 서비스의 실체를 파헤친 과정을 기록으로 남긴다.
1. 프로세스 역추적: 서비스 찾기
가장 먼저 수행한 작업은 현재 서버에서 무엇이, 어떻게 돌고 있는지 확인하는 것이었다. 일단 리눅스 서버에 접속한 뒤 표준 명령어를 통해 서비스의 정체를 파악했다.
- 실행 유닛 확인: systemctl list-units --type=service를 통해 관리되고 있는 서비스 목록을 훑었고, api-service라는 이름의 유닛을 발견했다. (물론 모니터링 서비스들도 있었다.)
- 데몬 설정 분석: systemctl cat api-service 명령어로 서비스의 내부를 들여다보았다. 여기서 WorkingDirectory를 통해 소스 코드의 위치를, ExecStart를 통해 Gunicorn이 엔진으로 사용되고 있음을 확인했다.
2. Gunicorn과 Flask의 역할 모델
분석 결과, 이 서비스는 파이썬 웹 표준인 WSGI(Web Server Gateway Interface) 구조를 따르고 있었다.
자바 개발자의 시각에서 본 구조는 다음과 같다.
| 구성 요소 | 실제 역할 | Java 진영 매칭 |
| Flask | 웹 프레임워크 | Spring Boot |
| Gunicorn | WSGI HTTP Server | Embedded Tomcat |
| systemd | 서비스 관리자 | Windows Service / Daemon |
- Gunicorn (WAS): 단순한 실행 도구가 아니라, 직접 서비스 포트를 바인딩하고 요청을 처리하는 WAS 엔진, 별도의 관리 포트 없이 설정된 서비스 포트 하나를 점유하여 리스닝(Listening) 함
- Flask (Logic): Gunicorn에 의해 로드되어 실행되는 비즈니스 로직
**WSGI(Web Server Gateway Interface, 위스기)
- 파이썬 애플리케이션(Flask, Django 등)과 웹 서버(Nginx, Apache 등) 사이에서 통신하기 위한 표준 프로토콜
- 자바 개발자에게 익숙한 개념으로 치면 서블릿 스펙(Servlet Spec)과 거의 동일한 역할
- WSGI 서버의 종류 (엔진)
- Gunicorn: 파이썬에서 가장 많이 쓰는 WSGI 서버 (자바의 톰캣 역할)
- uWSGI: 성능은 좋지만 설정이 복잡한 또 다른 WSGI 서버
- Werkzeug: Flask 내장 서버 (개발용으로만 쓰고 운영에선 안 씀)
3. 포트와 프록시의 연결 고리
서버에는 외부 접속을 위한 Nginx가 앞단에 배치되어 있었다. 구성도 없이 netstat과 lsof만으로 파악한 통신 흐름은 다음과 같다.
- Nginx (Port 443): 외부 접속을 받는 유일한 통로(L7 Proxy)
- Internal Pass: Nginx 설정 파일(proxy_pass)을 확인한 결과, 내부에서 가동 중인 Gunicorn의 서비스 포트로 트래픽을 넘겨주고 있었다.
- Endpoint: 구니콘은 이 포트를 점유하여 대기하다가 Flask 앱의 엔드포인트로 요청을 전달한다.
4. 실전 분석 명령어
문서가 없는 환경에서 유효했던 핵심 명령어 셋
- 포트 점유 프로세스 식별: sudo netstat -tnlp (포트별 PID 및 프로그램명 확인)
- 포트 기반 서비스 정체 파악: sudo lsof -i :[Port] (해당 포트를 사용하는 실행 파일 경로 역추적)
- 런타임 로그 분석: sudo journalctl -u [Service] -f (데몬이 뱉는 표준 출력 로그 실시간 모니터링)
5. 결론: "구조를 알면 소스가 보인다"
시스템 구성도가 없더라도 리눅스 서비스 데몬(systemd) -> WAS 엔진(Gunicorn) -> 프레임워크(Flask)로 이어지는 계층 구조를 이해하면 역추적은 생각보다 명확해진다. 자바 서비스 구조를 투영하여 분석한 결과, 엔진의 설정값(ExecStart)과 실제 소스 코드의 엔트리 포인트를 정확히 매칭시킬 수 있었고, 이를 통해 서비스 전체 흐름을 파악할 수 있었다.
TODO: 이제 파이썬 소스코드를 분석해야 한다. 하기 프로세스대로 할 예정. 힘내자!
1. 진입점(Entry Point) 식별 및 객체 초기화 분석
- 방법: ExecStart에 명시된 api_server:app 구조를 추적한다.
- 분석 포인트: api_server.py 내에서 Flask(__name__) 객체가 생성되는 시점을 찾고, 해당 파일에서 import 하는 설정 파일(Config)이나 초기화 로직(DB 연결, 로깅 설정 등)을 먼저 파악한다. 이는 자바의 ApplicationContext 로딩 과정을 분석하는 것과 같다.
2. API 엔드포인트 및 라우팅 맵핑(Controller) 추출
- 방법: grep -r "@app.route" . 또는 @blueprint.route를 검색한다.
- 분석 포인트: 스프링의 @RequestMapping 대신 사용되는 라우팅 데코레이터를 전수 조사한다.
- 소스가 git 관리가 안 되어 있고 서버에만 존재하여 험난할 듯 싶다.
3. 의존성 및 런타임 환경(Library) 분석
- 방법: requirements.txt 또는 Pipfile, pyproject.toml 존재 여부 확인.
- 분석 포인트: 자바의 pom.xml처럼 프로젝트에 사용된 외부 라이브러리 전체 리스트를 파악한다. 특히 메일 발송(smtplib, Flask-Mail), DB ORM(SQLAlchemy), 비동기 작업(Celery) 등 핵심 모듈의 버전을 확인하여 인프라와의 호환성을 검토한다.








