Redux

yonghee·2022년 1월 11일
0

Redux를 쓰는 이유

  1. Props 문법이 번거로울때 :
    모든 components가 Props 없이 state를 직접 꺼낼 수 있다(useSelector)
  2. state 변경 관리를 할때 : state는 store에 보관하게 되는데 store는 state가 관리 되는 오직 하나 뿐인 저장소이다 이곳에서 모든 state를 관리 하게 된다.
    각각의 component는 수정 요청만 가능하다(useDispatch)

출처:https://www.youtube.com/watch?v=QZcYz2NrDIs

큰틀에서 구조적 접근

상태에 어떤 변화를 일으켜야 할 때는 액션(Action) 이라는 것을 리듀서에 전달한다. 액션은 객체 형태로 되어 있으며, 상태를 변화시킬 때 이 객체를 참조하여 변화를 일으킨다.

리듀서가 액션을 받으면 전달받은 액션을 기반으로 상태를 어떻게 변경시켜야 할지 정한다. 액션을 처리하면 새 상태를 스토어에 저장한다.

용어정리

Action

Action은 말 그대로 어떤 액션을 취할 것인지 정의해 놓은 객체이다.

ex) { type:ADD_TO_CART, payload: request }

보통 다음과 같은 모양으로 구성됩니다. 여기서 type은 필수로 지정을 해 주어야 하며, 그 외의 것들은 선택적으로 사용할 수 있다.

이렇게 모든 변화를 action을 통해 취하는 것은 우리가 만드는 앱에서 무슨 일이 일어나고 있는지 직관적으로 알기 쉽게 하는 역할을 한다.

Dispatch는 Action을 전달하는 메서드이다. dispatch의 전달인자로 Action 객체가 전달됩니다. 그리고 Reducer를 호출해 state의 값을 바꾸는 역할을 한다.

Reducer

Reducer 는 현재의 state와 Action을 이용해서 새로운 state를 만들어 내는 pure function 이다. 또한 보이는 코드는 쇼핑몰에서 크게 볼 수 있는 장바구니 추가 액션에 대한 코드이다.

ex) const itemReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TO_CART:
      return Object.assign({}, state, {
        cartItems: [...state.cartItems, action.payload]
      })
    default:
      return state;
  }
}

보통 위와 같은 모양으로 구성됩니다. 위의 예시에서는 switch 문을 통해서 코드를 작성했지만 if 문으로 작성해도 무방하다.

Store

말 그대로 state가 관리되는 오직 하나뿐인 저장소의 역할을 한다. Redux 앱의 state가 저장되어 있는 공간. 다음은 createStore 메서드를 활용해 reducer를 연결하는 방법이다, createStore와 더불어 다른 리듀서의 조합을 인자로 넣어서 스토어를 생성할 수 있다.

ex) const store = createStore(rootReducer);

Reducer의 Immutability(불변성)

redux 의 state값을 변경할땐 불변성을 유지해주어야 한다.
왜냐 리덕스를 사용하는 이유 중 하나가 히스토리가 관리되기 때문이다
변경하기 전 state값과 변경후의 state의 값을 비교하려면 불변성을 지켜줘야된다.

const obj1 ={}
const obj2 ={}

obj1 === obj2 //false
 

obj1 과 obj2 를 비교하면 false이다 
왜냐 둘다 새 객체이기 때문이다.

const obj1 = {a:2};
const obj2 = obj1.a

obj2 === obj1.a // true

obj2에 obj1의 a속성값을 넣어주고
서로 비교하면 true가 된다.
이럴경우엔 참조관계가 되기 때문이다.

ex) 
const initialState = {
   name:'kim',
   job:'student',
   age:null;
}

