전문가를 위한 리액트

review · 2025-5-2

← 리스트로

전문가를 위한 리액트

개요

백본의 단점은 대이터바인딩 기능 부족, 중첩 모델이나 중첩 뷰의 대처 부족 너무 장황한 보일러 플레이트이다.

mvvm패턴은 vm이 너무 거대해 지고 복잡해지는 단점이 있다.

앵귤러는 성능에 문제가 있고 특히 복잡성에 문제가 있어서 뒤쳐졌다.

리액트는 매우 효율적인 방법으로 이벤트에 반응해 웹페이지를 업데이트 한다.

리액트는 컴포넌트 기반의 가상돔과 재조정을 사용하여, 매우 효율적인 방법으로 이벤트에 반응해 웹페이지를 업데이트한다.

리액트는 사용자가 화면에 나타내고자 하는 바를 선언적으로 표현할 수 있게 해준다.

재조정

재조정은 기존트리와 새로운 트리를 비교하여, 실제 돔의 어떤 부분을 업데이트 해야 하는지 결정한다. 재조정은 최소업데이트 부분만 탐색하여 변경을 최소화한다.

리액트는 상태를 불변하는 값의 집합으로 기술하는 패러다임을 따른다.

가변상태를 공유하는 상태와는 다르게 변경사항을 추적하기 쉬어지며 디버깅도 쉬어진다.

플러스패턴

리액트는 플럭스패턴이라고 불리는 단방향 데이터흐름 아키텍처를 따른다.

플럭스패턴으 단일 정보출처의 중앙집중식 상태관리로 테스트와 관심사분리가 쉬워진다.

JSX

JSX는 JS로 변환되는 JS 컴파일러의 확장구문이다.

JS컴파일러는 1.토큰화, 2.구문분석, 3.기계어코드생성 의 순서로 컴파일을 진핸한다.

문자열을 의미있는 토큰으로 분해하는 렉서는 부모 자식 정보를 갖는 토큰의 한 형태이다.

구문분석은 토큰을 트리화하며, 구문트리라고 하는 트리를 생성한다.

코드생성 단계에서, 구문트리를 바탕으로 직접실행 가능한 기계어로 변환한다.

컴파일러의 종류

  1. 네이티브 컴파일러

    • 특정 플랫폼에서 직접 실행되는 기계어를 만듬
  2. 크로스 컴파일러

    • 실행중인 플랫폼 외에 다른 플랫폼에서 실행되는 기계어를 만든다
  3. Jit 컴파일러

    • 미리 컴파일 하지 않고 실행되기 전에 컴파일한다.
  4. 인터프리터

    • 컴파일하지 않고 그냥 직접 실행한다.

JS 컴파일러

최근 현대의 브라우저들은 jit 컴파일러를 사용한다. 가장 인기있는 자바스크립트 런타임은 크롬브라우저다.

JSX를 트랜스파일러를 통해 컴파일러가 이해할수 있는 형태로 만든다음, js형태로 변환되어 컴파일 된다

트랜스파일러는 컴파일러라고도 할수 있으며, 소스대 소스 컴파일이라고도 부른다.

플라그마란 컴파일러가 특정 작업을 시작하도록 하는 지시어다, JSX플라그마는 태그 꺽쇠로 시작된다.

JSX 장점은 엘리먼트 내부에서 코드 실행이 가능하다는 점이다(표현식만 가능하다).

리플로우와 레이아웃 스레싱

offsetWidth 속성을 일기만 해도 문서를 다시 계산하는 리플로우가 발생한다.

실제로 브라우저의 querySelector같은 api는 문서탐색 비용이 크다.

레이아웃 스레싱이란 불필요한 레이아웃 계산이 연속으로 일어나는것을 말하며, 때에 따라서는 이런 레이아웃 스레싱을 방지하기위해 getBoundingRect와 같은 api를 사용하는게 더 나을수도 있다.

이벤트시스템

리액는 합성 이벤트시스템을 가지고 있어서 브라우저의 호완성 이슈를 해결한다.

리액트는 이벤트위임을 사용하여 이벤트를 루트에서 전부 처리한다.

가상돔 속성

$$typeof - 유효한 리액트 엘리먼트인지 판별하는 심벌. 종류로는 provider, fragment, element, profiler, portal 이 있다.

