| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- docker
- Agent Loop
- 객체지향
- 디자인패턴
- 어댑터패턴
- MCP
- SQL문법
- mcp client
- ai 에이전트
- docker network
- SQL기초
- mcp server
- Laravel
- SQL
- Tool Calling
- MySQL
- 데코레이터패턴
- 라라벨
- PHP객체지향
- 데이터베이스
- PHP
- dhgrp
- LLM
- ai agent
- 인덱스
- 객체복제
- linux 권한
- OOP
- 소프트웨어설계
- 매직메서드
- Today
- Total
개발블로그
Laravel 화면 데이터 구성 - Presenter, ViewModel, View Composer 본문
화면 데이터 구성 개념
라라벨에서는 Controller에서 필요한 데이터를 준비한 뒤 View에 전달할 수 있다.
Controller
↓
데이터 준비 / 가공
↓
Blade View
화면이 단순한 경우에는 Controller에서 Model을 조회하고 그대로 View에 전달하는 방식만으로도 충분
단, 화면이 복잡해지는 경우엔 Controller에 여러 책임들이 섞임
Blade에서 처리해도 여러 화면에서 반복되면 View가 화면 출력뿐 아니라 데이터 가공까지 많이 담당하게 됨
ex) 주문 상세 화면에서 다음과 같은 정보 필요
=> 주문 정보, 주문 상품, 결제 정보, 배송 정보, 쿠폰 정보, 취소 가능 여부, 화면에 표시할 상태값
핵심 => Controller나 Blade에 화면 관련 책임이 과도하게 몰리는 것을 줄이는 것.

