본문 바로가기
프로그래밍/react.js

고차 컴포넌트(Higher-Order Component, HOC)

by freeelifee 2025. 4. 14.
728x90

리액트(React)에서 고차 함수(Higher-Order Function)는 주로 고차 컴포넌트(Higher-Order Component, HOC) 패턴으로 사용됩니다. HOC는 컴포넌트를 매개변수로 받아 새로운 컴포넌트를 반환하는 함수입니다. 이는 컴포넌트 로직을 재사용하기 위해 React에서 자주 사용되는 패턴입니다.


고차 컴포넌트(HOC)의 간단한 예제

다음은 사용자 인증 상태에 따라 특정 컴포넌트를 보호하는 HOC를 구현한 예제입니다.

import React from "react";

// 고차 컴포넌트 정의
function withAuth(WrappedComponent) {
    return function ProtectedComponent(props) {
        const isAuthenticated = props.isAuthenticated; // 인증 상태 확인

        if (!isAuthenticated) {
            return <div>접근할 권한이 없습니다. 로그인하세요.</div>;
        }

        return <WrappedComponent {...props} />; // 원래 컴포넌트 반환
    };
}

// 사용 예제
function Dashboard(props) {
    return <div>환영합니다, {props.user}님!</div>;
}

// Dashboard 컴포넌트를 보호
const ProtectedDashboard = withAuth(Dashboard);

// 애플리케이션에서 사용
export default function App() {
    return (
        <div>
            <ProtectedDashboard isAuthenticated={true} user="홍길동" />
            {/* 위 isAuthenticated를 false로 변경하면 접근 불가 메시지가 출력됨 */}
        </div>
    );
}

작동 원리

  1. withAuth 함수는 다른 컴포넌트를 매개변수로 받습니다(WrappedComponent).
  2. 반환된 함수(ProtectedComponent)는 원래 컴포넌트를 렌더링하거나, 조건에 따라 다른 JSX를 반환합니다.
  3. 전달된 props는 ...props를 통해 원래 컴포넌트에 그대로 전달됩니다.

응용: 데이터 가져오기 로직의 재사용

HOC를 사용해 컴포넌트의 데이터 가져오기 로직을 재사용할 수도 있습니다.

import React, { useEffect, useState } from "react";

// 고차 컴포넌트 정의
function withData(WrappedComponent, fetchData) {
    return function DataFetchingComponent(props) {
        const [data, setData] = useState(null);

        useEffect(() => {
            fetchData().then((response) => setData(response));
        }, []);

        if (!data) {
            return <div>로딩 중...</div>;
        }

        return <WrappedComponent {...props} data={data} />;
    };
}

// 사용 예제
function UserList({ data }) {
    return (
        <ul>
            {data.map((user) => (
                <li key={user.id}>{user.name}</li>
            ))}
        </ul>
    );
}

// 사용자 데이터를 가져오는 HOC 적용
const fetchUsers = () =>
    Promise.resolve([{ id: 1, name: "홍길동" }, { id: 2, name: "김철수" }]);

const UserListWithData = withData(UserList, fetchUsers);

export default function App() {
    return (
        <div>
            <UserListWithData />
        </div>
    );
}

1. withData: 고차 컴포넌트 정의

  • withData는 HOC로, 두 가지 인수를 받습니다:
    • WrappedComponent: 원래 컴포넌트로, 이 컴포넌트에 새로운 기능(데이터 전달)을 추가합니다.
    • fetchData: 데이터를 가져오는 비동기 함수로, 해당 데이터를 WrappedComponent에 전달합니다.
  • useState와 useEffect를 사용해 데이터를 가져오고 상태를 관리합니다.

동작

  • useState를 통해 초기 상태를 null로 설정.
  • useEffect로 컴포넌트가 마운트될 때 fetchData를 호출하여 데이터를 가져옴.
  • 데이터를 성공적으로 가져오면 setData를 호출해 상태를 업데이트.
  • 데이터가 아직 없으면 "로딩 중..." 메시지를 표시하고, 데이터가 준비되면 WrappedComponent를 렌더링합니다.

2. UserList: 데이터 표시 컴포넌트

  • UserList는 data를 props로 받아 사용자 목록을 출력합니다.
  • data.map을 사용해 각 사용자 데이터를 <li> 태그로 렌더링합니다.

주요 역할

  • UI 부분만 담당하며, 데이터를 어떻게 가져오는지는 신경 쓰지 않습니다. 이는 HOC가 책임지므로 컴포넌트가 더 간결해집니다.

3. fetchUsers: 데이터 가져오기 함수

  • fetchUsers는 Promise를 반환하며, 사용자 데이터를 모방하여 [홍길동, 김철수] 목록을 제공합니다.
  • 실제 프로젝트에서는 API를 호출하여 데이터를 가져오는 비동기 함수로 대체할 수 있습니다.

