서버는 클라이언트로부터 request를 받으면 해당 클라이언트에게 response 데이터를 보내고
이 response 데이터를 기반으로 사용자가 화면을 보게 된다.
이때, 사용자가 볼 수 있는 화면을 생성하거나 랜더링(rendering)하는 방법에 여러가지가 있다.
rendering method
- SSR (Server Side Rendering)
- CSR (Cliendt Side Rendering)
- SSG (Static Site Generation)
- ISR (incremental static regeneration)
각 방법들은 성능, SEO(검색 엔진 최적화) 등에 따라 선택하고, 몇몇 웹 애플리케이션에서는 혼합해서 사용하기도 한다.
이번 게시글에서 여러 랜더링 방법을 알아보고, 내가 구축할 웹 서비스에 최적화된 방법을 선택해 보자!
SSR 는 Server Side Rendering 의 약자로 서버에서 request를 받은 뒤, 서버에서 HTML, CSS, 데이터를 조합해서 완전한 웹 페이지를 생성한다.
서버에서 생성한 웹 페이지를 클라이언트로 보내서, 브라우저에서는 이 웹 페이지를 랜더링해서 사용자에게 화면을 출력해준다.
이렇게 서버에서 먼저 웹 페이지를 생성하여 클라이언트로 보내게 되면 2가지 장점이 있다.
SSR
- 초기 로딩이 상대적으로 빠르다.
- SEO 에 최적화 되어있다.
브라우저에서 따로 랜더링 할 데이터가 없어 초기 로딩(완전한 웹 페이지) 에 매우 빠르다.
또, 서버에서 웹 페이지를 생성하기 때문에, 미리보기 또는 메타 데이터가 포함된 초기 HTML 로 인해서 SEO (Search Engine Optimization) 즉, 검색 엔진에 최적화 될 수 있다.
이렇게 SSR 을 말로 설명만 하면, 이해하기에 조금 애매할 수 있어서 장고 소스코드로 비교해가며 알아보자.
# views.py
def ssr_index(request):
# 서버에서 첫 로딩 HTML을 랜더링 해서 반환시키기
return render(request, "ssr.html", {"data": "이건 SSR"})
<!-- ssr.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>SSR</title>
</head>
<body>
<h1>SSR 페이지</h1>
<!-- 서버에서 렌더링한 내용 -->
{% if data %}
<div>{{ data }}</div>
{% else %}
<div>데이터 없음</div>
{% endif %}
</body>
</html>
위 소스와 같이 render 함수로 서버에서 미리 data
라는 데이터도 HTML 소스와 함께 웹 페이지를 생성한 뒤, 클라이언트로 보내서 브라우저에서 초기 로딩이 매우 빠르다.
CSR 는 Client Side Rendering 의 약자로 서버에서는 클라이언트로 HTML 틀을 보내주고, 브라우저에서 동적으로 화면을 생성해준다.
보통 CSR은 브라우저에서 JavaScript 를 통해 페이지를 랜더링하여 사용자에게 화면을 보여준다.
CSR 을 사용하면 얻게 되는 장점은 다음과 같다.
(SSR에서의 초기 로딩 속도와는 다른 의미이니 주의해서 보자.)
CSR
- 초기 로딩 속도가 빠르다. (SSR 과는 다른 로딩 속도가 빠르다.)
- 상호작용에 좋다.
- 서버에서의 부하가 적어진다.
CSR 방식은 서버에서 초기 HTML 빈 틀만 보내줘서 사이트 접속은 빠르지만, 이후 데이터를 로딩하는 부분에서는 브라우저에서 웹 페이지를 구성하므로 다소 늦을 수 있다.
또, JavaScript 를 사용하여 새로운 웹 페이지 로딩없이 상호작용이 가능하다.
# views.py
def csr_index(request):
# 초기 HTML(틀) 을 클라이언트로 전송
# 클라이언트에서는 JavaScript 로 콘텐츠를 동적으로 rendering
return render(request, "csr.html")
<!-- csr.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>CSR</title>
</head>
<body>
<h1>CSR 페이지</h1>
<!-- JavaScript로 동적으로 렌더링될 공간 -->
<div id="csrData"></div>
<script>
// 클라이언트 측 JavaScript로 내용을 동적으로 렌더링
var csrData = document.getElementById("csrData");
csrData.innerHTML = "<p>클라이언트에서 동적으로 렌더링한 내용</p>";
</script>
</body>
</html>
위 소스에서 서버로부터 HTML 소스를 받고, 클라이언트 측 브라우저에서는 JavaScript 를 통해 csrData
를 처리하여 사용자에게 동적인 웹 페이지를 보여준다.
초기 로딩 속도(접속 속도) 는 빠를지라도, 데이터의 양에 따라서 최종 화면까지는 느릴 수 있다.
SSG 는 Static Site Geration 의 약자로 주로 정적인 웹 사이트를 랜더링 하는데 사용된다.
SSG는 서버에서 미리 정적인 웹 페이지를 구현해서 클라이언트로 보내주고, 마찬가지로 브라우저에서도 따로 콘텐츠를 생성할 필요가 없어 로딩 속도가 매우 빠르다.
SSG
- 성능 최적화 (SERVER -> 브라우저 화면 까지 최소의 대기시간)
- SEO 에 최적화 되어있다.
- 동적 웹 페이지에 비해 안전하다.
SSG는 서버에서 미리 웹 페이지를 구성해놨기 때문에 서버에서 클라이언트의 브라우저가 사용자에게 화면을 띄울 때 까지의 과정에서 최소의 대기시간을 가진다.
또, SSR 과 마찬가지로 서버에서 웹 페이지를 구성하였기 때문에 SEO에 최적화 되어있다.
SSG는 다른 동적 방식 SSR과 CSR 과 달리 정적 웹 페이지라서 공격 벡터가 상대적으로 적어 안정적이다.
# views.py
def ssg_index(request):
# 서버에서 미리 생성해둔 웹 페이지를
# 클라이언트로 보낸다.
return render(request, "ssg.html")
<!-- ssg.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>SSG</title>
</head>
<body>
<h1>SSG 페이지</h1>
<div>서버에서 미리 생성한 정적 페이지</div>
</body>
</html>
위 소스에서는 장고 templates 에 미리 저장해둔 HTML(웹 페이지) 를 클라이언트로 보내고, 클라이언트측 브라우저에서는 그냥 이 페이지를 바로 화면에 띄워 매우 적은 대기시간을 가진다.
하지만, 서버에서 모든 request 에 대한 정적 웹 페이지를 생성하게 된다면 서버 데이터에 과부하가 걸리게 되므로 매우 비효율적이다.
따라서, SSG 방식과 다른 동적인 랜더링 방식을 적절히 사용하는 것이 좋다.
(애초에 그 어떤 request 가 올지 모르는데 모든 웹 페이지를 생성해 둔다는 것은 말이 안된다.)
ISR은 Incremental Static Regeneration 의 약자로 SSG 에서 발전한 정적 웹 페이지를 랜더링 하는 방법이다.
ISR은 초기에 SSG와 마찬가지로 정적 페이지를 클라이언트로 보내지만
일정한 주기로 일정 시간이나 어떤 이벤트가 발생되면 웹 페이지를 재생성 해준다는 차이점이 있다.
ISR
- SSG와 비슷하지만 일정시간마다 refresh
- SSG와 비슷한 특징을 가진다.
특정 분기점 마다 refresh 할 수 있는 특징을 가져서, 주로 뉴스 피드나 주식 사이트와 같은 곳에서 자주 쓰인다.
또, ISR을 장고에서 구현하려면 Next.js 와 같은 FE 프레임워크와 함께 사용된다.