프로젝트(fitmon) 진행 중 zustand store을 어떤거에 적용하는게 좋을까라는 의문점 정리

송연지·2025년 1월 17일
1

트러블슈팅

목록 보기
15/32

Toast와 Modal 상태 관리 개선안 (zustand 사용 사례 포함)

1. Toast 상태 관리

현재 상황

  • 특정 작업(예: 수정/삭제)에서는 탭 내에서만 Toast가 표시되어야 함.
  • 404 오류, 네트워크 오류 등전역적으로 Toast가 표시되는 것이 적합함.
  • 상태 관리와 타이머 로직을 효율적으로 관리할 필요가 있음.

결론: Toast 상태는 전역 관리로 통합

전역 관리로 통합하는 이유

  1. 혼동 방지:
    • Toast를 로컬과 전역 상태로 혼합해서 관리하면 개발자가 상태 관리 방식을 파악하기 어렵고, 유지보수성이 떨어질 수 있음.
    • 전역 상태로 통합하면 모든 Toast를 한 곳에서 일관되게 관리 가능.
  2. 일관성 유지:
    • 전역 상태로 관리하면 Toast 메시지 추가 및 제거, 상태 추적이 단순해짐.
    • 네트워크 오류와 같은 공통 이벤트를 처리하기 쉬워짐.
  3. 확장성:
    • 전역 상태로 관리하면 다양한 컴포넌트에서 Toast를 쉽게 트리거할 수 있음.
    • 예를 들어, 여러 네트워크 요청의 성공/실패 메시지를 단일 상태에서 처리 가능.

타이머 관리 방법: Toast 컴포넌트 내부에서 처리

Toast 컴포넌트에서 타이머를 관리하는 이유

  1. 응집도 향상:
    • 타이머 로직이 Toast 컴포넌트와 결합되어 UI와 동작이 밀접하게 연결됨.
    • Toast가 표시될 때, 지속 시간에 따라 자동으로 사라지는 동작을 Toast 컴포넌트 자체에서 관리 가능.
  2. 유연성:
    • duration을 prop으로 받아 Toast별 지속 시간을 다르게 설정할 수 있음.
    • 사용자 정의 애니메이션과 쉽게 통합 가능.
  3. 자동화:
    • 컴포넌트가 언마운트될 때 타이머가 자동으로 정리되므로 메모리 누수 방지.
    • 별도로 타이머 해제 로직을 작성할 필요가 없음.

왜 zustand에서 타이머를 관리하지 않는가?

  1. 상태와 동작의 분리:
    • zustand는 상태 관리를 담당하며, 타이머와 같은 UI 동작 로직을 zustand에 포함시키면 응집도가 떨어짐.
    • UI와 동작을 분리하면 코드 가독성과 유지보수성이 저하될 수 있음.
  2. 언마운트 시 문제:
    • zustand에서 타이머를 관리하면 컴포넌트가 언마운트될 때 타이머가 제대로 정리되지 않을 위험이 있음.
    • Toast 컴포넌트 내부에서 타이머를 관리하면 이런 문제를 방지할 수 있음.

zustand를 사용한 Toast 상태 관리 예시

import create from 'zustand';

// Toast 상태 정의
interface ToastState {
  toasts: { id: string; message: string; type: 'success' | 'error'; duration: number }[];
  addToast: (message: string, type: 'success' | 'error', duration?: number) => void;
  removeToast: (id: string) => void;
}

export const useToastStore = create<ToastState>((set) => ({
  toasts: [],
  addToast: (message, type, duration = 3000) => {
    const id = Date.now().toString(); // 고유 ID 생성
    set((state) => ({
      toasts: [...state.toasts, { id, message, type, duration }],
    }));
    // duration에 따라 자동 삭제
    setTimeout(() => {
      set((state) => ({
        toasts: state.toasts.filter((toast) => toast.id !== id),
      }));
    }, duration);
  },
  removeToast: (id) =>
    set((state) => ({
      toasts: state.toasts.filter((toast) => toast.id !== id),
    })),
}));

Toast 컴포넌트

tsx
코드 복사
import React from 'react';
import { useToastStore } from './toastStore';

export const ToastContainer = () => {
  const toasts = useToastStore((state) => state.toasts);
  const removeToast = useToastStore((state) => state.removeToast);

  return (
    <div className="toast-container">
      {toasts.map((toast) => (
        <div
          key={toast.id}
          className={`toast ${toast.type}`}
          onClick={() => removeToast(toast.id)}
        >
          {toast.message}
        </div>
      ))}
    </div>
  );
};

2. Modal 상태 관리

현재 상황

  • zustand를 사용하여 modalKey를 활용해 다중 모달을 관리 중.
  • 모달 개수가 많아질수록 관리 복잡도가 증가할 가능성 있음.

결론: 모달은 각각 로컬 상태로 관리

로컬 상태 관리 선택 이유

  1. 현재 컴포넌트 구조가 단순함:
    • 모달이 복잡하거나 다단계의 부모-자식 관계로 구성되지 않았음.
    • 각 모달이 독립적으로 작동하기 때문에 로컬 상태로 관리하는 것이 더 직관적임.
  2. 상태 충돌 방지:
    • 모달이 전역 상태로 관리되면 의도치 않은 상태 충돌 가능성이 있음.
    • 로컬 상태로 관리하면 모달 간 상태가 독립적으로 유지됨.
  3. 유지보수성 향상:
    • 각 모달의 로직이 해당 컴포넌트 내부에 캡슐화되어 관리가 쉬워짐.
    • zustand로 전역 관리를 하게 되면 모달 개수가 많아질수록 상태 관리 로직이 복잡해질 수 있음.

zustand 대신 로컬 상태 관리 예시

import React, { useState } from 'react';

export const MyModal = () => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open Modal</button>
      {isOpen && (
        <div className="modal">
          <p>This is a modal</p>
          <button onClick={() => setIsOpen(false)}>Close Modal</button>
        </div>
      )}
    </>
  );
};

Context API를 사용하지 않는 이유

  • Context API의 특성:
    • 부모-자식 관계가 깊거나 모달이 여러 계층에서 재사용되는 경우 적합.
    • 하지만 현재는 계층 구조가 단순하기 때문에 굳이 Context API를 사용할 필요가 없음.
  • 현재 방식의 이점:
    • 컴포넌트별로 상태를 독립적으로 관리할 수 있어 더 간단하고 직관적임.

결론 요약

Toast 상태 관리

  • zustand로 전역 관리: 모든 Toast를 전역 상태로 관리하여 일관성을 유지.
  • 타이머는 Toast 컴포넌트 내부에서 관리: UI 로직과 밀접한 동작은 컴포넌트 내부에서 처리.
  • 로컬 상태로 관리: 현재 컴포넌트 구조가 단순하므로 각 모달을 로컬 상태로 관리.
  • Context API는 도입하지 않음: 계층 구조가 단순한 상황에서는 필요 없음.

이러한 의문점은 계속 가져야하며 프론트엔드의 숙명!!!!!!!! 어떻게 하면 더 효율적일지 계속 생각해가자

profile
프론트엔드 개발쟈!!

0개의 댓글