객체 생성 로직을 별도의 팩토리 클래스나 함수로 분리하여, 객체 생성 방법을 캡슐화하는 디자인 패턴
먼저 두가지 컴포넌트를 정의해보자.
//Button.jsx
import React from 'react';
const Button=({label})=>{
return <button>{label}</button>;
}
export default Button;
//Input.jsx
import React from 'react';
const Input=({})=>{
return <input placeholder={placeholder}/>;
};
export default Input;
두 컴포넌트를 팩토리 패턴을 사용하지 않고 사용하면 다음과 같다.
//App.jsx
import React from 'react';
import Input from './Input';
import Button from './Button';
const App=()=>{
return (
<div>
<Input placeholder="Enter text"/>
<Button label="Click"/>
</div>
)
}
export default App;
위 예제는 겨우 2개의 import문이 추가되었지만, 복잡하고 방대한 UI를 가진 컴포넌트면 import문만 몇 십줄 적히게 된다.
이러한 상황에서 팩토리 패턴을 사용해 개선해보자!
// ComponentFactory.jsx
import React from 'react';
import Button from './Button';
import Input from './Input';
const componentMap = {
button: Button,
input: Input,
};
const ComponentFactory = ({ type, ...props }) => {
const Component = componentMap[type];
return Component ? <Component {...props} /> : null;
};
export default ComponentFactory;
먼저 위와 같이 팩토리 함수를 만들고 App에서 팩토리 함수를 사용해 type과 props를 전달하면, 동일한 인터페이스로 두가지 컴포넌트를 렌더링할 수 있다.
import React from 'react';
import ComponentFactory from './ComponentFactory';
const App = () => {
return (
<div>
<ComponentFactory type="button" label="Click" />
<ComponentFactory type="input" placeholder="Enter text" />
</div>
);
};
export default App;
타입스크립트를 사용해 팩토리 함수 사용시 특정 컴포넌트의 props 타입 추론이 가능하도록 만들어 보자.
// Button.tsx
import React, {FC} from 'react';
export type ButtonProps={
label: string;
}
const Button:FC<ButtonProps>=({label})=>{
return <button>{label}</button>;
}
export default Button;
// Input.tsx
import React, {FC} from 'react';
export type InputProps ={
placeholder: string;
}
const Input: FC<InputProps> = ({ placeholder }) => {
return <input placeholder={placeholder} />;
};
export default Input;
// ComponentFactory.tsx
import React,{ComponentType} from 'react';
import Button, { ButtonProps } from './Button';
import Input, { InputProps } from './Input';
type ComponentTypeMap={
button:ComponentType<ButtonProps>;
input:ComponentType<InputProps>;
};
const componentMap:ComponentTypeMap={
button:Button,
input:Input,
}
type ComponentProps<T extends keyof ComponentTypeMap>=T extends 'button'?ButtonProps:T extends 'input'?InputProps:never;
interface ComponentFactoryProps<T extends keyof ComponentTypeMap>{
type:T;
props:ComponentProps<T>;
}
const ComponentFactory=<T extends keyof ComponentTypeMap>({
type,
props,
}:ComponentFactoryProps<T>)=>{
const Component=componentMap[type];
return <Component {...props}/>;
};
export default ComponentFactory;
//App.tsx
import React, {FC} from 'react';
import ComponentFactory from './ComponentFactory';
const App: FC = () => {
return (
<div>
<ComponentFactory type="button" props={{ label: 'Click' }} />
<ComponentFactory type="input" props={{ placeholder: 'Enter text' }} />
</div>
);
};
export default App;
이렇게 작성하면 특정 컴포넌트에 해당되는 props만 자동완성이 생성된다!