state 객체로 묶기
25
작성한 질문수 8
mport { useState } from"react"; functionSignupForm() { const [username, setUsername] = useState(""); const [email, setEmail] = useState(""); const [agree, setAgree] = useState(false); functionhandleSubmit(e) { e.preventDefault(); // 페이지 새로고침 방지console.log("제출 데이터:", { username, email, agree }); alert${username}님, 가입이 완료되었습니다!); } return ( <formonSubmit={handleSubmit}><label> 사용자 이름 <inputtype="text"value={username}onChange={(e) => setUsername(e.target.value)} /> </label><br /><label> 이메일 <inputtype="email"value={email}onChange={(e) => setEmail(e.target.value)} /> </label><br /><label><inputtype="checkbox"checked={agree}onChange={(e) => setAgree(e.target.checked)} /> 약관에 동의합니다 </label><br /><buttontype="submit">가입하기</button></form> ); }
이학습 코드에선 왜
const [username, setUsername] = useState(""); const [email, setEmail] = useState(""); const [agree, setAgree] = useState(false);
이부분들을 객체로 안 묶고 따로 state로 관리 하는지가 궁금합니다!
답변 1
0
안녕하세요 코딩님. 처음 React를 접하거나 기본기를 다지는 과정에서 누구나 한 번쯤은 '관련된 데이터인데 왜 굳이 따로 관리할까?'라는 아키텍처적인 의문을 가지게 됩니다. React의 렌더링 및 상태 관리 메커니즘이라는 근본적인 측면에서 설명해 드리겠습니다.
가장 큰 이유는 학습자의 인지 부하를 줄이고, React의 상태 불변성(Immutability) 원칙을 직관적으로 보여주기 위함입니다.
과거 클래스형 컴포넌트의 상태 업데이트 메서드(this.setState)는 객체의 일부분만 업데이트해도 React가 얕은 병합(Shallow Merge)을 통해 자동으로 나머지 속성을 유지해 주었습니다. 하지만 함수형 컴포넌트의 훅(useState)은 기존 상태를 완전히 새로운 값으로 대체(Replace)하게 됩니다. 만약 이름, 이메일, 동의 여부라는 세 가지 상태를 하나의 폼 데이터 객체로 묶는다면 다음과 같이 코드를 작성해야 합니다.
const [formData, setFormData] = useState({
username: "",
email: "",
agree: false
});
// 상태 업데이트 시 전개 연산자(...)를 사용해 기존 상태를 복사해야 함
onChange={(e) => setFormData(prev => ({
...prev,
username: e.target.value
}))}
이렇게 입력 필드 하나를 변경할 때마다 상태 업데이트 함수 내부에서 이전 상태값을 가져와 전개 연산자를 사용해 기존 객체를 복사하고, 변경할 속성만 덮어씌워야 합니다. 학습 단계에서는 JavaScript의 전개 연산자와 React의 메모리 주소 비교 방식을 동시에 이해해야 하므로, 각 상태의 독립적인 변화를 명확히 보여주기 위해 분리하는 것이 정석입니다.
여기서 자바스크립트 엔진의 메모리 구조 관점으로 팩트 체크를 해보겠습니다. 객체는 힙(Heap) 메모리에 동적으로 할당되고, 변수는 그 메모리 주소를 참조합니다. React의 재조정(Reconciliation) 알고리즘은 상태 객체의 내부 프로퍼티가 변경된 것을 일일이 깊은 비교(Deep Compare)로 감지하지 않습니다. 대신 Object.is 비교를 통해 객체의 메모리 주소, 즉 포인터가 완전히 바뀌었는지를 O(1)의 시간 복잡도로 빠르게 참조 비교하여 렌더링을 트리거합니다. 따라서 전개 연산자를 통해 새로운 메모리 참조를 만들어내는 불변성 유지는 React에서 매우 중요한 근본 개념입니다.
또한, 상태를 개별 변수로 분리하면 향후 로직을 재사용하기 위해 Custom Hook으로 분리하기가 훨씬 수월해집니다. 현재 코드는 폼이 단순하지만 실무에서는 각 입력 필드마다 복잡한 유효성 검사가 필요합니다. 상태가 개별적으로 존재하면 입력 상태와 핸들러를 캡슐화하여 다음과 같이 별도의 훅으로 추상화하기 좋습니다.
// 입력 상태와 핸들러를 캡슐화한 Custom Hook 적용 예시
const usernameInput = useInput("");
const emailInput = useInput("");
// 컴포넌트 내부가 훨씬 깔끔해짐
<input type="text" {...usernameInput} />
이렇게 훅으로 분리하면 컴포넌트 내부에서는 단순히 훅이 반환한 객체를 전개하여 인풋 태그에 속성으로 넘겨주기만 하면 되므로 구조가 아주 깔끔해집니다. 반대로 만약 상태가 하나의 거대한 객체로 묶여 있다면, 특정 필드만의 로직을 분리해 재사용하거나 useEffect의 의존성 배열(Dependency Array)에 특정 필드만 넣고 불필요한 사이드 이펙트 없이 로직을 트리거하기가 매우 까다로워집니다.
성능 측면에서도 의문이 들 수 있습니다. '상태를 따로 관리하면 하나를 입력할 때마다 각각 렌더링이 발생해서 성능에 안 좋은 것이 아닌가?' 생각할 수 있죠.
과거 React 17 이전 버전에서는 합성 이벤트(Synthetic Event)가 아닌 Promise(비동기 통신 이후), setTimeout, 네이티브 이벤트 등 특정 상황에서 여러 상태를 업데이트할 때 리렌더링이 여러 번 발생할 위험이 있었습니다. 하지만 React 18부터 도입된 자동 배칭(Automatic Batching) 덕분에, 어떠한 환경이든 이벤트 핸들러 내에서 아무리 많은 상태 업데이트 함수를 개별적으로 호출하더라도 React가 이를 모아서 단 한 번의 리렌더링으로 일괄 처리합니다. 따라서 렌더링 최적화 관점에서도 상태를 객체로 묶어야 할 강제성이 완전히 사라졌습니다.
학습 단계를 넘어 실제 프로덕션 레벨이나 최신 React 패러다임에서는 이 접근법도 변화하고 있는데, 여기서 정말 유용한 실무 아키텍처 팁을 더해드리겠습니다.
최근의 프론트엔드 트렌드는 질문해주신 코드처럼 모든 입력값을 상태로 추적하는 제어 컴포넌트(Controlled Component) 방식 자체를 점진적으로 지양하는 추세입니다. 대신 웹 표준인 FormData와 비제어 컴포넌트, 그리고 React 19의 Actions를 활용하여 다음과 같이 클라이언트 측의 상태 관리 자체를 최소화하는 방향으로 아키텍처를 설계합니다.
// 최신 React/Next.js 환경에서의 폼 처리 패러다임 (상태 생략)
function SignupForm() {
function handleSubmit(formData) {
const data = Object.fromEntries(formData.entries());
console.log("제출 데이터:", data);
}
return (
<form action={handleSubmit}>
<input name="username" type="text" />
<input name="email" type="email" />
<input name="agree" type="checkbox" />
<button type="submit">가입하기</button>
</form>
);
}
특히 최신 Next.js 환경에서 Server Actions와 결합하게 되면, 폼 태그의 action 속성에 핸들러 함수를 바로 연결하고 인자로 넘어온 폼 데이터를 서버에서 직접 처리할 수 있습니다. 이는 클라이언트 측으로 전송되는 자바스크립트 번들 사이즈를 대폭 줄이고 하이드레이션(Hydration) 비용을 최소화합니다. 더 나아가 자바스크립트가 완전히 로드되기 전에도 폼 제출이 가능해지는 점진적 향상(Progressive Enhancement)을 달성할 수 있으며, 스트리밍(Streaming) 아키텍처와 맞물려 브라우저 네이티브에 가장 가까운 최적화를 이끌어냅니다.
결론적으로 해당 학습 코드는 React의 개별 상태 업데이트 로직을 명확히 이해시키고 객체 불변성 유지라는 추가적인 허들을 제거하기 위해 상태를 분리해 둔 아주 잘 설계된 입문용 패턴입니다. 실무에서 필드가 5개에서 10개 이상으로 늘어나는 복잡한 폼을 구축해야 한다면, 제어 컴포넌트 환경에서는 react-hook-form 같은 최적화 라이브러리를 적극적으로 사용하거나, 최신 환경에서는 방금 말씀드린 Action 기반의 웹 표준 폼 데이터 처리를 도입하는 것이 탄탄한 아키텍처 선택이 될 것입니다.
참고해주세요!
tsakmaster api키
0
10
0
terminal-setup을 해도 shift+Enter 키로 줄바꿈이 되지 않아요.
0
11
1
질문있어요!
1
20
2
ll- al 명령어 관련 문의
0
22
1
cursor ui 변경 문의
0
17
1
상태 표시줄 설치 오류
0
22
1
테마 적용 관련 질문
0
20
1
(macOS) themes.zip 압축 해제가 되지 않습니다.
0
22
2
API 400알람 관련문의
0
27
2
OrderServiceTest 상문주문 테스트 시 update 쿼리 문의
0
17
1
사이트가 너무 달라요
0
55
2
next.js 설치
0
24
1
미션 제출하는 곳
0
23
1
답변이 다른 문제..
0
24
1
123강 질문
0
23
2
백오피스를 개발할 때 아키텍처 구성에 대한 질문
0
20
2
실습에 사용된 MD파일 공유 가능한가요?
1
36
3
mcp서버 추가 후 setting.local.json
0
27
2
ai가 만든 강의인가요?
0
33
1
agents 관련 질문입니다!
0
32
1
plan 모드 결과에 대해
0
35
2
VSCode 설정 문의
0
31
2
클로드 계정 변경
0
32
2
(6.11) 회원가입시 프로필 정보 자동 생성하기 Q. 호출 순서 문의
0
23
1





