useStates는 왜 배열을 반환하는가?
React의 useState 훅은 상태 관리의 핵심 메커니즘입니다. 독특하게도 useState는 서로 다른 배열을 반환합니다. 왜 이렇게 디자인 되었는지 근본적인 이유를 탐구해보겠습니다.
Map을 사용하지 않는 이유
useState는 왜 서로 관련이 없는 두 개의 값이 있는 배열을 반환할까요? 자바스크립트는 파이썬의 tuple처럼 서로 다른 값을 나이스하게 결합할 수 없습니다.
그렇기에 map을 사용하면 훌륭하게 문제를 해결할 수 있을 것 같습니다.
모던 자바스크립트 문법에서는 구조분해를 활용해 객체를 배열하는 방법을 즐겨씁니다.
리액트 컴포넌트에 props을 전달할 때도 구조분해를 활용하여 객체를 전달합니다.
function button({name, color}){
return ...
}
useState 역시 다음 예시처럼 객체 구조분해로 사용하면 되지 않을까요??
const {
value: counter,
set: setCounter,
} = useState(0); // 실제로 동작하지는 않음
위 코드가 괜찮은 것 같지만 사실 큰 문제가 있습니다. map의 경우 구조 분해로 더 편리하게 쓸 수 있지만 여러개의 set 함수에 대해 대응할 수가 없습니다.
예를 들어 setCounter가 set라는 이름으로 저장되어 있고 여기에 setPrint라는 상태가 추가된다고 했을 때 set에는 이미 counter가 있기에 의미있는 이름으로 또 바꿔줘야 합니다.
이렇게 map의 key를 바꿔주는 행위가 굉장히 불편한 것입니다.
그래서 차라리 key로 맵핑하는 것보다 값 자체를 배열로 반환하여 처리하는 것입니다.
배열 구조 분해의 유연성
useState가 배열을 반환하는 또 하나의 주된 이유는 배열 구조 분해(destructuring)의 강력한 유연성 때문입니다.
const [state, setState] = useState(initialValue);
이 패턴은 개발자에게 다음과 같은 이점을 제공합니다.
- 명시적 이름 지정: 개발자가 상태와 상태 업데이트 함수에 원하는 이름을 자유롭게 부여할 수 있습니다.
- 즉각적인 가독성: 변수 이름만으로 해당 상태의 목적과 역할을 직관적으로 이해할 수 있습니다.
- 유연한 활용: 여러 상태를 동시에 선언하고 관리하기 쉽습니다.
설계 철학: 함수형 프로그래밍과의 조화
객체 형태의 상태 관리 디자인도 충분히 활용이 가능하지만 코드의 간결성과 유연성 측면에서 배열 구조 분해가 훨씬 유리합니다.
좀 더 구체적인 이유를 살펴봅시다.
불변성 유지
배열 반환은 함수형 프로그래밍의 핵심 원칙인 불변성(Immutability) 을 완벽하게 반영합니다. 상태 업데이트 함수(setState)는 항상 새로운 상태를 생성하며, 기존 상태를 직접 변경하지 않습니다.
* 기술적으로 객체도 동일한 불변성 원칙을 따를 수 있지만 배열 반환이 개발자의 편의성과 코드 가독성 측면에서 훨씬 효과적인 설계라고 생각합니다.
함수형 컴포넌트와의 통합
클래스 컴포넌트의 this.state와 this.setState()와 달리, 함수형 컴포넌트에서는 더 선언적이고 간결한 상태 관리 방식을 제공합니다.
고급 사용 예시
지금까지 설명한 내용을 다양한 상태 관리 상황에 맞춰서 확인해보겠습니다.
다중 상태 관리
다음 코드는 다중 상태를 관리하는 예시입니다. 이름, 이메일, 나이 3개의 값을 각각 상태관리 하려고 합니다.
이 때 각 상태 관리 함수를 배열 구조 분해로 관리하면 보다 독립적인 형태로 처리할 수 있게 됩니다.
function UserProfile() {
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
// 각 상태를 독립적으로 관리 가능
}
복잡한 상태 로직
예제 코드와 같이 복잡한 형태의 상태 관리 상황에서도 배열 구조 분해 형태를 통해 가독성 높은 코드를 획득할 수 있습니다.
function ComplexStateExample() {
const [user, setUser] = useState({
name: '',
preferences: {}
});
const updateUserName = (newName) => {
setUser(prev => ({
...prev,
name: newName
}));
};
}
정리: Map을 사용하지 않는 이유
- 정적 타이핑 어려움
- 이름 충돌 가능성
- 덜 직관적인 사용 패턴
- 직접적인 상태 업데이트가 어려움
감사합니다.
'Front-End > React' 카테고리의 다른 글
CRA vs Vite: React 개발 환경의 새로운 패러다임 (0) | 2025.02.10 |
---|---|
React의 useEffect 훅과 사이드 이펙트 알아보기 (1) | 2025.02.08 |
React 컴포넌트 구현 방식 비교: 화살표 함수 vs 일반 함수 (1) | 2025.02.07 |
useReducer 완벽하게 파헤치기 (1) | 2025.01.30 |
useState vs useRef: 언제 무엇을 써야 할까? (0) | 2025.01.29 |