반응형
Immer는 React에서 상태(state)를 업데이트할 때
마치 state.user.name = ... 처럼 직접 수정(mutate)하는 코드처럼 작성해도,
라이브러리 내부에서 안전한 불변성(immutability) 을 유지한 새로운 state를 만들어주는 도구다.
React는 상태 변경을 “값이 바뀌었다”가 아니라 참조(reference)가 바뀌었는지로 감지한다.
const [user, setUser] = useState({
name: 'Josh',
profile: { age: 30 },
})
function updateAge() {
user.profile.age = 31 // ❌ 원본 수정
setUser(user) // 참조 동일
}
문제점
- user.profile.age 값은 바뀌었지만
- user 객체 자체의 참조는 그대로라서
- React 입장에서는 “상태가 바뀌었는지” 판단이 애매하거나, 최적화 상황에서 변경을 놓칠 수 있음
결과적으로:
- 리렌더가 안 되거나,
- 특정 컴포넌트만 옛 값으로 남거나,
- memo/selector/derived state와 섞이면 버그가 더 심해질 수 있음
얕은 복사(Shallow Copy)로 불변 업데이트
React에서 안전하게 업데이트하려면 바뀌는 경로의 객체를 새로 만들고 나머지는 재사용해야 한다.
setUser(prev => ({
...prev,
profile: {
...prev.profile,
age: 31,
},
}))
- 중첩이 깊어질수록 ...spread가 계속 늘어난다
- 어느 레벨에서 복사를 빼먹으면 버그가 조용히 생긴다
- 조건/분기가 많아지면 업데이트 코드가 “로직”보다 “복사”가 더 커져 가독성이 무너진다.
Immer로 해결: “수정하는 것처럼 쓰되, 결과는 불변 업데이트”
Immer의 핵심 사용법은 produce(baseState, recipe) 형태다.
- baseState(prev) : 원본(절대 직접 변경되지 않음)
- recipe(draft) : 수정 가능한 초안(draft)에 업데이트 로직 작성
- 반환값 : Immer가 만들어 준 새로운 불변 state
import { produce } from 'immer'
setUser(prev =>
produce(prev, draft => {
draft.profile.age = 31
})
)
- draft는 “마음껏 수정해도 되는 임시 초안”
- prev(원본)는 실제로 바뀌지 않는다
- Immer가 변경된 부분만 새 객체로 만들고, 나머지는 기존 참조를 재사용한다
(이걸 구조적 공유(structural sharing) 라고 함)
즉, 개발자는 “무엇을 바꿀지”에 집중한다.
Immer는 state를 직접 고치는 것처럼 코드를 작성해도, 실제로는 draft(초안)에만 변경을 기록하고,
변경된 부분만 새 객체로 만들어 불변성을 유지해주는 도구이다.
반응형
'개발이야기 > React' 카테고리의 다른 글
| useActionState, useEffectEvent (0) | 2025.12.15 |
|---|---|
| ref - DOM 직접 접근 장치 (0) | 2025.11.25 |
| 이벤트 핸들링 (0) | 2025.11.17 |
| 컴포넌트 (0) | 2025.11.17 |
| render(), virtual DOM, JSX (0) | 2025.11.10 |