설치

npm i vuex

 

store 폴더에 관리하기 위해 store폴더, 파일 생성

store 폴더

 

store/store.js

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
  	//...
  },
  getter: {
  	//...
  },
  mutations: {
  	//...  
  },
  action: {
  	//...
  }
})

이런 형태로 작업하면 된다.

- state: 여러 컴포넌트에 공유되는 데이터

- getter: 연산된 state값을 접근하는 속성

- mutations: state값을 변경하는 이벤트 로직이나 메서드

- actions: 비동기 처리 로직을 선언하는 메서드

 

그리고 store 파일을 등록해줘야한다.

 

 

main.js

main.js

 

state

// vue파일 안에서 작업할 경우
// App.vue
data: {
    message: 'hello',
}
<p>{{ message }}</p>    

// store를 통해 작업할 경우
// store.js
state: {
    message: 'hello',
}

// App.vue
<p>{{ this.$store.state.message }}</p>
// App.vue
data: {
    message: 'hello',
}
<p>{{ message }}</p>    

// store.js
state: {
    message: 'hello',
}

// App.vue
<p>{{ this.$store.state.message }}</p>

 

 

getters

state에 접근하는 속성

computed()처럼 미리 연산된 값을 접근하는 속성

// store.js
getters: {
    getNumber(state){
        return state.num;
    },
    doubleNumber(state){
        return state.num * 2;
    }
}

// App.vue
<p>{{ this.$store.getters.getNumber }}</p>
<p>{{ this.$store.getters.doubleNumber }}</p>

 

 

mutations

state 값을 변경할 수 있는 유일한 방법,

mutations을 통해서 작업해야 devtools에서 추적이 가능.

devtools를 통해 유일한 추적

// store.js
mutations: {
    printNumbers(state) {
        return state.num
    },
    sumNumbers(state, anotherNum) {
        return state.num + anotherNum;
    }
},

// App.vue
this.$store.commit('printNumbers');
this.$store.commit('sumNumbers', 20);

// state를 변경하기 위해 mutations를 동작시킬 때 인자(payload)를 전달할 수 있음
// store.js
mutations: {
    modifyState(state, payload) {
        console.log(payload.str);
        return state.storeNum += payload.num;
    }
}

// App.vue
this.$store.commit('modifyState', {
    str: 'passed from payload',
    num: 20
});

 

 

actions

비동기 처리 로직을 선언하는 메서드. 비동기 로직을 담당하는 mutations

데이터 요청, promise, es6 async과 같은 비동기 처리는 모두 actions에 선언

// store.js
mutation: {
    doubleNumber(state) {
        state.num * 2;
    }
},
actions: {
    delayDoubleNumber(context) { 
                // context로 store의 메서드와 속성 접근
        context.commit('doubleNumber');
    }
}

// App.vue
// dispatch로 actions 호출
this.$store.dispatch('delayDoubleNumber');

actions에서 mutation에 접근하기 위한 경로로 첫번째 인자 context가 존재한다.

 

비동기 예제1

// store.js
mutations: {
    addCounter(state) {
        state.counter++;
    }
},
actions: {
    delayedAddCounter(context) {
        setTimeout(() => context.commit('addCounter'), 2000);
    }
}

// App.vue
methods: {
    incrementCounter(){
        this.$store.dispatch('delayedAddCounter');
    }
}

 

비동기 예제2

// store.js
mutations: {
    setData(state, fetchedData){
        state.product = fetchedData;
    }
},
actions: {
    fetchProductData(context) {
        return axios.get('https://domain.com/products/1')
.then(res => context.commit('setData', res));
    }
}

// App.vue
methods: {
    getProduct() {
        this.$store.dispatch('fetchProductData');
    }
}

자바스크립트 전역 스코프에 정의된 것은 코드 내의 어디서든지 접근이 가능하다는 것이 문제가 되고는 합니다.

이때 외부에 공유되면 안되거나 충돌날 위험이 있는 경우 즉시 실행 함수를 통해 해결하고는 합니다.

 

즉시 실행 함수(IIFE)

단순하게 함수와의 차이를 보자면 즉시 실행되는지 아닌지의 차이입니다.

함수표현식은 함수를 정의하고, 변수에 함수를 저장하고 실행하는 일련의 과정이 있습니다.

하지만 즉시실행함수는 이러한 과정없이 즉시 실행됩니다.

문법의 차이는 단순히 함수를 괄호 "()"로 랩핑한다는 것입니다.

 

형태

(function() {
    console.log('즉시 실행 함수'); 
}());

var now = (function () { 
    console.log('즉시 실행 함수')    
})();

 

익명, 기명 즉시 실행 함수

(function () { 
	console.log('익명 즉시 실행 함수'); 
}());

(function now() { 
	console.log('기명 즉시 실행 함수'); 
}());

 

사용이유

자바스크립트의 모듈 패턴에서 사용할 수 있습니다.

