PHP 프레임워크: Symfony와 Laravel 중 선택

게시 됨: 2022-03-11

오늘날 새 프로젝트를 시작할 때 중요한 결정 중 하나는 올바른 프레임워크를 선택하는 것입니다. 오늘날에는 복잡한 웹 애플리케이션 없이 처음부터 복잡한 웹 애플리케이션을 구축하는 것은 상상하기 어려워졌습니다.

Ruby용 Ruby on Rails 또는 Python용 Django와 같이 웹 개발에 널리 사용되는 많은 언어에는 "기본" 프레임워크가 있습니다. 그러나 PHP에는 그러한 단일 기본값이 없으며 선택할 수 있는 여러 인기 있는 옵션이 있습니다.

Google 트렌드와 GitHub에 따르면 가장 인기 있는 PHP 프레임워크는 13.7k 별을 가진 Symfony와 29k 별을 가진 Laravel입니다(이 기사 작성 당시).

이 기사에서는 이 두 프레임워크를 비교하고 각각을 사용하여 간단하고 일상적인 기능을 구현하는 방법을 보여 드리겠습니다. 이런 식으로 실제 예제 코드를 나란히 비교할 수 있습니다.

이 기사는 강력한 PHP 기술과 MVC 아키텍처 패러다임에 대한 이해를 전제로 하지만 Symfony 또는 Laravel에 대한 이전 경험은 필요하지 않습니다.

컨텐더스

라라벨

Laravel에 대해 말할 때 Laravel 버전 4 이상을 의미합니다. Laravel 4는 2013년에 출시되었으며 프레임워크를 완전히 재작성했습니다. 프레임워크의 기능은 모든 것이 하나의 거대한 코드 저장소에 있는 대신 Composer로 관리되는 별도의 구성 요소로 분리되었습니다.

Laravel은 배우고, 읽고, 유지하기 쉬운 간단하고 아름다운 구문으로 빠른 개발을 위한 프레임워크로 스스로를 선언합니다. 2016년 가장 인기 있는 프레임워크입니다. Google 트렌드에 따르면 다른 프레임워크보다 3배 더 인기가 있고 GitHub에서는 경쟁사보다 2배 더 많은 별을 가지고 있습니다.

심포니

Symfony 2는 2011년에 출시되었지만 기본 원칙이 다른 완전히 다른 프레임워크인 Symfony 1과 혼동해서는 안 됩니다. Fabien Potencier가 Symfony 2를 만들었고 현재 버전은 Symfony 2의 증분 버전인 3.2입니다. 따라서 단순히 Symfony2/3이라고 부르는 경우가 많습니다.

Laravel 4와 마찬가지로 Symfony 2는 분리된 구성 요소 집합으로 설계되었습니다. 여기에는 두 가지 이점이 있습니다. Symfony 프로젝트의 모든 구성 요소를 교체할 수 있고, Symfony가 아닌 프로젝트의 모든 Symfony 구성 요소를 가져와 사용할 수 있습니다. Symfony 구성 요소는 훌륭한 코드 예제 역할을 할 수 있으며 Drupal, phpBB 및 Codeception과 같은 많은 오픈 소스 프로젝트에서 사용됩니다. 실제로 Laravel 자체는 14개 이상의 Symfony 구성 요소를 사용합니다. 따라서 Symfony를 이해하면 다른 프로젝트와 함께 작업할 때 많은 이점을 얻을 수 있습니다.

프레임워크 설치

두 프레임워크 모두 PHP 내장 웹 서버를 통해 사용할 수 있는 설치 프로그램과 래퍼와 함께 제공됩니다.

심포니 설치

Symfony 설치는 다음과 같이 간단합니다.

 # Downloading Symfony installer sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony # Granting permissions to execute installer sudo chmod a+x /usr/local/bin/symfony # Creating new Symfony project symfony new symfony_project # Launching built-in server cd symfony_project/ && php bin/console server:start

그게 다야! Symfony 설치는 URL http://localhost:8000 에서 사용할 수 있습니다.

