7월 원티드 프리온보딩 프론트엔드 챌린지 사전과제에 대한 답을 나름대로 적어놓은 글입니다.
기존 방식인 서버에서 웹 페이지를 렌더링하는게 아닌 클라이언트 측 사용자의 브라우저에서 웹 페이지를 렌더링하는 기술을 의미합니다.
기존 SPA의 경우 비어있는 HTML을 응답받고 JS를 다운받아 클라이언트에서 JS를 이용해 DOM을 렌더링하는 방식입니다.
이 방식은 클라이언트에서 렌더링이 일어나므로 사용자가 첫 페이지를 보기 까지의 시간이 오래걸렸습니다. 이 때문에 사용자 경험에도 좋지 않고 이로인한 사용자의 이탈율도 많아졌습니다.
그리고 서비스의 SEO도 중요한데 비어있는 HTML을 받기 때문에 SEO에도 좋지 않았습니다.
하지만 SSR을통해 미리 처음 보여지는 화면을 서버에서 만들어 사용자가 웹 사이트에 접근했을때 응답해 빠르게 초기 서비스 화면을 보여줄 수 있습니다.
미리 HTML을 만들 수 있기 때문에 SEO에도 좋습니다.
그리고 서버에서 화면이 렌더링 되므로 Auth 접근을 해야할때 위험하게 클라이언트에게 토큰을 넘겨주지 않기 때문에 보안에도 뛰어납니다.
#!/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 }
github에 존재하는 nextjs 레포에서 next start를 했을때 실행되는 파일입니다.
전체가 nextstart라는 함수로 되어져있고 clicommand라는 타입인거를 보니 cli 환경에서 실행이 가능한 함수인거 같습니다.
특이한거는 async 함수라는점입니다. next start는 next build가 된 파일이 실제로 배포된 환경에서 실행됐음을 클라이언트에서 서버를 띄워서 확인하는 환경이기 때문에 이렇게 비동기 함수로 활용하는거 같습니다.
cli 환경에서 추가적인 옵션 부분을 선언한 부분 같습니다.
밑에 let args를 통해 명령어를 입력받고 try catch문으로 입력받은 명령문의 에러를 검사하는 로직이 작성되어있습니다.
밑에 loadConfig와 startServer 부분이 비동기적으로 위에서 세팅한 값으로 실행되는걸 확인할 수 있습니다.