전역 스코프에 정의하는 것이 아닌 즉시실행함수의 스코프에서 실행컨텍스트가 활성화됩니다.

이렇게되면 유효범위가 다른 곳에서 접근하려고 할때 은닉해주는 성질을 지닐 수 있습니다. (캡슐화)

물론 아예 접근이 안되는 것은 아닙니다. 접근하는 방법은 있습니다.

 

즉..

즉시실행함수로 일반적인 유효범위를 설정하는 언어에서와 같이 private와 public등의 캡슐화를 사용할 수 있습니다.

캡슐화를 해야하는 이유는 무엇일까요? 당연히 충돌을 방지하기 위해서입니다.

즉시실행함수는 플러그인이나 라이브러리에서 많이 사용됩니다.

 

예시

jQuery 라이브러리에서 $라는 전역변수를 사용하고 있는데, 또 다른 라이브러리를 도입하게되었을 경우...

추가 된 라이브러리도 $라는 전역변수가 있으면 충돌이 생기게됩니다.

이때 

즉시 실행 함수를 사용하여 $ 전역 변수의 충돌을 피할 수 있습니다.

(fucntion($) {
 	//.....
})(jQuery); // jQuery 인자로 전달

 

 

 


참고 

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/function

redux-saga 캡쳐

React는 MVC패턴에서 V(View)를 담당하고 있습니다.

앱이 가벼운 경우에는 State로 데이터를 핸들링 할 수 있지만,

앱이 커질 경우에는 데이터 관리를 별도로 해야하는 요구사항이 생기게됩니다.

이때 사용할 수 있는 것이 Redux와 MobX 등, 상태 관리 라이브러리입니다.

 

redux-saga는 redux의 액션 생성자와 reducer의 순수성을 유지하고 사이드 이펙트를 처리하기 위해 사용합니다.

여기서 사이드 이펙트는 부작용을 발생시키는 어떠한 효과가 아니라,

데이터 요청이나 비동기 작업, 브라우저 캐시 같이 순수하지 않은 작업들을 의미합니다.

 

 

Redux-Saga란?

redux-saga는 리덕스의 미들웨어입니다.

리덕스가 액션을 수행하면 redux-saga에서 디스패치하여 redux의 액션을 가로챈 뒤, 액션의 역할을 수행 하고

다시 액션을 발행하여 데이터를 저장하거나 다른 이벤트를 수행시킵니다.

redux-saga 동작 이미지 출처 uzihoon.com/post/181be130-63a7-11ea-a51b-d348fee141c4

 

redux-saga와 비슷한 라이브러리로 redux-thunk가 있지만,

redux-saga는 redux-thunk에 비해 이펙트 함수를 통해 다양한 작업들을 처리할 수 있습니다.

redux-saga가 좀 더 러닝커브가 높습니다.

  • 비동기 작업 시 기존 요청 취소 처리 가능
  • 특정 액션 발생 시 다른 액션이 디스패치 되도록 할 수 있음
  • 웹 소켓 사용 시 channel 기능을 사용하여 효율적 코드 관리가 가능
  • API 요청이 실패 했을때 재 요청하는 작업을 할 수 있음

이 외에도 다양한 비동기 작업들을 처리 할 수 있습니다.

 

 

자주 사용하는 헬퍼 함수

redux-saga는 스토어에 지정된 액션들이 디스패치되었을 때,

task를 만들기 위해 내부 함수들을 감싸는 헬퍼 이펙트를 제공합니다. 

 

all

제너레이터 함수를 배열의 형태로 인자로 넣어주면 병렬로 동시에 실행됩니다.

이때 모든 함수에 대한 결과가 resolve될 때까지 블럭됩니다. 

 

put

특정 액션의 디스패치하도록 합니다.

결과를 스토어에 디스패치(put) 합니다.

 

call, apply

순수 객체만 리턴하는 함수입니다. 오브젝트 메소드 호출을 지원합니다.

첫번째 파라미터는 함수이며 나머지 파라미터는 해당 함수에 넣을 인수 값 입니다.

액션이 발생하면 전달한 함수를 호출하여 실행합니다.

API가 리턴될때까지 블럭되며, 비동기 함수 호출 시 용이합니다.

call과 apply는 두번째 인자 값의 차이만 있습니다.

 

delay

설정된 시간 이후에 resolve를 하는 Promise 객체를 리턴합니다.

제너레이터를 정지하는데 사용할 수 있습니다.

 

takeEvery

액션이 발생하게되면 task를 실행합니다.

task가 종료되기 전에 또 다른 액션이 발생할 경우, 또 하나의 새로운 task를 실행합니다.

 

takeLatest

액션이 발생하게되면 task를 실행합니다.

만약 실행 중인 task가 있다면 기존 task를 종료하고 새로운 task를 실행합니다.

실수로 여러번 클릭했을때를 방지하거나 마지막에 요청된 데이터를 보여줄 때 사용합니다.

 

