블로그만들기(5) - Redux

anjoy·2021년 2월 16일
1

블로그만들기

목록 보기
5/13
post-thumbnail
post-custom-banner

리덕스(Redux)가 뭐지?

리덕스는 Flux 개념을 바탕으로 한 React에서 가장 많이 사용되고 있는 상태(State)관리 라이브러리로써 여러 컴포넌트에서 존재하는 State와 State관련 로직들을 다른 파일로 분리시켜 효율적으로 관리할 수 있게 도와줍니다.

또한, 컴포넌트끼리 State를 공유할 때 Props로 컴포넌트들을 거치지 않고 쉽게 State 값을 전달하거나 받을 수 있습니다.

위의 사진과 같이 기존에 React 컴포넌트에서 개별적으로 관리하던 State를 별도의 공간인 Store에서 저장, 관리하며 컴포넌트에서는 Store에서 원하는 State를 선택(Select)하여 읽어 사용할 수 있습니다.

💡 리덕스는 3가지 규칙이 존재합니다.

1. 한 애플리케이션에는 하나의 스토어가 존재한다.

사실 하나의 애플리케이션에 여러개의 스토어를 만들고 싶다면 만들 수는 있으나 권장하지 않습니다.
여러개의 스토어가 존재하면 어떠한 특정 업데이트가 빈번하게 이러날 가능성이 있으며, 리덕스 개발 도구(Redux dev tool과 같은...)를 활용하지 못하게 됩니다. 하나의 스토어를 가진 애플리케이션에서 특정한 부분을 완전하게 다른 애플리케이션으로 분리시킬 때에 여러개의 스토어를 만드는 것이 좋다고 생각합니다.

2. State는 읽기 전용입니다.

기존에 state를 업데이트할 때 setState 혹은 useState를 통해 반환받는 함수를 통해 업데이트를 합니다.
만약 배열의 state를 업데이트할 때 기존의 state에 push하거나 업데이트를 하지 않고 새로운 배열을 생성해서 교체하는 방식으로 state를 업데이트합니다. 배열을 제외하고도 객체를 업데이트할 때도 spread(...) 연산자나 Object.assign을 통해 새로운 객체로 업데이트합니다.

리덕스에서도 마찬가지로 새로운 상태를 생성해서 업데이트해주는 방식으로 진행해주면 나중에 개발자 도구를 통해 이전의 상태로 돌릴 수도 있고 이후의 상태로 돌릴 수도 있습니다.

Redux에서 state는 불변성을 유지해주어야하는데, 이는 내부적으로 데이터가 변경되는 것을 감지하기 위해 shallow equality 감사를 진행하기 때문입니다.

저는 블로그를 만들 때 immer 를 통해 불변성을 유지하며 state관리를 할 예정입니다.

3. State를 변화시키는 함수인 리듀서(Reducer)는 순수한 함수이여야 합니다.

참고 - 기본기를 쌓는 정아마추어 코딩블로그

여기서 순수한 함수라는 개념이 항상 햇갈리는데 부수효과(side effect: 외부의 상태를 변경하거나 인자의 상태를 직접 변경하는 것)가 없는 함수를 순수함수라고 합니다.

예를 들어 이해해보도록 합시다.

function add(a, b) {
  return a + b;
}

의 경우에 add 함수는 순수 함수입니다.

let c = 10;
function add2(a, b) {
  return a + b + c;
}

여기서 add2는 순수 함수가 아닙니다..\

왜냐하면 함수 내부에 외부 변수인 c가 존재하며 c의 값이 변하면 결과값도 변하기 떄문입니다. 만약에 외부 변수인 c가 변하지 않는 수인 상수라면 add2도 순수함수입니다.

let c = 10;
function add3(a, b) {
  c = b;
  return a + b;
}

위의 add3는 외부 변수인 c의 값을 변경하는 코드가 존재하여 부수효과가 발생하므로 순수함수가 아닙니다.

따라서 순수함수여야만 하는 리듀서는 다음과 같은 특징을 가집니다.

  • 리듀서 함수는 이전 상태와 액션 객체를 파라미터로 받습니다.
  • 이전의 상태는 절대 건드려선 안되고 새로운 상태 객체를 만들어 반환해야합니다.
  • 동일 파라미터의 리듀서는 항상 동일한 결과를 반환해야합니다.

그렇습니다. 리듀서만으로는 값을 변화시키거나 다른 결과값을 원하는 작업을 진행할 수 없습니다.

