Files
document-haness/docs/TechLog/tech-log-studio/one-route-many-hand-kept-lists/case/case-a-route-the-web-server-never-heard-of.md
T
DongHyeonkaandClaude Opus 5 6feee5ba57 docs(TechLog): 자료에 남아 있던 사람의 흔적을 제자리에 놓는다
writing-as-the-person-who-did-it 을 서브에이전트 셋으로 나눠 56편에 적용했다.
56편 중 20편만 고쳤다 — 나머지 36편은 SSOT 를 절 단위로 대조했을 때 옮길 흔적이
이미 옮겨져 있었거나 없었다. 없는 목소리를 채우지 않는다.

옮긴 것은 전부 SSOT 의 어느 절에서 왔는지 댈 수 있다.

  §3.4  「눈으로 찾을 일이 아니었다」— 세 계약을 파싱해 뽑은 이유
  §4.3  구현하지 않기로 한 것과 빠뜨린 것은 다르다
  §8.5  표의 마지막 줄을 더할 때 이 목록을 또 빠뜨렸다
  §9.4  「왜 주제 링크가 탐색으로 가지?」— 우회를 남겨 두면 계속 나온 질문
  §11.3 「세 버튼」을 실제 이름으로 되돌리고 두 언어가 섞인 것을 그 자리에
  §12.2 막지 않은 대신 메모리에 남긴 것
  §13.1 여섯 벌 인용이 어느 커밋이 짚은 말인지
  §13.3 고쳐 쓴 첫 안이 거절당한 것과 사용자가 고른 말 두 쌍
  §14.3 「문서가 그대로 나온다」는 구조 차이가 아니라 내용 양의 차이라는 정정
  §16.1 삭제가 막힌 실제 기록 이름과 그것을 막은 프로젝트 링크
  §16.9 주제 논지와 축 결론이 AI 가 써서 DB 에 직접 넣은 미검토 초안이라는 것
  §17.5 바운딩 박스로 잘못 지목한 대상이 「판단 기준」이었다는 것

검증 환경의 커밋 해시 하나가 틀려 있었다(ca1cfa2 → ca1fc92). SSOT §13.1 과
부록 A 가 적은 값이고 저장소에 그 커밋이 있다.

검사 넷 전부 통과한다 — check_prose 56편 error 0 · check_body PASS ·
check_evidence --repo 문제 없음 · verify-tech-log-tree 프로젝트 5 error 0 warn 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 16:31:48 +09:00

5.0 KiB

kind, slug, title, topic, topicName, project, status, lastVerifiedOn, sourceRevision, source
kind slug title topic topicName project status lastVerifiedOn sourceRevision source
CASE a-route-the-web-server-never-heard-of nginx 가 모르는 라우트는 새로고침에서 404 다 one-route-many-hand-kept-lists 라우트 하나가 울리는 손 목록 TechLog 게시 전 2026-09-04 tech-log@2026-09-02
final/document.md#§8.2
final/document.md#§12.6

nginx 가 모르는 라우트는 새로고침에서 404 다

/studio/releases 가 평문 404 를 돌려줬다. 라우트는 있고 청크도 빌드됐고 SPA 내부 이동으로는 화면에 닿는데, 하드 로드와 새로고침은 거기까지 가지 못한다. nginx 설정이 손으로 유지하는 배열에서 나오고 있었다.

관계

  • 라우트에 딸린 목록은 라우트 계약에서 유도하고, 유도할 수 없는 것은 대조 검사를 둔다 이 사건에서 굳힌 기준이다.
  • catch-all 라우트는 nginx 패턴으로 번역하지 않는다 같은 유도 규칙의 일부로 정했다.
  • 라우트 하나가 건드리는 여덟 곳과, 그것들이 우는 시점 같은 부류가 다른 목록에서 어떻게 우는지가 그 기록에 있다.

문제

SPA 안에서 이동하면 화면이 열린다. 주소창에 그 주소를 직접 넣거나 새로고침하면 nginx 가 평문 404 를 준다.