_owner - 개발모드에서만 있으며, 디버깅 용도로 부모 컴포넌트 정보에 접근할 수 있다.

파이버 재조정자

리액트는 상태변경이 연속으로 일어나면 한번으로 묶어서 일괄처리한다.

또한 Fragment를 이용하여 불필요한 동작을 최소화 하여 일괄처리한다.

리액트16 이전에는 스택 재종정자를 사용했으므로 이벤트의 우선순위가 없었다.

또한 스택 재정자는 업데이트를 중던하거나 취소할수도 없었다.

더블버퍼링

파이버 재조정자는 더블버퍼링이라는 기법에 힌트를 얻어 만들어졌다.

커밋시 이팩트들이 실행되는데 특히 useLayoutEffect와 같은 레이아웃 이팩트는 돔이 생성된 후 리얼 돔 트리에 반영되기 전에 생성된다. useEffect 는 리얼돔 트리에 반영 후 화면에 페인트 된후 생성된다.

Memo 효과

간단한 계산에 useMemo를 사용하면 사용비용이 계산비용보다 더 클수 있다.

리액트의 Memo는 항상 예상한대로 동작하지는 않는다. Memo의 동작에 관련된 규칙은 참고사항일 뿐이며, 실제 세밀한 동작은 리액트가 알아서 한다.

(예를들어 컨텍스트를 사용하는 경우에는 Prop참조가 변경되었다고 해도 메모가 동작하지 않을수도 있다)

메모를 사용한다고 해서 항상 좋은것은 아니다. 그러므로 강박적으로 사용하면 안되고 필요한 경우에만 사용하는게 좋다.

스칼리 값의 경우는 참조로 비교하지 않고 값으로 비교하기 때문에 거의 대부분의 상황에서 메모는 불필요하다, 내장 컴포넌트에 붙이는 이벤트 함수도 useCallback같은 훅을 써도 메모효과가 없으며 사실상 불필요한 작업이다.

리액트16 까지는 내장이벤트에 풀링처리가 되어있다. 하지만 최신브라우저에서는 효과가 미비하여 17부터는 풀링이 없어졌다.

리액트 컴파일러가 안정화되어 나오면 이런 메모훅이 불필요해줄수도 있다.

Lazy와 suspense를 이용한 지연로딩

suspense 컴포넌트 내부 코드에서 throw를 던지면 Suspense 컴포넌트에서 catch하여 사용된다.

useReducer

useReducer를 사용하며 테스트가 용이해지고, 추가분석 추적이 용이해진다. 대규모 상태에 대해 예측이 쉬워진다.

리액트에서 사용되는 단골 패턴

프리젠테이션/컨테이너 패턴

Ui와 상태처리의 관심사 분리패턴.

하지만 요즘에는 다 훅으로 대체 가능해져서 잘 사용되지 않는다.

고차 캄포넌트

특정 컴포넌트를 인자로 받아서, 그 특정 컴포넌트를 사용하며 그거에 더해서 로직이 추가된 컴포넌트를 리턴한다.

고차 컴포넌트 역시 훅이 나오면서 잘 사용되지 않는다.

중첨된 고차 컴포넌트

중첩된 고차 컴포넌트 패턴도 유용할수 있는데, 다만 깊이가 깊어지면 코드를 이해하기 힘들어진다, 이럴땐 compose나 pipe 기능을 만들어 사용하면 도움이 된다.

Memo 나 forwardRwf도 고차 컴포넌트의 일종이다

하지만 대부분의 경우 훅을 사용하면 더 간단하고 직관적이므로 굳이 고차 컴포넌트가 필요없다.

renderProp

prop으로 render 컴포넌트를 넘기는 패턴인데 훅으로 대체할수 있다.

// MouseTracker.js
export const MouseTracker = ({ children }) => {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMouseMove = (e) => setPosition({ x: e.clientX, y: e.clientY });
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  return children(position); // render prop
};

// 사용
<MouseTracker>
  {(pos) => <p>Mouse is at {pos.x}, {pos.y}</p>}
</MouseTracker>

제어프롭

제어 함수를 전달받아 사용

프럽 컬렉션

그룹화된 객체의 제어함수 그룹을 스프레드 프롭으로 사용하는걸 말한다.

프럽 게터

