NUXT 프로젝트 초기 세팅부터 상용 배포 까지
Writer: 장운서

nuxt 를 이용한 프로젝트 진행


이번 프로젝트는 vue 2.6.14, nuxt 2.15.8, typescript 2.8.1대의 버전을 활용해서 FE를 진행하게 되었습니다. 이번 프로젝트의 과정에 있어서 초기 세팅부터 상용 배포까지의 진행 과정을 확인해보도록 하겠습니다.

1. 초기 세팅

제가 코로나에 걸린 관계로 초기 세팅은 준걸 님께서 진행을 해주셨습니다. eslint는 7.32 버전대로 설치를 완료하였습니다.

figma에서 확인 할수 있듯이 버튼들의 초기 동작과 사이즈들을 모두 세분화 시켜주셔서 그에 맞게 초반 컴포넌트들을 제작해주셨습니다. sass는 1.49 버전대로 설치를 진행하였습니다.


2. 퍼블리싱 진행

초반 nuxt를 이용한 퍼블리싱 작업은 수월하게 진행하였습니다. 스타일을 입히는 작업은 sass로 진행을 했고 각각의 컬러와 폰트를 모듈로 구성하여 자주 쓰는 부분들에 대하여 상위에서 관리 할수 있도록 작업하였습니다. 처음 진행하는 nuxt기반 프로젝트지만 많은 수의 페이지가 있는 것이 아니었고 단순한 디자인 구조의 웹 화면구성이었기 때문에 퍼블리싱 작업은 큰 무리 없이 쉽게 작업을 진행 할수 있었습니다. css 대부분의 정렬을 display: flex 를 썻습니다. 달력 화면은 vuetify에 들어가 있는 datepicker를 사용하기로 하였으며 그 외 나머지 레이아웃들은 모두 직접 제작하였습니다.


3. typescript

typescript는 javascript 와는 다르게 정해져 있는 타입 아래에서만 움직여야 하고 대부분의 오브젝트들의 타입들을 정의 해줘야합니다. vuex를 사용하면서 해당 패턴들에 관련된 타입정의를 진행하지 않으면 오류가 나는 부분을 확인했고 첫 세팅으로 nuxt.config.js 파일을 ts 파일로 변환하였습니다.

후에 vue를 쭉 살펴보니 vuex 3.6.2 버전에서는 vuex(상태 관리를 위한 패턴이자 라이브러리) 패턴들에 대한 typescript의 타입 추론을 제공해주지 않는 상태였고 이 부분 관련해서 찾아본 두 가지 방법이 있었습니다.

이 부분은 다음 포스팅에서 따로 다루도록 하겠습니다.

nuxt에서의 typescript를 활용한 타입추론 방법

저희 팀이 타입스크립트를 적용한 이유는 다음과 같은 장점들 때문이었습니다.

1. 높은 수준의 코드 탐색과 디버깅

타입스크립트는 코드에 목적을 명시하고 목적에 맞지 않는 타입의 변수나 함수들에서 에러를 발생시켜 버그를 사전에 제거합니다. 또한 코드 자동완성이나 실행 전 피드백을 제공하여 작업과 동시에 디버깅이 가능해 생산성을 높일 수 있습니다. 실제로 한 연구에 따르면 모든 자바스크립트 버그의 15%가 사전에 타입스크립트로 감지할 수 있다고 합니다.

2. 자바스크립트 호환

타입스크립트는 자바스크립트와 100% 호환됩니다. 따라서 프론트엔드 또는 백엔드 어디든 자바스크립트를 사용할 수 있는 곳이라면 타입스크립트도 쓸 수 있습니다. 타입스크립트는 앱과 웹을 구현하는 자바스크립트와 동일한 용도로 사용 가능하며 서버 단에서 개발이 이루어지는 복잡한 대형 프로젝트에서도 빛을 발합니다.

3. 강력한 생태계

타입스크립트는 그리 오래되지 않은 언어임에도 불구하고 강력한 생태계를 가지고 있습니다. 대부분의 라이브러리들이 타입스크립트를 지원하며 마이크로소프트의 비주얼 스튜디오 코드(VSCode)를 비롯해 각종 에디터가 타입스크립트 관련 기능과 플러그인을 지원합니다.

4. 점진적 전환 가능

