💻 Frontend

코드숨_코드리뷰 스터디 4주차 회고

date
May 28, 2023
slug
codesoom4
author
status
Public
tags
React
TDD
Redux
summary
코드숨 코드리뷰 스터디 4주차 회고_리덕스
type
Post
thumbnail
다운로드.png
category
💻 Frontend
updatedAt
Jun 7, 2023 03:52 AM
 

1. 학습한 내용

 
1-1) Flux Architecture와 Redux
  • Flux Architecture는 페이스북에서 상태관리 문제를 해결하기 위한 일종의 패턴이다. ex) 좋아요를 눌렀을 때 실제 상태값으로 변경이 되지 않아 다른 유저들에게는 보이지 않는 에러가 발생
  • 규모가 커질수록 MVC 패턴의 데이터 관리는 복잡도를 야기하고, 어디에서 에러가 발생하는지 추적하기 어렵다
  • 이를 보완하기 위해서 Flux Architecture를 구축했는데 핵심적인 특징은 2가지이다. 1) Action이 Dispatcher를 통해 Store에 전달되는 흐름(단방향 데이터 흐름을 구축)하고, 2) Store와 App의 관심사를 분리한다.
  • 참고자료
1-2) React에서 Redux 활용
  1. 상태관리(데이터와 조작)을 store 한 곳에서 하자
  1. 그리고 이를 연결해주는 곳이 App이다.
 
어떻게 하냐
기존에 App에서 처리해주는 것은 handler함수에서 state와 setState를 통해 바꿔준다.
const [state, setState] = useState({
    newId: 100,
    taskTitle: '',
    tasks: [],
  });

  const { newId, taskTitle, tasks } = state;

  function handleChangeTitle(event) {
    setState({
      ...state,
      taskTitle: event.target.value,
    });
  }
 
Store를 활용해서 상태관리에 대한 관심사를 분리한다면 아래와 같이 updateTaskTitle이 하는 일을 모르게 된다.

function handleChangeTitle(e){
	return setState(updateTaskTitle(state,e.target.value))
}
 
그렇다면 위와 같은 형태의 관심사 분리를 redux를 활용해본다면?
 
redux의 주요 구성요소는 아래와 같다.
  1. reducer로 기존 상태와 액션을 받아서 다른 상태로 변경해주는 함수이다. reducer는 다음과 같은 구조를 갖게 된다.
function reducer(state, action) {
  // ...

  return state;
}
  1. 그렇다면 action은 뭐냐? 상태를 업데이트하게 되는 함수를 말한다.
action의 구성요소는 아래와 같다. 무슨 액션인지 나타나는 type과 인자 값으로 실리는 payload로 구성.
export function updateTaskTitle(taskTitle) {
  return {
    type: 'updateTaskTitle',
    payload: taskTitle,
  };
}
 
  1. Store는 위와같이 state와 action으로 구성된 reducer를 store로 감싸주면 전체 상태값을 저장하는 하나의 객체가 만들어지게 된다. 이러한 스토어는 액션이 발생하면 reducer를 실행해 상태값을 새로운 값으로 업데이트한다.
const store = createStore(reducer);
 
  1. Provider로 최상위 App 컴포넌트를 감싸주면 App에서 store에 접근할 수 있다.
ReactDOM.render(
  <Provider>
    <App />
  </Provider>
  ,
  document.getElementById('app'),
);
 
  1. store에 있는 state와 action을 실제로 꺼내거나, 발생시키는데 편리한 useDispatch, useSelector 훅을 이용할 수 있다.

function selector(state) {
  return {
    taskTitle: state.taskTitle,
    tasks: state.tasks,
  };
}

export default function App() {
  const { taskTitle, tasks } = useSelector(selector);

	function handleChangeTitle(event) {
    useDispatch(updateTaskTitle(event.target.value));
  }
1-3) Redux를 테스트하는 방법
Redux가 사용된 파일을 테스트 하기 위해서는 다음과 같은 단계가 필요하다
  1. mocks 폴더 생성 및 가짜함수 생성
__mocks__라는 이름으로 폴더를 생성하게 되면, 해당 모듈을 import한 jest 파일에서 해당 파일에 있는 함수로 모킹을 한다.
// __mocks__
export const useDispatch = jest.fn();

export const useSelector = jest.fn((selector) => selector({}));
  1. react-redux 모킹 및 가짜함수를 통한 결과값 반환 (useSelector,useDispatch)

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

