Q5. 검증 로직과 에러 메시지 책임 — 어느 컴포넌트에 둘까?
크루의 질문
“에러 책임을 어떤 컴포넌트에서 관리할지 고민했습니다.” — 윤돌·비비빙 페어
“input의 검증 규칙 및 로직을 어디에서 관리해야 할지.” — 아지·애니 페어
AS-IS 코드
🔍 읽기 전에
이 onChange 안에서 두 가지 일이 일어납니다.
값 저장과 검증, 둘이 같이 일어나야 하나요, 따로 일어날 수 있나요?
// src/entities/cardCVCNumberInputs/CardCVCNumberInputs.tsx
const errorMessage = getErrorMessage();
return (
<StyledContainer>
<label htmlFor="">CVC</label>
<StyledInputWrap>
<Input
value={CVCNumber}
onChange={(e) => {
changeCVCNumber("CVCNumber", e.target.value);
checkValidation({
length: CVC_NUMBER_LENGTH,
value: e.target.value,
type: "CVCNumber",
});
}}
isError={error.CVCNumber !== NO_ERROR}
width="100%"
maxLength={CVC_NUMBER_LENGTH}
placeholder="123"
/>
</StyledInputWrap>
{errorMessage ? (
<StyledErrorMessage>{errorMessage}</StyledErrorMessage>공식문서 단서 — 상태로 표현하는 사고
React 공식문서 Reacting to Input with State (한글 번역) Recap에서:
Declarative programming means describing the UI for each visual state rather than micromanaging the UI (imperative).
에러도 *시각 상태(visual state)*다. *“이 input이 유효한지는 무엇으로 결정되는가”*가 결정 기준 — 그 결정에 필요한 모든 데이터가 어디에 모여 있는지가 검증 로직의 자연스러운 자리다.
You Might Not Need an Effect (한글 번역)는 한 발 더 — “렌더링 도중 props/state로부터 계산할 수 있는 값은 useState에 두지 마라” — 즉 에러 메시지는 종종 상태가 아니라 파생값이다. state.cvc로부터 지금 유효한가가 매번 계산되어 표시될 수 있다면, 에러 상태라는 별도 state는 redundant.
선배 PR 읽기 가이드
선배 PR 읽기 가이드 — 펼쳐보기
선배 PR — 자식이 자기 에러를 보유한 패턴
읽는 관점: “onBlur, onChange, submit 시점 중 어디에서 검증하는지 따라가세요. inline review의 disabled, error, validation 코멘트가 사용자 경험 기준을 드러냅니다.”
선배 PR — 부모가 모든 에러를 보유한 패턴
읽는 관점: “부모가 에러 객체를 들고 확인 버튼 활성화를 계산하는 흐름을 보세요. 자식 컴포넌트가 단순해지는 대신 props 인터페이스가 넓어지는 비용을 같이 찾으면 좋습니다.”
선배 PR — custom hook이 검증을 보유한 패턴
읽는 관점: “validation hook이나 util로 빠진 함수가 UI 상태를 얼마나 알고 있는지 보세요. 순수 검증과 화면 표시 책임이 섞이는 지점이 주요 단서입니다.”
추가 읽을거리 — Kent C. Dodds · Dan Abramov
외부 글 모음 — 펼쳐보기
Kent C. Dodds — Don’t Sync State. Derive It!
- 원문: kentcdodds.com/blog/dont-sync-state-derive-it
- 한 줄 요약: 에러 메시지를 별도 state로 두고 동기화하는 대신, 입력값으로부터 파생시킬 수 있는지 먼저 보라. 윤돌·아지 페어의 “에러 책임을 어디서 관리할지” 질문에 적용해볼 수 있는 기준이다.
Dan Abramov — Before You memo()
- 원문: overreacted.io/before-you-memo
- 한 줄 요약: 성능 최적화를 위해
memo를 쓰기 전에, 상태를 어디로 옮기는가가 더 큰 영향을 준다. 검증·에러 책임이 어디에 있느냐가 리렌더링 범위에 미치는 비용을 다룬다.
연관 PR 더 보기
이 주제에 매핑된 1단계 PR 전체 — 펼쳐보기
자기 가설에 가까운 PR을 골라 추가로 비교해보세요.
| PR | 작성자 | 핵심 키워드 |
|---|---|---|
| #3 | @jho2301 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #4 | @SunYoungKwon | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #10 | @bucketHaneul | 객체/배열 state 구조 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #18 | @0imbean0 | 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #19 | @iborymagic | 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #71 | @DomMorello | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #79 | @wonsss | 검증·에러 처리 / 카드사/브랜드 판별 |
| #80 | @compy-ryu | state 위치 / 검증·에러 처리 |
| #84 | @greenblues1190 | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #89 | @onschan | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #91 | @moonheekim0118 | custom hook / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #94 | @euijinkk | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #96 | @jhy979 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #97 | @kamwoo | state 위치 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #100 | @woose28 | state 위치 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #102 | @liswktjs | state 위치 / 카드번호 4분할 / 검증·에러 처리 |
| #104 | @uk960214 | state 위치 / 객체/배열 state 구조 / 검증·에러 처리 |
| #106 | @jin7969 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #189 | @nlom0218 | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #194 | @shackstack | 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #198 | @ashleysyheo | state 위치 / 객체/배열 state 구조 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #200 | @hae-on | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #206 | @turtle601 | custom hook / 객체/배열 state 구조 / 검증·에러 처리 |
| #207 | @inyeong-kang | custom hook / 객체/배열 state 구조 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #213 | @wzrabbit | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #214 | @n0eyes | custom hook / 객체/배열 state 구조 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #216 | @yeopto | state 위치 / 검증·에러 처리 |
| #218 | @tkdrb12 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #223 | @geuntaek1013 | state 위치 / 검증·에러 처리 |
| #225 | @woo-jk | state 위치 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #232 | @Gilpop8663 | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #331 | @anttiey | state 위치 / 공통 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/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #339 | @Yoonkyoungme | state 위치 / 공통 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/컴포넌트 합성 / 검증·에러 처리 |
| #350 | @pakxe | 검증·에러 처리 / 카드사/브랜드 판별 |
| #351 | @novice0840 | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #353 | @ooherin | custom hook / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #354 | @hwinkr | state 위치 / 객체/배열 state 구조 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #355 | @greetings1012 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #357 | @00kang | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #358 | @dle234 | state 위치 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #359 | @rbgksqkr | custom hook / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #360 | @llqqssttyy | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #361 | @skiende74 | custom hook / 객체/배열 state 구조 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #362 | @Jaymyong66 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #364 | @soosoo22 | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 |
| #366 | @Parkhanyoung | custom hook / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #367 | @ss0526100 | custom hook / 객체/배열 state 구조 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #368 | @Todari | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #410 | @H0ngJu | 검증·에러 처리 |
| #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/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #419 | @spoyodevelop | 객체/배열 state 구조 / 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #420 | @ShinjungOh | state 위치 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #421 | @ExceptAnyone | state 위치 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #422 | @thgml05 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #423 | @yeji0214 | state 위치 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #424 | @guesung | 카드번호 4분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #425 | @Db0111 | state 위치 / 객체/배열 state 구조 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #426 | @rosielsh (대표) | state 위치 / 객체/배열 state 구조 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 |
| #427 | @sanghee01 | 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #428 | @kaori-killer | custom hook / 검증·에러 처리 |
| #430 | @yeongipark (대표) | state 위치 / 객체/배열 state 구조 / 카드번호 4분할 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #431 | @eunwoo-levi | state 위치 / 객체/배열 state 구조 / 검증·에러 처리 |
| #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/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #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분할 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #448 | @hoyyChoi | state 위치 / 객체/배열 state 구조 / 공통 Input/컴포넌트 합성 / 검증·에러 처리 / 카드사/브랜드 판별 |
| #450 | @Daeun-100 | state 위치 / 검증·에러 처리 |
| #452 | @kaori-killer | state 위치 / 검증·에러 처리 |
| #453 | @ExceptAnyone | state 위치 / 검증·에러 처리 / 카드사/브랜드 판별 |
195개 전체 인덱스(다른 주제 포함)는 부록에서 확인할 수 있습니다.





