[if(kakao) 2021] 'Next.js와 Typescript를 이용한 프론트엔드 개발기' 리뷰

nhchoi·2021년 11월 18일
1

if(kakao) 2021

목록 보기
1/2
post-thumbnail

해당 포스팅은 매년 진행되는 카카오의 컨퍼런스인 if(kakao) 2021에서
Next.js와 Typescript를 이용한 프론트엔드 개발기 세션을 듣고 작성한 내용 정리 및 후기입니다.

Next.js

Vercel에서 만든 Server Side RenderingClient Side Rendering을 동시에 지원하는 리액트 기반의 프레임워크
리액트 사에서도 서버사이드 렌더링 기술을 사용하고 싶다면 Next.js를 사용하라고 권장하고 있음.
Netfilx, Github, Uber, twitch 등 전세계 사람들이 사용하는 서비스에 적용될 정도로 성능, 안정성 측면에서 충분히 검증된 프레임워크이다.

동작방식

  • 사용자가 처음으로 웹페이지에 접근하게 되면 서버사이드 렌더링 방식으로 동작함.
  • 브라우저는 Next 서버에 페이지를 요청하고, 서버는 필요한 데이터를 API 서버에게 요청하게 됨
  • Next 서버는 API 서버로부터 받아온 데이터를 적용하여 사용자가 접근한 페이지를 내려주게 됨
  • User Interaction을 통한 페이지 이동의 경우는 페이지 로딩 시 다운받은 스크립트를 사용하여 클라이언트사이드 렌더링 방식으로 동작하게 된다.

Routing

일반적으로 리액트에서 라우팅 기능을 사용하려면 react-router 라이브러리를 사용해야했음
Router 컴포넌트를 import하여, 라우팅 할 페이지들의 경로와 렌더링 할 컴포넌트를 props로 전달해야하는 번거로움이 있었다.

하지만, Next는 파일 시스템 기반 라우팅을 사용하고 있어 react-router를 사용했을 때 생기는 번거로움이 사라진다.
Next는 빌드 과정에서 pages의 하위 디렉토리가 path로 설정되어 개발자가 라우팅에 대해 신경쓰지 않아도 되는 이점이 있다.

라우팅에는 크게 3가지의 방법이 있다.

  1. index routes(인덱스 라우팅)
  2. Nested routes(중첩된 라우팅)
  3. Dynamic routes(동적 라우팅)
pages/index.js -> /
pages/blog/first-post.js -> /blog/first-post
pages/blog/[slug].js -> /blog/:slug (/blog/hello-world)

Code Splitting

코드가 커질수록 번들링 된 파일의 크기는 증가하게 되고, 초기 로딩 속도가 지연될 수 있다.
이에 대한 해결 방법으로는 필요에 따라 번들 파일을 여러 개로 분리하는 코드 스플리팅 전략이 있다.

일반적으로

  • webpack과 babel 설정, 그리고 dynamic import()
  • React.lazy

를 사용하여 코드 스플리팅을 해왔음

하지만, Next.js는 Zero Config를 주장하고 있고, 개발자가 코드 스플리팅을 신경쓰지 않아도 된다.
Next.js는 빌드 과정에서 pages의 하위 디렉토리에 각 파일을 스플리팅하여 번들링하게 된다.
추가로, 공통으로 사용하고 있는 모듈들을 common 디렉토리 아래에 번들링하여 공통된 코드에 대한 전략적인 코드 스플리팅을 하고 있다.
ES2020에 추가된 dynamic import() 또한 지원하고 있다.


Next.js의 서버사이드 렌더링 흐름

  • Next.js는 _app.js_document.js라는 기본 컴포넌트를 가지고 있음
    • app 컴포넌트는 가장 먼저 실행되는 컴포넌트로, 접근하는 페이지의 컴포넌트를 실행하고 HTML의 body로 구성하게 됨
    • document 컴포넌트는 app 컴포넌트에서 구성한 body가 html 안쪽에 어떤 형태로 들어갈지 구성하는 곳
    • 두 컴포넌트는 서버에서 실행되므로 코드 작성 시 브라우저 API를 사용하지 않도록 항상 유의해야 한다.
  • 사용자가 페이지에 접근하였을때 Next.js 서버가 해당 페이지에 대한 요청을 받고, 먼저 App 컴포넌트가 실행이 됨.
  • page 컴포넌트, document 컴포넌트를 실행하여 모든 컨텐츠를 구성
  • HTML로 출력

추가로 컴포넌트를 구성하는 과정에서 getInitialProps가 정의되어있다면 API 서버로부터 데이터를 받아온 후, page props를 구성하게 된다.


카카오페이 구매 서비스 개발자들의 개발 경험

customize

Next.js 사용하여 개발 시 app 컴포넌트document 컴포넌트를 커스터마이징하는 것은 불가피함.

  • _app.js
    • 서비스 권한 체크 로직, 공통 레이아웃 로직 등 공통 비즈니스 로직을 처리하는 부분
    • 모든 페이지에 적용될 global CSS를 정의하는 부분
    • 주의할 부분 : getInitialProps를 정의할 경우 Next.js의 중요한 기능인 자동 정적 생성 기능이 동작하지 않음
  • _document.js
    • 공통적으로 적용할 마크업
    • app 컴포넌트 다음으로 실행되는 컴포넌트이며 공통으로 사용할 meta 태그 혹은 body 태그안에 들어갈 내용을 작성할 때 사용함.
    • 서비스에 필요한 스크립트를 추가하는 곳
    • 주의할 부분 : Html, Head, Main, NextScript 컴포넌트를 리턴해줘야하는 규칙이 있음
      • Head 컴포넌트는 모든 페이지에 적용되는 컴포넌트로 주의해야함
      • 만약, 페이지별로 적용을 하고싶으면 각 페이지에서 next/head 모듈의 Head 컴포넌트를 사용해야함
      • Main 컴포넌트의 외부 컴포넌트는 브라우저에서 초기화되지 않기 때문에 팝업, 사이드바와 같은 공통 페이지에서 사용하는 컴포넌트는 반드시 app 컴포넌트에 작성해야함.

