우선 Next.js는 자체 서버 위에서 돌아가니 server와 관련된 폴더에 관련된 코드가 있을 것이라고 생각했다. Next.js 레포를 들어가서 찾아보니 packages/next/src/server/lib/start-server.ts
파일을 발견할 수 있었다. 아래 코드는 start-server.ts
의 코드 일부이다.
export async function startServer({
dir,
prevDir,
port,
isDev,
hostname,
useWorkers,
allowRetry,
keepAliveTimeout,
onStdout,
onStderr,
}: StartServerOptions): Promise<TeardownServer> {
...
// setup server listener as fast as possible
const server = http.createServer(async (req, res) => {
try {
if (handlersPromise) {
await handlersPromise
handlersPromise = undefined
}
sockets.add(res)
res.on('close', () => sockets.delete(res))
await requestHandler(req, res)
} catch (err) {
res.statusCode = 500
res.end('Internal Server Error')
Log.error(`Failed to handle request for ${req.url}`)
console.error(err)
}
})
...
server.on('error', (err: NodeJS.ErrnoException) => {
if (
allowRetry &&
port &&
isDev &&
err.code === 'EADDRINUSE' &&
portRetryCount < 10
) {
Log.warn(`Port ${port} is in use, trying ${port + 1} instead.`)
port += 1
portRetryCount += 1
server.listen(port, hostname)
} else {
Log.error(`Failed to start server`)
console.error(err)
process.exit(1)
}
})
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)
})
}
위 형태로 구성된 함수인데, 위에 적어놓은 코드가 startServer
함수의 핵심이라는 생각이 들었다. Node.js 내장 모듈 http
를 통해 서버를 생성하는 부분을 확인할 수 있었다. socket이 있는걸로 봐서, HMR을 소켓을 통해 구현했구나 생각이 들었다. 그 외 server.on('error')
부분에서 우리가 실수로 3000번 포트를 사용하는 프로젝트의 서버를 두 개 열었을 때 볼 수 있는 에러 메세지를 확인할 수 있었다.
server.on('listening')
부분에서 우리가 next.js 프로젝트를 정상적으로 시작했을 때 우리를 반겨주는 콘솔을 볼 수 있었다.
몰랐던 부분도 있었는데, internal server를 디버깅할 수 있는 옵션이 있는 것을 확인할 수 있었다. 내부 에러가 생겼을 경우에 확인하기 힘든 경우가 있었는데, 이 옵션을 통해 디버깅을 할 수 있을 것 같다.
그리고 normalizeRepeatedSlashes
라는 유틸함수를 통해 인코딩 되지 않은 백슬래시를 포워드슬래시로 치환하여 정상적인 URL의 형태로 변환하는 부분도 확인할 수 있었다.