thumbnail
Redux를 사용해서 Counter(카운터) 만들기 (feat. React)
Mar 15, 2023

counter(카운터) 앱을 만들어보면서 리덕스 기초를 익혀보자.

기능 설계

counter app image

리덕스로 상태를 관리하고 이벤트가 발생할 때마다 변경된 상태를 화면에 띄워주는 기능을 구현할 것이다.

check list

+ 버튼으로 카운트 증가

- 버튼으로 카운트 감소

reset 버튼을 누르면 초기 상태로 되돌아가기


기본 세팅

리덕스 설치

아래 명령어로 redux와 관련된 패키지들을 설치하자.

$ yarn add redux react-redux @reduxjs/toolkit

앱 구성

하나의 파일에 리덕스 관련 코드를 다 넣어도 상관없지만 가독성을 위해 파일을 분리해 주었다.

📂 counter
├── public
│ ├── index.html
└── src
│ ├── action.js
│ ├── Counter.js
│ ├── index.js
│ ├── reducer.js
│ ├── store.js

기능 구현

메인 컴포넌트

메인 컴포넌트가 될 Counter.js에 다음과 같이 기본 틀을 만들어준다.

/* Counter.js */ import React from 'react'; export default function Counter() { return ( <div className="counter-wrap"> <label className="title">Counter</label> <span>0</span> <div className="btn-wrap"> <button>-</button> <button className="reset-btn">reset</button> <button>+</button> </div> </div> ); }

아직은 아무 동작도 하지 않는다.

Action(액션)

Action은 앱에서 발생한 일을 설명하는 이벤트라고 할 수 있는데, 상탯값 변경이 필요할 때 사용한다. type 속성값을 가지는 객체로써 생성되어야 하고 type에 해당하는 값은 고유하면서 액션이 하는 일을 설명할 수 있어야 한다.


카운터 앱에서는 카운트 증가, 감소, 리셋 세 가지 액션이 필요하므로 action.js에 다음과 같이 액션 함수를 작성하였다. 나중에 불러와 사용해야 하므로 export로 내보낸다.

/* action.js */ export const increaseCount = () => { return { type: 'count/increment' }; }; export const decreaseCount = () => { return { type: 'count/decrement' }; }; export const resetCount = () => { return { type: 'count/reset' }; };

Reducer(리듀서)

Reducerstateaction을 매개변수로 받아 필요한 경우 상태를 업데이트해서 반환하는 함수이다. 리듀서는 다음과 같은 세 가지 원칙을 따라야 한다.

💡 리듀서가 지켜야 할 규칙

  • state, action을 기반으로 새 상탯값만 계산해야 한다.
  • 기존 state를 수정하는 것이 아닌, 복사된 값을 변경하는 불변 업데이트를 실행해야 한다.
  • 비동기 논리를 수행하거나 임의의 값을 계산하거나 다른 부작용을 일으키면 안 된다.

위 규칙을 기억하면서 화면에 표시할 카운트 숫자를 state로 지정해 리듀서를 생성한다. 이 리듀서에 액션을 전달하면 switch 문을 거쳐 각 액션에 해당하는 상탯값 업데이트를 실행하는 것이다.

/* reducer.js */ // 초기 값은 0 const initialState = { count: 0, }; const countReducer = (state = initialState, action) => { const { count } = state; switch (action.type) { case 'count/increment': { return { ...state, count: count + 1, }; } case 'count/decrement': { return { ...state, count: count - 1, }; } case 'count/reset': { return { ...state, count: 0, }; } default: { return { ...state, }; } } }; export default countReducer;

store(스토어)

store는 상탯값을 가지고 있는 저장소라고 생각하면 된다. 리듀서를 전달함으로써 생성되어 내장 함수로 현재 state 값을 받아오거나 action을 전달해 state를 업데이트할 수 있다.

/* store.js */ import { configureStore } from '@reduxjs/toolkit'; import countReducer from './reducer'; const store = configureStore({ reducer: countReducer }); export default store;

이렇게 생성한 store를 리액트 앱에 연동시키기 위해 index.js 파일에 react-redux 라이브러리에 내장된 <Provider> 컴포넌트를 추가하고 store를 전달해준다.

import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux'; import Counter from './Counter'; import store from './store'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <Counter /> </Provider>, );

기능 적용하기

이제 메인 컴포넌트로 돌아와 store에 저장된 상탯값을 업데이트하거나 불러와 적용하기만 하면 된다.


useSelector()는 store의 상탯값으로부터 특정 정보를 가져올 때 사용한다. state => state.count와 같은 형식으로 상탯값을 반환한다.

useDispatch()는 action을 전달하여 상탯값 업데이트를 실행시키는 함수이다. 아까 action.js 파일에서 작성한 액션 함수들을 불러와 각 버튼에 적용할 이벤트 핸들러 함수를 만들어 주었다.

/* Counter.js */ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { decreaseCount, increaseCount, resetCount } from './action'; export default function Counter() { const count = useSelector(state => state.count); const dispatch = useDispatch(); const handleIncreaseCount = () => { dispatch(increaseCount()); }; const handleDecreaseCount = () => { dispatch(decreaseCount()); }; const handleResetCount = () => { dispatch(resetCount()); }; return ( // ... ); }

해당 함수나 값을 버튼 또는 span 내부에 전달해주면 카운터 앱이 잘 동작하는 것을 확인할 수 있다.

/* Counter.js */ export default function Counter() { //... return ( <div className="counter-wrap"> <label className="title">Counter</label> <span>{count}</span> <div className="btn-wrap"> <button onClick={handleDecreaseCount}>-</button> <button className="reset-btn" onClick={handleResetCount}> reset </button> <button onClick={handleIncreaseCount}>+</button> </div> </div> ); }

counter app


References

Table Of Contents
nxnaxx blog © 2022-2024 Powered By Gatsby.