당근 · FEConf 2023 2023
이벤트 기반 웹뷰 프레임워크 설계와 플러그인 생태계
당근의 웹뷰 통신 프레임워크 설계 — 이벤트 드리븐 아키텍처와 플러그인 시스템 (발표: 원지혁, 당근)
요약
핵심 토픽
학습 포인트
1. 이벤트 소싱 기반 상태 관리
이벤트 소싱은 '현재 상태 = 초기 상태 + 모든 이벤트의 누적 적용' 패턴입니다. Stack Flow에서 `aggregate(events)` 함수는 `[{type: 'push', screen: 'A'}, {type: 'push', screen: 'B'}, {type: 'pop'}]` 같은 이벤트 목록을 reduce해 현재 스택 `['A']`를 반환합니다. 장점: (1) 순수 함수라 외부 의존 없이 단위 테스트 가능, (2) 이벤트 로그로 버그 재현 가능, (3) 타임트래블 디버깅 가능. Redux reducer가 동일한 개념입니다.
핵심 용어
2. 코어 로직의 프레임워크 독립 설계
코어 스토어(상태 + 이벤트 처리)를 React와 완전히 분리하면 Vue, Svelte 등 다른 프레임워크에서도 재사용할 수 있습니다. 스토어는 `getState()`, `dispatch(action)`, `subscribe(listener)` 세 메서드만 노출합니다. React 통합은 `useSyncExternalStore(store.subscribe, store.getState)` 훅 하나로 연결합니다. useSyncExternalStore의 장점: 동시성 모드(Concurrent Mode)에서도 상태 일관성을 보장합니다(tearing 방지).
핵심 용어
3. History API의 한계와 웹뷰 화면 전환
브라우저 History API(popstate)는 현재 페이지의 이전 상태에 접근이 불가해 화면 전환 애니메이션 구현이 어렵습니다. 뒤로 가기 시 이전 화면을 DOM에 미리 유지해야 슬라이드 애니메이션이 가능한데, History API만으로는 이전 화면 렌더링 정보를 알 수 없습니다. Stack Flow는 History API 대신 자체 이벤트 스택으로 화면 히스토리를 관리해 이전 화면 상태를 항상 알 수 있게 합니다.
핵심 용어
4. 플러그인 시스템 설계 패턴
플러그인 시스템은 사용자가 라이브러리의 핵심 코드를 수정하지 않고 기능을 확장할 수 있게 합니다. Stack Flow의 방식: 라이프사이클 훅(onInit, onPush, onPop)을 배열 형태로 받아 각 플러그인을 순서대로 실행합니다. `createStackFlow({ plugins: [analyticsPlugin, historyPlugin] })`. 이 패턴은 GraphQL Yoga, webpack(tapable), rollup 등 주요 라이브러리에서 채택한 검증된 방식입니다. 플러그인으로 기능을 분리하면 코어는 가볍게 유지하면서 선택적으로 기능을 추가할 수 있습니다.
핵심 용어
면접 질문
Q1. 이벤트 소싱 패턴과 일반적인 상태 관리(useState, Redux)의 차이를 설명해 주세요.
힌트
[감점 답변] 정의만 반복하거나 "이벤트 소싱 패턴과 일반적인 상태 관리(useState, Redux)의 차이를 설명해 주세요."에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] 핵심 차이: 일반 상태 관리는 현재 상태만 저장하고 이전 상태를 버리지만, 이벤트 소싱은 이벤트 목록을 저장하고 현재 상태를 항상 재계산합니다. Redux의 reducer가 이벤트 소싱의 aggregate 함수와 같은 역할임을 언급하세요. 장점: 타임트래블 디버깅, 이벤트 재생으로 버그 재현, 감사 로그. 단점: 이벤트가 많아지면 상태 계산 비용 증가(이를 해결하는 스냅샷 패턴도 언급하면 좋음).
Q2. React의 useSyncExternalStore를 사용하는 이유와 동작 원리를 설명해 주세요.
힌트
[감점 답변] 정의만 반복하거나 "React의 useSyncExternalStore를 사용하는 이유와 동작 원리를 설명해 주세요."에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] Concurrent Mode 문제부터 설명하세요: React 18의 동시성 렌더링에서 외부 스토어를 useEffect + useState로 구독하면 렌더링 중 상태가 변경될 때 inconsistent한 UI(tearing)가 발생할 수 있습니다. useSyncExternalStore는 이를 방지하는 공식 API로, subscribe(변경 감지), getSnapshot(현재 값 반환) 두 함수를 받아 React와 외부 스토어를 안전하게 연결합니다.
Q3. 라이브러리를 설계할 때 확장성과 플러그인 시스템을 고려하는 방법은?
힌트
[감점 답변] 정의만 반복하거나 "라이브러리를 설계할 때 확장성과 플러그인 시스템을 고려하는 방법은?"에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] 두 가지 원칙을 설명하세요: (1) 코어는 최소한의 기능만 — 나머지는 플러그인으로 분리하면 코어를 가볍게 유지하고 사용자가 필요한 것만 선택할 수 있다, (2) 라이프사이클 훅 노출 — 초기화·액션 전후에 훅을 제공하면 사용자가 행동을 관찰하고 수정할 수 있다. webpack, rollup, GraphQL Yoga의 플러그인 시스템이 같은 패턴을 사용한다는 점도 언급하면 좋습니다.
Q4. 모바일 웹뷰에서 뒤로 가기 화면 전환 애니메이션을 구현할 때 어떤 기술적 도전이 있나요?
힌트
[감점 답변] 정의만 반복하거나 "모바일 웹뷰에서 뒤로 가기 화면 전환 애니메이션을 구현할 때 어떤 기술적 도전이 있나요?"에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] 두 가지 핵심 문제를 설명하세요: (1) History API의 한계 — popstate 이벤트로 뒤로 가기를 감지할 수 있지만 이전 페이지 정보를 알 수 없어 애니메이션 구현이 어렵다, (2) 이전 화면 DOM 유지 — 뒤로 가기 애니메이션을 위해 이전 화면을 DOM에 남겨둬야 하는데 이는 메모리 사용량을 늘린다. Stack Flow가 이를 자체 스택으로 해결한 방법을 설명하면 됩니다.
Q5. 비즈니스 로직과 프레임워크 코드를 분리하는 방법과 그 장점은?
힌트
[감점 답변] 정의만 반복하거나 "비즈니스 로직과 프레임워크 코드를 분리하는 방법과 그 장점은?"에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] 핵심 로직을 순수 JavaScript(또는 TypeScript)로 구현하고, React 바인딩은 별도 레이어로 분리하는 방법을 설명하세요. 장점: (1) 프레임워크 교체 시 핵심 로직은 그대로 유지, (2) 순수 함수라 테스트에서 jsdom 없이 단순 함수 호출로 검증 가능, (3) Next.js 서버 컴포넌트 같은 환경에서도 재사용 가능. 실제 예시로 Zustand의 코어가 React 없이도 사용 가능하다는 점을 언급하면 좋습니다.
선행 학습
- 브라우저 History API (pushState, popstate)
- React 상태 관리와 외부 스토어 개념
- 이벤트 기반 프로그래밍 패턴
핵심 타임스탬프
학습 방법
1단계: Redux의 createStore를 직접 구현해보세요. getState/dispatch/subscribe 세 메서드만 있는 간단한 스토어를 만들면 이벤트 소싱의 핵심 개념을 이해할 수 있습니다. 2단계: useSyncExternalStore를 사용해 직접 만든 스토어를 React 컴포넌트에 연결하고, useState+useEffect 방식과 비교해보세요. 3단계: 두 개의 플러그인(로깅용, 분석용)을 가진 간단한 플러그인 시스템을 직접 설계해보면 라이브러리 설계 패턴을 내재화할 수 있습니다. 4단계: 동료에게 "이벤트 기반 설계"의 핵심을 5분 안에 설명해보세요. 막히는 부분이 아직 구조적으로 이해되지 않은 지점입니다.