<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Nginx on 얼쑤 · Team Edo</title><link>https://team-edo.github.io/tags/nginx/</link><description>Recent content in Nginx on 얼쑤 · Team Edo</description><generator>Hugo</generator><language>ko-kr</language><lastBuildDate>Mon, 09 Nov 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://team-edo.github.io/tags/nginx/index.xml" rel="self" type="application/rss+xml"/><item><title>백엔드 무중단 배포</title><link>https://team-edo.github.io/posts/2026/11/docker-compose-blue-green-drain/</link><pubDate>Mon, 09 Nov 2026 00:00:00 +0000</pubDate><guid>https://team-edo.github.io/posts/2026/11/docker-compose-blue-green-drain/</guid><description>&lt;p>백엔드 컨테이너 하나를 새 버전으로 교체하면, 이전 컨테이너가 내려간 시점과 새 컨테이너가 요청을 받을 수 있는 시점 사이에 공백이 생긴다. 이 공백을 없애려면 순서를 바꿔야 한다. &lt;strong>이전 버전이 요청을 받는 동안 새 버전을 준비하고, 연결을 넘긴 뒤에도 이미 받은 요청과 작업을 마칠 때까지 이전 버전을 살려 둔다.&lt;/strong> 얼쑤는 기존 Docker Compose와 Nginx에 이 순서를 적용했다.&lt;/p>
&lt;h2 id="같은-호스트에-백엔드-슬롯-두-개를-뒀다">같은 호스트에 백엔드 슬롯 두 개를 뒀다&lt;/h2>
&lt;p>슬롯은 백엔드 한 인스턴스를 실행하는 자리다. Compose에 블루와 그린을 정의하고, 두 슬롯이 같은 런타임 설정을 쓰도록 했다. 배포할 때 비활성 슬롯에 새 버전의 JAR을 붙인다. 두 슬롯은 같은 MySQL을 사용한다. 사용자는 평소와 같은 HTTPS 주소로 접속하고, Nginx가 활성 슬롯으로 요청을 보낸다.&lt;/p></description></item><item><title>1.76MB 곡 목록에서 악보 덜어내기</title><link>https://team-edo.github.io/posts/2026/11/song-list-payload-and-gzip/</link><pubDate>Sun, 08 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/song-list-payload-and-gzip/</guid><description>&lt;p>얼쑤의 곡 목록 API를 Swagger UI에서 열었더니 &lt;code>Could not render responses&lt;/code>가 표시됐습니다. 브라우저 콘솔에는 &lt;code>Maximum call stack size exceeded&lt;/code>가 남았습니다. 응답을 살펴보니 111곡을 담은 JSON이 약 1.76 MB였고, 그중 약 99%가 곡마다 붙은 &lt;code>score&lt;/code>였습니다. 곡 하나를 고르는 화면에서 111곡의 악보까지 받고 있었습니다.&lt;/p>
&lt;h2 id="곡을-고를-때-필요한-필드만-목록에-남겼다">목록에서 악보 본문 분리&lt;/h2>
&lt;p>곡 하나당 메타데이터는 평균 189바이트였고, 악보는 평균 약 16 KB였습니다. 악보가 들어간 응답 타입을 목록과 상세에서 함께 쓴 결과였습니다. 곡이 늘면 각 곡의 악보 크기만큼 목록 응답도 함께 커지는 구조였습니다.&lt;/p></description></item><item><title>파일 교체 뒤에도 남은 컨테이너 설정</title><link>https://team-edo.github.io/posts/2026/11/reload-file-bind-mount-in-place/</link><pubDate>Wed, 04 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/reload-file-bind-mount-in-place/</guid><description>&lt;p>운영 Nginx 설정에 압축 옵션을 반영할 때, 설정 파일을 읽는 쪽이 어디인지부터 확인했습니다. Nginx 컨테이너는 호스트의 설정 디렉터리 전체 대신 설정 파일 하나를 읽기 전용으로 연결하고 있었습니다. 호스트에서 경로가 같은 파일을 새로 만들어 넣어도, 실행 중인 컨테이너는 이전 파일을 계속 볼 수 있는 구성입니다.&lt;/p>
&lt;h2 id="이름을-유지하는-것과-파일을-유지하는-것">같은 이름과 같은 파일의 차이&lt;/h2>
&lt;p>Linux에서 경로는 파일을 찾는 이름입니다. 파일 하나를 bind mount하면 마운트는 그때 찾은 파일의 실체에 연결됩니다. 임시 파일을 만든 뒤 &lt;code>rename&lt;/code>으로 기존 이름을 교체하면 호스트에서 같은 경로를 열 때 새 파일을 읽습니다. 컨테이너가 이미 연결한 파일은 그대로 남습니다. 반대로 기존 파일을 열어 내용을 덮어쓰면 inode를 유지하므로 컨테이너에서도 같은 파일의 바뀐 내용을 읽습니다.&lt;/p></description></item><item><title>1초 늦는 저장소로 SDK 타임아웃 검증</title><link>https://team-edo.github.io/posts/2026/10/storage-sdk-call-budget/</link><pubDate>Wed, 28 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/storage-sdk-call-budget/</guid><description>&lt;p>녹음 업로드 요청은 파일 검사와 객체 저장을 거친 뒤 응답합니다. 코드 리뷰에서 저장소 호출 상한이 &lt;strong>2분&lt;/strong>이라는 점을 확인했습니다. 이 경로에서 Nginx의 응답 읽기 제한은 &lt;strong>60초&lt;/strong>였습니다. 저장소가 느리면 애플리케이션이 오류 형식을 정해 응답하기 전에 프록시가 연결을 끝낼 수 있었습니다.&lt;/p>
&lt;h2 id="재시도까지-하나의-예산으로-묶었다">재시도를 포함한 40초 상한&lt;/h2>
&lt;p>파일 검사 도구에는 10초, 저장소 호출 전체에는 &lt;strong>40초&lt;/strong>를 배정했습니다. 두 설정을 더하면 50초입니다. Nginx의 60초는 연속한 읽기 사이의 대기 제한이고, 50초는 이 경로의 두 주요 작업에 배정한 예산입니다. 둘 다 실제 요청 시간의 측정값은 아닙니다. &lt;a href="https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout">Nginx의 응답 읽기 제한&lt;/a>&lt;/p></description></item><item><title>21MiB와 26MiB가 받은 서로 다른 413</title><link>https://team-edo.github.io/posts/2026/10/recording-upload-size-layers/</link><pubDate>Wed, 28 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/recording-upload-size-layers/</guid><description>&lt;p>얼쑤의 녹음 업로드를 확인하면서 &lt;strong>21MiB&lt;/strong>와 &lt;strong>26MiB&lt;/strong> 크기의 입력을 같은 Nginx를 거쳐 보냈습니다. 두 요청 모두 HTTP 413이었습니다. 21MiB 요청에는 백엔드가 만든 JSON 오류 코드 &lt;code>RECORDING_TOO_LARGE&lt;/code>가 들어 있었고, 26MiB 요청에는 Nginx가 만든 HTML 응답이 돌아왔습니다. 상태 코드는 같았지만 요청을 멈춘 곳이 달랐습니다.&lt;/p>
&lt;h2 id="파일-하나의-한도와-요청-본문의-한도를-나눴다">파일 한도와 요청 본문 한도&lt;/h2>
&lt;p>연주 녹음 한 파일에는 &lt;strong>20MiB&lt;/strong>, 즉 20,971,520바이트까지 허용하기로 했습니다. 앱은 녹음 파일을 multipart 요청의 한 파트로 보냅니다. 이 요청의 본문에는 파일 바이트 외에도 파트를 구분하는 경계와 파트 헤더가 붙습니다. 요청 본문은 Nginx를 지나고, Spring이 multipart를 파싱한 뒤에야 백엔드의 녹음 검사기가 파일 파트의 크기를 확인할 수 있습니다.&lt;/p></description></item></channel></rss>