Community코딩 & 개발github.com

joshi-chinmay-016/AgentProof

NitroStack SDK에서 가드, 인터셉터, 미들웨어, 파이프, 예외 필터를 구현하고 적용하기 위한 모범 사례

AgentProof란 무엇인가요?

이 스킬은 NitroStack SDK 기반의 AI 에이전트 툴(예: Claude Code, Cursor, Codex)에서 요청 검증, 인가 확인, 응답 변환, 로깅, 오류 처리, 성능 추적 등을 구현하기 위한 포괄적인 가이드를 제공합니다. 가드(Guard), 인터셉터(Interceptor), 미들웨어(Middleware), 파이프(Pipe), 예외 필터(Exception Filter)의 인터페이스 정의와 실제 사용 예제를 TypeScript 코드와 함께 설명합니다. 각 구성 요소는 데코레이터(@UseGuards, @UseInterceptors, @UseMiddleware, @UsePipes, @UseFilters)를 통해 툴 메서드에 적용되며, `@Injectable()`, `@Middleware()`, `@Pipe()` 데코레이터로 구현체를 선언합니다. 이를 통해 개발자는 일관된 방식으로 교차 관심사를 분리하고 선언적으로 툴 동작을 제어할 수 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/joshi-chinmay-016/AgentProof/tree/HEAD/agentproof/.agents/skills/middleware-pipeline

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

사용 시기

NitroStack 툴 메서드에서 요청 검증, 인가 확인, 응답 매핑, 로깅, 오류 처리 또는 성능 추적을 구현할 때 이 스킬을 사용합니다.


1. 가드 (Guard 및 @UseGuards)

가드는 인증, 인가 또는 기타 조건에 따라 툴 핸들러가 요청을 처리해야 하는지 결정합니다.

인터페이스:

import { Guard, ExecutionContext } from '@nitrostack/core';

export interface Guard {
  canActivate(context: ExecutionContext): boolean | Promise<boolean>;
}

예시:

import { Guard, ExecutionContext, Injectable } from '@nitrostack/core';

@Injectable()
export class RolesGuard implements Guard {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const userRoles = context.clientMetadata?.roles || [];
    return userRoles.includes('admin');
  }
}

@UseGuards(...)를 사용하여 가드를 적용합니다:

import { Tool, UseGuards, z } from '@nitrostack/core';
import { RolesGuard } from './roles.guard.js';

export class AdminTools {
  @Tool({
    name: 'delete_system_logs',
    description: '서버에서 모든 시스템 로그를 삭제합니다.',
    inputSchema: z.object({}),
  })
  @UseGuards(RolesGuard)
  async deleteLogs() {
    return { success: true };
  }
}

2. 인터셉터 (InterceptorInterface 및 @UseInterceptors)

인터셉터는 툴 메서드 실행의 입력 인수를 변환하거나 매핑된 출력을 가로챌 수 있습니다.

인터페이스:

import { ExecutionContext } from '@nitrostack/core';

