프로젝트 셋팅

대표적인 IT 지식 관련 뉴스 그룹 서비스인 Hacker news API를 연동한 Vue.js 프로젝트를 만들어 보자.

해커 뉴스

해커뉴스는 유명투자가이자 ‘해커와 화가’(한빛미디어 출판, 임백준 번역)의 저자, 그리고 스타트업 기업 대상 인큐베이터 플랫폼인 ‘Ycombinator’의 창시자 폴 그레이엄이 ‘Ycombinator’ 웹사이트를 통해 제공하는 IT 뉴스 서비스입니다. 유통되는 IT 뉴스의 성격은 클리앙과 매우 흡사하지만, 조금 더 전문적인 영역까지 다루고 있습니다.

해커뉴스 내의 각 뉴스들은 독자들에게 획득한 ‘좋아요’수와 게시된 날짜를 고려하여 최상단부터 노출순서가 정해집니다. 기존에는 URL을 통해 데이터를 긁어오는 방법이 한참 유행했었는데, 개발자 간의 더욱 활발한 지식 공유를 위하여 요즘에는 Firebase 와 함께 협업해서 API까지 제공해 주고 있습니다.

해커뉴스 - https://news.ycombinator.com/
해커뉴스 - API - https://github.com/HackerNews/API

Vue CLI 2 vs 3

  • 명령어
    • 2.x : vue init
    • 3.x : vue create
  • 웹팩 설정 파일
    • 2.x : 노출 0
    • 3.x : 노출 X
  • 프로젝트 구성
    • 2.x : 깃헙의 템플릿 다운로드
    • 3.x : 플러그인 기반으로 기능 추가
  • ES6 이해도
    • 2.x : 필요 X
    • 3.x : 필요 0

Vue 3.x 버전 CLI

vue create hacker-news

npm run serve

ESLint Error

image.png

vue.config.js

module.exports = {
  lintOnSave: false
};

라우터

npm i vue-router --save

src/router/index.js

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const router = new VueRouter({
  routes: [
    {
      path: "", // url 주소
      component: "" // component url 주소로 갔을 때 표시될 컴포넌트
    },
    {
      path: "",
      component: ""
    },
    {
      path: "",
      component: ""
    }
  ]
});
  • src/view
    • NewsView.vue
    • AskView.vue
    • JobsView.vue

라우터를 연결할 파일을 view 폴더에 생성해주고 path와 component에 불러와 각각 연결해준다.

{
   path: "/news",
   component: NewsView
},

마지막으로 App.vue 에서 라우터를 보여주면 된다.

App.vue

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

vu + tap 템플릿 코드 자동완성

API 연동

Hacker news API를 연동시키려고 한다.
API 연동을 담당하는 폴더와 파일을 생성해주자.

src/api/index.js

import axios from "axios";

const config = {
  baseUrl: "https://api.hnpwa.com/v0/"
};

function fetchNewsList() {
  return axios.get(`${config.baseUrl}news/1.json`);
}

function fetchJobsList() {
  return axios.get(`${config.baseUrl}jobs/1.json`);
}

function fetchAskList() {
  return axios.get(`${config.baseUrl}ask/1.json`);
}

export { fetchNewsList, fetchJobsList, fetchAskList };

axios를 사용해서 호출한 API를 해당하는 View 파일에 불러와 사용한다.

src/views/NewsView.vue

<template>
  <div>
    <div v-for="list in news">{{ list.title }}</div>
  </div>
</template>

<script>
import { fetchNewsList } from "../api/index";

export default {
  data() {
    return {
      news: []
    };
  },
  created() {
    fetchNewsList()
      .then(response => (this.news = response.data))
      .catch(error => console.log(error));
  }
};
</script>

뉴스 리스트에 해당하는 타이틀을 데이터를 가져온 뒤 v-for을 사용해 나열시키는데 성공했다.

image.png

Vuex 스토어 적용

Vuex는 Vue.js 애플리케이션에 대한 상태 관리 패턴 + 라이브러리 입니다. 애플리케이션의 모든 컴포넌트에 대한 중앙 집중식 저장소 역할을 하며 예측 가능한 방식으로 상태를 변경할 수 있습니다. 또한 Vue의 공식 devtools 확장 프로그램과 통합되어 설정 시간이 필요 없는 디버깅 및 상태 스냅 샷 내보내기/가져오기와 같은 고급 기능을 제공합니다.

  • 상태 는 앱을 작동하는 원본 소스 입니다.
  • 뷰 는 상태의 선언적 매핑입니다.
  • 액션 은 뷰 에서 사용자 입력에 대해 반응적으로 상태를 바꾸는 방법입니다

언제 사용해야 하나요?

