화면은 누가 그리나 — SSR · CSR, 그리고 그 사이의 것들
화면은 누가 그리나 — SSR · CSR, 그리고 그 사이의 것들
"Next.js 쓰면 SSR 아니에요?"라는 질문에서 시작한, 렌더링이 일어나는 '장소'에 대한 지형도 정리.
들어가며: "Next.js 쓰면 SSR 아니에요?"
앱인토스 앱은 전부 React로 짜는데, 이게 다 CSR이에요. 그런데 블로그나 랜딩 페이지 얘기가 나오면 갑자기 "그건 SSR이 유리하지"라는 말이 나오죠. 누가 물었어요. "같은 React인데 왜 어떤 건 CSR이고 어떤 건 SSR이에요? Next.js 쓰면 그냥 SSR 아니에요?"
여기엔 흔한 오해가 하나 있어요. SSR과 CSR을 프레임워크 이름이나 좋고 나쁨의 문제로 보는 것. 사실 둘의 차이는 딱 하나예요 — "HTML을 누가, 어디서 만드느냐." 서버가 만들면 SSR, 브라우저가 만들면 CSR. 그게 전부고, 나머지는 거기서 파생되는 결과들이에요.
가장 중요한 한 갈래: HTML을 누가 만드나
다른 건 다 잊어도 이것만 기억하면 절반은 끝나요. 사용자가 주소를 치고 화면이 보이기까지, "내용이 담긴 HTML"을 누가 만드느냐가 갈림길입니다.
CSR (Client-Side Rendering)
서버는 거의 빈 HTML 한 장 + JS 번들만 보냅니다. 브라우저가 그 JS를 내려받아 실행하면, 그제서야 화면이 그려져요. 즉 그리는 주체가 브라우저. React로 만든 일반 SPA(앱인토스 앱 포함)가 여기예요.
SSR (Server-Side Rendering)
서버가 요청을 받을 때마다 내용이 다 채워진 완성 HTML을 만들어서 보냅니다. 브라우저는 받자마자 글씨가 보여요. 그다음 JS가 붙어서(=하이드레이션) 버튼이 눌리는 '살아있는' 페이지가 됩니다. 그리는 주체가 서버. Next.js의 서버 렌더링이 대표적이에요.
CSR을 자세히 — 빈 도화지를 받아 직접 그린다
CSR의 첫 응답은 사실상 <div id="root"></div> 한 줄과 JS 묶음이에요. 브라우저가 JS를 다 받아 실행하기 전까지 사용자는 흰 화면(혹은 로딩 스피너)을 봅니다.
- 첫 화면은 느립니다. JS 다운로드 → 파싱 → 실행 → 데이터 fetch → 렌더, 이 과정을 다 거쳐야 글자가 보여요. 번들이 크면 더 늦어집니다.
- 이후는 빠릅니다. 한 번 떠 있으면 페이지 이동 시 서버에서 HTML을 다시 안 받고, 필요한 데이터(JSON)만 가져와 화면 일부만 갈아끼워요. 앱처럼 매끄럽죠.
- SEO엔 불리합니다. 검색 크롤러가 받는 건 빈 HTML이에요. 요즘 구글은 JS를 실행해 내용을 읽기도 하지만, 비용·지연·누락 위험이 있어 "확실히 걸려야 하는 페이지"엔 약점입니다.
- 서버는 편합니다. 정적 파일(HTML·JS) 한 벌을 CDN에 올려두면 끝. 요청마다 서버가 일할 게 없어요.
SSR을 자세히 — 완성품을 받아 손질만 한다
SSR은 서버가 요청을 받을 때마다 데이터까지 채운 HTML을 만들어 보냅니다. 그래서 브라우저는 첫 응답에 이미 글자가 다 들어있어요.
- 첫 화면이 빠릅니다. 받자마자 보이니 체감 속도(FCP)가 좋고, 느린 기기일수록 차이가 커요.
- SEO에 강합니다. 크롤러가 완성된 HTML을 그대로 읽으니 내용이 100% 노출돼요. 검색 유입이 중요한 페이지의 정석입니다.
- 대신 서버가 일합니다. 요청마다 렌더링을 도니 서버 비용·부하가 생기고, 서버 처리가 느리면 TTFB(첫 바이트까지 시간)가 늘어요.
- 하이드레이션이라는 함정. 받은 HTML은 '그림'일 뿐이라, JS가 붙기 전엔 버튼이 안 눌려요. 서버가 그린 것과 브라우저가 그릴 것이 어긋나면 hydration mismatch 경고가 나는데, 1인 개발자가 SSR에서 제일 자주 만나는 버그가 이거예요.
그 사이의 것들 — 사실 두 개가 아니다
SSR/CSR은 양 극단이고, 실무에서 많이 쓰는 건 그 사이 칸들이에요. 같은 "서버가 미리 그린다"도 언제 그리느냐로 갈립니다.
- SSG (정적 생성) — 요청 때가 아니라 빌드 때 HTML을 다 만들어 CDN에 올려둬요. 내용이 자주 안 바뀌는 블로그·문서·랜딩에 최적. 서버가 요청마다 일하지 않아 가장 빠르고 가장 쌉니다. (이 블로그도 개념상 여기예요.)
- ISR (증분 정적 재생성) — SSG인데 "N초마다 한 번씩 다시 만들기"를 붙인 것. 정적의 속도 + 가끔 갱신 둘 다 노릴 때.
- RSC (React Server Components) — 컴포넌트 단위로 "이건 서버에서, 이건 브라우저에서"를 섞어요. 서버 컴포넌트는 JS 번들에 안 실려서 클라 번들이 가벼워집니다. Next.js App Router의 기본값이에요.
그래서 "Next.js = SSR"은 정확히는 틀렸어요. Next.js는 위 방식들을 페이지·컴포넌트마다 고를 수 있게 해주는 틀이지, 자동으로 전부 SSR이 되는 게 아닙니다.
비교 표
| 축 | CSR | SSR | SSG(정적) |
|---|---|---|---|
| HTML 만드는 곳 | 브라우저 | 서버(요청마다) | 빌드 때 한 번 |
| 첫 화면 속도 | 느림 | 빠름 | 가장 빠름 |
| 이후 이동 | 매우 빠름 | 빠름(하이드레이션 후) | 보통(부분 CSR화 가능) |
| SEO | 약함 | 강함 | 강함 |
| 서버 부담 | 거의 없음 | 요청마다 발생 | 거의 없음 |
| 실시간/개인화 | 강함 | 강함 | 약함(고정 콘텐츠) |
| 운영 난이도 | 낮음 | 중~높음(hydration) | 낮음 |
| 전형적 예 | 앱인토스 앱, 대시보드 | 커머스 상세, 뉴스 | 블로그, 문서, 랜딩 |
언제 뭘 쓰나
질문 몇 개만 따라가면 대부분 답이 나와요.
- 검색 노출 불필요 + 로그인 뒤 앱형 화면 → CSR
- 검색 필요 + 내용 거의 안 바뀜 → SSG(정적)
- 검색 필요 + 가끔 갱신이면 충분 → ISR
- 검색 필요 + 매 요청 최신·개인화 필수 → SSR
그래서 나는 — 앱인토스는 CSR, 블로그는 정적
내가 운영하는 것들을 이 지도 위에 올려보면 답이 거의 자동으로 나와요.
- 앱인토스 앱들 → 전부 CSR. 토스 앱 안의 웹뷰에서 도니까 구글 검색에 걸릴 일이 없어요. SEO가 필요 없으니 CSR의 유일한 약점이 사라집니다. 대신 로그인 뒤 탭을 옮겨 다니는 앱형 화면이라 CSR의 매끄러운 전환이 딱 맞고, 정적 파일만 올리면 돼서 운영 부담이 0이에요. 1인 개발자에게 이게 제일 큽니다.
- 이 블로그 → 정적(SSG). 검색 유입이 생명인데 내용은 한번 쓰면 거의 안 바뀌어요. 요청마다 서버가 그릴 이유가 없으니 빌드 때 만들어 CDN에 얹는 게 가장 빠르고 가장 쌉니다.
- 고양이 타로 앱 → 서버를 아예 뺐죠. 지난 글에서 Edge Function으로 옮긴 그 결정도 결국 같은 사고예요. "이 화면이 서버 렌더링을 요구하는가?"를 먼저 묻고, 아니면 서버를 안 두는 쪽으로.
요점은 "SSR이 더 고급이라서 써야 한다"가 아니라, 내 화면이 서버 렌더링을 요구하느냐예요. 앱인토스 앱에 굳이 SSR을 얹었다면 서버 비용과 하이드레이션 버그만 떠안고 얻는 건 없었을 거예요.
마무리: 렌더링 위치는 도구지 목표가 아니다
"Next.js 쓰면 SSR 아니에요?"에 대한 가장 정직한 답은 "Next.js는 어디서 그릴지 고를 수 있게 해주는 틀일 뿐, 내 페이지가 그걸 요구해야 SSR을 쓴다"예요. 제일 흔한 실수는 제일 있어 보이는 방식을 먼저 고르고 거기에 화면을 끼워 맞추는 거더라고요.
순서는 반대여야 합니다. 먼저 묻기 — 이 화면이 검색에 걸려야 하나? 그다음 — 내용이 요청마다 바뀌나, 운영은 누가 감당하나? 이 두 질문이면 CSR · SSR · SSG · ISR 중 어디로 갈지 거의 정해져요. 프레임워크 선택은 그다음입니다.
"use client" 한 줄이 번들 크기에 미치는 영향을 실제 앱 기준으로 뜯어볼 예정이에요.