SSR / CSR / loader 정리
1. 서버와 브라우저
브라우저 — 사용자 개인의 노트북, 핸드폰에서 도는 것. 사람마다 각자 있음
서버 — 모든 사용자의 요청을 받는 Node 프로세스
개발 중 내 컴퓨터: Node 프로세스(서버) + 크롬(브라우저) ← 한 기계
배포 후 AWS: Node 프로세스 / 사용자 기기: 크롬
터미널은 컴퓨터가 아니라 프로세스의 출력을 보여주는 창. npm run dev로 띄운 Node 프로세스가 서버 역할을 하고, 그게 출력한 글자가 터미널에 나옴.
loader는 서버에서 돌기 때문에 console.log가 터미널에만 찍힘.
2. CSR vs SSR
컴포넌트 함수를 어디서 실행하느냐의 차이.
| CSR | SSR | |
| 서버에서 실행 | ❌ | ✅ 서버에서 실행 후 HTML 생성 |
| 브라우저에서 실행 | ✅ DOM 생성 | ✅ 검증 + 이벤트 연결 |
| 총 실행 | 1번 | 2번 |
| 서버가 보내는 것 | <div id="root"></div> | 내용이 채워진 HTML |
SSR에서 두 번째 실행이 하이드레이션. 화면을 새로 그리는 게 아니라 이미 있는 DOM에 이벤트와 상태를 붙이는 작업.
여기서 나오는 차이
CSR — 하이드레이션 불일치가 없음. new Date(), Math.random(), window 자유롭게 사용 가능
SSR — 두 실행 결과가 같아야 함. 다르면 경고, 심하면 React가 서버 HTML을 버리고 다시 그림(SSR 이점 소멸). 서버에는 window가 없어서 방어 필요
useEffect(() => {
const w = window.innerWidth; // useEffect는 브라우저에서만 실행
}, []);
3. HydrationBoundary
역할
서버 캐시를 브라우저 캐시로 옮기는 것.
서버와 브라우저는 다른 메모리라서 QueryClient도 각자 존재함. loader가 서버 쪽을 채워도 useQuery는 브라우저 쪽만 보기 때문에 "데이터 없네" 하고 다시 요청함.
단절을 잇는 방법
- dehydrate — 서버 캐시를 JSON으로 변환
- HydrationBoundary — 그 JSON을 브라우저 캐시에 넣어줌
전체 흐름
1. 서버에서 QueryClient 생성 ← 요청마다 새로 만들어야 함
2. ensureQueryData로 API 호출
3. dehydrate로 JSON 변환
4. HTML에 실어 전송
5. HydrationBoundary가 브라우저 QueryClient에 부어줌
언제 필요 없나
| loader만 사용 | ❌ useLoaderData로 끝 |
| loader + React Query | ✅ 두 시스템을 잇는 다리 |
| React Query만 사용 | ❌ 브라우저에서 가져오니 옮길 게 없음 |
useQuery는 useLoaderData를 안 봄. 자기 캐시만 봄. 그래서 다리가 필요한 것.
// loader만 쓰면 이게 끝
export async function loader() {
return { influencers: await getInfluencerList() };
}
export default function Home() {
const { influencers } = useLoaderData();
return <InfluencerList items={influencers} />;
}
4. loader를 쓰는 이유
- SEO — 서버가 데이터까지 채운 HTML을 보내니 크롤러가 읽을 수 있음
- 하이드레이션 깜빡임 방지 — 캐시를 안 넘기면 목록이 보였다가 사라졌다가 다시 나타남
그럼 왜 굳이 useQuery까지 쓰나
useLoaderData가 더 간단한데도 쓰는 이유:
- 프로젝트가 React Query 기반이라 일관성
- 다른 페이지에서도 같은 요청을 쓰므로 캐시 공유
useLoaderData는 라우트에 종속돼서 다른 페이지와 캐시를 공유하지 못함. React Query는 queryKey 기준이라 라우트와 무관하게 재사용됨.
5. SSR 확인 방법
Cmd + Option + U (맥) 또는 우클릭 → 페이지 소스 보기 → Cmd + F로 검색
- SSR로 그린 데이터 → 나옴
- CSR로 가져온 데이터 → 안 나옴
CSR 데이터는 브라우저가 JS를 실행한 뒤 DOM에 들어가는데, 소스 보기는 서버가 보낸 원본 HTML만 보여주기 때문. 크롤러가 보는 것도 이 화면.
한 줄 요약
컴포넌트 함수를 서버에서 실행하면 SSR,
브라우저에서만 실행하면 CSR이고,
loader는 서버에서 데이터를 미리 가져오는 역할,
HydrationBoundary는 그 데이터를 브라우저 캐시로 옮기는 다리다
'React' 카테고리의 다른 글
| js를 ts로 변환하기(리액트 프로젝트) (4) | 2026.01.09 |
|---|---|
| [react-query] 좋아요 낙관적 업데이트 (0) | 2025.09.29 |
| react-hook-form을 알아보자 (2) | 2025.08.13 |
| useState에서 상태 변경 시 바로 재렌더링 일어나지 않는 버그 (2) | 2025.08.13 |
| throttle과 debounce 사용하기 (2) | 2025.06.21 |