분명 새 뉴스가 올라왔는데, 사이트에는 어제 뉴스가 남아 있습니다. 로컬에서는 카드가 6개인데 배포 화면에서는 4개만 보이기도 했습니다. 이럴 때 캐시부터 전부 지우고 싶어집니다. 그런데 이번 사이트의 수정 기록을 다시 보면, 캐시와 API 경로, 대체 화면을 한 가지 문제로 묶어서 보면 안 됐습니다.
뉴스가 늦는 것과 뉴스 연결이 실패한 것은 다릅니다. 겉으로는 둘 다 ‘옛날 카드’로 보이니 헷갈리기 딱 좋습니다. 이 글은 시스템 디자인 사이트의 2026년 8월 28일·31일 코드 변경을 기준으로, 어디를 확인했고 무엇을 분리해야 하는지 정리한 기록입니다. 오늘 공개 API가 정상인지 판정하는 실시간 상태표는 아닙니다.
1. 카드 네 장은 어디서 온 걸까?
이 사이트는 JavaScript가 실행되기 전에도 읽을거리가 있도록 HTML에 편집 브리핑 4개를 넣습니다. 뉴스 API가 정상 응답하면 이 부분을 API 데이터로 바꾸고, 홈에서는 최대 6개를 보여줍니다. JavaScript를 끄거나 API 연결이 실패하면 정적 카드가 남습니다.
즉, 카드가 보인다는 사실만으로 실시간 연결 성공이라고 판단할 수는 없습니다. 오히려 보기 좋은 대체 화면이 장애를 가릴 수 있습니다. 그렇다고 대체 카드를 없애고 빈 화면을 보여줄 일은 아니죠. 대체 화면은 유지하되, 어떤 상태인지 구분할 수 있어야 합니다.
| 보이는 상태 | 확인할 내용 | 단정하면 안 되는 것 |
|---|---|---|
| 편집 브리핑 4개 | 정적 HTML인지, API 전환 전인지, 실패 후인지 | 4개니까 무조건 오류다 |
| API 카드 6개 | 응답 날짜, 데이터 출처, 현재 필터 | 6개니까 오늘 뉴스다 |
| 특정 날짜의 지난 브리핑 | 주소의 date 값과 이전·다음 탐색 상태 | 지난 날짜니까 캐시가 고장 났다 |
분류 필터를 선택하면 정상적으로 카드 수가 줄 수도 있습니다. 먼저 전체 필터인지 확인하고, 개발자 도구의 Network에서 실제 응답까지 같이 보는 편이 빠릅니다.
2. 로컬 서버와 배포 서버는 같은 서버가 아니었습니다
브라우저는 같은 사이트 안의 뉴스 API 경로를 부릅니다. 하지만 그다음 처리가 다릅니다. 로컬에서는 Node 서버가 요청을 받아 외부 뉴스 API를 호출하고, Vercel 배포에서는 rewrite 설정이 그 경로를 연결합니다. 화면 코드가 같아도 이 접점은 따로 확인해야 합니다.
- 원본 뉴스 API보고서 날짜·생성 시각
- 로컬 프록시 / 배포 rewriteURL·리다이렉트·응답 형식
- 브라우저 저장본저장 시각·유효 시간
- 뉴스 카드필터·표시 개수·대체 화면
8월 28일 변경에서는 뉴스 경로의 끝 슬래시를 맞췄습니다. 사이트는 trailingSlash: true를 사용하는데, 뉴스 경로는 슬래시 없는 형태로 정의되어 있었습니다. 로컬 라우트와 Vercel rewrite를 모두 /api/atlas-news/system-design/report/ 형태로 정리하고, 예전 경로는 308로 연결했습니다. 날짜 쿼리도 그대로 보존합니다.
이 기록이 말해주는 것은 ‘모든 옛 뉴스의 원인은 슬래시였다’가 아닙니다. 로컬 성공만으로 배포의 경로 매칭까지 검증한 셈 치면 안 된다는 것입니다. 응답 상태가 200이어도 JSON 대신 HTML이 왔다면 연결은 잘못된 것입니다. 상태 코드와 Content-Type을 함께 봐야 합니다.
코드 근거: 2026-08-28 뉴스 프록시·rewrite 변경. 저장소 접근 권한에 따라 GitHub 로그인이 필요할 수 있습니다.
3. 생성 시각과 저장 시각은 다른 시계입니다
다음으로 볼 것은 시간입니다. 보고서가 만들어진 generatedAt, 보고서가 다루는 날짜인 reportDate, 브라우저가 받아 저장한 savedAt은 의미가 다릅니다. 방금 저장했다고 방금 만든 뉴스가 되는 것은 아닙니다.
이 사이트의 브라우저 저장본은 sessionStorage에 있습니다. 8월 31일 변경에서 유효 시간을 300초에서 60초로 줄이고, 저장 키를 v3에서 v4로 바꿨습니다. 새 코드가 예전 저장 규칙의 데이터를 다시 사용하지 않게 한 조치입니다. 서버 측 응답이 갱신되어도 브라우저가 자기 저장본만 쓰는 구간을 줄이는 목적입니다.
// 이 프로젝트의 변경 전 → 변경 후
const ttlMs = 60 * 1000; // 기존 300 * 1000
const cacheKey = (endpoint) =>
`atlas.system-design-updates.v4:${endpoint}`;
여기서도 ‘1분 뒤면 무조건 최신’이라고 말하면 곤란합니다. 원본 보고서가 아직 생성되지 않았거나 중간 응답이 오래됐다면, 브라우저가 다시 받아도 같은 내용입니다. 또한 이 코드는 뉴스를 불러오는 시점에 저장본의 나이를 검사합니다. 열린 탭을 매분 자동 갱신하는 타이머와는 다릅니다.
브라우저 HTTP 캐시와 sessionStorage도 별개입니다. fetch의 cache: 'no-store'는 HTTP 캐시 사용을 제어하는 옵션이지, 애플리케이션이 먼저 읽은 sessionStorage를 지워주는 명령은 아닙니다. 이 둘을 섞어 보면 ‘캐시를 껐는데도 왜 그대로지?’라는 두 번째 혼란이 생깁니다. MDN의 Request.cache 설명에서도 해당 옵션의 대상은 브라우저 HTTP 캐시로 구분합니다.
코드 근거: 2026-08-31 저장 시간·캐시 키 변경.
4. 다음에 같은 일이 생기면 이 순서로 봅니다
가장 먼저 새로고침하면 문제를 잠깐 없앨 수는 있습니다. 대신 왜 그랬는지 볼 자료도 사라집니다. 가능하면 처음 보인 상태부터 남기는 편이 좋습니다.
- 화면을 기록합니다. URL, 선택한 날짜와 필터, 카드 수, 상태 문구를 같이 남깁니다. 날짜가 다른 탭끼리 비교하지 않습니다.
- Network 응답을 확인합니다. 실제 요청 주소, 리다이렉트, 최종 HTTP 상태, Content-Type을 봅니다. 200과 올바른 JSON은 함께 확인합니다.
- 응답의 날짜를 읽습니다. reportDate와 generatedAt을 기록하고 시각의 시간대를 맞춥니다. 한국 날짜와 UTC 날짜를 그대로 비교하면 하루 차이를 장애로 오인할 수 있습니다.
- 저장 계층을 나눠 봅니다. 응답 헤더의 Cache-Control·Age·ETag가 제공되는지, sessionStorage에는 어떤 savedAt이 있는지 확인합니다. 없는 헤더를 임의로 추정하지 않습니다.
- 마지막으로 화면 변환을 봅니다. 응답은 최신인데 카드가 이전이라면 정규화, 필터, 저장본 선택, 실패 분기를 좁혀 갑니다.
최근 뉴스 API는 최신 날짜로 요청했을 때의 동작과, 지난 날짜를 지정했을 때의 동작을 따로 검사해야 합니다. 날짜 탐색이 있는 서비스에서 ‘모든 응답은 오늘 날짜여야 한다’는 테스트를 만들면 정상인 지난 브리핑이 실패로 판정됩니다.
5. 새로고침 한 번 성공했다고 끝내지 않으려면
이 프로젝트의 뉴스 QA에는 API 실패와 JavaScript 비활성 상태에서도 대체 카드가 남는지 확인하는 검사가 있습니다. 성공 경로만 보던 때보다 중요한 변화입니다. API가 잠깐 끊겼을 때도 빈 화면 대신 읽을 수 있는 내용이 있어야 하기 때문입니다.
| 검사 | 통과 기준 |
|---|---|
| 정상 API 응답 | 응답 날짜와 화면 날짜가 같고, 유효 카드가 표시 한도 내에서 노출됨 |
| API 실패 | 정적 편집 카드가 유지되며, 실시간 성공으로 오해할 상태 문구를 쓰지 않음 |
| JavaScript 비활성 | 서버 HTML의 카드와 원문 링크를 그대로 읽을 수 있음 |
| 모바일 | 같은 데이터 기준으로 카드가 누락되지 않고 가로로 넘치지 않음 |
| 이전 날짜 탐색 | 요청 날짜가 URL과 응답, 화면 사이에서 일치함 |
날짜가 바뀌는 경계도 따로 볼 필요가 있습니다. 이전 보고서를 저장한 탭이 다음 진입 때 새 보고서를 받는지, 이미 열린 화면은 어떤 정책으로 바뀌는지 구분해서 확인합니다. 매분 다시 불러올지, 사용자가 재진입할 때만 갱신할지는 운영 비용과 기대하는 신선도를 보고 정할 일입니다.
결국 ‘최신’이라는 말에 기준이 필요했습니다
최신 뉴스는 매일 글 하나를 만드는 것만으로 끝나지 않습니다. 생성된 보고서가 올바른 경로를 거쳐 화면까지 도착해야 합니다. 경로가 틀린 상태에서 캐시 시간만 줄여도 해결되지 않고, 원본 생성이 늦는데 화면만 자주 갱신해도 새 글은 나오지 않습니다.
이 사이트에서 가져갈 교훈은 단순합니다. 카드 수는 표시 상태, 생성 시각은 데이터 신선도, HTTP 응답은 전달 상태입니다. 셋을 따로 보면 다음 장애 때 어디서부터 봐야 할지 훨씬 분명해집니다. 새로고침 버튼부터 누르기 전에, 지금 보고 있는 것이 원본인지 저장본인지 대체 화면인지 한 번만 확인해 보세요.


