04 · session & 쿠키

이 문서가 답하는 질문: session은 정확히 무엇이고 쿠키 / 캐시 / 스토리지 / webRequest가 왜 그 한 객체에 묶여 있는가? partition: 'persist:user-a'진짜 다른 origin처럼 작동한다는 게 무슨 뜻인가?


한 줄 답 (Pyramid Top)

session쿠키·캐시·로컬스토리지·서비스워커·webRequest·다운로드 큐를 한 묶음으로 들고 있는 객체다. 웹은 브라우저가 프로필 단위로 알아서 관리하지만, Electron은 partition 문자열로 내가 직접 격리 단위를 정의한다 — 같은 앱 안에서 “유저 A로 로그인된 창”과 “유저 B로 로그인된 창”을 같은 origin이지만 다른 세션으로 띄울 수 있다.


Why — 왜 session을 따로 노출했는가

웹 브라우저에서 세션 격리는 보통 프로필 분리 (Chrome의 “사용자 추가”) 또는 시크릿 창으로만 가능하다. Electron은 한 앱 안에서 N개의 격리된 세션을 동시에 띄워야 할 일이 많다.

시나리오웹 브라우저Electron
멀티 계정 (예: Slack 워크스페이스 3개)시크릿 창 / 별도 프로필partition: 'persist:workspace-${id}'
외부 컨텐츠 격리 (광고/임베드)iframe + COOP/COEPpartition: 'isolated-ad' (in-memory)
로그인 안 한 미리보기시크릿 창partition 없는 in-memory session
쿠키 전수 삭제 (로그아웃)브라우저 설정session.clearStorageData()
모든 요청에 헤더 주입확장만 가능session.webRequest.onBeforeSendHeaders

한 줄 결론: 멀티 계정 앱은 Electron의 킬러 유스케이스 중 하나이고, 그것을 가능하게 하는 것이 partition이다.


How — session 모델

1) 네 가지 세션 종류

defaultSession

const { session } = require('electron');
const defaultSession = session.defaultSession;
// = BrowserWindow의 webPreferences에 session/partition 안 줬을 때 쓰는 세션
  • 대부분의 앱은 기본 세션 하나로 충분.
  • 모든 윈도우가 이 세션을 공유 — 쿠키/캐시 공유.

Persistent partition — persist: 접두사

const userA = session.fromPartition('persist:user-a');
const winA = new BrowserWindow({
  webPreferences: { session: userA, ...secure }
});
  • 디스크의 별도 폴더에 쿠키/스토리지 저장.
  • 앱 재시작해도 유지.
  • 같은 origin이라도 완전히 격리user-a에서 로그인했어도 user-b에서는 로그아웃 상태.

In-memory partition — persist: 접두사 없음

const tmp = session.fromPartition('preview-' + Date.now());
  • 메모리만 사용, 앱 종료 시 사라짐.
  • 미리보기·일회성 외부 임베드에 적합.

2) 한 줄로 보는 격리 관계

핵심: origin은 같아도 session이 다르면 완전 격리. 웹 브라우저로는 같은 탭에서 불가능한 것.

3) 쿠키 다루기 — session.cookies

// 설정
await session.defaultSession.cookies.set({
  url: 'https://api.example.com',
  name: 'token',
  value: 'abc123',
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  expirationDate: Date.now() / 1000 + 3600,
});
 
// 조회
const cookies = await session.defaultSession.cookies.get({
  domain: 'example.com',
});
 
// 삭제
await session.defaultSession.cookies.remove('https://example.com/', 'token');
 
// 변경 이벤트
session.defaultSession.cookies.on('changed', (event, cookie, cause, removed) => {
  console.log(`cookie ${removed ? 'removed' : 'set'}:`, cookie.name);
});

주의: secure: true는 https origin에만 — http URL에 set하면 무시된다.

4) 스토리지 일괄 정리 — clearStorageData

// 모든 데이터 삭제 — 로그아웃
await session.clearStorageData();
 
// 종류 선택
await session.clearStorageData({
  storages: ['cookies', 'localstorage', 'indexdb'],
  origin: 'https://example.com',
});
 
// 캐시만 (서비스워커 포함)
await session.clearCache();

지원 storages 종류:

의미
cookies쿠키
localstoragewindow.localStorage
sessionstoragewindow.sessionStorage
indexdbIndexedDB
websqlWebSQL (deprecated)
filesystem파일 시스템 API
shadercacheGPU 셰이더 캐시
serviceworkersService Worker 등록
cachestorageCache Storage API

로그아웃 = clearStorageData() + 창 reload 가 모범 패턴.

5) Proxy 설정

await session.defaultSession.setProxy({
  proxyRules: 'http=proxy.example.com:8080;https=proxy.example.com:8443',
  proxyBypassRules: '<local>',
});
  • 사내망 앱·VPN 통합 시 필수.
  • 세션마다 다른 proxy를 줄 수 있다 (userA는 사내, userB는 외부).

6) 권한 — Camera·Mic·Notifications