기존의 자바스크립트 프로젝트를 타입스크립트로 전환하는데 부담이 있다면 추가 기능이나 특정 기능에만 타입스크립트를 도입함으로써 프로젝트를 점진적으로 전환할 수 있습니다. 자바스크립트에 주석을 추가하는 것에서부터 시작해 시간이 지남에 따라 코드베이스가 완전이 바뀌도록 준비 기간을 가질 수 있습니다.

이러한 이유로 이번 프로젝트에서 타입스크립트를 도입하게 됐습니다.


4. axios 적용 및 api 분리

api를 전역에서 쓸 수 있도록 관리하기 위해서 따로 폴더를 만들어서 관리하게 되었습니다. axios에도 typescrpt를 적용하였으며 해당 형태는 다음과 같습니다.

  1. api/index.ts
import axios, { AxiosPromise } from "axios";

const api = {
  login: "/v1/users/signin",
};

export function login(params: {}): AxiosPromise<LoginResponse> {
  const promise = instance.post(api.login, null, {
    params,
  });
  errorPromiseCatch(promise);
  return promise;
}
  1. data/interface/index.ts
// ...
import {
  AccountType,
  // ...
} from "~/data/enums";

//...

export interface LoginParam {
  institutionNumber: string;
  accountId: string;
  accountPw: string;
  accountType: AccountType;
}

// ...
  1. pages/login-page.vue

<script lang="ts">
  //...
  import { mapActions, mapMutations } from 'vuex'
  import { LoginParam } from '~/data/interface'
  import { AccountType } from '~/data/enums'

  //...

  methods: {
    ...mapActions({
      LOGIN: ActionTypes.LOGIN,
    }),

    //...

    const loginParam: LoginParam = {
        institutionNumber: this.number,
        accountId: this.id,
        accountPw: this.password,
        accountType: AccountType.MAT_EMR,
      }

      await this.LOGIN(loginParam);
      await this.$router.push('/sms');
    },

    //...


</script>
  1. api 를 호출합니다.
  2. 호출한 api의 타입을 정의합니다
  3. vue 파일에 data를 각 메소드에 맡게 통신합니다.

axios 대부분은 상단의 형태와 같은 형태로 각 페이지마다 데이터 객체를 받아왔으며 통신으로 받아온 data는 가장 상단 page 폴더에 있는 부분에서 자식 components로 전달하는 형태로 제작하였습니다.


5. SVG를 vue component화 시키기

vue 내부에서는 svg 파일 자체를 vue componenet 화 시켜서 해당 이미지를 필요에 따라 색상, 모양, 크기 등을 props를 내려주어 상황에 맞게 이미지의 속성을 변경해 줄 수 있습니다. 서버내부에서도 용량을 적게 사용하며 재사용성을 높여서 jpg,png보다 높은 효율성 띄워 사용할수 있습니다.