라라벨 설치

Laravel 설치 프로세스는 Symfony의 경우와 거의 동일하고 간단합니다. 유일한 차이점은 Composer를 통해 Laravel의 설치 프로그램을 설치한다는 것입니다.

 # Downloading Laravel installer using Composer composer global require "laravel/installer" # Creating new Laravel project laravel new laravel_project # Launching built-in server cd laravel_project/ && php artisan serve

이제 http://localhost:8000 을 방문하여 Laravel 설치를 확인할 수 있습니다.

참고: Laravel과 Symfony는 기본적으로 동일한 로컬 호스트 포트(8000)에서 실행되므로 이러한 기본 인스턴스를 동시에 실행할 수 없습니다. Laravel 서버를 시작하기 전에 php bin/console server:stop 을 실행하여 Symfony 서버를 중지하는 것을 잊지 마십시오.

프레임워크 설치 정보

다음은 기본 설치의 예입니다. 로컬 도메인으로 프로젝트를 구성하거나 한 번에 여러 프로젝트를 실행할 수 있는 것과 같은 고급 사용 예제의 경우 두 프레임워크 모두 Vagrant 상자를 제공합니다.

  • 라라벨 농가,
  • 심포니 농가.

기본 프레임워크 구성

Symfony 기본 구성

Symfony는 구성을 지정하기 위한 구문으로 YAML을 사용합니다. 기본 구성은 app/config/config.yml 파일에 있으며 다음 예와 같습니다.

 imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: services.yml } framework: secret: '%secret%' router: { resource: '%kernel.root_dir%/config/routing.yml' } # ... # Twig Configuration twig: debug: '%kernel.debug%' strict_variables: '%kernel.debug%' # ...

환경별 구성을 생성하려면 기본 구성 매개변수가 포함된 app/config/config_ENV.yml 파일을 생성합니다. 다음은 개발 환경에 대한 config_dev.yml 파일의 예입니다.

 imports: - { resource: config.yml } # ... web_profiler: toolbar: true # ...

이 예는 개발 환경에 대해서만 web_profiler Symfony 도구를 켭니다. 이 도구를 사용하면 브라우저 창에서 바로 애플리케이션을 디버그하고 프로파일링할 수 있습니다.

구성 파일에서 %secret% 구성도 확인할 수 있습니다. 이를 통해 별도의 parameters.yml 파일에 환경별 변수를 넣을 수 있습니다. 이 파일은 모든 시스템에서 고유할 수 있으며 버전 제어 하에 저장되지 않습니다. 버전 제어를 위해 parameters.yml 파일의 템플릿인 특수 parameters.yml.dist 파일이 있습니다.

다음은 parameters.yml 파일의 예입니다.

 parameters: database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null secret: f6b16aea89dc8e4bec811dea7c22d9f0e55593af

라라벨 기본 설정

Laravel 구성은 Symfony의 구성과 매우 다릅니다. 공통점은 둘 다 버전 제어 하에 저장되지 않은 파일( Laravel의 경우 .env )과 이 파일을 생성하기 위한 템플릿( .env.example )을 사용한다는 것입니다. 이 파일에는 다음 예와 같은 키 및 값 목록이 있습니다.

 APP_ENV=local APP_KEY=base64:Qm8mIaur5AygPDoOrU+IKecMLWgmcfOjKJItb7Im3Jk= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost

Symfony YAML 파일과 마찬가지로 Laravel용 파일도 사람이 읽을 수 있고 깨끗해 보입니다. PHPUnit 테스트를 실행할 때 사용할 .env.testing 파일을 추가로 생성할 수 있습니다.

