페이지 새로고침 시 빈화면이 나타남
SSR 페이지를 개발했을때, 새로고침을 하면 전체 HTML이 없어졌다가 나타나는 현상을 발견했다. ssr 페이지라서 HTML을 모두 전달받고나서 화면을 뿌려주는 원리인건 알겠는데, 사용자 경험(UX)측면에서 무언가 별로였다. 별로야.
그래서 다른 서비스들은 ssr페이지를 새로고침하면 어떻게 처리되는지 레퍼런스들을 찾아봤다. 내가 개발 중인 페이지처럼 빈화면을 잠깐 보여주는 곳도 있었고, 로딩 스피너 UI를 나타내는 웹사이트들도 있었다.
로딩 스피너를 넣을까?
처음엔 당연히 첫 화면에서에서는 스켈레톤 UI를 적용하는 것은 무의미하니, "로딩스피너 UI를 넣어야겠다"라고 생각했다. 그런데 여러 웹사이트들을 살펴보면서, 웹앱 형태로 제작된 사이트들은 로딩 스피터를 사용하는 것이 일반적이지만, 데스크탑 기반의 웹사이트에서는 이 방식이 어색하게 느껴졌다.
큰 화면에서 중앙에 로딩 스피터 하나만 덩그러니 있는 것도 UX적으로 미묘했다.. 그래서 다른 웹사이트들을 테스트해봤다. 그런데 당근마켓의 웹사이트를 살펴보니, SSR 페이지를 새로고침해도 UI는 그대로 유지되었고, 변경된 데이터들만 갱신되고 있었다.
오호라, 이건 어떻게 한거지?
이건 어떻게 구현 한거지 고민이 들었다.
가만 보니, 데이터를 캐싱하고, 업데이트된 데이터들만 패칭하는 방식을 사용한 것 같았다.
🛠️ 1. tanstack-query의 prefetchQuery 시도
처음에는 SSR을 사용하면서도 데이터를 유지하는 방법이 있을 것 같았다.
"tanstack-query의 prefetchQuery 함수를 이용하면 해결되지 않을까?? "
라고 생각했고, 그래서 아래처럼 코드를 변경했다.
await queryClinet.prefetchQuery({
queryKey:['popularLog'],
quertFn" getPopularLog,
});
그리고 각 컴포넌트의 useQuery에서도 코드를 추가했지만 결과는 동일한 SSR 페이지였다. 새로고침하면 여전히 기존 데이터가 날라가고, 데이터를 받아올때까지 빈 화면이 잠깐 보이는 문제가 해결되지 않았다.
🤔 ISR이 SSR과 비슷했던 것 같은데...
Next.js를 공부하면서 봤던 페이지 렌더링 방식이 떠올랐다.
그래서 다시 SSR, ISR 개념들을 찾아보면서 살펴봤다.
ISR은 SSR과 유사하지만, 페이지를 캐싱하고 일정 주기로 업데이트하는 방식이다.
"이걸 적용하면 빈 화면없이 당근마켓 웹사이트처럼 새로고침시에도 기존 데이터를 유지한 채 업데이트를 할 수 있을 것 같았다."
그리고 이 생각이 들었다.
"이 개념을 도대체 어따 써먹나 했는데 여기에 써먹는거였네"
🛠️ 2. ISR 방식 적용
우선 ISR의 작동방식은 이러하다.
1. 첫 번째 요청 또는 빌드
- 서버에서 데이터를 가져와 페이지를 생성하고 캐시한다. (SSR 방식과 동일)
2. 설정된 시간(revaildate)내 캐싱된 데이터 제공
- 캐시된 페이지를 즉시 제공한다.
- 서버에 새 데이터가 있더라도 캐시된 데이터를 제공한다.
3. 설정된 시간이후의 첫 요청
- 캐시된 페이지를 즉시 제공한다(빠른 응답을 위해)
- 동시에 백그라운드에서 새 데이터를 가져와 페이지를 재생성한다.
- 재생성이 완료되면 새 데이터를 캐시한다.
ISR 적용 코드
// ISR 설정: 300초(5분)마다 페이지 재검증
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 }
}).then(res => res.json());
공식적으로 Next.js 13부터 app 디렉토리에서 fetch를 확장하여 제공하는 기능이라 코드는 정말 간단했다.
📌 결과
결과는 이제 페이지를 새로고침해도 빈화면이 나타나지 않고 캐싱된 데이터가 나타난다. 다만 한가지 맘에 들지 않는 부분은 새로고침을 해도 큰 변화가 없으니 새로고침이 되었는지 안되었는지 모른다는 점인 것 같다. 🥹
그래도 이 계기를 통해서 SSR과 ISR을 차이에 대해서 확실하게 알게된 계기인 것 같다.
ISR, SSR 정리
특징 | SSR (Server Side Rendering) | ISR (Incremental Static Regeneration) |
렌더링 방식 | 요청이 있을 때마다 서버에서 페이지 생성. | 미리 생성된 정적 페이지를 일정 시간마다 갱신. |
데이터 패칭 시점 | 매 요청마다 서버에서 데이터 가져옴. | 초기 빌드 시 정적 페이지 생성 + 일정 주기마다 데이터 갱신 |
초기 로딩 속도 | 느림 | 빠름 |
서버 부하 | 높음 (모든 요청이 서버에서 처리됨) | 낮음 (캐싱된 페이지 제공) |
SEO 최적화 | ✅ 가능 | ✅ 가능 |
최신 데이터 반영 | ✅ 가능 | ❌ 즉시 반영 불가 |
적합한 경우 | 자주 변경되는 데이터 | 자주 변경되지만 즉시 갱신이 필요 없는 데이터 |
그러면 SSR말고 ISR만 사용하는게 이득아닌가?
여기까지 왔을때, 나는 SSR말고 ISR을 'revaildate : 1'을 설정하고 쓰는게 더 이득이 아닌가?' 라고 의문이 들었다.
이에 대한 대답은 결론부터 말하면 revaildate : 0 을 사용하면 SSR과 거의 동일하게 동작하겠지만, 완전히 대체할 순 없다. 즉 SSR처럼 동작할 순 있지만, SSR의 몇 가지 중요한 기능이 부족하다.
최적의 선택 기준
- SSR이 필요한 경우
- 로그인 상태에 따라 데이터를 다르게 보여줘야 할 때
- 요청마다 다른 데이터를 제공해야 할 때
- API 응답을 요청 시점에 직접 가공해야 할 때
마치며
이번 문제를 겪으며 ISR에 대한 개념을 좀 더 명확히 이해할 수 있었다. SSR은 항상 최신 데이터를 제공하고, ISR은 캐싱을 활용해 일정 주기로 갱신할 수 있다는 장점이 있다. 결국, 서비스의 특성과 요구사항에 따라 SSR,CSR,SSG,ISR을 적절히 선택하는 것이 중요할 것 같다.
읽어주셔서 감사합니다.
'프론트엔드 기록 > 리액트' 카테고리의 다른 글
<Link> 태그 페이지 이동 시 스크롤 유지 방지하기 (1) | 2025.03.25 |
---|---|
React Query와 Intersection Observer를 활용한 무한스크롤 구현 (0) | 2025.03.05 |
리액트 Flux 패턴이란?( vs MVC 모델 ) (2) | 2024.10.29 |
[Next.js 14] FSD 기능 관점 폴더 아키텍처에 대한 생각 (2) | 2024.10.04 |
리액트의 동작 원리, Virtual DOM (1) | 2024.09.12 |