마침 Next.js에 관심을 갖고 강의를 듣고 개인 프로젝트를 하고 있는 시점에서 원티드 프리온보딩 챌린지가 Next.js로 열렸다. 인원을 선별해서 진행하는지 잘 모르지만, 꼭 하고 싶다. 2주간의 몰입! 지금 시점의 나에게 너무나도 필요한 시간인듯 하다.
아래는 사전과제에 대한 내용이다..!
CSR(Client-side Rendering)이란 무엇이며, 그것의 장단점에 대하여 설명해주세요.
CSR
웹 앱의 렌더링 방식을 말하며, 브라우저로부터 데이터 요청을 받은 클라이언트가 서버로부터 데이터를 받아 화면에 렌더링하는 형식을 말한다. 데이터를 클라이언트측에서 처리하는 것이며, 쉽게 말해 데이터를 서버로부터 받아서 브라우저에 뿌려주는 주체가 클라이언트이다. Server로부터 빈 HTML과 JS 데이터를 한번에 다운받고(이때 유저는 화면에서 아무것도 보이지 않음) 그 후 빈 HTML에 JS를 이용하여 DOM을 동적으로 생성하여 Client의 브라우저가 렌더링하게 된다.
장점
1. 한번에 HTML,JS를 다운로드하기 때문에 첫 페이지 로딩 후, 사이트 다른 곳으로 이동 시 미리 다운받은 데이터들을 사용하기 때문에 로딩 속도가 빠르다.
2. 첫 페이지 렌더링 시 서버에서 받은 데이터로 대부분 데이터를 클라이언트가 처리하므로 서버 부담이 적다.
단점
1. 첫 페이지 렌더링 시 서버에서 HTML과 JS를 한번에 받아오므로 페이지 처음 로딩 시간이 길다.(웹 사이트 이탈 가능성이 높다)
2. 데이터가 서버에서 클라이언트로 페칭하는 시간 동안 빈 화면이 뜨게 된다.
3. SEO(Search Engine Optimization) 최적화에 좋지 않다.
SPA(Single Page Application)로 구성된 웹 앱에서 SSR(Server-side Rendering)이 필요한 이유에 대하여 설명해주세요.
SSR
웹 앱의 렌더링 방식을 말하며, 브라우저에서 요청 받은 데이터를 클라이언트에서 처리하지 않고 서버에서 바로 처리하여 클라이언트로 전송한다.
필요한 이유
SEO 최적화를 위해서 SSR를 사용하는 것이 좋다.
초기 로딩속도 개선이 필요하다면 SSR사용이 유리하다.
Next.js 프로젝트에서 yarn start(or npm run start) 스크립트를 실행했을 때 실행되는 코드를 Next.js Github 레포지토리에서 찾은 뒤, 해당 파일에 대한 간단한 설명을 첨부해주세요.
// next.js/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 }
npm run start’를 자주 입력했지만, 이 코드가 어떤 방식과 형태로 구성되어 실행되는지 생각해보지 못했다.
내 코드를 브라우저에서 확인하기 위해 ‘
우선 nextStart는 비동기 함수로 만들어졌고, 이를 위해 많은 모듈들을 import한 것을 볼 수 있다.
arg는 실행에 필요한 인자로 생각되고, npm run start가 실행되기 위해선 서버가 실행되어야 하기에 startServer라는 모듈이 따로 import 되어있다.
에러 처리를 위한 isError모듈과 getProjectsDir은 말 그대로 프로젝트 문서를 가져오는 것이다. 여기서 validArgs 타입으로 지정되어있는 port, hostname을 가져올 수 있다.
Next.js에서는 두가지 모드로 실행할 수 있다. 개발모드와 프로덕션 모드이다. 개발 모드에서는 개발 서버가 실행되고, 파일 변경 감지와 핫 리로딩 등의 기능이 활성화된다. 반면 프로덕션 모드에서는 미리 빌드된 페이지가 실행된다. PHASE_PRODUCTION_SERVER는 프로덕션 모드에서의 서버를 SSR과 API 엔드포인트 등의 서버 사이드 로직이 처리된다. Next.js 라이프사이클에 실행되는 여러 단계 중 하나의 단계라고 볼수 있다.
--keepAliveTimeout를 통해 arg의 옵션이 유효한지 확인하며, 마지막 await startServer({…})함수로 서버가 실행된다. 인자에는 dir(디렉토리), isDev(개발 모드 여부), hostname(호스트),port(포트번호), keepAliveTimeout(비활성 연결 종료까지 걸리는 시간), useWorkers(워커 프로세스 사용여부)로 이루어져 있다.
끝으로 export { nextStart }하여 다른 파일에서도 사용하도록 했다.
평소 프레임워크, 라이브러리에서 제공하는 기능들을 감사한 마음으로 사용만 했지, 그 기능들이 어떻게 구성되어 있고 실행되는지 살펴본건 처음이다. 이해가 가지 않는 내용들이 많지만 그래도 전체적인 뼈대가 어떻게 만들어져 있고, 어떤 방식으로 실행이 되는지 알게된 좋은 시간이었다.