Next.js를 개인 프로젝트나 회사 업무에 주로 사용하지만 제대로 이해하고 사용한건지 스스로 확신이 없었는데 프리온보딩 챌린지 과제가 굉장히 의미있는 내용들이라고 생각하고 공부할 겸 정리하기로 했습니다.
서버로부터 html 파일을 받아 브라우저에 그리는 작업입니다.
전통적인 서버 사이드 렌더링과 달리 서버에 HTML 문서를 요청하는 것이 아닌 하나의 HTML 파일에서 JS파일을 이용해 페이지를 렌더링하는 것입니다.
장점은 페이지 전환시 이미 받아져있는 JS 파일에서 필요한 부분만 변경하면 되기때문에 SSR보다 빠른 인터렉션이 가능합니다. UI 구성을 컴포넌트 단위로 관리할 수 있고, 코드의 재사용성이 높다는 장점이 있습니다. 최초 호출 이후에는 추가적인 HTML, JS, CSS 요청이 필요없이 페이지에 필요한 데이터만 요청하면 되므로 서버 호출을 줄일 수 있습니다. 컴포넌트들을 이용해 페이지를 유동적으로 생성하므로 비교적 중요하지 않은 정보는 조금 늦게 loading할 수 있는 lazy loading 지원이 가능합니다.
단점은 사용자가 사이트 진입하고 첫 요청을 보낼 때 JS 파일이 다 받아지고 컴파일이 완료되야 컨텐츠를 렌더링할 수 있으므로 첫 화면을 렌더링할 때 비교적 오랜 시간이 소요됩니다. HTML 파일 하나에서 동적으로 페이지를 렌더링하기때문에 SEO에 좋지 않습니다.
SSR은 서버에서 HTML 및 JS파일 등을 모두 다운로드해서 화면에 렌더링하는 방식으로 서버의 안정적인 인터넷 연결을 바탕으로 페이지를 생성해서 렌더링할 수 있는 장점이 있습니다.
SPA로 구성된 웹 앱에서 SSR이 필요한 이유는 SEO라고 할 수 있습니다. SSR의 경우 서버에서 렌더링된 html을 사용자에게 전달하기에 초기 로딩 속도를 줄일 수 있지만 CSR과 같이 필요한 부분만 수정되는 것이 아닌 매번 새 페이지를 로딩하고 렌더링하기때문에 서버에 부담을 주게 됩니다. 그러므로 메인 페이지, 상품 페이지 같이 SEO가 중요한 페이지들은 SSR로 렌더링하고 사용자 개인정보 등이 담긴 페이지 등은 CSR로 렌더링하는 방식으로 활용할 수 있다.
packages/next/bin/next.ts 파일에서 cli에 작성된 commend를 파악하게 됩니다.
41번째 줄에 foundCommand를 통해서 next에서 관리하고 있는 command에 해당하는지 파악한 후
const foundCommand = Boolean(commands[args._[0]])
64번째 줄에서 실제 command를 가져오게 되고
const command = foundCommand ? args._[0] : defaultCommand
128번째 줄에서 해당 command에 맞는 실행을 하게 됩니다.
commands[command]()
.then((exec) => exec(forwardedArgs))
.then(() => {
if (command === 'build') {
// ensure process exits after build completes so open handles/connections
// don't cause process to hang
process.exit(0)
}
})
start의 경우 packages/next/cli/next-start.ts 파일경로로 이동하게 되고 nextStart함수가 실행됩니다.
start의 option으로 입력된 argument들을 살펴본 다음, getProjectDir을 통해서 project의 파일 경로를 확인합니다.
host와 port, keepAliveTimeout 정보를 확인한 후에 75번째 줄의 startServer 함수를 통해 서버를 작동시키게 됩니다.
startServer({
dir,
hostname: host,
port,
keepAliveTimeout,
})
.then(async (app) => {
const appUrl = `http://${app.hostname}:${app.port}`
Log.ready(`started server on ${host}:${app.port}, url: ${appUrl}`)
await app.prepare()
})
.catch((err) => {
console.error(err)
process.exit(1)
})
packages/next/server/lib/start-server.ts 파일에서 startServer가 실행되고 57번째 줄에서 next함수가 실행되면서 packages/next/server/next.ts 파일의 NextServer 인스턴스가 실행되게 됩니다.
server.on('listening', () => {
const addr = server.address()
const hostname =
!opts.hostname || opts.hostname === '0.0.0.0'
? 'localhost'
: opts.hostname
const app = next({
...opts,
hostname,
customServer: false,
httpServer: server,
port: addr && typeof addr === 'object' ? addr.port : port,
})
requestHandler = app.getRequestHandler()
upgradeHandler = app.getUpgradeHandler()
resolve(app)
})