Skip to Content
페이먼츠 미션1단계 안티패턴anti-05. 4칸 고정인데 string[]

anti-05. 4칸 고정인데 string[] — 튜플 누락

빈도: 25건 중 9+건 — 카드번호 4칸·만료일 2칸은 고정 길이인데 string[]로 선언해 컴파일러가 길이를 보장하지 못한다. 더 나아가 훅 시그니처까지 string[]로 좁아지면 단일 string인 CVC를 [""] 배열로 감싸는 역방향 전염까지 발생한다.

패턴 한 줄

cardNumbers: string[] / expirationPeriod: string[] 형태로 타입을 열어두면, cardNumbers[0]을 사용하는 모든 코드가 런타임에서야 undefined 가능성을 만난다.

AS-IS 코드 (8기 PR 발췌)

🔍 읽기 전에

“내 타입 정의에서 string[]을 검색해보자. 그 배열의 최대/최소 길이는 컴파일러가 알고 있는가?”

사례 A — interface에 string[] 그대로

PR #515 src/types.ts

// src/types.ts interface CardInfo { cardNumbers: string[]; // 항상 4칸 expirationPeriod: string[]; // 항상 2칸 cvc: string; cardBrand: CardBrand; }

리뷰어 코멘트:

“string[] 대신 튜플(cardNumbers 4칸, expirationPeriod 2칸)“

사례 B — 단일 문자열을 굳이 배열로 감싸기

PR #524  — PR body 회고:

“cvc는 사실상 단일 문자열인데, useInputHandle 훅이 string[]을 받기 때문에 [""] 배열로 감싸서 사용했습니다.”

훅의 입력 타입이 string[]로 좁혀져 있어, 원래 string인 값까지 배열로 감싸는 역방향 전염이 일어났다.

리뷰어 eastroots92가 한 줄로 짚었다.

“string, string[] 둘다 고려 가능하게 해볼 수 있을까요?” → 훅 입력 타입 재검토 요청

이 사례가 보여주는 것은 — 고정 길이 배열을 string[]로 표현한 결정훅 시그니처컴포넌트 분리(anti-06)와 통째 lifting(anti-08) 까지 모두 같은 형태의 모양을 갖도록 잡아당긴다는 점이다. 타입은 단순한 안전망이 아니라 코드의 모양을 결정한다.

사례 C — 같은 형태, 다른 PR

PR #498  PR #513  PR #516  PR #518 cardNumbers: string[] 패턴 반복.

사례 D — 한걸음 더 — cardBrand: string

PR #505 CardPreview.tsx

리뷰어 코멘트:

cardBrand: string이 너무 넓습니다.”

PR #514 도 같은 신호:

“리터럴 유니온 타입을 쓰면 잘못된 값이 들어오는 것을 컴파일 타임에 잡을 수 있어요!”type NetworkBrand = 'visa' | 'master' 제안

타입 좁히기 누락은 길이뿐 아니라 값의 집합에도 적용된다.

리뷰어 피드백 (실제 인용)

  • PR #505  CardPreview.tsx:4cardBrand: string이 너무 넓습니다.”
  • PR #513  types.ts:6“튜플 타입을 사용하면 컴파일 타임에 길이를 보장할 수 있어요!”
  • PR #514  CardNetworkBrand.tsx:5“리터럴 유니온 타입을 쓰면 잘못된 값이 들어오는 것을 컴파일 타임에 잡을 수 있어요!”
  • PR #515  types.ts:3“string[] 대신 튜플(cardNumbers 4칸, expirationPeriod 2칸)”
  • PR #524  useInputHandle.ts:7“string, string[] 둘다 고려 가능하게 해볼 수 있을까요?”

토론 질문

  1. cardNumbers: string[] 타입을 가진 함수에서 cardNumbers[3]을 안전하게 사용하려면 — 어떤 런타임 검사가 필요한가? 그 검사 없이 사용하면 어떤 위험이 있는가?
  2. cardNumbers: [string, string, string, string] (튜플)로 바꾸면 — 그 검사 중 어떤 것이 컴파일러에게 옮겨지는가?
  3. cardBrand: string'visa' | 'master' | null로 좁히면 — 코드의 어떤 분기가 작성하지 않아도 되는 분기로 바뀌는가?

연관 카드

Last updated on