리액트에서 event.curretTarget.value의 값이 undefined가 되는 현상

목차

리액트로 폼을 다루다 보면 event.currentTarget.value 값이 갑자기 undefined로 찍히는 당황스러운 상황을 만나게 됩니다. 분명 입력값이 있는데도 값이 비어 버리는 이 현상은, 대부분 리액트 16 이전 버전의 SyntheticEvent 풀링(pooling) 동작과 관련이 있습니다. 이 글에서는 리액트 currentTarget 값이 undefined가 되는 원인을 짚고, 검증된 해결 방법과 함께 리액트 17 이후 무엇이 달라졌는지까지 정리합니다.

발생 환경

이 문제는 주로 리액트 16 이하 버전에서, 이벤트 핸들러가 끝난 뒤에 실행되는 코드(예: setState의 함수형 업데이트, setTimeout, Promise 콜백)에서 이벤트 객체를 참조할 때 발생합니다. 동기 코드에서는 정상적으로 값을 읽을 수 있지만, 비동기 시점에서 같은 이벤트를 참조하면 값이 사라집니다.

문제 상황

아래 코드처럼 setState의 콜백(함수형 업데이트) 안에서 event.currentTarget.value를 참조하면 값이 undefined로 나옵니다.

const [state, setState] = useState("Init");

return (
  <input
    type="text"
    value={state}
    onChange={(event) => {
      setState((prevState) => {
        return event.currentTarget.value; // undefined
      });
    }}
  />
);

원인: SyntheticEvent 풀링(Pooling)

리액트 currentTarget 값이 undefined가 되는 근본 원인은 리액트의 SyntheticEvent 풀링 때문입니다. 리액트는 성능 최적화를 위해 이벤트 객체를 매번 새로 만들지 않고, 하나의 객체를 재사용(pool)합니다.

이때 이벤트 핸들러 함수가 종료되는 즉시 SyntheticEvent 객체의 모든 속성이 null로 초기화되어 풀로 반환됩니다. 그런데 setState의 함수형 업데이트나 비동기 코드는 이벤트 핸들러가 끝난 다음 실행됩니다. 그 시점에는 이미 이벤트 객체가 초기화된 상태이므로, event.currentTarget.value를 참조하면 undefined가 되는 것입니다.

해결 방법

1. 값을 미리 추출해 변수에 저장 (권장)

가장 간단하고 안전한 방법은 이벤트 핸들러가 살아 있는 동안 값을 변수에 미리 꺼내 두는 것입니다. 이렇게 하면 클로저가 그 변수를 기억하므로, 비동기 콜백에서도 값이 유지됩니다.

const [state, setState] = useState("Init");

return (
  <input
    type="text"
    value={state}
    onChange={(event) => {
      const newValue = event.currentTarget.value; // 미리 값 추출
      setState((prevState) => {
        return newValue;
      });
    }}
  />
);

2. event.persist() 사용

이벤트 객체 자체를 비동기 시점까지 유지해야 한다면 event.persist()를 호출해 풀링을 막을 수 있습니다. 다만 이벤트 전체가 꼭 필요한 경우가 아니라면, 위의 1번 방식(값만 추출)을 권장합니다.

const handleChange = (event) => {
  event.persist(); // 이벤트 객체를 풀로 반환하지 않음
  setTimeout(() => {
    console.log(event.currentTarget.value); // 정상 동작
  }, 0);
};

리액트 17 이후에는 어떻게 바뀌었나

리액트 17부터는 SyntheticEvent 풀링이 완전히 제거되었습니다. 현대 브라우저에서 풀링으로 얻는 성능 이점이 사실상 없어졌기 때문입니다. 따라서 리액트 17 이상을 사용한다면 비동기 콜백에서 이벤트를 참조해도 값이 undefined가 되지 않으며, event.persist()도 더 이상 필요하지 않습니다.

즉, 지금 이 문제를 겪고 있다면 가장 근본적인 해결책은 리액트 버전을 17 이상으로 올리는 것입니다. 다만 레거시 코드를 유지보수하는 상황이라면 위의 값 추출 방식이 가장 무난합니다.

정리

  • 현상: 비동기·함수형 업데이트 콜백에서 event.currentTarget.value가 undefined.
  • 원인: 리액트 16 이하의 SyntheticEvent 풀링으로 이벤트 객체가 초기화됨.
  • 해결: ① 값을 변수에 미리 추출(권장), ② event.persist(), ③ 리액트 17+로 업그레이드.

자세한 배경은 리액트 공식 문서의 Event Pooling 문서를 참고하세요.

💬 댓글 0

💬 댓글을 남기려면?
첫 댓글을 남겨보세요 ✨

이 콘텐츠가 도움이 됐나요?

누스쿨 커뮤니티에서 더 많은 커리어 전략을 나눠요