쿠키 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 윈도우를 사용하면서도 번거롭게 새로 로그인하지 않아도 된다.