간단한 예제를 통해 Redux를 이해하기

hynnch2·2021년 5월 26일
9

현재 진행하는 토이 프로젝트에 react + mobx를 사용하기로 했는데, 아직 react의 상태관리에 대해 이해가 잘 되지 않아서 가장 널리 알려진 redux부터 공부를 시작했습니다..😂

간단한 redux 예제도 클론해보고 TodoList까지 해봤지만 막상 코드에 응용할려고 하면 다시 또 인터넷으로 back..
이렇다보니 다시 처음부터 공식문서를 보고 기초부터 다지기로 마음먹고 정리하게 되었습니다.

+) 이 글은 Redux 사용에 익숙하지 않은 초보 개발자가 쓴 글 입니다. 공식 문서와 redux관련 글을 참고하여 나름대로 Redux에 대해 이해했다고 생각하고, 이제 막 Redux를 입문하고자 하는 사람에게 도움이 되었으면 하는 바람으로 작성합니다. 틀린 부분이나 보충이 필요한 부분은 알려주시면 감사하겠습니다 👍


🤔 Redux란?

redux란 JavaScript app에서 state를 저장하는 것입니다.

??? : state는 뭐고, 이걸 왜 저장하는 거지?
=> state는 react 컴포넌트에서 유동적으로 바뀌는 값이고, 그 값에 따라 렌더링 결과가 달라질 수 있어서 중요합니다. 또, redux를 사용하지 않는다면 변경 될 컴포넌트와 관련된 다른 컴포넌트도 변경되어야 합니다.

redux를 이용하면 store와 컴포넌트가 연결되고 reducer를 통해 state를 변경 할 수 있습니다.

이해하기 쉽게 간단히 그림으로 나타내면

다음과 같은 컴포넌트 구조가 있을 때, 6번 컴포넌트의 state가 변경되고 2번 컴포넌트에도 적용되어야 한다면,

이렇게 컴포넌트를 거쳐야 합니다. 하지만 redux를 이용하면 store로 직접 연결할 수 있습니다.


😕 Redux 구조

redux가 뭔지 알았다면 이제 redux의 구조에 대해 알아보겠습니다. redux에서 중요한 것은 상태를 직접 변경하는 것이 아닌 "action"을 통해 변경하는 것입니다. 즉, redux에서 중요한 것은 다음과 같습니다.

state
action
reducer
store

추가로 dispatch, selector는 예제를 진행하며 설명하겠습니다.

  • state: 위의 내용과 동일, store에서 저장되어 있는 값.
  • action: store에 저장된 값을 변경시키는 방식을 정함.
  • reducer: (action, old state)를 받아서 new state로 변환시키는 함수.
  • store: redux를 이용하는 이유, store에 state를 저장.

여기까지 읽으면 대략적인 data flow가 이해 될 것이라 생각됩니다. 그럼 예제를 통해 state가 변하는 과정, redux가 작동하는 방식에 대해 자세히 들어가 보도록 하겠습니다 😎


간단한 숫자 카운트 예제.

  • reducer를 2개 사용. (rootReducer를 이용하기 위해)
  • add,sub,reset할 수 있는 reducer (addsubReducer)
  • 버튼 누른 횟수만 체크하는 reducer (countingReducer)
  • main화면에서 두 개의 state를 보여주는 방식으로 진행

1. action

먼저 action을 정의해야 합니다.
action이란 type 필드를 가지는 js object로 현재 프로그램에서 무엇이 발생하는지 묘사하는 것입니다.
ex) {type: 'increment'} , {type: 'addList', payload: text}
=> 전달 할 data가 있다면 payload로 전달하는 것이 관행. ✌️

2. reducer

action을 정의 했다면 이를 이용해 reducer를 정의해야 합니다. reducer는 state, action을 받아서 새로운 state를 반환하는 것입니다.

react의 reducer 정의는 다음과 같이 합니다.

(/src/reducers/addsub.js)

const initialState = {
	value: 0
}

export default function addsubReducer(state = initialState, action) {
	switch (action.type) {
		case 'increment': {
			return {
				...state,
				value: state.value + 1
			}
		}
		case 'decrement': {
			return {
				...state,
				value: state.value - 1
			}
		}
		case 'reset': {
			return {
				...state,
				value: 0
			}
		}
		default:
			return state
	}
}

(src/reducers/counting.js)

const initialState = {
	count: 0
}

export default function countingReducer(state = initialState, action) {
	switch (action.type) {
		case 'push': {
			return {
				...state,
				count: state.count + 1
			}
		}
		default:
			return state
	}
}
  1. initialState를 통해 초기 state를 저장
  2. application에서 dispatch를 통해 action을 받습니다. (뒤에 설명 할 예정)
  3. action.type을 보고 reducer를 실행.
    (...state: 불변성을 유지하기 위해 이전 state를 copy 하는 것)

다음과 같이 여러 개의 reducer를 지정했다면 redux의 combineReducer로 reducer를 통합시킬 수 있습니다.

(src/reducer.js)

import { combineReducers } from 'redux'

import addsubReducer from './reducers/addsub'
import countingReducer from './reducers/counting'

