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








