원클릭으로
new-service
Scaffold a service class for a given domain following project conventions
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Scaffold a service class for a given domain following project conventions
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Implement a numbered testing strategy phase from docs/testing_strategy_phases/phase_$ARGUMENTS_*.md step by step
Implement a numbered phase from docs/phases/phase-$ARGUMENTS.md step by step
Review changed code for correctness, security, and project conventions. Invoke this skill automatically whenever a complete unit of work is done — a full implementation phase, a complete feature (repository + service + route + component all connected), or just before creating a PR. Do NOT invoke after editing a single file or mid-way through a feature. If the user says "done", "finished", "phase complete", "ready to commit", or similar, trigger this review immediately.
Run prisma migrate dev, regenerate client to the correct output path, and verify import paths
Scaffold a Prisma-backed repository for a given Prisma model
Final pre-commit check: run type-check (npx tsc --noEmit), lint (npm run lint), and tests (npm test)
| name | new-service |
| description | Scaffold a service class for a given domain following project conventions |
| disable-model-invocation | true |
| argument-hint | [domain-name] |
Scaffold a service for the $ARGUMENTS domain.
src/services/$ARGUMENTS.service.tssrc/types/index.ts (add alongside existing schemas)import { ${camelCase}Repository } from '@/repositories/$ARGUMENTS.repository'
import { createInputSchema, type CreateInput } from '@/types'
export const ${camelCase}Service = {
async create(userId: string, input: CreateInput) {
createInputSchema.parse(input) // validate first
return ${camelCase}Repository.create({ ...input, userId })
},
async getById(id: string, userId: string) {
const record = await ${camelCase}Repository.findById(id)
if (!record) throw new Error('Not found')
if (record.userId !== userId) throw new Error('Unauthorized') // AFTER fetch
return record
},
async listForUser(userId: string) {
return ${camelCase}Repository.findMany({ userId })
},
async update(id: string, userId: string, input: CreateInput) {
createInputSchema.parse(input)
const existing = await ${camelCase}Repository.findById(id)
if (!existing) throw new Error('Not found')
if (existing.userId !== userId) throw new Error('Unauthorized')
return ${camelCase}Repository.update(id, input)
},
async delete(id: string, userId: string) {
const existing = await ${camelCase}Repository.findById(id)
if (!existing) throw new Error('Not found')
if (existing.userId !== userId) throw new Error('Unauthorized')
return ${camelCase}Repository.delete(id)
},
}
The check goes AFTER the fetch, not before. This is intentional:
'Not found' (becomes 404)'Unauthorized' (becomes 403)
Checking before the fetch would collapse both cases into 403 and leak that a record exists.userId flows in as a parameterServices do NOT call authService.getUser() internally. The API route retrieves the user from the session and passes userId into every service method. This keeps services testable in isolation.
@/repositories/...@/typesprisma directly — that is the repository's job@prisma/clientDefine in src/types/index.ts:
export const createInvoiceSchema = z.object({ ... })
export type CreateInvoiceInput = z.infer<typeof createInvoiceSchema>
npx tsc --noEmit