오랜만에 블로그에 정리를 하는 것 같네요!
오늘은 리액트-리덕스에 대해 정리를 해보겠습니다 😊
리액트 어플리케이션에서 리덕스를 사용할 때 장점
상태 업데이트에 관한 로직을 모듈로 따로 분리하여 컴포넌트 파일과 따로 관리할 수 있어 코드를 유지 보수하는 데 도움이 됩니다.
여러 컴포넌트에서 동일한 상태를 공유해야 할 때 매우 유용합니다.
실제 업데이트가 필요한 컴포넌트만 리렌더링되도록 쉽게 최적화해 줄 수도 있습니다.
바닐라 자바스크립트 환경에서 리덕스를 사용할 때는 스토어의 내장 함수인 store.dispatch
와 store.subscribe
함수를 사용했습니다.
리액트 애플리케이션에서는 store 인스턴스를 직접 사용하기보다는 주로 react-redux
라는 라이브러리에서 제공하는 유틸 함수(connext)와 컴포넌트(Provider)를 사용하여 리덕스 관련 작업을 처리합니다.
설치
$ npm install redux react-redux
리액트 프로젝트에서 리덕스를 사용할 때 가장 많이 사용하는 패턴은 프레젠테이셔널 컴포넌트와 컨테이너 컴포넌트를 분리하는 것입니다.
리덕스를 사용할 때는
actions
, constants
, reducers
라는 세 개의 디렉터리를 만들고 그 안에 기능별로 파일을 하나씩 만드는 방식입니다.액션 타입
, 액션 생성함수
, 리듀서 함수
를 기능별로 파일 하나에 몰아서 다 작성하는 방식 입니다.이번에는 Ducks 패턴을 사용해서 작성해보겠습니다.
(Ducks 패턴을 사용하여 액션 타입, 액션 생성 함수, 리듀서를 작성한 코드를 '모듈'이라고 합니다.)
modules/counter.js
//액션 타입 정의하기
const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";
액션 타입은 대문자!
문자열 내용은 '모듈 이름/액션 이름'과 같은 형태로 작성합니다.
액션 타입을 정의한 다음에는 액션 생성 함수를 만들어 주어야 합니다.
modules/counter.js
//액션 타입 정의하기
const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";
//액션 생성 함수 만들기
export const increase = () => ({type: INCREASE});
export const decrease = () => ({type: DECREASE});
modules/counter.js
//초기 상태와 리듀서 함수 만들기
const initialState = {
number: 0,
};
function counter(state = initialState, action) {
switch (action.type) {
case INCREASE:
return {
number: state.number + 1,
};
case DECREASE:
return {
number: state.number - 1,
};
default:
return state;
}
}
이 모듈의 초기 상태에는 number 값을 설정해 주었습니다. 리듀서 함수에는 현재 상태를 참조하여 새로운 객체를 생성해서 반환해줍니다.
export는 여러 개를 내보낼 수 있지만
export default는 단 한 개만 내보낼 수 있습니다.
불러오는 방식도 다릅니다.
import counter from './counter'; import {increase, decrease} from './counter'; //한꺼번에 불러오고 싶을 때 import counter, {increase, decrease} from './counter';
modules/todos.js
//액션 타입 정의
const CHANGE_INPUT = "todos/CHANGE_INPUT"; //인풋 값을 변경함
const INSERT = "todos/INSERT"; //새로운 todo를 등록함
const TOGGLE = "todos/TOGGLE"; //todo를 체크/체크 해제함
const REMOVE = "todos/REMOVE"; //todo를 제거함
//액션 생성 함수 만들기
//전달받은 파라미터는 액션 객체 안에 추가 필드로 들어감
export const changeInput = (input) => ({
type: CHANGE_INPUT,
input,
});
let id = 3; //insert가 호출될 때마다 1씩 더해집니다.
export const insert = (text) => ({
type: INSERT,
todo: {
id: id++,
text,
done: false,
},
});
export const toggle = (id) => ({
type: TOGGLE,
id,
});
export const remove = (id) => ({
type: REMOVE,
id,
});
insert
함수는 액션 객체를 만들 때 파라미터 외에 사전에 이미 선언되어 있는 id라는 값에도 의존합니다.
이 액션 생성 함수는 호출될 때마다 id 값에 1씩 더해 줍니다.
이 id 값은 각 todo 객체가 들고 있게 될 고윳값입니다.
객체에 한개 이상의 값이 들어가므로 불변성을 유지해 주어야 합니다.
modules/todos.js
//초기 상태 및 리듀서 함수 만들기
const initialState = {
input: "",
todos: [
{ id: 1, text: "리덕스 기초 배우기", done: true },
{
id: 2,
text: "리액트와 리덕스 사용하기",
done: false,
},
],
};
function todos(state = initialState, action) {
switch (action.type) {
case CHANGE_INPUT:
return {
...state,
input: action.input,
};
case INSERT:
return {
...state,
todos: state.todos.concat(action.todo),
};
case TOGGLE:
return {
...state,
todos: state.todos.map((todo) =>
todo.id === action.id ? { ...todo, done: !todo.done } : todo
),
};
case REMOVE:
return {
...state,
todos: state.todos.filter((todo) => todo.id !== action.id),
};
default:
return state;
}
}
나중에 createStore
함수를 사용하여 스토어를 만들 때 리듀서를 하나만 사용해야 합니다.