1014 TIL-U

Lilac00xx·2024년 10월 14일

Section 20 고급 리덕스

TIL1) 리덕스와 함께 useEffect 사용하기

리덕스와 useEffect의 조합은 비동기 데이터를 관리할 때 자주 사용된다. 일반적으로 리덕스는 상태 관리 도구로 전역 상태를 관리하고, useEffect는 컴포넌트가 마운트되거나 특정 상태가 변경될 때 비동기 작업을 수행하는 훅. 이 둘을 조합하면 리덕스 상태를 기반으로 컴포넌트가 비동기 작업을 수행할 수 있다.

리덕스의 역할: 애플리케이션 전역 상태를 관리하고, 그 상태를 바탕으로 컴포넌트가 렌더링된다.
useEffect의 역할: 리덕스 액션이 디스패치되면 비동기 작업(예: API 호출)을 트리거할 수 있다. 데이터를 가져오고, 결과를 기반으로 리덕스 상태를 업데이트하는 패턴이 일반적이다.

import { useDispatch, useSelector } from 'react-redux';
import { useEffect } from 'react';
import { fetchData } from './actions';

function MyComponent() {
  const dispatch = useDispatch();
  const data = useSelector(state => state.data);

  useEffect(() => {
    dispatch(fetchData()); // 컴포넌트가 마운트될 때 데이터를 가져오는 액션을 디스패치
  }, [dispatch]);

  return (
    <div>{data && data.length ? <DataList data={data} /> : 'Loading...'}</div>
  );
}

알수있는것

useEffect에서 리덕스 액션을 디스패치하여 비동기 데이터 요청을 처리한다.
리덕스 스토어에서 데이터를 가져오고, 컴포넌트의 렌더링을 트리거한다.
리덕스 상태가 변하면, 해당 변화를 감지하여 컴포넌트가 다시 렌더링된다.

TIL2) 리덕스로 Http State 및 피드백 처리하기

리덕스는 HTTP 요청 상태를 처리하는 데 매우 유용하다. 비동기 요청을 할 때 로딩, 성공, 실패 상태를 전역 상태로 관리하여 피드백을 사용자에게 적절히 제공할 수 있다.

요청 상태: 요청이 시작되었는지(로딩 상태), 성공했는지, 실패했는지에 대한 상태를 리덕스에서 관리한다.
피드백 제공: 이를 기반으로 사용자는 "로딩 중", "성공", "실패" 등의 메시지를 볼 수 있게 된다.

// actions.js
export const fetchData = () => async (dispatch) => {
  dispatch({ type: 'FETCH_START' });
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    dispatch({ type: 'FETCH_SUCCESS', payload: data });
  } catch (error) {
    dispatch({ type: 'FETCH_FAILURE', error });
  }
};

// reducer.js
const initialState = {
  data: null,
  loading: false,
  error: null,
};

export const dataReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'FETCH_START':
      return { ...state, loading: true, error: null };
    case 'FETCH_SUCCESS':
      return { ...state, loading: false, data: action.payload };
    case 'FETCH_FAILURE':
      return { ...state, loading: false, error: action.error };
    default:
      return state;
  }
};

알수있는것

리덕스는 요청의 시작, 성공, 실패 상태를 관리하여 피드백을 효과적으로 제공
로딩 상태를 통해 사용자는 응답을 기다리는 동안 진행 상황을 확인 할수있다.
에러 상태에서는 적절한 에러 메시지를 통해 사용자가 문제를 인식할 수 있도록 도와준다.

TIL3) 액션 생성자 Thunk 사용하기

Thunk는 비동기 작업을 다루기 위해 리덕스에서 많이 사용되는 미들웨어이다. Thunk를 사용하면 리덕스에서 비동기 코드를 더 간단하고 효율적으로 작성할 수 있다.

Thunk의 기본 아이디어: 액션 생성자가 함수를 반환하고, 이 함수가 디스패치할 액션을 조건에 따라 동적으로 결정한다.
비동기 작업 처리: API 호출과 같은 비동기 작업을 처리하고, 해당 작업이 완료되었을 때 상태를 업데이트하는 액션을 디스패치한다.

// actionCreators.js
export const fetchData = () => {
  return async (dispatch) => {
    dispatch({ type: 'FETCH_START' });
    try {
      const response = await fetch('/api/data');
      const data = await response.json();
      dispatch({ type: 'FETCH_SUCCESS', payload: data });
    } catch (error) {
      dispatch({ type: 'FETCH_FAILURE', error });
    }
  };
};