<template>
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="245"
    height="44"
    viewBox="0 0 245 44"
    fill="#fff"
  >
    <path
      fill-rule="evenodd"
      clip-rule="evenodd"
      d="M33.4731 21.8016C33.4731 23.5545 32.5998 24.4343 30.8488 24.4343H24.3686V33.5671H21.5068C19.7594 33.5671 18.8824 32.6895 18.8824 30.9351V24.4576L18.8817 24.4583H9.77727V21.5873C9.77727 19.8336 10.6506 18.9546 12.4016 18.9546H18.8817V9.82105H21.7436C23.4917 9.82105 24.3679 10.6993 24.3679 12.4538V18.9313L24.3686 18.9305H33.4731V21.8016ZM21.6251 0H0V21.695C0 33.6763 9.68133 43.3885 21.6251 43.3885C33.5682 43.3885 43.2503 33.6763 43.2503 21.695C43.2503 9.71228 33.5682 0 21.6251 0V0Z"
      fill="#fff"
    />
    <path
      fill-rule="evenodd"
      clip-rule="evenodd"
      d="M74.3528 30.4261C74.5882 29.2468 74.7052 28.0288 74.7052 26.7701C74.7052 25.5127 74.5882 24.3035 74.3528 23.1439C74.1182 21.9843 73.7156 20.9624 73.1475 20.0775C72.5793 19.1934 71.8448 18.4762 70.9438 17.9251C70.0415 17.3748 68.9059 17.0993 67.5349 17.0993C66.1624 17.0993 65.0072 17.3748 64.067 17.9251C63.1261 18.4762 62.3712 19.2029 61.8038 20.1074C61.2349 21.0119 60.8237 22.044 60.5694 23.2022C60.3136 24.3626 60.1872 25.5718 60.1872 26.8291C60.1872 28.0084 60.3238 29.1877 60.5984 30.3671C60.8724 31.5464 61.3134 32.5989 61.9215 33.5216C62.5282 34.4459 63.2925 35.1937 64.2138 35.7629C65.1336 36.3329 66.2416 36.6179 67.5349 36.6179C68.9059 36.6179 70.0524 36.3431 70.9729 35.7921C71.8935 35.2418 72.628 34.5049 73.1773 33.5807C73.7258 32.6579 74.1182 31.6054 74.3528 30.4261ZM78.8403 0C81.004 0 82.7586 1.75951 82.7586 3.93083V25.8037C82.7586 34.0218 78.3121 38.6603 75.9849 40.2478C73.6396 41.8477 70.6317 42.8681 66.4889 42.8681C63.789 42.8681 61.2061 42.4257 59.482 41.5416C57.7571 40.6567 56.3375 39.4584 55.2207 37.9438C54.104 36.4314 53.2612 34.6923 52.6931 32.7258C52.1249 30.7608 51.8408 28.7163 51.8408 26.5938C51.8408 24.55 52.1249 22.5748 52.6931 20.6673C53.2612 18.7613 54.104 17.0805 55.2207 15.6256C56.3375 14.1723 57.7383 13.0017 59.4239 12.1168C61.108 11.2327 63.0675 10.7903 65.3017 10.7903C67.1028 10.7903 68.8175 11.1737 70.4442 11.9404C72.0695 12.7065 73.3541 13.837 74.2942 15.3312H74.4119V0H78.8403ZM108.444 30.4562C108.679 29.2973 108.796 28.1078 108.796 26.8883C108.796 25.6711 108.679 24.4714 108.444 23.2913C108.209 22.1128 107.807 21.0712 107.239 20.1667C106.67 19.2628 105.907 18.526 104.947 17.9552C103.986 17.386 102.801 17.1003 101.391 17.1003C99.9804 17.1003 98.8048 17.386 97.8639 17.9552C96.9238 18.526 96.1682 19.2628 95.6007 20.1667C95.0326 21.0712 94.6315 22.1128 94.3961 23.2913C94.1607 24.4714 94.0437 25.6711 94.0437 26.8883C94.0437 28.1078 94.1607 29.2973 94.3961 30.4562C94.6315 31.6158 95.0326 32.6581 95.6007 33.5816C96.1682 34.5051 96.9238 35.242 97.8639 35.7923C98.8048 36.3433 99.9804 36.6181 101.391 36.6181C102.801 36.6181 103.986 36.3433 104.947 35.7923C105.907 35.242 106.67 34.5051 107.239 33.5816C107.807 32.6581 108.209 31.6158 108.444 30.4562ZM112.852 15.1841C114.224 16.5799 115.282 18.2701 116.026 20.2549C116.77 22.241 117.143 24.4517 117.143 26.8884C117.143 29.3264 116.77 31.5277 116.026 33.4927C115.282 35.4592 114.224 37.1393 112.852 38.5343C111.48 39.9309 109.825 41.0016 107.885 41.748C105.946 42.4943 103.78 42.8682 101.391 42.8682C99.0004 42.8682 96.8447 42.4943 94.9252 41.748C93.0049 41.0016 91.3585 39.9309 89.9882 38.5343C88.6158 37.1393 87.5579 35.4592 86.8139 33.4927C86.0692 31.5277 85.6972 29.3264 85.6972 26.8884C85.6972 24.4517 86.0692 22.241 86.8139 20.2549C87.5579 18.2701 88.6158 16.5799 89.9882 15.1841C91.3585 13.789 93.0049 12.7073 94.9252 11.9406C96.8447 11.1738 99.0004 10.7911 101.391 10.7911C103.78 10.7911 105.946 11.1738 107.885 11.9406C109.825 12.7073 111.48 13.789 112.852 15.1841ZM140.131 32.69C140.684 31.3248 141.999 30.4261 143.467 30.4261H148.778C148.229 34.4757 146.661 37.5611 144.075 39.6836C141.489 41.8068 138.178 42.868 134.142 42.868C131.869 42.868 129.782 42.4846 127.883 41.7179C125.98 40.9518 124.365 39.8811 123.033 38.5042C121.7 37.1289 120.661 35.4882 119.917 33.5807C119.173 31.6747 118.801 29.5813 118.801 27.3007C118.801 24.9428 119.143 22.751 119.829 20.7262C120.514 18.7021 121.524 16.9528 122.856 15.4783C124.188 14.0045 125.815 12.8544 127.734 12.0285C129.655 11.2035 131.85 10.7902 134.318 10.7902C136.12 10.7902 137.854 11.0263 139.52 11.4979C141.185 11.9695 142.674 12.6874 143.987 13.6503C145.3 14.6139 146.368 15.8129 147.191 17.2473C148.014 18.6825 148.484 20.3829 148.601 22.348H140.431C139.882 18.8494 137.824 17.0993 134.26 17.0993C132.926 17.0993 131.81 17.4047 130.909 18.0133C130.007 18.6241 129.272 19.4091 128.704 20.3727C128.136 21.3363 127.734 22.3975 127.5 23.5565C127.264 24.7168 127.147 25.8663 127.147 27.0062C127.147 28.1068 127.264 29.2278 127.5 30.3671C127.734 31.5078 128.117 32.5391 128.645 33.4633C129.175 34.3876 129.889 35.1434 130.792 35.7331C131.693 36.3227 132.79 36.6179 134.083 36.6179C136.082 36.6179 137.619 36.0574 138.697 34.9371C139.301 34.3088 139.78 33.5595 140.131 32.69ZM162.56 17.2186H162.03V32.3136C162.03 33.7291 162.267 34.673 162.736 35.1439C163.207 35.6162 164.147 35.8516 165.557 35.8516C166.028 35.8516 166.478 35.8327 166.909 35.7926C167.341 35.7547 167.752 35.6949 168.144 35.6162V42.1024C167.439 42.2205 166.654 42.2985 165.793 42.3379C164.932 42.3772 164.088 42.3969 163.266 42.3969C161.972 42.3969 160.747 42.3087 159.591 42.1316C158.435 41.9552 157.417 41.6112 156.535 41.1002C155.653 40.5893 154.957 39.8619 154.448 38.918C153.939 37.9748 153.684 36.7365 153.684 35.2036V2.4771H162.03V11.6165H168.144C168.144 14.7106 165.644 17.2186 162.56 17.2186ZM189.233 30.3087V27.1833C188.881 27.4989 188.44 27.7431 187.912 27.9202C187.381 28.0973 186.814 28.2446 186.207 28.3626C185.598 28.4807 184.962 28.5791 184.297 28.6571C183.629 28.7366 182.963 28.835 182.298 28.9523C181.671 29.0704 181.053 29.2278 180.446 29.4239C179.838 29.6214 179.309 29.8867 178.86 30.2205C178.409 30.5544 178.046 30.9771 177.772 31.4881C177.497 31.999 177.36 32.6477 177.36 33.4342C177.36 34.1813 177.497 34.8103 177.772 35.3205C178.046 35.8322 178.418 36.2345 178.889 36.5297C179.358 36.8242 179.907 37.0304 180.534 37.1485C181.161 37.2666 181.807 37.3256 182.474 37.3256C184.12 37.3256 185.393 37.0508 186.295 36.5005C187.195 35.9502 187.861 35.2913 188.293 34.5246C188.724 33.7578 188.989 32.9823 189.087 32.1951C189.184 31.4101 189.233 30.7803 189.233 30.3087ZM197.58 35.3207C197.58 36.6976 197.658 38.0139 197.815 39.272C197.971 40.53 198.245 41.4739 198.638 42.1022H191.588C190.732 42.1022 189.962 41.5154 189.795 40.6721C189.793 40.667 189.793 40.6627 189.792 40.6576C189.694 40.1663 189.625 39.6648 189.586 39.1539C188.253 40.53 186.686 41.4929 184.884 42.0432C183.081 42.5935 181.239 42.8682 179.358 42.8682C177.908 42.8682 176.557 42.6919 175.302 42.3376C174.048 41.9841 172.952 41.4338 172.011 40.6867C171.071 39.9403 170.336 38.9972 169.807 37.8565C169.278 36.7165 169.013 35.3608 169.013 33.7879C169.013 32.0583 169.316 30.6333 169.925 29.513C170.532 28.3928 171.316 27.4992 172.275 26.83C173.235 26.1617 174.333 25.6609 175.567 25.3264C176.802 24.9925 178.046 24.7272 179.3 24.5304C180.554 24.3336 181.787 24.1762 183.003 24.0581C184.217 23.9401 185.295 23.7637 186.235 23.5275C187.176 23.2921 187.92 22.9481 188.469 22.4961C189.017 22.0442 189.272 21.3853 189.233 20.5202C189.233 19.6171 189.086 18.8991 188.793 18.3685C188.498 17.8372 188.106 17.4246 187.617 17.1294C187.127 16.835 186.559 16.6389 185.912 16.5398C185.266 16.4428 184.569 16.3925 183.825 16.3925C182.18 16.3925 180.887 16.7468 179.946 17.4538C179.006 18.1615 178.456 19.3408 178.3 20.9917H169.954C170.072 19.0267 170.562 17.3955 171.423 16.0981C172.285 14.8007 173.383 13.7591 174.716 12.9727C176.047 12.1869 177.546 11.6264 179.212 11.2926C180.877 10.9588 182.552 10.7911 184.237 10.7911C185.766 10.7911 187.312 10.8997 188.881 11.1155C190.447 11.332 191.879 11.7547 193.172 12.383C194.464 13.0127 195.522 13.8867 196.345 15.0069C197.169 16.1272 197.58 17.6119 197.58 19.4589V35.3207ZM206.386 0C208.551 0 210.305 1.76024 210.305 3.93083V42.1013H201.958V0H206.386ZM232.748 23.5784L245 42.1021H238.304C236.184 42.1021 234.215 40.9971 233.103 39.1844L226.544 28.4808L223.312 31.6063V42.1021H214.965V0H219.393C221.557 0 223.312 1.76024 223.312 3.93083V22.5842L231.224 14.3355C232.891 12.5986 235.191 11.6161 237.595 11.6161H243.708L232.896 22.1841C232.52 22.5529 232.457 23.1382 232.748 23.5784Z"
      fill="#fff"
    />
  </svg>
