기술 스택 전환을 위한 대대적인 Django 서버 정리 작업을 작년(2021년) 중반기까지 진행한 이후, 회사 차원에서 필요로 하는 대규모 기능들을 잇따라 개발하게 되면서 기술 스택 전환을 위한 작업은 다소 지연되었습니다. 이 포스팅은 해당 기능들의 개발이 완료되었던 작년 말에 진행한 서버 배포 라인 변경 작업에 관해 기술합니다.
1. 기존 설계에 대한 번복 (Docker 도입의 계기)
Docker 기반의 ECS 배포 환경을 구축하게 된 계기를 설명하려면, 이 포스팅에서 기술 스택의 전환을 위해 설계했던 구조에 대한 번복이 먼저 필요할 것 같다. 여기서 설계했던 구조는 결과적으로 현재 구축한 ECS 배포 환경과 완전히 다르기 때문이다. 따라서 기존의 설계 중 어떤 부분이 왜 뒤집어졌는지 먼저 살펴보자.
1-1. API 서버 환경 별도 구축 (X) → 기존 환경 그대로 활용 (O)
처음에는 API 서버를 위해 기존 환경과는 별개의 환경을 구축해야 한다고 설계하였다. 그러나 기존 환경과 API 서버 환경 모두에 동일한 Django 코드를 배포하는 것은 구조상으로도, 시간상으로도 비효율적이라고 판단했다. 또한 API 서버를 위한 Django 설정 파일을 따로 만들 필요도 딱히 없는 게, 새로 구현할 API들은 DRF를 이용할 계획이기 때문에 DRF를 위한 설정을 기존의 설정 파일에 추가해주기만 하면 되는 문제였다. 그리고 사실 생각해 보면 이미 기존 서비스에도 몇몇 API가 구현되어 있고, 이를 위해 굳이 별도의 환경을 사용하진 않았었다. 따라서 API 서버를 위한 환경을 따로 구축하지 않고, 기존의 환경을 그대로 활용하는 것으로 결론을 내렸다. 기존의 환경을 활용하면 동일한 도메인을 사용한다는 의미이므로 CORS나 쿠키 등의 까다로운 문제를 신경 쓸 필요가 없어지는 장점도 있다.
1-2. 프론트 엔드 서버 환경 별도 구축 (X) → 기존 환경 그대로 활용 (O)
마찬가지로 처음에는 프론트 엔드 서버를 위한 환경도 따로 구축해야 한다고 설계하였다. 그러나 별도의 환경을 구축하면 별도의 도메인을 사용하게 되므로 CORS, 쿠키, 웹 스토리지 등의 측면에서 신경 쓸 부분이 많아진다. 또한 도메인이 다르면 기존 환경의 페이지 및 새 환경의 페이지 간의 링크를 구현할 때 별도로 신경 써줘야 하는 부분이 있는 것도 다소 부담스러웠다. 따라서 프론트 엔드 서버를 위한 환경을 따로 구축하지 않고, 이 역시 기존의 환경을 그대로 활용하는 것으로 결론을 내렸다.
결국, API 서버와 프론트 엔드 서버를 모두 기존의 환경에서 처리하도록 결론을 내린 셈이다. 사실 이러한 생각을 하게 된 근본적인 계기는 여러 개의 환경을 추가 구축할 때의 비용에 대한 걱정이었다. 아무래도 여러 개의 AWS Elastic Beanstalk 환경을 갖추는 것은 큰 비용이 들기 때문이다.
1-3. AWS Elastic Beanstalk 활용 (X) → Docker 활용 (O)
결론적으로 API 서버와 프론트 엔드 서버를 모두 기존의 환경에서 처리하도록 결론을 내렸는데, 사실 기존에 사용하던 AWS의 Elastic Beanstalk(이하 EB)에서는 하나의 환경에 하나의 플랫폼만 지정할 수 있게 되어 있다. 따라서 두 개의 플랫폼(Python, Node.js)을 동시에 구축해야 하는 우리로서는 큰 문제가 아닐 수 없었다.
이때 떠올린 것이 바로 Docker이다. 사실 예전부터도 팀 내부에서 개발 환경의 구축이 까다롭다는 이유로 Docker의 도입을 언급한 적이 많았지만, 늘 우선순위에 밀려 실천에는 옮기지 못했었다. 그런데 이번 기회에 서버 배포 라인을 Docker 기반으로 변경한다면, 개발 환경 구축도 굉장히 빠르고 간편해질 뿐만 아니라 앞서 언급한 문제도 함께 해결할 수 있었다. Docker를 활용한다면 하나의 환경(EC2)에 여러 컨테이너로 여러 플랫폼을 구축하는 것이 쉽기 때문이다. 더군다나 안 그래도 PostgreSQL, Django, Python 등의 버전 업그레이드가 필요한 시점이었기 때문에 Docker를 도입한다면 각 기술의 버전 업그레이드도 독립적이고 간편한 방식으로 진행할 수 있게 된다.
한편 Docker를 활용한다면 웹 서버의 커스터마이징도 EB보다 간편해지기 때문에, 기존의 페이지와 새로 개발할 페이지 간의 링크를 구현하는 것도 쉬워진다. 새로 개발할 페이지의 URL은 특정 prefix로 시작하게끔 약속하고, 웹 서버에서 해당 prefix로 시작하는 URL에 대한 요청은 프론트 엔드 서버로 포워딩하도록 설정하면 되기 때문이다.
따라서 우리는 드디어 본격적으로 Docker를 도입하기로 결심하였고, 여러 조사 끝에 Docker 기반의 배포 환경을 구축하기 위한 서비스로 AWS의 Elastic Container Service(이하 ECS)를 선택하게 되었다. 어차피 EB는 이미 유행이 꽤나 지난 서비스였기 때문에, 이는 시대의 흐름을 타는 트렌디한 결정이기도 하였다.
1-4. 프로젝트를 둘로 분리 (X) → 하나의 프로젝트로 관리 (O)
Docker를 기반으로 하나의 환경에서 모든 것을 처리하도록 결정하고 보니, 굳이 프로젝트를 둘로 분리할 필요성이 느껴지지 않았다. 하나의 프로젝트로 관리하더라도, 각 플랫폼의 코드를 별도의 Docker 이미지로 빌드하고 이것들을 가지고 각각의 컨테이너를 생성하면 되기 때문이다. 오히려 프로젝트를 둘로 분리하면 각각에 대해 ECS 배포 방식도 따로 설계해줘야 하는 번거로움이 있을 것이고, 두 개의 프로젝트를 왔다 갔다 해야 하는 개발자분들도 불편함을 느낄 것이다. 따라서 프로젝트는 굳이 둘로 분리하지 않고 기존의 프로젝트를 그대로 사용하기로 하였다.
2. Docker 기반의 ECS 배포 환경 구축
보안상의 이유로, ECS 배포 환경을 어떻게 구축했는지에 대한 상세한 내용까지는 여기에 기술하지 않는다. 그러나 기본적인 ECS의 개념 및 배포 방법에 대한 설명은 이 포스팅에, 그리고 ECS 배포 과정에서 겪었던 여러 트러블슈팅에 대한 기록은 이 포스팅에 기술되어 있으니 필요하다면 읽어보기 바란다.
결론적으로 우리가 구축한 ECS 배포 환경의 구조는 대략 다음과 같다.
여기서 주목할 만한 부분은, 아직까지는 React를 도입하지 않았다는 것이다. 그러나 어떻게 React를 도입하면 될지 그림만 보고도 쉽게 추측이 가능하다. 각각의 Task에 Django 컨테이너와 Nginx 컨테이너가 존재하는데, 여기에 React 컨테이너를 하나 추가하고 특정 prefix로 시작하는 URL에 대한 요청은 Nginx가 React 컨테이너로 포워딩해주도록 설정하는 것이다. 이렇듯 새로운 플랫폼의 앱을 어렵지 않게 추가할 수 있다는 것이 곧 Docker의 강점이다. 이로써 React를 도입하기 위한 기본적인 발판을 대부분 마련하였다.