<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>HTTP on 얼쑤 · Team Edo</title><link>https://team-edo.github.io/tags/http/</link><description>Recent content in HTTP on 얼쑤 · Team Edo</description><generator>Hugo</generator><language>ko-kr</language><lastBuildDate>Tue, 03 Nov 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://team-edo.github.io/tags/http/index.xml" rel="self" type="application/rss+xml"/><item><title>206 응답 뒤에 숨은 CORS 오류</title><link>https://team-edo.github.io/posts/2026/11/cors-after-audio-redirect/</link><pubDate>Tue, 03 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/cors-after-audio-redirect/</guid><description>&lt;p>얼쑤의 모범 연주 음원 API를 Swagger에서 확인하다가 &lt;code>Failed to fetch&lt;/code>를 만났습니다. 같은 음원의 처음 2바이트를 &lt;code>curl&lt;/code>로 요청하니 저장소는 HTTP 206을 반환했습니다. 저장소가 바이트를 보내고도 브라우저에서 읽을 수 없는 이유를 찾으려면, 처음 요청한 API와 음원을 실제로 돌려주는 서버를 나눠 봐야 했습니다.&lt;/p>
&lt;h2 id="302-뒤의-응답을-확인했다">302 이후의 응답 추적&lt;/h2>
&lt;p>Swagger 페이지와 음원 API는 같은 출처에 있었습니다. API는 곡과 음원의 게시 상태를 확인한 뒤 HTTP 302의 &lt;code>Location&lt;/code> 헤더에 R2 서명 URL을 담았습니다. 브라우저가 리다이렉트를 따라가면 음원 본문은 다른 출처인 R2에서 내려옵니다. 그 응답에는 Swagger 페이지의 출처를 허용하는 &lt;code>Access-Control-Allow-Origin&lt;/code>이 없었습니다. 브라우저는 저장소 응답을 받았지만 페이지의 자바스크립트에 넘기지 않았습니다. &lt;a href="https://fetch.spec.whatwg.org/#cors-check">Fetch 표준의 CORS 검사&lt;/a>&lt;/p></description></item><item><title>곡 목록은 200, 콘솔 오류는 어디서 왔나</title><link>https://team-edo.github.io/posts/2026/11/console-error-origin-before-api/</link><pubDate>Tue, 03 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/console-error-origin-before-api/</guid><description>&lt;p>얼쑤의 곡 목록 API를 점검할 때 Chrome 콘솔에 &lt;code>Cannot read properties of undefined (reading 'sentence')&lt;/code> 오류가 &lt;strong>아홉 건&lt;/strong> 찍혔습니다. 곡 목록을 요청하는 동안 보인 오류라 처음에는 응답을 처리하는 코드부터 봐야 할지 판단하기 어려웠습니다. 같은 요청의 응답은 &lt;strong>HTTP 200&lt;/strong>이었고 본문에는 &lt;code>isSuccess: true&lt;/code>가 들어 있었습니다.&lt;/p>
&lt;h2 id="콘솔에서는-스택의-파일-출처를-봤습니다">스택에서 파일 출처 확인&lt;/h2>
&lt;p>오류 아홉 건의 스택에는 브라우저 확장의 &lt;code>contentScript.js&lt;/code>가 있었습니다. 파일명만으로는 앱의 스크립트인지 알기 어려웠고, 출처가 확장 프로그램의 주소라는 점이 단서였습니다. 같은 기록에는 Swagger UI의 번들에서 발생한 응답 표시 오류도 따로 있었습니다. 두 종류의 메시지는 비슷한 시점에 떴지만 서로 다른 스크립트에서 실행된 코드였습니다. &lt;a href="https://developer.chrome.com/docs/devtools/console/reference">Chrome DevTools 콘솔 문서&lt;/a>&lt;/p></description></item><item><title>HTTP 406에도 공통 오류 본문 적용하기</title><link>https://team-edo.github.io/posts/2026/10/keep-common-error-body-for-http-406/</link><pubDate>Wed, 28 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/keep-common-error-body-for-http-406/</guid><description>&lt;p>앱은 요청에 실패하면 HTTP 상태와 응답 본문의 &lt;code>result.errorCode&lt;/code>를 읽습니다. 컨트롤러가 거절한 요청에 공통 형식을 쓰더라도, Spring이 컨트롤러 호출 전에 예외를 처리하면 다른 형태의 본문이 내려갈 수 있습니다. 지원하지 않는 응답 형식을 요청해 406이 나오는 경우가 그 예였습니다.&lt;/p>
&lt;h2 id="상태와-헤더를-살리고-본문을-공통-형식으로-맞췄다">상태와 헤더를 유지한 공통 본문&lt;/h2>
&lt;p>필수 쿼리 파라미터가 없거나 값의 형식이 틀리면 Spring MVC가 400을 만듭니다. 공통 예외 처리기는 이때 앱이 사용하는 &lt;code>INVALID_REQUEST&lt;/code> 본문을 돌려줍니다. 405는 허용 메서드를 알려 주는 &lt;code>Allow&lt;/code> 헤더도 보존해야 합니다. 본문을 맞추면서 Spring이 이미 정한 상태와 헤더를 함께 전달한 이유입니다.&lt;/p></description></item><item><title>악보 체류 기록의 재전송 오류 구분</title><link>https://team-edo.github.io/posts/2026/10/permanent-score-view-sync-rejections/</link><pubDate>Wed, 28 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/permanent-score-view-sync-rejections/</guid><description>&lt;p>얼쑤 앱은 악보 화면을 떠날 때 그동안 보인 시간과 연습으로 인정한 시간을 기록합니다. 네트워크가 끊겨도 기록을 남길 수 있도록 앱은 닫힌 구간을 기기의 대기열에 넣었다가 다시 보냅니다. 저는 팀과 이 전송 규칙을 정리하고 서버의 저장 API를 구현했습니다.&lt;/p>
&lt;p>이 API에서 상태 코드는 앱의 다음 동작을 정합니다. 5xx나 연결 오류가 오면 구간을 보관하고 다음 기회에 보냅니다. 400이나 404가 오면 해당 구간은 대기열에서 빼고 오류를 기록합니다. 401에는 토큰을 갱신하는 경로가 따로 있습니다. 리뷰에서 같은 요청을 다시 보내도 해결될 이유가 없는 &lt;strong>두 가지 경우&lt;/strong>가 500으로 분류되는 것을 확인했습니다.&lt;/p></description></item></channel></rss>