Cache

이전 페이지의 스크롤이 유지되지 않는 상황은 어떻게 처리할까?
next/router 모듈로부터 Router 객체를 가져온 후 이벤트 메소드를 통해 routeChangeStart 이벤트에서 스크롤 정보를 캐싱하고 routeChangeComplete 이벤트에서 캐시된 스크롤 정보를 불러와서 페이지에 적용하는 방식으로 처리함

API Cache
popstate를 이용하여 뒤로가기 여부를 판단하고 해당 Flag를 Store에 저장함
이 로직은 app 컴포넌트에 작성하여 모든 페이지에 적용될 수 있도록 함
page 컴포넌트 별로 정의한 getInitialProps에서 popSelector를 통해 스토어에 저장된 뒤로가기 Flag를 가져온 후 action을 dispatch할지 cached 된 데이터를 그대로 사용할지 여부를 판단하였음


Routing

Next.js는 Router 객체, Link 컴포넌트를 사용해서 원활한 경로 변경을 제공함

  • Router 객체
    • router.replace(..., ..., { shallow : true })
    • replace를 통해 경로 전환이 가능함
    • 옵션으로 shallow를 사용할 수 있음
  • Link 컴포넌트
    • shallow props를 사용할 수 있음
    • <Link href="..." shallow>
  • shallow 옵션을 사용하면 getInitialProps, getServerSideProps 메소드가 호출되지 않고 현재 상태를 유지한 채 경로 전환이 가능하다.



Typescript

타입스크립트 사용 이유

  • 정적 타입을 지원하기 때문에 컴파일 단계에서 오류를 포착할 수 있음
  • IDE에 정보 표시로 개발 효율성이 향상됨
  • API 데
    이터 모델링으로 데이터에 대해 쉽게 파악할 수 있음

타입스크립트 환경설정

  1. tsconfignext.config를 typescript 확장자인 *.ts, *.tsx로 변경함
  2. 스트립트 파일 확장자를 *.ts, *.tsx로 변경함
    a. 이 부분에서 엄청난 타입 오류 발생 → 모두 any 타입으로 지정함
  3. eslint 설정 후에 발생하는 오류를 처음부터 모두 잡지 않고 off 또는 warn 처리함
  4. 빌드를 돌려봄
    a. 돌아간다면 아주 행복
    b. 에러가 발생함

Next.js는 Page 디렉터리 하위 리액트 컴포넌트로 Page를 구성하게 됨

카카오페이 구매에선 각 페이지 폴더 하위로 action, reducer, saga를 구성하였었음

Pages
  L path
    L page.jsx
  L store
    L action.js
    L reducer.js
    L saga.js

타입스크립트 전환 이후에 jsx 문법을 사용하는 파일은 tsx 확장자로 바꿔주었음 (saga.js 파일도 jsx 문법을 사용하는 부분 때문에 tsx 확장자로 바꿈)
Pages
  L path
    L page.tsx
  L store
    L action.ts
    L reducer.ts
    L saga.tsx

그런데 !!
Next.js에선 page 폴더 하위에 구성된 리액트 컴포넌트로 페이지를 구성하기 때문에 pages 폴더 하위에서 tsx 확장자를 가지는 파일은 반드시 리액트 컴포넌트를 export 해야함 ...
jsx 문법을 사용하기 위해 saga 파일을 페이지로 export하면.. 페이지로 구성되어버리는 문제가 발생.. ㅠ

따라서, 페이지를 구성하는 것이 아닌 컴포넌트(saga, action 등)는 따로 분리하는 것이 적절

Pages
  L path
    L page.tsx
Store
  L path
    L action.ts
    L reducer.ts
    L saga.ts

포팅 전략

typescript 포팅 전략
포팅하며 현재 서비스 되고 있는 프로젝트 동작에 문제가 없어야 하기 때문에 문제가 발생하더라도 최소한으로 문제를 대처하기 위해 점진적으로 진행하도록 한다.

순차적 목표
1. config부터 빌드 성공까지 환경 설정을 완료하는 것 + jenkins 빌드 설정
2. API 모델의 Type 정의
3. Redux에 정의된 모델을 적용
4. Component 내 type 정의


* * *

앞으로

getInitialProps 대신..

Next.js에선 getInitialProps 보다 getStaticPropsgetServerSideProps 사용을 권장하고 있음
getInitialProps는 클라이언트와 서버에서 모두 동작하기 때문에 로직이 클라이언트와 서버로 나뉘어져 코드의 복잡도를 높일 수 있다.
정적 생성에서 사용하는 getStaticProps와 서버사이드에서 동작하는 getServerSideProps 사용을 권장하고 있음


Next.js 11

  • next/script
    • 써드파티 스크립트를 배치할 때 번거로움을 줄여줌
    • beforeInteractive, afterInteractive로 우선순위를 쉽게 설정하여 로드 성능을 향상시킬 수 있음
    • onLoad 콜백을 지정해서 스크립트 로드 이후에 동작하는 콜백 정의도 할 수 있음
  • next/image
    • 정적 이미지의 너비와 높이를 자동으로 설정해주는 기능
    • placeholder 속성에 blur 값 설정 가능










재밌게 잘 들었습니다 :)

profile
👩‍💻

0개의 댓글