그런 작업을 하기위해서는 리덕스 미들웨어를 사용해주어야합니다. (미들웨어는 Redux를 애플리케이션에 연결한 뒤에 알아봅시다.)

React.js + Next.js 애플리케이션에 Redux 연결하기

리액트에서 리덕스를 사용하기 위해서는 아래의 라이브러리들이 설치되어야 합니다.

  • redux : 리덕스 모듈
  • react-redux : 리액트 컴포넌트에서 리덕스를 사용하기 위한 유용한 도구들이 포함되어 있습니다.

먼저 간단한 리듀서 함수를 작성해봅시다.

// reducers/posts.tsx
const initialState = {
	posts: [],
	isLoaddingPosts: false,
	isLoadedPosts: false,
	loadPostsErrorReason: '',
};

export const LOAD_POSTS_REQUEST = 'LOAD_POSTS_REQUEST';
export const LOAD_POSTS_SUCCESS = 'LOAD_POSTS_SUCCESS';
export const LOAD_POSTS_FAILURE = 'LOAD_POSTS_FAILURE';

const postsReducer = (state = initialState, action) => {
	switch (action.type) {
		case LOAD_POSTS_REQUEST: {
			return {
				...state,
				isLoaddingPosts: true,
			};
		}
		case LOAD_POSTS_REQUEST: {
			return {
				...state,
				isLoaddingPosts: false,
				posts: dummyPost,
			};
		}
		case LOAD_POSTS_REQUEST: {
			return {
				...state,
				isLoaddingPosts: false,
				loadPostsErrorReason: '불러오기 실패',
			};
		}
	}
};

export default postsReducer;

와 같이 작성하고 reducers/index.tsx에서 combineReducers를 통해 root 리듀서를 생성해줍니다.

// reducers/index.tsx
import { combineReducers } from 'redux';

import posts from './posts';

const rootReducer = (state, action) => {
	const combinedReducer = combineReducers({
		posts,
	});
	return combinedReducer(state, action);
};

export default rootReducer;

이제 리듀서는 간단하게 생성을 했으니 Store를 생성해주어야겠습니다.

저는 store/configureStore.tsx 파일에 코드를 작성하여 선언한 요소를 사용하기위해 해당 파일에서 store를 생성하는 코드를 작성했습니다.

// store/configureStore.tsx
import { applyMiddleware, createStore, compose } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import { createWrapper } from 'next-redux-wrapper';

import reducers from '../reducers';

const configureStore = () => {
	const enhancer =
		process.env.NODE_ENV === 'production' ? compose(applyMiddleware()) : composeWithDevTools(applyMiddleware());
	const store = createStore(reducers, enhancer);
	return store;
};

const wrapper = createWrapper(configureStore, { debug: process.env.NODE_ENV === 'development' }); // debug: true -> 리덕스 로그 출력

export default wrapper;

Next.js 프레임워크를 통해 프로젝트를 빌드하기 때문에 next-redux-wrapper 모듈의 createWrapper 메소드를 사용해서 Next.js의 pages/_app.tsx 안에서 컴포넌트를 감싸줄 wrapper를 생성해주겠습니다.

createWrapper를 통해 감싸주는 이유는 Next.js에서 제공하는 getServerSideProps, getStaticProps등에서 리덕스 스토어에 접근할 수 있어야 하는데 next-redux-wrapper에서 이것을 도와주기 떄문입니다.

미들웨어, 영속성 등의 서드파티 기능을 store에 추가하기 위해 인핸서createStore의 인자로 넘겨줍니다.

Redux 모듈과 함께 제공되는 인핸서는 applyMiddleware() 뿐입니다.

production 환경에서는 compose()를 통해 인핸서를 조합해주었고, development 환경에서는 Redux dev tool을 사용하기 위해 redux-devtools-extensioncomposeWithDevTools로 함수를 조합해줍니다.

이제 작성한 wrapper_app.tsx 에 연결해봅시다.

// pages/_app.tsx

// import ...

import wrapper from '@store/configureStore';

const App = ({ Component, pageProps }: AppProps) => {
	return (
      	// tags
	);
};

export default wrapper.withRedux(App);

크롬 브라우저에서 개발자모드 창 상단의 Redux를 클릭해보면 리덕스가 연결되어 설정해준 intialState가 적용되어 있는 것을 볼 수 있습니다.

profile
안녕하세요 벨로그에서 자기소개를 한 줄로 쓰라고 되어있는데 최대한 한 줄로 자기 소개를 해보겠습니다. 제 이름은 .....
post-custom-banner

0개의 댓글