React 이벤트·입력 처리의 핵심 흐름
합성 이벤트, 입력→state 흐름, controlled vs uncontrolled·폼 실수 방지 기준을 설명
React에서 이벤트와 입력 처리는 “DOM 이벤트를 어떻게 다루느냐”의 문제가 아니다. 핵심은 사용자 상호작용을 상태 변화로 번역하는 규칙이다. React는 브라우저 이벤트를 그대로 노출하지 않고 감싼다. 이유는 단순하다. UI의 진실을 DOM이 아니라 state에 두기 위해서다.
이벤트 핸들러 구조: “행동”을 상태 변화로 변환
개념
React의 이벤트 핸들러는 DOM을 조작하는 함수가 아니라 상태를 변경하는 함수다.
1
<button onClick={handleClick}>저장</button>
- 이벤트 발생 → 핸들러 호출
- 핸들러 내부에서 state 변경
- state 변경 → UI 재계산
왜 중요한가
이 구조는 다음을 보장한다.
- 단방향 흐름: 이벤트 → state → UI
- UI 변경의 단일 진실: state
- 렌더링 시점과 무관한 예측 가능성
이벤트 핸들러에서 DOM을 직접 만지지 않기 때문에, 렌더링과 상호작용이 분리된다.
언제 문제가 되는가
1) 이벤트에서 DOM을 직접 조작
1
2
3
function handleClick() {
document.querySelector('.btn').classList.add('active');
}
이 코드는 다음 렌더에서 React가 다시 계산하면 사라질 수 있다. 이벤트의 결과는 DOM이 아니라 state로 표현해야 한다.
2) 핸들러를 “실행 결과”로 전달
1
<button onClick={handleClick()}> // ❌
렌더 시 즉시 실행된다. React는 함수 값을 기대한다.
React가 브라우저 이벤트를 감싸는 이유
개념
React 이벤트는 네이티브 DOM 이벤트가 아니라 합성 이벤트(Synthetic Event)다.
왜 중요한가
React가 이벤트를 감싸는 이유는 기술적 편의가 아니라 일관성이다.
- 브라우저 간 이벤트 차이 추상화
- 이벤트 처리 시점과 렌더링 모델의 정합성 유지
- 이벤트 핸들링을 “UI 계산 모델” 안으로 편입
즉, 이벤트도 React 세계의 일부로 만든다.
언제 문제가 되는가
- 이벤트 객체를 비동기적으로 오래 보관하려 할 때
- 네이티브 이벤트와 동일하다고 가정하고 접근할 때
해결 원칙:
- 이벤트 값은 즉시 추출
- 필요한 데이터만 state로 옮긴다
양방향 바인딩의 의미: “입력 ↔ 상태의 동기화”
개념
양방향 바인딩은 “입력이 바뀌면 state가 바뀌고, state가 바뀌면 입력도 바뀌는 구조”다.
1
2
3
4
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
왜 중요한가
이 구조의 본질은 입력 요소를 UI가 아니라 state의 표현으로 만든다는 점이다.
- 입력값의 진실은 DOM이 아니라 state
- 입력 검증, 초기화, 조건 제어가 쉬워짐
- UI와 데이터가 분리되지 않는다
언제 문제가 되는가
1) value만 주고 onChange를 안 줌
1
<input value={name} /> // 입력 불가
React는 이 입력을 읽기 전용으로 취급한다.
2) 모든 입력을 무조건 state로 관리
- 타이핑마다 렌더 발생
- 복잡한 폼에서 성능/복잡도 증가
→ 이때 controlled vs uncontrolled 판단이 필요하다.
Controlled vs Uncontrolled: 통제권의 선택
Controlled 컴포넌트
개념
입력값을 state가 완전히 통제한다.
1
2
3
const [email, setEmail] = useState("");
<input value={email} onChange={e => setEmail(e.target.value)} />
왜 중요한가
- 즉시 검증
- 조건부 비활성화
- 다른 state와의 연동이 쉽다
언제 문제가 되는가
- 단순 입력인데 과도한 state 관리
- 입력이 많아질수록 코드 비대화
Uncontrolled 컴포넌트
개념
입력값을 DOM에 맡기고, 필요한 시점에만 읽는다.
1
2
3
const emailRef = useRef();
<input ref={emailRef} />
왜 중요한가
- 코드 단순
- 렌더링 부담 감소
- 제출 시점에만 값이 필요할 때 적합
언제 문제가 되는가
- 입력값에 따른 UI 반응이 필요할 때
- 실시간 검증/조건부 렌더링이 필요할 때
선택 기준 요약
| 상황 | 권장 |
|---|---|
| 실시간 검증/조건 제어 | Controlled |
| 제출 시점만 필요 | Uncontrolled |
| 다른 state와 강한 연동 | Controlled |
| 단순 폼/성능 민감 | Uncontrolled |
Form 처리에서 자주 생기는 실수
1) submit 기본 동작 방치
1
<form onSubmit={handleSubmit}>
preventDefault()를 빼먹으면 페이지 리로드로 state가 날아간다.
2) input마다 state 남발
firstName,lastName,email,phone…- 검증 로직이 흩어짐
대안:
- 관련 입력은 하나의 객체 state
- 또는 커스텀 훅으로 로직 캡슐화
3) 파생 state로 검증 결과 저장
1
const [isValid, setIsValid] = useState(false);
검증 결과는 보통 입력값으로부터 계산 가능하다. 동기화 비용만 늘린다.
4) 제출 로직과 입력 로직 혼합
- 입력 핸들러에서 서버 요청
- submit 핸들러에서 값 가공 부족
원칙:
- 입력 단계: 값 수집/검증
- 제출 단계: 최종 데이터 처리
정리: React 이벤트 처리의 핵심 규칙
- 이벤트는 DOM 조작이 아니라 state 변경 트리거
- React가 이벤트를 감싸는 이유는 일관된 UI 계산 모델을 유지하기 위해서
- 양방향 바인딩은 입력을 state의 표현으로 만드는 장치
- controlled/uncontrolled는 통제권 선택의 문제
- form 처리의 적은 문법이 아니라 과도한 state와 동기화
결론은 단순하다. 사용자 행동 → state 변화 → UI 재계산 이 흐름을 깨지 않으면, 이벤트와 입력은 복잡해지지 않는다.
