WEB/REACT

[Next.js] CSR 장단점 / SSR 필요성 / Next.js yarn start했을 때의 내부 살펴보기

heeney 2023. 7. 4. 02:22
728x90

원티드의 프리온보딩 챌린지를 통해 작성하게된 글임을 밝힙니다.

이 의미가 아니긴 한데 next.js 찍어먹기 시작

Next.js 좋다 좋다 말만 들었지 (도입을 시도했으나)사내에서 사용하고 있지 않아서 더더욱 궁금한 유니콘 같은 존재라 원티드 프리온보딩을 통해 공부해볼 수 있는 좋은 기회가 생겨서 감사히 참여하게 되었다.

 

1. CSR이란 무엇일까?

클라이언트가 렌더링 할게!

client-side-rendering, 영문 뜻 그대로 클라이언트 단(웹 브라우저)에서 JavaScript를 사용하여 동적으로 콘텐츠를 생성하고 렌더링하겠다는 의미이다. 서버에는 데이터를 요청하고, JavaScript로 컨트롤 한다.

SSR은 반대로 서버에서 동적으로 콘텐츠를 생성하고 렌더링하겠다는 의미가 된다.

CSR, 장점?

  1. 깜빡임이 없는 매끄러운 UX
    클라이언트에서 응답 데이터를 기반으로 새로운 페이지가 아닌 변경된 부분에 대해서만 업데이트한다.
    사용자 이벤트에 반응하여 실시간으로 상호작용할 수 있으며 변경된 부분만 업데이트하게 되면 페이지가 전환될 때마다 Blinking Issue가 없어서 사용자의 경험을 해치지 않는다.
  2. 페이지간 이동시 빠르게 응답하여 로드
  3. 페이지를 구성할 때 필요한 데이터만 요청하므로 서버 부하 절감
  4. UI 재사용 편리
    변화된 부분만 업데이트하므로 UI를 재사용하기 편리하다.
  5. 개발 프로세스 단순화
    클라이언트 <-> 서버간 각자 독립적으로 작업하게 되므로 프로세스가 단순화 된다.

CSR, 단점?

// index.html
<div id="root"></div>

// src/App.js
function App() {
  return (
    <div className="App">Hello World!</div>
  );
}
export default App;

// src/index.js
ReactDOM.render(<App />, document.getElementById('root'));
App.js의 컨텐츠를 id="root"인 div 요소에 넣어서 렌더링하는구나!
그러면 처음에는 무조건 id="root"인 요소 내부는 텅- 비어있네?
  1. 초기 페이지 로딩 시간 소요
    초기 페이지 로딩시 모든 HTML과 JavaScript를 다운로드해야하기 때문에 이로 인해 로딩 시간이 더 소요된다.
    큰 규모의 애플리케이션에서는 초기 로딩 데이터 양이 크게 증가할 것이다.
  2. SEO 대응 부족
    초기에 HTML 콘텐츠가 비어져있다.. (root 참고) 그래서 검색 엔진이 크롤링할 데이터가 없다.
  3. 보안 취약
    클라이언트 측에서 로직이 실행된다는 것은 보안 문제가 유발될 수 있다는 뜻이기도 하다.

 

2. SPA로 구성된 웹앱에서 SSR이 필요한 이유는?

단점으로 언급된 요인들 때문이다.

가장 많이 언급되는 내용은 크게 두 가지가 있는데, SPA 웹앱에 SSR이 도입될 경우 서버가 렌더링을 담당하게 되면서 초기 페이지 로딩 시간 단축, SEO 최적화 가능이라는 장점이 있다.

서버에서 온전한 HTML을 렌더링하여 사용자에게 빠르게 콘텐츠를 제공하므로 로딩시간이 줄어들어 사용자 경험을 향상시킨다. 또한 해당 HTML을 통해 검색 엔진이 크롤링하기 쉬운 환경을 제공하니, 이 두가지 내용만으로도 사용해야하는 이유는 충분하다.

 

3. Next.js 프로젝트에서 yarn start 스크립트를 실행했을 때 실행되는 코드를 Next.js 공식 레포지토리에서 찾은 뒤 해당 파일 분석

처음에 무작정 yarn start를 진행하면 에러가 발생한다.

Error: Could not find a production build in the '~/.next' directory.
Try building your app with 'next build' before starting the production server.

이는 yarn build를 통해 빌드를 먼저 진행하라는 의미이다.

이제 공식 레포지토리에서 yarn start를 입력했을 때 실행되는 코드를 살펴보자.

분석하기 위해 검색을 해야하는데, 나의 경우 package.json에서 "start": "next start" 라고 작성되어 있는 부분을 통해 검색을 들어갔다.
내가 yarn start를 입력하여 next start가 실행된다면, 이 실행되는 부분을 찾아보면 나오지 않을까? 라는 막연한 생각이었다.

'started server on'를 검색했을 때 내가 원하는 내용을 확인할 수 없어서 next start 부터 찾아보기 시작했다.

추측하면서 여러 파일을 훑어보다가 아래 파일을 찾았다:

packages/next/src/server/lib/commands.ts

cli, start 등 키워드를 보아하니 yarn start와 연관있어보인다. /cli/next-start 파일이 유력해졌다.

packages/next/src/cli/next-start.ts

뭐 어쩌구 저쩌구 내용은 많은데 가장 중요한건 await startServer

위에는 다 옵션을 사용할 수 있는 것들로 추측되고, Invalid 어쩌구 하는거보니 에러핸들링도 해주고 있는듯 했다.
그중 await startServer에서 hostname, port 등이 눈에 띈다. startServer를 들어가본다.

packages/next/src/server/lib/start-server.ts

yarn start를 입력했을 때 나오는 로그 내용을 해당 파일에서 확인할 수 있었다.

let targetHost = hostname

  await new Promise<void>((resolve) => {
    server.on('listening', () => {
      const addr = server.address()
      port = typeof addr === 'object' ? addr?.port || port : port

      let host = !hostname || hostname === '0.0.0.0' ? 'localhost' : hostname

      let normalizedHostname = hostname || '0.0.0.0'

      if (isIPv6(hostname)) {
        host = host === '::' ? '[::1]' : `[${host}]`
        normalizedHostname = `[${hostname}]`
      }
      targetHost = host

      const appUrl = `http://${host}:${port}`

      if (isNodeDebugging) {
        const debugPort = getDebugPort()
        Log.info(
          `the --inspect${
            isNodeDebugging === 'brk' ? '-brk' : ''
          } option was detected, the Next.js proxy server should be inspected at port ${debugPort}.`
        )
      }

      Log.ready(
        `started server on ${normalizedHostname}${
          (port + '').startsWith(':') ? '' : ':'
        }${port}, url: ${appUrl}`
      )
      resolve()
    })
    server.listen(port, hostname)
  })

에러 핸들링도 해주고, 실제로 준비가 됐을 경우 Log.ready를 뱉은 후 reslove()처리 해준다.

server.listen은 말그대로 해당 port, hostname을 기반으로 서버를 듣고(?) 열어주고 있다는 뜻으로 해석된다. 그래서 아마 cli에서 서버 연결이 끊어지면 server.listen도 throw Error 할 것이라고 추측한다.
이 부분은 좀 더 찾아보고 내용을 보강해야할듯 하다..!

 

프레임워크 및 라이브러리 뜯어본다는게 이런거였구나 느꼈다.. 항상 개발공부하면서 더 딥다이브할 수 있도록 노력해야겠다 ㅠㅠ

 

 

728x90