Vuex는 공유된 상태 관리를 처리하는 데 유용하지만, 개념에 대한 이해와 시작하는 비용도 함께 듭니다. 그것은 단기간과 장기간 생산성 간의 기회비용이 있습니다.
대규모 SPA를 구축하지 않고 Vuex로 바로 뛰어 들었다면, 시간이 오래 걸리고 힘든일일 것입니다. 이것은 일반 적인 일입니다. 앱이 단순하다면 Vuex없이는 괜찮을 것입니다. 간단한 글로벌 이벤트 버스만 있으면됩니다. 그러나 중대형 규모의 SPA를 구축하는 경우 Vue컴포넌트 외부의 상태를 보다 잘 처리할 수 있는 방법을 생각하게 될 가능성이 있으며 Vuex는 자연스럽게 선택할 수 있는 단계가 될 것입니다. Redux의 저자인 Dan Abramov의 좋은 인용이 있습니다.

Hacker News 프로젝트를 만들면서는 Vuex를 사용하지 않아도 구현할 수 있는 부분이지만,
컴포넌트간에 상태를 공유하고 복잡한 로직을 갖는 큰 프로젝트에 대비하기 위해 Vuex를 적용시켜 공부해보려고 한다.

image.png

이전 api 호출방식에 vuex를 적용시켜보자

image.png

이전 방식에서는 api를 바로 뷰에 가져왔지만 vuex를 적용해서 api를 vuex에서 관리하게 된다.

npm i vuex

설치가 완료되면 스토어를 따로 관리해주는 폴더인 scr/store/index.js 파일을 만들어준다.

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {
    news: []
  }
});

main.js

new Vue({
  render: h => h(App),
  router,
  store
}).$mount("#app");

메인파일에 불러와 적용 후 크롬 Vue 개발자 도구에서 확인해 보면 잘 적용이 됐다.

image.png

이제 View 파일에서 호출했던 API를 store에서 호출하고 가져오는 방식으로 바꿔본다.

store/index.js

actions: {
    FETCH_NEWS() {
      fetchNewsList()
        .then(response => {
          console.log(response);
        })
        .catch(error => console.log(error));
    }
  }

NewsView.vue

 created() {
    this.$store.dispatch('FETCH_NEWS')
   }

Mutations

actions은 백엔드 API 호출을 담당하며, 호출한 API를 state에 전달하기 위해서는 mutations에 commit을 사용해 전달한다.

mutations: {
    SET_NEWS(state, news) {
      state.news = news;
    }
  },
  actions: {
    FETCH_NEWS(context) {
      fetchNewsList()
        .then(response => {
          context.commit("SET_NEWS", response.data); // commit 사용
        })
        .catch(error => console.log(error));
    }
  }

response.data를 state.news에 넣어주었다.

image.png

마지막으로 NewsView.vue에서 store의 news 데이터를 사용해주면 Vuex를 사용해 호출한 API 사용할 수 있다.

    <div v-for="list in this.$store.state.news">{{ list.title }}</div>

Vuex Helper

조금 더 깔끔하게 사용하는 방식이 있는데 바로 핼퍼를 사용하는 것이다.

Helpers는 컴포넌트의 computed나 methods 속성에 Vuex 저장소(Store) 내용을 바인딩하여 좀 더 직관적으로 사용할 수 있게 도와준다.
그리고 마크업 관점에서 코드가 길어지는걸 대비할 수 있다.
Vuex에서 사용하는 상태(State), 게터(Getters), 변이(Mutations), 액션(Actions)을 맵핑할 수 있다.

  • mapState - state를 연결해주는 함수
  • mapGetters - getters를 연결해주는 함수
  • mapMutations - mutations를 연결해주는 함수
  • mapActions - actions를 연결해주는 함수

mapState

this.$store.state.news을 좀 더 간편하게 가져오기 위해 vuex에서 제공하는 mapState를 사용한다.

<template>
  <div>
    <div v-for="item in ask">{{ item.title }}</div>
  </div>
</template>

<script>
import { mapState } from "vuex";

export default {
  computed: {
    ...mapState({
      ask: state => state.ask    // 상태를 ask에 담아줌
    })
  },
  created() {
    this.$store.dispatch("FETCH_ASK");
  }
};
</script>

mapGetters

getters는 store에서의 computed이다.
상태를 리턴하는 함수를 만들어서 ask를 반환한다.

store/index.js

getters: {
    fetchedAsk(state) {
      return state.ask;
    }
  },

ask뷰 파일에 내장 함수인 mapGetters를 불러와 사용하면 좀 더 간결한 코드를 작성할 수 있다.

AskView.vue

import { mapState, mapGetters } from "vuex";

computed: {
    ...mapGetters({
      fetchedAsk: "fetchedAsk"
    })
  },

참조

장기효님의 Vue.js 인프런 강좌