[원티드 프리온보딩 프론트엔드 챌린지] 사전과제

2023. 6. 14. 11:18기타

1. CSR(Client-Side Rendering)이란 무엇이며, 그것의 장단점에 대하여 설명해주세요.

1) CSR이란,

- 클라이언트 측에서 렌더링을 진행한다는 의미이다. 브라우저 측에서 JavaScript를 다운받고 실행시킨다. 이 실행이 완전히 끝나면 그때 사용자는 화면을 볼 수 있고 페이지를 작동시킬 수 있다.

- SPA(Single Page Application)에서 쓰이는 기법이다.

 

2) CSR의 단계

(1) User가 Website 요청을 보냄.

(2) CDN이 HTML 파일과 JS로 접근할 수 있는 링크를 클라이언트에 보낸다.

(3) 클라이언트는 HTML과 JS를 다운로드 받는다.

(4) 다운이 완료된 JS가 실행된다. 데이터를 위한 API가 호출된다. (이때 유저들은 placeholer를 보게 됨.)

(5) 서버가 API로부터의 요청에 응답한다.

(6) API로부터 받아온 data를 placeholder 자리에 넣어준다. 이제 페이지는 상호작용이 가능해진다.

 

3) CSR의 장점

- 초기 로딩 이후 구동 속도가 빠르다.

- 클라이언트에 자원을 몰아서 사용하기 때문에 서버에 부하가 적다.

 

4) CSR의 단점

- 초기에 HTML 문서를 받은 후, JS 파일을 다운로드하고 실행한 후에 컨텐츠를 렌더링하므로 초기 로딩 속도가 느리다.

- 검색 엔진은 자동화 로봇인 '크롤러' 가 웹 사이트를 읽는데, 이 크롤러가 JS 파일을 실행하지 않고 페이지를 분석하기 때문에 페이즈의 컨텐츠 인식이 어려워 SEO 대응이 어려울 수 있다.

 

2. SPA(Single Page Application)로 구성된 웹 앱에서 SSR(Server-Side Rendering)이 필요한 이유에 대하여 설명해주세요.

1) 검색 엔진 최적화

- SPA의 단점 중 하나인 검색 엔진 대응이 있다. 검색 엔진은 자동화 로봇인 '크롤러'가 웹 사이트를 읽는데, 이 웹 크롤러가 JS파일을 실행하지 않고 페이지를 분석하기 떄문에 페이지의 컨텐츠 인식이 어려워 SEO 대응이 어렵다는 것이다. 하지만 SSR은 서버쪽에서 초기 렌더링을 수행하여 완전한 HTML 문서를 생성한 후 클라이언트에 전달하는 방식이기 때문에 SEO 대응에 유리하다.

 

2) 초기 로딩 속도 개선

- SPA는 초기에 HTML 문서를 받은 후, JS 파일을 다운로드하고 실행한 후에 컨텐츠를 렌더링하기 떄문에 초기 로딩 속도가 느리다. SSR을 사용하게 되면 서버에서 완전히 렌더링을 수행한 후 클라이언트에 전달하기 때문에 사용자는 빠른 시간 안에 초기 컨텐츠를 볼 수 있게 된다.

 

3. Next.js 프로젝트에서 yarn start(or npm run start) 스크립트를 실행했을 때 실행되는 코드를 Next.js Github 레포지토리에서 찾은 뒤, 해당 파일에 대한 간단한 설명을 첨부해주세요.

1) package.json 

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },

- package.json의 scripts 부분에서 start에 관련된 부분을 찾을 수 있었다.

next start에 대한 부분은 next.js / packages / next / src / cli 부분에서 찾을 수 있었다.

 

2) next-start.js

- 해당 파일은 next.js / packages / next / src / cli 안에서 찾을 수 있었다.

- 아래는 전체 코드이다.

#!/usr/bin/env node

import arg from 'next/dist/compiled/arg/index.js'
import { startServer } from '../server/lib/start-server'
import { getPort, printAndExit } from '../server/lib/utils'
import isError from '../lib/is-error'
import { getProjectDir } from '../lib/get-project-dir'
import { CliCommand } from '../lib/commands'
import { resolve } from 'path'
import { PHASE_PRODUCTION_SERVER } from '../shared/lib/constants'
import loadConfig from '../server/config'

