# Symfony Live Components와 UX 3.0: JavaScript 없이 리액티브 앱 구축하기 (2026년) > Symfony Live Components와 UX 3.0을 활용하여 JavaScript 프레임워크 없이 리액티브 인터페이스를 구축하는 방법을 다룹니다. LiveProp, LiveAction, 폼 처리, 지연 로딩, URL 바인딩을 포함한 완전한 튜토리얼입니다. - Published: 2026-05-10 - Updated: 2026-05-10 - Author: SharpSkill - Tags: symfony, live components, symfony ux, php, twig, reactive ui - Reading time: 10 min --- Symfony Live Components는 PHP와 Twig만으로 리액티브하고 실시간적인 사용자 인터페이스를 구현하는 기능입니다. 2026년 4월에 릴리스된 [Symfony UX 3.0](https://symfony.com/blog/symfony-ux-3-0-0-released)에서는 2.x의 모든 지원 중단 기능이 제거되었으며, 최소 요구 사항이 PHP 8.4 및 Symfony 7.4로 상향되었습니다. 또한 4개의 폐기된 패키지가 제외되어 역대 가장 경량화된 UX 툴킷이 되었습니다. > **Live Components란?** > > Live Components는 모든 Twig 컴포넌트를 상태를 유지하는 인터랙티브 요소로 변환합니다. 사용자 상호작용에 따라 서버 측에서 재렌더링이 이루어지며, JavaScript를 작성할 필요가 전혀 없습니다. 데이터 바인딩, 액션, 유효성 검사, 폼 처리가 모두 PHP 내에서 처리됩니다. ## Symfony UX 3.0과 Live Components 설정 설치에는 Symfony 7.4 이상, PHP 8.4 이상, 그리고 프론트엔드 에셋을 위한 AssetMapper 또는 Webpack Encore가 필요합니다. ```bash # terminal composer require symfony/ux-live-component # With AssetMapper (recommended for Symfony 7.4+) php bin/console importmap:require @symfony/ux-live-component # Verify installation php bin/console debug:twig --filter=component ``` AssetMapper는 Node.js, Webpack 또는 기타 빌드 단계 없이 JavaScript를 제공합니다. Live Components를 구동하는 Stimulus 컨트롤러는 자동으로 로드됩니다. ## LiveProp을 활용한 리액티브 검색 컴포넌트 만들기 사용자가 입력할 때마다 결과를 필터링하는 검색 바를 JavaScript 없이 구현할 수 있습니다. `#[LiveProp]` 속성은 프로퍼티를 상태 유지(stateful)로 표시하며, 암호화된 URL 안전 토큰을 통해 재렌더링 간에 값을 유지합니다. ```php query) < 2) { return []; } return $this->productRepository->search( query: $this->query, category: $this->category === 'all' ? null : $this->category, limit: 20, ); } } ``` 해당 Twig 템플릿에서는 `data-model`을 사용하여 입력 필드를 프로퍼티에 바인딩합니다. ```twig {# templates/components/ProductSearch.html.twig #}
{% for product in this.products %}

{{ product.name }}

{{ product.price|format_currency('EUR') }}
{% else %} {% if query|length >= 2 %}

No results for "{{ query }}".

{% endif %} {% endfor %}
``` `debounce(300)` 수정자는 마지막 키 입력 후 300밀리초를 대기한 뒤 요청을 전송합니다. 리스트 항목의 `key` 속성은 효율적인 DOM 모핑을 가능하게 하여 변경된 요소만 업데이트됩니다. > **LiveProp의 URL 바인딩** > > LiveProp에서 `url: true`를 설정하면 프로퍼티 값이 쿼리 파라미터로 브라우저 URL에 동기화됩니다. 단 하나의 속성으로 북마크 가능하고 공유 가능한 검색 결과를 구현할 수 있습니다. ## LiveAction으로 사용자 액션 처리하기 데이터 바인딩 외에도, `#[LiveAction]`은 PHP 메서드를 프론트엔드에서 호출 가능한 액션으로 노출합니다. 인자는 `#[LiveArg]`를 통해 전달됩니다. ```php cartService->add($productId, $quantity); $this->items = $this->cartService->getItems(); // Notify other components on the page $this->emit('cart:updated', [ 'count' => count($this->items), ]); } #[LiveAction] public function removeItem(#[LiveArg] int $itemId): void { $this->cartService->remove($itemId); $this->items = $this->cartService->getItems(); $this->emit('cart:updated', ['count' => count($this->items)]); } public function getTotal(): float { return array_sum(array_map( fn(CartItem $item) => $item->getPrice() * $item->getQuantity(), $this->items, )); } } ``` 템플릿에서는 `live_action()` 헬퍼 또는 `data-action` 속성을 사용하여 액션을 트리거합니다. ```twig {# templates/components/ShoppingCart.html.twig #}

