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

WAYPIL·2023년 6월 26일
0

Next.js 프로젝트에서 yarn start 혹은 npm run start 스크립트를 실행했을 때 실행되는 코드(파일)과, 해당 파일에 대한 간단한 설명을 첨부해주세요.


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

#!/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. npx next start 뒤에 무슨 옵션이 붙을 수 있는지 명시

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

    // Aliases
    '-h': '--help',
    '-p': '--port',
    '-H': '--hostname',
  }

먼저 딕셔너리를 하나 생성한다. npx next start 뒤에 무슨 옵션들이 붙을 수 있는지를 정의한다.

나중에 저 키워드들을 이용해서 아래와 같이 활용할 수 있다.

npm next start my-app -p 8080 --hostname localhost

잠깐! --keepAliveTimeout이 무엇인가?

npm next start --keepAliveTimeout 10s
10초 동안 클라한테 소식 없으면 그냥 끊어버려라

웹 서버는 일정 시간 동안 클라로부터 요청이 없을 경우, 서버 측에서 자동으로 연결을 끊는 기능을 가지고 있다. 기본 값은 10초인데, 이것보다 더 짧게 or 길게 설정하고 싶을 때 --keepAliveTimeout 옵션을 사용한다.

시간을 너무 길게 설정하면 쓸데없는 연결까지도 계속 유지되어 메모리 사용량이 증가하고, 복수의 요청에 병목 현상이 발생할 수 있다. 반대로 시간을 너무 짧게 설정하면 클라-서버 간 '끊기고-연결하고-끊기고-연결하고'가 계속 반복되기 때문에 (마찬가지로) 메모리 사용량이 증가하고, 로딩/버퍼링 및 네트워크 오류가 발생할 확률이 높아진다.


2. 양식에 맞는 옵션인가?

  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
  }

npm next start my-app -p 8080 --hostname localhost에서 -p 8080 --hostname localhost가 argv에 해당하는 부분이다.

여기서 --asdfjkslf 같은 잘못된 키워드를 사용하면 catch로 넘어간다.


3. --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)
  }

npm next start --help라고 입력했다면 console.log가 실행되고 코드가 종료된다.


4. 옵션으로 받은 값들 각 변수에 할당

  const dir = getProjectDir(args._[0])
  const host = args['--hostname']
  const port = getPort(args)
  const keepAliveTimeoutArg: number | undefined = args['--keepAliveTimeout']

5. --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
    )
  }

if문 부분이 좀 난잡하게 되어 있는데, 이를 보기 쉽게 풀어 쓰면 다음과 같다.

  const 값이_존재함 = (typeof keepAliveTimeoutArg) !== 'undefined';
  const 값이_NaN임 = Number.isNaN(keepAliveTimeoutArg);
  const 값이_무한대임 = !Number.isFinite(keepAliveTimeoutArg);
  const 값이_음수임 = keepAliveTimeoutArg < 0;

  const 잘못된_값임 = 값이_존재함 && (값이_NaN임 || 값이_무한대임 || 값이_음수임);

  if (잘못된_값임) {
    printAndExit(
      `Invalid --keepAliveTimeout, expected a non negative number but received "${keepAliveTimeoutArg}"`,
      1
    )
  }

한마디로 --keepAliveTimeout 뒤에 말도 안 되는 값 들어오면 에러 메시지 내뿜고 코드 종료하라는 것.


6. --keepAliveTimeout에 제대로 된 값이 들어오면?

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

제대로 된 값이 들어왔으니까 1의 자리에서 올린다. (예: 3.1 → 4)

--keepAliveTimeout을 애당초 설정 안 했으면 undefined이기 때문에, 그냥 undefined로 둔다.


7. 사전 작업 마무리

  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,
  })

(4번 항목에서 선언했던 4개의 변수들과 더불어) 서버의 Config 설정을 불러오고, 그걸 startServer()에 적용하여 본격적으로 서버를 실행한다.

export { nextStart }

마지막으로 1~7까지의 작업을 수행하는 nextStart()를 다른 파일에서 사용할 수 있도록 내보내기를 허용한다.


결론

next-start.ts 코드는 크게 2가지의 일을 담당한다.

  1. npx next start 뒤에 붙는 --help, --port, --hostname, --keepAliveTimeout 옵션의 정의. 그리고 무결성 체크
  2. 옵션/설정을 바탕으로 서버 실행.
profile
Self-improvement Guarantees Future.

0개의 댓글