다음 저장소에서 CI가 apps/web만 압축하면 Nginx 설정은 어디로 갈까.

repository/
  apps/web/
  packages/shared/
  deployment/web/.platform/nginx/conf.d/upload.conf
  turbo.json

애플리케이션은 정상 빌드돼도 deployment/web/.platform은 번들 밖에 남는다. 그 폴더를 포함하더라도 경로를 그대로 보존하면 Beanstalk가 기대하는 ZIP 루트의 .platform과 달라진다.

이 글의 질문은 저장소의 Nginx 파일이 플랫폼이 읽는 배포 경로까지 전달됐는가다. Turbo의 앱 빌드와 Beanstalk source bundle 조립을 별개의 확인 단계로 둔다.

배포 루트는 의도적으로 조립한다

Beanstalk source bundle은 전체를 감싸는 상위 폴더 없이 파일을 넣어야 한다. 모노레포의 저장소 루트가 자동으로 애플리케이션 실행 루트가 되는 것은 아니다. Source bundle 루트 요구를 기준으로 앱 파일과 플랫폼 확장 설정의 위치를 정한다.

패키징 단계에서는 비어 있는 임시 stage를 만들고 필요한 경로를 명시적으로 복사한다.

stage_dir="$(mktemp -d "${RUNNER_TEMP}/web-source-bundle.XXXXXX")"
bundle_dir="$(mktemp -d "${RUNNER_TEMP}/web-bundle-output.XXXXXX")"
bundle_path="$bundle_dir/web-source-bundle.zip"
mkdir -p "$stage_dir/.platform"
cp -R deployment/web/.platform/. "$stage_dir/.platform/"
cp deployment/web/Procfile "$stage_dir/Procfile"

# 앱 산출물과 실행 의존성은 사용 플랫폼에 맞춰 이 루트에 복사한다.
(cd "$stage_dir" && zip -r "$bundle_path" .)

이 코드는 CI에서 RUNNER_TEMP를 제공하는 조건의 패키징 예제다. stage와 출력 폴더를 매번 새로 만들어 이전 ZIP의 항목이 남지 않게 한다. 기존 ZIP에 파일을 추가·교체하는 방식은 이번 stage에서 삭제한 항목을 남길 수 있다. Next.js standalone 출력, Node.js 의존성 또는 Java JAR 복사는 실제 플랫폼에 맞춰 추가한다. 이 조각만으로 실행 가능한 앱 번들을 완성했다고 보지 않는다.

파일의 존재보다 ZIP 내부 경로를 검사한다

같은 셸 단계에서 생성한 bundle_path로 배포 직전에 압축 파일의 목록을 확인한다. 별도 CI 단계로 나누면 이 경로도 작업 출력으로 전달한다.

unzip -Z1 "$bundle_path" \
  | rg '^\.platform/nginx/conf\.d/upload\.conf$'

예상 경로가 없으면 패키징 작업을 실패시키도록 연결한다. stage에 파일이 있어도 최종 ZIP이 다른 디렉터리에서 만들어졌다면 이 검사는 실패한다. 설정 내용의 해시를 커밋·아티팩트 식별자와 묶으면 서버 파일과 비교할 수 있다. 파일 안에 비밀값이 있는지는 메타데이터 공유 전에 확인한다.

저장소 설정을 stage와 ZIP 루트에 조립한 뒤 서버 설치·Nginx 유효 설정·외부 요청까지 확인하는 흐름

그림의 각 화살표는 다른 증거가 필요한 전달 단계다. ZIP 존재 검사는 include 문맥이나 reload 성공까지 보장하지 않는다.

서버 파일에서 유효 설정으로 넘어간다

Beanstalk의 .platform/nginx/conf.d/*.conf는 HTTP 블록, .platform/nginx/conf.d/elasticbeanstalk/*.conf는 기본 server 블록을 확장한다. 파일 위치와 지시어 문맥을 맞춰야 한다. 플랫폼 프록시 확장 규칙을 사용하는 플랫폼 버전과 함께 확인한다.

서버에 새 파일이 있으면 실제 Nginx 프로세스가 어떤 설정으로 시작됐는지, include됐는지, 더 구체적인 location에서 재정의됐는지 조사한다. nginx -t는 문법 검사이고 nginx -T는 설정 출력까지 포함한다. Nginx 실행 옵션에 따른 확인 뒤 적용·reload 결과를 별도로 확인한다.

마지막으로 서비스 경로에 요청을 보낸다. 업로드 제한이라면 경계 크기, 라우팅이라면 해당 경로를 확인한다. rolling 배포 중 한 인스턴스의 성공만 확인하면 서로 다른 설정이 섞인 상태를 놓칠 수 있어 대상별 파일·버전도 연결한다.

강제 빌드 성공이 캐시 원인을 증명하지는 않는다

--force로 다시 빌드한 뒤 설정이 적용됐다고 하자. 그 과정에서 패키징까지 다시 수행했다면 cache miss와 파일 복사가 동시에 바뀌었다. 이것만으로 Turbo 캐시가 원인이라고 확정할 수 없다.

Turbo의 입력 해시와 복원할 outputs, 별도 패키징이 복사할 파일, 플랫폼 include를 나눠 확인한다. 캐시 문서는 빌드 파일 복원 계약을 설명하며 ZIP 조립을 대신하지 않는다. 환경값이 이전 번들에 남는 문제와 플랫폼 파일이 ZIP에서 빠지는 문제도 구분해야 한다.

이 방식은 앱과 배포 설정이 서로 다른 폴더에 있는 저장소에서 유효하다. 배포 단위에 실행 파일, 필요한 플랫폼 설정, 목록·해시와 버전 ID를 함께 담고 그 단위로 롤백한다. 다음 조사에서는 캐시 삭제보다 먼저 ZIP 루트의 예상 경로를 확인한다.