npx claudepluginhub hbs9312/hbs9312-plugins --plugin backflowThis skill uses the workspace's default tool permissions.
ultrathink
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
ultrathink
당신은 백엔드 개발자입니다. FS의 비즈니스 룰과 TS의 처리 흐름을 서비스 계층으로 구현합니다.
.backflow/service-registry.md (있으면)$ARGUMENTS 에서:
FS의 모든 비즈니스 룰(BR)이 서비스 코드에 1:1 대응해야 합니다.
FS 비즈니스 룰 → 서비스 코드
──────────────────────────────────────────────────────────────
BR-001: 워크스페이스당 화자 최대 50명 → validateQuota() + 에러
BR-003: 화자명 중복 불가 → checkDuplicate() + 에러
BR-005: Admin/Member만 등록 가능 → 가드/미들웨어로 위임 (B5)
BR-007: 오디오 3초 이상 → validateAudio() + 에러
backend.md의 module_pattern에 따름:
# flat 패턴
{service_dir}/{feature}.service.ts
# feature-module 패턴
{src_root}/modules/{feature}/services/{feature}.service.ts
@Injectable()
export class SpeakerService {
constructor(
private readonly speakerRepo: SpeakerRepository,
private readonly eventEmitter: EventEmitter2, // 비동기 처리 시
) {}
async enroll(dto: EnrollSpeakerDto, context: RequestContext): Promise<Speaker> {
// TS 서버 측 검증 순서를 그대로 따름
// 1. 쿼터 확인 (BR-001)
await this.validateQuota(context.workspaceId)
// 2. 이름 중복 확인 (BR-003)
await this.checkDuplicate(dto.name, context.workspaceId)
// 3. 오디오 검증 (BR-007)
this.validateAudio(dto.audioFile)
// 4. 생성
const speaker = await this.speakerRepo.create({ ... })
// 5. 비동기 작업 트리거 (TS에 비동기로 명시된 경우)
this.eventEmitter.emit('speaker.enrolled', { speakerId: speaker.id })
return speaker
}
}
TS에 "서버 측 검증 순서"가 명시되어 있으면 그 순서 그대로 구현합니다. 순서가 다르면 에러 응답이 달라져 프론트엔드와 불일치합니다.
this.prisma.$transaction()@Transaction() 또는 queryRunnererror_handling.error_class로 변환// BR-001 위반
throw new AppException(
ErrorCode.QUOTA_EXCEEDED,
'워크스페이스당 최대 화자 수를 초과했습니다',
)
TS에 외부 호출이 있으면:
// TS: "임베딩 API 호출, 타임아웃 30초, 재시도 2회, 실패 시 상태 failed"
try {
await this.embeddingClient.process(audioData, { timeout: 30_000 })
} catch (error) {
await this.speakerRepo.updateStatus(speakerId, 'failed')
this.logger.error('Embedding failed', { speakerId, error })
}