함수 빌더 패턴(Function Builder Pattern): React 테이블 셀 렌더링을 “설정 가능한 함수”로 표준화하기

“컬럼마다 조건이 다르고, 스켈레톤/빈값/포맷팅도 섞여 있는데… 매번 컴포넌트를 새로 짜야 할까?”

실무에서 테이블·리스트 셀을 많이 다룰수록 공감하셨을 질문입니다.

아래 원본 문서를 바탕으로 옵션을 받아 ‘특화된 렌더링 함수’를 만들어내는 함수 빌더 패턴을 그대로 정리해 드립니다.


패턴 개요

함수 빌더 패턴은 설정(config)을 받아 특정 목적에 맞게 구성된 함수를 반환하는 고차함수(HOF) 패턴입니다. 전통적 팩토리 패턴의 함수형 버전으로, 객체 대신 ‘함수’를 생성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
// 함수 빌더 시그니처
type FunctionBuilder<TConfig, TFunction> = (config: TConfig) => TFunction;

// 실제 구현
const createGenericTextRenderer = <TRow extends BaseRowWithSkeleton>(
defaultOptions: Partial<RendererOptions> = {},
): SkeletonAwareRenderer<TRow, string | undefined> => {
// 설정을 클로저에 캡처
return (value, row, options = {}) => {
const mergedOptions = {...defaultOptions, ...options};
// 실제 렌더링 로직
};
};

핵심 개념

  • 두 단계 실행: (1) 빌드 단계에서 옵션을 고정 → (2) 실행 단계에서 값/행을 받아 렌더
  • 클로저 활용: 빌드시 캡처한 설정을 실행 시점에도 사용
  • 부분 적용: 매개변수 일부를 미리 고정하고, 나머지는 실행 시 주입

구현 분석

타입 시스템(행·옵션·렌더러 타입)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export interface BaseRowWithSkeleton<T = any> {
id: string;
_isSkeletonRow?: boolean;
_original?: T;
}

export interface RendererOptions {
skeletonWidth?: string;
emptyMessage?: string;
formatter?: (value: any) => string;
onClick?: (value: any, row: any) => void;
className?: string;
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
color?: string;
}

export type SkeletonAwareRenderer<TRow extends BaseRowWithSkeleton, TValue> = (
value: TValue,
row: TRow,
options?: RendererOptions,
) => React.ReactNode;

빌더 함수 구현(텍스트 렌더러)

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
export const createGenericTextRenderer = <TRow extends BaseRowWithSkeleton>(
defaultOptions: Partial<RendererOptions> = {},
): SkeletonAwareRenderer<TRow, string | undefined> => {
return (value, row, options = {}) => {
const mergedOptions = {...defaultOptions, ...options};
const {skeletonWidth = '80%', emptyMessage = '', formatter} = mergedOptions;

if (isGenericSkeletonRow(row)) {
return renderSkeletonCell(skeletonWidth);
}

const displayValue = formatter ? formatter(value) : value;

if (!displayValue && emptyMessage) {
return (
<Text c="dimmed" size={mergedOptions.size}>
{emptyMessage}
</Text>
);
}

return (
<Text
c={mergedOptions.color}
size={mergedOptions.size}
className={mergedOptions.className}
>
{displayValue}
</Text>
);
};
};

구현기법

  • 제네릭 매개화로 다른 Row 타입에 재사용:
1
createGenericTextRenderer<FlatAssetItem>({...})
  • 옵션 병합 전략: 실행 시점 > 빌드 시점 > 기본값
1
const mergedOptions = {...defaultOptions, ...options};
  • 조건부 렌더링: 스켈레톤/빈값/일반 값 처리 분기

설계 결정과 근거

왜 함수 빌더인가?

  • A. React와의 자연스러운 통합
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const MyTable = () => {
const textRenderer = createGenericTextRenderer<DataType>({
skeletonWidth: '90%',
emptyMessage: '데이터 없음'
});

return (
<Table>
{data.map(row => (
<TableRow key={row.id}>
<TableCell>{textRenderer(row.name, row)}</TableCell>
</TableRow>
))}
</Table>
);
};
  • B. 메모리 효율성

    • 클래스 인스턴스 없이 함수만 생성
    • 엔진의 함수 최적화·GC 부담 최소화
  • C. 함수형 패러다임 지원

    • 불변성·순수성·조합성

패턴의 장단점

장점

  • 높은 재사용성/설정 분리/타입 안전성/메모리 효율/조합 가능성
1
2
3
4
5
6
7
8
9
10
11
const basicText = createGenericTextRenderer<DataType>();
const errorText = createGenericTextRenderer<DataType>({ color: 'red', emptyMessage: '오류 발생' });
const compactText = createGenericTextRenderer<DataType>({ size: 'sm', skeletonWidth: '60%' });

const renderer = createGenericTextRenderer({ skeletonWidth: '80%', emptyMessage: '데이터 없음' });
renderer(dynamicValue, dynamicRow, { className: isSelected ? 'selected' : '' });

const typed = createGenericTextRenderer<UserData>({
formatter: (value: string) => value.toUpperCase(),
});
typed('text', userData);

단점

  • 학습 곡선(빌드/실행 2단계 사용법)
  • 클로저 내부 상태 디버깅 난도
  • 일부 상황에서 명시적 타입 표기가 필요

다른 패턴과의 비교

vs 팩토리 패턴

  • 객체 인스턴스 vs 함수 반환
  • React에선 함수형이 자연스러움

vs 전통적 빌더 패턴

  • 메서드 체이닝 vs 객체 리터럴로 간결
  • 불변 설정과 컴파일 타임 타입 검증 유리

vs 전략 패턴

  • 다양한 알고리즘 교체 vs 설정 가능한 렌더링 합성
  • 복잡성: 전략 > 함수 빌더

결론

  • React 친화성, 타입 안전성, 메모리 효율, 확장성, DX 면에서 함수 빌더 패턴이 적합할 수도 있다.

함수 빌더 패턴(Function Builder Pattern): React 테이블 셀 렌더링을 “설정 가능한 함수”로 표준화하기

https://devch.co.kr/categories/웹앱/React/react-why_use_build_pattern_250930/

Author

Chaehyeon Lee

Posted on

2025-09-30

Updated on

2025-10-09

Licensed under

댓글