안녕하세요, 여러분! 20년 넘게 React와 함께한 “코딩하는곰”입니다. 오늘은 React 개발자라면 한 번쯤 마주치는 고민, “컴포넌트가 리렌더링될 때 input 값이 초기화되는 문제”에 대해 깊이 있게 다루어보려고 합니다. 특히 Controlled와 Uncontrolled 컴포넌트의 차이를 중심으로 어떤 상황에서 어떤 접근법이 더 적합한지, 실제 프로젝트에서 겪었던 경험과 함께 상세히 설명드리겠습니다. 이 글을 통해 여러분의 React 폼 처리 기술을 한 단계 업그레이드하시길 바랍니다!
React에서 컴포넌트가 리렌더링될 때 input 값이 사라지는 현상은 많은 개발자들을 당황하게 만듭니다. 이 문제의 근본적인 원인은 React의 상태 관리와 컴포넌트 생명주기에 있습니다.
React는 상태(state)가 변경되거나 부모 컴포넌트에서 전달받는 props가 변경될 때 컴포넌트를 리렌더링합니다. 이 과정에서 가상 DOM(Virtual DOM)이 재생성되고, 실제 DOM과의 비교(diffing)를 통해 필요한 부분만 업데이트됩니다. 하지만 input 요소의 경우, 이 메커니즘이 예상과 다르게 동작할 때가 있습니다.
// 문제가 발생하는 일반적인 예시function ProblematicForm() {const [count, setCount] = useState(0);const handleSubmit = (e) => {e.preventDefault();// 폼 제출 처리};return (<div><button onClick={() => setCount(count + 1)}>카운트 증가: {count}</button><form onSubmit={handleSubmit}><input type="text" placeholder="입력해보세요" /></form></div>);}
위 예제에서 버튼을 클릭할 때마다 컴포넌트가 리렌더링되면서 input에 입력한 값이 사라집니다. 이는 input이 uncontrolled 상태로 관리되고 있기 때문입니다.
⚡ 개발 실력을 향상시키고 싶다면, (Vue.js) provide/inject API로 중첩 컴포넌트 데이터 공유하는 완벽 가이드를 참고해보세요.
Controlled 컴포넌트는 React의 상태(state)를 통해 input의 값을 완전히 제어하는 방식입니다. input의 value prop을 React 상태로 설정하고, onChange 이벤트를 통해 상태를 업데이트하는 패턴을 말합니다.
import React, { useState } from 'react';function ControlledForm() {const [formData, setFormData] = useState({username: '',email: '',password: ''});const handleChange = (e) => {const { name, value } = e.target;setFormData(prev => ({...prev,[name]: value}));};const handleSubmit = (e) => {e.preventDefault();console.log('제출된 데이터:', formData);};return (<form onSubmit={handleSubmit}><div><label htmlFor="username">사용자명:</label><inputtype="text"id="username"name="username"value={formData.username}onChange={handleChange}/></div><div><label htmlFor="email">이메일:</label><inputtype="email"id="email"name="email"value={formData.email}onChange={handleChange}/></div><div><label htmlFor="password">비밀번호:</label><inputtype="password"id="password"name="password"value={formData.password}onChange={handleChange}/></div><button type="submit">제출</button></form>);}
import React, { useState, useCallback, useMemo } from 'react';function OptimizedControlledForm() {const [formData, setFormData] = useState({username: '',email: '',password: ''});// useCallback을 사용한 메모이제이션const handleChange = useCallback((e) => {const { name, value } = e.target;setFormData(prev => ({...prev,[name]: value}));}, []);// useMemo를 사용한 계산값 메모이제이션const isFormValid = useMemo(() => {return formData.username.length > 0 &&formData.email.includes('@') &&formData.password.length >= 8;}, [formData]);const handleSubmit = useCallback((e) => {e.preventDefault();if (isFormValid) {console.log('제출된 데이터:', formData);}}, [formData, isFormValid]);return (<form onSubmit={handleSubmit}>{/* 입력 필드들 */}</form>);}
로또 번호를 QR코드로 빠르게 확인하고 싶다면, AI 기반 로또 번호 추천 앱 지니로또AI를 다운로드해보세요.
Uncontrolled 컴포넌트는 React 상태 대신 DOM 자체에서 input 값을 관리하는 방식입니다. useRef 훅을 사용하여 DOM 요소에 직접 접근하고, 필요한 시점에 값을 읽어옵니다.
import React, { useRef } from 'react';function UncontrolledForm() {const usernameRef = useRef(null);const emailRef = useRef(null);const passwordRef = useRef(null);const handleSubmit = (e) => {e.preventDefault();const formData = {username: usernameRef.current.value,email: emailRef.current.value,password: passwordRef.current.value};console.log('제출된 데이터:', formData);};return (<form onSubmit={handleSubmit}><div><label htmlFor="username">사용자명:</label><inputtype="text"id="username"ref={usernameRef}defaultValue="" // 초기값 설정/></div><div><label htmlFor="email">이메일:</label><inputtype="email"id="email"ref={emailRef}defaultValue=""/></div><div><label htmlFor="password">비밀번호:</label><inputtype="password"id="password"ref={passwordRef}defaultValue=""/></div><button type="submit">제출</button></form>);}
import React, { useRef, useState } from 'react';function HybridForm() {const emailRef = useRef(null);const [emailError, setEmailError] = useState('');const validateEmail = () => {const email = emailRef.current.value;if (!email.includes('@')) {setEmailError('유효한 이메일 주소를 입력해주세요');} else {setEmailError('');}};const handleSubmit = (e) => {e.preventDefault();validateEmail();if (!emailError) {const formData = {email: emailRef.current.value};console.log('제출된 데이터:', formData);}};return (<form onSubmit={handleSubmit}><div><label htmlFor="email">이메일:</label><inputtype="email"id="email"ref={emailRef}onBlur={validateEmail} // 포커스를 잃을 때만 검증defaultValue=""/>{emailError && <span style={{color: 'red'}}>{emailError}</span>}</div><button type="submit">제출</button></form>);}
Controlled 컴포넌트를 선택해야 할 때:
로또 번호를 QR코드로 빠르게 확인하고 싶다면, AI 기반 로또 번호 추천 앱 지니로또AI를 다운로드해보세요.
지금까지 React에서 Controlled와 Uncontrolled 컴포넌트의 차이와 각각의 장단점, 사용 시나리오에 대해 자세히 알아보았습니다. 두 접근법 모두 각자의 장점이 있으며, 상황에 맞게 적절히 선택하는 것이 중요합니다. 개인적인 경험으로는 대부분의 경우 Controlled 컴포넌트를 사용하는 것이 React의 데이터 흐름과 잘 맞으며 유지보수성이 뛰어납니다. 하지만 성능이 중요한 대규모 애플리케이션에서는 Uncontrolled 컴포넌트를 적절히 활용하는 것이 좋습니다. 여러분의 프로젝트 요구사항, 성능 요구치, 개발 팀의 선호도에 따라 최적의 접근법을 선택하시길 바랍니다. React 개발자로서 이 두 패턴을 자유자재로 활용할 수 있다면 더 견고하고 효율적인 애플리케이 션을 개발할 수 있을 것입니다. 다음 포스팅에서는 이어서 React 폼 라이브러리 비교와 실제 프로젝트 적용 사례에 대해 더 깊이 있게 다루어보겠습니다. 질문이 있으시면 언제든지 댓글로 남겨주세요! 함께 성장하는 React 개발자가 되는 그날까지, 코딩하는곰이 함께하겠습니다!
👍 믿을 수 있는 건강기능식품 트렌드를 알고 싶다면, 두뇌엔 포스파티딜세린&징코를 참고해보세요.
