배포 과정은 개념만 탑재하고
세부적인 스크립트 작성이나, 내용은 전부 제미나이한테 try & error로 물어보고,
현재 안되는 예상 원인을 추론하고, 물어보면서
받은 답변을 적용하면서 진행함.
일단. 리눅스 스크립트 자체를 짜는 방법이나, cert bot을 이용해 비대화형으로 ssl 인증서를 발급받는 방법은
아예 몰랐던 부분이었고,
배포, 개발, 로컬 테스트의 ssl 인증 처리를 전부 동일하게 처리해서,
배포를 위해 개발, 로컬 테스트 환경을 동일하게 맞추는것이 중요한거지,
빌드 및 배포 과정의 기본 개념을 탑재하기 위해
리눅스 쉘스크립트나,
도커 컨테이너 빌드를 위해서 어떤 이미지를 도커 허브에서 써야하는지 알아야한다는 지식은
전부 제미나이에게 맡김.
지금 당장은 공통적인 프로세스를 진행하는것이 우선순위가 높고,
현재의 문제를 해결하기 위해, AI에게 맡겨서, 생기는 지식의 부채가 그렇게 큰 비용이 아닌 것으로 예상되서,
허비되는 시간의 소모를 줄이도록 노력함.
누군가 나에게, 아무 내용도 없는 파일을 주고, A to Z 로 전부 직접 작성하라고 한다면, 못하겠지만,
이미 있는 내용을 수정하라고 한다면, 제미나이와 답변을 주고 받으면서,
어느정도 기초적인 스크립트는 짤 수 있는 수준인 것 같음..
지금도 머리 속이 꼬이고 꼬여서, 제미나이에게 내용 정리까지 맡김....
배포 프로세스를 간단한게 정리를 하면
개발자가 배포를 할때 전부 손수 작성해야하는 명령어는 gitlab-ci 를 통해서 진행할 수 있고,
사전에 진행되어야하는 동작들을 작성한 쉘 스크립트를 (이미 동작중인 이전 버전의 동일한 이름의 컨테이너 정리, )
gitlab-ci 를 통해서 배포할때마다 동작시키고,
이 스크립트는 docker-compose 파일을 실행시키고,
docker-compose는 dockerfile을 실행시킴.
대충 계층적으로는 이렇게 작성할 수 있는데....
.gitlab-ci.yml -> deploy.sh -> docker-compose.yml -> .dockerfile
gitlab-ci.yml에 script를 적는 부분이 있으니,
어차피 각각 script가 실행되려면,
입력한 내용이 오류없이 수행되어야 다음줄이 수행되는 것 같은데,
deploy.sh 에 적은 내용을 전부 분리해서.
.gitlab-ci.yml에서 개별적으로 분리해서 돌리는게 더 나을 것 같다는 생각이 들기도하고...
제미나이 추천으로는 하나의 스크립트로 전부 진행되게 하는걸 추천함.
(깃랩 ci, 배포 프로세스는 오늘까지 9일 째 진행중이고, 10일안에 끝낸다고 하니까, 시간 최소화를 위해서 deploy.sh 만 이용해서 완벽하게 끝내는걸 추천함)
🚀 배포 프로세스 총정리 (The Legend of 8 Days)
1. 초기 상태와 목표
- 목표: 도메인에 HTTPS를 적용하고, 프론트(React)와 백엔드(Spring)를 도커로 배포하기.
- 구성: Cloudflare(DNS) + Docker + Nginx + Certbot(SSL).
2. 주요 장애물과 해결 과정 (삽질의 연대기)
① SSL 무한 리디렉션 사건
- 문제: 접속하면 "페이지가 작동하지 않음(리디렉션 횟수 초과)" 발생.
- 원인: Cloudflare의 SSL 설정이
Flexible로 되어 있어,Cloudflare(HTTP) <-> 서버(HTTPS)간에 서로 "너 HTTP로 와!", "아니 HTTPS로 가!" 하며 핑퐁 게임을 함. - 해결: Cloudflare 설정을
Full (Strict)로 변경하여 종결.
② 8080 포트 점유 사건 (루비의 습격)
- 문제: 백엔드 컨테이너가
Address already in use에러로 죽음. - 원인: 도커 외부(호스트)에서 GitLab 내부 서비스인 Ruby(Puma/Workhorse) 가 8080 포트를 이미 쓰고 있었음.
- 해결: 백엔드 호스트 포트를 8081로 변경 (
8081:8080). 하지만 컨테이너 간 통신은 여전히 8080을 써야 한다는 도커 네트워크의 핵심을 파악함.
③ Nginx 기본 페이지만 나오는 현상
- 문제: SSL도 되고 접속도 되는데, 정작 내 앱은 안 나오고 "Welcome to Nginx"만 나옴.
- 원인:
server_name설정 미비 (www 유무 등).- 가장 큰 착각: 프론트엔드 빌드 파일을 Nginx에 넣어주지 않음. 쌩판 순정 Nginx 이미지만 돌리고 있었음.
- 해결: Dockerfile을 멀티 스테이지 빌드(
node에서 빌드 ->nginx로 복사) 구조로 개편하여 앱을 Nginx 내부에 박제함.
④ Vite의 보안 차단 (Allowed Hosts)
- 문제: Nginx 설정을 마쳤으나
Blocked request. This host... is not allowed발생. - 원인: 프론트를 '개발 서버(Vite)' 모드로 띄웠을 때, Vite가 외부 도메인 접속을 보안상 거부함.
- 해결:
vite.config.ts에allowedHosts설정을 추가하려 했으나, 최종적으로는 Nginx 정적 서빙 방식으로 선회하면서 자연스럽게 해결됨.
⑤ 유령 컨테이너와 이름 충돌 (Error 137 & Conflict)
- 문제: 배포 스크립트 실행 시 컨테이너 이름 충돌로 중단되거나 메모리 부족(137) 발생.
- 원인: 서비스 이름을 바꾸면서
docker-compose down이 예전 이름의 컨테이너(Orphan)를 잡지 못함. - 해결: 스크립트에
--remove-orphans와docker rm -f nginx-proxy강제 삭제 로직을 추가하여 자동화 완성.
3. 사용자님이 했던 주요 착각 (정리의 핵심)
"프론트도 컨테이너로 따로 돌려야 하나?"
- 초기에는 Vite 개발 서버를 컨테이너로 띄우려 하셨으나, 운영 환경에서는 Nginx가 빌드된 파일(
dist)을 직접 서빙하는 게 정석임을 깨닫고 구조를 통합하셨습니다.
- 초기에는 Vite 개발 서버를 컨테이너로 띄우려 하셨으나, 운영 환경에서는 Nginx가 빌드된 파일(
"포트 번호의 혼동 (8081 vs 8080)"
- 외부 개방 포트와 내부 통신 포트의 차이를 명확히 구분하게 되셨습니다. (Nginx -> 백엔드는 내부 포트 8080!)
"파일 vs 폴더 마운트"
- 설정 파일이 없을 때 도커가 멋대로 폴더를 만들어버리는 특성 때문에 고생하셨지만, 이제는
Dockerfile에 직접COPY하여 마운트 에러를 원천 차단하는 방식을 익히셨습니다.
- 설정 파일이 없을 때 도커가 멋대로 폴더를 만들어버리는 특성 때문에 고생하셨지만, 이제는
4. 현재 완성된 최종 구조
- Nginx-Proxy (Single Point): 80/443 포트를 담당하며, 내부에 React 빌드 파일을 가지고 있음.
- Backend: 8081(호스트 개방) 및 8080(내부 통신)으로 동작.
- Deploy Script: 인증서 자동 갱신 + 찌꺼기 컨테이너 청소 + 무중단에 가까운 재빌드 로직.
어디까지나 개념을 알게된거지, 여기에 작성된 내용에서도, 내가 겪은 try&error 내용이 전부 작성된것도 아니고,
제미나이가 적어준 이 내용 및 해결 과정을 위해서 작성해야하는 스크립트를 전부 작성할 수 있는 것도 아닌걸 잘 알고있음.
제미나이를 통해서,
아예 할 수 도 없던 부분을,
어중간하게라도 알고, 어중간하게라도 진행 할 수 있게 된단거지,
베테랑이 아니라서, 문제가 발생했을때 무엇이 원인이고, 어떻게 해결 할 수 있다는 건 아니라는걸 명심하기.
(즉, 나대지 않기, 나대지 말기. 애매~~한 추론만 할 뿐)
이제 해야하는 것
- 구글 클라우드 API를 ( 배포 환경 ), ( 테스트 환경 : 개발브랜치, 로컬) 을 분리해서 다룰지, 하나로 뭉쳐서 다룰지
- 깃랩 ci 변수에 구글 로그인 환경변수를 주입하고, 소스코드에서, 가져쓰도록 설정하기
- 현재 img 태그를 이용해서, 로컬 테스트시에는 src 폴더 내부의 img/svg 경로의 이미지 파일을 제대로 호출하는데, 배포나, 개발 브랜치에서 호출을 못하고 있음. 리액트에서 어떻게 작정해야, 이미지 파일을 제대로 호출할 수 있는지(배포에서, assert 에 이미지가 없음. 제미나이 설명으로는 이미지가 제대로 추가 됐다면 빌드시, dist 폴더 안에 assert 폴더가 있고, 이 폴더 안에 이미지 파일들이 있어야 한다고 함.)
'개발 회고 > 개념 깨달음' 카테고리의 다른 글
| 라이브러리는 개발 편의다. (0) | 2026.01.01 |
|---|---|
| HTTP와 잃어버렸던 기억 (0) | 2025.04.09 |
댓글