안녕하세요, 소플입니다.
최근 리액트 공식 블로그에 굉장히 오랜만에 새로운 글이 올라왔습니다.
React Labs: What We've Been Working On – February 2024
해당 글은 현재 리액트 개발 현황에 대해 공유하고 리액트 버전19가 출시될 날이 멀지 않았다고 예고하는 내용이었습니다.
글의 내용 중에서도 유독 눈에 띄는 것이 있는데요, 바로 React Compiler입니다.
이번 매거진에서는 이 React Compiler에 대해서 한 번 살펴보도록 하겠습니다.
React Compiler라는 것이 처음 소개된 것은 2021년에 개최된 React Conf에서였습니다.
https://www.youtube.com/watch?v=lGEMwh32soc
당시 React Forget이라는 이름으로 소개된 React Compiler는 리액트의 재렌더링 비용을 최소화하기 위해서 개발되었다고 합니다.
리액트의 컴포넌트는 같은 입력(props
)에 대해 항상 같은 출력(element
)을 리턴해야 합니다.
또한 상태(state
)가 변경되었을 경우에는 재렌더링(re-rendering)이 발생하게 됩니다.
이러한 리액트의 프로그래밍 모델은 굉장히 합리적이긴 하지만,
때때로 불필요하게 너무 많은 재렌더링을 유발하는 경우가 생기게 됩니다.
그래서 불필요한 재렌더링을 막고 최적화 하기 위해서는,
개발자가 일일이 useMemo
, useCallback
훅이나 memo
함수를 사용해서 처리를 해줘야 했습니다.
이러한 불편함을 해소하기 위해서 등장한 것이 바로 React Forget입니다.
React Forget은 auto-memoizing compiler라고도 부르는데,
의미 그대로 useMemo
및 useCallback
호출과 동일한 호출을 자동으로 생성하는 컴파일러입니다.
그래서 React Forget을 사용하게 되면, 개발자가 별도의 Memoization 처리를 해주지 않아도,
리액트의 프로그래밍 모델을 유지하면서 재렌더링 비용을 최소화할 수 있다는 것이죠.
처음 React Compiler가 소개된지 약 6개월이 지나고 컴파일러에 새로운 아키텍처를 적용했다는 소식이 올라오게 됩니다.
새로운 아키텍처는 Local mutation 같은 복잡한 패턴들도 분석하여 자동으로 memoization을 해주며,
컴파일 시간을 최적화 할 수 있는 여러가지 가능성도 열어주었다고 합니다.
그리고 개발자들이 컴파일러의 작동 방식을 더 쉽게 이해하고 사용할 수 있도록 하기 위해,
Playground 또한 함께 개발 중이며 컴파일러가 출시될 때 함께 공개될 예정이라고 합니다.
NOTE. Local mutation이란?
리액트 컴포넌트 내부(local)에서 렌더링 과정 중에 변수를 선언하고 변경(mutation)하여 사용하는 방식을 의미합니다.
리액트 컴포넌트는 순수 함수여야 하기 때문에, 컴포넌트 외부에서 선언한 변수를 컴포넌트 내부에서 변경하는 것은 문제를 발생시킬 수 있지만,
아래 예시 코드와 같이 컴포넌트 내부에서 동일한 렌더링 과정 중에 선언한 변수를 사용하는 것은 괜찮습니다.
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaGathering() {
let cups = [];
for (let i = 1; i <= 12; i++) {
cups.push(<Cup key={i} guest={i} />);
}
return cups;
}
새로운 아키텍처를 적용했다는 소식이 발표되고 약 9개월 정도가 지난 시점에 React Compiler에 대한 새로운 소식이 올라오게 됩니다.
그리고 해당 글에서는 React Compiler를 automatic reactivity compiler라고 소개합니다.
컴파일러를 개발하면서 리액트의 프로그래밍 모델을 더욱 깊게 이해할 수 있었고,
그 과정에서 리액트 컴파일러가 단순히 auto-memoizing compiler가 아닌 automatic reactivity compiler라고 정의하게 된 것입니다.
그렇다면 여기서 말하는 리액트의 프로그래밍 모델은 뭘까요?
그것은 바로 개발자가 UI를 현재 상태(state)의 함수(function)로 정의하는 것입니다.
그리고 리액트는 상태가 변경될 때마다 컴포넌트가 다시 렌더링될 것이라는 멘탈 모델을 제공합니다.
'반응하다'라는 뜻을 가진 React라는 단어처럼 상태가 변경되면 반응하는 것이죠.
그리고 이러한 성질을 Reactivity라고 표현합니다.
하지만 리액트가 가진 높은 반응성 때문에 때때로 의도치 않게 수많은 재렌더링이 발생하게 됩니다.
그래서 React Compiler는 리액트 앱이 상태 값이 의미있게 변경되는 경우에만 재렌더링을 하도록 함으로써,
적당한 반응성을 갖도록 하는 것을 목표로 하고 있습니다.
현재 리액트에서는 객체의 reference(identity)가 변경되면 재렌더링이 발생하지만,
React Compiler을 사용하면 실제로 값이 의미있게 변경되는 경우에만 재렌더링을 하겠다는 것이죠.
그리고 Meta 내에서 실제로 production 서비스에 적용함으로써 React Compiler의 출시가 임박했다는 것을 암시하기도 했습니다.
지난 달에 리액트 공식 블로그에 올라온 글에서 React Compiler가 머지 않아 공개될 것이라는 소식을 전해왔습니다.
처음에는 내부 연구 목적으로 개발하기 시작했던 프로젝트가,
지금은 실제로 웹용 인스타그램에서 사용되고 있으며 오픈소스로 공개될 예정이라고 밝혔습니다.
https://www.youtube.com/watch?v=qOQClO3g8-Y
출시되면 그 파급력이 굉장히 큰 프로젝트인만큼 굉장히 오랜 기간동안 개발과 검증을 거친 것 같습니다.
그리고 Meta 내부의 수많은 코드들을 대상으로 테스트도 진행했다고 합니다.
제 생각에는 아마도 올해 릴리즈될 리액트 버전19와 함께 하반기쯤에 공개되지 않을까 싶습니다.
그렇다면 React Compiler가 출시된 이후에는 리액트 개발 과정이 어떻게 달라질까요?
개발자들 입장에서는 기존에 리액트를 사용해서 개발하는 과정이 크게 달라지는 부분은 없을 겁니다.
왜냐하면 React Compiler가 해주는 역할은 자동으로 코드를 분석하고 필요한 부분에 자동으로 memoization을 적용해주는 것이기 때문이죠.
일일이 useMemo
, useCallback
훅이나 memo
함수를 사용해서 처리해주지 않아도,
불필요한 재렌더링이 일어나지 않는 최적화 된 성능의 애플리케이션을 개발할 수 있을 것으로 기대하고 있습니다.
하지만 리액트의 원칙(Rules of React), Memoization의 개념, 그리고 React Compiler의 역할에 대해서는 당연히 잘 이해하고 있어야겠죠.
원리를 이해하지 못하고 사용하는 것은 개발자의 성장 관점에서 장기적으로 좋지 않기 때문입니다.
제가 리액트 강의에서 지금은 거의 쓰지 않는 클래스 컴포넌트와 컴포넌트의 생명주기(Lifecycle)를 굳이 설명하고 넘어가는 이유가 이것이기도 합니다ㅎㅎ
어찌됐든 저 또한 앞으로 다가올 리액트 버전19와 React Compiler가 굉장히 기대됩니다.
출시된 이후에 직접 사용해보고 매거진을 통해서 더 자세한 내용을 다뤄보도록 하겠습니다.
그럼 저는 다음에 또 유익한 글로 찾아뵙겠습니다!
지금까지 소플이었습니다. 감사합니다 😀
지금 가입하고 새로운 매거진을 이메일로 받아보세요!
Copyright ⓒ Soaple. All rights reserved.