[모던 리액트 Deep Dive] 04장 서버 사이드 렌더링 (2)

H Kim·2024년 3월 17일
0

기술 책 읽기

목록 보기
17/20
post-thumbnail
post-custom-banner

4.3 Next.js 톺아보기

  • 아무것도 없는 상태에서 서버 사이드 렌더링까지 지원하는 리액트 애플리케이션까지 만드는 데는 많은 노력을 필요로 하며, 실제로 리액트 팀에서도 권하지 않는 작업이다. 이 중, Next.js는 리액트 서버 사이드 렌더링 프레임워크로 가장 많은 인기를 얻고있는 프레임워크이다.


4.3.1 Next.js란?

  • Next.js는 Vercel이라는 미국 스타트업에서 만든 리액트 기반 서버 사이드 렌더링 프레임워크다. PHP에 영감을 받아 만들어졌으며, 실제로도 PHP의 대용품으로 사용되기 위해 만들었다고 언급한 것으로 봐서 최초에 설계 당시부터 서버 사이드 렌더링을 염두에 뒀던 것으로 보인다.


4.3.2 Next.js 시작하기

  • Next.js는 create-next-app을 제공해 개발자가 빠르게 Next.js 기반 프로젝트를 생성할 수 있게 돕는다.

  • package.json

    • next: Mext.js의 기반이 되는 패키지
    • eslint-config-next: Next.js 기반 프로젝트에서 사용하도록 만들어진 ESLint 설정으로, 구글과 협업해 만든 핵심 웹 지표(core web vital)에 도움이 되는 규칙들이 내장돼 있다. Next.js 기반 프로젝트라면 꼭 사용하는 것을 추천하며, eslint-config-aribnb와 같은 기존에 사용하던 규칙이 있다면 이에 추가로 함께 사용하는 것을 추천한다.
  • next.config.js

    • reactStrictMode: 리애그의 엄격 모드와 관련된 옵션으로, 리액트 애플리케이션 내부에서 잠재적인 문제를 개발자에게 알리기 위한 도구다. 특별한 이유가 없다면 켜두는 것이 도움이 된다.
    • swcMinify: Vercel에서는 SWC라 불리는 또 다른 오픈소스를 만들었는데, 이 도구는 번들링과 컴파일을 더욱 빠르게 수행하기 위해 만들어졌다. SWC가 바벨에 비해 더 빠른 속도를 보여주기 때문에 특별한 이유가 없다면 SWC를 쓰는 것을 권장한다.
/** @type {import('next').NextConfig} */