4. UserListWithData: HOC 적용

  • withData(UserList, fetchUsers)를 호출하여 UserList 컴포넌트에 데이터를 가져오는 기능을 추가.
  • 반환된 컴포넌트(UserListWithData)는 데이터 가져오기 로직을 포함하며, 데이터가 준비되면 UserList에 전달합니다.

5. App 컴포넌트

  • App에서는 단순히 UserListWithData를 렌더링.
  • 데이터 로직과 UI 로직이 분리되어 있어 유지보수가 쉬워집니다.

실행 흐름 요약

  1. 컴포넌트 마운트: UserListWithData가 렌더링되면 useEffect가 실행되어 fetchUsers를 호출.
  2. 데이터 가져오기: fetchUsers가 Promise를 통해 데이터를 반환.
  3. 상태 업데이트: 데이터를 가져오면 setData를 호출해 상태를 업데이트.
  4. UI 렌더링: 데이터가 준비되면 UserList 컴포넌트가 렌더링되며 사용자 목록을 표시.

HOC의 장점

  1. 코드 재사용: 동일한 로직(예: 인증, 데이터 로딩 등)을 여러 컴포넌트에서 쉽게 공유 가능.
  2. 구조적 일관성: 컴포넌트 간의 공통 패턴을 단일 구현으로 통합.
  3. 확장성: 컴포넌트 로직을 독립적으로 추가하거나 변경 가능.

HOC는 React에서 강력한 패턴 중 하나지만, 최근에는 React Hooks가 등장하며 많은 경우 HOC 대신 커스텀 Hooks를 사용하는 경향이 있습니다.


React의 커스텀 Hooks(Custom Hooks)재사용 가능한 상태 로직을 캡슐화하는 함수입니다. React의 기본 Hooks(useState, useEffect, 등)을 활용하여 고유한 로직을 작성한 후, 이를 다양한 컴포넌트에서 재사용할 수 있도록 만듭니다. 컴포넌트의 복잡성을 줄이고 코드 재사용성을 높이기 위해 사용됩니다.

커스텀 Hooks의 특징

  1. 함수로 정의: 커스텀 Hooks는 일반적인 자바스크립트 함수입니다. 다만, 이름이 반드시 use로 시작해야 React 규칙에 부합합니다.
  2. React Hooks 사용 가능: useState, useEffect 등 기본 Hooks를 내부에서 자유롭게 사용할 수 있습니다.
  3. 상태 로직 공유: 중복되는 상태 관리 코드를 없애고 컴포넌트 간 로직을 추상화합니다.
  4. 컴포넌트 렌더링과 독립적: 커스텀 Hook은 컴포넌트에서 독립적으로 작성되며, 자체적으로 DOM을 조작하지 않습니다.

커스텀 Hook 작성 예제

아래는 윈도우 크기를 추적하는 간단한 커스텀 Hook 예제입니다.

import { useState, useEffect } from "react";

// 커스텀 Hook 정의
function useWindowSize() {
    const [windowSize, setWindowSize] = useState({
        width: window.innerWidth,
        height: window.innerHeight,
    });

    useEffect(() => {
        // 윈도우 리사이즈 이벤트 핸들러
        function handleResize() {
            setWindowSize({
                width: window.innerWidth,
                height: window.innerHeight,
            });
        }

        window.addEventListener("resize", handleResize);
        return () => window.removeEventListener("resize", handleResize);
    }, []);

    return windowSize; // 상태 반환
}

// 사용 예제
function App() {
    const size = useWindowSize();

    return (
        <div>
            <h1>창 크기:</h1>
            <p>{`가로: ${size.width}, 세로: ${size.height}`}</p>
        </div>
    );
}

export default App;

커스텀 Hook의 작동 원리

  1. useWindowSize는 React의 useState와 useEffect를 사용해 상태와 사이드 이펙트를 관리합니다.
  2. 윈도우 크기(window.innerWidth와 window.innerHeight)를 추적하며 변경될 때마다 상태를 업데이트합니다.
  3. 반환된 상태는 App 컴포넌트에서 사용되며, size를 통해 화면 크기를 동적으로 표시합니다.

커스텀 Hook의 장점

  • 코드 재사용성: 여러 컴포넌트에서 동일한 로직을 쉽게 재사용 가능.
  • 테스트 가능성 증가: 상태 로직이 컴포넌트에서 분리되므로 테스트가 쉬워짐.
  • 가독성 향상: 컴포넌트 코드에서 상태 관리 로직을 분리하여 더욱 깔끔하게 유지.

커스텀 Hooks는 프로젝트의 복잡도를 줄이고 React 애플리케이션 개발의 생산성을 높이는 데 매우 유용합니다.

728x90

'프로그래밍 > react.js' 카테고리의 다른 글

useState  (0) 2025.04.14
Next.js  (0) 2025.04.14
리액트 렌더링  (0) 2025.04.11
리액트 네이티브로 TodoApp 구현하기 - 1  (1) 2024.12.15
react native  (0) 2024.11.27