Q3. 카드번호 — UI는 4개의 input, 데이터는 1개의 문자열로 다룰 수 있을까?
크루의 질문
“첫번째 인풋에 4자, 2번째 인풋에 2자, 3번째 인풋에 4자, 4번째 인풋에 4자의 번호를 입력하여 총 14자의 카드번호를 입력받았을 때, 이를 결국 하나의 문자열(숫자로 구성된)로 이어붙여 다루게 되는데, 이 정보를 바탕으로 카드 미리보기를 보여주면 빠진 2자가 가장 뒤에 있다고 표시하게 될 것 같습니다. 이를 위해 굳이 카드 번호를 4개의 영역으로 나눠 관리해야 하는지 고민이 듭니다.” — 클라우디·해니 페어
“하나의 데이터(카드번호)이지만 input이 4개인 경우, 상태를 나누는 것이 맞을까?” — 도넛·두부 페어
AS-IS 코드
🔍 읽기 전에
카드번호의 진짜 단위는 1개일까요, 4개일까요?
이 코드에서 두 단위가 만나는 자리는 어디인가요?
// src/components/CardAdditionForm.js
const formatCardNumbers = (numbers) => {
const [...firstTwoNumbers] = numbers.slice(0, 2);
const [...secondTwoNumbers] = numbers.slice(2);
const hiddenNumbers = secondTwoNumbers.map((value) =>
FORMAT_CHAR.HIDDEN_NUMBER.repeat(value.length)
);
return [...firstTwoNumbers, ...hiddenNumbers]
.map((number) => (number ? number : ""))
.join(FORMAT_CHAR.CARD_NUMBERS_SEPARATOR);
};
const formatExpirationDate = (expirationDate) => {
return Object.values(expirationDate)
.filter((value) => value !== "")
.join(FORMAT_CHAR.EXPIRATION_DATE_SEPARATOR);
};
const CardAdditionForm = (props) => {
const [
cardNumbers,
cardNumbersInputRef,
onCardNumbersChange,공식문서 단서 — Reacting to Input with State
React 공식문서 Reacting to Input with State (한글 번역)는 명령형 사고에서 선언형 사고로 옮겨가는 다섯 단계를 안내한다:
- Identify your component’s different visual states.
- Determine what triggers those state changes.
- Represent the state in memory using
useState.- Remove any non-essential state variables.
- Connect the event handlers to set the state.
특히 4단계의 “필수적이지 않은 state를 제거한다” 는 기준이 클라우디·해니 페어의 고민과 직접 만난다. 여기에 Choosing the State Structure 의 “렌더링 중 기존 props/state로 계산할 수 있으면 state에 두지 않는다” 는 원칙을 함께 적용해볼 수 있다. 4분할은 UI 표현일 뿐 데이터의 본질은 1개의 문자열이라면, 4분할은 파생이고 상태로 둘 필요가 없다.
이 카드는 Q2와 함께 읽으면 좋다. Q2가 “여러 종류의 상태를 묶을까” 를 묻는다면, Q3은 “같은 종류의 상태를 UI 단위에 맞춰 나눌까” 를 묻는다.
선배 PR 읽기 가이드
선배 PR 읽기 가이드 — 펼쳐보기
추가 읽을거리 — Kent C. Dodds
외부 글 모음 — 펼쳐보기
Don’t Sync State. Derive It!
- 원문: kentcdodds.com/blog/dont-sync-state-derive-it
- 한 줄 요약: 글의 직접 예시는 tic-tac-toe의 winner/status나 form의 warning message처럼 명확한 파생값이다. 같은 정신을 카드번호 4분할에 한 단계 옮겨 적용하면 — 4분할 UI가 단지 표현이라면 4개를 동기화하지 말고 1개의 source(또는 그 반대)에서 파생시키는 쪽이 동기화 비용을 없애 준다. 클라우디·해니의 직관과 같은 방향.
useState lazy initialization and function updates
- 원문: kentcdodds.com/blog/use-state-lazy-initialization-and-function-updates
- 한 줄 요약: 4분할 input의 setter가 이전 값을 참조해야 할 때,
setX(prev => ...)함수형 업데이트가 왜 필요한지. 자동 포커스 이동 같은 연속된 업데이트에서 자주 만나는 상황.
연관 PR 더 보기
이 주제에 매핑된 1단계 PR 전체 — 펼쳐보기
자기 가설에 가까운 PR을 골라 추가로 비교해보세요.
| PR | 작성자 | 핵심 키워드 |
|---|---|---|
| #3 | @jho2301 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #4 | @SunYoungKwon | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #8 | @goni-ssi | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #9 | @0307kwon (대표) | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 카드사/브랜드 판별 |
| #11 | @Tanney-102 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 카드사/브랜드 판별 |
| #17 | @zigsong | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 카드사/브랜드 판별 |
| #18 | @0imbean0 | 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #22 | @ddongule | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #29 | @swon3210 | state 위치 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 카드사/브랜드 판별 |
| #71 | @DomMorello | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #74 | @LAH1203 | custom hook / 카드번호 4분할 |
| #75 | @intae92 | state 위치 / 카드번호 4분할 |
| #77 | @JUDONGHYEOK | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #78 | @soyi47 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #82 | @hwangstar156 | state 위치 / 카드번호 4분할 / 카드사/브랜드 판별 |
| #84 | @greenblues1190 | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #89 | @onschan | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #90 | @prefer2 | 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #91 | @moonheekim0118 | custom hook / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #94 | @euijinkk | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #95 | @KangYunHo1221 | state 위치 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #96 | @jhy979 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #97 | @kamwoo | state 위치 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #100 | @woose28 | state 위치 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #102 | @liswktjs | state 위치 / 카드번호 4분할 / 검증·에러 처리 |
| #103 | @kkojae91 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #105 | @ronci | custom hook / 카드번호 4분할 / 카드사/브랜드 판별 |
| #106 | @jin7969 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #187 | @gabrielyoon7 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #189 | @nlom0218 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #190 | @dladncks1217 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 카드사/브랜드 판별 |
| #191 | @kyw0716 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #193 | @suyoungj | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 |
| #194 | @shackstack | 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #197 | @xodms0309 | state 위치 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #199 | @guridaek | state 위치 / 카드번호 4분할 |
| #200 | @hae-on | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #201 | @HyeryongChoi | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #205 | @2yunseong | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #210 | @chsua | 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #212 | @jw-r | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 |
| #213 | @wzrabbit | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #218 | @tkdrb12 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #227 | @jeongwusi | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #230 | @gyeongza | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #232 | @Gilpop8663 | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #332 | @seongjinme | 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #333 | @jinyoung234 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #334 | @vi-wolhwa | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #336 | @jaeml06 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #337 | @jinhokim98 | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #338 | @Largopie | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #340 | @chosim-dvlpr | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #341 | @simorimi | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #343 | @brgndyy | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #344 | @soi-ha | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #345 | @lurgi | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #348 | @pp449 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #349 | @BadaHertz52 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #351 | @novice0840 | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #353 | @ooherin | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #355 | @greetings1012 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #357 | @00kang | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #359 | @rbgksqkr | custom hook / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #360 | @llqqssttyy | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #362 | @Jaymyong66 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #364 | @soosoo22 | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #365 | @ImxYJL | 객체/배열 state 구조 / 카드번호 4분할 / 카드사/브랜드 판별 |
| #366 | @Parkhanyoung | custom hook / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #368 | @Todari | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #411 | @AHHYUNJU | state 위치 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #412 | @mlnwns | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #413 | @Beomtae | custom hook / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #414 | @ohgus | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #415 | @eunoia-jaxson | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #416 | @jeongyou | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #417 | @sooyeoniya | custom hook / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #418 | @MinSungJe | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 카드사/브랜드 판별 |
| #419 | @spoyodevelop | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #420 | @ShinjungOh | state 위치 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #422 | @thgml05 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #424 | @guesung | 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #427 | @sanghee01 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #430 | @yeongipark (대표) | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #432 | @ha-kuku | 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #433 | @eunsoA | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #434 | @minji2219 | state 위치 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #435 | @dev-dino22 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #436 | @bunju20 | 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #437 | @aydenote | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #438 | @wo-o29 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 |
| #439 | @kimyou1102 | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #440 | @dlsxjzld | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #441 | @hanheel | 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #443 | @jin123457 | 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #444 | @mun-kyeong | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #445 | @Daeun-100 | state 위치 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
195개 전체 인덱스(다른 주제 포함)는 부록에서 확인할 수 있습니다.





