React에서 컴포넌트를 작성하는 것은 단순히 기능을 구현하는 것이 아니라, 잘 작동하면서도 우아하게 기능하는 컴포넌트를 만드는 예술입니다. 오늘은 React 컴포넌트를 전문가처럼 작성하는 방법을 알아보며, 가독성, 재사용성, 효율성에 중점을 두고 설명해드리겠습니다.
1. 기본 List 컴포넌트 만들기
먼저, 간단한 List
컴포넌트부터 시작해 보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React from 'react';
const List = ({ data }) => { return ( <ul> {data.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> ); };
export default List;
|
이 컴포넌트는 배열 형태의 data
를 받아 이를 목록 형태로 렌더링합니다.
2. HOC로 컴포넌트 확장하기
고차 컴포넌트(Higher-Order Components, HOC)는 컴포넌트의 구조를 변경하지 않고 기능을 확장할 수 있는 강력한 패턴입니다. HOC는 다른 컴포넌트를 감싸 추가적인 기능을 제공하도록 설계되었습니다.
예를 들어, withLoading
HOC를 사용해 로딩 상태를 표시하는 방법은 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13
| import React from 'react';
function withLoading(Component) { return function WithLoading({ isLoading, ...props }) { if (isLoading) { return <div>Loading...</div>; } return <Component {...props} />; }; }
export default withLoading;
|
이 HOC는 isLoading
prop을 확인하여, isLoading
이 true
일 때는 “Loading…” 메시지를, 그렇지 않을 때는 감싼 컴포넌트를 렌더링합니다. 이를 통해 데이터 로딩 중에도 사용자 경험을 자연스럽게 유지할 수 있습니다.
비슷하게, withErrorHandling
HOC를 사용해 에러 상태를 관리할 수도 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13
| import React from 'react';
function withErrorHandling(Component) { return function WithErrorHandling({ error, ...props }) { if (error) { return <div>Error: {error.message}</div>; } return <Component {...props} />; }; }
export default withErrorHandling;
|
에러가 발생하면 withErrorHandling
은 에러 메시지를 표시하고, 그렇지 않으면 컴포넌트를 일반적으로 렌더링합니다. 이 HOC는 데이터 가져오기(fetch) 오류 또는 컴포넌트 수명 주기 내에서 발생하는 문제를 처리하는 데 유용합니다.
이제 withLoading
과 withErrorHandling
을 결합해 로딩과 에러 상태를 모두 처리할 수 있는 컴포넌트를 만들 수 있습니다. 이 접근법은 코드 재사용과 관심사 분리를 촉진하여, 컴포넌트의 유지 보수를 더 용이하게 하고 이해하기 쉽게 만듭니다.
3. Hooks로 데이터 가져오기
React의 hooks는 클래스 없이도 상태나 기타 React 기능을 사용할 수 있게 합니다. useFetch
는 API에서 데이터를 가져오는 커스텀 훅입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import { useState, useEffect } from 'react';
const useFetch = (url) => { const [data, setData] = useState([]); const [isLoading, setLoading] = useState(false); const [error, setError] = useState(null);
useEffect(() => { const fetchData = async () => { setLoading(true); try { const response = await fetch(url); if (!response.ok) { throw new Error('Network response was not ok'); } const json = await response.json(); setData(json); } catch (error) { setError(error); } finally { setLoading(false); } };
fetchData();
return () => { }; }, [url]);
return { data, isLoading, error }; };
export default useFetch;
|
이 훅은 로딩 상태, 데이터 저장, 에러 처리를 담당하여, 컴포넌트에서 데이터를 쉽게 가져와 표시할 수 있도록 합니다.
4. 앱 구성하기
마지막으로, 모든 요소를 App
컴포넌트에 통합합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import React from 'react'; import withLoading from './hocs/withLoading'; import withErrorHandling from './hocs/withErrorHandling'; import useFetch from './hooks/useFetch'; import List from './components/List';
const ListWithLoading = withLoading(List); const ListWithErrorHandling = withErrorHandling(ListWithLoading);
const App = () => { const { data, isLoading, error } = useFetch('https://api.example.com/data');
return ( <div> <h1>List Component</h1> <ListWithErrorHandling data={data} isLoading={isLoading} error={error} /> </div> ); };
export default App;
|
이 예제에서는 useFetch
훅을 사용하여 데이터를 로드하고, List
컴포넌트에 전달합니다. 이 List
컴포넌트는 withLoading
과 withErrorHandling
HOC로 확장되어 로딩과 에러 상태를 처리합니다.
결론
프로처럼 컴포넌트를 작성하는 것은 전체적인 그림을 보는 것에서 시작됩니다. 가독성, 유지보수성, 재사용성이 높은 컴포넌트를 만드는 것이 핵심입니다. HOC와 hooks 같은 패턴을 사용하면, 시간이 지나도 견고하고 효율적인 코드베이스를 구축할 수 있습니다.