- Presenter => 하나의 객체를 어떻게 표시할지
ex) price = 35000 => 35,000원, status=paid => 결제완료
- ViewModel => 하나의 화면에 어떤 데이터를 구성할지
ex) 주문 상세 화면 => Order + Items + Payment + Shipping + canCancel
분리가 필요한 시점
- 표현 로직이 반복되기 시작함
- 화면에 전달할 데이터가 많아짐
- 여러 Model의 데이터를 조합해야 함
- Controller가 화면 준비 코드로 길어짐
- Blade에 데이터 가공 로직이 많아짐
Presenter
개념
데이터를 화면에 표시하기 좋은 형태로 가공하는 역할을 담당하는 구조
=> 화면 표현 관점에서 해석해서 제공하는 역할
의의
각 Blade에서 같은 변환을 반복하면 표현 규칙이 여러 곳에 흩어짐
Presenter을 쓰면 홤녀에서 사용하는 표현 규칙을 한곳에서 관리할 수 있음
Presenter에 넣기 좋은 로직
기준 : 화면 표현에 필요한가? 이 객체를 화면에서 어떻게 보여줄 것인가?
| 종류 | 예 |
| 금액 표현 | 35000 → 35,000원 |
| 날짜 표현 | 2026-08-12 → 2026.08.12 |
| 상태 표현 | paid → 결제 완료 |
| 문자열 조합 | 이름, 주소 등의 표시값 |
| 화면용 Label | 상태명, 등급명 |
| 화면용 표시 여부 | 특정 Badge 표시 등 |
EX]
/**
금액표시
*/
public function formattedPrice(): string
{
return number_format($this->order->price) . '원';
}
// 35000 -> 35,000원
/**
상태 Label
*/
public function statusLabel(): string
{
return match ($this->order->status) {
'paid' => '결제 완료',
'cancelled' => '결제 취소',
default => '처리 중',
};
}
// paid -> 결제 완료
/**
날짜 표시
*/
public function formattedCreatedAt(): string
{
return $this->order->created_at->format('Y.m.d');
}
// 2026-08-12 13:30:00 -> 2026.08.12
기본 구조
프로젝트에 직접 구성 (관례적임)
app/
└── Presenters/
└── OrderPresenter.php
기본 예제
namespace App\Presenters;
use App\Models\Order;
class OrderPresenter
{
public function __construct(
private Order $order
) {
}
public function formattedPrice(): string
{
return number_format($this->order->price) . '원';
}
public function statusLabel(): string
{
return match ($this->order->status) {
'paid' => '결제 완료',
'cancelled' => '결제 취소',
default => '처리 중',
};
}
}
Controller에서 사용
use App\Presenters\OrderPresenter;
public function show(Order $order)
{
return view('orders.show', [
'order' => $order,
'presenter' => new OrderPresenter($order),
]);
}
Blade
<h1>주문 {{ $order->id }}</h1>
<p>{{ $presenter->formattedPrice() }}</p>
<p>{{ $presenter->statusLabel() }}</p>
ViewModel
개념
특정 화면에 필요한 데이터를 하나의 객체로 구성하는 역할
하나의 화면 전체에 어떤 데이터가 필요한지에 집중
의의
화면이 단순하다면 Controller에서 바로 데이터를 전달하면 된다.
하지만 화면에 필요한 데이터가 많아지면 Controller로 점점 커질 수 있음.
(Controller가 요청 처리뿐 아니라 특정 화면에 필요한 데이터 구성까지 담당해야 함)
ViewModel을 사용하면 화면 데이터 구성 부분을 분리할 수 있음.
ex) 주문 상세 페이지
주문 상세 화면
├── 주문 정보
├── 주문 상품
├── 결제 정보
├── 배송 정보
├── 쿠폰 정보
└── 취소 가능 여부
이 화면은 Order 하나만으로 구성되지 않을 수 있음.
아래와 같이 구성
Order+OrderItems+Payment+Shipping+Coupon+화면 상태값
↓
OrderDetailViewModel
↓
주문 상세 화면
특징
- 같은 객체를 사용하는 화면이라도 필요한 데이터는 서로 다를 수 있음.
=> 실제 화면의 요구사항을 기준으로 구성 - 여러 Model 데이터를 구성할 수도 있음
ex) 마이페이지 - User, 최근 주문, 찜한 상품, 알림 개수, 회원 등급
=> User, Orders, Favorites, Notification Count, Grade → MyPageViewModel → mypage/index.balde.php - 화면 상태값 구성 가능
=> 단순히 여러 Model을 모으는 것만이 아니라 화면에 필요한 상태값을 제공하는 데도 사용 가능
역할 범위
지향
주로 특정 화면을 구성하는 데 필요한 데이터와 상태
| 종류 | 예 |
| 여러 데이터 조합 | Order + Payment + Shipping |
| Relation 데이터 | 주문 상품 목록 |
| 화면 상태 | 버튼 표시 여부 |
| 화면용 Collection | 화면에 보여줄 목록 |
| 여러 결과 묶음 | 게시글 + 댓글 + 작성자 정보 |
| 화면 단위 값 | 총 개수, 활성 Tab 등 |
지양
Service처럼 사용하면 다시 책임이 섞임
ex) 결제 승인, 주문 생성, 재고 차감, DB저장, Transaction, 외부 API 호출, 메일 발송, 주문 상태 변경
실제 상태를 변경하기 보다는,
이미 존재하는 데이터 → 화면에서 사용하기 좋은 구조로 구성 → View
기본구조
Laravel에서 정해진 ViewModels 폴더는 없기 때문에 프로젝트에서 필요에 따라 직접 구성
app/
└── ViewModels/
└── OrderDetailViewModel.php
기본 예제
namespace App\ViewModels;
use App\Models\Order;
class OrderDetailViewModel
{
public function __construct(
public readonly Order $order
) {
}
public function items()
{
return $this->order->items;
}
public function payment()
{
return $this->order->payment;
}
public function shipping()
{
return $this->order->shipping;
}
public function canCancel(): bool
{
return $this->order->status === 'paid';
}
}
Controller에서는 ViewModel하나만 View에 전달
use App\ViewModels\OrderDetailViewModel;
public function show(Order $order)
{
return view('orders.show', [
'viewModel' => new OrderDetailViewModel($order),
]);
}
Blade
<h1>주문 {{ $viewModel->order->id }}</h1>
@foreach ($viewModel->items() as $item)
<p>{{ $item->name }}</p>
@endforeach
@if ($viewModel->canCancel())
<button>주문 취소</button>
@endif
View Composer
개념
특정 View가 렌더링될 때 필요한 데이터를 자동으로 연결하는 Laravel 공식 기능
Laravel에서는 View Composer를 Callback 또는 Class 형태로 등록할 수 있으며,
연결된 View가 렌더링될 때 Composer가 실행됨.
결과적으로,
Controller가 직접 전달하지 않아도 특정 View가 렌더링될 때 필요한 데이터를 연결할 수 있음.
ex) 여러 페이지에서 공통으로 사용하는 Sidebar
sidebar.blade.php가 사용될 때마다 항상 categories 데이터가 필요하다면,
각 Controller에서 반복해서 전달할 수 있음.
하지만 같은 View가 여러 Controller에서 사용되고 항상 동일한 종류의 데이터가 필요하다면
View Composer로 이 로직을 한곳에 모을 수 있음
언제사용?
특히 동일한 View가 여러 곳에서 사용되면서 항상 같은 종류의 데이터를 필요로 할 때 유용
ViewModel과 차이
| 구분 | ViewModel | View Composer |
| 기준 | 화면 | 특정 View |
| 목적 | 화면 전체 데이터 구성 | 반복되는 View 데이터 공급 |
| 사용 방식 | Controller 등에서 직접 전달 | View 렌더링 시 실행 |
| 예 | 주문 상세 데이터 | Sidebar Category |
| 구분 | 🟠 관례 | 🔵 Laravel 공식 |
| 핵심 질문 | 이 화면에는 무엇이 필요한가? | 이 View에는 항상 무엇이 필요한가? |
기본흐름

