디자인 패턴 (C&P , Custom-hook)

robin Han·2025년 8월 27일
1

Presentation & Container

Presentation

  • UI에만 집중
  • 데이터를 직접 관리하지 않고 props로 받음
  • 상태(state)는 거의 없음, 있더라도 UI 관련 로컬 상태만 사용
import { ArrowLeft, Dot } from 'lucide-react';
import { twMerge } from 'tailwind-merge';
import Link from 'next/link';

interface StepperProps {
  label: string;
  step: number;
}

export default function Stepper({ label, step }: StepperProps) {
  return (
    <div className="flex h-8 w-full flex-row items-center justify-between">
      <Link href="/">
        <ArrowLeft className="m-1 size-6" />
      </Link>
      <div className="text-BodyMD text-text-normal">{label}</div>
      <div className="flex flex-row items-center gap-2">
        <Dot
          className={`bg-primary size-2.5 rounded-full text-transparent ${
            step === 0 &&
            'shadow-[0_0_10px_0_rgba(92,255,241,0.70)] outline-1 outline-[#96AAFF]'
          }`}
        />
    /..../
      </div>
    </div>
  );
}

Container

  • 데이터 관리와 로직 담당
  • API 호출, 상태 관리, 이벤트 핸들링 등 수행
  • Presentation 컴포넌트를 포함하여 props로 데이터 전달
export default function BudgetPage() {
  const [step, setStep]= useState(0)
   const fetchUsers = async () => {
      const response = await fetch("https://fetchStep");
      const data = await response.json();
      setStep(data.step);
  };
  return(
      <Stepper label={'Stepper'} step={step} />
  )
}

단점 :

  • 상위 컴포넌트 의존
  • 재사용성 낮음
  • 컴포넌트 크기 커짐

Custom Hook

import { useState, useEffect } from "react";

export function useDebounce<T>(value: T, delay: number) {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debounced;
}

// 사용
const debouncedSearch = useDebounce(searchTerm, 300);
  • 재사용성 높음

단점 :

  • 추상화로 인해 이해 어려움
  • 과도한 의존성

Hook Rule

  • Hook은 React 함수 컴포넌트 또는 커스텀 훅에서만 호출
  • Hook은 항상 같은 순서로 호출
  • Hook 이름은 use로 시작
  • Hook은 컴포넌트 최상단에서 호출
  • 의존성 배열 관리 필수

0개의 댓글