takeLeading

액션이 발생하게되면 task를 실행합니다.

해당 task의 실행이 완료되기 전까지 뒤에 오는 이벤트들을 블럭합니다.

이후 task가 완료되면 액션에 대해 수신합니다.

 

throttle

초 이내 요청을 한 번만 보냅니다.

마지막 함수가 호출된 후 일정 시간이 지나기 전 재 호출하지 않습니다.

스크롤 이벤트 사용 시 용이합니다.

 

debounce

초 이내 요청을 한 번만 보냅니다.

처음 함수나 마지막 함수만 호출 후 일정시간이 지나기 전 재 호출하지 않습니다.

 

 

제너레이터함수

ES6에 포함된 제너레이터 함수는 function* 키워드로 작성합니다.

redux-saga는 이 제너레이터 함수를 적극 활용한 사례입니다.

제네레이터는 제네레이터함수의 반환이며 redux-saga가 작성한 함수를 호출하여 반환받는 객체가 제너레이터입니다.

*이터레이터 프로토콜과 이터러블 프로토콜을 준수합니다.

// 코드출처: https://mskims.github.io/redux-saga-in-korean/basics/DeclarativeEffects.html 
import { takeEvery } from "redux-saga/effects"
import Api from "./path/to/api"

function* watchFetchProducts() {
  yield takeEvery("PRODUCTS_REQUESTED", fetchProducts)
}

function* fetchProducts() {
  const products = yield Api.fetch("/products")
  console.log(products)
}

yield를 이용하여 이펙트들을 호출하고 수행된 내용을 다시 돌려 받아 그 액션을 수행합니다.

redux-saga는 제네레이터를 통해 이펙트를 수행하는 역할을 합니다.

 


참고, 출처

이터러블 (Iterable)

순회가능한 객체를 말합니다. 

순회 가능한 객체는 Symbol.iterator 심볼 속성을 가지고 있으며 이터레이터 객체를 반환하는 객체를 말합니다.

이러한 것을 이터러블 프로토콜이라고 하며 이터러블 객체라고 합니다.

 

이터레이터 (Iterator)

이터러블 메소드로 반환하는 객체입니다.

next 메소드를 구현하고 있으며 value, done을 반환하는 객체입니다.

반환되는 IteratorResult는 {done: boolean, value: any} 형태의 단순한 객체입니다.

next 메소드를 통해 모든 값을 돌고 나면 done이 true로 나오며 끝납니다.

한번 끝난 이터레이터는 다시 돌아가지 않으며 value가 undefined로 리턴합니다

이러한 것을 이터레이터 프로토콜이라고 합니다.

 

for of 루프는 순회 시작 전 [Symbol.iterator]() 메소드를 호출하여 이터레이터 객체를 얻습니다.

그 후에 순차적으로 next 메소드를 호출하며 하나씩 순회합니다.

보통 이터러블 프로토콜과 이터레이터 프로토콜을 하나의 객체에 모두 구현하는 것이 일반적입니다.

// 영원히 0을 반환하는 무한 이터레이터 예시
var zeroesForeverIterator = {
    [Symbol.iterator]: function () {
        return this;
    },
    next: function () {
        return {done: false, value: 0};
    }
};

 

사용자 이터레이터 객체 생성

이터러블 프로토콜은 obj[Symbol.iterator]: Function => Iterator로 표현할 수 있습니다.

그 후 이터레이터 프로토콜을 따라 value, done가 들어있는 오브젝트를 반환하는 next 메소드를 가진 객체를 반환하면 됩니다. 

const iterable1 = {};

iterable1[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

console.log([...iterable1]); // Array [1, 2, 3]

 

제너레이터 (Generator)  

이터러블, 이터레이터 객체를 만드는 손 쉬운 방법입니다.

동작은 function* 문법을 사용해서 작성하며 next를 통해 실행합니다.

next 메소드를 통해 실행이 되며 yield 문을 만나면 정지합니다. 

next 메소드를 호출함으로써 값을 소비할 수 있습니다. 

function* 문법을 통해 함수가 생성되고 최초로 호출될때 함수 내부의 어떠한 코드도 실행되지 않습니다.

이때 생성자 함수가 반환되는데 이것을 제너레이터 함수라합니다. 그 후 값을 소비하며 함수로 부터 반환되는 객체를 제너레이터라고 합니다.

 

즉, 제너레이터 객체는 이터레이터 객체입니다. 

(이터레이터 프로토콜을 따르고 있습니다.)

 

예시

값이 더이상 업을 경우 value: undefined, done: true가 반환된 것이 확인됩니다.

function* generatorFunc() {
  yield 1;
  yield 2;
  yield 3;
}

const it = generatorFunc();

console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }

 

제너레이터 적극 활용한 예로 redux-saga가 있습니다.

 

 

 

 


 

참고 및 정의 출처

+ Recent posts