1. 마이그레이션 파일은 매번 생성되는데…
데이터베이스의 마이그레이션 파일은 모델에 변동 사항이 생길 때마다 생성된다. 물론 한 작업 내에서 여러 개의 변동 사항이 생기는 거라면 그것들을 전부 하나의 마이그레이션 파일에 포함시킬 수 있다. 그러나 이미 배포가 된, 지난 작업에서 생긴 변동 사항을 이번 작업에서 새로 생길 변동 사항과 묶는 것은 허용되지 않는다. 다시 말해서, 이미 데이터베이스에 적용된 마이그레이션 파일을 수정하면 안 된다는 것이다.
왜냐하면 데이터베이스상에는 각 마이그레이션 파일의 적용 여부와 관련한 로그들이 저장되어 있기 때문이다. 이로 인해 특정 마이그레이션 파일의 내용을 수정하더라도 그것은 이미 데이터베이스에 적용된 마이그레이션 파일로 취급되기 때문에 추가로 작성해준 변동 사항이 데이터베이스에 적용되지 않게 된다.
이러한 이유로 마이그레이션 파일은 매 작업마다 새로 생성할 수밖에 없다. 또한 개발자가 한 작업 내에서 생기는 모든 변동 사항을 하나의 마이그레이션 파일에 묶도록 매번 꼼꼼히 신경 써주는 것도 어렵다. 이러한 현상이 반복되다 보면 마이그레이션 파일들이 무수히 많이 쌓이게 된다. 실제로 오픈갤러리 프로젝트도 마이그레이션 파일 정리 작업을 진행하기 전에는 몇 백 개의 마이그레이션 파일들이 있었다.
2. 마이그레이션 파일이 많아지면?
그런데 마이그레이션 파일들이 많은 것이 무엇이 문제일까? 그냥 단순히 파일 개수가 많아서 불편한 걸까? 그것은 아니다. 일반적으로 CI(Continuous Integration)는 깨끗한 상태의 컨테이너에 해당 프로젝트를 위한 모든 환경을 구축하면서 개시가 된다. 그리고 그 과정은 해당 프로젝트 내의 모든 마이그레이션 파일들을 데이터베이스에 적용시키는 과정까지도 포함한다. 따라서 마이그레이션 파일들이 많을수록 CI는 당연히 느려진다. 이것이 문제의 핵심이다.
더군다나, CD(Continuous Deployment)는 일반적으로 CI 이후에 진행되기 때문에 CI가 느려지면 배포에 걸리는 시간도 늘어나는 셈이 된다. 개인 프로젝트라면 크게 상관이 없겠지만, 한 기업의 서비스 측면에서 배포의 시간이 늘어난다는 것은 절대로 좋은 현상이 아니다. 배포에 걸리는 시간이 길수록 서비스의 안정성은 떨어지고 사용자의 만족도도 떨어질 수밖에 없기 때문이다. 따라서 마이그레이션 파일들의 개수를 줄여서 테스트와 배포에 걸리는 시간을 줄이는 게 좋다. 이번 포스팅에서는 오픈갤러리가 어떻게 해서 몇 백 개의 마이그레이션 파일들을 단 몇 개의 마이그레이션 파일들로 줄였는지에 대해 그 방법을 한 번 소개하도록 할 것이다.
3. 마이그레이션 관련 명령어 (showmigrations, migrate --fake)
오픈갤러리가 마이그레이션 파일들의 개수를 줄인 방법을 소개하기에 앞서, 기본적으로 알고 있어야 하는 마이그레이션 관련 명령어 두 가지의 동작 원리를 간단히 소개하겠다. 여기서 설명하는 내용을 이해해야 뒤에서 소개할 방법을 완전히 이해할 수 있기 때문이다.
3-1. showmigrations
이는 현재 존재하는 각각의 마이그레이션 파일들에 대해 그것에 대응하는 로그가 데이터베이스에 존재하는지 확인하는 명령어이다. 실제로 각 마이그레이션 파일은
python manage.py migrate
명령어에 의해 데이터베이스에 적용되는 순간 해당 마이그레이션 파일에 대한 로그도 데이터베이스에 저장된다. 이 명령어는 단순히 그러한 로그의 존재 여부를 확인하는 원리로 동작하는 것이다.3-2. migrate --fake
fake라는 단어가 의미하는 것처럼, 이는 인자로 명시한 마이그레이션 파일까지 데이터베이스에 적용이 된 것처럼 '속인다'는 의미를 담고 있다. 조금 더 구체적으로 설명하면 다음과 같다.
특정 마이그레이션 파일을 대상으로 fake 마이그레이션을 수행하면, 해당 마이그레이션 파일까지 데이터베이스에 적용된 것처럼 데이터베이스의 로그들을 수정한다. 즉, fake 마이그레이션의 대상이 되는 마이그레이션 파일 이후의 마이그레이션 파일들에 대응하는 로그들은 데이터베이스에서 삭제하는 것이다.
참고로, fake 마이그레이션을 수행할 때 더 이상 존재하지 않는 마이그레이션 파일들에 대응하는 로그들은 삭제되지 않는다. 정확히는, 현재 존재하지 않는 마이그레이션 파일들에 대한 로그들은 지울 수가 없는 것이다.
아래부터는 본격적으로 오픈갤러리 프로젝트에서 수백 개의 마이그레이션 파일들을 단 몇 개의 마이그레이션 파일들로 줄이기 위해 진행했던 작업들의 절차를 하나씩 설명한다.
4. Step ① 실서버 배포 전 로컬에서 미리 진행해야 하는 작업
먼저, 실서버에 배포하기 전에 먼저 로컬에서 다음과 같은 작업들을 진행하였다.
4-1. 각 앱의 마이그레이션 로그 전부 삭제
각 앱의 마이그레이션 로그들을 전부 삭제하였다. 마이그레이션 파일 이름 대신에
zero
를 사용하면, 마이그레이션 파일을 하나도 적용하지 않은 것처럼 로그들을 수정한다는 의미가 된다. (사실 이는 필수 과정이 아니므로 넘어가도 될 듯하다. 단순히 불필요한 로그들을 삭제하고 싶은 강박 때문에 진행한 작업이다.)4-2. 마이그레이션 파일 전부 삭제
기존 마이그레이션 파일들을 전부 삭제하였다. 단 몇 개의 마이그레이션 파일들만을 새로 생성하는 것이 목적이기 때문이다. 다음과 같이
rm -rf
명령어를 이용하면 모든 마이그레이션 파일들을 일괄 삭제할 수 있다.4-3. 각 앱의 models.py 파일 수정
다른 앱의 모델을 참조하는
ForeignKey
, OneToOneField
, ManyToManyField
타입의 필드들을 주석 처리하였다. 또한 다대다 관계에서 중간 매체 역할을 수행하는 Through
모델의 unique_together
옵션과 같이 해당 타입의 필드들을 명시하고 있는 부분들도 주석 처리하였다. 이는 앱 간의 순환적 의존성이 발생하지 않도록 하기 위함이었다.4-4. 1차 마이그레이션 파일 생성
각 앱에 대해 마이그레이션 파일을 생성하였다.
4-5. 앞서 처리한 주석 전부 해제
4-3 과정에서 처리해둔 모든 주석들을 전부 해제하여 원래 상태로 되돌려 놓았다. 이제는 각 모델에 대한 테이블이 모두 생성되어 순환적 의존성이 발생하지 않을 것이기 때문이다.
4-6. 2차 마이그레이션 파일 생성
다시 한번 각 앱에 대해 마이그레이션 파일을 생성하였다. 이때 디폴트 값 설정과 관련된 물음에는
1
과 None
을 차례로 입력해줬다. 어차피 필드 값들이 잘 채워져 있는 데이터베이스를 그대로 사용할 것이기 때문이다(마이그레이션 파일만 조작하고 있는 것일 뿐).4-7. fake 마이그레이션
fake 마이그레이션을 통해 각 앱의 마이그레이션 로그들을 수정해줬다. 지금 존재하는 모든 마이그레이션 파일들이 데이터베이스에 적용되었다고 속이기 위함이다. 사실 결론만 놓고 보면 지금의 모델 코드 상태와 데이터베이스의 상태가 동일하기 때문에 이렇게 속여도 문제가 없는 것이다. 단, 이때는
python manage.py showmigrations
명령어를 이용하여 X가 표시되지 않은 마이그레이션 파일이 있는 모든 앱들을 찾아 명령어에 포함시켜줘야 했다. (4-1 과정을 진행한 결과로, 커스텀 앱이 아닌 내장 혹은 외장 패키지의 앱도 fake 마이그레이션이 일부 적용된 듯하였기 때문이다. 4-1 과정을 진행하지 않았다면 이렇게까지는 안 해도 될 것 같은데 실험해보지는 않았다.)4-8. 마이그레이션 테스트
이제
python manage.py migrate
명령어를 실행하여 모든 것이 잘 되었는지 테스트해보았다. 적용시킬 마이그레이션 파일들이 없다고 출력된 것을 보고(No migrations to apply), 문제가 없음을 확인하였다.5. Step ② 실서버 배포 시 진행해야 하는 작업
실서버 배포일이 되었을 때는 다음과 같은 작업들을 진행하였다.
- 실서버 마이그레이션 파일들을 백업해뒀다. 혹시 모르기 때문이다.
- Step ① 과정의 4-7 단계가
python manage.py migrate
명령어보다 먼저 실행되도록 배포 스크립트의 내용을 수정하였다. 배포 과정에서 fake 마이그레이션을 수행해야 하기 때문이다.
- 배포를 진행하였다. (1차 배포)
- 배포 스크립트의 내용을 원래 상태로 되돌려 놓았다. 즉, fake 마이그레이션 명령어를 삭제하였다.
- 다시 배포를 진행하였다. (2차 배포)
이와 같은 과정으로 마이그레이션 파일들의 개수를 각 앱별로 1~2개만 존재하는 정도로 줄일 수 있게 되었다. 정석적인 방법은 아닐 수 있겠지만 이 방법을 고안하기 위해 치열하게 고민을 하기도 했고, 나름 철저하게 잘 완수한 것 같아 굉장히 보람찬 경험이었다.