React Hooks로 Receipto의 상태 관리하기

soleil_lucy·2025년 6월 15일
2

소개

Receipto는 총무가 모임에서의 결제 내역을 체계적으로 정리하고 공유할 수 있도록 도와주는 웹 애플리케이션입니다. 이번 글에서는 Receipto에 필요한 상태를 React Hooks를 통해 관리하는 방법에 대해 작성해보았습니다.

Receipto의 요구사항

Receipto의 요구사항은 다음과 같습니다:

  • 총무가 모임에서 사용한 결제 내용, 금액, 인원 수, 날짜, 제목을 입력해야 합니다.
  • 입력한 내용을 기반으로 결제 내역을 조회합니다.
  • 총 결제 금액을 계산합니다.
  • 1/N로 금액을 나눕니다.
  • 모임에 참여한 분들에게 공유할 내역을 생성합니다.

Receipto UI

이해를 돕기 위해 현재 구현된 UI 스크린샷을 첨부합니다.

Receipto UI(제목, 날짜, 결제 내역 추가가 보임)

Receipto UI(결제 내역, 인원 수 입력, 정산 결과, 공유하기 버튼이 보임)

상태 정의하기

위 요구사항을 만족하기 위해 다음과 같은 상태가 필요하겠다고 생각했습니다:

  • 제목: 모임을 식별하기 위한 제목입니다.
  • 날짜: 모임이 진행된 날짜입니다.
  • 결제 내역: 모임에서의 결제 내역입니다.
  • 인원 수: 모임에 참여한 인원의 수입니다.

TypeScript의 interface로 정의:

interface PaymentHistory {
  id: string;
  content: string;
  amount: number;
}

interface Receipt {
  title: string;
  date: Date | undefined;
  histories: PaymentHistory[];
  peopleCount: number;
}

React Hook 선택: useState vs useReducer

useState

useState는 React 함수형 컴포넌트에서 가장 기본적으로 사용하는 상태 관리 Hook입니다. 컴포넌트 내부에서 간단하게 상태 값을 선언하고, 그 값을 변경할 수 있는 setter 함수를 제공합니다.

useState는 배열을 반환하며, 첫 번째 요소는 현재 상태 값, 두 번째 요소는 상태를 변경하는 함수(set 함수)입니다. 상태 변경 시 set 함수를 호출하면 React가 상태를 갱신하고 컴포넌트를 다시 렌더링합니다.

예시:

import { useState } from 'react';

export default function CounterComponent() {
  const [count, setCount] = useState(0);

  // ...
}

useReducer

useReduceruseState와 유사하게 상태를 관리하지만, 상태 변경 로직을 컴포넌트 외부의 reducer 함수로 분리할 수 있습니다. 복잡한 상태(여러 하위 값을 포함하거나, 상태 변경 로직이 복잡한 경우) 관리에 적합합니다.

useReducer[state, dispatch] 형태의 배열을 반환합니다. 상태를 변경하려면 dispatch 함수에 action 객체를 전달합니다. 이 action은 reducer 함수에서 처리되어 새로운 상태를 반환합니다.

예시:

import { useReducer } from 'react';

function reducer(state, action) {
  if (action.type === 'incremented_age') {
    return {
      age: state.age + 1
    };
  }
  throw Error('Unknown action.');
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });

  // ...
}

useState와 useReducer의 차이점

구분useStateuseReducer
사용 목적단순한 상태(숫자, 문자열 등) 관리복잡한 상태(여러 값, 복잡한 변경 로직) 관리
상태 변경 방식set 함수로 직접 변경dispatch로 action을 전달, reducer 함수에서 처리
로직 위치컴포넌트 내부에 상태 변경 로직이 위치상태 변경 로직을 reducer 함수로 분리 가능
코드 구조간단하고 직관적임구조적이고 일관성 있게 복잡한 로직 관리 가능

useReducer 훅을 사용하여 Receipto 상태 관리

useStateuseReducer 훅의 정의 및 차이를 공부한 결과, Receipto에서는 단순한 상태보다 복잡한 상태(Receipto에서는 여러 값을 사용하여 복잡한 상태라고 생각)를 관리해야 하므로 useReducer를 선택하는 것이 적합하다고 판단했습니다.

실제 적용해보기: useReducer로 Receipto 상태 관리하기

Reducer 함수 정의

먼저, 상태의 업데이트를 처리하는 reducer 함수를 정의합니다. 각 액션 타입에 따라 상태를 어떻게 변화시킬지 설정합니다.

function reducer(state: Receipt, action: PaymentAction): Payment {
  const { type, payment } = action;
  switch (type) {
    case 'CHANGED_TITLE':
      return { ...state, title: payment.title };
    case 'CHANGED_DATE':
      return { ...state, date: payment.date };
    case 'ADD_PAYMENT_HISTORY':
      return { ...state, histories: [...state.histories, payment.history] };
    case 'DELETE_PAYMENT_HISTORY':
      return {
        ...state,
        histories: state.histories.filter(
          (history) => history.id !== payment.history.id,
        ),
      };
    case 'CHANGED_PEOPLE_COUNT':
      return { ...state, peopleCount: payment.peopleCount };
    default:
      return state;
  }
}

초기값 및 훅 설정

useReducer를 통해 초기 상태를 설정하고, 상태 변경을 위한 dispatch 함수를 얻습니다.

const [payment, dispatch] = useReducer(reducer, {
  title: '',
  date: undefined,
  histories: [],
  peopleCount: 0,
});

상태 업데이트 및 컴포넌트 적용

컴포넌트 내에서 dispatch를 사용하여 상태를 업데이트합니다.

예를 들어, 제목이 변경될 때 dispatch를 호출하여 변경된 상태를 반영합니다.

<Input
  id="title"
  value={title}
  onChange={(e) =>
    dispatch({
      type: 'CHANGED_TITLE',
      payment: { title: e.target.value },
    })
  }
/>

회고

이번 글을 통해 React Hooks 중 상태를 관리할 때 사용하는 useStateuseReducer의 차이에 대해 공부할 수 있었습니다. useState는 단순한 상태(숫자, 문자열 등)를 관리할 때 유용하며, useReducer는 여러 값이나 복잡한 변경 로직이 필요한 복잡한 상태를 관리하기에 적합하다는 것을 배웠습니다.

useState가 익숙했던 터라 useReducer가 복잡할 것이라 생각했지만, 실제로 사용해 보니 상대적으로 코드가 복잡하지만 상태를 오히려 더 깔끔하게 관리할 수 있는 훅이라고 생각했습니다. useReducer를 통해 액션 타입으로 상태 업데이트 로직을 명확하게 표현할 수 있어 어떤 상태의 값이 바뀌는가를 쉽게 파악할 수 있었습니다.

다음에는 Receipto의 UX 개선과 코드 리팩토링, 그리고 최적화 작업에도 도전해 보고자 합니다.

참고 자료

profile
여행과 책을 좋아하는 개발자입니다.

0개의 댓글