Skip to content

Guards

Guard는 IPC 요청의 유효성을 검증하는 파이프라인이다. 모든 Guard가 통과해야 핸들러가 실행된다.

파이프라인 흐름

요청 → Guard(canActivate) → Pipe(transform) → Handler → 응답
         ↓ (실패)            ↓ (에러)          ↓ (에러)
    ExceptionHandler    ExceptionHandler   ExceptionHandler
         ↓                   ↓                  ↓
       응답                 응답                응답
  1. 모든 Guard의 canActivate()를 순서대로 실행한다
  2. Guard가 false를 반환하면 ExceptionHandlercatch()에 에러를 전달한다
  3. Guard 통과 후 Pipe를 실행한다
  4. Pipe 통과 후 핸들러를 실행한다

IpcContext

Guard와 ExceptionHandler에 전달되는 컨텍스트 객체:

ts
interface IpcContext {
  event: IpcMainInvokeEvent;  // Electron IPC 이벤트
  channel: string;            // IPC 채널 이름
  window: BrowserWindow;      // 요청을 보낸 윈도우 (미상 시 메인 폴백)
  args: unknown[];            // 핸들러 인자
}

IpcGuard 인터페이스

ts
interface IpcGuard {
  canActivate(ctx: IpcContext): boolean | Promise<boolean>;
}

커스텀 Guard 작성

ts
import type { IpcGuard, IpcContext } from '@repo/electron-ipc';

class AuthGuard implements IpcGuard {
  canActivate(ctx: IpcContext): boolean {
    // 인증된 요청만 허용
    const token = ctx.args[0];
    return typeof token === 'string' && isValidToken(token);
  }
}

SenderGuard

패키지에 내장된 Guard. IPC 메시지가 등록된 윈도우의 webContents에서 온 것인지 검증한다.

ts
import { SenderGuard } from '@repo/electron-ipc';

createApp({
  guards: [new SenderGuard()],
  // ...
});

외부 웹 페이지를 최상위로 띄운 창(OAuth 팝업 등)도 BrowserWindow라 기본으로는 통과한다. 그런 창이 있으면 isAppWindow로 걸러낸다.

ts
createApp({
  guards: [new SenderGuard({ isAppWindow: (win) => !isPopup(win) })],
  // ...
});

내부 구현:

ts
class SenderGuard implements IpcGuard {
  canActivate(ctx: IpcContext): boolean {
    const window = BrowserWindow.fromWebContents(ctx.event.sender);
    // 본 앱 윈도우(메인 + 서브)의 최상위 webContents만 허용한다.
    // 창에 얹힌 자식 뷰(WebContentsView 등)는 외부 웹 컨텐츠일 수 있어 거부한다.
    if (window === null || window.webContents !== ctx.event.sender) return false;
    return this.isAppWindow(window); // 기본은 모든 창 허용
  }
}

GuestViewGuard

SenderGuard의 대칭인 내장 Guard. 발신자가 본 앱 윈도우에 얹힌 자식 뷰(WebContentsView 등)인지 검증한다 — send 채널(게스트 → 메인 단방향)의 발신자 정책으로 sendGuards에 쓴다.

ts
import { GuestViewGuard } from '@repo/electron-ipc';

createApp({
  guards: [new SenderGuard()],      // handle: 앱 창만
  sendGuards: [new GuestViewGuard()], // send: 게스트 뷰만
  // ...
});

내부 구현:

ts
class GuestViewGuard implements IpcGuard {
  canActivate(ctx: IpcContext): boolean {
    const window = BrowserWindow.fromWebContents(ctx.event.sender);
    // 앱 창에 얹힌 자식 뷰만 허용 — 최상위 webContents(앱 UI)와
    // 어느 창에도 속하지 않은 발신자는 거부한다.
    return window !== null && window.webContents !== ctx.event.sender;
  }
}

contract에 sends가 있는데 sendGuards를 지정하지 않으면 부트스트랩 에러다 (기본 거부).

적용

Guard는 createApp()guards 배열로 전달한다. 배열 순서대로 실행되며, 하나라도 false면 즉시 중단한다.

ts
createApp({
  guards: [new SenderGuard(), new AuthGuard()],
  modules: [WindowModule, MyModule],
});

다음 단계