</template>
<script>
export default {
  name: 'NavLogo',
  props: {
    opacity: {
      type: String,
    },
  },
}
</script>

위 형태와 같이 컴포넌트를 제작하여 평소에 쓰던 vue component 형태로 svg 파일을 사용하시면 됩니다.


6. vue 컴포넌트에서 typescript 정리

typescript-file

data 폴더 안에 enums, types, interface를 각각 폴더로 만들어 axios로 받아오거나 보내는 data 들을 store에 저장하고 저장한 data타입들을 따로 모아서 정리해두었습니다.

vue 페이지에서는 typescript를 사용하기 위해 vue extends 문법을 사용하였으며 data 안에서 따로 타입정의를 해주었습니다.

data-type

대부분의 data들은 가장 상위 페이지에서 computed를 거쳐 자식 컴포넌트로 전달되는데 당연하게도 computed는 vue 메소드중 하나이기 떄문에 computed안에서 함수를 사용할때에도 아래와 같이 함수에 타입을 지정해주면 됩니다.

computed-type

methods에서 aixos로 받아온 data를 불러올땐 api들을 모듈화 시켯기 떄문에 사용하고 싶은 page 에서 사용하고 싶은 타입의 method 들을 호출, 호출한 해당 method에 3번에서 지정해둔 타입을 가져와 methods의 타입을 지정해주면서 사용합니다. 저희는 vuex의 mapActions, mapMutations 등을 사용했기 때문에 아래와 같이 method 를 불러오면서 사용하였습니다.

axios-map-data

api는 async, await로 가져오고 값은 아래와 같이 sotre에 저장시켜주는데 actions로 가져왓기 때문에 마찬가지로 정의해준 타입정의를 가져와 적용시켜줍니다.

api-async



읽어주셔서 감사합니다 🙇‍♀️