[원티드] 프리온보딩 프론트엔드 챌린지 7월 - 사전과제

Hwang Won Tae·2023년 6월 23일
3

Front-end

목록 보기
8/9
post-thumbnail
post-custom-banner

올해 1월부터 지금까지,
수 많은 이력서를 제출하고 뜨거운 합격을 맞이하면서 어느정도 멘토링의 필요성을 느끼는 지금,
운이 좋게도 원티드에서 프리온보딩 프론트엔드 챌린지를 진행하는 것이 아닌가?

취업 코칭 및 이력서 특강까지,
삽질하고 있는 내게 좋은 기회가 될 것 같아 바로 지원하였다.

프론트엔드 뿐만 아니라 다양한 분야를 지원해준다. 비용이 따로 없어 손해볼 것 없으니 일단 신청 버튼 누르자.

사전과제


사전과제는 챌린지 참여를 다짐하고 학습을 준비하기 위한 용도이며 학습 커리큘럼이 사전 미션을 기반으로 내용이 구성되어 있다고 한다. 내용은 아래와 같다.

  1. CSR(Client-side Rendering)이란 무엇이며, 그것의 장단점에 대하여 설명해주세요.
  2. SPA(Single Page Application)로 구성된 웹 앱에서 SSR(Server-side Rendering)이 필요한 이유에 대하여 설명해주세요.
  3. Next.js 프로젝트에서 yarn start(or npm run start) 스크립트를 실행했을 때 실행되는 코드를 Next.js Github 레포지토리에서 찾은 뒤, 해당 파일에 대한 간단한 설명을 첨부해주세요.

1번, 2번처럼 CSR의 정의와 장단점, SPA의 SSR 필요성 등은 익숙히 보았으나,
3번처럼 프레임워크의 코드 작동원리와 구조를 직접 뜯어보는 경험은 정말 좋았다.

CSR(Client-side Rendering)


CSR (Client-side Rendering)은 웹 애플리케이션의 렌더링 방식 중 하나로, 브라우저에서 JavaScript를 이용하여 동적으로 콘텐츠를 생성하는 방식이다. CSR에서는 초기에 빈 페이지가 로드되고, 클라이언트 측 JavaScript 코드가 실행되면서 필요한 데이터를 서버로부터 비동기적으로 가져와서 렌더링한다.

장점

  1. 사용자 경험(UX)이 향상될 수 있다.

    사용자가 페이지를 빠르게 로드하고 상호작용할 수 있으며, 필요한 데이터만 가져와서 업데이트할 수 있다.

  2. 서버 부하가 감소한다.

    서버는 초기 렌더링 후 클라이언트 측에서 대부분의 작업을 처리하므로, 서버의 자원 사용량이 감소한다.

단점

  1. 초기 로딩 속도가 느릴 수 있다.

    페이지가 비어 있는 상태로 시작하고, 필요한 데이터와 JavaScript를 다운로드해야 하므로 초기 로딩 시간이 필요하다.

  2. 검색 엔진 최적화(SEO)에 어려움을 겪을 수 있다.

    검색 엔진은 CSR 방식으로 렌더링된 페이지의 콘텐츠를 인식하기 어렵다.

SSR(Server-Side Rendering)


SSR (Server-side Rendering)은 웹 애플리케이션의 렌더링 방식 중 하나로, 서버에서 초기 페이지 렌더링을 처리하여 클라이언트에 완전한 HTML 페이지를 전달하는 방식입니다. 클라이언트의 요청에 따라 서버에서 필요한 데이터를 가져와 페이지를 동적으로 생성한 후, 완성된 HTML 페이지를 클라이언트에 제공합니다.

장점

  1. 초기 로딩 속도가 빠르다.

    서버에서 완전한 HTML 페이지를 생성하여 전달하기 때문에, 사용자는 초기에 빠르게 콘텐츠를 볼 수 있다.

  2. 검색 엔진 최적화(SEO)가 용이하다.

    검색 엔진은 HTML 페이지를 쉽게 읽고 인덱싱할 수 있으므로, 검색 결과에 페이지가 포함되기 쉽다.

  3. 모든 사용자에게 동일한 콘텐츠를 제공한다.

    서버에서 페이지를 완전히 렌더링하기 때문에, 서버에서 처리되는 로직이 클라이언트의 환경에 의해 영향을 받지 않는다.

단점

  1. 서버의 부하가 증가할 수 있다.

    모든 요청마다 서버에서 페이지를 렌더링하므로, 서버의 자원 사용량이 증가할 수 있다.

  2. 복잡한 상호작용이 필요한 경우 추가적인 클라이언트 측 JavaScript 코드가 필요할 수 있다.

    SSR은 초기 렌더링만 처리하므로, 클라이언트 측에서 추가적인 상호작용을 위한 JavaScript 코드를 작성해야 할 수 있다.

SPA(Single Page Application)로 구성된 웹 앱은 왜 SSR이 필요할까?


SPA(Single Page Application)로 구성된 웹 앱에서 SSR(Server-side Rendering)이 필요한 이유는

검색 엔진 최적화(SEO)초기 로딩 속도 개선 때문이다.

이는 SPA로 구성된 웹 앱들은 기본적으로 CSR이다.
SSR은 CSR의 단점을 극복하는 장점을 가졌기 때문에 이를 확인하기 위해 앞선 개념을 다진 것이다.

