React

Next.js 프로젝트를 create하고 run할 때 실행되는 script 뜯어봅시다

bas96 2023. 7. 11. 12:50

npx create-next-app@버전 을 통해서 편하게 Next.js project를 만들 수 있다.

하지만 Next 서버가 어떻게 만들어지는지 어떻게 시작되는지는 자세하게 알지 못했다.

 

Next.js github 을 참고해서 프로젝트를 생성하고 실행할 때 어떻게 동작하는지 알아보았다.

 

1. Project 생성하기 

npx create-next-app@12.3.4 명령어를 이용해서 내가 원하는 디렉토리에 Next.js 프로젝트를 만들었다. 

이때 알게된 것은 Next project의 생성 시점이다. 

 

차근차근 보면 먼저 npx 명령어로 버전을 설저한 후 이름을 설정하는 프롬프트가 뜬다. 


npx create-next-app@12.3.4

✔ What is your project named? … hj-next-app

여기에 나는 hj-next-app으로 이름을 설정하고 enter를 누르니까 

Creating a new Next.js app in /Users/khj/Documents/02_projects/hj-next-app.

 

이렇게 프로젝트가 만들어졌다.

 

npx create-next-app 을 쳤을 때가 아니라 이름을 설정해야 비로소 프로젝트가 만들어진다.

 

그때 Next 스크립트에서는   packages > create-next-app > index.ts > run 함수 가 실행된다.

그 프로세스는 아래에서 알 수 있다. 

// packages > create-next-app > index.ts > run 함수 

async function run(): Promise<void> {
  const conf = new Conf({ projectName: 'create-next-app' })

  if (program.resetPreferences) {
    conf.clear()
    console.log(`Preferences reset successfully`)
    return
  }

  if (typeof projectPath === 'string') {
    projectPath = projectPath.trim()
  }

  if (!projectPath) {
    const res = await prompts({
      onState: onPromptState,
      type: 'text',
      name: 'path',
      message: 'What is your project named?', // -> 
      initial: 'my-app',
      validate: (name) => {
        const validation = validateNpmName(path.basename(path.resolve(name)))
        if (validation.valid) {
          return true
        }
        return 'Invalid project name: ' + validation.problems![0]
      },
    })

    if (typeof res.path === 'string') {
      projectPath = res.path.trim()
    }
  }

...


}

 

 

 

이렇게 프로젝트 이름을 설정하고 

"... (너무 길다) " 에 있는 try 문을 거치면 

packages > create-next-app > create-app.ts > createApp 함수가 실행되고 Next project가 만들어진다. 

 

 

2. yarn start

그럼 next 앱을 실행하면 어떤 코드가 실행될까?

 

그런데 yarn start 명령어를 치면 아래 에러가 나온다.

When running `next start` or a custom server in production mode a production build is needed.

그 이유는 프로덕션 모드에서는 next start 스크립트를 실행하는데 프로덕션에서는 빌드된 파일들이 필요하기 때문이다. 

 

 

(공식문서 내용)

next build생산을 위해 애플리케이션의 최적화된 버전을 생성합니다. 이 표준 출력에는 다음이 포함됩니다.

  • getStaticProps또는 자동 정적 최적화를 사용하는 페이지용 HTML 파일
  • 전역 스타일 또는 개별 범위 스타일용 CSS 파일
  • Next.js 서버에서 동적 콘텐츠를 사전 렌더링하기 위한 JavaScript
  • React를 통한 클라이언트 측 상호작용을 위한 JavaScript

https://nextjs.org/docs/pages/building-your-application/deploying

 

Building Your Application: Deploying | Next.js

Congratulations! You're here because you are ready to deploy your Next.js application. This page will show how to deploy either managed or self-hosted using the Next.js Build API. next build generates an optimized version of your application for production

nextjs.org

 

 

아래는 애러를 성명해주는 공식문서이다. 그래서 프로덕션이 아닌 개발모드에서는 next dev를 사용한다.

https://nextjs.org/docs/messages/production-start-no-build-id

 

Could not find a production build

Using App Router Features available in /app

nextjs.org

 

 

그래서 next start 를 위한 next build를 해주었다면 , 

next start 스크립트를 실행하면 기본적으로 http://localhost:3000/ 서버에서 실행된 프로젝트를 볼 수 있다. 

 

이건 어떻게 실행됐을까? 

 

packages > next > src > server > lib > start-server.ts > startServer 함수

를 보면 알수있다.

Next.js는 node 위에서 동작하는 javascript 기반의 프레임워크임으로 node에서 제공하는 http module을 사용할 수 있다. 그래서 아래 코드를 보면 http 모듈을 이용해서 서버를 생성하고 프로젝트를 보내주는 코드를 확인할 수 있었다.

 


import type { Duplex } from 'stream'
import type { IncomingMessage, ServerResponse } from 'http'
import type { ChildProcess } from 'child_process'

import http from 'http' // -> 요기! ✅
import { isIPv6 } from 'net'
import * as Log from '../../build/output/log'
import { normalizeRepeatedSlashes } from '../../shared/lib/utils'
import { initialEnv } from '@next/env'
import {
  genRouterWorkerExecArgv,
  getDebugPort,
  getNodeOptionsWithoutInspect,
} from './utils'


...


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

}

 

프로젝트를 create, start, run, build를 간단하게 뜯어보았지만 동작원리를 공부하는데에 큰 도움이 된 것같다.

 

프레임워크를 그냥 띡 실행해보는게 아니고 앞으로도 이슈를 만났을 때 깊이있게 이해하면서 문제해결을 더 잘할수있도록 접근해야겠다.

 

 

-------------------------------------------

Next.js 12.3.4 버전으로 실습해보았습니다.