-
React Activity?프론트엔드 2026. 3. 12. 18:47
React 렌더링
Activity 컴포넌트를 이해하기 전에 먼저 React에서 상태 업데이트가 어떻게 이루어지고 왜 리렌더링이 발생하는지 이해할 필요가 있다.
React 공식 문서에서는 Activity가 hidden 상태일 때 렌더링을 수행하지 않는다고 설명하고 있기 때문에, 이를 이해하기 위해 먼저 React의 기본 렌더링 과정을 살펴보았다.
예를 들어 다음과 같은 코드가 있다고 하자.
function App() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> 클릭 </button> ); }버튼을 클릭하면 setCount가 호출되고 React는 다음과 같은 과정을 통해 UI를 업데이트한다.
setState ↓ updateQueue에 update 추가 ↓ render phase 시작 ↓ 컴포넌트 함수 실행 ↓ 새로운 UI 계산 ↓ Reconciliation (이전 UI와 비교) ↓ commit phase ↓ DOM 업데이트
Reconciler와 Reconciliation
위 과정에서 중요한 부분은 Reconciliation(재조정) 단계이다.
React는 이전 UI와 새로운 UI를 비교하여 어떤 부분이 변경되었는지 판단하고 필요한 부분만 DOM을 업데이트한다.
이 작업을 수행하는 것이 Reconciler이며, 이 과정에서 React는 Fiber Tree라는 자료구조를 사용한다.
Fiber Tree
Fiber Tree는 React가 컴포넌트 구조를 관리하기 위해 사용하는 내부 트리 구조이다.
각 컴포넌트는 하나의 Fiber Node로 표현되며, 다음과 같은 정보가 저장된다.
type → 컴포넌트 타입
props → 전달된 props
memoizedState → 현재 state
child → 첫 번째 자식 노드
sibling → 형제 노드
return → 부모 노드예를 들어 다음과 같은 컴포넌트 구조가 있을 때
<App> <Header /> <Content /> </App>React 내부에서는 다음과 같은 Fiber Tree가 만들어진다.
Fiber(App)
├ Fiber(Header)
└ Fiber(Content)
상태 업데이트가 발생하면
setState가 호출되면 React는 바로 state를 변경하지 않고 먼저 updateQueue에 상태 변경 요청을 저장한다.
예를 들어 다음과 같은 상태가 있다고 하자.
function App() { const [count, setCount] = useState(0) const [name, setName] = useState("A") }React 내부에서는 Hook 상태가 다음과 같이 관리된다.
Fiber(App)
memoizedState
↓
Hook1
state = 0
next →
Hook2
state = "A"이때 count를 증가시키는 함수가 호출되면
setState 호출
↓
updateQueue에 update 추가
↓
render phase에서 updateQueue 처리
↓
새로운 state 계산
↓
memoizedState 업데이트이 과정에서 React는 새로운 Fiber Tree(workInProgress)를 생성한다.
Fiber Tree 비교
React는 상태 업데이트가 발생하면
current Fiber Tree
VS
workInProgress Fiber Tree를 비교한다.
이 비교 과정이 바로 Reconciliation이며, 이를 수행하는 것이 Reconciler이다.
구조는 다음과 같다.
React
└ Fiber Tree
└ Fiber Node
├ memoizedState
├ updateQueue
└ hooks
왜 Fiber Tree를 두 개 유지할까?
React 내부에는 두 개의 Fiber Tree가 존재한다.
current Fiber tree
workInProgress Fiber tree- current → 현재 화면에 렌더링된 UI 상태
- workInProgress → 다음 UI 상태를 계산하는 트리
렌더링 과정은 다음과 같이 진행된다.
render phase current ───────────► workInProgress ▲ │ │ │ └────── commit ────────┘React는 workInProgress tree에서 새로운 UI를 계산한 뒤 commit 단계에서 current tree와 교체한다.
이 구조 덕분에
- 렌더링 도중 오류가 발생해도 기존 UI는 유지되고
- 계산이 완료된 결과만 DOM에 반영되기 때문에
UI 업데이트가 안정적으로 이루어질 수 있다.
React Activity
- React Activity는 새롭게 추가된 컴포넌트로 컴포넌트를 "숨김 상태로 유지하면서" 상태를 보존한다고 한다.
- 보통 React에서 버튼을 클릭하면 state 상태가 변경이 되어, 리렌더링이 발생한다.
- 하지만 Activity의 경우는 unmount 되지 않아 리렌더링이 발생하지 않는다
<Activity mode="hidden"> <ChatPanel /> </Activity>Activity가 어떻게 리렌더링이 일어나지 않도록 할까
- Activity가 리렌더링을 하지 않는 이유를 간단히 말하면 일종의 스킵을 통해 리렌더링을 하지 않는다.
- 보통의 상태 업데이트가 일어난다면 아래와 같이 일어나지만, React Activity는 다르다.
setState ↓ updateQueue에 update 추가 ↓ render phase 시작 ↓ 컴포넌트 함수 실행 ↓ 새 Fiber 생성 ↓ Reconciliation ↓ commit phase ↓ DOM 업데이트- React는 memoizedState를 통해 visible한 상태인지 hidden 상태인지를 관리한다고 한다.
- 다시 React 동작원리를 보면 리액트에서 렌더링은 아래와 같이 렌더단계에서 내부에서는 Fiber를 하나씩 순회하며 렌더링 작업을 수행한다고 한다.
- React는 memoizedState를 통해 Offscreen Fiber가 visible 상태인지 hidden 상태인지 관리한다.
memoizedState === null → visible
memoizedState !== null → hidden- Activity가 hidden 상태가 되면 React는 해당 Fiber의 memoizedState에 OffscreenState 객체를 저장한다.Fiber(Activity) │ └ Fiber(Offscreen) memoizedState = OffscreenState (hidden) │ └ Fiber(ChatPanel)App ├ Header ├ Activity │ └ ChatPanel │ ├ MessageList │ └ Input └ FooterActivity는 Offscreen Fiber로 구현되며, hidden 상태일 때 memoizedState에 OffscreenState가 저장된다. React는 이를 통해 hidden 상태를 판단하고 subtree 렌더링을 스킵하지만, Fiber 구조 자체는 유지되기 때문에 subtree는 제거되지 않는다.
Activity 사용은 그러면 언제?
- 뭐 대충 Activity가 이전값을 memoziedState에 OffsreenState에 저장되어 Fiber노드가 유지되어 유지가 된다는거는 알겠는데 이거를 언제 써야 잘 썼다고 소문이 날까?
- 유튜브나 강의를 보면 Activity를 상태 조건에 따라 컴포넌트 렌더링 되는 모달? 같은 경우 사용하면 무작정 좋다고 하는데 이거는 아닌거 같다.
- 나도 처음은 그냥 오 편하고 좋네? 이 생각으로 있는 모달 모두 Activity처리헀는데 생각을 조금만 해보니 오히려 문제가 있을수도 있지 않을까? 생각이 들었다. 뭐 예를들어 잠깐 보여주는 모달 toast나 그런것들 까지 굳이?? 오히려 저 상태값을 저장하고 있는 메모리한테 미안하다 라는 생각이 들었다.
- 그래서 올바르게 사용하고자 한다면 유지가 되어야하는 유저 정보를 보여주는 모달? 같은 거나? 댓글모달 아니면 회원가입할때 유지되어야 하는 상태일때 사용하면 좋을거 같다.
- 회원가입 단계형 모달
- 결제 단계 모달
- 사람들이 그냥 좋다고 하는 것에는 항상 나쁜점도 있는거 같다. 항상 잘 알아보고 적절할때 스킬을 사용하면 더 멋진 개발자가 되지 않을까 생각이 들었다.
'프론트엔드' 카테고리의 다른 글
클라이언트 중복 방어의 한계와 서버 이중 방어 — isSubmitting부터 idempotency key까지 (0) 2026.06.07 AbortController 도입시도 (0) 2026.06.01 쿠키, 세션, 스토리지 (1) 2025.12.06 React 딥다이브 (0) 2025.10.20 프로젝트를 하면서 고민점들 (라우팅 렌더링) (0) 2025.10.18