2026. 7. 8.
React state 직접 수정이 화면 업데이트를 막는 이유
AI가 만든 React 화면에서 변수나 콘솔 값은 바뀐 것 같은데 UI가 그대로라면 CSS나 새로고침보다 state 직접 수정을 먼저 의심해야 합니다. 객체와 배열을 새 값으로 바꾸는 최소 수정 순서를 정리합니다.

AI가 만든 React 화면에서 버튼을 눌렀고, 콘솔에는 값도 바뀐 것 같은데 화면은 그대로일 때가 있습니다. 이때 초보자는 보통 CSS, 새로고침, 컴포넌트 재작성부터 의심합니다. 하지만 객체나 배열 state를 쓰는 화면이라면 먼저 볼 곳은 따로 있습니다. state 안의 값을 직접 고친 뒤 같은 값을 다시 넣고 있지 않은지입니다.
결론부터 말하면, React 화면이 안 바뀌는 첫 확인은 "값이 정말 바뀌었나"가 아니라 "React가 새 값이라고 볼 수 있는 형태로 바뀌었나"입니다. react.dev의 객체 state 문서는 객체를 직접 바꾸지 말고 새 객체나 복사본을 만들어 state에 넣으라고 설명합니다. 배열 state 문서도 배열을 읽기 전용처럼 다루고, push, pop, splice처럼 원본을 바꾸는 방식 대신 새 배열을 만드는 방식을 권합니다. 화면이 그대로라면 전체 재작성보다 같은 참조를 다시 넣은 줄부터 찾는 편이 빠릅니다.
먼저 직접 수정 줄을 찾습니다
가장 흔한 모양은 객체 state를 꺼내서 값을 바꾼 뒤 같은 객체를 setter에 다시 넣는 코드입니다.
const [profile, setProfile] = useState({
name: "Mina",
level: "beginner"
});
function promote() {
profile.level = "junior";
setProfile(profile);
}
profile.level 값은 바뀐 것처럼 보입니다. 하지만 profile이라는 객체 자체는 같은 객체입니다. React의 useState 문서는 다음 state가 이전 state와 Object.is 비교로 같으면 컴포넌트와 자식 렌더링을 건너뛸 수 있다고 설명합니다. 그래서 이런 코드는 초보자에게 특히 헷갈립니다. 콘솔에는 값이 바뀐 것처럼 보이는데, 화면은 새 값으로 그려지지 않을 수 있습니다.
수정은 새 객체를 만들어 넣는 방식으로 시작합니다. 여기서 쓰는 spread 문법은 developer.mozilla.org의 JavaScript 문서가 설명하는 객체·배열 복사 표현과 같은 계열입니다.
function promote() {
setProfile({
...profile,
level: "junior"
});
}
이 코드는 "기존 값 대부분은 유지하되 level만 바뀐 새 객체"를 만듭니다. React가 새 state로 볼 수 있는 값이 setter에 들어갑니다. 여기서 목표는 멋진 패턴을 외우는 것이 아닙니다. 바뀐 줄이 원본을 직접 건드리는지, 새 값을 만들어 넘기는지를 구분하는 것입니다.
React select가 안 바뀔 때 value부터 보는 순서도 같은 관점으로 읽을 수 있습니다. 화면 입력이 말을 듣지 않을 때는 보이는 UI보다 먼저 state가 어디서 읽히고 어디서 바뀌는지 좁혀야 합니다.
배열은 push 대신 새 배열을 만듭니다
AI가 만든 할 일 목록, 카드 목록, 장바구니 예제에서 자주 나오는 실수는 배열에 push를 쓰는 코드입니다.
const [todos, setTodos] = useState<string[]>([]);
function addTodo(text: string) {
todos.push(text);
setTodos(todos);
}
push는 새 배열을 돌려주는 함수가 아닙니다. 기존 배열을 바꿉니다. React 공식 문서의 배열 state 가이드는 state 안의 배열을 읽기 전용처럼 다루라고 설명하고, push, pop, splice 같은 원본 변경 메서드를 피하라고 안내합니다.
새 항목을 추가할 때는 spread로 새 배열을 만듭니다.
function addTodo(text: string) {
setTodos([...todos, text]);
}
항목 하나를 바꿀 때는 map이 더 안전합니다.
function renameTodo(index: number, nextText: string) {
setTodos(
todos.map((todo, todoIndex) =>
todoIndex === index ? nextText : todo
)
);
}
항목을 지울 때는 filter로 남길 항목만 새 배열로 만듭니다.
function removeTodo(index: number) {
setTodos(todos.filter((_, todoIndex) => todoIndex !== index));
}
이 세 가지를 외워야 하는 이유는 문법 때문이 아닙니다. AI에게 "배열 업데이트 고쳐줘"라고만 말하면 컴포넌트를 크게 바꿀 수 있습니다. 반대로 "push로 원본 배열을 바꾸는 줄만 새 배열 업데이트로 바꿔 달라"고 말하면 수정 범위가 줄어듭니다.
중첩 객체는 바꾸는 깊이까지 복사합니다
spread를 한 번 썼는데도 화면이 이상하면 중첩 객체를 봐야 합니다. 예를 들어 설정 화면이 이런 state를 쓴다고 가정해 봅니다.
const [settings, setSettings] = useState({
user: {
name: "Mina",
theme: "light"
}
});
아래 코드는 겉 객체만 새로 만들고, 안쪽 user 객체는 직접 바꿉니다.
function changeTheme() {
settings.user.theme = "dark";
setSettings({ ...settings });
}
이 코드는 어떤 경우에는 화면이 바뀌는 것처럼 보일 수도 있습니다. 그래서 더 위험합니다. 다른 컴포넌트가 settings.user를 props로 받거나 memoized 되어 있으면 같은 안쪽 객체 때문에 문제가 이어질 수 있습니다. React 공식 문서도 중첩 객체를 업데이트할 때는 바꾸는 경로의 객체를 새로 만들어야 한다고 설명합니다.
안전한 형태는 이렇게 바꾸는 깊이까지 복사하는 것입니다.
function changeTheme() {
setSettings({
...settings,
user: {
...settings.user,
theme: "dark"
}
});
}
이때 초보자가 기억할 기준은 간단합니다. 점으로 들어가서 바꾸는 값이 있다면, 그 경로의 객체도 새로 만들어야 합니다. settings.user.theme을 바꾼다면 user도 새 객체가 되어야 합니다.
AI에게는 state 모양과 문제 줄만 붙입니다
이 문제를 AI에게 맡길 때 가장 나쁜 요청은 다음과 같습니다.
React 화면이 안 바뀝니다. 전체 코드 고쳐 주세요.
이 문장은 AI가 CSS, 이벤트, 라우팅, 컴포넌트 구조까지 한 번에 바꾸게 만듭니다. 지금 필요한 것은 전체 재작성 요청이 아니라 state 업데이트 줄을 좁히는 요청입니다.
React 화면에서 값은 바뀐 것 같은데 UI가 그대로입니다.
전체 컴포넌트를 다시 만들지 말고 state 직접 수정 여부만 봐 주세요.
state 모양:
[예: profile, todos, settings 객체를 붙여 넣기]
문제가 의심되는 수정 줄:
[예: todos.push(text); setTodos(todos);]
기대 화면 변화:
[예: 버튼 클릭 후 목록에 새 항목이 보여야 합니다]
바꾸지 말아야 할 것:
[예: CSS, API 호출, 컴포넌트 파일 분리 방식]
요청:
1. 원본 객체나 배열을 직접 바꾸는 줄이 있는지 표시해 주세요.
2. 새 객체/새 배열을 만들어 setter에 넣는 최소 수정으로 바꿔 주세요.
3. 중첩 객체라면 어느 깊이까지 복사해야 하는지 주석으로 설명해 주세요.
이 프롬프트의 핵심은 AI에게 정답을 길게 쓰게 하는 것이 아닙니다. AI가 건드릴 범위를 state 업데이트 경로로 제한하는 것입니다. AI가 파일을 너무 많이 고칠 때 범위를 막는 프롬프트를 써 본 독자라면 같은 원리입니다. 문제를 작게 자르면 수정도 작아집니다.
오늘은 네 줄만 채우고 다시 봅니다
React state 직접 수정 문제는 처음에는 어렵게 느껴지지만, 확인 순서는 짧습니다.
화면에서 안 바뀌는 값:
그 값을 들고 있는 state:
원본을 직접 바꾸는 줄:
새 객체/새 배열로 바꿀 줄:
이 네 줄이 비어 있으면 AI는 추측으로 고칩니다. 네 줄이 채워지면 수정 범위가 push, 직접 대입, 중첩 객체 복사 중 하나로 줄어듭니다. 코딩사관학교 초보자라면 오늘은 한 가지만 기억하면 됩니다. React 화면이 그대로일 때 첫 행동은 전체 재작성 요청이 아니라 state 직접 수정 줄 찾기입니다.
이 글은 AI 코딩과 개발 학습의 일반 정보 제공 목적입니다. 도구, 모델, 커리큘럼, 요금은 버전과 시점에 따라 달라질 수 있으므로 실습이나 도입 전 공식 문서와 최신 릴리스 노트를 확인하세요.
참고 출처
- React Docs, Updating Objects in State: https://react.dev/learn/updating-objects-in-state
- React Docs, Updating Arrays in State: https://react.dev/learn/updating-arrays-in-state
- React Docs,
useState: https://react.dev/reference/react/useState - MDN, Spread syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
다음으로 읽을 기사
같은 흐름으로 이어 읽기 좋은 기사만 추려 보여줍니다.
첫 번째 댓글을 남겨보세요
여러분의 생각이 다른 독자에게 도움이 됩니다.
댓글 0
이 글을 읽은 독자들의 생각을 나눠보세요.