describe('FormContainer', () => {
  jest.mock('react-redux');
  useSelector.mockImplementation((selector) => selector({
    title: '투썸플레이스',
    sort: '카페',
    location: '신도림',
  }));
  const dispatch = jest.fn();
  useDispatch.mockImplementation(() => dispatch);
  ...
	...
   expect(dispatch).toBeCalledWith({
        type: 'ADD_RESTAURANT',
1-4) reducer에서 switch, if 사용하지 않기
reducer 공식문서에서는 보통 switch문의 예제를 보여준다. 그러나 switch문과 if문을 왜 사용하면 안좋은지를 알아보자
let number  = 2;

switch (number) {
    case 1: 
        let message = "first number";
        console.log(message)
        break;
    case 2:
        let message = "second number";
        console.log(message)
        break;
  case 3:
        let message = "third number";
        console.log(message)
        break;
    default
        let message = "second number";
        console.log(message)
        break;
}

//This throws a syntax error: identifier "message" 
//has already been declared
우선 switch의 경우에는 호이스팅 이슈가 있다.
switch case 문 안에서 동일한 변수를 선언할수 없다.
중괄호를 적게 되면 변수 범위를 제한할 수 있다고는 하지만, 중괄호를 적지 않았을 때를 계속 신경써야한다. 아래의 참고자료에 더 자세하게 나와있다.
 
if 문은 복잡해질수록 코드의 가독성이 현저히 떨어지게 된다.
 
우리가 그렇다면 리덕스에서 각각의 type에 맞는 값을 불러오는데 더 깔끔한 코드를 어떻게 만들 수 있을까?
두 가지 방법이 있다.
  1. 객체형태의 actionCreator를 만들어서 해당 값에 맞는 함수를 리턴
const initialState = {
  id: 100,
  title: '',
  sort: '',
  location: '',
  restaurants: [],
};

const actionCreator = {
  UPDATE_INPUT: (state, { payload }) => {
    const { name, value } = payload;
    return ({
      ...state,
      [name]: value,
    }
    );
  },
  ADD_RESTAURANT: (state) => {
    const {
      id, title, sort, location, restaurants,
    } = state;
    return {
      ...state,
      id: id + 1,
      title: '',
      sort: '',
      location: '',
      restaurants: [
        ...restaurants, {
          id, title, sort, location,
        }],
    };
  },
};

export default function reducer(state = initialState, action) {
  if (actionCreator[action.type]) {
    const { payload } = action;
    return actionCreator[action.type](state, { payload });
  }

  return state;
}
 
  1. Redux-actions
위와 같이 반복되는 Action선언과 함수형태로 reducer를 선언하는 것을 도와주는 redux-actions라이브러리를 사용할 수도 있다.
이를 사용하면 아래와 같이 코드를 만들 수 있게 된다.
import { handleActions, createAction } from 'redux-actions';

const UPDATE_TASK_TITLE = 'task/UPDATE_TASK_TITLE';
const ADD_TASK = 'task/ADD_TASK';
const DELETE_TASK = 'task/DELETE_TASK';

export const updateTaskTitle = createAction(UPDATE_TASK_TITLE);
export const addTask = createAction(ADD_TASK);
export const deleteTask = createAction(DELETE_TASK);

const initialState = { newId: 100, taskTitle: '', tasks: [] };

const reducer = handleActions({
  [UPDATE_TASK_TITLE]: (state, { payload }) => ({
    ...state,
    taskTitle: payload.taskTitle,
  }),
  [ADD_TASK]: (state) => {
    const { newId, tasks, taskTitle } = state;
    return {
      ...state,
      newId: newId + 1,
      taskTitle: '',
      tasks: [...tasks, { id: newId, title: taskTitle }],
    };
  },
  [DELETE_TASK]: (state, { payload }) => {
    const { tasks } = state;
    return {
      ...state,
      tasks: tasks.filter((task) => task.id !== payload.id),
    };
  },
}, initialState);
  1. 그냥 제일 쉬운 RTK(toolkit)쓰자..
    1. 사실 toolkit은 위의 모든 것들을 쉽게 쓸 수 있게 헬퍼함수들이 잘 구축되어있다..
    2. 이번에는 Redux 본연에 대해서 좀 더 깊게 알아보기 위해 toolkit을 쓰진 않았지만
    3. 현업에서 쓴다면 걍 toolkit 쓸 것 같다..!!
 
1-5) 기타
Redux test
  1. 정규표현식으로 테스트를 넣을 경우 포함된 것들을 모두 찾아주지만
  1. 문자열로 넣어주면 완성된 텍스트가 있는지를 찾아줌
    1. notion image
 
 

2. 회고

Redux 깊게 이해하기 및 test코드 작성 적응하기

  • redux를 깊게 알아보는데 도움이 되었다. 특히 기본 구조는 알고 있었지만,
  • switch,if를 사용하지 않고 각 action들을 호출하는 것을 공부하면서 리덕스 구조가 더 확 이해가 되었다.
  • 뿐만 아니라 redux-actions와 toolkit의 헬퍼가 어떤 부분을 편리하게 해주는지 알게 된 계기가 되었다.
  • redux 테스트 구조를 짜고보니 아직 속도는 빠르진 않지만 어느정도 적응이 되는 것 같다.
  • 그런데 시간이 너무 오래걸려서 이걸 현업에서 쓸 수 있는 수준이 되려면 어느정도 속도,생산성이 나와야되나 고민이다.
 

학습에 소홀했던 주간

  • 코드숨 과정을 하면서 그간 잘 안밀리면서 했는데, 한 주 동안 2곳의 면접과 주말 가족모임이 겹치게 되면서 이래저래 시간을 못썼다.
  • 사실 면접을 보고나서도 빨리 털어내고 할 것들을 했어야했는데, 고민과 후회가 머릿속에 떠나지 않아 집중이 잘 안됐다.
  • 집중하기 위해서 산책도하고, 차를 마셔보기도 했는데 좀 처럼 멘탈이 잘 잡히지 않는 걸 보니 부담감과 조급함이 계속 쌓여서 터진 것 같다. 그래서 일요일 과제마감이 되어서야 결국 코드리뷰 PR을 보내게 됐다.
  • 그래서 천천히 하자는 생각에 오늘은 좀 쉬다가 밤늦게서야 이렇게 회고를 쓰면서 다시 정신차리자는 다짐을 한다.
 

프로젝트 준비하기

  • 코드숨 뿐만 아니라 개인적으로 Ts를 학습하고 있는데 거의 끝나간다.
  • 이제 어느정도 테스트코드를 짜는 법을 알았으니 프로젝트를 하려고 한다.
  • 어떤 프로젝트를 하면 좋을지 5주차에는 고민을 해봐야겠다.