배포가 바꾸는 것 (수정중)
서버 한 대 안 에서 서버 안 부품(Apache·gunicorn·Django·PostgreSQL)을 봤습니다. 이 강의는 그 부품들 위에서 "배포한다"는 한마디가 실제로 일으키는 변화를 하나씩 엽니다. 배포 절차를 외우기 전에 "그 절차가 왜 그 순서인지"를 이해하는 시간입니다.
1. 배포가 맞추는 다섯 가지
흔히 "배포했어 / prod 에 올렸어"라고 한 마디로 말하지만, 서버에서 실제로 일어나는 건 다섯 가지를 새 버전에 맞추는 작업입니다.
| # | 무엇을 | 어떻게 (명령) | 서버 안의 어느 부품 |
|---|---|---|---|
| ① | 코드(디스크) | git checkout <태그> / git pull |
Django 코드 |
| ② | 실행 프로세스 | systemctl restart <proj>-prod.service |
gunicorn |
| ③ | DB 구조 | migrate |
PostgreSQL |
| ④ | 정적 파일 | collectstatic |
Apache 가 주는 /static/ |
| ⑤ | 라이브러리 | pip install -r requirements.txt |
(필요할 때만) |
이 다섯이 서로 어긋나면 사고가 납니다. 그래서 배포 절차는 이 다섯을 올바른 순서로 맞추는 것입니다. 먼저 큰 그림입니다.
그림: 배포 = ① 코드(git checkout) ② gunicorn 재시작 ③ DB migrate ④ collectstatic ⑤ pip 를 새 버전에 맞추는 일. 재시작 안 하면 옛 코드 유지, migrate 는 되돌리기 어려움. 데이터·도메인·세션은 그대로 유지.
2. ① 코드 교체
가장 먼저, 서버 디스크(/home/ubuntu/<proj>/src)의 코드 파일을 새 버전으로 바꿉니다. git checkout <태그> 나 git pull 이 하는 일이 이것입니다. GitHub 에 올라간 새 코드를 서버로 당겨와 파일을 교체합니다.
하지만 여기서 가장 흔한 오해가 나옵니다. 👇
3. ② 프로세스 재시작
핵심: gunicorn 은 시작할 때 코드를 메모리에 한 번 읽어 들고, 그 뒤로는 메모리에 든 코드로 계속 일합니다. 그래서 디스크 파일만 새로 바꿔도, 돌고 있는 gunicorn 은 여전히 옛 코드를 실행합니다.
새 코드를 실제로 반영하려면 gunicorn 을 재시작해야 합니다.
sudo systemctl restart popax-prod.service
이 순간 gunicorn 워커들이 새로 떠서 디스크의 새 코드를 다시 읽어 들입니다. 그제서야 사용자가 새 버전을 보게 됩니다.
- 그래서 "분명히 배포(코드 교체)했는데 안 바뀌었어요"의 90%는 재시작을 안 했거나, 재시작이 실패한 경우입니다.
- 백그라운드 작업(celery·huey, 예:
popax-prod-huey.service)이 있으면 그것도 재시작해야 그쪽 코드가 새로 반영됩니다. - 정적 파일(④)만 바꾼 배포라면 gunicorn 재시작이 굳이 필요 없을 수도 있습니다. 무엇이 바뀌었느냐에 따라 무엇을 재시작할지 정해집니다.
4. ③ DB 구조 정렬 (migrate)
코드(Django 모델)가 바뀌면서 데이터베이스의 구조(표·컬럼) 자체를 바꿔야 할 때가 있습니다. 예를 들어 "회의록에 '작성일' 항목을 새로 추가"하면 DB 표에 새 컬럼이 필요합니다. 이 구조 변경을 적용하는 게 마이그레이션입니다.
<PROJ>_PRODUCTION=True python manage.py migrate --noinput
왜 가장 조심스러운가:
- 실서버 데이터 위에서 구조를 바꿉니다.
popax_prod에는 진짜 사용자 데이터가 들어 있습니다. 구조를 잘못 바꾸면 데이터가 깨지거나 손실될 수 있습니다. - 되돌리기가 어렵습니다. 코드는 옛 태그로
checkout하면 곧장 롤백되지만, 마이그레이션은 한 번 적용되면 자동으로 깔끔히 되돌려지지 않습니다(특히 컬럼 삭제·데이터 변환). - 그래서 마이그레이션은 절대 자동·무인으로 돌리지 않습니다. 배포 절차에서 migrate 는 사람이 목록을 확인하고 백업·영향을 점검한 뒤 직접 실행하는 단계로 둡니다(휴먼 게이트).
기억할 것: 코드 교체는 쉽게 되돌릴 수 있지만, DB 마이그레이션은 그렇지 않습니다. 그래서 배포에서 가장 신중해야 하는 단계입니다.
5. ④ 정적 파일 · ⑤ 라이브러리
④ collectstatic: CSS·JS·이미지를 바꿨다면, 서버 한 대 안 에서 본 Apache 가 주는 폴더(popax_static_cdn/static/)에 새 파일을 모아 둬야 합니다.
<PROJ>_PRODUCTION=True python manage.py collectstatic --noinput
이걸 빠뜨리면 코드는 새 버전인데 화면 스타일(CSS)만 옛날 것으로 보이는 어긋남이 생깁니다.
⑤ pip install: requirements.txt 가 바뀌었을 때(새 라이브러리 추가 등)만 합니다. 안 바뀌었으면 건너뜁니다.
pip install -r requirements.txt # requirements 바뀐 경우에만
6. 안 바뀌는 것
무엇이 안 바뀌는지를 아는 것도 똑같이 중요합니다.
- 데이터(행): 사용자·회의록 같은 내용물은 그대로입니다. 배포는 "코드·구조"를 바꾸지, "쌓인 데이터"를 지우지 않습니다(서버 한 대 안 의 "코드와 데이터 분리"). 단, ③ 마이그레이션이 데이터를 변환하도록 짜였다면 그건 의도된 변경입니다.
- 도메인·IP:
meetings.popupstudio.ai와52.79.132.206은 배포와 무관하게 그대로입니다(요청이 서버에 닿는 경로). - 로그인 세션: 대개 유지됩니다(세션은 DB·쿠키에 있고 코드 교체와 분리).
그래서 보통 사용자는 잠깐의 재시작(수 초) 외에는 배포를 눈치채지 못합니다.
마무리
- 핵심 정리: "배포"는 ① 코드(
git checkout) ② 프로세스 재시작(systemctl restart, 안 하면 gunicorn 이 옛 코드 유지!) ③ DB 구조(migrate, 가장 조심, 되돌리기 어려움) ④ 정적 파일(collectstatic) ⑤ 라이브러리(pip, 필요시)를 새 버전에 맞추는 일입니다. 데이터·도메인·세션은 그대로 남습니다. 다섯이 어긋나면 사고가 나므로 순서가 중요합니다. - 주의 사항: ① 재시작 빠뜨림: "배포했는데 안 바뀜"의 최대 원인으로, 코드 파일만 바꾸고 gunicorn 을 안 재운 경우입니다. ② collectstatic 빠뜨림: 코드는 새 버전인데 CSS 만 옛날입니다. ③ migrate 를 가볍게 봄: 실데이터 위 구조 변경이라 백업·영향 확인 없이 돌리면 위험합니다. ④ 마이그레이션이 필요한데 코드만 배포: 새 코드가 없는 컬럼을 찾아 에러가 납니다(코드와 DB 구조는 짝으로 맞춰야 합니다).
- 다음 시간: 여기까지 시스템이 어떻게 구성되고 도는지 봤습니다. 이제 이 위에서 Claude Code 로 실제로 개발하는 법(스킬·에이전트·훅)을 살펴봅니다.
- 자습 권장: 최근 배포 하나를 떠올려(또는
git log로 골라) "이 변경은 ①~⑤ 중 무엇을 건드렸을까?"를 분류해 봅니다. 모델을 바꿨으면 ③ migrate 필요, CSS 만 고쳤으면 ④ 만, 이런 식입니다. 그리고 prod 박스에서journalctl -u <proj>-prod.service -n 30으로 마지막 재시작(②)이 언제였는지 확인해 봅니다.