728x90
반응형
Container / Presentational Pattern
- 컴포넌트를 두 가지 역할로 분리하여, 각 컴포넌트가 특정 책임만을 가지도록 하는 패턴입니다.
- Presentational Component (UI 렌더링에 집중)
- 데이터와 상태를 관리하지 않으며, 단순히 props로 전달된 데이터를 화면에 보여주는 역할만 합니다.
- Container Component (데이터 관리 및 상태 관리)
- 데이터를 가져오고 상태를 관리하며, Presentational Component에 데이터를 전달합니다. 컨테이너 컴포넌트는 비즈니스 로직을 처리하고, 데이터 흐름을 관리하는 역할을 합니다.
- Presentational Component (UI 렌더링에 집중)
- 장점
- Presentational Component는 UI에만 집중하고, Container Component는 애플리케이션의 상태와 데이터를 관리합니다. 이를 통해 관심사의 분리를 강제할 수 있습니다.
- Presentational Component는 UI를 담당하는 컴포넌트로, 애플리케이션 곳곳에서 다양한 용도로 재사용할 수 있습니다.
- Presentational Component는 애플리케이션의 로직을 변경하지 않기 때문에, 디자이너가 코드 지식 없이 UI만 변경할 수 있습니다. 또한, 한 번 변경하면 앱 전체에서 일관된 스타일로 적용할 수 있습니다.
- Presentational Component는 보통 순수 함수(pure function)로 작성되기 때문에, 주어진 데이터에 대해 어떤 내용을 렌더링할지 예측 가능하고, 별도의 데이터 저장소를 모킹(mocking)할 필요 없이 쉽게 테스트할 수 있습니다.
- Hooks를 사용하면 Container/Presentational 패턴을 사용하지 않고도 동일한 결과를 얻을 수 있습니다. 예를 들어, SWR을 사용하여 데이터를 쉽게 가져오고, 조건에 맞게 리스팅이나 스켈레톤 컴포넌트를 렌더링할 수 있습니다. 함수형 컴포넌트에서 Hooks를 사용하여 코드를 간결하고 유지 보수하기 쉽게 만들 수 있기 때문에, 굳이 컴포넌트를 Container와 Presentational로 분리할 필요가 없습니다.
HOC ( Higher-Order Components ) Pattern
- Higher-Order Components (HOC)는 컴포넌트를 래핑(wrapping)하여 로직을 쉽게 전달할 수 있게 해주는 패턴입니다.
- 주로 여러 컴포넌트에서 공통적으로 사용하는 로직을 한 곳에 모으고, 이를 다른 컴포넌트에 적용하는 데 유용합니다.
// HOC: 버튼에 스타일을 추가하는 함수
function withStyles(Component) {
return props => {
const style = { padding: '0.2rem', margin: '1rem' }
// Component에 추가된 props를 전달하고 렌더링
return <Component style={style} {...props} />
}
}
const Button = () = <button>Click me!</button>
const Text = () => <p>Hello World!</p>
// HOC 적용
const StyledButton = withStyles(Button)
const StyledText = withStyles(Text)
- 장점
- HOC 패턴을 사용하면 재사용 가능한 로직을 한 곳에 모을 수 있습니다. 이렇게 하면 동일한 로직을 여러 번 작성하는 것을 피할 수 있고, 중복된 코드에서 발생할 수 있는 버그를 줄일 수 있습니다.
- HOC는 스타일, 로직, 상태 관리 등을 컴포넌트에서 분리하여 재사용할 수 있게 해줍니다. 이로 인해 컴포넌트는 시각적인 부분만 담당하고, 로직은 HOC로 분리되어 가독성 및 유지보수성도 향상됩니다.
- 단점
- 여러 개의 HOC가 중첩되어 있을 때, 각 HOC가 어떤 props를 전달하는지 파악하기 어려울 수 있습니다. 이는 디버깅을 어렵게 만들고, 애플리케이션을 확장하기 어려운 상황을 만들 수 있습니다.
- HOC가 래핑된 컴포넌트에 새로운 props를 전달하면서, 원래 컴포넌트의 props와 이름이 충돌할 수 있습니다. 이를 방지하려면 props 이름을 변경하거나 병합하는 방법을 신경 써야 합니다.
- 여러 개의 HOC를 중첩하여 사용하는 경우, 렌더링 성능에 영향을 미칠 수 있습니다. 매번 HOC가 새 컴포넌트를 생성하기 때문입니다.
Render Props Pattern
- 컴포넌트가 다른 컴포넌트에 렌더링할 내용을 함수의 형태로 전달하는 패턴입니다.
import React, { useState } from 'react';
// Counter 컴포넌트: 상태를 관리하고, render prop을 통해 UI를 제공
const Counter = ({ render }) => {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
{/* render prop을 호출하여 부모에게 렌더링할 내용을 전달 */}
{render(count)}
</div>
);
};
// App 컴포넌트: render prop을 통해 UI 정의
const App = () => {
return (
<div>
<h1>Render Props Example</h1>
<Counter render={(count) => <h2>Current Count: {count}</h2>} />
</div>
);
};
export default App;
- 장점
- render-props 는 매번 다르게 전달될 수 있기 때문에, render-props 를 받는 컴포넌트를 다양한 용도로 재사용할 수 있습니다. 이로 인해 컴포넌트가 여러 가지 케이스에 맞게 쉽게 변형되어 재사용 가능합니다.
- render-props 를 사용하면 애플리케이션의 로직과 렌더링 컴포넌트를 분리할 수 있습니다. 상태를 관리하는 컴포넌트는 데이터를 자식 컴포넌트로 전달하고, 자식 컴포넌트는 이 데이터를 렌더링만 하면 됩니다. 이를 통해 컴포넌트 간의 역할이 명확히 나뉘어 코드가 더 깔끔하고 유지보수하기 쉬워집니다.
- 고차 컴포넌트(HOC)에서는 props가 암묵적으로 전달되는 문제가 있었지만, render-props 에서는 전달되는 props가 명시적으로 정의됩니다. 따라서 render-props 의 인수 목록을 통해 어떤 props가 전달되는지 명확히 알 수 있습니다. 이로 인해 props가 어디에서 오는지 정확하게 추적할 수 있습니다.
- 단점
- render-props 을 사용할 때, props로 함수를 전달하고 이를 깊은 컴포넌트 트리까지 전달하는 방식은 코드가 복잡해질 수 있습니다.
- 주 리렌더링이 발생할 수 있어 성능에 영향을 미칠 수 있습니다. 예를 들어, render-props 을 통해 함수가 매번 생성되기 때문에 불필요한 리렌더링이 발생할 수 있습니다. 이를 해결하려면 React.memo나 useCallback을 사용하는 것이 좋습니다.
- React의 Hooks는 컴포넌트 간의 재사용성과 데이터 공유 방식을 개선했기 때문에, render-props 패턴이 필요한 경우가 많이 줄어들었습니다. Hooks는 상태를 관리하고 로직을 재사용할 수 있는 방법을 제공하므로, render-props 대신 Hooks를 사용하는 것이 더 간단하고 직관적일 수 있습니다.
Provider Pattern
- 주로 React의 Context API에서 사용되는 패턴으로 컴포넌트 트리에서 특정 데이터를 전역적으로 공유하고, 하위 컴포넌트들에서 해당 데이터를 자동으로 접근할 수 있도록 만들어 줍니다.
- Context API는 컴포넌트 트리 내에서 props drilling (즉, 여러 컴포넌트 간에 props를 일일이 전달하는 문제)을 해결할 수 있는 방법을 제공합니다. Provider 컴포넌트는 데이터를 제공하고, Consumer 또는 useContext 훅을 통해 하위 컴포넌트들이 그 데이터를 사용할 수 있게 합니다.
- Provider : 데이터를 제공하는 역할을 합니다. Context.Provider 컴포넌트를 사용하여 데이터를 하위 컴포넌트들에게 전달합니다.
- Consumer : 데이터를 사용하는 역할을 합니다. Context.Consumer 또는 useContext 훅을 사용하여 제공된 데이터를 사용할 수 있습니다.
- 장점
- Provider를 사용하면 상태를 전역적으로 관리하고, 여러 컴포넌트 간에 상태를 쉽게 공유할 수 있습니다.
- 컴포넌트 트리의 여러 레벨을 거쳐 props를 전달하는 문제 ( Props Drilling ) 를 해결합니다.
- 상태나 데이터를 필요로 하는 컴포넌트가 의존하는 다른 컴포넌트들에 대한 의존성을 줄입니다.
- 단점
- Provider로 감싼 컴포넌트가 제공하는 데이터가 변경될 때마다 해당 데이터를 사용하는 모든 하위 컴포넌트가 리렌더링됩니다. 이로 인해 불필요한 리렌더링이 발생할 수 있습니다. 특히 애플리케이션이 커지고, 데이터 변경이 자주 일어나는 경우 성능에 큰 영향을 미칠 수 있습니다.
- Context API를 사용하면 전역 상태 관리가 간편하지만, 이를 남용하면 오히려 코드가 복잡해질 수 있습니다. 애플리케이션에서 모든 상태를 Context로 관리하려고 하면, 상태가 전역적으로 퍼져서 어떤 데이터가 어디서 사용되는지 추적하기 어려워질 수 있습니다.
- Provider를 사용하면 상태가 UI와 밀접하게 결합됩니다. 이렇게 되면 컴포넌트가 복잡해지고, 상태 변화에 따라 UI가 자주 리렌더링될 수 있습니다. 이로 인해, UI와 상태 로직의 분리가 어려워질 수 있습니다.
Compound Pattern
- 부모 컴포넌트가 하위 컴포넌트들을 관리하는 방식으로, 서로 관련이 있는 여러 개의 컴포넌트를 그룹화하여 상호작용을 정의하는 패턴입니다.
- 이 패턴은 상태 관리를 부모 컴포넌트에서 하고, 하위 컴포넌트들은 이를 기반으로 렌더링하는 구조로 이루어집니다.
- 장점
- 부모 컴포넌트에서 상태를 관리하고 하위 컴포넌트들이 이를 소비하므로, 상태 관리가 일관되게 이루어집니다. 즉, 상태 관리의 중앙 집중화가 가능합니다.
- 컴포넌트 간 상호작용을 효율적으로 관리할 수 있습니다. 부모가 상태를 관리하고, 하위 컴포넌트들이 해당 상태를 참조하여 렌더링되므로, 컴포넌트 간의 의존성이 명확해집니다.
- 컴포넌트가 각자의 역할에 맞게 분리되어 있기 때문에 다른 프로젝트에서도 재사용이 가능합니다.
- 각 컴포넌트가 명확한 책임을 가지며, 상태와 UI가 구분되어 있어 코드를 읽고 이해하기 쉽습니다.
- 단점
- 부모 컴포넌트가 상태를 관리하고 하위 컴포넌트들이 이를 사용하는 구조이기 때문에, 컴포넌트 트리가 복잡해지면 상태를 관리하는 부모 컴포넌트가 부담이 커질 수 있습니다.
- 컴파운드 패턴에서 부모-자식 관계가 강하게 결합되어 있기 때문에, 개별 컴포넌트의 독립적인 테스트가 어려울 수 있습니다. 특히 상태나 상호작용이 복잡할 경우, 해당 로직을 테스트하는 데 추가적인 작업이 필요할 수 있습니다.
728x90
반응형
'react' 카테고리의 다른 글
react-sounds (2) | 2025.06.06 |
---|---|
렌더링 패턴 ( Streaming SSR + RSC ) (0) | 2025.04.26 |
렌더링 패턴 ( ISR ) (0) | 2025.04.26 |