Skip to content

Pipes

Pipe는 Guard를 통과한 IPC 요청의 인자를 변환하거나 검증하는 파이프라인이다. 핸들러가 실행되기 전에 역직렬화, 유효성 검사 등을 수행한다.

파이프라인 흐름

요청 → Guard(canActivate) → Pipe(transform) → Handler → 응답
         ↓ (실패)            ↓ (에러)          ↓ (에러)
    ExceptionHandler    ExceptionHandler   ExceptionHandler
         ↓                   ↓                  ↓
       응답                 응답                응답
  1. Guard를 모두 통과한다
  2. 글로벌 Pipe를 순서대로 실행한다
  3. 핸들러별 Pipe(@UsePipes)를 순서대로 실행한다
  4. 변환된 인자로 핸들러를 실행한다

Pipe에서 throw하면 ExceptionHandler가 처리한다.

IpcPipe 인터페이스

ts
interface IpcPipe {
  transform(args: unknown[], ctx: IpcContext): unknown[] | Promise<unknown[]>;
}
매개변수타입설명
argsunknown[]핸들러에 전달될 인자 배열
ctxIpcContextIPC 요청 컨텍스트 (채널, 윈도우 등)

반환값은 다음 Pipe 또는 핸들러에 전달할 변환된 인자 배열이다.

커스텀 Pipe 작성

Zod 스키마로 인자를 파싱하는 Pipe 예시:

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

const SCHEMA_MAP: Record<string, ZodType> = {
  'theme:set': ThemeSchema,
  'profile:create': CreateProfileSchema,
};

class ZodPipe implements IpcPipe {
  transform(args: unknown[], ctx: IpcContext) {
    const schema = SCHEMA_MAP[ctx.channel];
    if (!schema) return args;
    return [schema.parse(args[0])];
  }
}

특정 필드를 검증하는 Pipe 예시:

ts
class CwdPipe implements IpcPipe {
  transform(args: unknown[], _ctx: IpcContext) {
    const params = args[0];
    if (params && typeof params === 'object' && 'cwd' in params) {
      const cwd = (params as { cwd: unknown }).cwd;
      if (typeof cwd === 'string' && cwd.length > 0) {
        validateCwd(cwd);
      }
    }
    return args;
  }
}

적용 방법

글로벌 Pipe

createApp()pipes 배열로 전달한다. 모든 IPC 요청에 적용된다.

ts
createApp({
  guards: [new SenderGuard()],
  pipes: [new ZodPipe()],
  exceptionHandlers: [new ErrorHandler()],
  modules: [WindowModule, MyModule],
});

핸들러별 Pipe

@UsePipes() 데코레이터로 특정 핸들러에만 Pipe를 적용한다. 글로벌 Pipe 이후에 실행된다.

ts
import { Controller, UsePipes } from '@repo/electron-ipc';

@Controller(myContract)
class MyController {
  @UsePipes(new CwdPipe())
  startSession(params: SessionParams) {
    // CwdPipe를 통과한 후 실행된다
    return this.service.start(params);
  }

  listSessions() {
    // CwdPipe가 적용되지 않는다
    return this.service.list();
  }
}

실행 순서

글로벌 Pipe → 핸들러별 Pipe 순서로 실행된다. 각 Pipe의 반환값이 다음 Pipe의 입력이 된다.

ts
// 글로벌: [ZodPipe]
// 핸들러별: [CwdPipe]

// 실행 순서:
// 1. ZodPipe.transform(args, ctx)  → parsedArgs
// 2. CwdPipe.transform(parsedArgs, ctx) → validatedArgs
// 3. handler(...validatedArgs)

효과

Pipe를 도입하면 핸들러에서 역직렬화와 검증 보일러플레이트가 제거된다:

ts
// Before — 핸들러가 파싱과 검증을 직접 수행
startSession(params: unknown) {
  const parsed = SessionSchema.parse(params);
  validateCwd(parsed.cwd);
  return this.service.start(parsed);
}

// After — Pipe가 처리하고, 핸들러는 비즈니스 로직에 집중
@UsePipes(new CwdPipe())
startSession(params: SessionParams) {
  return this.service.start(params);
}

다음 단계