VueJS | Stock-Trader Project Tutorial (3)

이 포스팅은 Max의 Vuejs 강좌 내용을 정리한 것이고, VueJS의 개념을 익히고 연습해보기 위한 튜토리얼을 다룬 글 입니다.


이전 포스팅에서는 vuex를 활용한 프로젝트의 상태 관리 환경 세팅과 vuex의 기본적인 사용방법에 대해서 알아 보았습니다. 이번 포스팅에서는 또 다른 컴포넌트의 상태 관리 설정을 추가하고 컴포넌트 간의 상태 변화에 따라 뷰가 갱신될 수 있도록 구현해보겠습니다.

1. Portfolio 컴포넌트 생성하기

  • 기본적인 Portfolio 컴포넌트 구성은 이전 포스팅에서 Stocks 컴포넌트를 구성한 방법과 유사하므로 코드만 첨부하고 넘어가겠습니다.
    • 하나만 설명드리자면 16라인에 v-model.number을 디렉티브를 사용한 부분이 있는데요, v-model을 그냥 사용하지 않고 뒷부분에 .number를 붙인 이유는 인풋요소에서 입력한 값에 따라 버튼에 속성을 조절하려고 했는데 인풋요소에서 넘어오는 값이 문자열로 넘어와서 유효성 체크가 되지 않아 숫자 형태로만 넘어가도록 해준것 입니다.
    • 저도 v-model.number 이렇게 타입 지정까지 해주는 부분이 있는 줄 몰랐었는데 이부분의 오류를 해결하려다 보니 찾게 되었네요.
src/components/portfolio/Stock.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<template>
<div class="col-md-4 col-sm-6">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">
{{ propStock.name }}
<small>(Price: {{ propStock.price }} | Quantity: {{propStock.quantity }})</small>
</h3>
</div>
<div class="panel-body">
<div class="pull-left">
<input
type="number"
class="form-control"
placeholder="Quantity"
v-model.number="quantity"
>
</div>
<div class="pull-right">
<button
class="btn btn-success"
@click="sellStock"
:disabled="quantity <= 0 || !Number.isInteger(quantity)"
>Sell</button>
</div>
</div>
</div>
</div>
</template>

<script>
export default {
props: ['propStock'],

data() {
return {
quantity: 0
}
},
methods: {
sellStock() {
const order = {
stockId: this.propStock.id,
stockPrice: this.propStock.price,
quantity: this.quantity
};
}
}
}
</script>
src/components/portfolio/Portfolio.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<app-stock v-for="stock in stocks" :propStock="stock"></app-stock>
</div>
</template>

<script>
import Stock from './Stock.vue';
export default {
data() {
return {
// 이 단계에서는 임시적으로 data에 stocks를 추가하여 사용하고 portfolio store module 설정 후 수정할것 입니다.
stocks: []
}
},
components: {
appStock: Stock
}
}
</script>

2. Portfolio store module 생성하기

src/store/modules/portfolio.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const state = {
funds: 10000,
stocks: []
};

const mutations = {
'BUY_STOCK'(state, {stockId, quantity, stockPrice}) {
const record = state.stocks.find(element => element.id === stockId);
if(record) {
record.quantity += quantity;
} else {
state.stocks.push({
id: stockId,
quantity: quantity
});
}
state.funds -= stockPrice * quantity;
},
'SELL_STOCK'(state, {stockId, quantity, stockPrice}) {
const record = state.stocks.find(element => element.id === stockId);
if(record.quantity > quantity) {
record.quantity -= quantity;
} else {
state.stocks.splice(state.stocks.indexOf(record), 1);
}
state.funds += stockPrice * quantity;
}
};

const actions = {
sellStock({commit}, order) {
commit('SELL_STOCK', order);
}
};

const getters = {
stockPortfolio(state, getters) {
return state.stocks.map(stock => {
const record = getters.stocks.find(element => element.id === stock.id);
return {
id: stock.id,
quantity: stock.quantity,
name: record.name,
price: record.price
}
});
},
funds(state) {
return state.funds;
}
};

export default {
state,
mutations,
actions,
getters
}

이번 글에서는 store 내의 각 요소에 대해서 조금 설명을 해보려고 합니다.

1. state - funds, stocks

  • state에는 해당 컴포넌트 내/외부에서 사용할 데이타들을 미리 정의 해놓고 어디에서나 접근해서 사용 할 수 있게 해줍니다.
  • funds는 stock을 구매 할 수 있는 전체 자산의 상태이고, stocks는 Stocks컴포넌트에서 구매한 stock의 상태입니다.

2. mutations - BUY_STOCK, SELL_STOCK

  • muntation은 vuex에서 state를 변경 할 수 있는 유일한 방법입니다. 주로 action에서 commit함수를 통해 mutation을 호출하고 필요한 변수를 전달하는 방식으로 많이 사용합니다.
  • 이전 포스팅을 다시 확인해보시면 BUY_STOCK을 호출하는 action은 stocks 모듈에 선언되어 있습니다. 해당 action을 사용하는 위치가 Stocks컴포넌트에 있기 때문에 그렇지요, 하지만 portfolio 모듈 안에 선언되어 있는 fundsstacks state를 수정하기 위해서는 portfolio 내부의 mutation으로만 변경 할 수 있기 때문에 변경 할 state에 접근 가능한 mutation 사용해야 합니다.