애플리케이션 구성은 config 디렉토리의 .php 파일에 저장됩니다. 기본 구성은 app.php 파일에 저장되고 구성 요소별 구성은 <component>.php 파일(예: cache.php 또는 mail.php )에 저장됩니다. 다음은 config/app.php 파일의 예입니다.

 <?php return [ 'name' => 'Laravel', 'env' => env('APP_ENV', 'production'), 'debug' => env('APP_DEBUG', false), 'url' => env('APP_URL', 'http://localhost'), 'timezone' => 'UTC', 'locale' => 'en', // ... ];

프레임워크 구성: Symfony 대 Laravel

Symfony의 애플리케이션 구성 메커니즘을 사용하면 다양한 환경에 대해 서로 다른 파일을 생성할 수 있습니다. 또한 YAML 구성에 복잡한 PHP 로직을 삽입하는 것을 방지합니다.

그러나 Laravel이 사용하는 기본 PHP 구성 구문이 더 편안할 수 있으며 YAML 구문을 배울 필요가 없습니다.

라우팅 및 컨트롤러

일반적으로 백엔드 웹 애플리케이션에는 각 요청을 읽고 요청 내용에 따라 응답을 생성하는 한 가지 주요 책임이 있습니다. 컨트롤러는 애플리케이션 메서드를 호출하여 요청을 응답으로 변환하는 역할을 하는 클래스이고 라우터는 특정 요청에 대해 실행해야 하는 컨트롤러 클래스 및 메서드를 감지하는 데 도움이 되는 메커니즘입니다.

/posts/{id} 경로에서 요청한 블로그 게시물 페이지를 표시하는 컨트롤러를 만들어 보겠습니다.

Laravel의 라우팅 및 컨트롤러

제어 장치

 <?php namespace App\Http\Controllers; use App\Post; class BlogController extends Controller { /** * Show the blog post * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { return view('post', ['post' => Post::findOrFail($id)]); } }

라우터

 Route::get('/posts/{id}', 'BlogController@show');

GET 요청에 대한 경로를 정의했습니다. /posts/{id} 와 일치하는 URI가 있는 모든 요청은 BlogController 컨트롤러의 show 메소드를 실행하고 매개변수 id 를 해당 메소드에 전달합니다. 컨트롤러에서 전달된 id 로 모델 POST 의 객체를 찾고 Laravel 도우미 view() 를 호출하여 페이지를 렌더링하려고 합니다.

Symfony의 라우팅 및 컨트롤러

Symfony에서 exampleController 는 조금 더 큽니다.

 <?php namespace BlogBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; class PostController extends Controller { /** * @Route("/posts/{id}") * @param int $id * @return \Symfony\Component\HttpFoundation\Response */ public function indexAction($id) { $repository = $this->getDoctrine()->getRepository('BlogBundle:Post'); $post = $repository->find($id); if ($post === null) { throw $this->createNotFoundException(); } return $this->render('BlogBundle:Post:show.html.twig', ['post'=>$post]); } }

주석에 @Route("/posts/{id}”) 가 이미 포함되어 있음을 알 수 있으므로, routing.yml 구성 파일에 컨트롤러를 포함하기만 하면 됩니다.

 blog: resource: "@BlogBundle/Controller/" type: annotation prefix: /

단계별 로직은 라라벨의 경우와 동일합니다.

라우팅 및 컨트롤러: Symfony 대 Laravel

이 단계에서는 Laravel이 Symfony보다 훨씬 낫다고 생각할 수 있습니다. 처음에는 사실입니다. 시작하기가 훨씬 더 쉽고 쉬워 보입니다. 그러나 실제 응용 프로그램에서는 컨트롤러에서 Doctrine을 호출해서는 안 됩니다. 대신 게시물을 찾거나 HTTP 404 예외 를 throw하는 서비스를 호출해야 합니다.

템플릿

Laravel은 Blade라는 템플릿 엔진과 함께 제공되고 Symfony는 Twig와 함께 제공됩니다. 두 템플릿 엔진 모두 두 가지 주요 기능을 구현합니다.

  1. 템플릿 상속
  2. 블록 또는 섹션

두 기능 모두 재정의 가능한 섹션이 있는 기본 템플릿과 해당 섹션의 값을 채우는 하위 템플릿을 정의할 수 있습니다.

블로그 게시물 페이지의 예를 다시 살펴보겠습니다.

Laravel 블레이드 템플릿 엔진

 // base.blade.php <html> <head> <style></style> <title>@section('page-title') Welcome to blog! @show </title> </head> <body> <div class="container"> <h1>@yield('title')</h1> <div class="row"> @yield('content') </div> </div> </body> </html> // post.blade.php @extends('base') @section('page-title')Post {{ $post->title }} - read this and more in our blog.@endsection @section('title'){{ $post->title }}@endsection @section('content') {{ $post->content }} @endsection

이제 컨트롤러에서 Laravel에게 post.blade.php 템플릿을 렌더링하도록 지시할 수 있습니다. 이전 Controller 예제에서 view('post', …) 호출을 기억하십니까? 다른 템플릿에서 상속되었음을 코드에서 알 필요가 없습니다. 이는 모두 보기 수준에서 템플릿에서만 정의됩니다.

Symfony Twig 템플릿 엔진

 // base.html.twig <html> <head> <style></style> <title>{% block page_title %} Welcome to blog! {% endblock %} </title> </head> <body> <div class="container"> <h1>{% block title %}{% endblock %}</h1> <div class="row"> {% block content %}{% endblock %} </div> </div> </body> </html> // show.html.twig {% extends '@Blog/base.html.twig' %} {% block page_title %}Post {{ post.title }} - read this and more in our blog.{% endblock %} {% block title %}{{ post.title }}{% endblock %} {% block content %} {{ post.content }} {% endblock %}

템플릿: Symfony 대 Laravel

구조적으로 Blade 및 Twig 템플릿은 매우 유사합니다. 둘 다 PHP 코드로 템플릿을 생성하고 빠르게 작동하며 둘 다 if 문 및 루프와 같은 제어 구조를 구현합니다. 두 엔진의 가장 중요한 기능은 기본적으로 출력을 이스케이프한다는 것이므로 XSS 공격을 방지하는 데 도움이 됩니다.

구문을 제외하고 주요 차이점은 Blade에서는 PHP 코드를 템플릿에 직접 삽입할 수 있지만 Twig에서는 그렇지 않다는 것입니다. 대신 Twig를 사용하면 필터를 사용할 수 있습니다.

예를 들어 문자열을 대문자로 바꾸려면 Blade에서 다음을 지정합니다.

 {{ ucfirst('welcome friend') }}

반면에 Twig에서는 다음을 수행합니다.

 {{ 'welcome friend'|capitalize }}

Blade에서는 일부 기능을 확장하는 것이 더 쉽지만 Twig는 템플릿에서 직접 PHP 코드를 허용하지 않습니다.

의존성 주입

응용 프로그램에는 다양한 상호 의존성을 가진 다양한 서비스와 구성 요소가 있습니다. 생성된 개체와 해당 종속성에 대한 모든 정보를 어떻게든 저장해야 합니다.

다음 구성 요소인 서비스 컨테이너가 있습니다. 요청된 서비스를 생성하고 생성된 객체 및 해당 종속성에 대한 정보를 저장하는 PHP 객체입니다.

다음 예를 고려해 보겠습니다. 새 블로그 게시물을 만드는 책임이 있는 메서드를 구현하기 위해 PostService 클래스를 만들고 있습니다. 이 클래스는 데이터베이스에 정보를 저장하는 역할을 하는 PostRepositorySubscriberNotifier 한 사용자에게 새 게시물에 대해 알리는 역할을 하는 SubscriberNotifier라는 두 가지 다른 서비스에 의존합니다. 작동하게 하려면 이 두 서비스를 PostService 의 생성자 인수로 전달해야 합니다. 즉, 이러한 종속성을 주입해야 합니다.

Symfony 종속성 주입 예제

먼저 샘플 서비스를 정의하겠습니다.

 <?php // src/BlogBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { public function persist(Post $post) { // Perform save to db } }
 <?php // src/BlogBundle/Service/SubscriberNotifier.php namespace BlogBundle\Service; use BlogBundle\Entity\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } }
 <?php // src/BlogBundle/Service/PostService namespace BlogBundle\Service; use BlogBundle\Entity\Post; use BlogBundle\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }

다음은 종속성 주입 구성입니다.

 # src/BlogBundle/Resources/config/services.yml services: # Our main service blog.post_service: class: BlogBundle\Service\PostService arguments: ['@blog.post_repository', '@blog.subscriber_notifier'] # SubscriberNotifier service. It could also have its own dependencies, for example, mailer class. blog.subscriber_notifier: class: BlogBundle\Service\SubscriberNotifier # Repository. Don't dive deep into it's configuration, it is not a subject now blog.post_repository: class: BlogBundle\Repository\PostRepository factory: 'doctrine.orm.default_entity_manager:getRepository' arguments: - BlogBundle\Entity\Post

이제 Service Container 개체에서 코드의 아무 곳에서나 사후 서비스를 요청할 수 있습니다. 예를 들어 컨트롤러에서 다음과 같을 수 있습니다.

 // Controller file. $post variable defined below $this->get('blog.post_service')->create($post);

Service Container는 훌륭한 구성 요소이며 SOLID 설계 원칙에 따라 애플리케이션을 빌드하는 데 도움이 됩니다.

관련 항목: Symfony 구성 요소를 사용한 진정한 종속성 주입

Laravel 의존성 주입 예제

Laravel에서 종속성을 관리하는 것이 훨씬 쉽습니다. 같은 예를 생각해 봅시다.

 <?php // app/Repository/PostRepository.php namespace App\Repository; use App\Post; class PostRepository { public function persist(Post $post) { // Perform save to db } }
 <?php // app/Service/SubscriberNotifier.php namespace App\Service; use App\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } }
 <?php // app/Service/PostService.php namespace App\Service; use App\Post; use App\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; public function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }

여기에 Laravel의 장점이 있습니다. 종속성 구성을 만들 필요가 없습니다 . Laravel은 생성자 인수 유형에서 PostService 에 대한 종속성을 자동으로 스캔하고 자동으로 해결합니다.

또한 컨트롤러 메서드에서 주입을 사용하여 메서드 인수에서 "type-hinting"하여 PostService 를 사용할 수 있습니다.

 <?php namespace App\Http\Controllers; use App\Post; use App\Service\PostService; class BlogController extends Controller { public function create(PostService $service) { $post = new Post(['title' => 'Title', 'content' => 'Content']); $service->create($post); return redirect('/posts/'.$post->id); } }

의존성 주입: Symfony 대 Laravel

Laravel의 자동 감지는 훌륭하게 작동합니다. Symfony에는 기본적으로 꺼져 있고 종속성 구성에 autowire: true 를 추가하여 켤 수 있는 "autowire"라는 유사한 기능이 있지만 일부 구성이 필요합니다. Laravel 방식은 더 간단합니다.

객체 관계형 매핑(ORM)

데이터베이스 작업을 위해 두 프레임워크 모두 ORM(Object-Relational Mapping) 기능과 함께 제공됩니다. ORM은 데이터베이스의 레코드를 코드의 개체에 매핑합니다. 이렇게 하려면 데이터베이스의 각 레코드 유형(또는 각 테이블)에 대한 모델을 생성해야 합니다.

Symfony는 타사 프로젝트 Doctrine을 사용하여 데이터베이스와 상호 작용하는 반면 Laravel은 자체 라이브러리 Eloquent를 사용합니다.

Eloquent ORM은 데이터베이스와 작업하기 위해 ActiveRecord 패턴을 구현합니다. 이 패턴에서 각 모델은 데이터베이스에 대한 연결을 인식하고 상호 작용할 수 있습니다. 예를 들어 데이터베이스에 데이터를 저장하거나 레코드를 업데이트 또는 삭제할 수 있습니다.

Doctrine은 모델이 데이터베이스에 대해 아무것도 모르는 Data Mapper 패턴을 구현합니다. 그들은 데이터 자체만을 알고 있습니다. 특별한 별도 레이어인 EntityManager 는 모델과 데이터베이스 간의 상호 작용에 대한 모든 정보를 저장하고 모든 작업을 처리합니다.

차이점을 이해하기 위해 예를 들어 보겠습니다. 모델에 기본 id 키, 제목, 콘텐츠 및 작성자가 있다고 가정해 보겠습니다. Posts 테이블은 작성자 id 만 저장하므로 Users 테이블에 대한 관계를 생성해야 합니다.

교의

모델 정의부터 시작하겠습니다.

 <?php // src/BlogBundle/Entity/User.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * User * * @ORM\Table(name="user") * @ORM\Entity */ class User { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="name", type="string", length=255) */ private $name; }
 <?php // src/BlogBundle/Entity/Post.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * Post * * @ORM\Table(name="post") * @ORM\Entity(repositoryClass="BlogBundle\Repository\PostRepository") */ class Post { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var string * * @ORM\Column(name="title", type="string", length=255) */ protected $title; /** * @var string * * @ORM\Column(name="content", type="text") */ protected $content; /** * @var User * * @ORM\ManyToOne(targetEntity="BlogBundle\Entity\User") * @ORM\JoinColumn(name="author_id", referencedColumnName="id") */ protected $author;

여기에서 모델 매핑 정보를 생성했으며 이제 도우미를 사용하여 메서드 스텁을 생성할 수 있습니다.

 php bin/console doctrine:generate:entities BlogBundle

다음으로 포스트 리포지토리 메서드를 정의합니다.

 <?php // src/BlobBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { /** * Store post to database * * @param Post $post */ public function persist(Post $post) { $this->getEntityManager()->persist($post); $this->getEntityManager()->flush(); } /** * Search posts with given author's name * * @param string $name * @return array */ public function findByAuthorName($name) { return $this->createQueryBuilder('posts') ->select('posts') ->join('posts.author', 'author') ->where('author.name = :name') ->setParameter('name', $name) ->getQuery() ->getResult(); } }

이제 서비스 또는 예를 들어 PostController 에서 이러한 메서드를 호출할 수 있습니다.

 // To search for posts $posts = $this->getDoctrine()->getRepository('BlogBundle:Post')->findByAuthorName('Karim'); // To save new post in database $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post);

웅변

User 모델은 Laravel과 함께 제공되며 기본적으로 정의되어 있으므로 Post 에 대해 하나의 모델만 정의하면 됩니다.

 <?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { public function author() { return $this->belongsTo('App\User', 'author_id'); } }

그게 모델의 전부입니다. Eloquent에서는 모델 속성을 정의할 필요가 없습니다. 데이터베이스 테이블 구조를 기반으로 동적으로 구축하기 때문입니다. 새 게시물 $post 를 데이터베이스에 저장하려면 다음을 호출해야 합니다(예: 컨트롤러에서).

 $post->save();

주어진 이름을 가진 작성자의 모든 게시물을 찾으려면 해당 이름을 가진 사용자를 찾고 모든 사용자의 게시물을 요청하는 것이 가장 좋습니다.

 $posts = Post::whereHas('author', function ($q) { $q->where('name', 'Karim'); })->get();

ORM: Symfony 대 Laravel

ORM과 관련하여 Eloquent는 PHP 개발자에게 훨씬 친숙하고 Doctrine보다 배우기 쉽습니다.

이벤트 디스패처와 미들웨어

Symfony 대 Laravel 수명 주기

프레임워크에 대해 이해해야 할 가장 중요한 것 중 하나는 프레임워크의 수명 주기입니다.

심포니 및 이벤트 디스패처

요청을 응답으로 변환하기 위해 Symfony는 EventDispatcher를 사용합니다. 결과적으로 이러한 이벤트를 처리하기 위해 다양한 수명 주기 이벤트와 특수 이벤트 리스너를 발생시킵니다. 처음에는 요청 정보가 포함된 kernel.request 이벤트를 전달합니다. 이 이벤트의 기본 기본 수신기는 라우터 구성 요소를 호출하여 현재 요청에 대한 적절한 경로 규칙을 찾는 RouterListener 입니다. 그런 다음 다른 이벤트가 단계별로 실행됩니다. 일반적인 이벤트 리스너는 보안 검사, CSRF 토큰 확인 및 로깅 프로세스입니다. 요청 수명 주기에 일부 기능을 추가하려면 사용자 지정 EventListener 를 만들고 필요한 이벤트를 구독해야 합니다.

라라벨과 미들웨어

Laravel은 다른 솔루션인 미들웨어를 사용합니다. 나는 미들웨어를 양파와 비교하는 것을 좋아합니다. 귀하의 애플리케이션에는 특정 계층이 있고 요청은 컨트롤러로 가는 도중에 이러한 계층을 통과하여 전달됩니다. 따라서 애플리케이션 로직을 확장하고 요청 수명 주기에 일부 기능을 추가하려면 미들웨어 목록에 추가 계층을 추가해야 하며 Laravel이 이를 실행합니다.

REST API

블로그 게시물을 관리하기 위한 기본 CRUD 예제를 만들어 보겠습니다.

  • 만들기 - POST /posts/
  • 읽기 - GET /posts/{id}
  • 업데이트 - PATCH /posts/{id}
  • 삭제 - DELETE /posts/{id}

Symfony의 REST API

Symfony에는 빠른 REST API 생성을 위한 즉시 사용 가능한 간편한 솔루션이 없지만 훌륭한 타사 번들 FOSRestBundleJMSSerializerBundle 있습니다.

FOSRestBundleJMSSerializerBundle 을 사용한 최소 작업 예제를 살펴보겠습니다. AppKernel 에서 설치하고 켠 후 JSON 형식을 사용하고 URL 요청에 포함될 필요가 없도록 번들 구성에서 설정할 수 있습니다.

 #app/config/config.yml fos_rest: routing_loader: default_format: json include_format: false

라우팅 구성에서 이 컨트롤러가 REST 리소스를 구현하도록 지정해야 합니다.

 #app/config/routing.yml blog: resource: BlogBundle\Controller\PostController type: rest

이전 예에서 리포지토리에서 지속 메서드를 구현했습니다. 이제 삭제 방법을 추가해야 합니다.

 // src/BlogBundle/Repository/PostRepository.php public function delete(Post $post) { $this->getEntityManager()->remove($post); $this->getEntityManager()->flush(); }

다음으로 입력 요청을 수락하고 모델에 매핑할 양식 클래스를 만들어야 합니다. CLI 도우미를 사용하여 수행할 수 있습니다.

 php bin/console doctrine:generate:form BlogBundle:Post

다음 코드와 함께 생성된 양식 유형을 받게 됩니다.

 <?php // src/BlogBundle/Form/PostType.php namespace BlogBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class PostType extends AbstractType { /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('title')->add('content'); } /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'BlogBundle\Entity\Post', 'csrf_protection' => false ]); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'post'; } }

이제 컨트롤러를 구현해 보겠습니다.

참고: 제가 보여드릴 코드는 완벽하지 않습니다. 일부 디자인 원칙을 위반하지만 쉽게 리팩토링할 수 있습니다. 주요 목적은 단계별로 각 방법을 구현하는 방법을 보여주는 것입니다.

 <?php // src/BlogBundle/Controller/PostController.php namespace BlogBundle\Controller; use BlogBundle\Entity\Post; use BlogBundle\Form\PostType; use FOS\RestBundle\Controller\FOSRestController; use FOS\RestBundle\View\View; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class PostController extends FOSRestController { /** * @param $id * @return Response */ public function getPostAction($id) { $view = new View(); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $view->setData(['post' => $post]); } return $this->handleView($view); } /** * @param Request $request * @return Response */ public function postPostAction(Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = new Post; $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_CREATED); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } return $this->handleView($view); } /** * @param $id * @param Request $request * @return Response */ public function patchPostAction($id, Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } } return $this->handleView($view); } /** * @param $id * @return Response */ public function deletePostAction($id) { $view = new View(null, Response::HTTP_NOT_FOUND); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post !== null) { $this->getDoctrine()->getRepository('BlogBundle:Post')->delete($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); } return $this->handleView($view); } }

FOSRestBundle 를 사용하면 각 메서드에 대한 경로를 선언할 필요가 없습니다. 컨트롤러 메서드 이름이 있는 규칙을 따르기만 하면 JMSSerializerBundle 이 자동으로 모델을 JSON으로 변환합니다.

라라벨의 REST API

먼저 경로를 정의해야 합니다. 라우팅 규칙의 API 섹션에서 이 작업을 수행하여 일부 기본 미들웨어 구성 요소를 끄고 다른 구성 요소를 켤 수 있습니다. API 섹션은 routes/api.php 파일에 있습니다.

 <?php // routes/api.php Route::resource('/posts', 'BlogController');

모델에서 $fillable 속성을 정의하여 모델의 생성 및 업데이트 메서드에 변수를 전달해야 합니다.

 <?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['title', 'content']; // …

이제 컨트롤러를 정의해 보겠습니다.

 <?php // app/Http/Controllers/BlogController.php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use Illuminate\Http\Response; class BlogController extends Controller { public function show(Post $post) { return $post; } public function store(Request $request) { $post = Post::create($request->get('post')); return response(null, Response::HTTP_CREATED, ['Location'=>'/posts/'.$post->id]); } public function update(Post $post, Request $request) { $post->update($request->get('post')); return response(null, Response::HTTP_NO_CONTENT, ['Location'=>'/posts/'.$post->id]); } public function destroy(Post $post) { $post->delete(); return response(null, Response::HTTP_NO_CONTENT); } }

Symfony에서는 JSON에서 오류를 래핑한 FosRestBundle 을 사용하고 있습니다. Laravel에서는 직접 해야 합니다. 예상 JSON 요청에 대해 JSON 오류를 반환하려면 예외 처리기에서 렌더링 메서드를 업데이트해야 합니다.

 <?php // app/Exceptions/Handler.php namespace App\Exceptions; use Exception; use Illuminate\Auth\AuthenticationException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; class Handler extends ExceptionHandler { /** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $exception * @return \Illuminate\Http\Response */ public function render($request, Exception $exception) { if ($request->expectsJson()) { $status = 400; if ($this->isHttpException($exception)) { $status = $exception->getStatusCode(); } elseif ($exception instanceof ModelNotFoundException) { $status = 404; } $response = ['message' => $exception->getMessage(), 'code' => $exception->getCode()]; return response()->json($response, $status); } return parent::render($request, $exception); } // ... }

REST API: Symfony 대 Laravel

보시다시피 일반적인 REST API의 경우 Laravel은 Symfony보다 훨씬 간단합니다.

승자 고르기: Symfony 또는 Laravel?

모든 것이 최종 목표에 달려 있기 때문에 Laravel과 Symfony 사이에 명확한 승자는 없습니다.

다음과 같은 경우 Laravel이 더 나은 선택입니다.

  • 이것은 배우기 쉽고 더 간단한 구문과 더 나은 학습 자료를 가지고 있기 때문에 프레임워크에 대한 첫 번째 경험입니다.
  • 빠른 애플리케이션 개발에 좋고 Laravel 개발자를 찾기 쉽기 때문에 스타트업 제품을 만들고 가설을 확인하고 있습니다.

Symfony는 다음과 같은 경우 최상의 옵션입니다.

  • 확장성이 뛰어나고 유지 관리가 쉬우며 잘 구조화된 복잡한 엔터프라이즈 애플리케이션을 구축하고 있습니다.
  • Symfony는 향후 6년 동안 예측 가능한 릴리스 계획을 가지고 있으므로 대규모 장기 프로젝트의 마이그레이션을 구축하고 있으므로 놀라움이 있을 가능성이 적습니다.