이 글에서 다루는 것
브라우저 확장 프로그램에서 한 화면이 데이터를 저장한 뒤 다른 화면에 최신 목록이 나타날 때까지의 흐름을 따라갔다. 특정 제품의 구현이나 수치가 아니라, Content Script·Background Service Worker·Side Panel처럼 분리된 실행 공간을 연결할 때 어디서나 적용할 수 있는 메시지 계약과 상태 갱신 원칙만 정리한다.
먼저 알아둘 개념
RPC(Remote Procedure Call) 는 다른 실행 공간에 구조화된 요청을 보내 작업 결과를 받는 방식이다. 캐시 무효화(cache invalidation) 는 저장해 둔 조회 결과가 오래됐다고 표시해 최신 값을 다시 읽게 하는 처리다. 단일 진실 공급원(single source of truth) 은 같은 데이터의 최종 기준을 한 저장소에만 두는 원칙이다.
메모리로 직접 연결되지 않은 화면들
웹페이지에서 사용자가 선택한 내용을 저장하고 확장 프로그램의 Side Panel에서 목록을 보여 주는 일반적인 기능을 생각했다. Content Script는 현재 탭의 DOM을 읽을 수 있고, Background Service Worker는 저장과 권한을 맡으며, Side Panel은 목록을 렌더링한다. 세 공간은 동시에 떠 있어도 같은 메모리 객체를 공유하지 않는다.
그래서 저장 버튼부터 목록 갱신까지를 한 줄씩 추적했다. UI의 sendRequest()는 runtime.sendMessage()로 { type, payload } 요청을 보내고, Background listener는 받은 값을 runtime schema로 검사한다. 검증을 통과한 요청만 operation router가 등록된 handler에 전달하고, handler는 transaction을 마친 뒤 { ok, data | error } 형태로 결과를 돌려준다.
저장 응답만으로 다른 화면은 갱신되지 않는다
처음에는 저장 응답이 오면 모든 화면이 새 데이터를 알 것처럼 생각하기 쉽다. 하지만 응답은 요청한 화면으로만 돌아간다. 별도 Side Panel이 최신 목록을 보여 주려면 저장 이후 상태 변경을 알리는 또 다른 경로가 필요하다.
여기서는 전체 새 데이터를 이벤트에 복사하지 않고 “이 범위의 데이터가 바뀌었다”는 작은 신호만 방송하는 구조를 살펴봤다. Side Panel은 신호를 받으면 관련 TanStack Query cache를 무효화하고 IndexedDB의 최신 상태를 다시 읽는다. 명령의 성공 여부를 알려 주는 응답과, 다른 화면이 재조회할 계기를 주는 알림은 책임이 다르므로 분리하는 편이 이해하기 쉬웠다.
→ invalidateQueries와 staleTime
이벤트를 놓쳐도 데이터가 사라지지 않게 하기
Side Panel이 닫혀 있으면 방송을 받을 listener가 없을 수 있다. 그래도 변경 신호가 데이터 자체가 아니라 재조회 계기일 뿐이라면 문제되지 않는다. 나중에 Side Panel을 열 때 최초 query가 IndexedDB를 읽어 최신 상태를 얻는다.
이 구조에서 IndexedDB가 단일 진실 공급원이고 query cache는 다시 만들 수 있는 복사본이다. 이벤트 수신 여부를 데이터 보존 조건으로 만들지 않았기 때문에 실행 공간의 수명이 서로 달라도 일관성을 회복할 수 있다. 반대로 이벤트 payload를 새 원본처럼 저장하면 이벤트를 놓친 화면과 받은 화면의 상태가 갈라질 수 있다.
배운 점
Chrome이 제공하는 메시지 API와 애플리케이션의 RPC 계층을 구분해야 한다. 플랫폼은 값을 옮겨 주지만, 어떤 요청을 허용하는지, 어떤 handler를 실행하는지, 실패를 어떤 형식으로 돌려주는지는 직접 설계해야 한다.
또한 요청 응답과 변경 알림을 한 메커니즘으로 보지 않아야 한다. 요청자는 작업 결과를 받고, 다른 화면은 변경 신호를 받아 필요한 범위만 재조회한다. 마지막으로 캐시는 편의를 위한 복사본으로 두고 최신 데이터의 기준은 하나의 저장소에 두면, 화면이 닫혔다 열리거나 Background가 다시 시작되어도 상태를 복구하기 쉽다.