웹 서버가 그 경로의 존재를 들은 적이 없기 때문이다. 서빙 계약이 어느 경로를 SPA 로 넘길지 정하는데, 그 목록에 없는 경로는 넘어가지 않는다.

결론

서빙 계약의 절반은 라우트 레지스트리에서 유도하고 있었고 나머지 절반은 손으로 유지하는 배열이었다.

서빙 계약의 공개 절반은 라우트 레지스트리에서 패턴을 유도한다. Studio 절반은 손으로 유지하는 배열이었고, 손으로 유지하는 배열이 실패하는 방식 그대로 실패했다^/studio/assets$ 위의 주석이 바로 그 버그를 한 번 고친 기록이고, 라우트를 더하니 즉시 반복됐다.

공개 절반도 유도라고 하기 어려웠다. 번들된 픽스처에 우연히 들어 있던 공개 경로를 전부 열거하고, 생성된 nginx 가 정확히 그것들을 location = 블록으로 게시했다. 빌드 이후에 게시된 기록은 SPA 에 묻기도 전에 엣지에서 404 였다. 경로 27개가 얼어 있었고 28번째는 무엇이든 닿을 수 없었다.

지금은 라우트 계약에서 등록된 Public 라우트마다 정규식 하나를 만든다. 파라미터는 한 세그먼트만 잡고 슬래시는 잡지 않으므로 /cases/a/b 는 404 로 남는다.

검증 환경

tech-log-frontend : ab8c6c1 · 6784eb1 엣지 : nginx (서빙 계약에서 생성) 확인 방식 : 생성된 서빙 패턴이 라우트 계약과 같은지 보는 테스트

재현 조건

  1. 라우트를 하나 더하고 서빙 계약 배열에는 넣지 않는다
  2. 빌드하고 배포한 뒤 그 주소를 주소창에 직접 넣는다 — 평문 404 가 온다
  3. SPA 안에서 링크로 이동한다 — 화면이 열린다

본문

내부 이동은 되고 하드 로드는 안 된다

SPA 안에서 이동할 때는 라우터가 화면을 그리므로 웹 서버가 개입하지 않는다. 주소를 직접 넣거나 새로고침하면 웹 서버가 먼저 그 경로를 받는다.

서빙 계약에 그 경로가 없으면 nginx 는 SPA 로 넘기지 않고 404 를 준다.

손으로 유지하는 절반

서빙 계약의 공개 절반은 라우트 레지스트리에서 패턴을 유도한다. Studio 절반은 손으로 유지하는 배열이었고, 손으로 유지하는 배열이 실패하는 방식 그대로 실패했다^/studio/assets$ 위의 주석이 바로 그 버그를 한 번 고친 기록이고, 라우트를 더하니 즉시 반복됐다.

얼어붙은 27개

공개 절반도 유도가 아니었다. 서빙 계약이 번들된 픽스처에 우연히 들어 있던 공개 경로를 전부 열거하고, 생성된 nginx 가 정확히 그것들을 location = 블록으로 게시했다.

빌드 이후에 게시된 기록 — 백엔드를 두는 이유 그 자체 — 은 SPA 에 묻기도 전에 엣지에서 404 였다. 경로 27개가 얼어 있었고 28번째는 무엇이든 닿을 수 없었다.

라우트 계약에서 유도한다

지금은 라우트 계약에서 등록된 Public 라우트마다 정규식 하나를 만든다. 파라미터는 한 세그먼트만 잡고 슬래시는 잡지 않으므로 /cases/a/b 는 404 로 남는다.

같은 구조 때문에 robots.txt 도 404 였다. 파일은 이미지에 있었지만 nginx 설정이 서빙할 파일을 하나씩 명시하는 구조라 등록되지 않은 것은 SPA 폴백으로 떨어졌고, 크롤러가 index.html 을 규칙으로 읽을 수는 없으므로 규칙이 없는 것과 같았다.

확인하지 못한 것

경로 27개가 얼어 있던 상태의 생성 결과물은 남기지 않았다. 지금 생성되는 패턴이 라우트 계약과 같은지만 테스트가 본다.