const rootReducer = combineReducers({
	value: addsubReducer,
	count: countingReducer
})

export default rootReducer

3. store

reducer까지 정의했다면 store를 만들 수 있습니다.
stores는 redux의 createStore를 통해 만들 수 있습니다.

(src/store.js)

import { createStore } from 'redux'
import rootReducer from './reducer'

const store = createStore(rootReducer)

export default store

여기까지 redux를 이용해서 코드를 작성했습니다. redux는 상태관리 라이브러리로써 react뿐만 아니라 vanilla js, Vue.js에서도 사용할 수 있습니다. 하지만 우리는 react에서 사용할 것이기 때문에 react-redux hook을 이용하면 쉽게 redux를 사용할 수 있습니다.

4. React-redux hook 🙌

react-redux hook의 함수인 useDispatch, useSelector를 설명하기 전에 우리가 hook없이 redux를 사용하는 방식에 대해 먼저 알아보겠습니다.

redux에 있는 함수는 다음과 같습니다.
1. getState(): 현재 state를 받음.
2. dispatch(): action을 reducer한테 보내서 state를 update시킴.
3. subscribe(): state가 변경되면 callback함수 호출.

즉, 실행 흐름을 보면
subscribe()를 지정 -> dispatch()를 통해 state 변화 -> subscribe에서 getState()를 통해 state를 가져오고, 그 값으로 component를 다시 render함.

여기서 subscribe를 작성하고 다시 re-render하는 작업은 복잡하고 힘든 일 입니다. 이 문제를 react-redux hook을 통해 해결할 수 있습니다. 💪

react-redux hook의 종류

  • useSelector: redux의 state관리를 도와줌. react component에서 redux의 store 내부의 data를 읽을 수 있다.
    (좋은점: state가 변경되면 이를 반영하기 위해 subscribe를 사용해야 했지만, useSelector를 이용하면 자동적으로 subscribe하는 효과가 있음)
  • useDispatch: store.dispatch를 대신하여 간단히 사용할 수 있다. useDispatch의 return값은 store.dispatch()이다.
  • provider: react-redux hook이 원하는 redux store에 접근할 수 있게 해줌. hook은 js함수이므로 저절로 store.js에서 store를 가져올 수 없다. 이를 해결하기 위해 react-redux한테 store를 전달해야 하는데 이 때 provider 사용.

hook을 이용해서 예제를 마저 작성해보겠습니다.

(src/index.js)

import { Provider } from 'react-redux'
import store from './store'

ReactDOM.render(
	<React.StrictMode>
		<Provider store={store}>
			<App />
		</Provider>
	</React.StrictMode>,
	document.getElementById('root')
);

provider를 통해 우리가 작성한 store를 지정해주고

(src/App.js)

import { useSelector, useDispatch } from "react-redux";


function App() {
	const dispatch = useDispatch()

	const { value } = useSelector(state => state.value)
	const { count } = useSelector(state => state.count)

	const addValue = () => {
		dispatch({ type: 'increment' })
	}
	const subValue = () => {
		dispatch({ type: 'decrement' })
	}
	const resetValue = () => {
		dispatch({ type: 'reset' })
	}
	const pushButton = () => {
		dispatch({ type: 'push' })
	}

	return (
		<div className="App">
			<div>
				value: {value}
			</div>
			<button onClick={addValue}> + </button>
			<button onClick={subValue}> - </button>
			<button onClick={resetValue}> reset </button>
			<div>
				count: {count}
			</div>
			<button onClick={pushButton}> click </button>
		</div>
	);
}

export default App;

react-redux hook인 useDispatch와 useSelector를 이용해서 마무리 할 수 있습니다.

<실행화면>

정상적으로 렌더링 되는 것을 확인할 수 있습니다.


👏 Redux 정리

공식 api를 토대로 번역하고 작성하다보니 글이 조금 길어졌지만 핵심만 생각하면 간단히 정리할 수 있습니다.

  1. 저장할 state와 action을 구상한다.
  2. action을 토대로 reducer들을 작성한다.
  3. rootReducer로 reducer들을 합친다.
  4. rootReducer를 store에 저장한다.
  5. provider를 이용해 store에 접근할 수 있게 한다.
  6. useDispatch, useSelector를 이용하여 맛있게 코딩한다.

코드 짤 때 오류가 발생하거나 필요한 내용만 급하게 찾아보기 바빴는데 한번 정리하면서 글을 작성하였더니 더 세밀하게 이해하는 계기가 된 것 같습니다. 위의 내용을 읽으셔서 redux에 대한 이해가 조금이나마 더 되었으면 하는 바람입니다. 부족하지만 긴 글 읽어주셔서 감사합니다. 🙂

+ 위의 내용 중 조언이 필요하거나 수정 및 보완 해주실 내용이 있으시면 댓글로 작성 부탁드립니다!! :)



[출처]

profile
more than yesterday

1개의 댓글

comment-user-thumbnail
2023년 10월 13일

굳 Redux 에 대해 이해가 잘 안되었는데 , 이 포스팅으로 이해 완료 많은도움 되었습니다.
감사합니다.

답글 달기