
사용자가 기다리는 시간을 어떻게 줄일 수 있을까?
이전에 정리했던 웹 성능 최적화, Lazy Loading, Code Splitting, Tree Shaking, 이미지 최적화가 주로 불필요한 리소스를 줄이거나 늦게 불러오는 것에 가까웠다면,
지연시간 최적화는 한 단계 더 나아가서 필요한 데이터를 더 빠르게 가져오게 만드는 전략이라고 볼 수 있다.
지연시간 최적화(Latency Optimization)
사용자가 요청을 보낸 뒤 응답을 받기까지 걸리는 시간을 줄여, 페이지나 기능이 더 빠르게 반응하도록 만드는 최적화 방식
예를 들어 사용자가 버튼을 눌렀을 때, 다음과 같은 과정이 일어난다.
사용자 클릭 → 서버 요청 → 서버 처리 → 응답 도착 → 화면 렌더링
이 과정에서 기다리는 시간이 길어지면 사용자는 서비스를 “느리다”고 느끼게 된다.
그래서 지연시간 최적화의 핵심은 다음과 같다.
사용자가 요청하기 전에 미리 준비하거나, 가까운 곳에서 빠르게 가져오거나, 이미 받은 것은 다시 요청하지 않도록 하는 것
이번 주제의 핵심 태그는 Caching, Prefetching, CDN이다.
Caching은 한 번 받아온 데이터나 리소스를 저장해두고, 다음에 필요할 때 다시 서버에 요청하지 않고 저장된 것을 재사용하는 방식이다.
예를 들어 로고 이미지, 폰트, CSS, JavaScript 파일처럼 자주 바뀌지 않는 리소스는 매번 새로 받을 필요가 없다.
첫 방문:
서버에서 이미지 다운로드 → 브라우저에 저장
두 번째 방문:
서버 요청 없이 브라우저 캐시에서 바로 사용
이렇게 하면 네트워크 요청 자체가 줄어들기 때문에 훨씬 빠르게 느껴질 수 있다.
이전에 Network 탭에서 이미지 크기 대신 (disk cache) 라고 표시된 것을 본 적이 있다.
이것이 바로 브라우저 캐시다.
(disk cache)
→ 브라우저가 이전에 저장해둔 파일을 디스크에서 다시 가져온 것
(memory cache)
→ 메모리에 저장된 파일을 바로 가져온 것
즉, 이미 받아온 이미지를 또 다운로드하지 않고 재사용했기 때문에 네트워크 비용이 줄어든 것이다.
프론트엔드에서는 크게 두 가지 캐싱을 생각할 수 있다.
이미지, CSS, JavaScript, 폰트 같은 파일을 캐싱하는 것이다.
logo.png
main.css
bundle.js
Pretendard.woff2
이런 리소스들은 브라우저 캐시나 CDN 캐시를 활용할 수 있다.
API로 받아온 데이터를 일정 시간 동안 재사용하는 것이다.
예를 들어 React Query를 사용하면 다음과 같이 캐싱 전략을 줄 수 있다.
useQuery({
queryKey: ['posts'],
queryFn: getPosts,
staleTime: 1000 * 60 * 5,
});
여기서 staleTime은 “이 시간 동안은 데이터가 신선하다고 보고 다시 요청하지 않겠다”는 의미다.
즉, 사용자가 페이지를 왔다 갔다 해도 매번 API를 다시 호출하지 않기 때문에 응답 대기시간이 줄어들 수 있다.
Prefetching은 사용자가 곧 필요로 할 가능성이 높은 리소스나 데이터를 미리 가져와두는 방식이다.
쉽게 말하면 사용자가 누르기 전에 미리 준비해두기다.
예를 들어 사용자가 게시글 목록 페이지에 있다고 가정해보자.
게시글 목록 페이지
→ 사용자가 특정 게시글을 클릭할 가능성이 높음
→ 상세 페이지 데이터나 코드를 미리 불러와두기
그러면 사용자가 실제로 클릭했을 때 이미 준비되어 있기 때문에 페이지 전환이 훨씬 빠르게 느껴질 수 있다.
Lazy Loading과 Prefetching은 어느 정도 반대 방향의 전략처럼 볼 수 있다.
Lazy Loading
→ 당장 필요 없는 것은 나중에 불러오기
Prefetching
→ 곧 필요할 것 같은 것은 미리 불러오기
하지만 둘의 목적은 같다.
사용자가 느끼는 대기시간을 줄이는 것
다만 접근 방식이 다르다.
Lazy Loading은 초기 로딩 부담을 줄이는 전략이고, Prefetching은 이후 행동을 빠르게 만드는 전략이다.
React Query를 사용하면 특정 데이터를 미리 가져올 수 있다.
예를 들어 채용 공고 목록에서 사용자가 카드에 마우스를 올렸을 때 상세 데이터를 미리 가져올 수 있다.
const queryClient = useQueryClient();
const handlePrefetchRecruitDetail = (id: number) => {
queryClient.prefetchQuery({
queryKey: ['recruitDetail', id],
queryFn: () => getRecruitDetail(id),
});
};
사용 예시는 다음과 같다.
<RecruitCard
onMouseEnter={() => handlePrefetchRecruitDetail(recruit.id)}
onClick={() => navigate(`/recruit/${recruit.id}`)}
/>
그러면 사용자가 클릭하기 전에 상세 데이터를 미리 요청해둘 수 있다.
마우스 올림 → 상세 데이터 미리 요청
클릭 → 이미 캐시된 데이터 사용
결과적으로 상세 페이지가 더 빠르게 뜨는 것처럼 느껴질 수 있다.
이전에 배웠던 React.lazy()도 Prefetching과 연결해서 생각할 수 있다.
Code Splitting은 페이지별 JavaScript 코드를 나누는 것이었다.
const RecruitDetailPage = lazy(() => import('./RecruitDetailPage'));
이렇게 하면 상세 페이지 코드는 처음부터 받지 않고, 상세 페이지에 들어갈 때 받아오게 된다.
그런데 사용자가 곧 상세 페이지에 들어갈 것 같다면, 그 JavaScript 파일을 미리 가져오는 전략도 가능하다.
Code Splitting
→ 필요한 페이지 코드를 분리함
Prefetching
→ 곧 필요할 분리된 코드를 미리 가져옴
즉, Code Splitting과 Prefetching은 함께 사용하면 좋은 전략이다.
CDN(Content Delivery Network)은 전 세계 여러 지역에 서버를 분산해두고, 사용자와 가까운 서버에서 리소스를 전달해주는 방식이다.
원래 서버가 미국에 있다고 가정해보자.
한국 사용자가 이미지를 요청하면 미국 서버까지 요청이 가야 한다.
한국 사용자 → 미국 서버 → 한국 사용자
거리가 멀수록 응답이 느려질 수 있다.
그런데 CDN을 사용하면 한국이나 일본처럼 더 가까운 CDN 서버에서 리소스를 받을 수 있다.
한국 사용자 → 가까운 CDN 서버 → 한국 사용자
그래서 이미지, JavaScript, CSS, 폰트 같은 정적 파일을 훨씬 빠르게 받을 수 있다.
CDN은 단순히 가까운 서버 역할만 하는 것이 아니라, 리소스를 CDN 서버에 저장해두는 캐시 서버 역할도 한다.
첫 번째 사용자:
원본 서버에서 이미지 가져옴 → CDN에 저장
다음 사용자:
원본 서버까지 가지 않고 CDN에서 이미지 받음
즉, CDN은 이렇게 이해할 수 있다.
사용자 가까이에 있는 캐시 저장소
그래서 CDN은 Caching과 아주 강하게 연결되어 있다.
| 개념 | 핵심 | 예시 |
|---|---|---|
| Caching | 이미 받은 것을 저장해두고 재사용 | 이미지, JS, API 데이터 재사용 |
| Prefetching | 곧 필요할 것을 미리 가져오기 | 상세 페이지 데이터 미리 요청 |
| CDN | 가까운 서버에서 리소스 제공 | 이미지, CSS, JS를 CDN에서 전달 |
조금 더 쉽게 말하면 다음과 같다.
Caching
→ 다시 안 받기
Prefetching
→ 미리 받아두기
CDN
→ 가까운 곳에서 받기
예를 들어 채용 공고 서비스를 만든다고 가정해보자.
사용자가 홈 화면에 들어오면 다음과 같은 흐름이 일어난다.
1. 홈 페이지 접속
2. 공고 목록 API 요청
3. 공고 카드 이미지 로딩
4. 사용자가 공고 상세 페이지 클릭
5. 상세 페이지 데이터 요청
6. 상세 페이지 화면 렌더링
여기서 최적화 전략을 적용하면 다음과 같다.
공고 카드 이미지
→ CDN에서 빠르게 가져오기
한 번 받아온 공고 목록 데이터
→ React Query 캐시에 저장하기
사용자가 상세 카드에 관심을 보임
→ 상세 데이터 Prefetching 하기
로고, 폰트, JS 파일
→ 브라우저 캐싱 활용하기
그러면 사용자는 페이지를 이동할 때마다 매번 기다리지 않아도 된다.
사용자가 같은 리소스를 반복해서 요청하지 않도록 Caching을 활용하고, 곧 필요할 가능성이 높은 데이터를 Prefetching으로 미리 준비하며, 정적 리소스는 사용자와 가까운 CDN을 통해 전달할 수 있다.
전반적으로 개념 설명이 깔끔하게 잘 정리되어 있어 한번에 이해하면서 읽었던 것같아요! 비유도 적절하고 흐름이 자연스럽게 읽혔습니다.
지난 주의 주제였던 Lazy Loading과 Prefetching을 비교한 부분이 특히 좋았던 것같습니다. 반대 방향처럼 보이지만 목적이 같다는 설명이 인상적이었고, Code Splitting이랑 연결해서 설명한 것도 이해하기 쉬웠습니다.