Nuxt.js + Express 환경에서 Bootstrap-Vue 및 기타 UI 프레임워크 사용중 SSR 처리가 느려지는 성능 문제 주의하세요.

물론 현재 Nuxt.js 나 Vue.js 도 상당히 성숙환 개발 환경이 되가고 있습니다만… 아직도 아무생각없이 쓰기에는 성능 문제가 발생할 확률이 높은 문제가 있습니다.

이번에 설정 관련 페이지를 만들면서 Bootstrap-Vue ( 현재 사용중인 버전은 2.0.0-rc.24 ) 를 사용해서 만들었는데… 입력란이나 버튼들을 합쳐서 한 100여개정도 들어가는 정도의 페이지였습니다. 처음엔 크게 신경을 쓰지 않았는데 본 서버에 배포하고 나서 테스트하니 페이지 로딩에 거의 4 ~ 500ms 초 정도 먹더군요. 전체 로딩도 아니고 html 페이지 하나 로당하는게 걸리는 시간이 이정도인 겁니다.

깜짝놀랐습니다. 용량도 30여 KiB 정도인데.. 로컬 개발환경에서도 테스트 해보니 본서버 정도는 아니라도 200 ~ 300ms 정도 소요되는 것으로 보아 문제가 있는게 보이더군요.

그래서 혹시나 AsyncData() 에서 axios 로 API 서버로 호출하는 게 시간이 많이 걸리는 건가하고 아래와 같이 axios 응답시간도 테스트 해봤습니다.


    const instance = axios.create()

    instance.interceptors.request.use(config => {
      config.metadata = {
        startTime: new Date()
      }
      return config
    }, error => {
      return Promise.reject(error)
    })

    instance.interceptors.response.use(response => {
      response.config.metadata.endTime = new Date()
      response.duration = response.config.metadata.endTime - response.config.metadata.startTime
      console.log(new Date(), 'response', response.duration, 'ms')
      return response
    }, error => {
      error.config.metadata.endTime = new Date()
      error.duration = error.config.metadata.endTime - error.config.metadata.startTime
      console.log(new Date(), 'error', error.duration, 'ms')
      return Promise.reject(error)
    })

테스트 결과 5 ~ 20ms 내에서 크게 문제없이 응답을 받고 있어서 API 서버 호출쪽은 문제가 없는 것으로 판단했습니다.

API 서버쪽 문제가 없다면 결국은 클라이언트 웹서버 단에서 문제가 있는게 분명합니다. Nuxt.js 나 Vue.js 혹은 사용중인 express 문제인지 확인하기 위해 사용중인 레이아웃에 내용만 비워서 테스트 해봤습니다. 결과는 20 ~ 40ms 정도 양호한 결과를 보여주더군요.

그래서 이때부터 사용중인 컴포넌트들을 의심하고 검증하는 작업을 진행했습니다.

Bootstrap-Vue 컴포넌트 테스트

페이지에서는 <b-form-group> 과 <b-input> 컴포넌트를 많이 사용하는데 테스트를 다음과 같은 코드를 200줄 정도 넣고 테스트 해봤습니다.

<template>
<div>
<b-form-group label="제목" :label-cols-sm="2"><b-form-input v-model="value1"></b-form-input></b-form-group>
<b-form-group label="제목" :label-cols-sm="2"><b-form-input v-model="value1"></b-form-input></b-form-group>
<b-form-group label="제목" :label-cols-sm="2"><b-form-input v-model="value1"></b-form-input></b-form-group>
... x 200개
</div>
</template>

<script>
export default {
  data() {
    return {
      value1: ''
    }
  }
}
</script>

본서버와 마찬가지로 4~500ms 정도 나오더군요. dev 로 실행해서 그런건가 해서 build 해서 해봤지만 거의 차이가 없었습니다. 게다가 같은 변수를 v-model 로 넣어서 그런지 값을 입력하면 엄청난 랙이 생기면서 200개의 입력란에 느리게 입력이 됩니다. 매우 충격이었습니다. Vue 는 빠르다고 생각해왔는데…

Native HTML Tag + Bootstrap 4 CSS

다시 이번에는 동일한 모양으로 bootstrap-vue 가 아닌 원래 bootstrap 4 의 기본 html 태그와 CSS 클래스만 사용하는 코드로 변경해서 200 개 정도 배치하고 테스트 해봤습니다.

<template>
<div>
<div class="form-group row">
  <label class="col-form-label col-sm-2">제목</label>
    <div class="col-sm-10">
     <input v-model="value1" class="form-control" />
    </div>
</div>
<div class="form-group row">
  <label class="col-form-label col-sm-2">제목</label>
    <div class="col-sm-10">
     <input v-model="value1" class="form-control" />
    </div>
</div>
<div class="form-group row">
  <label class="col-form-label col-sm-2">제목</label>
    <div class="col-sm-10">
     <input v-model="value1" class="form-control" />
    </div>
</div>
... x 200개

</div>
</template>

<script>
export default {
  data() {
    return {
      value1: ''
    }
  }
}
</script>

결과는 50ms 대로 응답시간이 나오더군요. 또한 입력란에 입력을 하면 랙없이 200개의 입력란에 동시에 입력이 됩니다. 와우! 결국 Vue 가 느린게 아니라 Bootstrap-Vue 의 컴포넌트가 느린것이었습니다.

Bootstrap 4 CSS + Custom Component

마지막으로 비슷한 기능의 자체 컴포넌트를 만들어 200개를 배치하고 테스트 해봤습니다.

약 60 ~ 70ms 정도로 그다시 손실없이 응답결과를 받을 수 있었습니다. 하지만 입력란에 입력을 하면 Boostrap-Vue 컴포넌트때만큼은 아니더라도 상당한 입력 지연이 발생합니다.

그리고 위 각 상황별로 v-for 로 200개를 반복해서 넣는 방식으로 해도 그냥 하드코딩한것과 거의 동일했습니다.

결론

  • UI 프레임워크 (특히 Bootstrap-Vue) 를 사용할 때는 성능 이슈가 생길 수 있는 점을 감안해서 사용해야 합니다.
  • 어떤 경우에던지 컴포넌트로 감싸서 사용하면 일정부분 성능하락의 이슈(SSR 서버렌더링 속도, 입력 지연)가 항상 발생할 가능성이 높습니다. (이런 문제는 React.js 에서는 어떤지 궁금하네요. 아니면 Vue.js 에서도 제가 모르는 해결책이 있는 건지..)
  • 결국 페이지 성격에 따라 동일한 형태의 내용이 반복적으로 나오는 경우에는 가급적 컴포넌트를 사용하지 않고 기본 html 태그를 사용해 v-for 반복문으로 처리하는 것이 가장 좋습니다. 특히 초기페이지 같이 많은 내용이 나와야 하는 경우 필수적일 것으로 판단됩니다.
  • 반면에 페이지내에 몇번 사용하지 않는다면 작업 편의를 위해 UI 프레임워크를 적극적으로 활용하는 것도 개발 속도면에서 좋은 선택이라고 생각되네요.

Vue.js 에서 SSR 렌더링이나 입력 성능 저하가 없는 컴포넌트는 만들기는 힘든 걸까요? Vue.js 고수님들의 고견이 필요합니다~! 댓글 달아주시면 감사하겠습니다.