const reducer = (state=initialState, action) =>{
 switch (action.type) {
    case TEST:
      return {
        ...state,
        age:action.data
      };
    default:
      return state
} 

그래서 redux에서 새로운 state값을 return할 경우 새 객체로 리턴을해준다
새객체로 리턴해주면 전의 state객체와 return해준 새 객체가 false가 되기때문에 전 state와 후 state값을 기록할 수 있기 때문이다.

새객체가 return 되면 redux는
age 의 값이 null 에서 우리가 action으로 넘긴 데이터 값으로 바뀐다는 걸 인지하고 state값을 추적해준다.

또한 객체 구조 분해 할당(...state)으로 모든 값을 넣어주되 바뀌는 부분만 변경해주어야 된다
굳이 하나하나 코드를 똑같이 쓸 필요도 없을 뿐더러 메모리가 낭비되기 때문에 위의 방식으로 해주는것이 좋다.

const initialState = {
   name:'kim',
   job:'student',
   age:null;
}

const reducer = (state=initialState, action) =>{
 switch (action.type) {
    case TEST:
      return state.age = 20;
      break;
    default:
      return state
} 

만약 새로운 객체를 리턴하지 않는다면 state객체에서 age의 값은 2가 되고 참조관계가 되기때문에 redux는 둘다 true라고 생각하여 데이터의 값을 변경하지 않는다.

불변성과 Object.assign()

Redux의 state 업데이트는 immutable한 방식으로 변경해야 한다는 것. Redux의 장점 중 하나인 변경된 state를 로그로 남기기 위해서 꼭 필요한 작업이다.

리덕스를 사용하면서 state 를 업데이트 하기 위해서는 자바스크립트의 순수 함수 (pure function) 을 사용하게 된다. 그리고 이것을 reducer 라고 한다. 이 때 reducer 는 불변성을 가져야 한다. 다시 말하면 reducer 를 구성하는 자바스크립트 함수를 작성할 때, 그 내부에서 동작하는 메소드를 포함한 모든 것들은 immutable 한 방식이어야 한다.

reducer 를 작성하는 방법 중 하나 바로 Object.assign() 메소드를 활용하는 방식이다.

"어? Object.assign() 은 mutable 한 방식으로 작동하지 않나요?" 라고 생각할 수도 있습다. 실제로 Object.assign() 공식문서를 살펴보면 동작 예시에서 target 객체가 바뀐 것을 알 수 있습니다.

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);  // expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);  // expected output: Object { a: 1, b: 4, c: 5 }

target 객체는 위에서 보시는 것처럼 변했습니다. returnedTarget 도 마찬가지이구요. 그렇다면 source 는 어떨까요?

console.log(source); // expected output: Object { b: 4, c: 5 };
source 는 바뀌지 않은 것을 확인할 수 있다. target 객체만 바뀌는 것이죠. 따라서 reducer 함수 내에서 Object.assign() 메소드를 활용할 때, 다음과 같은 방식으로 사용하여 불변성을 유지할 수 있다.

return Object.assign({}, 기존 state, {
  변경하려는 state 내부의 키: [...기존 state 안에 담겨있던 값, action.payload 를 통해 전달받은 값]
})

Redux 공식문서

공식 문서를 통해서도 확인하실 수 있듯이 connect parameter를 통해 mapStateToProps, mapDispatchToProps 등의 메서드를 이용하는 방법과 Redux hooks를 이용하는 방법이 있다. 이 중에서 Redux hooks가 보다 최근에 나온 방법이고 비교적 사용하기 쉽기 때문에 Redux hooks를 이용해서 market-list를 만들었다.

Redux hooks에서는 크게useSelector(), useDispatch() 이 2가지의 메서드를 기억하면 된다

useSelector()

먼저 useSelector()는 컴포넌트와 state를 연결하는 역할을 합니다. 컴포넌트에서 useSelector 메서드를 통해 store의 state에 접근할 수 있다.
useSelector의 전달인자로는 콜백 함수를 받으며 콜백 함수의 전달인자로는 state 값이 들어 간다.

useDispatch()

useDispatch()는 Action 객체를 Reducer로 전달해 주는 메서드이다. Action 이 일어날만한 곳은 클릭 등의 이벤트가 일어나는 컴포넌트이다

profile
필요할 때 남기는 날것의 기록 공간

0개의 댓글