컴포넌트 내부함수? 외부함수?

송연지·2025년 1월 7일
0

트러블슈팅

목록 보기
13/32

1. 문제 정의

React 컴포넌트를 만들 때 가장 고민되는 부분:

  • 상태(state)를 어디서 관리해야 할까?
  • 함수들을 컴포넌트 내부에 둬야 할까, 외부에 둬야 할까?

2. 문제 상황

// 1. 여러 곳에서 재사용되는 Toast
function SomePage() {
// Toast를 여러 곳에서 각각 이렇게 만들어야 할까요?
  const [isOpen, setIsOpen] = useState(false);
  const closeToast = () => {
    setTimeout(() => setIsOpen(false), 3000);
  };
}

// 2. 여러 컴포넌트와 연결된 Tab
function TabPage() {
// Tab의 상태가 여러 컴포넌트에 영향을 미칩니다
  const [currentTab, setCurrentTab] = useState('home');
// API 호출도 필요하고// URL도 변경해야 하고// 다른 컴포넌트도 업데이트해야 하고...
}

3. 해결 방법

  • A. 내부 상태 관리가 좋은 경우

    // ✅ 좋은 예: 내부 상태 관리
    function Toast({ isOpen, message }) {
      const [isVisible, setIsVisible] = useState(false);
    
      useEffect(() => {
        if (isOpen) {
          setIsVisible(true);
          const timer = setTimeout(() => setIsVisible(false), 3000);
          return () => clearTimeout(timer);
        }
    	  }, [isOpen]);
    
      return isVisible ? <div>{message}</div> : null;
    }
    
    // 사용하기 쉽습니다
    <Toast message="성공!" />
    

    이렇게 하면 좋은 점:

    1. 사용하기 쉬워요 - 복잡한 설정 없이 바로 사용
    2. 실수할 일이 적어요 - 모든 동작이 내부에서 처리됨
    3. 코드 중복이 없어요 - 로직이 한 곳에만 있음
  • B. 외부 상태 관리가 좋은 경우

    // ✅ 좋은 예: 외부 상태 관리
    function Tab({ currentTab, onTabChange }) {
      return (
        <div>
          {items.map(item => (
            <button onClick={() => onTabChange(item.id)}>
              {item.label}
            </button>
          ))}
        </div>
      );
    }
    
    // 사용하는 쪽에서 필요한 모든 동작을 정의할 수 있어요
    function ParentPage() {
      const handleTabChange = async (tabId) => {
        setCurrentTab(tabId);
        await fetchData(tabId);
        updateURL(tabId);
      };
    
      return <Tab onTabChange={handleTabChange} />;
    }
    

    이렇게 하면 좋은 점:

    1. 원하는 대로 커스텀할 수 있어요
    2. 다른 기능과 연결하기 쉬워요
    3. 여러 컴포넌트와 상태를 공유할 수 있어요

4. 결론

  • 내부 상태 관리를 선택할 때(toast)
    // 어디서 써도 같은 방식으로 동작하는 Toast
    <Toast message="저장 완료!" />
    <Toast message="오류 발생!" />
    
    • 다른 컴포넌트와 소통이 거의 없을 때
    • 컴포넌트의 동작이 늘 같을 때
    • 재사용이 많이 필요할 때
  • 외부 상태 관리를 선택할 때(tab, alert)
    // 상황마다 다르게 동작하는 Alert
    <Alert onConfirm={handleDelete} />// 삭제 확인
    <Alert onConfirm={handleSave} />// 저장 확인
    <Alert onConfirm={handleLogout} />// 로그아웃 확인
    • 컴포넌트마다 다르게 동작해야 할 때
    • API 호출이나 데이터 처리가 필요할 때
    • 여러 컴포넌트가 같은 상태를 봐야할 때

5. 한 줄 요약

"상태를 어디서 관리할지는 컴포넌트의 독립성, 복잡성, 확장 가능성에 따라 결정된다!”

  1. 컴포넌트가 "혼자서도 잘 동작"하면 → 내부 상태
  2. 컴포넌트가 "다른 것과 소통"해야 하면 → 외부 상태

+++

🌟 1. 내부 상태 관리 (Toast)

  • 언제 적합한가?
    • 컴포넌트가 독립적이며, 부모나 다른 컴포넌트와의 의존성이 없을 때.
    • 동일한 패턴의 동작을 여러 곳에서 재사용할 때.

예시

  • Toast: 특정 이벤트 발생 시 메시지를 짧게 보여주는 데 최적.
  • Modal: 단순히 열리고 닫히는 기본 모달의 경우.

👍 추가 이점

  • 캡슐화: 내부 동작을 숨기고 단순한 API만 노출. 사용하기 쉬움.
  • 코드 중복 감소: 여러 곳에서 동일한 로직을 공유.
  • 설정 비용 감소: 상태를 부모 컴포넌트에서 관리하지 않으므로 코드가 간결.

🌟 2. 외부 상태 관리 (Tab, Alert)

  • 언제 적합한가?
    • 상태가 여러 컴포넌트에 영향을 미칠 때.
    • 사용자 입력, API 호출 등 외부 데이터와 연결될 때.
    • 컴포넌트의 동작이 상황에 따라 달라질 때.

예시

  • Tab: 탭 전환 시 데이터를 로드하거나 URL을 업데이트할 경우.
  • Alert: "확인" 버튼의 동작이 상황별로 다를 경우.

👍 추가 이점

  • 확장성: 컴포넌트를 다양한 상황에 맞게 재활용 가능.
  • 유연성: 부모 컴포넌트가 상태를 제어하기 때문에 더 복잡한 로직 추가 가능.
  • 테스트 용이: 외부에서 상태를 주입받아 테스트 시 mock 데이터를 쉽게 사용할 수 있음.

🤔 3. 추가 고려 사항

성능

  • 상태가 불필요하게 상위 컴포넌트로 끌어올려지면 리렌더링 비용 증가 가능.
  • 따라서, 외부 상태 관리가 필요 없으면 내부 상태 관리가 낫다.

사용 편의성

  • 내부 상태 관리: 빠르게 개발할 때 유리 (컴포넌트 자체 완결성).
  • 외부 상태 관리: 여러 컴포넌트가 협업해야 하는 프로젝트에서 필수.

전역 상태 관리 도구

  • Toast, Alert처럼 앱 전역에서 동작하는 요소는 Context API나 상태 관리 라이브러리(Redux, Zustand 등)를 사용하는 것도 고려 가능.
  • : ToastManagerAlertProvider를 만들어 전역적으로 상태 관리.

💡 결론

  • 내부 상태 관리는 독립적이고 재사용성이 중요할 때 사용.
  • 외부 상태 관리는 유연성과 확장성이 중요할 때 사용.

적용 예시

  • Toast → 내부 상태
  • Tab, Alert → 외부 상태 (상황에 따라 전역 상태도 고려 가능)
profile
프론트엔드 개발쟈!!

0개의 댓글