session.defaultSession.setPermissionRequestHandler((wc, permission, cb) => {
  // permission: 'media' | 'geolocation' | 'notifications' | 'fullscreen' | ...
  if (permission === 'notifications') return cb(true);
  if (permission === 'media' && wc.getURL().startsWith('https://meet.myapp.com/')) {
    return cb(true);
  }
  cb(false);
});
  • 웹에서는 사용자가 popup으로 답하는 권한을 — Electron에서는 내가 코드로 결정.
  • 디폴트는 거부 — 명시적으로 허용해야 한다.

What — session 객체의 능력 한 표

API의미
session.cookies쿠키 CRUD + 이벤트
session.clearStorageData()통합 삭제
session.clearCache()HTTP 캐시 비우기
session.webRequest.*모든 네트워크 요청 가로채기
session.protocol.handle()커스텀 protocol 처리
session.setProxy()프록시
session.setPermissionRequestHandler()권한 결정
session.setUserAgent()UA 변경
session.downloadURL() / 'will-download'다운로드
session.serviceWorkersService Worker 관리
session.extensionsChrome 확장 로드 (Electron 28+)

다운로드 흐름

session.defaultSession.on('will-download', (event, item, wc) => {
  item.setSavePath('/path/to/save/' + item.getFilename());
 
  item.on('updated', (event, state) => {
    if (state === 'progressing') {
      console.log(`${item.getReceivedBytes()} / ${item.getTotalBytes()}`);
    }
  });
  item.once('done', (event, state) => {
    console.log('download', state); // 'completed' | 'cancelled' | 'interrupted'
  });
});
 
// 트리거
wc.downloadURL('https://example.com/big.zip');

How (advanced) — 멀티 계정 패턴

// 워크스페이스마다 별도 session
function openWorkspace(workspaceId) {
  const sess = session.fromPartition(`persist:ws-${workspaceId}`);
 
  // 워크스페이스별 헤더 주입
  sess.webRequest.onBeforeSendHeaders((details, cb) => {
    details.requestHeaders['X-Workspace-Id'] = workspaceId;
    cb({ requestHeaders: details.requestHeaders });
  });
 
  const win = new BrowserWindow({
    webPreferences: {
      session: sess,
      preload: preloadPath,
      contextIsolation: true,
      sandbox: true,
    },
  });
  win.loadURL('https://example.com/workspace/' + workspaceId);
  return win;
}
 
openWorkspace('alpha'); // 창 1
openWorkspace('beta');  // 창 2 — 완전 독립 로그인

Slack·Notion·Discord 같은 멀티 워크스페이스 데스크톱 앱이 모두 이 패턴. 사용자는 한 창에서 다른 계정으로 동시에 작업한다.


What-if — 흔한 함정

함정증상해법
partition: 'foo' (persist: 없음)재시작하면 로그인 풀림'persist:foo'
같은 origin인데 partition 분리 안 함두 워크스페이스 쿠키가 섞임 → 같은 계정으로 보임session.fromPartition()로 격리
clearStorageData()그 창의 로드된 페이지는 그대로페이지에 남은 in-memory state가 살아 있음wc.reload() 추가
setProxy()app.whenReady() 전에 호출”Session not ready”ready 후에
webRequest 핸들러에서 동기 무거운 작업모든 페이지가 느려짐필터 좁히기 + 비동기
모든 origin에 setPermissionRequestHandler(cb(true))외부 사이트가 카메라 켤 수 있음origin 화이트리스트
cookies.seturl 누락어떤 도메인에 set할지 모름 → 실패url 필수

Insight — Slack의 진짜 비밀

“한 Electron 앱 안에서 10개 워크스페이스를 동시에 띄울 수 있는 이유”

Slack 데스크톱은 워크스페이스마다 별도 webContents + 별도 session.fromPartition('persist:ws-${id}') 를 갖는다. 같은 slack.com origin이지만 진짜 다른 브라우저인 것처럼 격리된다. 그래서 한 워크스페이스에서 로그아웃해도 다른 워크스페이스는 그대로. 쿠키만이 아니라 Service Worker 캐시, IndexedDB 데이터, localStorage까지 따로.

이 패턴은 웹 브라우저로는 시크릿 창 + 일반 창의 2개 격리만 가능하다는 한계를 그대로 노출한 Electron만의 강점이다.


다음 문서

  • 세션을 다른 창에 어떻게 연결하는가 — 05 다중 창
  • 외부 link로 들어온 호출을 어떤 session으로 처리할까 — 06 protocol
  • 세션 위에 권한을 어떻게 좁힐까05 보안

한 단락 요약

session쿠키·캐시·스토리지·webRequest·다운로드·권한을 묶은 객체다. partition으로 같은 origin도 완전 격리된 세션 N개를 운영할 수 있고, 이것이 멀티 계정 데스크톱 앱(Slack·Notion·Discord)이 데스크톱에서 살아남은 비결이다. 웹의 프로필 분리가 브라우저에 매여 있다면, Electron의 partition내 앱 안에서 내가 정의하는 격리 단위다.