게으른개발너D
React에서 쓰이는 Design Pattern 본문
React에서 자주 사용되는 디자인 패턴들은 상태 관리, 컴포넌트 구조, 코드 재사용성 등과 관련되어 있다.
디자인 패턴들은 상황에 맞게 선택하여 사용할 수 있으며, 복잡한 애플리케이션에서는 상태 관리나 컴포넌트 구조를 명확히 하는 데 큰 도움이 된다.
대표적인 몇 가지 패턴을 간단한 예시와 함께 소개하겠다.
Container-Presenter Pattern
이 패턴은 컴포넌트의 역할을 명확히 분리하기 위해 자주 사용된다.
Container 컴포넌트는 상태와 비즈니스 로직을 관리를, Presenter 컴포넌트는 UI를 담당한다.
// Presenter: UI를 담당
const UserProfile = ({ user }) => (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
// Container: 데이터 처리와 상태 관리
const UserProfileContainer = () => {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then(setUser); // API 호출을 통해 사용자 정보 가져오기
}, []);
if (!user) return <div>Loading...</div>;
return <UserProfile user={user} />;
};
Compound Components Pattern
컴파운드 컴포넌트 패턴은 여러 컴포넌트가 서로 상호작용하는 복잡한 UI를 만들 때 유용하다.
부모 컴포넌트가 자식 컴포넌트 간의 관계를 관리하며, 자식 컴포넌트가 독립적으로 동작할 수 있게 한다.
const Dropdown = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && <div>{children}</div>}
</div>
);
};
const DropdownItem = ({ children }) => (
<div>
{children}
</div>
);
const App = () => (
<Dropdown>
<DropdownItem>Item 1</DropdownItem>
<DropdownItem>Item 2</DropdownItem>
<DropdownItem>Item 3</DropdownItem>
</Dropdown>
);
Render Props Pattern
컴포넌트에서 데이터를 렌더링할 때 렌더링 로직을 외부에서 정의할 수 있도록 하는 패턴이다.
고차 컴포넌트 (HOC Compoenents Pattern)보다 유연성이 높다.
const MouseTracker = ({ render }) => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (e) => {
setMousePosition({ x: e.clientX, y: e.clientY });
};
return <div onMouseMove={handleMouseMove}>{render(mousePosition)}</div>;
};
const App = () => (
<MouseTracker render={({ x, y }) => (
<h1>The mouse position is ({x}, {y})</h1>
)} />
);
Higher-Order Components (HOC) Pattern
HOC는 기존 컴포넌트를 확장하여 기능을 추가하는 패턴이다.
중복 코드를 줄이고, 재사용 가능한 로직을 구현할 수 있다.
const withLoading = (Component) => {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) return <div>Loading...</div>;
return <Component {...props} />;
};
};
const UserList = ({ users }) => (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
const UserListWithLoading = withLoading(UserList);
const App = () => {
const [isLoading, setIsLoading] = useState(true);
const [users, setUsers] = useState([]);
useEffect(() => {
fetchUsers().then(data => {
setUsers(data);
setIsLoading(false);
});
}, []);
return <UserListWithLoading isLoading={isLoading} users={users} />;
};
Custom Hooks Pattern
React Hook을 재사용하기 쉽도록 만든 패턴으로, 컴포넌트 외부로 상태 관리 및 로직을 추출해 모듈화한다.
const useFetchData = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return { data, loading };
};
const App = () => {
const { data, loading } = useFetchData('https://api.example.com/data');
if (loading) return <div>Loading...</div>;
return <div>Data: {JSON.stringify(data)}</div>;
};
유행이 지난 패턴?
위의 대표적인 디자인 패턴은 모두 유용해 보이나, 현재 필요성이 없어져 유행이 지나 다른 패턴으로 대체되거나 잘 사용하지 않는 패턴들이 있다. 최근 트렌드와 개발 환경에 따라 많이 쓰이는 패턴과 사용 빈도가 줄어든 패턴을 구분해보겠다.
유행이 지난 패턴
- Higher-Order Components (HOC):
- HOC는 한때 널리 사용되었지만, React 16.8에서 Hooks가 도입된 이후로 그 사용이 점차 줄어들고 있다. HOC는 복잡한 코드 구조와 디버깅 문제를 야기할 수 있어서 Custom Hooks로 대체되는 경우가 많다.
- 대체 기술: Custom Hooks 또는 Render Props.
- Render Props:
- Render Props도 HOC처럼 로직을 재사용하기 위한 좋은 방법이었지만, Hooks 도입 이후 많은 경우 Custom Hooks로 대체되었다. Render Props는 복잡한 컴포넌트 구조를 만들 수 있기 때문에 가독성이 떨어질 수 있다.
- 대체 기술: Custom Hooks.
많이 쓰이는 패턴
- Custom Hooks Pattern:
- React Hooks는 매우 많이 사용되며, 특히 상태 관리, 비즈니스 로직, API 호출 등을 분리해 관리할 때 자주 사용된다.
- React의 함수형 컴포넌트 구조와 완벽하게 조화를 이루며, 상태와 효과를 쉽게 관리할 수 있도록 도와주기때문에 재사용이 가능하고 가독성 높은 코드를 작성할 수 있다..
- Component Composition (컴포넌트 합성):
- 이 패턴은 컴포넌트를 여러 개의 작은 컴포넌트로 나누어 재사용성을 높이는 방식이다. React에서 권장하는 방법이며, 중첩된 구조보다는 컴포넌트를 조합하는 방식으로 UI를 구축한다.
- 이를 통해 복잡한 UI를 관리하기 쉬워지고, 상태와 UI를 더 잘 분리할 수 있다.
- Context API와 Reducer 사용:
- Context API와 useReducer를 조합하여 전역 상태를 관리하는 패턴이다. Redux 같은 외부 라이브러리 없이도 복잡한 상태 관리를 가능하게 한다.
- React 자체에서 제공하는 기능으로 간단한 상태 관리가 가능하고, 복잡한 의존성을 줄일 수 있다.
- Atomic Design (원자적 디자인):
- 컴포넌트 기반 설계 방식으로, 컴포넌트를 원자(Atomic), 분자(Molecular), 유기체(Organisms) 등의 단위로 구분해 설계하는 방법이다. UI를 모듈화하고 일관성 있는 디자인 시스템을 만들기 위해 많이 사용된다.
- UI 컴포넌트 라이브러리를 구축할 때나 대규모 프로젝트에서 재사용 가능한 컴포넌트를 구성하기에 적합하다.
- React Query, Zustand와 같은 상태 관리 라이브러리 사용:
- React Query와 같은 라이브러리는 서버 상태 관리와 캐싱을 쉽게 해주고, Zustand는 간결한 전역 상태 관리 솔루션으로 인기를 끌고 있다.
- Redux와 같은 복잡한 상태 관리 라이브러리보다 더 간단하고 사용하기 쉬운 옵션들이 주목받고 있다.
아래 예시는 React Query를 사용하여 서버 데이터를 관리하고, Custom Hook을 통해 API 호출 로직을 분리한 예시이다.
import { useQuery } from 'react-query';
// Custom Hook
const useFetchUserData = (userId) => {
return useQuery(['user', userId], () =>
fetch(`https://api.example.com/users/${userId}`).then(res => res.json())
);
};
const UserProfile = ({ userId }) => {
const { data, isLoading, error } = useFetchUserData(userId);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
);
};
const App = () => <UserProfile userId={1} />;
결론!
요즘은 Custom Hooks, Component Composition, Context API와 같은 패턴이 많이 사용되며, React Query나 Zustand와 같은 경량 상태 관리 라이브러리의 사용도 트렌드에 맞는 방식이다.
'개발 > ReactJS' 카테고리의 다른 글
React에서 SVG 사용하기 (Typescript) (0) | 2023.10.31 |
---|---|
[Custom Hooks 2] useEffect 이용 (1) | 2023.10.30 |
[Custom Hooks 1] useState 이용 (0) | 2023.10.30 |
React Query 개념 (1) | 2023.08.30 |
string 형태의 html을 html로 렌더링하기 (dangerouslySetInnerHTML) (0) | 2023.07.08 |