이는 자바스크립트 파일에 타입스크립트의 타입 도움을 받기 위해 추가된 코드다. 해당 주석이 있다면 next의 NextConfig를 기준으로 타입의 도움을 받을 수 있는 반면, 없다면 일일이 타이핑해야 한다.

  • pages/_app.tsx

    • 에러 바운더리를 사용해 애플리케이션 전역에서 발생하는 에러 처리
    • rese.css 같은 전역 CSS 전역
    • 모든 페이지에 공통으로 사용 또는 제공해야 하는 데이터 제공 등
    • _app.tsx의 render() 내부에 console.log()를 추가해서 아무 메시지나 기록해 보자. 그리고 페이지를 새로고침하면 해당 로그가 브라우저 콘손창이 아닌 Next.js를 실행한 터미널에 기록되는 것을 볼 수 있다. 또 여기에서 페이지를 전환하면 더 이상 서버에서 로깅되지 않고 브라우저에 로깅되는 것을 확인할 수 있다. 이러한 사실로 미뤄 봤을 때 최초에는 서버 사이드 렌더링을, 이후에는 클라이언트에서 _app.tsx의 렌더링이 실행된다는 것을 짐작할 수 있다.
  • pages/_document.tsx

    • _app.tsx가 애플리케이션 페이지 전체를 초기화하는 곳이라면, _document.tsx는 애플리케이션의 HTML을 초기화하는 곳이다.
    • _app.tsx는 렌더링이나 라우팅에 따라 서버나 클라이언트에서 실행될 수 있지만 _document는 무조건 서버에서 실행된다. 다라서 이 파일에서 onClick과 같은 이벤트 핸들러를 추가하는 것은 불가능하다.
    • _document.tsx에서만 할 수 있는 또 한 가지 작업은 바로 CSS-in-JS의 스타일을 서버에서 모아 HTML로 제공하는 작업니다.
    • _app.tsx와 _document.tsx의 차이점은 _app.tsx는 Next.js를 초기화하는 파일로 Next.js의 설정과 관련된 코드를 모아두는 곳이며, 경우에 따라 클라이언트 모두에서 렌더링될 수 있다. _document.tsx는 Next.js로 만드는 웹사이트의 뼈대가 되는 HTML 설정과 관련된 코드를 추가하는 곳이며, 반드시 서버에서만 렌더링된다.
  • pages/_error.tsx

    • 이 에러 페이지는 위와 같이 클라이언트에서 발생하는 에러 또는 서버에서 발생하는 500 에러를 처리할 목적으로 만들어졌다.
    • 개발 모드에서는 이 페이지에 방문할 수 없고 에러가 발생하면 Next.js가 제공하는 개발자 에러 팝업이 나타나게 된다. 이 페이지가 잘 작동하는지 확인하려면 프로덕션으로 빌드해서 확인해 봐야 한다.
  • pages/404.tsx

    • 404 페이지를 정의할 수 있는 파일이다. 만들지 않으면 Next.js에서 제공하는 기본 404 페이지를 볼 수 있고, 원하는 스타일의 404 페이지를 이곳에서 만들 수 있다.
  • pages/500.tsx

    • _error.tsx와 500.tsx가 모두 있다면 500.tsx가 우선적으로 실행된다. 마찬가지로 500이나 error 페이지가 없다면 기본적으로 Next.js에서 제공하는 페이지를 볼 수 있으며, 별도로 생성해 에러 페이지를 정의할 수 있다.
  • pages/indes.tsx

    • _app.tsx, _error.tsx, _document.tsx, 404.tsx, 500.tsx가 Next.js에서 제공하는 예약어로 관리되는 페이지라면 지금부터는 개발자가 자유롭게 명칭을 지정해 만들 수 있는 페이지다.
    • 라우팅이 파일명으로 이어지는 구조가 바로 react-pages에서 처음 만들어졌으며, Next.js에서 현재까지 이어지고 있다. Next.js는 react-pages처럼 라우팅 구조는 다음과 같이 /pages 디렉터리를 기초로 구성되며, 각 페이지에 있는 default export로 내보낸 함수가 해당 페이지의 루트 컴포넌트가 된다.
    • /pages/index.tsx: 웹사이트의 루트이며, localhost:3000과 같은 루트 주소를 의미한다.
    • /pages/hello.tsx: /pages가 생략되고, 파일명이 주소가 된다. 즉, 여기서는 /hello이며, localhost:3000/hello로 접근할 수 있다.
    • /pages/hello/world.tsx: localhost:3000/hello/world로 접근 가능하다. 디렉터리의 깊이만큼 주소를 설정할 수 있다.
    • 한 가지 주의해야 할 점은 hello/index.tsx와 hello.tsx 모두 같은 주소를 바라본다는 것이다. 필요에 따라 적절하게 선택하면 된다.
    • /pages/hello/[greeting].tsx: []의 의미는 여기에 어떠한 문자도 올 수 있다는 뜻이다.
    • /pages/hi/[...props].tsx: 자바스크립트의 전개 연산자가 떠오르는 이 형식은 실제로도 작동이 전개 연사자와 동일하다. /hi를 제외한 /hi 하위의 모든 주소가 여기로 온다. 즉, localhost:3000/hi/hello, localhost:3000/hi/hello/world, localhost:3000/hi/hello/world/foo 등이 여기로 오게 된다.
    • [] 안의 내용은 변수로 처리된다는 점을 기억해야 한다.
  • /pages/api/hello.ts

    • /pages/api/hello.ts는 /api/hello로 호출할 수 있으며, 이 주소는 다른 pages 파일과 다르게 HTML 요청을 하는 게 아니라 단순히 서버 요청을 주고받게 된다.
    • 여기에 있는 코드는 당연히 오직 서버에서만 실행된다. window나 document 등 브라우저에서만 접근할 수 있는 코드를 작성하면 당연히 문제가 발생한다.

  • 서버 라우팅과 클라이언트 라우팅의 차이
    • 내부 페이지 이동 시 <a> 대신 <Link>를 사용한다.
    • window.location.push 대신 router.push를 사용한다.


