state-processor skill
POST, PUT, PATCH, DELETE와 같은 쓰기 작업의 영속성 및 사이드 이펙트를 처리하는 state processor를 생성합니다.
- GitHub 스타
- 29
- 파일
- 1
- 마지막 커밋
- 2026년 10월 22일
- 라이선스
- —
- 허용된 도구
- —
state-processor skill은 무엇을 하나요?
state-processor은(는) agent skill입니다. POST, PUT, PATCH, DELETE와 같은 쓰기 작업의 영속성 및 사이드 이펙트를 처리하는 state processor를 생성합니다.
skill의 SKILL.md에서 생성된 요약입니다.
state-processor skill 정보
이 SKILL은 API Platform에서 쓰기 작업과 사이드 이펙트를 관리할 수 있게 해줍니다. 개발자는 CQRS 스타일 작업의 Command 측면, 특히 POST, PUT, PATCH, DELETE 요청을 처리하는 state processor를 생성할 수 있습니다. 에이전트가 비밀번호 해싱, 관련 엔티티 생성, 이메일 알림 트리거와 같은 커스텀 영속성 로직을 구현해야 할 때, 이 SKILL을 사용하여 쓰기 단계 동안 데이터가 처리되는 방식을 정의할 수 있습니다.
개발자는 이러한 processor를 사용하여 Doctrine ORM persist processor나 MongoDB ODM remove processor와 같은 내장 서비스를 데코레이트할 수 있습니다. 이 방식을 통해 표준 영속성 사이클 전후에 로직을 추가할 수 있습니다. 또한 GET 작업의 write 플래그를 true로 설정하여 소프트 삭제(soft-delete) 기능을 구현하거나 파일 다운로드를 위한 Symfony Response 객체를 반환하는 것과 같은 고급 유스케이스도 지원합니다. ProcessorInterface를 활용하여 Symfony 및 Laravel Eloquent를 포함한 다양한 프레임워크에서 작동합니다.
agent가 수행할 수 있는 작업
- 1커스텀 사이드 이펙트 구현주문이 성공적으로 영속화된 직후 이메일 알림을 보내는 로직을 추가합니다.
- 2소프트 삭제 로직 처리데이터베이스에서 리소스를 완전히 삭제하는 대신 삭제된 상태로 표시하는 재사용 가능한 processor를 생성합니다.
- 3파일 다운로드 생성이진 파일 응답을 반환하는 processor를 트리거하도록 GET 작업을 구성합니다.
- 4관련 엔티티 생성 자동화새 계정이 등록될 때마다 mailbox와 같은 자식 리소스가 자동으로 생성되도록 보장합니다.
skill의 SKILL.md를 기반으로 작성되었습니다. 모든 사용 사례는 해당 문서의 인용구와 대조 확인되었습니다.
state-processor skill 설치 방법
각 명령은 skill 폴더를 클라이언트의 skills 디렉터리에 복사합니다. 클라이언트를 재시작하거나 새 세션을 시작하여 로드하세요.플러그인에 state-processor skill 설치
/plugin marketplace add api-platform/skillset /plugin install api-platform@api-platform-skillset
Claude Code에 state-processor skill 설치
~/.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processorClaude Code (project)에 state-processor skill 설치
.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor .claude/skills/state-processorCodex에 state-processor skill 설치
~/.agents/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.agents/skills/state-processorGemini CLI에 state-processor skill 설치
~/.gemini/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.gemini/skills/state-processorClaude.ai에 state-processor skill 설치
npx degit api-platform/skillset/skills/state-processor state-processor && zip -r state-processor.zip state-processor # claude.ai → Customize → Skills → + → Create skill → Upload a skill → state-processor.zip
SKILL.md
GitHub에서 보기Creating State Processors
State Processors handle persistence and side effects for write operations (Post, Patch, Put, Delete) — the write phase of an operation, the Command side of API Platform's CQRS-style split (a state-provider handles the Query/read side).
A processor runs when the operation's write flag is true. You normally leave it
unset and API Platform resolves it from the request: write defaults to "the HTTP
method is not safe", and read (the provider) to "the operation has URI
variables, or the method is safe".
| Operation | processor runs (write) |
provider runs (read) |
|---|---|---|
Get (item) / GetCollection |
no | yes |
Post (collection) |
yes | no (no URI vars, unsafe) |
Put, Patch, Delete (item) |
yes | yes |
A Get defaults to write: false, so its processor never fires until you set
write: true (file downloads, report generation — see Returning HTTP Responses
below). See operations → Read & write phases for the full read → deserialize → validate → write → serialize lifecycle.
Basic Structure
<?php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
/**
* @implements ProcessorInterface<YourResource, YourResource>
*/
final class YourProcessor implements ProcessorInterface
{
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
// Business logic + persistence
return $data;
}
}
Decorating Built-in Processors
Wrap the default processor to add side effects before/after persistence:
use Symfony\Component\DependencyInjection\Attribute\Autowire;
final class OrderCreateProcessor implements ProcessorInterface
{
public function __construct(
#[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
private ProcessorInterface $persistProcessor,
private MailerInterface $mailer,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
// Pre-persistence: set relationships, hash passwords, etc.
$data->setCreatedBy($this->getUser());
$result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);
// Post-persistence: send notifications, trigger events
$this->mailer->send(new OrderConfirmation($result));
return $result;
}
}
Doctrine service names
| Persistence | Persist Processor | Remove Processor |
|---|---|---|
| ORM | api_platform.doctrine.orm.state.persist_processor |
api_platform.doctrine.orm.state.remove_processor |
| MongoDB ODM | api_platform.doctrine_mongodb.odm.state.persist_processor |
api_platform.doctrine_mongodb.odm.state.remove_processor |
Laravel (Eloquent):
ProcessorInterfaceis identical and resources reference a processor by class-string (processor: YourProcessor::class). The built-in Eloquent processors are bound in the container by class name —ApiPlatform\Laravel\Eloquent\State\PersistProcessor(handles create/update, including BelongsTo/HasMany relations andstandard_put) and…\State\RemoveProcessor. To decorate, type-hint that concrete class in your constructor instead of using a#[Autowire(service: 'api_platform.doctrine.*')]attribute. Side-effect patterns (mailers, events) work the same via Laravel's container. Returning a SymfonyResponsefrom a processor is also supported.
Creating Related Entities in a Processor
Create child resources alongside the main resource:
final class AccountCreateProcessor implements ProcessorInterface
{
public function __construct(
#[Autowire(service: 'api_platform.doctrine_mongodb.odm.state.persist_processor')]
private ProcessorInterface $persistProcessor,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
$this->hashPassword($data);
// Create related entities before persisting
foreach (['INBOX', 'Sent', 'Trash', 'Drafts'] as $name) {
$mailbox = new Mailbox();
$mailbox->path = $name;
$data->addMailbox($mailbox); // cascade: ['persist']
}
// Single persist call saves parent + children
return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
}
}
Soft-Delete Processor
Reusable processor for soft-deleting resources:
/**
* @implements ProcessorInterface<Account|Domain, Account|Domain>
*/
readonly class SoftDeleteProcessor implements ProcessorInterface
{
public function __construct(private DocumentManager $dm) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
$data->isDeleted = true;
$this->dm->persist($data);
$this->dm->flush();
return $data;
}
}
Assign to Delete operations:
new Delete(processor: SoftDeleteProcessor::class)
Combine with a collection extension that filters isDeleted = false to hide soft-deleted records from queries (see securing-collections skill).
Returning HTTP Responses (File Downloads)
Processors can return a Symfony Response for binary content:
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Response;
/**
* @implements ProcessorInterface<Message, Response>
*/
final class DownloadProcessor implements ProcessorInterface
{
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Response
{
$content = $this->buildContent($data);
$response = new Response($content);
$response->headers->set('Content-Type', 'application/octet-stream');
$response->headers->set('Content-Disposition',
HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $data->getId() . '.bin')
);
return $response;
}
}
Use with a Get operation that has write: true:
new Get(
uriTemplate: '/orders/{id}/download',
write: true,
processor: DownloadProcessor::class,
name: '_api_order_download',
)
Handling Both Persist and Delete
When a single processor handles Create/Update AND Delete, inject both services:
public function __construct(
#[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
private ProcessorInterface $persistProcessor,
#[Autowire(service: 'api_platform.doctrine.orm.state.remove_processor')]
private ProcessorInterface $removeProcessor,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
if ($operation instanceof DeleteOperationInterface) {
return $this->removeProcessor->process($data, $operation, $uriVariables, $context);
}
return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
}
Processor Parameters
- $data: The deserialized/validated object
- $operation: Operation metadata (Post, Patch, Delete, etc.)
- $uriVariables: URI path variables
- $context: Includes
previous_data(before modification),request,resource_class
Best Practices
- Always return data from processors — omitting breaks serialization
- Decorate built-in processors rather than replacing them
- Keep processors focused: one processor per operation when logic differs significantly
- Use
$context['previous_data']in Patch/Put to detect what changed - Throw
HttpExceptionsubclasses for domain errors (BadRequestHttpException,UnprocessableEntityHttpException)
이 스킬의 파일
1state-processor skill 질문
Claude Code에 state-processor skill을 어떻게 설치하나요?
npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processor를 실행합니다. Claude Code는 ~/.claude/skills(모든 프로젝트) 또는 .claude/skills(한 프로젝트)에서 skills를 로드하며, 설명이 작업과 일치할 때 skill을 사용합니다.
state-processor skill을 사용할 수 있는 클라이언트는 무엇인가요?
Agent Skills 형식을 읽는 모든 클라이언트(Claude Code, Codex, Gemini CLI, claude.ai(ZIP으로 업로드))가 사용할 수 있습니다. skill 내 스크립트는 클라이언트가 코드 실행을 허용하는 경우에만 실행됩니다.
state processor는 언제 실행되나요?
작업의 'write' 플래그가 true로 설정되었을 때 실행되며, 일반적으로 POST, PUT, PATCH, DELETE 작업 중에 실행됩니다.
GET 작업에도 processor를 사용할 수 있나요?
네, 하지만 GET 작업에 'write: true'를 명시적으로 설정해야 합니다. 이는 파일 다운로드나 보고서 생성과 같은 작업에 유용합니다.
쓰기 작업 중에 이메일 전송과 같은 사이드 이펙트를 추가하려면 어떻게 하나요?
기본 제공되는 persistence processor를 데코레이트하여 데이터가 저장되기 전이나 후에 로직을 실행할 수 있습니다.
API Platform에서 provider와 processor의 차이점은 무엇인가요?
state-provider는 CQRS 스타일 분리에서 Query(읽기) 측면을 담당하고, state-processor는 Command(쓰기) 측면을 담당합니다.