form은 특이한 태그라서 input, textarea 등은 자기만의 상태를 갖게 된다.
React에서도 상태를 관리하는데, 이걸 React에서 상태로 만들어 변화에 따라 리렌더링을 유발하며 직접 제어하느냐(제어컴포넌트) DOM에 맡기고 리렌더링을 피하느냐(제어컴포넌트)의 두 가지 경우가 있는 것.
event.target.value and ref.current.value are two distinct methods for accessing the value of an HTML input element, primarily within a React context. Their usage and implications differ significantly.
event.target.value is accessed within an event handler, while ref.current.value can be accessed at any point after the ref is initialized and the component has rendered.
event.target.value is for capturing real-time changes in user input for controlled components, whereas ref.current.value is for direct DOM access, often for uncontrolled components or specific DOM interactions.
Using event.target.value in conjunction with setState will typically trigger re-renders. Accessing ref.current.value directly does not cause re-renders.
For most form inputs in React, event.target.value combined with controlled components is the recommended approach for managing input state and ensuring a predictable UI. Refs should be used sparingly and for specific use cases where direct DOM interaction is required.
장점
단점
현재 코드는 "ref의 리렌더링 방지 효과"는 못 살리지만,
"value를 state로 안 올리는 효과"는 얻고 있습니다.
완전히 리렌더링을 막으려면 글자수 표시를 포기해야 합니다.
"그럼, 리렌더링 횟수는 똑같이 일어나더라도, state 개수가 줄어드는 게 좋은 이유는 뭐야? 그리고 이 이유가, 'useRef should be sparingly used'라는 문구를 상쇄할 만큼 유의미해?"
솔직히 말하면, 이 경우에는 state 개수를 줄이는 것이 큰 의미가 없습니다. 오히려 코드가 더 복잡해지는 단점이 있습니다.
State를 줄여도 별 이득이 없는 이유
1. 리렌더링은 똑같이 발생
typescript// 현재 방식 (ref + state)
setNameLength(value.length) // 리렌더링 1회
// state만 쓰는 방식
setName(value) // 리렌더링 1회
성능상 차이 전혀 없음.
nameLength (number) vs name (string)
실제 메모리 차이는 무시할 수준
typescript
// ref + state: 두 가지 패턴 혼재
const nameRef = useRef<HTMLInputElement>(null);
const [nameLength, setNameLength] = useState(0);
// 제출 시: nameRef.current?.value
// state만: 일관된 패턴
const [name, setName] = useState("");
// 제출 시: name
State만 쓰는 게 더 명확하고 React스러움.
"Ref는 sparingly use" 원칙
React 공식 문서의 의도는:
Ref를 써야 할 때:
DOM 직접 조작 (focus, scroll, 애니메이션)
리렌더링 없이 값 저장 (타이머 ID, 이전 값 추적)
외부 라이브러리 통합
Ref를 피해야 할 때:
폼 입력 관리 ← 당신의 경우
UI에 표시되는 데이터
React가 추적해야 하는 상태
결론: 당신의 코드는 개선이 필요합니다
현재 방식은 "ref의 장점도 못 살리면서" 복잡도만 높입니다.
네, 렌더링 횟수를 줄이면 성능에 도움이 됩니다. 하지만 실제 효과는 상황에 따라 다릅니다.
렌더링 최적화의 성능 효과
이론적 이득:
typescript// 현재: 타이핑할 때마다 리렌더링
"a" 입력 → 리렌더링
"ab" 입력 → 리렌더링
"abc" 입력 → 리렌더링
// 3글자 = 3번 리렌더링
// 최적화: 리렌더링 없음
"abc" 입력 → 리렌더링 0번
각 리렌더링마다 일어나는 작업:
Virtual DOM 재생성
Diffing 알고리즘 실행
하위 컴포넌트들 재평가
useEffect, useMemo 등 재실행
실제 성능 측정
하지만 당신의 폼 컴포넌트 수준에서는 차이가 거의 없습니다:
측정해보면:
typescriptconst start = performance.now();
// 리렌더링 발생
const end = performance.now();
console.log(렌더링 시간: ${end - start}ms); // 보통 0.1~1ms
이유:
컴포넌트가 상대적으로 단순함
하위 컴포넌트가 많지 않음
복잡한 계산이나 네트워크 요청 없음
언제 렌더링 최적화가 중요한가
의미 있는 경우:
typescript// 1. 대용량 리스트
{items.map(item => )} // 1000개 아이템
// 2. 복잡한 계산
const expensiveValue = useMemo(() => {
return heavyCalculation(data); // 100ms 소요
}, [data]);
// 3. 깊은 컴포넌트 트리
{/ 50개 레벨 /}
의미 없는 경우 (당신의 상황):
단순한 폼 컴포넌트
몇 개 안 되는 input 필드
복잡한 자식 컴포넌트 없음
결론
렌더링 횟수를 줄이는 것은 이론적으로는 항상 좋습니다. 하지만:
당신의 폼에서는 체감할 수 있는 성능 향상이 없음
코드 복잡도 증가의 단점이 더 큼
유지보수성과 가독성이 더 중요함
"premature optimization is the root of all evil" 원칙에 따라, 실제 성능 병목이 발생하기 전까지는 명확하고 유지보수하기 쉬운 코드가 우선입니다.
당신의 경우 controlled component (state만 사용)가 가장 적절한 선택입니다.