프럽 게터 사용하면 프럽컬렉션으로 부터 조합된 새로운 프럽을 리턴받아 사용할 수 있다. (제어프롭이나 프롭컬렉션의 게터 버전인 샘이다)

복합컴포넌트

복합 컴포넌트는 기능을 위한 컴포넌트를 만들때 children과 컨텍스트를 사용해서 처리하는 것이다.

렌더러의 제어를 부모에게 넘기므로서 가독성이 향상된다 (위의 renderProp예제와 같은 패턴이다)

상태리듀서

리듀서를 한번 감싸서 다음 상태를 리턴하기 전에 한번더 다른 기능을 함성하는 패턴이다.(데코레이터 패턴과 비슷하다)

서버사이드 리엑트

클라이언트 사이드 앱은 seo에 약하며 네트워크폭포로 인한 성능문제가 있다.

반면 서버렌더링은 초기 랜더링이 빠르고 seo에도 좋으며 보안도 더 좋다. 웹접근성도 더 좋다.

리액트는 서버사이드로 사용되기위해서 하이드레이션이라는 기술을 사용하지만, 요즘에는 지속가능성이라는 기법이 대두되고 있다.

rendertostridng , rendertopileablestream 은 가상돔을 문자열화 시킨다.

특히 스트림방식은 suspense를 완벽하게 ssr로 지원한다.

스트림 방식은 Html 청크를 비동기로 클라이언트에 전송한다.

스트림의 종류

  1. 읽기가능 스트림 - 파일읽기 , http 응답
  2. 쓰기가능 스트림 - http 응답전송
  3. 양방향 스트림 - 채팅 앱, 양방향 소켓 통신
  4. 변환 스트림 - 데이터 가공, 압축"

백프레셔스티림을

데이터가 유실되지 않도록 입력과 출력 스트림간에 속도가 안맞을시, 한쪽의 상태를 잠깐 자동으로 멈추도록 구현한 것이다.

리액스 스트림랜더러 구현

프즈라는 서버렌더 아키텍트의 일부로 스트림구현했다.

이 서버기능으로 suspensefallback이 클라이언트 사이드가 필요없이 서버 stream으로만 구현된다

스트림방식으로 서버에서 클라이언트 브라우저로 html문자열을 위에서 부터 아래까지 순차적으로 랜더링 되며,

스트림의 상단에는 플레이스홀더를 만들어 놓고, 따라오는 스트림에 문자열에서 스크립트가 실행되며 suspense 의 html문자열을 플레이스홀더에 끼워넣는 방식이다.

서버사이드 리액트는 프래임워크의 사용을 권장

서버랜더링은 예외사항과 복잡한 처리가 많기 때문에 직접 구현하기 보다는 프레임워크의 사용을 권장한다.

리액트의 동시성 처리

리액트는 랜더링 프로세스를 파이버라고 하는 더 작고 관리하기 쉬운 작업단위로 분할하여 처리하므로 중요한 변경을 먼저 처리하고 덜 중요한 변경은 뒤로 미룰수 있다.

리액트 스케줄러는 사용자 인터렉션으로 민감하게 ui에 반영되야 하는걸 먼저 처리한다. 나머지는 브라우저 스레드의 유휴시간에 처리된다.

useTransition을 사용하면 중요하지 않은 업데이트를 미룰수 있다.

리액트 스케줄러

리액트스케줄러는 파이버 재조정자와는 별개로 동작한다.

스캐줄러의 주된 기능은 마이크로테스크를 사용하여 이벤트의 우선순위를 관리하는 것이다.

setState가 실행됨면 이 업데이트는 가장 우선순위가 높은 Sync 레인에 할당되어 마이크로테스큐에 들어간다.

반면 우선순위가 낮은 업데이트는 transition 레인에 배치되어 마이크로테스크큐에 등록된다.

ensureisrootschuled 함수는 현재 업데이트가 스캐줄에 등록외어있는지 확인하고 없으면 실행하고, 있으면 다시 대기후 ensureisrootschuled 로 다시 스캐줄링을 시도한다.

리액트의 랜더레인은 일종의 리액트의 자체 큐라고 볼수 있다.

useTransition

사용자는 useTransition 등으로 우선순위를 지정할수 있다. useTransition으로 스캐줄링된 작업은 trasition 레인에 등록된다.