const nextStart: CliCommand = async (argv) => {
  const validArgs: arg.Spec = {
    // Types
    '--help': Boolean,
    '--port': Number,
    '--hostname': String,
    '--keepAliveTimeout': Number,

    // Aliases
    '-h': '--help',
    '-p': '--port',
    '-H': '--hostname',
  }
  let args: arg.Result<arg.Spec>
  try {
    args = arg(validArgs, { argv })
  } catch (error) {
    if (isError(error) && error.code === 'ARG_UNKNOWN_OPTION') {
      return printAndExit(error.message, 1)
    }
    throw error
  }
  if (args['--help']) {
    console.log(`
      Description
        Starts the application in production mode.
        The application should be compiled with \`next build\` first.

      Usage
        $ next start <dir> -p <port>

      <dir> represents the directory of the Next.js application.
      If no directory is provided, the current directory will be used.

      Options
        --port, -p          A port number on which to start the application
        --hostname, -H      Hostname on which to start the application (default: 0.0.0.0)
        --keepAliveTimeout  Max milliseconds to wait before closing inactive connections
        --help, -h          Displays this message
    `)
    process.exit(0)
  }

  const dir = getProjectDir(args._[0])
  const host = args['--hostname']
  const port = getPort(args)

  const keepAliveTimeoutArg: number | undefined = args['--keepAliveTimeout']
  if (
    typeof keepAliveTimeoutArg !== 'undefined' &&
    (Number.isNaN(keepAliveTimeoutArg) ||
      !Number.isFinite(keepAliveTimeoutArg) ||
      keepAliveTimeoutArg < 0)
  ) {
    printAndExit(
      `Invalid --keepAliveTimeout, expected a non negative number but received "${keepAliveTimeoutArg}"`,
      1
    )
  }

  const keepAliveTimeout = keepAliveTimeoutArg
    ? Math.ceil(keepAliveTimeoutArg)
    : undefined

  const config = await loadConfig(
    PHASE_PRODUCTION_SERVER,
    resolve(dir || '.'),
    undefined,
    undefined,
    true
  )

  await startServer({
    dir,
    isDev: false,
    hostname: host,
    port,
    keepAliveTimeout,
    useWorkers: !!config.experimental.appDir,
  })
}

export { nextStart }

 

코드들에 대해 부분적으로 살펴봤다.

 

1. 옵션 명시 및 확인

const validArgs: arg.Spec = {
    // Types
    '--help': Boolean,
    '--port': Number,
    '--hostname': String,
    '--keepAliveTimeout': Number,

    // Aliases
    '-h': '--help',
    '-p': '--port',
    '-H': '--hostname',
  }
  let args: arg.Result<arg.Spec>
  try {
    args = arg(validArgs, { argv })
  } catch (error) {
    if (isError(error) && error.code === 'ARG_UNKNOWN_OPTION') {
      return printAndExit(error.message, 1)
    }
    throw error
  }

- next start 뒤에 올 수 있는 옵션들에 대한 명시다. 만약 명시된 옵션이 아니라면 에러 메시지를 던지고 실행을 종료한다.

- types에는 옵션들에 대한 명시, aliases는 약자로 types들을 실행시킨다.

 

2. --help 옵션

if (args['--help']) {
    console.log(`
      Description
        Starts the application in production mode.
        The application should be compiled with \`next build\` first.

      Usage
        $ next start <dir> -p <port>

      <dir> represents the directory of the Next.js application.
      If no directory is provided, the current directory will be used.

      Options
        --port, -p          A port number on which to start the application
        --hostname, -H      Hostname on which to start the application (default: 0.0.0.0)
        --keepAliveTimeout  Max milliseconds to wait before closing inactive connections
        --help, -h          Displays this message
    `)
    process.exit(0)
}

- --help나 -h 옵션을 사용한 경우, next start 뒤에 사용할 수 있는 옵션들을 프린트해주고 종료한다.

 

3. 서버 실행에 필요한 변수 선언

const dir = getProjectDir(args._[0])
const host = args['--hostname']
const port = getPort(args)

const keepAliveTimeoutArg: number | undefined = args['--keepAliveTimeout']
if (
    typeof keepAliveTimeoutArg !== 'undefined' &&
    (Number.isNaN(keepAliveTimeoutArg) ||
      !Number.isFinite(keepAliveTimeoutArg) ||
      keepAliveTimeoutArg < 0)
) {
    printAndExit(
      `Invalid --keepAliveTimeout, expected a non negative number but received "${keepAliveTimeoutArg}"`,
      1
    )
}

const keepAliveTimeout = keepAliveTimeoutArg
    ? Math.ceil(keepAliveTimeoutArg)
    : undefined

const config = await loadConfig(
    PHASE_PRODUCTION_SERVER,
    resolve(dir || '.'),
    undefined,
    undefined,
    true
)

- 서버 실행에 필요한 directory 주소와 hostname, port 번호등을 입력받은 값이나 기본 값으로 선언 또는 초기화한다.

- 이후 --keepAliveTimeout의 값이 유효하지 않다면 에러 메시지를 리턴하며 종료한다.

- 만약 --keepAliveTimeout의 값이 유효하다면 keepAliveTimeout에 값을 저장하고 config를 설정해준다.

 

4. 서버 실행 

await startServer({
    dir,
    isDev: false,
    hostname: host,
    port,
    keepAliveTimeout,
    useWorkers: !!config.experimental.appDir,
})

- 위의 내용들이 전부 유효하다면 서버를 실행시킨다.

 


관련 링크

1. Next.js 레포지토리

2. Next.js에서 yarn start(npm run start)를 실행했을 때 흐름