배포 전략과 환경 분리: ‘말 바꾸기’에 대처하는 개발서버

기획서보다 강력한 한마디, “일단 한번 보여주세요”

공공기관 SI 현장에서 홀로 개발을 도맡게 된 나에게 가장 큰 벽은 기술이 아니었다. 바로 ‘불확실성’이었다. 치밀한 요구사항 명세서에 따라 설계가 이루어지는 이상적인 환경은 없었다. 개발 지식이 부족한 담당자들은 화면을 직접 보기 전까지는 본인이 무엇을 원하는지조차 모르는 경우가 많았다.

“음, 그림으로 보니까 잘 모르겠는데… 실제로 눌러볼 순 없나요?”

나는 기능을 하나 만들 때마다 로컬(내 컴퓨터)에서 실행하고, 화면을 캡처하고, 빨간 펜으로 주석을 단 ‘보고서 파일(HWP/PPT)’을 매번 만들어 메일로 보냈다. 하지만 정적인 사진만으로는 담당자를 만족시킬 수 없었다.

“아, 아까 그 기능이 더 나은 것 같아요. 다시 되돌려주세요.” “이 버튼 누르면 어떻게 되나요? 영상으로 찍어주세요.”

어제는 A가 좋다더니 오늘은 B를 찾는다. 로컬에서 코드를 수정하고, 다시 캡처하고, 보고서를 쓰는 무한 루프. 개발 시간보다 보고서 쓰는 시간이 더 길어지는 주객전도의 상황이었다.

나는 절실하게 깨달았다. ‘내 컴퓨터 화면을 캡처해서 보내는 게 아니라, 담당자가 직접 들어와서 마음껏 눌러보고 깨트려볼 수 있는 놀이터(개발 서버)가 필요하다.’

백 마디 말(보고서)보다 한 번의 실행(개발 서버)이 낫다.

기회는 우연히 찾아온다: 버려진 서버의 재발견

마침 회사 분리 과정에서 운 좋게도 우리 팀에 할당된 유휴 서버(빈 컴퓨터)가 한 대 생겼다. 사양은 좋지 않았지만, 리눅스(Linux)가 깔려 있고 공인 IP가 연결된 소중한 자원이었다.

“팀장님, 이 남는 서버 제가 써도 될까요? 담당자 확인용 서버로 구축하고 싶습니다.”

허가는 떨어졌다. 나는 이 빈 깡통 서버에 지금까지 배웠던 모든 기술을 쏟아부어 ‘나만의 작은 인프라 제국’을 건설하기 시작했다. 이것은 지난 몇 달간의 삽질(Works on My Machine & The Pipeline 시리즈)을 총정리하는 작업이었다.

  1. GitLab (코드 저장소): 사설 저장소를 설치해 코드를 안전하게 관리한다.
  2. Docker Registry (이미지 저장소): 빌드된 도커 이미지를 저장할 우리만의 창고를 만든다.
  3. Portainer (컨테이너 관리): CLI가 무서운 나를 위해 도커를 GUI로 관리할 수 있게 한다.
  4. Nginx (웹 서버): 프론트엔드와 백엔드 요청을 교통정리 해주는 문지기를 세운다.
빈 서버 위에 지금까지 배운 모든 기술을 집약하여 인프라를 완성했다.

해결책: 환경의 완전한 분리 (Dev vs Prod)

이제 나에게는 두 개의 무대(Server)가 생겼다. 나는 담당자의 변덕을 수용하면서도 운영 서버를 지키기 위해 배포 전략을 분리했다.

  1. 개발 서버 (Development): ‘자유로운 실험실’
    • 담당자가 “그거 됐나요?” 물어볼 틈도 주지 않는다.
    • develop 브랜치에 코드가 올라오면 파이프라인이 즉시 돌아서 자동 배포된다.
    • 담당자는 언제든 개발 서버 URL로 접속해서 기능을 테스트하고 피드백을 준다.
  2. 운영 서버 (Production): ‘검증된 성역’
    • 담당자가 “이 기능으로 최종 확정입니다!”라고 선언했을 때만 배포된다.
    • master 브랜치로 코드를 합치고(Merge), 내가 GitLab에서 ‘배포 승인 버튼’을 눌러야만(Manual) 배포된다.

[Code Verification] 조건부 배포로 질서 잡기

GitLab CI는 브랜치별로 배포 경로를 다르게 지정할 수 있다. 이 전략을 코드로 구현하면 다음과 같다.

stages:
  - build
  - deploy

# 1. 빌드 (공통)
build_job:
  stage: build
  script:
    - ./gradlew build
    - docker build -t my-reg/app:$CI_COMMIT_SHA .
    - docker push my-reg/app:$CI_COMMIT_SHA

# 2. 개발 서버 배포 (자동)
# 'develop' 브랜치에 코드가 올라오면 묻지도 따지지도 않고 배포한다.
deploy_to_dev:
  stage: deploy
  script:
    - ssh user@dev-server "docker service update --image my-reg/app:$CI_COMMIT_SHA app-dev"
  only:
    - develop 

# 3. 운영 서버 배포 (수동 승인)
# 'master' 브랜치에 코드가 올라와도, 내가 버튼을 누르기 전까진 배포되지 않는다.
deploy_to_prod:
  stage: deploy
  script:
    - ssh user@prod-server "docker service update --image my-reg/app:$CI_COMMIT_SHA app-prod"
  only:
    - master
  when: manual # 핵심! '사람이 버튼을 눌러야만' 실행된다.

핵심 포인트:

  • only: - develop: 개발 브랜치는 무조건 자동 배포. 속도가 생명이다.
  • when: manual: 운영 브랜치는 사람이 한 번 더 확인하고 누르는 안전장치를 둔다.

마치며: 비로소 완성된 ‘나 혼자 산다’

이제 1인 개발자인 나에게도 완벽한 시스템이 갖춰졌다.

  1. 로컬(Local): 내 컴퓨터에서 기능 개발.
  2. 개발(Dev): git push 한 방이면 5분 뒤 개발 서버에 반영. 담당자가 직접 확인.
  3. 운영(Prod): 컨펌된 기능만 모아서 안전하게 클릭 한 번으로 배포.

입사 초기, 캡처 도구를 켜고 한글 파일에 주석을 달며 야근하던 나는 이제 없다. 담당자가 “이거 좀 바꿔주세요”라고 하면, “네, 수정해서 개발 서버에 올렸으니 확인해 보세요”라고 말하며 여유롭게 커피를 마실 수 있게 되었다. 시스템이 나를 대신해 일하고 있기 때문이다.

이것으로 길었던 [Re: Booting] 프로젝트의 막을 마친다. CS 기초부터 시작해 리눅스, 도커, 그리고 CI/CD 파이프라인까지. 맨땅에 헤딩하며 쌓아 올린 이 지식들이, 지금도 어딘가에서 홀로 고군분투하고 있을 ‘생계형 풀스택 개발자’들에게 작은 등불이 되기를 바란다.

“배포 전략과 환경 분리: ‘말 바꾸기’에 대처하는 개발서버”에 대한 1개의 생각

댓글 남기기