Python 2, Django 1, 100만 줄의 레거시 코드. 그리고 매달 이중으로 나가는 클라우드 비용.
데이트팝이 대규모 마이그레이션을 결심한 배경이에요. 단순히 인프라를 옮기는 게 아니라, Python 2에서 3.11로, Django 1에서 4로 전면 현대화하는 작업이었죠.
안녕하세요, 데이트팝 백엔드 개발자 김용범이에요. 이 글에서는 백엔드 개발자 2명이 100만 줄의 레거시 코드를 어떻게 전환하고 있는지, 그 과정에서 마주한 기술적 의사결정들을 공유해보려고 해요.
왜 마이그레이션을 결정했나
- 이중 운영의 비용 부담
데이트팝은 GCP와 AWS를 동시에 운영하고 있었어요. 메인 애플리케이션은 GCP의 Google App Engine에서 구동되고, 일부 서비스는 AWS에서 운영되는 구조였죠. 두 클라우드를 동시에 운영하다 보니 비용이 이중으로 나가고, 인프라 관리도 복잡해지는 문제가 있었어요.
- GAE의 양날의 검
GAE는 yaml 파일 하나로 배포가 가능하고, 인프라 관리 없이 트래픽을 처리할 수 있다는 점에서 초기 스타트업에게 매력적인 선택지였어요. 그런데 이 유연함이 시간이 지나면서 오히려 문제가 됐어요.
오토스케일링이 너무 관대하게 작동했거든요. 트래픽이 조금만 튀어도 인스턴스가 확 늘어나고, 줄어드는 건 느렸어요. 실제로 필요한 것보다 훨씬 많은 인스턴스가 유지되면서 비용 누수가 생겼죠. - 레거시의 무게
초창기에 구축된 서버는 Python 2와 Django 1으로 작성되어 있었어요. Python 2는 2020년에 공식 지원이 종료됐고, Django 1도 보안 업데이트가 중단된 지 오래였어요. 새로운 라이브러리를 도입하고 싶어도 Python 2를 지원하지 않는 경우가 대부분이었고, 개발자 채용 공고에 Python 2라고 쓰기도 부담스러운 상황이었어요.
이런 배경에서 저희는 AWS 단일화 + 레거시 현대화를 동시에 진행하기로 결정했어요.
무엇을 전환했나

