4.1.1 싱글 페이지 애프리케이션의 세상
싱글 페이지 애플리케이션이란?
렌더링과 라우팅에 필요한 대부분의 기능을 서버가 아닌 브라우저의 자바스크립트에 의존하는 방식.
페이지를 불러온 이후에는 서버에서 HTML을 내려받지 않고 하나의 페이지에서 모든 작업을 처리한다.
페이지 전환 시 새로운 HTML페이지를 요청하는 게 X
자바스크립트에서 다음 페이지의 렌더링에 필요한 정보만 HTTP요청으로 가져온 다음,
그 결과를 바탕으로 <body/> 내부에 DOM을 추가, 수정, 삭제하는 방법으로 페이지가 전환된다.
전통적인 방식의 애플리케이션과 싱글 페이지 애플리케이션의 작동 비교
과거에는 페이지 전환이 발생할 때마다 새롭게 페이지를 요청, HTML 페이지를 다운로드해 파싱 하는 작업을 거쳤다.
>> 페이지를 처음부터 새로 그려야 해서 부자연스러움
그러나 이러한 페이지 전환을 모두 자바스크립트로 한다면 최초 한번 모든 리소스를 다운로드한 후, 페이지를 전환할 때 추가로 리소스를 다운할 필요가 없다.
경우에 따라 페이지 전체를 새로 렌더링하는 것 X >> 페이지 전환에 필요한 일부 영역만 다시 그리기 때문에 매끄러운 UI
싱글 페이지 애플리케이션으로 사용자의 기기와 인터넷 속도 등 웹 전반을 이루는 환경이 크게 개선됐지만,
실제 사용자들이 느끼는 웹 애플리케이션의 로딩 속도는 별 차이가 없거나 오히려 더 느리다는 문제.
이를 개선하고자 서버에서 페이지를 렌더링해 제동하는 기존 방식의 웹 개발이 다시 떠오르고 있다.
4.1.2 서버 사이드 렌더링이란?
최초에 사용자에게 보여줄 페이지를 서버에서 렌더링해 빠르게 사용자에게 화면을 제공하는 방식.
싱글 페이지 애플리케이션(SPA) VS 서버 사이드 렌더링(SSR)
싱글 페이지 애플리케이션
- 동작 방법
- 사용자가 페이지에 들어오면 빈 HTML에 필요한 데이터와 컴포넌트를 클라이언트(브라우저)에서 렌더링
- 서버는 필요한 데이터(API)를 제공하고, 렌더링은 브라우저가 처리한다.
- 장점
- 빠른 페이지 전환 > 새로고침 없이 부드럽게 작동(앱처럼 동작함)
- 트래픽 감소 > 서버로부터 전체 HTML을 계속 받지 않아도 됨.
- 단점
- 처음에 모든 자바스크립트를 다운해야하기 때문에 초기 로딩 속도가 느리다.
- 검색 엔진이 자바스크립트를 실행하지 않으면 콘텐츠를 못 읽기 때문에 검색 엔진 최적화가 어렵다.
서버 사이드 렌더링
- 동작 방법
- 사용자가 페이지 요청 > 서버에서 필요한 데이터를 가져와 완성된 HTML을 생성 > 브라우저로 전달 > 화면에 표시
- 이후에 자바스크립트가 실행되어 페이지에 동작을 추가한다.
- 장점
- 서버에서 만들어진 HTML을 바로 보여줄 수 있어 초기 로딩이 빠르다.
- 검색 엔진과 SNS공유 등 메타데이터 제공이 쉽다. (검색 엔진 최적화에 유용하기 때문)
- 모든 요청이 완전히 완료된 이후에 완성된 페이지를 제공하기 때문에 누적 레이아웃 이동이 적다.
- 사용자의 디바이스 성능에 비교적 자유롭다.
- 인증 혹은 민감한 작업을 서버에서 수행하고 그 결과만 브라우저에 제공하기 때문에 보안에 좀 더 안전하다.
- 단점
- 모든 요청마다 서버가 HTML을 생성해야 해서 부담이 크다.
- 페이지 이동마다 서버 요청이 발생해 페이지 전환이 느리다.
4.1.3 SPA와 SSR을 모두 알아야 하는 이유
서버 사이드 렌더링 역시 만능이 아니다
웹페이지의 설계와 목적, 우선순위에 따라 싱글 페이지 애플리케이션이 더 효율적일 수도 있다.
싱글 페이지 애플리케이션과 서버 사이드 렌더링 애플리케이션
- 가장 뛰어난 싱글 페이지 애플리케이션은 가장 뛰어난 멀티 페이지 애플리케이션보다 낫다.
- 평균적인 싱글 페이지 애플리케이션은 평균적인 멀티 페이지 애플리케이션보다 느리다.
둘 중 어느 것이 완벽하게 옳다고 말할 수 없으므로 두 가지를 모두 이해하고
필요에 따라 맞는 방법을 사용할 수 있어야 한다.
4.2 서버 사이드 렌더링을 위한 리액트 API 살펴보기
리액트는 프런트엔드 라이브러리로 브라우저 자바스크립트 환경에서 렌더링 할 수 있는 방법을 제공하지만 서버에서 렌더링 하는 API 또한 제공한다.
이 API는 당연히 Node.js와 같은 서버 환경에서만 실행할 수 있으며 window 환경에서 실행 시 에러가 발생할 수 있다.
리액트에서 서버 사이드 렌더링을 실행할 때 사용되는 API를 확인하기 위해서는 react-dom/server.js를 확인하면 된다.
여기에 있는 함수들이 무엇인지 또 어떤 역할을 하는지 살펴보자.!
4.2.1 renderToString
인수로 넘겨받은 리액트 컴포넌트를 렌더링해 HTML문자열로 반환하는 함수
서버 사이드 렌더링을 구현하는 데 가장 기초적인 API
최초의 페이지를 HTML로 먼저 렌더링하는 역할을 하는 함수
예시 코드
const result = ReactDOMServer.renderToString(
React.createElement('div', { id: 'root' }, <SampleComponent />),
)
동작 방법
- 서버에서 리액트 컴포넌트를 준비한다.
- renderToString 함수를 사용해 컴포넌트를 HTML 문자열로 변환한다.
- 변환된 HTML 문자열을 브라우저로 보내면, 브라우저는 이를 화면에 렌더링 한다.
- 이후 리액트가 클라이언트에서 동작을 재연결 하여 인터랙티브 한 동작을 추가한다.
4.2.2 renderToStaticMarkup
renderToString과 매우 유사한 함수로 두 함수 모두 리액트 컴포넌트를 기준으로 HTML 문자열을 만든다.
renderToStaticMarkup은 HTML에서 리액트 전용 속성 (ex. data-reactroot) 같은 추가적인 DOM속성을 생성하지 않는다.
따라서 리액트 전용 속성이 제거된 깔끔한 HTML 결과물을 생성하며, HTML 크기를 약간 줄여주는 장점이 있다.
4.2.3 renderToNodeStream
리액트에서 서버 사이드 렌더링(SSR)을 할 때, HTML을 스트리밍 방식으로 브라우저에 전송하는 기능을 제공한다.
기존의 renderToString과는 달리, HTML을 한 번에 모두 생성하지 않고, 조각(chunk) 단위로 나눠서 전송한다.
- 차이점
- renderToString: 전체 HTML을 한 번에 생성하고 브라우저로 보낸다. HTML 생성이 끝날 때까지 기다려야 브라우저에 표시된다.
- renderToNodeStream: HTML을 한 번에 모두 생성하지 않고, 스트림 형태로 조금씩 브라우저에 전송한다. 브라우저는 전송받은 부분부터 바로 렌더링 할 수 있다.
- 장점
- 빠른 초기 렌더링: 데이터를 모두 처리하지 않아도, 사용자가 부분적으로라도 페이지를 볼 수 있다.
- 큰 데이터 처리 최적화: 메모리를 효율적으로 사용하고, 서버 부하를 줄일 수 있다.
- 사용 사례:
- 대규모 HTML이나 복잡한 UI를 렌더링할 때 초기 표시 시간을 줄이고 싶을 때.
- 블로그, 쇼핑몰 같은 곳에서 빠른 사용자 경험이 중요한 경우.
4.2.4 renderToStaticNodeStream
renderToNodeStream과 제공하는 결과물은 동일하나, renderToStaticMarkup과 마찬가지로 리액트 자바스크립트에 필요한 리액트 속성이 제공되지 않는다.
hydrate를 할 필요가 없는 순수 HTML 결과물이 필요할 때 사용하는 메서드다.
4.2.5 hydrate
리액트에서 서버 사이드 렌더링(SSR)으로 미리 생성된 HTML에 이벤트와 리액트 상태를 연결하는 과정(즉, 클라이언트 쪽에서의 초기화 작업)을 수행한다.
역할
- 서버에서 이미 렌더링 된 HTML을 재사용하면서, 클라이언트 측에서 React 컴포넌트를 "활성화"시킨다.
- HTML만으로는 동적인 동작(클릭, 입력 등)이 없기 때문에, 브라우저에서 이를 연결해 주는 작업이 필요하다.
사용 방법
import { hydrate } from 'react-dom';
import App from './App';
const rootElement = document.getElementById('root');
hydrate(<App />, rootElement);
HTML에 미리 렌더링 된 <div id="root"> 내부 내용을 기준으로 React 컴포넌트를 연결.
차이점:
- render: HTML이 없는 상태에서 React 컴포넌트를 처음부터 렌더링 한다.
- hydrate: 이미 서버에서 렌더링 된 HTML이 있는 상태에서 React 컴포넌트를 연결하여, DOM 업데이트를 최소화한다.
주의할 점:
- 서버와 클라이언트의 렌더링 결과가 다르면 경고 메시지가 나타날 수 있다.
- 이런 문제를 방지하려면 서버와 클라이언트의 렌더링 결과를 정확히 맞추는 것이 중요하다.
Next.js
4.3.1 Next.js란?
Vercel이라는 미국 스타트업에서 만든 풀스택 웹 애플리케이션을 구축하기 위한 리액트 기반 프레임워크.
서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG)을 쉽게 구현할 수 있도록 설계되었다.
- 서버 사이드 렌더링을 더 쉽게 하기 위해 Facebook의 실험적 프로젝트인 react-page에서 시작된 아이디어를 발전시켜 탄생했다.
- Next.js는 React로 만들어진 애플리케이션의 URL 구조를 효과적으로 관리하고, 서버와 클라이언트에서 모두 렌더링이 가능하다.
- 사용자층이 넓고, Vercel의 강력한 지원을 받으며 지속적으로 업데이트되고 있다. 최신 웹 기술(SWR, SWC, Turbopack 등)과의 통합도 잘 되어 있어 리액트 서버 사이드 렌더링 프로젝트의 대표적인 선택지로 자리 잡았다.
- 2023년 기준으로 13 버전까지 출시되었으며, 다른 프레임워크(Remit, Hydrogen 등) 보다 더 많이 사용된다.
4.3.2 Next.js 시작하기
create-next-app으로 개발자가 빠르게 Next.js 기반 프로젝트를 생성할 수 있게 돕는다.
npx create-next-app@latest --ts
프로젝트 생성 후 파일을 열어 구조를 살펴보자.
package.json
npm프로젝트를 살펴볼 때는 package.json을 먼저 봐야 한다.
프로젝트 구동에 필요한 모든 명령어 및 의존성이 포함돼 있어 프로젝트의 대략적인 모습을 확인할 수 있다.