Learned

지난 19 섹션에서는 리덕스 기초를 다루면서 리덕스의 기본 개념과 액션 등 구성 요소를 배웠습니다. 특히 상태를 중앙에서 관리하는 것이 컴포넌트 간의 상태 전달을 보다 명확하게 해준다는 점을 학습하면서 리덕스의 강력한 이점을 실감할 수 있었다. 하지만, 이번 20 섹션에서 다룬 고급 리덕스는 기초를 넘어서며 확실히 난이도가 더 있었다.

특히 리덕스를 비동기 작업과 결합하는 과정에서 어려움이 있었다. useEffect와 리덕스를 조합하는 방식은 흔하게 쓰이는 패턴이지만, 언제 데이터를 불러오고 리덕스 스토어에 업데이트해야 할지, 컴포넌트의 마운트 시점과 상태 업데이트 간의 상호작용을 고려하는 부분이 까다로웠다.

예를 들어, 컴포넌트가 마운트될 때 useEffect를 이용해 데이터를 가져오는 경우, 의존성 배열에 대해 신경을 써야 했다. 만약 이 배열을 잘못 설정하면 불필요하게 비동기 요청이 여러 번 발생하거나, 리덕스 상태가 원하는 타이밍에 갱신되지 않는 문제가 생길 수 있기 때문이다.

의존성 배열과 디스패치: 가장 먼저 고려한 것은 useEffect의 의존성 배열. 이 배열을 빈 배열 []로 설정하면 컴포넌트가 처음 마운트될 때만 한 번 실행되므로, 불필요한 데이터 요청이 발생하지 않는다.그리고 이 안에서 리덕스 액션을 디스패치하여 데이터를 가져오도록 설정했다.

질문거리

Thunk는 리덕스 미들웨어 중 하나로 비동기 작업을 처리하는 데 매우 유용하지만, 최근에 많이 언급되는 리덕스 사가(Redux Saga)와의 차이점에 대해 궁금하다.

특히 Thunk와 달리 Saga는 '효과'를 관리하는데, 이를 통해 더 나은 성능을 얻거나 코드 유지보수성이 개선되는지. Thunk에서 Saga로 전환해야 하는 기준? 상황이 있는지 궁금합니당

비동기 작업의 결과에 따라 리덕스 액션이 디스패치되는데, 특정 상태 변화에 따라 useEffect가 다시 실행되어 무한 루프에 빠지지 않도록 하려면 어떤 전략을 쓰는 것이 좋을까요? 의존성 배열의 설정과 비동기 작업의 흐름을 관리하는 팁! 이 있을까요~!


profile
Challenge & Change

1개의 댓글

comment-user-thumbnail
2024년 10월 17일

저도 react-query를 이용한 이후에는 redux 미들웨어는 사용한 적이 없어서 잘 기억은 안 나네요.

Thunk와 Saga는 큰 틀(사용 의도 등)에서는 문법에 차이가 있는 것 외에 큰 차이는 없는 것으로 알고 있습니다.
다만 기존 redux의 철학에 더 맞는 쪽은 Saga라고 할 수 있는데, 기존 redux의 action은 비동기 작업이나 다른 side effect에 해당하는 '효과'를 허용하지 않기 때문에 action과 비동기 로직이 확실히 분리되어 있는 Saga 쪽이 Thunk에 비해 명확한 로직을 가지고 있다고 할 수 있을 거 같아요.

둘 다 같은 내용을 작성할 수 있지만, Thunk는 단순히 action이 객체가 아닌 함수를 리턴할 수 있도록 돕는 역할이라면 Saga는 상태관리와 비동기 작업을 통합하기 위해 필요한 다른 기능들도 제공하기 때문에 복잡한 기능 구현을 하기 위해서는 Saga를 이용하는 게 작성해야 하는 코드량은 더 적습니다. 하지만 라이브러리가 제공하는 기능들에 대한 이해가 있어야 작성하거나 읽을 수 있기 때문에 이런 부분은 고려하면 좋을 거 같아요.

useEffect에서 상태 변화가 무한 루프를 야기하는 걸 방지하기 위해서는 dependency array에 포함된 state를 hook 내부에서 변경하는(직접 변경하지 않더라도 side effect에 의해 변경되는) 일이 없게 조심하는 게 필요할 거 같네요!

답글 달기