4.3.3 Data Fetching

  • 데이터 불러오기 전략
  • 이 함수는 pages/의 폴더에 있는 라우팅이 되는 파일에서만 사용할 수 있으며, 예약어로 지정되어 반드시 정해진 함수명으로 export를 사용해 함수를 파일 외부로 내보내야 한다. 이를 활용하면 서버에서 미리 필요한 페이지를 만들어서 제공하거나 해당 페이지에 요청이 있을 때마다 서버에서 데이터를 조회해서 미리 페이지를 만들어서 제공할 수 있다.

  • getStaticPaths와 getStaticProps
    • 정적으로 결정된 페이지를 보여주고자 할 때 사용되는 함수다. getStaticProps와 getStaticPaths는 반드시 함께 있어야 사용할 수 있다.
    • getStaticPaths는 접근 가능한 주소를 정의하는 함수다.
    • getStaticProps는 앞에서 정의한 페이지를 기준으로 해당 페이지로 요청이 왔을 때 제공할 props를 반환하는 함수다.
    • 이렇게 사용자가 접근할 수 있는 페이지를 모조리 빌드해 두고 배포하면 사용자는 굳이 페이지가 렌더링되는 것을 기다릴 필요 없이 이미 완성돼 있는 페이지를 받기만 하면 되므로 굉장히 빠르게 해당 페이지를 확인할 수 있다.
    • getStaticPaths 함수의 반환값 중 하나인 fallback 옵션은 이렇게 미리 빌드해야 할 페이지가 너무 많은 경우에 사용 가능하다. paths에 미리 빌드해 둘 몇 개의 페이지만 리스트로 반환하고, truesk "blocking"으로 값을 선언할 수 있다. 이렇게 하면 next build를 실행할 때 미리 반환해 둔 paths에 기재돼 있는 페이지만 앞서와 마찬가지로 미리 빌드하고, 나머지 페이지의 경우에는 다음과 같이 작동한다.
    • true: 사용자가 미리 빌드하지 않은 페이지에 접근할 경우, 빌드되기 전까지는 fallback 컴포넌트를 보여주고, 빌드가 완료된 이후에 해당 페이지를 보여주는 옵션이다.
    • "blocking": 별도의 로딩과 같은 처리를 하지 않고, 단순히 빌드가 완료될 때까지 사용자를 기다리게 하는 옵션이다. 서버 사이드에서 렌더링할 때까지 대기한 다음, 렌더링이 완료되면 해당 페이지를 제공한다.
  • getServerSideProps

    • 서버에서 실행되는 함수이며 해당 함수가 있다면 무조건 페이지 진입 전에 이 함수를 실행한다. 이 함수는 응답값에 따라 페이지의 루트 컴포넌트에 props를 반환할 수도, 혹은 다른 페이지로 리다이렉트시킬 수도 있다. 이 함수가 있다면 Next.js는 꼭 서버에서 실행해야 하는 페이지로 분류해 빌드 시에도 서버용 자바스크립트 파일을 별도로 만든다.
    • 서버에서만 실행되기 때문에 아래와 같은 제약이 있다.
    • window.document와 같이 브라우저에서만 접근할 수 있는 객체에는 접근할 수 없다.
    • API 호출 시 /api/some/path와 같이 protocolrhk domain 없이 fetch 요청을 할 수 없다. 브라우저와 다르게 서버는 자신의 호스트를 유출할 수 없기 때문이다. 반드시 완전한 주소를 제공해야 fetch가 가능하다.
    • 여기에서 에러가 발생한다면 500.tsx와 같이 미리 정의해 둔 에러 페이지로 리다이렉트된다.
    • 서버 사이드 렌더링은 루트 컴포넌트붙터 시작해 모든 컴포넌트를 실행해 완성하므로 클라이언트에서만 실행 가능한 변수, 함수, 라이브러리 등은 서버에서 실행되지 않도록 별도로 처리해야 한다.
    • 또한 이 함수는 사용자가 매 페이지를 호출할 때마다 실행되고, 이 실행이 끝나기 전까지는 사용자에게 어떠한 HTML도 보여줄 수 없다. 따라서 getServerSideProps 내부에서 실행하는 내용은 최대한 간결하게 작성하기 위해 꼭 최초에 보여줘야 하는 데이터가 아니라면 getServerSideProps보다는 클라이언트에서 호출하는 것이 더 유리하다. 이는 반드시 해당 페이지를 렌더링하는 데 있어 중요한 역할을 하는 데이터만 가져오는 것이 좋다.
  • getInitialProps

    • 대부분의 경우에는 getStaticProps나 getServerSideProps를 사용하는 것을 권장하며, getInitialProps는 굉장히 제한적인 예시에서만 사용된다.


