ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Nuxt.js] Nuxt 학습기 - (2) LifeCycle & 렌더링 모드
    Front-End(Web)/Vue 2021. 11. 28. 19:54
    반응형

    🤔 서론

    앞서 언급한, Nuxt.js의 SSR모드는 Universal SSR 이라고 지칭하며, 이는 고전적인 정적 SSR 방식과는 조금 다르다.

    새로운 SSR 모드가 반영되게 된 배경으로는, 아무래도 기존 SSR의 단점인 데이터 통신 비효율성과 클라이언트 퍼포먼스를 개선하기 위해 CSR의 장점을 일부 차용한 것이다.

     

    이러한 Universal 모드를 비롯해서, Nuxt.js 에서 지원하는 3가지 렌더링 모드(SPA, Universal, Static) 에 대해 자세히 알아보겠다!


    📗 Nuxt.jsLifecycle

    마지막으로, Nuxt.js의 각 사이드(Server, Client 및 라우팅 Navigation 시) Lifecycle을 좀 더 구체적으로 알아보고 포스팅을 마무리하고자 한다.

     

    1. Server (어플리케이션의 모든 초기 request 시 실행)

    • 서버 시작 : nuxt start
    • 생성 프로세스 시작 : nuxt generate
    • Nuxt hooks
    • serverMiddleware
    • plugins(Server) : nuxt.config.js 정의된 순서대로
    • nuxtServerInit :  Server의 첫 LifeCycle Hook. Entry Point의 Store Action을 위한 매서드. (index.js 에서만 정의가능)
    • middleware : 페이지 컴포넌트 렌더링 전 호출. 렌더링 조건에 활용되며, 전역, 레이아웃, 페이지 등 각각에 정의가능
    • validate : 페이지 컴포넌트 렌더링 전 호출. 동적 라우팅 파라미터(dynamic route parameter) 정의에 유용하다.
    • asyncData, fetch : 페이지 컴포넌트 렌더링 전 매번 호출. 반환값이 컴포넌트 data() 프로퍼티에 병합(merge).
    • beforeCreate, created : Vue LifeCycle 메서드. beforeCreate는 Vue 인스턴스 초기화 시 호출.
    • new fetch : Vue 인스턴스 생성 이후 Store 갱신. (Nuxt 2.12 버전에 반영)
    • render : routeContext. 상태 일렬화(Nuxt.js hook)
    • render : route. HTML 렌더링(Nuxt.js hook)
    • render : routeDone. HTML을 브라우저로 보냄
    • generate : before. HTML 파일들을 생성 (Nuxt.js hook)
    • generate : page. HTML 편집가능
    • generate : routeCreated. Route 생성
    • generate : done. 모든 HTML 생성완료

     

    2. Client (Nuxt 모드에 상관없이 브라우저에서 실행되는 LifeCycle)

    • HTML 파일을 받음. assets 로딩(.css, .js 등)
    • Vue Hydration (JS 반영)
    • middleware : 미들웨어 또는 라우트 미들웨어.
    • plugins(Client) : nuxt.config.js 정의된 순서대로
    • asyncData : 페이지 컴포넌트 로딩 전 호출. 비동기로 호출되며, 반환값이 컴포넌트 data() 프로퍼티에 병합(merge).
    • beforeCreate, created : Vue LifeCycle 메서드. beforeCreate는 Vue 인스턴스 초기화 시 호출.
    • new fetch : Vue 인스턴스 생성 이후 Store 갱신. (Nuxt 2.12 버전에 반영)
    • beforeMount, mounted : Vue LifeCycle 메서드

     

    3. Navigate (<nuxt-link>, Client의 middleware 부터 동일하게 발생)

    • middleware : 미들웨어 또는 라우트 미들웨어.
    • asyncData : 페이지 컴포넌트 로딩 전 호출. 비동기로 호출되며, 반환값이 컴포넌트 data() 프로퍼티에 병합(merge).
    • beforeCreate, created : Vue LifeCycle 메서드. beforeCreate는 Vue 인스턴스 초기화 시 호출.
    • new fetch : Vue 인스턴스 생성 이후 Store 갱신. (Nuxt 2.12 버전에 반영)
    • beforeMount, mounted : Vue LifeCycle 메서드

    📗 Nuxt.js Rendering 모드

    Nuxt에는 3가지 빌드(혹은 렌더링) 옵션들이 존재한다. nuxt.config.js 의 build 옵션 혹은 package.json의 npm 명령어를 통해 빌드 방식을 달리할 수 있다.

    // nuxt.config.js
    
    export default {
      mode:'spa',	// or 'universal'
      build: {
        ssr:true 혹은 false,
      }
    }

    Nuxt.js는 SPA(Single Page Application), Universial App, Static App을 지원한다.

    이는, package.json 에서 혹은 nuxt.config.js의 mode 프로퍼티를 통해 설정할 수 있다. (mode: 'spa / 'universial')

     

     

    1. SPA (Single Page Application)

    우리가 흔히 사용하는 Vue.js, 혹은 React.js 등의 개발방법과 같은 CSR 기반의 어플리캐이션 개발모드이다.

    그렇기에, 개발모드를 실행하면 페이지 소스가 적으며, 별도로 server를 포함하지 않는다.

     

    * dev - development mode

     

    * build & start - production mode

     

    (build: Webpack을 통한 어플리케이션 빌드. CSS, JS 파일을 최소화한 프로덕션 버전 / start: 빌드 이후, 프로덕션 서버로 앱 실행)

    // cmd
    yarn build	// nuxt.config.js mode: 'spa' 설정시
    yarn build --spa

    .nuxt는 기본적으로 숨겨져있다가, build 시에 나타나는 폴더이다.

    dev 혹은 build를 통해 생성된 소스파일들이 저장되며, 파일들이 수정될 시 자동으로 갱신된다.

    (즉, 이 파일에서 디버깅하는 것은 효과적이나 버전관리에는 부적합. .gitignore에도 추가하는 것이 좋다!)

     

     

    2. Universial App (SSR + CSN)

    'universial' 모드 혹은 SSR(Server-Side Rendering) + CSN(Client-Side Navigation) 혼용모드로 이해하면 된다.

    위에서 언급했듯, 첫 화면만 과거의 서버 렌더링 처럼 완성된 HTML을 뿌려주고(SSR), 이 후엔 AJAX로 동적 라우팅을 수행하여 필요한 데이터 만 가져올 수 있다면 좋겠다(CSN)고 생각하여 등장한 새로운 SSR 방식이다. 

     

    * dev - development mode

     

    페이지를 미리 그려놓으므로, SPA에 비해 소스코드가 증가한 것을 볼 수 있다.

     

    * build & start - production mode

    universal 모드에선 이처럼, HTML 소스코드가 형성되어 SSR이 적용된 것을 알 수 있다.

    또한, 상단의 CSS 역시 dev버전에 비해 상당히 축약된 것이 보이며, 배포용으로 적절하도록 압축된 것을 알 수 있다.

    하지만 초기화면만 SSR이고, 이후 라우팅시에는 화면이 깜빡이며 리페인팅되는 CSR 플로우를 탄다.

     

     

    * 최초 SSR 라이프사이클

     

    Nuxt.js SSR 라이프사이클 순서대로 일어나는 메서드(혹은 동작) 들을 정리했다. 각 메서드들의 구체적인 기능 및 예시는 다음 포스팅에 세부적으로 작성하겠다!

     

    Universal App 의 동작과정은 위 그림과 같다. 첫 라이프사이클 전체로 SSR을, 이후 Navigate에선 부분적 CSN으로 그리는 것이다.

     

    1) nuxtServerInit

     

    Universal 모드에서 사용할 수 있는 Store Action 메서드이다.

    이는 SSR 시점에서 맨처음 실행되며, Store에 미리 정보를 담기 위한 비동기 통신을 요청할 때 주로 사용된다.

    이 함수의 인자는, 첫 번째는 '스토어 컨텍스트', 두 번째는 '넉스트 기본 컨텍스트' 이다.

    // store/index.js
    actions: {
      nuxtServerInit(storeContext, nuxtContext) {
        storeContext.commit('뮤테이션 함수명');
        if (process.server) {
          const { req, res, beforeNuxtRender } = nuxtContext;
        }
      }
    }

     

    2) middleware

     

    middleware는 미리 정의된 함수이며, 페이지 또는 레이아웃을 렌더링하기 전에 실행하는 사용자 정의 기능을 설정한다.

    주로, 로그인 체크, 유효성 검사 등에 활용하며, 하나의 middleware는 하나의 js파일에 정의된다.

    전체 페이지에 적용하려면 nuxt.config.js의 router 옵션을, 해당 페이지에 적용하려면 컴포넌트에서 middleware 옵션을 추가하면 된다.

     

    3) validate

     

    Nuxt에서 사용자가 제출하는 정보들을 검사하는 validate() 메서드를 제공한다.

    이는, _id.vue 등 동적 라우팅 페이지에 사용되며, 파라미터는 { params, query, store } 의 객체 형태이다.

    Data 셋팅보다는 유효성 검사의 목적이 크기에, Boolean을 반환해야하고 false의 경우 layouts 디렉토리의 error.vue 페이지로 이동시켜준다.

     

    4) asyncData & fetch

     

    asyncData() 역시 Nuxt 메서드로, API데이터를 초기에 가져와서 세팅하는 역할을 담당한다. (페이지 레벨에서만 사용 가능)

    라이프사이클 상 validate 다음에 호출되나, 컴포넌트가 렌더링되기 전의 시점이므로 this 활용이 불가하다.

    대신, 컴포넌트의 data와 merge되므로, 여기서 같은 데이터 스트럭쳐를 반환하는 것이 이상적이다.

     

    fetch() 는 API데이터를 컴포넌트가 아닌 Store(Vuex)에 세팅하기 위한 메서드이다.

    컴포넌트와 페이지 모두 적용가능하며, context.store.commit() 을 통해 mutation을 트리거할 수 있다.

     

    두 메서드 모두 Nuxt의 context를 인자로 받으며, 그렇기에 여기의 route, store 등의 정보를 활용하기가 유용하다.

     

    5) Render & Navigate

     

    위 과정이 모두 끝나면 SSR이 마무리되고 렌더링이 시작된다.

    이후, <nuxt-link> 혹은 $router 이동 함수 등으로 URL을 변경할 경우 Navigate되면서 middleware 사이클부터 다시 진행되는데, 이것이 CSN(Client-Side Navigation) 에 해당되는 것이다.

     

     

    * CSN 과정

     

    Nuxt.js의 Universal 모드에서는 URL 이동시 로드해야할 부분만 서버에서 렌더링하고 클라이언트로 넘겨준다.

    이 때, 서버 렌더링으로 인한 깜빡임이 발생하지 않는데, 이것은 CSN의 Pre-fetch 과정 때문이다.

     

    1) Pre-fetch

     

    Pre-fetch(프리패치)는 말 그대로 다음 페이지를 미리 가지고 오는것으로, <nuxt-link> 를 통해 이것이 가능하다.

     

    이는, Vue Router의 <router-link> 역할을 수행할뿐만 아니라, 내부 <nuxt-link>에 해당하는 다음 페이지들을 참조해 백그라운드에서 chunk 파일로 다운로드해온다.

    이 때, 미리 가져온 데이터(chunk 파일)은 js파일이다. 즉, 새로운 HTML을 요청하는 것이 아닌 JS파일을 통해 브라우저의 렌더링을 구현하는 것이다.

     

    2) Hydration

     

    렌더링 과정을 마치고 브라우저로 전달된 HTML파일 위에 JS코드들을 실행하는 동작이다.

    그렇기에, Hydration 과정을 통해 SSR 앱에 동작과 반응성을 부여하는 것이다.

     

     

    3. Static App

    Nuxt.js는 Universal App, SPA 외에도 정적 페이지(Static App) 개발모드를 지원한다.

    모든 페이지가 Pre-rendering(프리랜더링) 된 빌드를 생성하고, SSR을 하지 않기 때문에 자체 server를 포함하지 않는다.

     

    이를 구현하기 위해, 아래와 같이 nuxt.config.js 설정 및 배포를 진행해야한다.

    // nuxt.config.js
    
    export default {
      target: 'static',
    }
    // build cmd
    npm run generate
    yarn generate

    그러면, dist(default)에 모든 페이지가 렌더링된 빌드가 생성된다.

     

    하지만, _id 처럼 동적 라우팅되는 페이지들은 pre-rendering이 되지 않으므로, nuxt.config.js에서 generate 옵션을 설정해준다.

    // nuxt.config.js
    
    generate: {
      routes: function() {
        return [
          '/list/id',
        ]
      }
    }
    
    
    // 모든 id값을 넣는 방법
    
    const axios = require('axios'); 
    
    generate: {
        routes: function () {
          return axios.get('http://test.com/posts')
            .then(res => {
                const routes = []
                for (const key in res.data) {
                   routes.push('/posts/' + key)
                }
                return routes
            })
        }
      },

     

    - 요약

    구분 SPA Universal Static Web
    mode spa universal spa
    universal
    명령어 nuxt build --spa
    nuxt generate --spa
    nuxt build nuxt generate --spa
    nuxt generate
    특징 Nuxt는 html이 1개가 아니다. 진입점이 다름 SSR을 위한 서버가 필요함 pre-rendering된 결과를 얻을 수 있음
    동작 최초 view(내용이 빈 html) 접근 후 SPA방법으로 동작 최초 진입점을 서버에서 렌더링 후 이후에는 SPA처럼 동작 최초 진입점을 미리 렌더링된 페이지로 접근 후 이후에는 SPA처럼 동작
    페이지
    소스보기
    안보임 보임 spa 모드: 안보임
    universal 모드: 보임
    서버 start 명령어를 사용하면 필요
    정적 파일 호스팅은 불필요
    필요 불필요
    SEO 불리 유리 spa 모드: 불리
    universal 모드: 유리
    • SEO가 중요한 쇼핑몰, 블로그, SNS, 게시판 등 서비스는 universal 모드로 build & start 를 권장
    • 일부 페이지만 SEO가 필요하거나 프로모션, 기업소개 홈페이지 등 페이지 수가 적은 경우에는 generate로 Static 으로 그려줌
    • 내부용 서비스, 로그인 등 SEO가 필요없는 경우 SPA가 유리. (단, Nuxt SPA는 route마다 HTML이 생기므로 vue-cli 추천)

     


    이번에는 Nuxt.js의 렌더링 모드들을 비교할뿐만 아니라, Universal App이라고 하는 새로운 SSR방식을 정확히 정립하게 된 유의미한 포스팅이었다!

     

    단지 SSR만을 위한 프레임워크로 이해한 Nuxt.js 였으나, 실제로는 더 많은 모드들을 지원했다.

    특히, Universal SSR은 SSR과 CSR 모두의 장점을 혼용하기 위해 새로이 고안된 방법인 것도 확인하였다.

     

    나는 Nuxt.js 토이를 선행하고 포스팅을 했기에 디렉토리 구조나 라이프사이클 내 메서드들이 익숙했으나,

    아마 이 글을 처음 접하는 독자들은 아직 middleware, validate 등의 효용이 확실하게 와닿고 있지 않을것이다.

     

    이 부분에 대해 다음 포스팅을 진행해보도록 하겠다!

     

    [출처]

     

    반응형
Designed by Tistory.