기본 구조
Class 기반 View Composer를 위한 기본 디렉터리가 정해져 있지 않음.
따라서 프로젝트에서 직접 구조를 정할 수 있으며, Laravel 공식 문서에서는 다음과 같은 구조를 예를 든다.
app/
└── View/
└── Composers/
└── SidebarComposer.php
역할 범위
지향
여러 화면에서 공통으로 사용되는 View가 있고,
그 View가 항상 같은 종류의 데이터를 필요로 하는 경우에 적합.
| 종류 | 예 |
| 공통 메뉴 데이터 | Navigation 메뉴 |
| 공통 영역 데이터 | Header, Sidebar 정보 |
| 사용자 표시 데이터 | 알림 개수, 사용자 메뉴 |
| 공통 목록 데이터 | 카테고리, 인기 항목 |
| 특정 Partial 데이터 | 해당 Partial에서 항상 사용하는 값 |
지양
화면 전체의 데이터를 대신 준비하거나, 실제 애플리케이션의 상태를 변경하는 역할까지 담당하기 시작하면 책임이 커질 수 있음
문법
Composer 정의
↓
View와 Composer 연결
↓
데이터 전달
↓
Blade에서 사용
Composer 정의 및 데이터 전달
use Illuminate\View\View;
class ComposerClass
{
// compose() 안에서 View에 필요한 데이터를 전달
public function compose(View $view): void
{
// 한개만 전달 하는 경우
$view->with('변수명', $데이터);
// 여러 데이터 전달 => 배열로 묶을 수 있음
$view->with([
'변수명1' => $데이터1,
'변수명2' => $데이터2,
]);
}
}
View와 Composer 연결
use App\View\Composers\ExampleComposer;
use Illuminate\Support\Facades\View;
public function boot(): void
{
View::composer(
'연결할 View', // 적용할 View
Composer클래스::class // 실행할 Composer
);
// 여러 View에 같은 Composer 연결 가능
View::composer(['View 이름1', 'View 이름2'],ComposerClass::class);
// 간단한 경우, Composer Class없이 바로 작성 가능
View::composer('View 이름', function (View $view){
$view->with('변수명',$데이터);
});
}
Blade에서 사용
Composer가 전달한 데이터는 일반적으로 Controller에서 전달한 View 변수와 동일하게 사용할 수 있음.
View와 Composer 연결
<!-- Collection이나 배열 -->
@foreach ($변수명 as $항목)
{{ $항목 }}
@endforeach
'STUDY > Laravel' 카테고리의 다른 글
| [Laravel 데이터 및 상태 관리] Redis 활용 (0) | 2026.08.14 |
|---|---|
| [Laravel 데이터 및 상태 관리] 성능 및 데이터 재사용 - Cache (0) | 2026.08.13 |
| [Laravel 데이터 및 상태 관리] 사용자 상태 관리 - Cookie와 Session (0) | 2026.08.13 |
| Laravel 프로젝트 구조와 주요 클래스·MVC패턴 개요 (0) | 2026.08.11 |