쿠키 API를 활용한 로그인 저장/로드 기능 구현


개요

브라우저 쿠키를 여러벌 저장했다가 필요한 것들만 복원하는 기능을 구현하면 여러개의 로그인을 동시에 관리할 수 있을 것이다.

배경

개인 사용 목적으로 브라우저 확장을 이리저리 만들고 있는데, 이 프로젝트의 최종 목표는 인터넷 분석 툴들 엿먹이기였다. Google Analytics 등의 툴들은 유저 방문 기록을 쿠키에 저장했다가 알게모르게 자기네 서버로 전송하고 있는데, 이걸 그냥 보내지 않도록 막기만 할 것이 아니라, 가짜정보를 왕창 실어 보내면 훨씬 더 분석이 어려워지지 않겠느냐… 라는게 기본 아이디어.

하지만 쿠키 내용을 분석해서 이게 웹사이트에 필요한 쿠키인지, 아니면 그냥 분석툴에 필요한 쿠키인지 구분하는 것은 쉽지 않다. 그래서 일단 더 쉬운, 쿠키를 세이브/로드해서 여러개의 로그인을 동시에 관리할 수 있게 하는 기능부터 구현하기로 했다.

뭐… 내가 원신 쿠폰 코드를 여러 계정에 넣을때 좀 더 편하게 넣기 위해서 이러는 것만은 아니다.

디자인

브라우저 확장의 팝업 윈도우에서 UI를 제공하고, background script에서 쿠키를 읽고 쓰는 작업을 다뤄야 하기 때문에 두 프로세스 사이의 통신이 가능해야 한다. 여기서는 일전에 작성한 브라우저 확장을 위한 RPC를 이용해서 구현한다.

먼저 서버 역할을 할 background script에서 제공하는 API들을 다음과 같이 정의했다.

export type SavedCookie = {
  // ... properties included in the saved cookie
}

export type SaveStatus =
  "saved" | // in both cookie store and storage
  "unsaved" | // in cookie store but not in storage
  "modified" | // in both cookie store and storage, but value is different
  "stale" | // in storage but not in cookie store
  "unused"; // saved in storage, but domain does not match current url

export type CookieWithStatus = SavedCookie & { status: SaveStatus; };

export interface BackgroundService {
  /**
   * List cookies available from current page
   * 
   * @param url to query cookies
   * @returns cookies with save status
   */
  listCookies(url: string): Promise<CookieWithStatus[]>;
  /**
   * Save a cookie.
   * 
   * @param cookie to save
   */
  addCookie(cookie: SavedCookie): Promise<void>;
  /**
   * Remove a saved cookie from storage.
   * 
   * @param cookie to remove
   */
  removeCookie(cookie: Pick<SavedCookie, "domain" | "path" | "name">): Promise<void>;
  /**
   * Open a website with new incognito window and configured cookies.
   * 
   * @param url to open
   */
  openWithCookies(url: string): void;  
}

원래 API들이 더 단순할 수도 있는데, 구현하다보니 팝업 윈도우에만 쓰일 것이기 때문에 프론트를 위한 백엔드 패턴이 되었다.

구현

쿠키 저장은 chrome.storage API를 이용해서 구현했다. 나중에 여러 개의 쿠키 셋을 동시에 저장할 수 있도록 하기 위해 쿠키 셋 목록과, 각 쿠키 셋을 하나의 JS 객체 형태로 저장했다. storage에서 자체적으로 atomic update를 지원하지 않기 때문에 직접 구현한 락을 사용했다.

결론

이제 incognito 윈도우를 사용하면서도 번거롭게 새로 로그인하지 않아도 된다.