Communitygithub.com

laravel-patterns

Laravel 아키텍처 패턴, 라우팅/컨트롤러, Eloquent ORM, 서비스 레이어, 큐, 이벤트, 캐싱 및 프로덕션 애플리케이션용 API 리소스. Laravel 앱(컨트롤러, Eloquent, 서비스 레이어, 큐 또는 API 리소스)을 빌드하거나 검토할 때 사용합니다.

laravel-patterns란 무엇인가요?

이 스킬은 확장 가능하고 유지 관리가 용이한 프로덕션급 Laravel 애플리케이션을 구축하기 위한 아키텍처 패턴을 제공합니다. 컨트롤러, 서비스, 액션, 모델 간의 명확한 경계를 정의하여 관심사를 분리하고 코드 구성을 개선하는 데 중점을 둡니다. Eloquent ORM 사용의 모범 사례(예: 타입 모델, 캐스트, 스코프, 열성적 로딩)를 포함하여 N+1 문제를 방지하고 도메인 로직의 일관성을 유지하는 방법을 다룹니다. 또한 명시적 바인딩과 범위 바인딩을 통한 라우팅, 요청 검증을 위한 Form Request, 일관된 API 응답을 위한 API 리소스, 무거운 작업을 위한 큐 및 캐싱 전략을 다룹니다. 이 스킬은 프로젝트 구조, 마이그레이션, 서비스 컨테이너 바인딩, 쿼리 객체, 트랜잭션, 설정 관리를 포함한 실제 예시를 제공하므로 Laravel 개발자에게 포괄적인 참고 자료 역할을 합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/laravel-patterns

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

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

문서

Laravel 개발 패턴

확장 가능하고 유지 관리가 용이한 프로덕션급 Laravel 아키텍처 패턴입니다.

사용 시기

  • Laravel 웹 애플리케이션 또는 API 구축
  • 컨트롤러, 서비스 및 도메인 로직 구조화
  • Eloquent 모델 및 관계 작업
  • 리소스 및 페이지네이션이 포함된 API 설계
  • 큐, 이벤트, 캐싱 및 백그라운드 작업 추가

