리액트 17과 18의 변경 사항 살펴보기 (1)

keemsebeen·2024년 12월 20일

모던 리액트 Deep Dive

목록 보기
15/18

10.1 애플리케이션에서 확인하기

리액트 17버전은 16버전과 다르게 새롭게 추가된 기능이 없으며 호환성이 깨지는 변경사항을 최소화했다는 점이 가장 큰 특징이다.

리액트의 점진적인 업그레이드

이전에는 새로운 주 버전이 릴리즈되면 이전 버전에서의 API 제공을 완전히 중단해 버리고, 전체 어플리케이션을 새롭게 업그레이드하기를 요구했다. 하지만 리액트 17버전부터는 점진적인 업그레이드가 가능해진다. 즉, 전체 애플리케이션 트리는 리액트 17이지만 일부 트리와 컴포넌트에서만 리액트 18을 선택하는 점진적인 버전 업이 가능해진다.

이벤트 위임 방식의 변경

두가지 예제코드를 보자.

import { useEffect, useRef } from 'react';

export default function Button() {
  const buttonRef = useRef<HTMLButtonElement | null>(null);

  useEffect(() => {
    if (buttonRef.current) {
      buttonRef.current.onclick = function click() {
        alert('HI');
      };
    }
  }, []);

  function HI() {
    alert('HI');
  }
  return (
    <>
      <button onClick={HI}>리액트 버튼</button>
      <button ref={buttonRef}>그냥 버튼</button>
    </>
  )
}

리액트 버튼은 리액트 어플리케이션에서 DOM에 이벤트를 추가하는 방식으로 이벤트를 추가했다. 반면 그냥 버튼은 직접 DOM을 참조해서 가져온 다음, 이벤트에 직접 함수를 추가했다.

리액트 버튼은 click 이벤트에 noop(no operation)라는 핸들러가 추가돼 있는 것을 볼 수 있다. 그냥 버튼의 경우 이벤트 리스너에 click이 추가된다.

리액트는 이벤트 핸들러를 해당 이벤트 핸들러를 추가한 각각의 DOM 요소에 부착하는 것이 아니라, 이벤트 타입(click, change)당 하나의 핸들러를 루트에 부착한다.

리액트 17부터는 이벤트 위임이 모두 document가 아닌 리액트 컴포넌트 최상단 트리(div#root루트) 요소로 바뀌었다. 바닐라 자바스크립트, 제이쿼리 등이 혼재되는 경우 혼란을 방지하기 위해서다. 변경에 따라 각 이벤트는 해당 리액트 컴포넌트 트리 수준으로 격리되므로 이벤트 버블링으로 인한 혼선을 방지할 수 있다.

import React from ‘react’가 더 이상 필요 없다: 새로운 JSX transform

JSX는 브라우저가 이해할 수 있는 코드가 아니므로 바벨이나 타입스크립트를 활용해 JSX를 실행하기 위해 일반적인 자바스크립트로 변환하는 과정이 필요했다. 그러나 17버전 부터는 바벨과 협력해 import 구문 없이도 JSX를 변환할 수 있게 됐다.

JSX를 변환할 때 필요한 모듈인 react/jsx-runtime 을 불러오는 require문도 함께 추가됐기 때문이다.

이는 번틀링 크기도 줄일 수 있고, 컴포넌트 작성을 더욱 간결하게 해준다.

그 밖의 주요 변경 사항

이벤트 풀링 제거
이벤트를 처리하기 위한 이벤트인 SyntheticEvent 라는 이벤트가 존재했다. 이는 새로 이벤트를 만들때마다 메모리 할당 작업이 일어나 메모리 누수가 발생했다. 비동기 이벤트 핸들러에 접근하기 위해서는 별도의 메모리 공간에 합성 이벤트 객체를 할당해야 한다는 점, 모던 브라우저에서는 이와 같은 방식이 성능 향상에 도움이 안되기 때문에 폴링 개념이 삭제됐다.

useEffect 클린업 함수의 비동기 실행
16버전까지는 동기적으로 처리됐다. 클린업 함수가 완료되기 전까지 다른 작업을 방해하므로 불필요한 성능 저하로 이어지는 문제가 있었다. 컴포넌트의 커밋 단계가 완료될 때까지 지연된다. 즉, 화면이 업데이트가 완전히 끝난 이후에 실행되도록 바뀌었으며, 이로써 약간의 성능적인 이점이 생겼다.

컴포넌트의 undefined 반환에 대한 일관적인 처리
forwardRef나 memo에서 undefined를 반환하는 경우에는 별다른 에러가 발생하지 않는 문제가 있었다.

profile
프론트엔드 공부 중인 김세빈입니다. 👩🏻‍💻

0개의 댓글