[study] Redux 기본

kind J·2022년 10월 27일
0
post-thumbnail

이전 스터디에서 리액트, next.js 와 함께 Redux 를 쓰는 방법을 익혔었는데 단순 React 에서 Redux를 쓰는 법이 궁금해졌다. 벨로퍼트님의 모던 리액트 정리해놓은 자료를 보면서 Redux에 대해 정리해보려고한다.

리덕스 설치

루트 디렉터리에 가서
redux 와 react-redux 를 설치한다.

yarn add redux react-redux

리덕스 모듈 만들기

node birds 만들기 강좌에서 리덕스를 쓸때 이런 파일 구조로 만들었었다.

saga, reducer로 나누어 다른 파일에서 관리했더니 불편한점은 하나의 기능을 수정하려고 하면 이기능과 관련된 여러개의 파일을 수정해야 하는 것이였다.

이 불편함을 개선하고자 나온 것이 Ducks 패턴이라고 한다.

Ducks 패턴은 구조 중심이 아니라 기능중심으로 파일을 나눈다. 단일 기능을 작성하거나 수정할 때 하나의 파일만 다루면 되므로 직관적인 코드 작성이 가능하다.

즉 action type, action creator 함수, saga, reducer 를 하나의 파일에서 관리하는 것이다.

modules/counter.js


// 액션타입
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';

// 액션 크리에이터 함수
export const increase = () => ({type: INCREASE});
export const decrease = () => ({type: DECREASE});

//초기값
const initialState = 0;

//리듀서
export default function counter(state = initialState, action) {
  switch(action.type) {
    case INCREASE:
      return state + 1;
    case DECREASE:
      return state - 1;
    default :
      return state;
  }
}

리듀서는 이전 상태와 액션을 받아서 다음 상태를 만들어 주는 함수이다.

modules/index.js

그 다음 루트 리듀서를 만들어 준다.


import {combineReducers} from 'redux';
import counter from  './counter';

const rootReducer = combineReducers({counter});

export default rootReducer;

combineReducers 는 redux 에서 제공하는 리듀서들을 합쳐주는 메서드이다. 리듀서(함수)를 쉽게 합치기 위해서 사용한다.

지금은 counter 리듀서(함수) 밖에 없지만 리듀서가 여러개라면


import counter from  './counter';
import user from  './user';
import post from  './post';

const rootReducer = combineReducers({counter, user, post});

이렇게 하면 된다.

user 와 post 의 initialState 는 combineReducers 가 알아서 합쳐서 넣어준다.


프로젝트에 리덕스 적용하기(스토어 생성하기)

프로젝트에 리덕스를 적용할 때는 src/index.js 에서 루트 리듀서를 불러와서 이를 통해 새로운 스토어를 만들고 Provider 를 사용해서 프로젝트에 적용을 한다.

src/index.js


import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import * as serviceWorker from "./serviceworker";
import { createStore } from "redux";
import { Provider } from "react-redux";
import rootReducer from "./modules";

const store = createStore(rootReducer);

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

이렇게 작성하고 나니 createStore 는 deprecate 되었다고 한다.
@reduxjs/toolkit 의 configureStore 를 쓰라고 권장하고 있다. toolkit 적용은 나중에 해보도록 하고 일단 가이드 대로 대체하고 계속 진행해보자.


import { legacy_createStore as createStore } from "redux";


🚩 스토어 생성


const store = createStore(rootReducer);

  • Store 는 리덕스에서 가장 핵심적인 인스턴스이다. 이 안에 현재 상태를 내장하고 있고 구독중인 함수들이 상태가 업데이트 될 때마다 다시 실행되게 해준다.


🚩 Provider에 스토어값 설정하기


   <Provider store={store}>
        <App />
    </Provider>,

  • Provider 는 react-redux 라이브러리에 내장되어 있는 리액트 앱에 store 를 손쉽게 연동할 수 있도록 도와주는 컴포넌트이다. 이 컴포넌트를 불러온 다음 연동할 컴포넌트를 감싸준 다음 Provider 컴포넌트의 props 로 store 값을 설정해 주면 된다.



프레젠테이셔널 컴포넌트 준비

프레젠테이셔널 컴포넌트는 오직 뷰만을 담당하는 컴포넌트이다. 이안에는 DOM 엘리먼트 style 을 갖고 있고 프레젠테이셔널 컴포넌트나 컨테이너 컴포넌트를 가지고 있을 수도 있다.
하지만 리덕스 스토어에는 직접적인 접근 권한이 없으며 오직 props 로 만 데이터를 가져올 수 있다. 또한 대부분의 경우 state 를 가지고 있지 않으며 갖고 있을 경우 데이터에 관련된 것이 아니라 UI에 관련된 것이여야한다. 주로 함수형 컴포넌트로 작성되며, state 를 갖고 있거나 최적화를 위해 LifeCycle 이 필요해 질때 클래스형 컴포넌트로 작성된다.

src/components/Counter.js

function Counter({ number, onIncrease, onDecrease }) {
  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}> +1 </button>
      <button onClick={onDecrease}> -1 </button>
    </div>
  );
}

export default Counter;

컨테이너 컴포넌트

프레젠테이셔널 컴포넌트들과 컨테이너 컴포넌트들을 관리하는 것을 담당한다. 주로 내부에 DOM 엘리먼트가 직접적으로 사용되는 경우가 없다. 사용되는 경우는 감싸는 용도일 때만 사용된다.
스타일을 가지고 있지 않는다. 스타일은 모두 프레젠테이션 컴포넌트에서 정의되어야한다. 상태를 가지고 있을 때가 많으며 리덕스에 직접 접근할 수 는 없다.

이를 통해 UI와 Data 가 분리되어 프로젝트를 이해하기 쉽고 컴포넌트 재 사용률도 높아진다.

src/containers/counterContainer.js

import React from "react";
import Counter from "../components/Counter";
import { useSelector, useDispatch } from "react-redux";
import { increase, decrease } from "../modules/counter";

function CounterContainer() {
  const number = useSelector((state) => state.counter);
  const dispatch = useDispatch();

  const onIncrease = () => {
    dispatch(increase());
  };
  const onDecrease = () => {
    dispatch(decrease());
  };

  return (
    <Counter number={number} onIncrease={onIncrease} onDecrease={onDecrease} />
  );
}

export default CounterContainer;

로컬 서버를 실행하면 위와같이 뜨는 것을 확인할 수 있다.

Reference

https://velog.io/@dolarge/React-Redux-Ducks-%ED%8C%A8%ED%84%B4
https://redux.vlpt.us/1-2-presentational-and-container-components.html

profile
프론트앤드 개발자로 일하고 있는 kind J 입니다.

0개의 댓글