작동 방식

  • 명확한 경계(컨트롤러 -> 서비스/액션 -> 모델)를 중심으로 앱을 구조화합니다.
  • 명시적 바인딩과 범위 지정 바인딩을 사용하여 라우팅을 예측 가능하게 유지하고, 접근 제어를 위해 여전히 권한 부여를 적용합니다.
  • 타입 모델, 캐스트 및 스코프를 선호하여 도메인 로직의 일관성을 유지합니다.
  • IO 집약적인 작업은 큐에 넣고 비용이 많이 드는 읽기는 캐시합니다.
  • 구성을 config/*에 중앙 집중화하고 환경을 명시적으로 유지합니다.

예제

프로젝트 구조

명확한 레이어 경계(HTTP, 서비스/액션, 모델)가 있는 기존 Laravel 레이아웃을 사용합니다.

권장 레이아웃

app/
├── Actions/            # 단일 목적의 사용 사례
├── Console/
├── Events/
├── Exceptions/
├── Http/
│   ├── Controllers/
│   ├── Middleware/
│   ├── Requests/       # Form request 유효성 검사
│   └── Resources/      # API 리소스
├── Jobs/
├── Models/
├── Policies/
├── Providers/
├── Services/           # 도메인 서비스 조정
└── Support/
config/
database/
├── factories/
├── migrations/
└── seeders/
resources/
├── views/
└── lang/
routes/
├── api.php
├── web.php
└── console.php

컨트롤러 -> 서비스 -> 액션

컨트롤러를 얇게 유지하세요. 오케스트레이션은 서비스에, 단일 목적 로직은 액션에 배치합니다.

final class CreateOrderAction
{
    public function __construct(private OrderRepository $orders) {}

    public function handle(CreateOrderData $data): Order
    {
        return $this->orders->create($data);
    }
}

final class OrdersController extends Controller
{
    public function __construct(private CreateOrderAction $createOrder) {}

    public function store(StoreOrderRequest $request): JsonResponse
    {
        $order = $this->createOrder->handle($request->toDto());

        return response()->json([
            'success' => true,
            'data' => OrderResource::make($order),
            'error' => null,
            'meta' => null,
        ], 201);
    }
}

라우팅 및 컨트롤러

명확성을 위해 경로-모델 바인딩과 리소스 컨트롤러를 선호합니다.

use Illuminate\Support\Facades\Route;

Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('projects', ProjectController::class);
});

경로 모델 바인딩 (범위 지정)

범위 지정 바인딩을 사용하여 테넌트 간 액세스를 방지합니다.

Route::scopeBindings()->group(function () {
    Route::get('/accounts/{account}/projects/{project}', [ProjectController::class, 'show']);
});

중첩 경로 및 바인딩 이름

  • 이중 중첩을 방지하기 위해 접두사와 경로를 일관성 있게 유지합니다(예: conversation vs conversations).
  • 바인딩된 모델과 일치하는 단일 매개변수 이름을 사용합니다(예: Conversation의 경우 {conversation}).
  • 중첩 시 부모-자식 관계를 적용하기 위해 범위 지정 바인딩을 선호합니다.
use App\Http\Controllers\Api\ConversationController;
use App\Http\Controllers\Api\MessageController;
use Illuminate\Support\Facades\Route;

Route::middleware('auth:sanctum')->prefix('conversations')->group(function () {
    Route::post('/', [ConversationController::class, 'store'])->name('conversations.store');

    Route::scopeBindings()->group(function () {
        Route::get('/{conversation}', [ConversationController::class, 'show'])
            ->name('conversations.show');

        Route::post('/{conversation}/messages', [MessageController::class, 'store'])
            ->name('conversation-messages.store');

        Route::get('/{conversation}/messages/{message}', [MessageController::class, 'show'])
            ->name('conversation-messages.show');
    });
});

매개변수가 다른 모델 클래스로 확인되도록 하려면 명시적 바인딩을 정의합니다. 사용자 정의 바인딩 로직의 경우 Route::bind()를 사용하거나 모델에서 resolveRouteBinding()을 구현합니다.

use App\Models\AiConversation;
use Illuminate\Support\Facades\Route;

Route::model('conversation', AiConversation::class);

서비스 컨테이너 바인딩

명확한 의존성 연결을 위해 서비스 프로바이더에서 인터페이스를 구현체에 바인딩합니다.

use App\Repositories\EloquentOrderRepository;
use App\Repositories\OrderRepository;
use Illuminate\Support\ServiceProvider;

final class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(OrderRepository::class, EloquentOrderRepository::class);
    }
}

Eloquent 모델 패턴

모델 구성

final class Project extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'owner_id', 'status'];

    protected $casts = [
        'status' => ProjectStatus::class,
        'archived_at' => 'datetime',
    ];

    public function owner(): BelongsTo
    {
        return $this->belongsTo(User::class, 'owner_id');
    }

    public function scopeActive(Builder $query): Builder
    {
        return $query->whereNull('archived_at');
    }
}

사용자 정의 캐스트 및 값 객체

엄격한 타입 지정을 위해 열거형 또는 값 객체를 사용합니다.

use Illuminate\Database\Eloquent\Casts\Attribute;

protected $casts = [
    'status' => ProjectStatus::class,
];
protected function budgetCents(): Attribute
{
    return Attribute::make(
        get: fn (int $value) => Money::fromCents($value),
        set: fn (Money $money) => $money->toCents(),
    );
}

N+1 문제 방지를 위한 열성적 로딩

$orders = Order::query()
    ->with(['customer', 'items.product'])
    ->latest()
    ->paginate(25);

복잡한 필터를 위한 쿼리 객체

final class ProjectQuery
{
    public function __construct(private Builder $query) {}

    public function ownedBy(int $userId): self
    {
        $query = clone $this->query;

        return new self($query->where('owner_id', $userId));
    }

    public function active(): self
    {
        $query = clone $this->query;

        return new self($query->whereNull('archived_at'));
    }

    public function builder(): Builder
    {
        return $this->query;
    }
}

글로벌 스코프 및 소프트 삭제

기본 필터링에는 글로벌 스코프를, 복구 가능한 레코드에는 SoftDeletes를 사용합니다. 계층적 동작을 의도하지 않는 한 동일한 필터에 대해 글로벌 스코프 또는 명명된 스코프 중 하나만 사용하고 둘 다 사용하지 마십시오.

use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;

final class Project extends Model
{
    use SoftDeletes;

    protected static function booted(): void
    {
        static::addGlobalScope('active', function (Builder $builder): void {
            $builder->whereNull('archived_at');
        });
    }
}

재사용 가능한 필터를 위한 쿼리 스코프

use Illuminate\Database\Eloquent\Builder;

final class Project extends Model
{
    public function scopeOwnedBy(Builder $query, int $userId): Builder
    {
        return $query->where('owner_id', $userId);
    }
}

// 서비스, 리포지토리 등에서
$projects = Project::ownedBy($user->id)->get();

다단계 업데이트를 위한 트랜잭션

use Illuminate\Support\Facades\DB;

DB::transaction(function (): void {
    $order->update(['status' => 'paid']);
    $order->items()->update(['paid_at' => now()]);
});

마이그레이션

명명 규칙

  • 파일 이름은 타임스탬프를 사용합니다: YYYY_MM_DD_HHMMSS_create_users_table.php
  • 마이그레이션은 익명 클래스(명명된 클래스 없음)를 사용합니다. 파일 이름이 의도를 전달합니다.
  • 테이블 이름은 snake_case이며 기본적으로 복수형입니다.

마이그레이션 예제

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('orders', function (Blueprint $table): void {
            $table->id();
            $table->foreignId('customer_id')->constrained()->cascadeOnDelete();
            $table->string('status', 32)->index();
            $table->unsignedInteger('total_cents');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('orders');
    }
};

Form Request 및 유효성 검사

유효성 검사는 Form Request에 유지하고 입력을 DTO로 변환합니다.

use App\Models\Order;

final class StoreOrderRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()?->can('create', Order::class) ?? false;
    }

    public function rules(): array
    {
        return [
            'customer_id' => ['required', 'integer', 'exists:customers,id'],
            'items' => ['required', 'array', 'min:1'],
            'items.*.sku' => ['required', 'string'],
            'items.*.quantity' => ['required', 'integer', 'min:1'],
        ];
    }

    public function toDto(): CreateOrderData
    {
        return new CreateOrderData(
            customerId: (int) $this->validated('customer_id'),
            items: $this->validated('items'),
        );
    }
}

API 리소스

리소스와 페이지네이션을 사용하여 API 응답을 일관되게 유지합니다.

$projects = Project::query()->active()->paginate(25);

return response()->json([
    'success' => true,
    'data' => ProjectResource::collection($projects->items()),
    'error' => null,
    'meta' => [
        'page' => $projects->currentPage(),
        'per_page' => $projects->perPage(),
        'total' => $projects->total(),
    ],
]);

이벤트, 작업 및 큐

  • 부작용(이메일, 분석)을 위해 도메인 이벤트를 실행합니다.
  • 느린 작업(보고서, 내보내기, 웹훅)에는 큐에 넣은 작업을 사용합니다.
  • 재시도 및 백오프가 있는 멱등성 핸들러를 선호합니다.

캐싱

  • 읽기 집약적인 엔드포인트와 비용이 많이 드는 쿼리를 캐시합니다.
  • 모델 이벤트(생성/업데이트/삭제) 시 캐시를 무효화합니다.
  • 관련 데이터를 캐시할 때 태그를 사용하여 쉽게 무효화합니다.

구성 및 환경

  • 비밀은 .env에, 구성은 config/*.php에 유지합니다.
  • 환경별 구성 재정의를 사용하고 프로덕션에서 config:cache를 사용합니다.

Individual skills in this repo

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

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

관련 스킬