[Redux] 왜 redux를 사용해야 할까? (feat. 검색 기능)

김뀨뀨·2023년 2월 11일
2

💬 최근 몇달간 React로 본격적으로 웹 FE를 개발하며 하며 느낀 라이브러리 사용, 상태 관리에 관한 생각들을 정리해보려고 한다.

리액트 상태 관리

⚛️ 리액트로 웹 개발을 하다보면 가장 많이 신경쓰게 되는 것 중 하나가 상태, 즉 state 관리이다. state는 변할 수 있는 정보를 담고 있고, React는 이 state의 변화에 따라 리렌더링을 한다. 하지만 이 리렌더링 시점은 개발자가 정확히 예측할 수 없고, 통제가 어려워 이런 점을 React의 단점으로 얘기하기도 한다.

프로젝트 규모가 커지면 수많은 state들이 생기고, 컴포넌트끼리 중첩되면서 필요한 state들을 아래로 내려주게 된다. (= props drilling을 한다. 참고로 state의 흐름은 단방향이기 때문에 하위 컴포넌트에서 상위 컴포넌트의 state에 접근할 수 없다. )

하지만 예를 들어 가장 하위의 컴포넌트에서 최상위 컴포넌트에 있는 state가 필요하다면? 🧐
매우 많은 props drilling이 발생하고 이는 당연히 효율적이지 못한 방법이다. 따라서 React로 개발을 할 때 상태를 관리해주는 라이브러리를 함께 쓰는 방법이 존재한다.

다양한 상태 관리 툴들이 존재하는데,
https://medium.com/@pitis.radu/react-state-management-in-2022-345c87922479 작년 7월 작성된 게시글에서 리액트 상태 관리 라이브러리의 발전 과정을 소개하고 있다.

Redux 외에도 MobX, Zustand, Recoil 등등이 존재하고 주위에서 본 대학생, 취준생, 주니어 레벨 프로젝트에서는 굳이 사용하지 않거나 거의 Redux / Recoil을 채택하는 것 같았다.

Redux

그래서 일단 만만한게 Redux라고 StackOverflow 클론 코딩을 할 때 팀원들과 상태 관리 툴로는 Redux를 사용하기로 했다.

여기서 Redux에 대해 알아야할 것이 있는데 state를 관리해준다고 해서 Redux가 마냥 좋은 것이 아니다.

1) Action, Dispatch, Store가 연계해 동작하는 다소 헷갈리는 패턴을 알고 있어야 하고,
2) 보일러 플레이트 코드가 발생해서 프로젝트가 무거워진다.

따라서! 상태 관리 라이브러리로 Redux를 사용한다면 Redux에 대한 이해와 왜 사용해야하는지 타당한 이유가 필요할 것이다.

그러나! StackOverflow 클론 프로젝트 코드를 이제와 다시 보니 왜 그렇게 했는지 의문투성이이다.
굳이 redux의 상태 저장소인 store에 저장할 필요가 없는 정보들까지 모두 state에 저장하고 있고, axios 요청도 redux 코드 안에서 하고 있는 요상한🤢 방식을 사용했었다.

기술 스택에 대한 충분한 이해가 부족한 상태에서 성급하게 도입해서 그렇지 않았나.. 뒤늦게라도 반성해본다.

Redux Toolkit

어쨌든 이어서 좀 더 규모가 있고, 자체적으로 기획한 서비스를 개발하게 되는데, 이번에는 Redux의 단점을 보완해줄 수 있는 Redux-Toolkit을 사용해보기로 한다.

장점

Redux-Toolkit

  • Redux보다 간편한 설정으로 사용할 수 있고,
  • Redux를 사용할 때 따로 설치해야 하는 패키지들을 내부적으로 포함하고 있으며,
  • 보일러 플레이트 코드가 줄어들고,
  • 불변(immutable)한 업데이트가 가능하다.

아까 보았던 Redux의 flux 구조인데, Redux를 사용한 코드에서는 action, reducer, store가 따로 존재한다면, Redux-Toolkit은 Slice라는 구조 안에 이것들을 모두 담고 있다. 당연히 코드의 양 자체도 줄고 사용하기도 편하다.

불변성 지원

내가 Redux-Toolkit을 사용하자고 제안한 가장 큰 이유이다.

...
case POST_QUESTION_COMMENT:
      return {
        ...state,
        data: {
          ...state.data,
          commentsWithUser: [
            ...state.data.commentsWithUser,
            action.payload.data,
          ],
        },
      };
...

실제 StackOverflow 클론 프로젝트의 코드인데, Redux는 불변성을 지원하지 않기 때문에 store에 저장한 state를 바꿀 때 위처럼 중첩된 모든 단계에서 복사가 필요하다. (이것 때문에 꽤 골치아팠다.)

그리고 이런 방법은 개발자의 실수를 유발하고 보일러 플레이트 코드 또한 발생시킨다.

따라서 Redux-Toolkit은 내부적으로 immer 라이브러리를 사용해 mutative한 코드 형태로 작성해도 불변한 업데이트가 이루어지게 한다.

...
builder.addCase(asyncPostPairingComment.fulfilled, (state, action) => {
      state.data.pairingRes.comments.push(action.payload);
      state.status = 'fulfilled';
    });
...

Redux-Toolkit을 사용한 프로젝트의 실제 코드인데, push와 같은 메서드의 사용이 가능해진다.

검색 기능 개발기