- next: Next.js의 기반이 되는 패키지
- eslint-config-next: Next.js 기반 프로젝트에서 사용하도록 만들어진 ESLint 설정. 구글과 협업해 만든 핵심 웹 지표에 도움이 되는 규칙들이 내장돼 있다.
next.config.js
Next.js 프로젝트의 환경 설정을 담당하며, Next.js를 자유자재로 다루려면 반드시 알아야 하는 파일.

- @type으로 시작하는 주석: 자바스크립트 파일에 타입스크립트의 타입 도움을 받기 위해 추가된 코드.(없다면 일일이 타이핑해야 한다)
- reactStrictMode: 리액트 애플리케이션 내부에서 잠재적인 문제를 개발자에게 알리기 위한 도구.
- swcMinify: SWC를 기반으로 코드 최소화 작업을 할 것인지 여부를 설정하는 속성.(SWC > 번들링과 컴파일을 더욱 빠르게 수행하기 위해 만들어진 도구)
pages/_app.tsx
Next.js 애플리케이션의 전체 페이지에 공통적으로 적용되는 설정을 관리하는 파일이다.
이 파일은 모든 페이지의 시작점을 담당하며, 주로 아래와 같은 작업을 수행한다.
- 에러 처리: 애플리케이션 전역에서 발생하는 에러를 처리.
- 글로벌 스타일 적용: reset.css 등 모든 페이지에 적용할 공통 CSS 설정.
- 데이터 제공: 모든 페이지에서 공통적으로 사용되는 데이터를 제공.
또한, 서버 사이드와 클라이언트 사이드에서 페이지 렌더링 과정을 확인할 수 있는 중요한 역할도 수행한다.
예를 들어, console.log를 통해 서버와 클라이언트에서 실행되는 차이를 확인할 수 있다.
즉, _app.tsx는 Next.js 애플리케이션의 전역 설정과 공통 기능을 관리하는 핵심 파일이다.
pages/_document.tsx
create-next-app이 기본으로 생성해주지 않는다 >> 없어도 실행에 지장이 없는 파일임을 의미
_app.tsx가 애플리케이션 페이지 전체를 초기화하는 곳이라면, _document.tsx는 애플리케이션의 HTML을 초기화하는 곳이다.
- HTML 문서 구조 초기화: html, body 등의 태그를 설정.
- 헤드 설정: 검색 엔진 최적화 및 커스텀 메타 태그를 추가.
- 서버 사이드에서 한 번만 렌더링 되며, 페이지별 동작은 지원하지 않음.
차이점
_app.tsx 는 Next.js를 초기화하는 파일로, 설정과 관련된 코드를 모아두는 곳 > 경우에 따라 서버와 클라이언트 모두에서 렌더링 될 수 O
_document.tsx는 HTML 설정과 관련된 코드를 추가하는 곳 > 반드시 서버에서만 렌더링 된다.
pages/_error.tsx
역시 create-next-app이 기본으로 생성해주지 않는다.
Next.js 프로젝트 전역에서 발생하는 에러를 적절하게 처리하고 싶을 때 사용한다.
개발 모드에서는 이 페이지에 방문할 수 없고, 에러가 발생하면 Next.js가 제공하는 개발자 에러 팝업이 나타난다.
잘 작동하는지 확인하려면 프로덕션으로 빌드해서 확인해 봐야 한다.
pages/404.tsx
404 페이지를 정의할 수 있는 파일.
만들지 않으면 Next.js가 제공하는 기본 404페이지를 볼 수 있고, 원하는 스타일의 404페이지를 만들 수 있다.
pages/500.tsx
서버에서 발생하는 에러를 핸들링하는 페이지.
_error.tsx와 500.tsx가 모두 있다면 500.tsx가 우선 실행된다.
마찬가지로 원하는 스타일로 만들 수 있고, 만들지 않으면 기본으로 제공하는 페이지를 볼 수 있다.
pages/index.tsx
Next.js에서 애플리케이션의 루트 경로(ex. http://localhost:3000/)에 해당하는 기본 페이지를 정의하는 파일.
Next.js 애플리케이션의 기본 시작 페이지 역할을 하며, 파일 기반 라우팅 시스템을 통해 쉽게 경로를 설정할 수 있도록 지원한다.
- index.tsx는 기본적으로 루트 경로(/)에 매핑된다.
- 다른 경로는 pages 디렉터리 내의 파일 이름을 기준으로 자동으로 설정된다.
- [... props]. tsx와 같은 파일 이름을 사용하면 여러 경로를 한꺼번에 처리할 수 있는 동적 라우팅을 설정할 수 있다.
서버 라우팅과 클라이언트 라우팅의 차이
Next.js는 서버 라우팅과 클라이언트 라우팅을 혼합하여 사용한다.
예를 들어 <a> 태그와 비슷한 동작을 하는 next/link 태그와 비교하며 차이를 확인해 보자.
<a> 태그:
- 브라우저의 개발자 도구(Network 탭)에서 클릭 시 모든 리소스를 새로 요청(HTML, CSS, JS, 이미지 등).
- 전체 페이지 새로고침.
next/link:
- 개발자 도구에서 클릭 시 필요한 데이터만 요청하거나 추가 요청 없이 클라이언트에서 처리.
- 전체 페이지 새로고침 없음.
Next.js는 이렇게 서버 사이드 렌더링의 장점, 즉 사용자가 빠르게 볼 수 있는 최초 페이지를 제공한다는 점과
싱글 페이지 애플리케이션의 장점인 자연스러운 라우팅이라는 두 가지 장점을 모두 살리기 위해 이러한 방식으로 작동한다.
/pages/api/hello.ts
Next.js의 API 라우트로, 서버에서 직접 API 요청을 처리할 수 있도록 만들어진 파일이다.
- /api/hello 경로로 호출 가능하며, API 요청에 대해 JSON 데이터를 반환.
- 일반적인 페이지 라우트와는 다르게 HTML을 반환하지 않고, 서버 요청을 처리하기 위한 엔드포인트 역할.
- 프런트엔드와 백엔드를 한 프로젝트에서 관리할 수 있는 구조를 제공.
예시 사용:
- /pages/api/hello.ts가 있다면, 브라우저 또는 코드에서 /api/hello로 GET 요청을 보내 JSON 데이터를 받을 수 있다.
4.3.3 Data Fetching
서버 사이드 렌더링(SSR)을 지원하기 위해 데이터를 미리 가져오는 방법이다.
이를 통해 서버에서 데이터를 받아 필요한 페이지를 생성해 사용자에게 제공한다.
여기에서 활용할 수 있는 함수에는 어떤 것들이 있는지 살펴보자.
getStaticPaths와 getStaticProps
이 두 함수는 어떠한 페이지를 CMS(Contents Management System)나 블로그, 게시판과 같이
사용자와 관계없이 정적으로 결정된 페이지를 보여주고자 할 때 사용되는 함수이다.
두 함수는 반드시 함께 있어야 사용할 수 있다.
getServerSideProps
앞의 두 함수가 정적인 페이지 제공을 위해 사용된다면,
getServerSideProps는 서버에서만 실행되는 함수로 페이지가 요청되기 전에 서버에서 이 함수를 실행하여 필요한 데이터를 가져온다.
서버에서 가져온 데이터를 페이지의 props로 전달하여 렌더링에 사용된다.
주로 사용자의 요청에 따라 동적인 데이터를 가져와야 하는 경우(실시간 데이터 조회 등)에 사용한다.
getInitialProps
getStaticProps나 getServerSideProps가 나오기 전에 사용할 수 있었던 유일한 데이터 불러오기 수단이었다.
getInitialProps는 페이지가 렌더링되기 전에 데이터를 미리 가져와 컴포넌트에 전달한다.
대부분의 경우에는 getStaticProps나 getServerSideProps를 사용하는 것을 권장하나,
과거에 작성된 Next.js 코드에는 getInitialProps만 존재하고,
_app.tsx와 같이 일부 페이지에서는 getInitialProps밖에 사용할 수 없기 때문에 반드시 알고 있어야 한다.
4.3.4 스타일 적용하기
Next.js에서 적용할 수 있는 다양한 스타일 방식을 살펴보자.!
- 전역 스타일
- 스타일을 초기화하는 등 애플리케이션 전체에 공통으로 적용하고 싶은 스타일이 있다면 _app.tsx에 적용하면 된다.
- 컴포넌트 레벨 CSS
- [name]. module.css와 같은 명명 규칙을 준수하여 컴포넌트 레벨의 CSS를 추가할 수 있다.
- SCSS와 SASS
- sass패키지를 설치하면 별도의 설정 없이 바로 동일하게 스타일을 사용할 수 있다.
- CSS-in-JS
- 자바스크립트 내부에 스타일시트를 삽입하는 방식의 스타일링 (ex.styled-components)
4.3.6 next.config.js 살펴보기
Next.js 실행과 사용자화에 필요한 다양한 설정을 추가할 수 있는 파일이다.
실무에서 자주 사용되는 설정
- basePath
- 일종의 URL을 위한 접두사라 볼 수 있다.
- 개발환경에서 localhost:3000/ 에 basePath:"docs"와 같이 문자열을 추가하면 localhost:3000/docs에 서비스가 시작된다.
- poweredByHeader
- Next.js는 응답 헤더에 X-Power-by : Next.js 정보를 제공하는데, false를 선언하면 이 정보가 사라진다.
- 보안 관련 설루션에서는 powered-by헤더를 취약점으로 분류하므로 false로 설정하는 것이 좋다.
- redirects
- 특정 주소를 다른 주소로 보내고 싶을 때 사용된다.
- assetPrefix
- next에서 빌드된 결과물을 동일한 호스트가 아닌 다른 CDN 등에 업로드하고자 하면 이 옵션에 해당 CDN 주소를 명시하면 된다.
'모던 리액트 Deep Dive 스터디' 카테고리의 다른 글
9장 발표자료 (2) | 2024.12.08 |
---|---|
9장 - 모던 리액트 개발 도구로 개발 및 배포 환경 구축하기 (0) | 2024.12.04 |
4장 - 서버 사이드 렌더링(2) (0) | 2024.11.29 |
4장 - 서버사이드 렌더링 (3) | 2024.11.28 |
크롬 개발자 도구를 활용한 애플리케이션 분석 (2) | 2024.11.23 |