04
4주차 스터디
10~12장 · 3개 챕터
10. 리액트의 프롭스와 컴포넌트 패턴 돌아보기
리액트의 props
- 트리 구조 + 단방향 데이터 흐름
- 리액트는 상태 업데이트 시 불변성 전재 -> 이전 값과 새로운 값의 참조 만을 비교하는 얕은 비교(Shallow Comparison)를 사용해 상태 변경 확인 (Object.is)
프롭스를 사용하는 컴포넌트 패턴
- children props 활용
- 컴포넌트 내부에 고정된 UI를 두는 대신, children을 통해 외부에서 콘텐츠를 주입
- 슬롯 Props 패턴 (Named Slots)
- 단일 children 대신 여러 개의 props를 통해 특정 위치에 콘텐츠를 전달하는 방식
- 예를 들어 header, footer, actions 같은 명시적인 슬롯을 제공하면, 컴포넌트의 구조를 유지하면서도 각 영역 커스터마이징 가능
- 컴파운드 컴포넌트 패턴 (Compound Component)
- 하나의 부모 컴포넌트와 그에 종속된 하위 컴포넌트를 조합해 사용하는 패턴
- 내부 상태를 공유하면서도 선언적으로 조합 가능
11. 리액트의 상태와 배칭 돌아보기
리액트의 상태 정의와 종류
- 지역상태/파생상태
- 지역상태: 컴포넌트 내부에서 직접 관리/외부에서 참조 불가능한 상태
- 파생상태: 기존 상태나 props로부터 계산된 값
- 상태는 불변해야 한다.
- 리액트는 Object.is() 기반의 비교(참조 비교)를 통해 상태 변경을 감지하므로, 상태가 바뀔 때는 항상 새로운 값(객체) 을 생성해야 한다
- 참조값이 바뀌면(= Object.is가 다르면) 모든 Consumer가 무조건 다시 읽게해, 일관성 있는 최신 값을 보장한다.
- 직접 수정하면 변경을 감지하지 못해 리렌더링이 일어나지 않을 수 있다.
- 상태 끌어올리기
- 여러 컴포넌트가 동일한 상태를 공유해야 할 때, 가장 가까운 공통 부모에서 상태를 관리하고 props로 내려주는 패턴
- 특징: 상태 일관성과 단방향 데이터 흐름을 유지
컴포넌트 간 데이터 흐름
- 리액트는 단방향 데이터 흐름(부모 → 자식) 구조
- 특징: 데이터의 출처를 명확하게 하고, 예측 가능한 구조를 만듦. 관심사 분리를 통해 불필요한 렌더링을 줄이고 유지보수를 쉽게 함
리액트 배칭 돌아보기
- 정의: 여러 상태 업데이트를 하나로 묶어 한 번만 렌더링하는 최적화 메커니즘이다.
- 특징: 여러
setState()호출 → 한 번의 리렌더링 -> 성능 최적화 및 불필요한 렌더 방지 - React 17 이전에는 이벤트 핸들러 내부에서만 자동 배칭이 적용되었고, 비동기 작업에서는
unstable_batchedUpdates가 필요했음 - React 18부터는 대부분의 상황(Promise, setTimeout 등)에서 자동 배칭이 적용
flushSync()
flushSync(() => { setState(newState) })
- 동기적으로 상태를 즉시 반영해야 하는 경우 사용한다.
- 일반적인 상황에서는 필요하지 않으며, DOM 측정 등 특수한 경우에만 사용한다.
12. 리액트를 구성하는 뿌리, 파이버 돌아보기
리액트 스택 재조정자 알아보기 (React v15 이전)
- Fiber 아키텍처는 React에서 Reconciliation 성능 최적화를 위해 도입된 구조입니다.
- React v15 이전에는 Stack 기반으로 렌더링 작업을 처리했습니다.
- 특징: 호출 스택 기반의 재귀 순회 -> 호출 스택에서 작업이 하나씩 처리됩니다.
- 작업이 중단될 수 없으며, 한 번 시작된 작업은 끝날 때까지 실행됩니다.
- 문제점
- 브라우저의 메인 스레드가 점유되기 때문에 렌더링 작업이 오래 걸리면 UI가 멈추는 UI 렉 문제가 발생
- 사용자 입력 같은 긴급 작업이 대기 상태로 밀림
- 작업을 나눠 처리하거나 우선순위를 조정할 수 없음
파이버 아키텍처 알아보기 (React v16+)
- Fiber 아키텍처는 작업을 Fiber Node라는 작은 단위로 나누고 우선순위를 부여해, 중요한 작업(사용자 입력, 애니메이션 등)이 먼저 처리될 수 있도록 했습니다.
- 호출 스택 대신 작업 단위(Fiber 객체) 기반 처리
- 렌더링을 작은 단위로 쪼개서 실행
- 작업의 중단, 재개, 우선순위 조정 가능
파이버 우선순위와 레인(Lanes) 모델
- 파이버 아키텍처에서는 모든 업데이트가 동일하게 처리되지 않음. 작업마다 레인(Lane) 이라는 개념을 부여해 우선순위를 스케줄링
- 레인: 작업의 긴급도를 표현하는 단위
- 사용자 입력과 같은 긴급한 작업은 높은 우선순위 레인
- 데이터 패칭과 같은 덜 긴급한 작업은 낮은 우선순위 레인에 배정
- 이점: 메인 스레드를 독점하지 않으면서도, 사용자 경험을 해치지 않는 방식으로 렌더링을 수행 가능
파이버 트리 구조 : 재귀없는 순회
- 포인터 기반 트리 구조를 사용
- 각 Fiber 노드는 다음과 같은 연결 포인터를 가짐
- child : 자식 노드
- sibling : 형제 노드
- return : 부모 노드
- 재귀 호출 없이 while 루프 기반으로 객체 포인터를 따라 이동하며 트리를 순회
더블 버퍼링과 얼터네이트(Alternate) 포인터
- "더블 버퍼링" 전략
- 사용 이유: 렌더링 도중 화면에 보이는 UI를 직접 수정하면, 사용자는 완성되지 않은 “깨진 UI”를 보게 되는데, 이를 방지하기 위함
- 동작 원리: 두 개의 파이버 트리 유지하고, 렌더링이 완료되면 WIP 트리를 Current 트리로 교체
- Current 트리 : 현재 화면에 반영되어 사용자에게 보이는 UI 트리
- Work-In-Progress(WIP) 트리 : 상태 변경 이후, 화면에 보이지 않는 곳에서 새롭게 렌더링을 수행하는 트리
- 이점: 사용자에게는 항상 완성된 UI만 표시됨
- 얼터네이트(Alternate) 포인터
- 각 Fiber 노드는 alternate 포인터를 통해 Current 노드와 그에 대응하는 WIP 노드를 서로 참조
- 이점: 기존 노드를 재사용할 수 있고 매 렌더마다 전체 트리를 새로 생성하지 않아도 되며 메모리 효율성과 성능을 함께 확보할 수 있음