그래도 어쨌든 따로 라이브러리를 사용해 state를 관리한다는 것 자체가 비용이 드는 일이기 때문에 🍒체리픽 서비스를 개발하면서는 redux-toolkit 사용에 대해 고민하고 불필요한 사용은 줄이고자 노력했다.

그래서 멘토님께 어떤 state를 Redux로 관리해야하는가 를 질문 드렸었는데,
모든 컴포넌트들이 알아야하는 전역적인 state - 예를 들어 다크모드/라이트모드 라던가 isLogin 여부 같은 정보가 아니면 굳이 Redux로 관리할 필요가 없지 않느냐고 말씀하셨다.
(이때도 아하 모먼트가 있었다.)

하지만 머리로는 이해하고 마음으로 이해하고 있지는 못하던 와중에, 검색 기능을 개발하다 문제가 생겼다.

내가 개발하려고 하는 것은 대략 이런 구조의 검색 결과 페이지였는데, 처음에는 단순한 방법을 사용했다.

검색창(Searchbar) 컴포넌트에서 검색어 state를 갖고 있고,
→ 엔터를 클릭하면 useNavigate로 검색어 결과 페이지로 이동한다.
→ 검색어는 useParams를 이용해 url에 있는 파라미터를 가져와서 사용한다.

🚨 하지만 위의 방식으로 구현했을 때 문제가 있었다.


↑ 프로젝트 당시 팀원분이 수정 요청을 하시면서 친절하게 보내주신 움짤이다..!!
검색창에 있던 검색어 state가 사라져서 이상하게 동작한다.

검색어 state를 Searchbar 컴포넌트에서 지역적으로 갖고 있기 때문에 검색 결과 페이지 컴포넌트의 동작과 연계해서 state를 제어하기가 어려웠다.

또, 검색어 페이지 -> 다른 페이지로 이동하는 경우 등등에도 검색창에 검색어가 그대로 남아있거나 하는 문제가 발생하고,
이는 사용자 경험, UX 관점에서 매우매우 좋지 않다고 생각했다.

💡 그래서 검색어 state를 redux의 store에 넣어서 전역적으로 관리하는 방법을 선택했다.

searchSlice.js

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  keyword: '',
  mode: false,
};

export const searchSlice = createSlice({
  name: 'search',
  initialState,
  reducers: {
    setSearchKeyword: (state, { payload }) => {
      state.keyword = payload.keyword;
    },
    setSearchMode: (state, { payload }) => {
      state.mode = payload.mode;
    },
  },
});

export const { setSearchKeyword, setSearchMode } = searchSlice.actions;

export default searchSlice.reducer;

export const selectSearchKeyword = (state) => state.search.keyword;
export const selectSearchMode = (state) => state.search.mode;
  • input창에서 바뀌는 검색어인 keyword와, 검색 중인지 여부를 판단하는 mode를 searchSlice의 store에 저장하고 있다.
  • 검색 mode를 따로 관리하는 이유는
    검색어를 입력 -> 엔터를 눌렀을 때 검색이 다시 이루어지고 검색어 페이지가 리렌더링이 일어나야 하기 때문이다. (아니면 input의 value가 바뀔 때마다 검색이 됨)

🔍 이렇게 해서 결과적으로 스무스하게 동작하는 검색 기능을 구현할 수 있었다!
(https://cherry-pick.co.kr/ -> 여기서 확인 가능 : )

라이브러리 사용에 관한 고찰

🍒 CherryPick 프로젝트에서는 상태관리 라이브러리로 Redux, Redux-Toolkit을 사용하면서 로그인 여부, 닉네임, 이메일, 프로필 이미지 등 사용자 정보를 store에 담아 관리했다.

사용자 인증 등을 FE 에이스 팀원분이 담당하셨는데, 최근하고있는 프로젝트에서 팀원분이 작성하셨던 코드를 참고하면서 위와 같은 방식으로 구현해보려고 하고 있다.

🤔 근데 현재 프로젝트를 같이 하는 팀원이 그 전 프로젝트에서 상태관리 라이브러리 없이 React로 개발을 했다고 해서 어떤 방식으로 했는지 코드를 보여달라고 했다!!
util 폴더에 api request를 잘 정리해 놓고 필요할 때마다 서버에 정보를 요청해서 받아오는 방식이었던걸로 기억한다.

규모가 아주 큰 프로젝트가 아니라면 굳이 Redux의 틀에 갇힐 필요도 없고, 상태관리 라이브러리를 사용해야만 한다고 생각할 필요도 없는 것 같다는 생각이 들었다.
(사실 html, css, javascript만으로도 모든 걸 만들 수 있으니까..!🫢 )

어떤 기술 스택을 채택할 것인가..!

여기까지 본격적으로 웹 FE 개발을 공부하고, 프로젝트를 하면서 상태관리 라이브러리, Redux에 관해 느꼈던, 배웠던 것들을 정리해 보았다.

면접에서 프로젝트에 대해 설명할 때 왜 이 기술 스택을 사용했는지 설명하는게 중요한 부분이라고 들었는데,
그럴 수 있으려면 개발하는 서비스와 기술스택에 대한 충분한 이해와 고민의 시간이 필요하다고 느꼈다. 중요한만큼 어려운 부분인 것 같다. 🥲

이 글을 누가 읽을지는 모르겠지만 몇개월간 배웠던 것들을 정리해서 기록해본다!!

profile
개발로 밥벌이 하고 싶은 사람

0개의 댓글