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/COEP | partition: '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 | 쿠키 |
localstorage | window.localStorage |
sessionstorage | window.sessionStorage |
indexdb | IndexedDB |
websql | WebSQL (deprecated) |
filesystem | 파일 시스템 API |
shadercache | GPU 셰이더 캐시 |
serviceworkers | Service Worker 등록 |
cachestorage | Cache 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.serviceWorkers | Service Worker 관리 |
session.extensions | Chrome 확장 로드 (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.set 시 url 누락 | 어떤 도메인에 set할지 모름 → 실패 | url 필수 |
Insight — Slack의 진짜 비밀
“한 Electron 앱 안에서 10개 워크스페이스를 동시에 띄울 수 있는 이유” —
Slack 데스크톱은 워크스페이스마다 별도
webContents+ 별도session.fromPartition('persist:ws-${id}')를 갖는다. 같은slack.comorigin이지만 진짜 다른 브라우저인 것처럼 격리된다. 그래서 한 워크스페이스에서 로그아웃해도 다른 워크스페이스는 그대로. 쿠키만이 아니라 Service Worker 캐시, IndexedDB 데이터, localStorage까지 따로.이 패턴은 웹 브라우저로는 시크릿 창 + 일반 창의 2개 격리만 가능하다는 한계를 그대로 노출한 Electron만의 강점이다.
다음 문서
- 세션을 다른 창에 어떻게 연결하는가 — 05 다중 창
- 외부 link로 들어온 호출을 어떤 session으로 처리할까 — 06 protocol
- 세션 위에 권한을 어떻게 좁힐까 — 05 보안
한 단락 요약
session은 쿠키·캐시·스토리지·webRequest·다운로드·권한을 묶은 객체다.partition으로 같은 origin도 완전 격리된 세션 N개를 운영할 수 있고, 이것이 멀티 계정 데스크톱 앱(Slack·Notion·Discord)이 데스크톱에서 살아남은 비결이다. 웹의 프로필 분리가 브라우저에 매여 있다면, Electron의 partition은 내 앱 안에서 내가 정의하는 격리 단위다.