Cart ({{ items|length }})

{% for item in items %}
{{ item.name }} x{{ item.quantity }} {{ (item.price * item.quantity)|format_currency('EUR') }}
{% endfor %}
Total: {{ this.total|format_currency('EUR') }}
``` `emit()` 메서드는 페이지 내 다른 Live Components에 이벤트를 브로드캐스트합니다. 예를 들어, 장바구니 배지 컴포넌트는 `#[LiveListener('cart:updated')]`로 리슨하여 전체 페이지를 새로고침하지 않고도 카운트를 업데이트할 수 있습니다. ## JavaScript 없는 실시간 폼 유효성 검사 Live Components는 [Symfony Forms](https://symfony.com/doc/current/forms.html)와 직접 통합됩니다. `ComponentWithFormTrait`는 폼 상태, 유효성 검사 오류, 제출을 컴포넌트 라이프사이클에 연결합니다. ```php createForm(RegistrationType::class, $this->initialFormData ?? new User()); } #[LiveAction] public function save(EntityManagerInterface $em): mixed { // Submits + validates — re-renders with errors if invalid $this->submitForm(); $user = $this->getForm()->getData(); $em->persist($user); $em->flush(); $this->addFlash('success', 'Account created.'); return $this->redirectToRoute('app_login'); } } ``` ```twig {# templates/components/RegistrationForm.html.twig #}
{{ form_start(form, { attr: { 'data-action': 'live#action:prevent', 'data-live-action-param': 'save' } }) }} {{ form_row(form.email) }} {{ form_row(form.plainPassword, { label: 'Password' }) }} {{ form_row(form.fullName) }} {{ form_end(form) }}
``` 유효성 검사 오류는 사용자가 필드 간에 이동할 때 인라인으로 표시됩니다. `data-model`의 `on(change)` 수정자가 blur 시 재렌더링을 트리거하여 Symfony Validator의 제약 조건 메시지가 즉시 표시됩니다. ## 성능을 위한 지연 로딩과 레이지 로딩 대시보드, 분석 차트, 긴 목록 등 무거운 컴포넌트는 지연 렌더링의 이점을 누릴 수 있습니다. 초기 페이지 로드를 차단하는 대신 컴포넌트가 플레이스홀더를 렌더링하고, 페이지가 준비된 후 AJAX로 콘텐츠를 가져옵니다. ```php analytics->getMetrics($this->period); } } ``` ```twig {# Parent page: load the dashboard after the page renders #} {# Or load when scrolled into view #} ``` 컴포넌트 템플릿 내의 플레이스홀더 매크로는 실제 콘텐츠가 로드되는 동안 사용자에게 표시할 내용을 정의합니다. ```twig {# templates/components/AnalyticsDashboard.html.twig #}

Analytics — {{ period }}

{% for metric in this.metrics %}
{{ metric.label }} {{ metric.value|number_format }}
{% endfor %}
{% macro placeholder(props) %}
{% endmacro %} ``` `loading="defer"` 모드는 페이지 로드 직후 AJAX 요청을 발행합니다. `loading="lazy"` 모드는 IntersectionObserver를 사용하여 컴포넌트가 뷰포트에 스크롤될 때만 요청이 발행됩니다. 두 방식 모두 초기 페이지 응답을 빠르게 유지합니다. > **UX 3.0에서 CSRF 제거** > > Symfony UX 3.0은 Live Components의 CSRF 토큰을 동일 출처/CORS 보호로 대체했습니다. `#[AsLiveComponent]`의 `csrf` 인자는 더 이상 존재하지 않습니다. 서버가 동일 출처 정책을 적용하고 있는지 확인해야 합니다. ## 이벤트를 통한 컴포넌트 간 통신 Live Components는 이벤트 시스템을 통해 통신합니다. 자식 컴포넌트가 이벤트를 발행하고, 부모(또는 형제) 컴포넌트가 리슨하여 반응합니다. ```php count = $cartService->getItemCount(); } // Automatically re-renders when ShoppingCart emits 'cart:updated' #[LiveListener('cart:updated')] public function onCartUpdated(#[LiveArg] int $count): void { $this->count = $count; } } ``` 이 패턴을 통해 컴포넌트 간 결합도가 낮아집니다. ShoppingCart 컴포넌트는 CartBadge를 직접 참조하지 않으며, 이벤트 버스가 연결을 처리합니다. ## Symfony UX 3.0의 주요 변경 사항 UX 3.0은 정리(cleanup) 릴리스입니다. 2.x에서 지원 중단 경고 없이 실행되던 애플리케이션은 최소한의 수정으로 업그레이드할 수 있습니다. | 변경 사항 | 이전 (2.x) | 이후 (3.0) | |--------|-------------|-------------| | CSRF 보호 | `#[AsLiveComponent]`의 `csrf: true` | 동일 출처/CORS (자동) | | Twig CVA 함수 | `cva()` | twig/html-extra 3.12+의 `html_cva()` | | 컴포넌트 기본 설정 | 선택 사항 | `twig_component.defaults` 필수 | | 제거된 패키지 | Swup, LazyImage, Typed, TogglePassword | 네이티브 API 또는 UX Toolkit | | PHP 요구 사항 | 8.1 이상 | 8.4 이상 | | Symfony 요구 사항 | 6.4 이상 | 7.4 이상 | 2.x 사이클 동안 도입된 [UX Toolkit](https://symfony.com/blog/symfonyux-2-32-0-released)은 Shadcn UI 또는 Flowbite 4.0으로 스타일링된 프리빌트 UI 컴포넌트(Button, Dialog, Card, Table, Pagination)를 제공하여 제거된 패키지의 빈자리를 채웁니다. [Symfony 면접 준비](/technologies/symfony/interview-questions/events-subscribers)를 하고 있다면, Live Components와 이벤트 시스템에 대한 이해는 시니어급 포지션에서 갈수록 중요해지고 있습니다. [Symfony 면접 질문 가이드](/blog/symfony/symfony-interview-questions)에서는 이 실무 지식과 잘 어울리는 기초 주제를 다루고 있습니다. ## 결론 - Live Components는 CRUD 중심의 Symfony 애플리케이션 대부분에서 JavaScript 프레임워크의 필요성을 제거합니다. 데이터 바인딩, 액션, 유효성 검사, 폼 처리가 모두 PHP와 Twig에서 이루어집니다 - `#[LiveProp(writable: true, url: true)]`는 단 하나의 속성으로 북마크 가능하고 공유 가능한 상태 유지 인터페이스를 생성합니다 - 지연 로딩과 레이지 로딩(`loading="defer"` / `loading="lazy"`)은 무거운 컴포넌트가 비동기적으로 렌더링되는 동안 초기 페이지 로드를 빠르게 유지합니다 - UX 3.0은 CSRF 토큰을 폐지하고 동일 출처/CORS 보호를 도입했습니다. 움직이는 부품이 적은 더 단순한 보안 구조입니다 - 이벤트 시스템(`emit` / `#[LiveListener]`)은 전역 상태 관리 없이 컴포넌트 간 느슨한 결합 통신을 가능하게 합니다 - API 구축에는 백엔드 중심 아키텍처를 위해 [Symfony 7의 API Platform](/blog/symfony/symfony-7-api-platform-best-practices)을 보완적으로 활용할 수 있습니다 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ko/blog/symfony/symfony-live-components-ux-3-reactive-apps-without-javascript