2026. 7. 8.

React state 직접 수정이 화면 업데이트를 막는 이유

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

4 min read
React state 직접 수정이 화면 업데이트를 막는 이유 대표 이미지

AI가 만든 React 화면에서 버튼을 눌렀고, 콘솔에는 값도 바뀐 것 같은데 화면은 그대로일 때가 있습니다. 이때 초보자는 보통 CSS, 새로고침, 컴포넌트 재작성부터 의심합니다. 하지만 객체나 배열 state를 쓰는 화면이라면 먼저 볼 곳은 따로 있습니다. state 안의 값을 직접 고친 뒤 같은 값을 다시 넣고 있지 않은지입니다.

결론부터 말하면, React 화면이 안 바뀌는 첫 확인은 "값이 정말 바뀌었나"가 아니라 "React가 새 값이라고 볼 수 있는 형태로 바뀌었나"입니다. react.dev의 객체 state 문서는 객체를 직접 바꾸지 말고 새 객체나 복사본을 만들어 state에 넣으라고 설명합니다. 배열 state 문서도 배열을 읽기 전용처럼 다루고, push, pop, splice처럼 원본을 바꾸는 방식 대신 새 배열을 만드는 방식을 권합니다. 화면이 그대로라면 전체 재작성보다 같은 참조를 다시 넣은 줄부터 찾는 편이 빠릅니다.

React state를 직접 수정해 화면이 갱신되지 않는 상황을 설명한 작업 화면

먼저 직접 수정 줄을 찾습니다

가장 흔한 모양은 객체 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로 원본 배열을 바꾸는 줄만 새 배열 업데이트로 바꿔 달라"고 말하면 수정 범위가 줄어듭니다.

React 객체와 배열 state를 확인하는 네 단계 체크리스트

중첩 객체는 바꾸는 깊이까지 복사합니다

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 업데이트 줄을 좁히는 요청입니다.

AI에게 React 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 코딩과 개발 학습의 일반 정보 제공 목적입니다. 도구, 모델, 커리큘럼, 요금은 버전과 시점에 따라 달라질 수 있으므로 실습이나 도입 전 공식 문서와 최신 릴리스 노트를 확인하세요.

참고 출처

다음으로 읽을 기사

같은 흐름으로 이어 읽기 좋은 기사만 추려 보여줍니다.

댓글 0

이 글을 읽은 독자들의 생각을 나눠보세요.

비밀번호(선택)

첫 번째 댓글을 남겨보세요

여러분의 생각이 다른 독자에게 도움이 됩니다.