프론트엔드 디자인 패턴 - #1 팩토리 패턴(Factory pattern)

Noma·2024년 6월 25일
0
post-custom-banner

팩토리 패턴

객체 생성 로직을 별도의 팩토리 클래스나 함수로 분리하여, 객체 생성 방법을 캡슐화하는 디자인 패턴

예시 코드

먼저 두가지 컴포넌트를 정의해보자.

//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;

장점

  1. 객체 생성 로직의 캡슐화
    객체 생성 로직이 중앙화되어, 각 컴포넌트의 생성 방식을 관리하는 하나의 팩토리 함수나 클래스에서 이를 처리. 이렇게 하면 객체 생성 로직이 여러 곳에 흩어져 있지 않고, 한 곳에서 관리되므로 유지보수가 용이하다.
  2. 확장성 향상
    새로운 타입의 컴포넌트를 추가할 때, 기존 코드를 거의 수정할 필요 없이 componentMap에 새로운 컴포넌트를 추가하는 것만으로 확장이 가능
  3. 재사용성 향상
    컴포넌트 생성 로직을 재사용할 수 있어 다양한 곳에서 동일한 팩토리 함수를 호출해 일관된 방식으로 컴포넌트를 생성할 수 있다. 코드 중복을 줄이고 일관성 유지에 도움이 된다.

with TypeScript

타입스크립트를 사용해 팩토리 함수 사용시 특정 컴포넌트의 props 타입 추론이 가능하도록 만들어 보자.

방법

  1. 컴포넌트 별로 Props 타입 정의
  2. 타입스크립트 유틸리티 타입 활용
  3. 팩토리 함수에서 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만 자동완성이 생성된다!

profile
오히려 좋아
post-custom-banner

0개의 댓글