스케줄러가 업데이트 순서를 정하고 그 후에 우선순위에 의해 선택된 업데이트 작업이 fiber재조정에 의해 이루어진다고 보면 된다.

useDeferredvalue

useDeferredvalueuseTransition 과 비슷하누맥락에서 사용되지만, 값에 대해 동작하며 사용자 인터렉션을 해치지 않는 유휴시간에 값이 변경되어 출력된다.

어떻게 보면 디바운스와 비슷하게 동작하지만, 사용자 입장에서 더 간단하고 효과적이게 동작한다.

useSyncExtrnalStore

useSyncExtrnalStore 는 컴포넌트에 영향을 미치는 외부값을 사용할때 티어링버그(타이밍버그) 가 나지 않도록 값을 동기화시켜준다

사용법 예

import { useSyncExternalStore } from 'react';

function subscribe(callback) {
  window.addEventListener('resize', callback);
  return () => window.removeEventListener('resize', callback);
}

function getSnapshot() {
  return window.innerWidth;
}

function getServerSnapshot() {
  return 1024; // SSR일 때 기본값
}

function WindowWidth() {
  const width = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
  return <div>현재 너비: {width}px</div>;
}

프레임워크 Remix

Remix는 entry.server.tsx 가 사용자에게 노출되어 있어서 동작방식이 투명하다. 이 파일을 지우면 내부 디폴트 코드로 동작한다.

리믹스는 봇의 요청은 하이드레이션을 제거한 문서만 리턴함으로서 성능을 향상시킨다.

서버컴포넌트

서버컴포넌트는 초기렌더링시 html문자열과함께 rsc payload 라고 하는 json 문자열을 함께 전송한다. 이건 가상돔 트리객체를 직렬화한것이다.

이후 페이지가 변경될때는 rsc payload만 잔송하고 클라이언트에서 가상돔으로 변경하여 재랜더리우하는 방식으로 동작한다

서버 컴포넌트는 클라이언트 컴포넌트를 대체할 수 없다. 이 둘은 승자가 없는 상호보완적 개념이다.

서버액션

서버액션과 서버함수는 같은 말이다.

서버액션은 클라이언트의 어디서든 사용할 수 있다

폼이 아닌 곳에서도 서버액션을 사용할 수 있다. 이게 리액트가 서버액션을 서버함수라고 바꿔 부르는 이유다.

서버함수를 useTransition과 함께 사용하면 낙관적 업데이트를 사용할 수 있다.

다른 UI 라이브러리

뷰는 컴포지션api 라는 방식으로 상태를 리액티브하게 만드는데 후에, 이게 사람들이 말하는 시그널패턴이라는 것이다.

원래는 옵저버패턴이라고 불리며 널리사용되었으며, 뷰나 몹엑스 등에서 사용되면서 시그널 패턴으로 더 잘 알려지게 되었다.

심지어 앵귤러도 시그널패턴에서 착안한 변경관리 시스템을 만들었다.(하지만 사용성이 어렵다고 한다)

스벨트

스벨트에서도 시그널패턴을 사용한다.

솔리드

솔리드는 가상돔을 사용하는 대신 세분화된 반응형 시스템을 사용한다. (가상돔으로부터 업데이트 하지 않고, 데이터를 시그널패턴으로 감지하고 있다가 상태에 의존성이 있는 엘리먼트를 직접 업데이트 하는 방식)

리액트를 거친반응성이라고하고 솔리드를 세분화된 반응성이라고 한다.

초기에는 최소한의 스크립트 만으로 보여지며, 사용자의 인터렉션에 따라 필요한 스크립트가 자동으로 로드된다.

리액트느누사실 다룬 반응형 라이브러리와느누다르게ㅜ덩작하며. 자동으로 변경사항으루전파하는 대신, 사용자가ㅠ명시적으로ㅠ상태를 설정하므로ㅠ좀 더 예츧가능하다"

리액트는 사실 다른 반응형 라이브러리와는 다르게 동작한다.

자동으로 변경사항을 전파하는 대신, 사용자가 명시적으로 상태를 설정하므로 좀더 예측 가능한 형태다. 다른 반응형 라이브러리들은 좀더 자동화되어 있지만, 좀 더 예측하기 힘들다.

리액트를 잘 사용하려면 사용방식에 통달하는 것 보다는 리액트의 사고방식을 잘 이해하여야 한다.