Files
2nd/10_Wiki/Topics/Frontend/설정 객체 및 룩업 테이블 설계(Configuration Objects and Lookup Tables).md
T
Antigravity Agent f8b21af4be Wiki cleanup: error-doc removal, dedup merge, link normalization
10_Wiki/Topics 대규모 정리:
- 오류 캡처/미완성 stub 문서 227개 제거
- 교차폴더 중복 43클러스터 병합 (63파일 → redirect)
- 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건
- 카테고리 MOC 6개 신규 생성
- Graph 섹션 미해결 related-keyword 링크 10,058건 제거

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 23:52:15 +09:00

5.9 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-설정-객체-및-룩업-테이블-설계-configuration- 설정 객체 및 룩업 테이블 설계(Configuration Objects and Lookup Tables) 10_Wiki/Topics verified self
Lookup Table
Config Object
Dispatch Table
Map-based Branching
none A 0.92 applied
pattern
refactoring
clean-code
frontend
javascript
2026-05-10 pending
language framework
typescript na

설정 객체 및 룩업 테이블 설계(Configuration Objects and Lookup Tables)

매 한 줄

"매 if/switch chain 의 data structure (Map/Record) 의 substitute". 매 control flow 의 data 로 transform — 매 add/modify 의 O(1) extension, 매 type-safe (TS Record), 매 testable. Refactoring catalog (Fowler) 의 "Replace Conditional with Polymorphism" 의 lightweight variant.

매 핵심

매 substitute targets

  • 매 Long if/else if chain.
  • 매 Switch with simple return.
  • 매 Magic-number/string branching.
  • 매 Strategy pattern (lightweight).

매 lookup table 의 structure

  1. Map / Record: key → value/handler.
  2. Default fallback: ?? or explicit default key.
  3. Type-safe key: TypeScript keyof / discriminated union.
  4. Frozen at module load: as const / Object.freeze.

💻 패턴

Before (if-chain) → After (lookup)

// X — 매 add 의 매 modification 의 source
function getDiscount(tier: string): number {
  if (tier === 'bronze') return 0.05;
  else if (tier === 'silver') return 0.10;
  else if (tier === 'gold') return 0.15;
  else if (tier === 'platinum') return 0.20;
  else return 0;
}

// O — data-driven, type-safe, O(1) lookup
const DISCOUNT = {
  bronze: 0.05,
  silver: 0.10,
  gold: 0.15,
  platinum: 0.20,
} as const satisfies Record<string, number>;

type Tier = keyof typeof DISCOUNT;
const getDiscount = (tier: Tier): number => DISCOUNT[tier] ?? 0;

Dispatch table (handler map)

type Action = 'create' | 'update' | 'delete';

const handlers: Record<Action, (id: string) => Promise<void>> = {
  create: async (id) => api.create(id),
  update: async (id) => api.update(id),
  delete: async (id) => api.delete(id),
};

async function dispatch(action: Action, id: string) {
  await handlers[action](id);
}

Discriminated union + exhaustive check

type Event =
  | { type: 'click'; x: number; y: number }
  | { type: 'key'; code: string }
  | { type: 'scroll'; delta: number };

const reducer: { [K in Event['type']]: (e: Extract<Event, { type: K }>) => void } = {
  click: (e) => console.log('click', e.x, e.y),
  key:   (e) => console.log('key', e.code),
  scroll:(e) => console.log('scroll', e.delta),
};

function handle(e: Event) {
  (reducer[e.type] as (e: Event) => void)(e);
}

Config object (UI variants)

const BUTTON_STYLE = {
  primary:   'bg-blue-600 text-white hover:bg-blue-700',
  secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
  danger:    'bg-red-600 text-white hover:bg-red-700',
  ghost:     'bg-transparent text-blue-600 hover:bg-blue-50',
} as const;

type Variant = keyof typeof BUTTON_STYLE;

export function Button({ variant = 'primary', children, ...rest }:
  { variant?: Variant; children: React.ReactNode } & React.ComponentProps<'button'>) {
  return <button className={BUTTON_STYLE[variant]} {...rest}>{children}</button>;
}

Validation rule registry

const VALIDATORS = {
  email:    (v: string) => /^[^@]+@[^@]+\.[^@]+$/.test(v) || 'invalid email',
  phone:    (v: string) => /^\+?[\d\s-]{10,}$/.test(v)    || 'invalid phone',
  required: (v: string) => v.trim().length > 0           || 'required',
} as const;

function validate(field: keyof typeof VALIDATORS, value: string) {
  return VALIDATORS[field](value);
}

Status code → message

const HTTP_MESSAGE: Record<number, string> = {
  200: 'OK',
  400: 'Bad Request',
  401: 'Unauthorized',
  403: 'Forbidden',
  404: 'Not Found',
  500: 'Server Error',
};
const messageFor = (code: number) => HTTP_MESSAGE[code] ?? 'Unknown';

i18n table

const I18N = {
  en: { hello: 'Hello', bye: 'Bye' },
  ko: { hello: '안녕', bye: '안녕히' },
  ja: { hello: 'こんにちは', bye: 'さようなら' },
} as const;

type Lang = keyof typeof I18N;
type Key  = keyof typeof I18N[Lang];

const t = (lang: Lang, key: Key) => I18N[lang][key];

매 결정 기준

상황 Approach
단순 mapping (key → value) 매 plain Record
Behavior dispatch 매 handler Map
Behavior + state 매 Strategy class (full polymorphism)
매 2-3 case 매 if/ternary 의 OK — over-engineering 의 avoid
매 5+ case 매 lookup table 의 prefer
Runtime extension 매 Map (mutable)
Compile-time freeze as const satisfies Record<...>

기본값: 매 5+ branch + simple value/handler → lookup. 매 type-safe as const satisfies.

🔗 Graph

🤖 LLM 활용

언제: 긴 if/switch chain 의 detect, refactor 의 propose, type 의 tighten. 언제 X: 매 stateful behavior — Strategy class 의 use.

안티패턴

  • Mutable global table: 매 race + test pollution — as const 의 freeze.
  • Stringly-typed key: Record<string, T> 만 — keyof 의 narrow.
  • Default 누락: obj[key] 의 undefined access — ?? 의 add.
  • Side effect 의 handler: 매 lookup map 의 pure function 의 keep.

🧪 검증 / 중복

  • Verified (Refactoring 2/e Fowler — "Replace Conditional with Polymorphism", Clean Code Martin Ch.6, TypeScript handbook Record).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — lookup/dispatch/config 7 patterns 의 정리