4.3.4 스타일 적용하기

  • 전역 스타일은 _app.tsx로 CSS Reset을 사용한다.
  • 컴포넌트 레벨 CSS는 [name].module.css와 같은 명명 규칙만 준수하면 되며, 이 컴포넌트 레벨 CSS는 다른 컴포넌트의 클래스명과 겹쳐서 스타일에 충돌이 일어나지 않도록 고유한 클래스명을 제공한다.
  • SCSS와 SASS 또한 install --save-dev sass와 같은 명령어로 설치하면 별도의 설정 없이 바로 동일하게 스타일을 사용할 수 있다.
  • 리액트 트리 내부에서 사용하고 있는 styled-components의 스타일을 모두 모은 다음, 이 각각의 스타일에 유니크한 클래스명을 부여해 스타일이 충돌하지 않게 클래스명과 스타일을 정리해 이를 _document.tsx가 서버에서 렌더링할 때 React.Context 형태로 제공하는 것이다.

  • 프로덕션 모두로 빌드했더니 <style /> 태그 내부가 비어 있는데, 스타일은 정상적으로 적용돼 있어요. 어떻게 된 일인가요?
    • styled-component가 개발 모드와 다르게 프로덕션 모드에서는 이른바 SPEEDY_MODE라고 하는 설정을 사용하기 때문이다. 이 설정이 켜져 있으면 HTML에 스타일을 적용하는 대신 자바스크립트를 활용해 CSSDOM 트리에 직접 스타일을 넣는다. 이 때문에 HTML의 <style />은 비어 있지만 필요한 스타일은 모두 자바스크립트로 삽입했기 떄문에 HTML은 문제없이 렌더링 할 수 있게 된다. 그리고 이름에서 알 수 있듯 기존 스카일링 방식보다 훨씬 빠른 것으로 나타났다.
    • 실제 스타일이 어떻게 삽입돼 있는지 확인하고 싶다면 document.styleSheets를 활용하면 된다.


4.3.5 _app.tsx 응용하기

  • _app.tsx가 Next.js로 만든 모든 서비스가 진입하는 초초 진입점이라고 언급했다. 이러한 특성을 활용하면 사용자가 처음 서비스에 접근했을 때 하고 싶은 무언가를 여기에서 처리할 수 있다.
  • 웹서비스를 최초에 접근했을 때만 실행하고 싶은 내용을 app.getInitialProps 내부에 담아 둘 수 있다.
  • userAgent 확인이나 사용자 정보와 같은 애플리케이션 전역에서 걸쳐 사용해야 하는 정보 등을 호출하는 작업을 수행할 수 있을 것이다.


4.3.6 next.config.js 살펴보기

  • next.config.js는 Next.js 실행에 필요한 설정을 추가할 수 있는 파일이다.
post-custom-banner

0개의 댓글