TIL 22-05-03

thisisyjin·2022년 5월 3일
0

TIL 📝

목록 보기
37/113

React.js

Today I Learned ... react.js

🙋‍♂️ Reference Book

🙋‍ My Dev Blog


리액트를 다루는 기술 DAY 16

  • Redux 라이브러리

Redux 라이브러리

  • 리액트 상태 관리 라이브러리.
  • 컴포넌트의 state 업데이트 관련 로직을 다른 파일로 분리시켜 효율적 관리 가능.
  • 여기저기서 사용되는 전역 상태 관리시 효과적.
    -> Redux 외에도 Context API로도 가능.
  • 규모가 큰 프로젝트의 경우에는 context API보다 Redux를 사용하는 것이 좋음.
    -> 코드의 유지 보수성 + 작업 효율 극대화.
  • 미들웨어(middleware) 기능을 제공하여 비동기 작업을 효율적으로 관리.

Redux 개념 정리

action

  • state에 어떠한 변화가 필요하면 -> action 객체가 발생.
  • 액션 객체는 반드시 type 필드를 가지고 있어야 함.
  • 보통은 type의 값을 SNAKE_CASE로 작성함.
{
  type: 'TOGGLE_VALUE'
}
  • type 필드 외의 값은 사용자 마음대로 작성해도 됨. (예> text, data, row, cell 등)

action 생성 함수

= action creator.

const addTodo = (data) => {
  return { type: 'ADD_TODO', data };
}
  • state에 변화를 일으켜야 할 때 마다 액션 객체를 만들어야 하는데, 매번 직접 작성하면 번거로우므로
    액션 생성 함수를 따로 만들어서 관리한다.

reducer

  • 변화를 일으키는 함수.
  • 액션을 생성하여 발생시키면(=dispatch) - 리듀서가 현재 state와 액션 객체를 인자로 받아옴.
    -> 두 값을 참고하여 새로운 state를 만들어 반환해줌.
const reducer(state, action) {
  switch(action.type) {
    case INCREMENT:
      return {
        ...state,
        counter: state.counter + 1
      };
  }
}

❕ 참고 - useReducer 사용시

const reducer = (state, action) => {
    // switch문
}

const ComponentName = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

store

  • 프로젝트에 리덕스를 적용하기 위해 store를 생성해야 함.
  • 한 프로젝트당 단 하나의 store만 가질 수 있음.
  • 현재 어플리케이션의 상태(state) + reducer 이 들어있음.
    -> 그 외에도 몇 가지 중요한 내장함수 有.

dispatch

  • store에 있는 내장 함수 중 하나.
  • action 객체를 발생시키는 것.
store.dispatch(action객체);
  • dispatch 함수가 호출되면 스토어는 reducer함수를 실행시켜 새로운 state를 생성함.

dispatch() -> store가 reducer 실행 -> 새로운 state 생성.

  • useReducer (Hook)을 사용하면, return [state, dispatch] 를 하게 된다.

subscribe

  • store에 있는 내장 함수 중 하나.
  • subscribe() 안에 리스너 함수를 넣어서 호출하면,
    액션이 dispatch되어 state가 바뀔때마다 리스너함수가 호출됨.
const listener = () => {
  console.log('상태가 업데이트됨');
}

const unsubscribe = store.subscribe(listener);
unsubscribe();

Parcel 프로젝트

  • 리덕스는 리액트에 종속되는 라이브러리가 아니므로, 다른 라이브러리/프레임워크와 사용 가능하다.
    (vanila JS와도 함께 사용할 수 있다.)

🙋‍♂️ 참고 - Vue에서도 사용할 수 있지만, Redux와 유사한 Vuex를 주로 사용함.

Parcel-bundler 설치

  • parcel-bundler (global 설치)
$ yarn global add parcel-bundler
$ mkdir vanila-redux

$ cd vanila-redux
$ touch index.html index.js

index.html

<!DOCTYPE html>
<html>
<body>
    <div>바닐라 자바스크립트</div>
    <script src="./index.js"></script>
</body>
</html>

index.js

console.log('hello parcel');
  • parcel명령어 실행
$ parcel index.html

간단한 UI 구성

index.css

.toggle {
  border: 2px solid black;
  width: 64px;
  height: 64px;
  border-radius: 32px;
  box-sizing: border-box;
}

.toggle.active {
  background: yellow;
}

index.html (수정)

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="index.css">
</head>
<body>
    <div class="toggle"></div>
    <hr />
    <h1>0</h1>
    <button id="increase">+1</button>
    <button id="decrease">-1</button>
    <script src="./index.js"></script>
</body>
</html>

DOM 레퍼런스생성

  • UI를 관리할 때 리액트와 같은 라이브러리를 사용하지 않으므로, DOM을 직접 수정해줘야 함.
  • index.js 상단에 querySelectort을 미리 선언해줌.
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#increase');
const btnDecrease = document.querySelector('#decrease');

액션 정의

  • state에 변화를 일으키는 것을 액션이라 한다.
  • 우선 action에 이름을 정의해준다.(고유한 이름이여야 함)

액션 이름 정의

const TOGGLE_SWITCH = 'TOGGLE_SWITCH';
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';
  • 위의 액션 이름을 사용하여 액션 객체를 만드는 액션 생성함수를 작성함.
  • 액션 객체는 반드시 type을 가져야 하며, 추후 state 업데이트시 참고할 값을 추가로 넣을 수 있음.