export interface InterceptorInterface {
  intercept(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown>;
}

예시:

import { InterceptorInterface, ExecutionContext, Injectable } from '@nitrostack/core';

@Injectable()
export class TimingInterceptor implements InterceptorInterface {
  async intercept(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown> {
    const start = Date.now();
    const result = await next();
    const duration = Date.now() - start;
    context.logger.info(`실행 시간: ${duration}ms`);
    return {
      ...result,
      _meta: { durationMs: duration }
    };
  }
}

3. 예외 필터 (ExceptionFilterInterface 및 @UseFilters)

예외 필터는 가드, 인터셉터 또는 툴 핸들러 자체에서 발생한 모든 오류를 포착하여 사용자 친화적인 JSON 페이로드로 매핑합니다.

인터페이스:

import { ExecutionContext } from '@nitrostack/core';

export interface ExceptionFilterInterface {
  catch(exception: unknown, context: ExecutionContext): unknown | Promise<unknown>;
}

예시:

import { ExceptionFilterInterface, ExecutionContext, Injectable } from '@nitrostack/core';

@Injectable()
export class CustomExceptionFilter implements ExceptionFilterInterface {
  catch(exception: unknown, context: ExecutionContext) {
    const message = exception instanceof Error ? exception.message : '알 수 없는 오류';
    return {
      error: true,
      message,
      timestamp: new Date().toISOString()
    };
  }
}

툴 메서드에 @UseFilters(...)를 사용하여 필터를 적용합니다:

import { Tool, UseFilters, z } from '@nitrostack/core';
import { CustomExceptionFilter } from './custom-exception.filter.js';

export class LoggingTools {
  @Tool({
    name: 'generate_report',
    description: '시스템 사용 보고서를 생성합니다.',
    inputSchema: z.object({}),
  })
  @UseFilters(CustomExceptionFilter)
  async generateReport() {
    throw new Error('아직 보고서 생성이 구현되지 않았습니다.');
  }
}

4. 미들웨어 (MiddlewareInterface, @Middleware 및 @UseMiddleware)

미들웨어는 요청이 툴 핸들러에 도달하기 전에 실행되며, next()를 호출하여 핸들러 실행을 감쌀 수 있습니다.

인터페이스:

import { ExecutionContext } from '@nitrostack/core';

export interface MiddlewareInterface {
  use(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown>;
}

예시:

import { Middleware, MiddlewareInterface, ExecutionContext } from '@nitrostack/core';

@Middleware()
export class LoggingMiddleware implements MiddlewareInterface {
  async use(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown> {
    context.logger.info(`툴 진입: ${context.toolName}`);
    try {
      const result = await next();
      context.logger.info(`툴 종료: ${context.toolName}`);
      return result;
    } catch (error) {
      context.logger.error(`툴 오류: ${error}`);
      throw error;
    }
  }
}

툴 메서드에 @UseMiddleware(...)를 사용하여 미들웨어를 적용합니다:

import { Tool, UseMiddleware, z } from '@nitrostack/core';
import { LoggingMiddleware } from './logging.middleware.js';

export class StationTools {
  @Tool({
    name: 'fetch_logs',
    description: '스테이션 작업 로그를 가져옵니다.',
    inputSchema: z.object({}),
  })
  @UseMiddleware(LoggingMiddleware)
  async fetchLogs() {
    return { status: 'operational' };
  }
}

5. 파이프 (PipeInterface, @Pipe 및 @UsePipes)

파이프는 입력 인수가 툴 핸들러 메서드에 도달하기 전에 변환하거나 검증하는 데 사용됩니다.

인터페이스:

import { ArgumentMetadata } from '@nitrostack/core';

export interface PipeInterface<T = unknown, R = unknown> {
  transform(value: T, metadata: ArgumentMetadata): R | Promise<R>;
}

예시:

import { Pipe, PipeInterface, ArgumentMetadata } from '@nitrostack/core';

@Pipe()
export class TrimPipe implements PipeInterface<Record<string, unknown>, Record<string, unknown>> {
  transform(value: Record<string, unknown>, metadata: ArgumentMetadata) {
    const trimmed: Record<string, unknown> = {};
    for (const [key, val] of Object.entries(value)) {
      trimmed[key] = typeof val === 'string' ? val.trim() : val;
    }
    return trimmed;
  }
}

툴 메서드에 @UsePipes(...)를 사용하여 파이프를 적용합니다:

import { Tool, UsePipes, z } from '@nitrostack/core';
import { TrimPipe } from './trim.pipe.js';

export class MessagingTools {
  @Tool({
    name: 'send_message',
    description: '다른 스테이션에 메시지를 보냅니다.',
    inputSchema: z.object({ text: z.string() }),
  })
  @UsePipes(TrimPipe)
  async sendMessage(input: { text: string }) {
    return { sentText: input.text };
  }
}

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

관련 스킬