고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수입니다.
일반적인 고차 컴포넌트의 정의이다.
먼저 고차 함수(HOF)에 대해서 알아보자. 고차함수란 다른 함수를 인자로 받거나 함수를 반환하는 함수를 고차 함수라고 한다.
React에서는 고차 컴포넌트(HOC)가 고차 함수(HOF)를 활용한 일반적인 패턴이다.
고차 컴포넌트(이하 HOC)는 코드 재사용성을 높이는 데 매우 유용하다.
여러 컴포넌트에서 공통적으로 사용되는 로직이 있다면, 이를 HOC로 만들어 재사용할 수 있다.
예를 들어, 인증 체크나 권한 관리 등의 로직은 애플리케이션 전반에서 사용될 수 있다. 이러한 로직을 HOC로 작성하면 중복 코드를 줄일 수 있다.
HOC는 컴포넌트를 인자로 받아 새로운 컴포넌트를 반환하는 함수이다.
이때 반환된 컴포넌트에는 HOC에서 구현한 로직이 포함되어 있다. 따라서 HOC를 사용하는 컴포넌트에서는 추가적인 작업 없이 해당 로직을 활용할 수 있다.
이를 통해 여러 컴포넌트에서 동일한 로직을 중복해서 작성하지 않아도 된다.
유지보수 측면에서도 HOC 내부의 로직만 수정하면 해당 로직을 사용하는 모든 컴포넌트에 변경 사항이 적용되므로 효율적이다.
// withAuthentication.js
import React, { useEffect, useState } from 'react';
import { auth } from './auth';
const withAuthentication = (WrappedComponent) => {
const AuthenticatedComponent = (props) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
auth.isAuthenticated().then((authenticated) => {
setIsAuthenticated(authenticated);
});
}, []);
return isAuthenticated ? (
<WrappedComponent {...props} />
) : (
<div>Loading...</div>
);
};
return AuthenticatedComponent;
};
export default withAuthentication;
// MyComponent.js
import React from 'react';
import withAuthentication from './withAuthentication';
const MyComponent = (props) => {
return <div>My Component</div>;
};
export default withAuthentication(MyComponent);
HOC를 사용하면 컴포넌트에 전달되는 props를 조작하거나 추가할 수 있다. 이를 통해 컴포넌트의 기능을 확장하거나 필요한 데이터를 제공할 수 있다.
HOC는 감싸고 있는 컴포넌트(Wrapped Component)에 props를 전달할 때, 기존 props에 추가적인 props를 합쳐서 전달할 수 있다.
이렇게 하면 Wrapped Component에서는 HOC에서 추가된 props를 자연스럽게 사용할 수 있다.
예를 들어, 컴포넌트에 전역 상태나 컨텍스트 데이터를 props로 주입하고 싶다면 HOC를 활용할 수 있다.
또한, 외부 라이브러리나 서비스에서 제공하는 데이터를 props로 전달하거나, 부모 컴포넌트에서 전달받은 props를 가공하여 자식 컴포넌트에 전달할 수도 있다.
이렇게 HOC를 사용하면 컴포넌트 간의 결합도를 낮추고, 컴포넌트의 재사용성과 유연성을 높일 수 있다.
// withExtraProp.js
import React from 'react';
const withExtraProp = (WrappedComponent) => {
const ComponentWithExtraProp = (props) => {
const newProps = {
extraProp: '추가적인 props',
...props,
};
return <WrappedComponent {...newProps} />;
};
return ComponentWithExtraProp;
};
export default withExtraProp;
// MyComponent.js
import React from 'react';
import withExtraProp from './withExtraProp';
const MyComponent = (props) => {
return <div>{props.extraProp}</div>;
};
export default withExtraProp(MyComponent);
HOC를 사용하면 컴포넌트의 렌더링 조건을 제어할 수 있다. 이를 통해 특정 상황에 따라 다른 UI를 렌더링하거나, 로딩 중일 때 로딩 인디케이터를 표시하는 등의 작업을 수행할 수 있다.
HOC는 Wrapped Component를 렌더링하기 전에 특정 조건을 확인할 수 있다.
예를 들어, 데이터 로딩 중일 때는 로딩 인디케이터를 보여주고, 로딩이 완료되면 실제 컴포넌트를 렌더링할 수 있습니다.
또한, 에러가 발생했을 때는 에러 메시지를 표시하거나, 인증되지 않은 사용자에게는 로그인 페이지로 리디렉션하는 등의 동작을 구현할 수 있다.
이렇게 렌더링 제어 로직을 HOC로 분리하면, 여러 컴포넌트에서 동일한 로직을 중복해서 작성할 필요가 없어진다.
또한, 렌더링 조건을 HOC 내부에서 관리하므로 컴포넌트 자체의 복잡성을 낮출 수 있다.
// withLoadingIndicator.js
import React, { useState, useEffect } from 'react';
const withLoadingIndicator = (WrappedComponent) => {
const ComponentWithLoadingIndicator = (props) => {
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// 데이터 로딩 완료 후 isLoading 상태 변경
const timer = setTimeout(() => {
setIsLoading(false);
}, 2000);
return () => clearTimeout(timer);
}, []);
return isLoading ? <div>Loading...</div> : <WrappedComponent {...props} />;
};
return ComponentWithLoadingIndicator;
};
export default withLoadingIndicator;
// MyComponent.js
import React from 'react';
import withLoadingIndicator from './withLoadingIndicator';
const MyComponent = (props) => {
return <div>My Component</div>;
};
export default withLoadingIndicator(MyComponent);
HOC를 사용하면 코드 재사용성, 유연성, 가독성 등의 측면에서 많은 이점을 얻을 수 있다.
그러나 HOC를 과도하게 사용하면 코드가 복잡해질 수 있으므로, 적절한 수준에서 활용하는 것이 중한것 같다.
또한 React Hooks가 등장한 이후에는 HOC 대신 커스텀 hooks를 사용하는 경우가 많아졌지만, HOC 패턴 자체는 여전히 유용한 기술 이라고 생각한다.