검색 엔진 최적화(SEO)의 중요성

  1. 검색 엔진에서 노출

    대부분의 웹 사용자는 검색 엔진을 통해 정보를 찾는다. 웹 사이트가 검색 엔진에서 상위에 노출되면 더 많은 유저들이 해당 사이트를 방문할 가능성이 높아진다. 나조차도 최상단의 링크를 먼저 누르기 때문에 SEO가 그만큼 중요하다고 할 수 있다.

  2. 유기적인 트래픽 증가

    검색 엔진에서 유기적인 트래픽을 유입받을 수 있다. 검색 엔진은 사용자가 원하는 정보를 제공하기 위해 웹 사이트의 콘텐츠를 인덱싱하고 순위를 매긴다. 웹 사이트가 관련성 높은 키워드에 대해 상위에 노출되면 사용자가 해당 키워드로 검색할 때 웹 사이트로 유입되는 트래픽이 증가하며 이는 매출과 연관이 생기게 된다.

  3. 브랜드 가시성 강화

    높은 검색 엔진 순위는 웹 사이트의 브랜드 가시성을 강화할 수 있다. 상위에 노출되는 웹 사이트는 사용자에게 신뢰와 신뢰성을 전달할 수 있으며, 브랜드 인지도를 향상시킨다. 사용자들은 일반적으로 상위 순위에 있는 사이트를 신뢰하고 방문하는 경향이 있다.

  4. 경쟁 우위 확보

    SEO를 통해 경쟁사보다 더 나은 검색 엔진 순위를 얻을 수 있다. 경쟁이 치열한 온라인 시장에서 상위 순위에 있는 웹 사이트는 경쟁사들보다 더 많은 유저들의 관심을 받을 수 있고, 비즈니스 성과를 향상시킬 수 있다.

  5. 사용자 경험 개선

    SEO는 사용자 경험을 개선하는 데에도 도움을 준다. 검색 엔진은 웹 사이트의 속도, 모바일 친화성, 콘텐츠 품질 등을 고려하여 순위를 결정한다. 따라서 SEO를 통해 웹 사이트를 최적화하면 사용자들에게 더 나은 경험을 제공할 수 있다.

초기 로딩 속도의 중요성

구글 리서치 2017에 따르면 로딩 속도가 3초를 넘어가면서부터 이탈률이 높게 나타나는 것으로 조사되었다.

SPA(Single Page Application)는 기본적으로 CSR(Client-side Rendering)이기 때문에 JavaScript를 로드하면서 사용자가 이탈할 수 있다.

이로 인해 초기 로딩 속도는 웹 서비스에서 굉장히 중요하다고 할 수 있다.

유튜브는 이처럼 JavaScript로 데이터를 가져오기 전에 초기 화면을 세팅하여 로딩을 개선하였다.

정리

SPA는 클라이언트에서 페이지를 동적으로 렌더링하므로, 초기 로딩 시에는 비어 있는 페이지가 로드된다. 이로 인해 검색 엔진은 초기 상태에서의 콘텐츠를 인식하기 어려워 SEO에 불리하다는 단점이 있다.
또한, 초기 로딩 시 필요한 JavaScript와 데이터를 다운로드해야 하므로 초기 로딩 속도가 상대적으로 느리다.

SSR은 서버에서 페이지를 렌더링하여 클라이언트에 전달하는 방식으로, 초기 로딩 시에도 완전한 페이지를 제공할 수 있다.
이는 검색 엔진이 콘텐츠를 인식하는 데 도움이 되며, 초기 로딩 속도를 개선하여 사용자 경험을 향상시킬 수 있다.

하지만 서버에서 페이지를 렌더링하는 만큼 서버의 부하가 증가한다는 단점이 있다는 것을 인지하자.

Next.js 프로젝트에서 yarn start(or npm run start) 스크립트를 실행했을 때

우리는 Next.js 프레임워크를 통해 프로젝트를 생성할 수 있다.
먼저, 프로젝트를 생성하는 명령어를 터미널에 입력한다.

npx create-next-app my-app

성공적으로 생성되었다. 생성된 프로젝트의 pakage.json에서 script를 확인해보자.

...
"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
...
  • next dev : 로컬에서 development 환경으로 웹 서버를 실행한다.
  • next build : 작성된 코드를 build 한다.
  • next start : 로컬에서 production 환경으로 웹 서버를 실행한다. 이때, 빌드된 .next/ 폴더가 있어야 한다.
  • next lint : 정적 분석 도구로 내가 작성한 코드가 규칙에 적합한지 확인한다.

이제 어떤 코드가 next start 명령어로 작동되는지 Next.js Github 레포지토리로 이동해보자.

처음에는 헤맸으나 명령어에 맞는 코드네임이 잘 구분되어 있어 찾을 수 있었다.

#!/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>`

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

// packages/next/src/cli/next-start.ts

작성된 코드를 보면 우리가 --help와 같은 옵션에 대한 실행 내용이 있다.
또한, 이러한 옵션을 줄여 쓰기 위해 alias 설정까지 하나하나 해주는 것을 깨달았다.

import를 보면 { startServer } 객체를 가져와 적용하는 것을 확인할 수 있다.
내부 코드를 들여다보자.

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

...

// packages/next/src/server/lib/start-server.ts

코드를 살펴보면 createServer 메서드를 사용하여 서버를 활성화 해준다는 것을 알 수 있었다.
별 생각 없이 입력했던 npm start에서 Socket을 열어 서버를 활성화 해주고 관련 옵션들에 관해 콘솔을 찍어준다는걸 직접 확인할 수 있었다.

프레임워크가 좋지만, 무작정 사용하기 보다 근본을 이해하는 것이 중요하다는 것을 느꼈다.
이러한 개념을 이해하고 환경세팅을 하나하나 만들 수 있는 능력을 갖추는 것이 나의 개발 수명을 결정짓는 하나의 척도가 아닐까.

profile
For me better than yesterday
post-custom-banner

0개의 댓글