기존 GCP 서비스들을 AWS로 매핑하는 과정에서 단순 1:1 대체가 아니라, 각 서비스의 특성과 향후 확장성을 고려해서 선택했어요.
| GCP | AWS | 선택 이유 |
|---|---|---|
| GAE | ECS (EC2) | MSA 전환 대비, 컨테이너 기반 관리 |
| Memcache | ElastiCache (Redis) | 캐싱 외 세션, 큐, Pub/Sub 확장성 |
| Cloud SQL | Aurora DB | |
| Cloud Storage | S3 | |
| DataStore | DynamoDB | |
| TaskQueue | Celery + ECS | Lambda 대비 코드 관리 용이 |
| CronJob | EventBridge + Lambda |
- ECS (EC2 Provider)를 선택한 이유
컨테이너 오케스트레이션 선택지는 여러 가지가 있었어요. EKS, ECS Fargate, ECS EC2, 심지어 GAE와 유사한 Elastic Beanstalk도 검토했죠.
ECS EC2를 선택한 이유는 두 가지예요.
- 첫째, MSA 전환 준비예요. 저희는 향후 서비스를 분리할 때 컨테이너 단위로 관리하고 싶었어요. ECS는 이를 위한 적절한 복잡도를 제공해요. EKS도 고려했지만, 현재 팀 규모 대비 오버 엔지니어링이라고 판단했어요.
- 둘째, 비용이에요. Fargate는 관리 편의성은 좋지만, 동일 워크로드 기준으로 EC2보다 비용이 높아요. 저희는 리소스 사용량을 직접 제어하면서 비용을 최적화하는 방향을 선택했어요.
- Memcache 대신 Redis를 선택한 이유
기존 GAE의 Memcache는 단순 캐싱 용도로만 사용하고 있었어요. AWS로 이관하면서 ElastiCache의 Memcached를 선택할 수도 있었는데, 저희는 Redis를 선택했어요.
Redis는 단순 캐싱 외에도 세션 관리, 메시지 큐, Pub/Sub 패턴을 지원해요. 당장은 캐싱만 사용하더라도, 서비스가 확장될 때 별도의 인프라 추가 없이 Redis 하나로 여러 요구사항을 해결할 수 있겠다고 생각했어요. - Lambda에서 Celery로 전환한 이유
처음에는 GAE의 TaskQueue를 SQS + Lambda 조합으로 대체하려고 했어요. 비동기 작업이 필요할 때 SQS에 메시지를 넣고, Lambda가 이를 처리하는 구조였죠.
그런데 운영하면서 문제가 드러났어요. Lambda 함수가 계속 늘어났거든요.
비동기 작업의 종류가 다양해질수록 각각에 대응하는 Lambda 함수를 만들어야 했어요. 처음에는 몇 개 안 됐는데, 어느새 관리해야 할 함수가 꽤 많아졌어요. 배포 파이프라인도 복잡해지고, 로직이 여러 Lambda에 분산되면서 전체 흐름을 파악하기도 어려워졌죠.
결국 Celery로 전환했어요. Celery Worker를 ECS에 배포하고, 비동기 작업들을 하나의 코드베이스에서 관리하게 됐어요. Task 추가가 함수 하나 추가하는 것으로 끝나니 관리 부담이 확 줄었어요.
어떻게 진행했나
- 코드 먼저, 인프라는 그 다음
마이그레이션 순서를 정할 때 두 가지 선택지가 있었어요. 인프라를 먼저 옮기고 코드를 맞추거나, 코드를 먼저 현대화하고 인프라를 옮기거나.
저희는 코드 먼저 전략을 선택했어요.
Python 2 코드를 그대로 AWS로 옮겨봤자, 어차피 Python 3로 전환해야 했거든요. 순서가 바뀌면 같은 코드를 두 번 건드려야 할 수도 있었어요. 그래서 먼저 Python 3.11 + Django 4로 코드를 현대화한 다음, AWS 인프라로 이관하는 순서로 진행했어요.
- 주요 난관과 해결 방법
라이브러리 호환성이 가장 큰 문제였어요.
Python 2에서만 동작하는 라이브러리들이 있었는데, 어떤 건 Python 3 버전이 아예 없었어요. 이런 경우 두 가지 방법으로 해결했어요. 비슷한 기능을 제공하는 대체 라이브러리를 찾거나, 해당 기능만 직접 구현하거나. 대체 라이브러리로 갈 때는 API가 달라서 호출부를 전부 수정해야 하는 경우도 있었어요.
Python 2 전용 문법도 곳곳에 숨어 있었어요.
print문은 찾기 쉬웠는데, unicode/str 처리가 까다로웠어요. Python 2에서는 문자열 기본 타입이 byte였고, Python 3에서는 unicode가 기본이거든요. 인코딩 관련 코드를 하나하나 확인하면서 수정해야 했어요.
Django 1 → 4 전환도 만만치 않았어요.
Django는 버전 간 breaking change가 많은 프레임워크예요. 1 → 2에서 URL 설정 방식이 바뀌었고, 2 → 3에서 비동기 지원이 추가되면서 일부 API가 변경됐어요. 공식 릴리즈 노트를 버전별로 하나씩 확인하면서 영향받는 코드를 수정했어요.
- ALB 경로 패턴을 활용한 롤백 전략
트래픽 전환은 신중하게 진행했어요. 한 번에 모든 트래픽을 새 서버로 보내는 건 리스크가 너무 컸거든요.
AWS ALB(Application Load Balancer)의 경로 패턴 기능을 활용해서, 특정 엔드포인트 그룹별로 트래픽을 새로운 서버로 보낼 수 있게 구성했어요. 예를 들어
/api/users/*경로만 먼저 새 서버로 보내보고, 문제가 없으면 다음 그룹으로 넘어가는 방식이었죠.문제가 발생하면 해당 경로의 트래픽만 다시 기존 서버로 돌리면 돼요. 전체 서비스에 영향을 주지 않고 부분적으로 롤백할 수 있는 구조예요. 이 방식 덕분에 안정적으로 점진적 전환을 진행할 수 있었어요.
현재 상태와 성과
현재 마이그레이션은 대부분 완료된 상태예요. 대부분의 서비스가 AWS에서 운영되고 있고, 남은 부분들을 순차적으로 전환하고 있어요.
가장 큰 성과는 비용 40% 절감이에요. GCP와 AWS 이중 운영을 제거하고, ECS에서 리소스 사용량을 직접 제어하면서 달성한 수치예요.
코드 측면에서는 Python 3.11과 Django 4라는 현대적인 스택 위에서 개발할 수 있게 됐어요. 새로운 라이브러리 도입이 자유로워졌고, 채용 공고에 Python 3이라고 쓸 수 있게 된 것도 나름의 성과예요.
마치며
레거시 마이그레이션은 많은 개발자들이 꺼리는 작업이에요. 새로운 기능을 만드는 것처럼 눈에 보이는 성과가 바로 나오지 않고, 기존 시스템을 깊이 이해해야 하며, 항상 "잘 돌아가던 걸 왜 건드려?"라는 리스크를 안고 있으니까요.
하지만 이 과정에서 얻는 것도 있었어요. 서비스의 전체 구조를 그림으로 그릴 수 있게 됐고, 수년간 쌓인 비즈니스 로직을 이해하게 됐어요. 무엇보다 "이건 건드리면 안 돼"라는 영역이 줄어들었어요.
결국 마이그레이션의 핵심은 얼마나 안전하게 점진적으로 할 수 있느냐였어요. ALB 경로 패턴 롤백 전략이 없었다면 훨씬 힘들었을 거예요.
데이트팝은 이번 마이그레이션을 기반으로 향후 MSA 전환을 준비하고 있어요. 비슷한 고민을 하고 계신 분들께 조금이나마 도움이 됐으면 좋겠어요.
읽어주셔서 감사합니다.