3. actions - sellStock

  • Portfolio 컴포넌트 내에서 state를 변경하는 기능을 하는 부분은 stocks 되파는 부분뿐 입니다. stocks를 되팔때 sellStock action에서는 클라이언트에서 팔고자하는 입력받은 수량만 SELL_STOCK mutation에 전달하고 전달 받은 데이타를 가지고 state를 변경하는 것은 mutation에서 실행하는 것입니다.

4. getters - stockPortfolio, funds

  • getter를 사용하는 목적은 프로젝트 어디서나 state의 상태를 좀더 쉽게 조회하기 위함입니다.
  • stockPortfolio getter에서는 파라메터로 getters 라는 것을 받고 있는데 이 파라메터를 사용하면 store에 등록되어 있는 모든 getter를 담고 있는 객체를 참조 할 수 있습니다.
src/store/store.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from 'vue';
import Vuex from 'vuex';

import stocks from './modules/stocks';
import portfolio from './modules/portfolio';

Vue.use(Vuex);

export default new Vuex.Store({
modules: {
stocks,
portfolio
}
});

5. store에 추가하기

  • 모듈 생성이 완료 되었으면 store에 등록해줍니다. 이제 프로젝트 어디서나 등록한 portfolio 모듈을 사용 할 수 있습니다.

3. Portfolio 컴포넌트에 vuex 연결하기

src/components/portfolio/Stock.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...생략
<script>
// mapActions 헬퍼를 사용하기 위해 vuex를 임포트해줍니다.
import { mapActions } from 'vuex';

export default {
props: ['propStock'],

data() {
return {
quantity: 0
}
},
methods: {
// mapActions 헬퍼를 사용하여 해당 컴포넌트의 속성에 맵핑해줍니다.
...mapActions({
placeSellOrder: 'sellStock'
}),
sellStock() {
const order = {
stockId: this.propStock.id,
stockPrice: this.propStock.price,
quantity: this.quantity
};
// sellStock action을 placeSellOrder라는 이름의 메소드로 사용합니다.
this.placeSellOrder(order);
this.quantity = 0;
}
}
}
</script>
  • 이전 포스팅에서는 store를 사용할 때 this.$store.{...} 방법으로 일일이 찾아와서 사용을 했었는데 이번에는 헬퍼함수를 통해 설정해보았습니다.
  • mapActions 헬퍼를 사용하여 store에 등록되어 있는 action중에 필요한 action만 불러와서 사용합니다.
  • 컴포넌트 내에서는 mutation을 건드릴 일은 없습니다. 모든 동작은 action을 통해서 실행됩니다.

    • ...mapActions과 같이 spread operator를 사용하기 위해서 babel모듈도 설치해줘야 합니다.
      npm install --save-dev babel-preset-stage-2
    • 설치한 다음 .babelrc 파일에 다음과 같이 설정을 변경해 줍니다. .babelrc 파일은 프로젝트 제일 상단인 src 폴더와 같은 위치에 있습니다.
1
2
3
4
5
6
{
"presets": [
["es2015", {"modules": false}],
["stage-2"]
]
}
src/components/portfolio/Portfolio.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...생략
<script>
// mapGetters 헬퍼를 사용하기 위해 vuex를 임포트해줍니다.
import { mapGetters } from 'vuex';
import Stock from './Stock.vue';

export default {
// 위 예제에서 사용했던 data부분을 삭제하고 mapGetters 헬퍼를 사용하여 필요한 state를 맵핑해줍니다.
computed: {
...mapGetters({
stocks: 'stockPortfolio'
}),
},
components: {
appStock: Stock
}
}
</script>
  • mapGetters 헬퍼를 사용하여 Portfolio 컴포넌트에서 필요한 데이타를 조합해둔 stockPortfolio getter 를 stocks로 참조하여 사용합니다.

4. Stocks 컴포넌트 수정하기

  • 이전 포스팅에서 만들었던 Stocks 컴포넌트의 Stock.vue에서 buyStock 메소드의 결과를 console.log로 확인하였었습니다. portfolio 모듈에 BUY_STOCK mutation을 생성하여서 이를 사용 할 수 있도록 수정해줍니다.
  • 먼저 src/store/modules/stocks.js 에서 buyStock action의 commit(); 부분을 commit('BUY_STOCK', order); 로 수정합니다.
  • 그리고 src/components/stocks/Stock.vue 에서 buyStock method의 console.log(order); 부분을 this.$store.dispatch('buyStock', order); 로 수정합니다.

모두 완료되었다면 아래 영상과 같이 Stock 컴포넌트와 Portfolio 컴포넌트 간에 State의 변화를 확인 할 수 있을것입니다.
git clone 으로 프로젝트를 시작하셨다면 git checkout step05 로 이번 포스팅의 마지막 결과물을 확인 하실 수 있습니다.
다음 포스팅은 마지막으로 몇가지 기능을 추가하고 vue-resourcefirebase를 활용해서 DB에 데이타를 저장하고 로드하는 기능까지 구현해보려고 합니다.
필요하신 부분이나 보완할 점이 있다면 댓글로 의견 남겨주시기 바랍니다. 감사합니다.


- 끝 -

Share Comments