액션 생성함수 작성

const toggleSwitch = () => ({ type: TOGGLE_SWITCH });
const increase = (diff) => ({ type: INCREASE, diff });
const decrease = () => ({ type: DECREASE });

state 초기값(initialState) 설정

const initialState = {
    toggle: false,
    counter: 0
};

초기값의 형태는 자유임. 숫자나 문자열일 수도 있고, 객체일수도 있음.

reducer 함수 정의

  • 변화를 일으키는 reducer 함수.
  • 인자로는 stateaction값을 받아옴.
const reducer = (state = initialState, action) => {
    switch (action.type) {
        case TOGGLE_SWITCH:
            return {
                ...state,
                toggle: !state.toggle
            };
        case INCREASE:
            return {
                ...state,
                counter: state.counter + action.diff
            };
        case DECREASE:
            return {
                ...state,
                counter: state.counter - 1
            };
        default:
            return state;
    }
}
  • reducer가 맨 처음 호출될 때는 state가 undefined가 된다.
    -> 기본값을 initialState로 설정해주기 위해 인자에 =기호로 기본값을 설정해줌.

  • reducer에서는 상태의 불변성을 유지하며 데이터에 변화를 줘야 함.
    -> spread(...)를 이용하면 편리.

참고 - 객체 구조가 복잡해지면 immer 라이브러리를 함께 사용.
이전 포스팅 참조

setData(produce(data, draft => {
        draft.array.push(info);
      }));

-> produce 함수 임포트. 마치 불변성 신경 안쓰듯이 push, splice 등 사용.

스토어 생성

  • reduxcreateStore() 함수 사용.
  • 함수의 인자로는 리듀서 함수(reducer)을 넣어줘야 함.
import { createStore } from 'redux';

...
const store = createStore(reducer);

참고 - 이제는 createStore을 대체하는 redux Toolkit의 configureStore 메서드를 권장한다.

// configureStore 적용시
const store = configureStore({ reducer });

-> 참고링크

render 함수

const render = () => {
    const state = store.getState();
    if (state.toggle) {
        divToggle.classList.add('active');
    } else {
        divToggle.classList.remove('active');
    }

    counter.innerText = state.counter;
};

render();
  • state.toggle이 true면 active라는 클래스를 add하고,
    state.toggle이 false면 active를 제거함.

  • counter.innerText에 state.counter을 집어넣음. (렌더링)

subscribe

  • state가 바뀔 때 마다 방금 만든 render 함수가 호출되도록 설정.
  • 스토어의 내장함수 subscribe를 이용.
    -> 인자로 리스너 함수가 들어감.
  • 여기서는 리스너 함수가 render 함수가 됨.
store.subscribe(render);
  • React 프로젝트에서는 이 함수를 직접 사용하지 않음.
  • 컴포넌트에서 리덕스 상태를 조회하는 과정에서 react-redux 라이브러리가 이 작업을 대신해줌.

dispatch

  • 액션을 발생시키는 함수.
  • store의 내장함수 dispatch. (=store.dispatch)

    cf> reducer은 변화를 일으키는 함수. 즉, 액션(+state)을 조작하는 함수임.

divToggle.onclick = () => {
    store.dispatch(toggleSwitch());
};

btnIncrease.onclick = () => {
    store.dispatch(increase());
};

btnDecrease.onclick = () => {
    store.dispatch(decrease());
};

RESULT


리덕스의 규칙

1. 단일 스토어

  • 하나의 어플리케이션 안에는 하나의 스토어만 존재.
  • 여러개의 스토어가 있다면, 상태 관리가 복잡해질 수 있음.

2. 읽기 전용 state

  • setState를 이용하여 state를 업데이트 할 때도,
    객체나 배열을 업데이트하는 과정에서 불변성을 지키기 위해 spread(...)를 사용하거나 immer 을 사용했음.
  • 리덕스도 마찬가지로 state 변경시 기존 객체는 건들이지 않고 새로운 객체를 생성해야 함.

불변성을 왜 유지해야 하는지?

  • 내부적으로 데이터가 변경되는 것을 감지하기 위해 얕은 비교를 하기 때문.
  • 깊은 비교를 하는 것 보다 얕은 비교를 하여 좋은 성능을 유지할 수 있음.

3. reducer는 순수함수

  • reducer 함수는 반드시 '순수함수' 여야 함.

✅ 순수함수의 조건

  • reducer은 이전 상태(state)와 action객체를 인자로 받음.
  • 인자 외의 값에는 의존해서는 안됨. (외부 변수 등)
  • 이전 상태는 건드리지 않고, 새로운 상태 객체를 만들어 반환해야함.
  • 똑같은 인자로 호출된 reducer 함수는 언제나 같은 결과가 나와야함.
  • 값이 바뀔만한 동작은 (예> Date함수, 랜덤 값 생성, 네트워크 요청 등) 반드시 reducer 바깥에서 해줘야함.
    -> 예> action 생성함수에서, or 리덕스 미들웨어에서 처리하면 OK.

참고 - 주로 비동기 작업(네트워크 요청 등)은 리덕스 미들웨어를 통해 관리.

참고 2 - 지난번 useReducer에서 배웠듯이 리액트에서 state는 비동기적으로 변하지만,
Redux에서는 동기적으로 변함. (순서대로)


과정 요약

  1. 액션 타입 (이름) + 액션 생성함수 작성
  2. reducer 함수 작성
  3. store 생성
profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글