📌

수민님 - useReducer

 

5.2 reducer 함수란?

reducer 함수는 현재 상태와 action 값을 전달받아 새로운 상태를 반환하는 함수입니다.
 
// 현재 상태(state)와 행동(action)을 인자로 받습니다. // 함수 선언식 function Reducer(state, action) => { return { ... } } // 또는 // 함수 표현식 const Reducer = (state, action) => { return { ... } }
 

5.2.1 action과 type

action은 현재 상태의 업데이트를 위해 필요한 정보를 담은 값으로, 보통 객체의 형태를 띱니다. 하지만, action의 값은 문자열이나 숫자여도 상관이 없습니다.
reducer의 로직은 action이 새로운 상태를 계산하는 데 쓰이는지 확인하기 위해 action.type을 체크합니다. 이 때, action들이 분명한 의미를 가지고 유용한 정보를 주도록 설명하는 방식의 type 필드를 작성해야 합니다. 하지만, useReducer에서 사용하는 action 객체에 type 필드를 명시하는 것 또한 자유입니다.
 
// 아래 코드는 5.3에서 다룰 코드에서 발췌하였습니다. switch (action.type) { // Reducer 함수로 전달된 action 객체의 값이 // { type: "LOGIN_SUCCESS" } 라면 아래의 구문들이 실행됩니다. case "LOGIN_SUCCESS": return { ...state, user: action.payload, isLogin: true, message: "로그인 성공!", }; . . . }
 
위의 코드는 switch문을 사용하여 action 객체의 type 필드에 매핑되어 있는 값, 즉 “LOGIN_SUCCESS” 케이스에 따라 상태를 업데이트합니다.
 

5.2.2 reducer 함수의 조건 - 순수 함수

reducer를 순수 함수로 작성하는 이유는 예상대로 동작하도록 보장하는 것에 목적이 있습니다. 부가적으로, Redux의 변경감지 알고리즘에 의해서도 reducer를 반드시 순수 함수로 작성해야 합니다.
따라서, reducer 함수 내에서 reducer 함수 스코프 바깥의 변수를 수정하거나 사용해서는 안됩니다. 비동기 로직인 AJAX 호출, Promise 객체 사용이 불가능하고, 추가적으로 Date.now(), Math.random()과 같이 무작위 값을 반환하는 함수를 사용할 수 없습니다.
 
 
💡
순수 함수(pure function) VS 비순수 함수 (impure function) 동일한 매개변수가 주어지면 항상 동일한 결과를 반환하는 함수를 일컫습니다. 순수 함수를 사용하여 부수 효과(side effect)가 발생하는 것을 방지할 수 있습니다.
// 순수 함수, pure function function pureAdd(num1, num2) { return num1 + num2; } console.log(add(1, 2)); // 3 console.log(add(1, 2)); // 3
 
pureAdd 함수는 외부 환경에 의한 영향을 받지 않고, 주어진 매개변수가 같을 때 같은 결과를 반환하므로 순수 함수라 할 수 있습니다.
 
let sideNumber = 10; // 비순수 함수, impure function function impureAdd(num1, num2) { return num1 + num2 + sideNumber; } console.log(add(1, 2)); // 13 sideNumber = 20; console.log(add(1, 2)); // 23
 
impureAdd 함수 스코프 외부에서 선언된 sideNumber 변수가 impureAdd 함수에서 다뤄지고 있고, 주어진 매개변수가 같아도 다른 결과를 반환하므로 비순수 함수라 할 수 있습니다.
 

5.2.3 reducer 함수의 조건 - 상태 변이(state mutation) 지양

상태는 불변의 데이터 구조를 띠어야 합니다. 하지만 번번히 상태의 원본을 수정하거나 추가, 혹은 덮어쓰는 방식을 사용할 경우 상태 변이가 발생하게 됩니다. 이는 여러가지 문제를 야기합니다. 먼저, 기존의 상태를 사용하던 다른 컴포넌트에서 에러를 발생시켜 리렌더링 시에 예기치 않은 문제가 생길 수 있습니다. 이를 디버그하더라도, 상태가 어디서 변경되었는지 알기 어렵습니다.
이를 방지하기 위해, 깊은 복사를 사용하여 업데이트한 상태를 반환하여 기존 상태를 대체합니다. 구체적인 방법으로는 Object.assign() 또는 spread 연산자를 사용하여 깊은 복사를 수행합니다. 이를 통해 상태의 불변을 유지할 수 있습니다.
 
💡
깊은 복사(deep copy)? 데이터(값) 자체를 복사하여 다른 주소값을 가지는 새로운 변수(또는 객체)로 생성하는 것을 일컫습니다.
 
function Reducer(state, action) { switch (action.type) { case "LOGIN_SUCCESS": // ❌ 아래와 같은 상태 변이를 지양 state[user] = action.payload; state[isLogin] = true; state[message] = "로그인 성공!"; return state; . . . }
 
function Reducer(state, action) { switch (action.type) { case "LOGIN_SUCCESS": // ✅ 깊은 복사를 통해 새로운 객체를 반환 return { ...state, user: action.payload, isLogin: true, message: "로그인 성공!", }; . . . }