Symfony 8 ์๋ฒฝ ๊ฐ์ด๋: PHP 8.4 ๋ ์ด์ง ์ค๋ธ์ ํธ, ๋ฉํฐ์คํ ํผ, 2026๋ ๋ฉด์ ๋๋น๊น์ง
Symfony 8์ PHP 8.4๋ฅผ ํ์๋ก ์๊ตฌํ๋ฉฐ ๋ค์ดํฐ๋ธ ๋ ์ด์ง ์ค๋ธ์ ํธ, AbstractFlowType, ํธ์ถ ๊ฐ๋ฅ ์ปค๋งจ๋ ๋ฑ ๋ค์์ ์ ๊ธฐ๋ฅ์ ํ์ฌํ์ต๋๋ค. ์ฃผ์ ๊ธฐ๋ฅ์ ์ฝ๋ ์์ ์ ํจ๊ป ๋ถ์ํ๊ณ 2026๋ ๋ฉด์ ๋๋น ํฌ์ธํธ๋ฅผ ์ ๋ฆฌํฉ๋๋ค.

2025๋ 11์์ ๋ฆด๋ฆฌ์ค๋ Symfony 8์ Symfony 7.x ์ฃผ๊ธฐ ๋์ ๋์ ๋ ๋ชจ๋ ์ง์ ์ค๋จ(deprecation) ํญ๋ชฉ์ ์ ๊ฑฐํ๊ณ , PHP 8.4 ์ด์์ ํ์ ์๊ตฌ์ฌํญ์ผ๋ก ์ค์ ํ์ต๋๋ค. ์ด ์๊ตฌ์ฌํญ ๋๋ถ์ ๋ค์ดํฐ๋ธ ๋ ์ด์ง ์ค๋ธ์ ํธ, ํ๋กํผํฐ ํ , ์๋ก์ด HTML5 ํ์ ๋ฑ PHP ์ธ์ด ์์ค์ ๊ธฐ๋ฅ์ด ํ๋ ์์ํฌ ๋ด๋ถ์์ ์ง์ ํ์ฉ๋ฉ๋๋ค. ์ด ๊ธ์์๋ ์ผ์์ ์ธ ๊ฐ๋ฐ ์ํฌํ๋ก์ ์ํฅ์ ๋ฏธ์น๋ ์ฃผ์ ๊ธฐ๋ฅ, ๋ฉด์ ์ค๋น์ ๋์์ด ๋๋ ํฌ์ธํธ, ๊ทธ๋ฆฌ๊ณ ํ๋ก๋์ ์ ๊ทธ๋ ์ด๋ ์ ์ฃผ์ํด์ผ ํ ์ฌํญ์ ์ฝ๋์ ํจ๊ป ์์ธํ ๋ถ์ํฉ๋๋ค.
Symfony 8.0์ PHP 8.4 ์ด์์ ํ์๋ก ํ๋ฉฐ, ๋ค์ดํฐ๋ธ ๋ฉํฐ์คํ
ํผ(AbstractFlowType), #[Argument]์ #[Option] ์์ฑ์ ํ์ฉํ ํธ์ถ ๊ฐ๋ฅ ์ฝ์ ์ปค๋งจ๋, 3๊ฐ์ ์๋ก์ด JSON/ObjectMapper ์ปดํฌ๋ํธ๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋ํ DI ์ปจํ
์ด๋์ Doctrine ๋ชจ๋์์ ํ๋ก์ ์ฝ๋ ์์ฑ์ด PHP 8.4 ๋ค์ดํฐ๋ธ ๋ ์ด์ง ์ค๋ธ์ ํธ๋ก ๋์ฒด๋์์ต๋๋ค.
๋ค์ดํฐ๋ธ ๋ ์ด์ง ์ค๋ธ์ ํธ๊ฐ ํ๋ก์ ์ฝ๋ ์์ฑ์ ๋์ฒดํ๋ค
PHP 8.4์์๋ ReflectionClass::newLazyGhost()์ ReflectionClass::newLazyProxy()๋ฅผ ํตํด ์์ง ์์ค์ ๋ ์ด์ง ์ค๋ธ์ ํธ๊ฐ ๋์
๋์์ต๋๋ค. Symfony 8์ ์ด ๊ธฐ๋ฅ์ ์ง์ ํ์ฉํ์ฌ, DependencyInjection ์ปดํฌ๋ํธ๊ฐ ๋ ์ด์ ๋ ์ด์ง ์๋น์ค์ฉ ํ๋ก์ ํด๋์ค๋ฅผ ์์ฑํ์ง ์์ต๋๋ค. ๋์ ์ฒซ ๋ฒ์งธ ํ๋กํผํฐ ์ ๊ทผ ์ ์ด๊ธฐํ๋๋ ๊ณ ์คํธ ์ค๋ธ์ ํธ๊ฐ ์์ฑ๋ฉ๋๋ค.
์ค์ง์ ์ธ ์ํฅ์ ์๋นํฉ๋๋ค. ๊ธฐ์กด์ Symfony์ ํ๋ก์ ๊ธฐ๋ฐ ๋ ์ด์ง ๋ก๋ฉ๊ณผ ํธํ๋์ง ์์๋ final ํด๋์ค์ readonly ํด๋์ค๊ฐ ๋ณ๋์ ์ฐํ ๋ฐฉ๋ฒ ์์ด ์ ์์ ์ผ๋ก ๋์ํฉ๋๋ค. Doctrine๋ ๋์ผํ ํํ์ ๋ฐ์ ์ํฐํฐ ํ๋ก์์ ์ฝ๋ ์์ฑ์ด ๋ถํ์ํด์ก์ผ๋ฉฐ, 10๋
์ด์ ์ง์๋ ์ ์ง๋ณด์ ๋ถ๋ด์ด ํด์๋์์ต๋๋ค.
namespace App\Service;
use Symfony\Component\DependencyInjection\Attribute\Lazy;
#[Lazy]
final readonly class HeavyReportGenerator
{
public function __construct(
private DatabaseConnection $db,
private PdfEngine $pdf,
private CacheInterface $cache,
) {
// Constructor runs only when a method is actually called
}
public function generate(int $reportId): string
{
$data = $this->db->fetchReport($reportId);
return $this->pdf->render($data);
}
}#[Lazy] ์์ฑ์ ํด๋น ์๋น์ค๋ฅผ ๊ณ ์คํธ ์ค๋ธ์ ํธ๋ก ์ธ์คํด์คํํ๋๋ก ์ง์ ํฉ๋๋ค. ์ปจํ
์ด๋๋ HeavyReportGenerator์ ์ธํ์ ๋์ผํ ์
ธ์ ์ฃผ์
ํฉ๋๋ค. ์์ฑ์์ 3๊ฐ์ ์ฃผ์
๋ ์์กด์ฑ์ generate()๊ฐ ํธ์ถ๋ ๋์๋ง ์คํ๋ฉ๋๋ค. ์กฐ๊ฑด๋ถ๋ก ์ฌ์ฉ๋๋ ์๋น์ค(ํน์ ๊ด๋ฆฌ์ ์์
์์๋ง ํธ๋ฆฌ๊ฑฐ๋๋ ๋ณด๊ณ ์ ์์ฑ ๋ฑ)์ ๊ฒฝ์ฐ, ๋งค ์์ฒญ๋ง๋ค ๋ฐ์ํ๋ ๋ถํ์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ๊ณผ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ด ์ ๊ฑฐ๋ฉ๋๋ค.
#[Autowire(lazy: true)] ๋ณํ์ ์ฌ์ฉํ๋ฉด, ์๋น์ค ํด๋์ค ์์ฒด๋ฅผ ์์ ํ์ง ์๊ณ ๋ ํธ์ถ ์ธก์์ ๋ ์ด์ง ์ฃผ์
์ด ๊ฐ๋ฅํฉ๋๋ค.
namespace App\Controller;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class DashboardController
{
public function __construct(
#[Autowire(lazy: true)]
private HeavyReportGenerator $reportGenerator,
) {}
}๋ฉด์ ์์๋ ๊ณ ์คํธ ์ค๋ธ์ ํธ์ ๋ฒ์ถ์ผ ํ๋ก์์ ์ฐจ์ด์ ๋ํ ์ง๋ฌธ์ด ์์ฃผ ์ถ์ ๋ฉ๋๋ค. ๊ณ ์คํธ ์ค๋ธ์ ํธ๋ ์๋ ์ธ์คํด์ค๋ฅผ ๊ทธ ์๋ฆฌ์์ ์ด๊ธฐํํฉ๋๋ค. ๋ฒ์ถ์ผ ํ๋ก์๋ ์์ ํ ์ด๊ธฐํ๋ ๋ณ๋์ ์ธ์คํด์ค์ ์ฒ๋ฆฌ๋ฅผ ์์ํฉ๋๋ค. Symfony๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ณ ์คํธ๋ฅผ ์ฌ์ฉํ์ง๋ง, ํฉํ ๋ฆฌ๊ฐ ๊ด๋ จ๋ ๊ฒฝ์ฐ ์๋์ผ๋ก ํ๋ก์๋ก ์ ํํฉ๋๋ค.
AbstractFlowType์ ํ์ฉํ ๋ฉํฐ์คํ ํผ
Symfony 8 ์ด์ ์๋ ๋ฉํฐ์คํ
ํผ ๊ตฌํ์ ์ํด CraueFormFlowBundle ๊ฐ์ ์๋ํํฐ ๋ฒ๋ค์ด ํ์ํ์ต๋๋ค. ์ด์ ํ๋ ์์ํฌ๊ฐ AbstractFlowType์ ์์ฒด์ ์ผ๋ก ์ ๊ณตํฉ๋๋ค. ์ด๋ ์คํ
๋ณ ์ ํจ์ฑ ๊ฒ์ฌ, ์กฐ๊ฑด๋ถ ๋ถ๊ธฐ, ๋ด์ฅ ๋ค๋น๊ฒ์ด์
์ ๊ฐ์ถ ๋ค์ดํฐ๋ธ ํผ ํ๋ก์ฐ ์์ง์
๋๋ค.
namespace App\Form;
use Symfony\Component\Form\Flow\AbstractFlowType;
use Symfony\Component\Form\Flow\FormFlowBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserSignUpType extends AbstractFlowType
{
public function buildFormFlow(FormFlowBuilderInterface $builder, array $options): void
{
$builder->addStep('personal', UserPersonalType::class);
$builder->addStep('professional', UserProfessionalType::class);
$builder->addStep('account', UserAccountType::class);
$builder->add('navigator', NavigatorFlowType::class);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => UserSignUp::class,
'step_property_path' => 'currentStep',
]);
}
}๊ฐ ์คํ
์ด๋ฆ์ ์ ํจ์ฑ ๊ฒ์ฌ ๊ทธ๋ฃน์ผ๋ก๋ ๊ธฐ๋ฅํฉ๋๋ค. UserSignUp ๋ฐ์ดํฐ ํด๋์ค๋ ํ์ฑ ์คํ
๋ง ๊ฒ์ฆํ๊ธฐ ์ํด ๊ทธ๋ฃน ์ ์ฝ ์กฐ๊ฑด์ด ์ ์ฉ๋ #[Valid]๋ฅผ ์ฌ์ฉํฉ๋๋ค.
namespace App\DTO;
use Symfony\Component\Validator\Constraints as Assert;
class UserSignUp
{
public function __construct(
#[Assert\Valid(groups: ['personal'])]
public Personal $personal = new Personal(),
#[Assert\Valid(groups: ['professional'])]
public Professional $professional = new Professional(),
#[Assert\Valid(groups: ['account'])]
public Account $account = new Account(),
public string $currentStep = 'personal',
) {}
}์ปจํธ๋กค๋ฌ๋ ํ์ค Symfony ํผ ์ฒ๋ฆฌ ํจํด์ ๋ฐ๋ฆ
๋๋ค. isFinished()๋ ๋ง์ง๋ง ์คํ
์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํต๊ณผํ ํ์๋ง true๋ฅผ ๋ฐํํฉ๋๋ค.
#[Route('/signup')]
public function __invoke(Request $request): Response
{
$flow = $this->createForm(UserSignUpType::class, new UserSignUp())
->handleRequest($request);
if ($flow->isSubmitted() && $flow->isValid() && $flow->isFinished()) {
$this->userService->register($flow->getData());
return $this->redirectToRoute('app_signup_success');
}
return $this->render('signup/flow.html.twig', [
'form' => $flow->getStepForm(),
]);
}๋ค๋น๊ฒ์ด์
๋ฒํผ(NextFlowType, PreviousFlowType, FinishFlowType, ResetFlowType)์ include_if๋ฅผ ํตํ ์กฐ๊ฑด๋ถ ๋ ๋๋ง, skip์ ํตํ ์คํ
๊ฑด๋๋ฐ๊ธฐ, back_to๋ฅผ ํตํ ์ง์ ๋ค๋น๊ฒ์ด์
์ ์ง์ํฉ๋๋ค. ์ปค์คํ
JavaScript ์ํ ๊ด๋ฆฌ ์์ด๋ ์ค๋ฌด์์ ์ฌ์ฉ๋๋ ๋๋ถ๋ถ์ ์์ ๋ ํจํด์ ๊ตฌํํ ์ ์์ต๋๋ค.
Symfony ๋ฉด์ ์ค๋น๊ฐ ๋์ จ๋์?
์ธํฐ๋ํฐ๋ธ ์๋ฎฌ๋ ์ดํฐ, flashcards, ๊ธฐ์ ํ ์คํธ๋ก ์ฐ์ตํ์ธ์.
์ ๋ ฅ ์์ฑ์ ํ์ฉํ ํธ์ถ ๊ฐ๋ฅ ์ปค๋งจ๋
์ฝ์ ์ปค๋งจ๋์์ ๋ ์ด์ Command ๊ธฐ๋ณธ ํด๋์ค๋ฅผ ์์ํ๊ฑฐ๋ configure()๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ ํ์๊ฐ ์์ต๋๋ค. __invoke() ๋ฉ์๋๊ฐ PHP ์์ฑ์ ํตํด ์ธ์์ ์ต์
์ ์ง์ ์ ๋ฌ๋ฐ์ต๋๋ค.
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'app:create-user',
description: 'Create a new user account',
)]
class CreateUserCommand
{
public function __invoke(
SymfonyStyle $io,
#[Argument(description: 'The username for the new account')]
string $username,
#[Argument(description: 'The email address')]
string $email,
#[Option(shortcut: 'r', description: 'Role to assign')]
string $role = 'ROLE_USER',
#[Option(description: 'Activate immediately')]
bool $activate = false,
): int {
// User creation logic
$io->success(sprintf('User "%s" created with role %s.', $username, $role));
return Command::SUCCESS;
}
}Symfony 8์์๋ Backed Enum์ด ์ธ์์ ์ต์ ์์ ์ง์๋ฉ๋๋ค. ๋ฌธ์์ด ์ ๋ ฅ์ด ํด๋นํ๋ Enum ์ผ์ด์ค๋ก ์๋ ๋ณํ๋ฉ๋๋ค.
enum CloudRegion: string {
case UsEast = 'us-east';
case UsWest = 'us-west';
case EuWest = 'eu-west';
}
// In the command
public function __invoke(
SymfonyStyle $io,
#[Argument] CloudRegion $region,
#[Option] ?ServerSize $size = null,
): int {
$io->info(sprintf('Deploying to %s', $region->value));
return Command::SUCCESS;
}ํ๋ผ๋ฏธํฐ๊ฐ ๋ง์ ์ปค๋งจ๋์ ๊ฒฝ์ฐ, #[MapInput]์ ์ฌ์ฉํ์ฌ ์
๋ ฅ์ DTO์ ๋งคํํ ์ ์์ต๋๋ค.
class DeployInput
{
#[Argument]
public CloudRegion $region;
#[Option]
public string $branch = 'main';
#[Option(shortcut: 'f')]
public bool $force = false;
}
// src/Command/DeployCommand.php
#[AsCommand(name: 'app:deploy')]
class DeployCommand
{
public function __invoke(
SymfonyStyle $io,
#[MapInput] DeployInput $input,
): int {
// Access $input->region, $input->branch, $input->force
return Command::SUCCESS;
}
}์ด ํจํด์ผ๋ก configure() + execute()์ ๋ณด์ผ๋ฌํ๋ ์ดํธ๊ฐ ์ ๊ฑฐ๋๋ฉฐ, DTO๋ฅผ ์ง์ ์์ฑํ์ฌ ์ปค๋งจ๋๋ฅผ ํ
์คํธํ ์ ์๊ฒ ๋ฉ๋๋ค.
JsonStreamer, JsonPath, ObjectMapper ์ปดํฌ๋ํธ
Symfony 8์๋ 3๊ฐ์ ์๋ก์ด ๋ ๋ฆฝํ ์ปดํฌ๋ํธ๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
JsonStreamer๋ ์ ์ฒด ๋ฌธ์๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ํ์ง ์๊ณ ๋์ฉ๋ JSON ํ์ด๋ก๋๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. ๋ฉ๊ฐ๋ฐ์ดํธ ๊ท๋ชจ์ ์๋ต์ ๋ค๋ฃจ๋ API(๋๋ ๋ฐ์ดํฐ ๋ด๋ณด๋ด๊ธฐ, ๋ถ์ ํผ๋, ๋ก๊ทธ ์์ง ๋ฑ)์์ json_decode()์ ๋ฉ๋ชจ๋ฆฌ ํ๊ณ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค.
JsonPath๋ ์ค์ฒฉ๋ JSON ๊ตฌ์กฐ๋ฅผ ํ์ํ๊ธฐ ์ํ RFC 9535์ ์๋ธ์ ์ ๊ตฌํํฉ๋๋ค. ์ ์ฒด ์๋ต์ ๋์ฝ๋ฉํ๊ณ ๋ฐฐ์ด์ ์๋์ผ๋ก ์ํํ๋ ๋์ , ๊ฒฝ๋ก ํํ์์ผ๋ก ๋์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ์ถ์ถํ ์ ์์ต๋๋ค.
ObjectMapper๋ PHP ์์ฑ์ ์ฌ์ฉํ์ฌ DTO์ ๋ฐฐ์ด/JSON ๊ฐ์ ๋ณํ์ ์ํํ๋ฉฐ, ์์์ ์ผ๋ก ์์ฑํ๋ ํ์ด๋๋ ์ด์ ๋ก์ง์ ๋์ฒดํฉ๋๋ค. Serializer ์ปดํฌ๋ํธ์ ๊ฒฐํฉํ๋ฉด API ์์ฒญ/์๋ต ๋งคํ์ ์ ์ฒด ๋ผ์ดํ์ฌ์ดํด์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
2026๋
Symfony ๋ฉด์ ์์๋ ๋ ์ด์ง ์ค๋ธ์ ํธ(๊ณ ์คํธ vs ํ๋ก์), AbstractFlowType์ ๋ผ์ดํ์ฌ์ดํด, configure()์์ ํธ์ถ ๊ฐ๋ฅ ์ปค๋งจ๋๋ก์ ์ ํ์ด ๋น์ถ ์ฃผ์ ์
๋๋ค. ์ด๋ฌํ ํจํด์ ๋ํ ์ดํด๋ ๋ ๊ฑฐ์ ์ง์์ด ์๋ ํ์ฌ ํ๋ ์์ํฌ ๋ฒ์ ์ ๋ํ ์๋ จ๋๋ฅผ ๋ณด์ฌ์ค๋๋ค.
๋ณด์ ๋ฐ ๊ฐ๋ฐ์ ๊ฒฝํ ๊ฐ์ ์ฌํญ
CSRF ๋ณดํธ๊ฐ ๋ ์ด์ ์๋ฒ ์ธก ์ธ์ ์ ํ์๋ก ํ์ง ์์ต๋๋ค. ์๋ก์ด ์คํ ์ดํธ๋ฆฌ์ค CSRF ๊ตฌํ์ HTTP ์บ์ฑ๊ณผ ํธํ๋์ด, API ์ฐ์ ์ํคํ ์ฒ ๋ฐ CDN ์บ์ ํ์ด์ง์์ ํ์ฉ ๊ฐ๋ฅํฉ๋๋ค.
Messenger ์ปดํฌ๋ํธ๋ ๋ฉ์์ง ์๋ช ์ ์ง์ํฉ๋๋ค. ํ์ด๋ก๋์ ๋ํ ์ํธํ ์๋ช ์ผ๋ก ํ๋ก๋์์ ์ปจ์๋จธ ๊ฐ์ ์๋ณ์กฐ๋ฅผ ๋ฐฉ์งํฉ๋๋ค. ๋ฉ์์ง ๋ฌด๊ฒฐ์ฑ์ด ์ค์ํ ์์คํ (๊ฒฐ์ ์ฒ๋ฆฌ, ๊ฐ์ฌ ์ถ์ ๋ฑ)์์ ์ปค์คํ ์๋ช ๋ฏธ๋ค์จ์ด๋ฅผ ๋์ฒดํ ์ ์์ต๋๋ค.
์๋ก์ด ์ ํจ์ฑ ๊ฒ์ฌ ์ ์ฝ ์กฐ๊ฑด์ ๋ฌธ์ ์ธํธ, MAC ์ฃผ์, ISO ์ฃผ ๋ฒํธ, ๋จ์ด ์, YAML ๊ตฌ๋ฌธ, ์ฌ๋ฌ๊ทธ, Twig ํ
ํ๋ฆฟ, ๋์์ ํ์ผ ๋ฑ์ ์ง์ํฉ๋๋ค. #[Slug] ์ ์ฝ ์กฐ๊ฑด ํ๋๋ง์ผ๋ก๋ ์์ฃผ ์ฌ์ฉ๋๋ ์ปค์คํ
๋ฐธ๋ฆฌ๋ฐ์ดํฐ๋ฅผ ์ ๊ฑฐํ ์ ์์ต๋๋ค.
์ํ๋ฆฌํฐ ๋ณดํฐ(Security Voter)๊ฐ ํ๋กํ์ผ๋ฌ์์ ๊ฒฐ์ ์ด์ ๋ฅผ ์ค๋ช ํ๊ฒ ๋์์ต๋๋ค. ๊ธฐ์กด์ ์ด๋ค ๋ณดํฐ๊ฐ ์ ๊ทผ์ ๊ฑฐ๋ถํ๋์ง ์ถ์ธกํด์ผ ํ๋ ์ธ๊ฐ ๋ก์ง ๋๋ฒ๊น ์ด, Symfony ํ๋กํ์ผ๋ฌ ํด๋ฐ์์์ ์ง์ ์กฐํ๋ก ๋ฐ๋์์ต๋๋ค.
Symfony 8.0์ 7.x ์ฃผ๊ธฐ์ ๋ชจ๋ ์ง์ ์ค๋จ ํญ๋ชฉ์ ์ญ์ ํ์ฌ, 13,202์ค์ ํ์ ํธํ์ฑ ์ฝ๋๋ฅผ ์ ๊ฑฐํ์ต๋๋ค. ๊ถ์ฅ ์ ๊ทผ ๋ฐฉ์์ ๋จผ์ Symfony 7.4๋ก ์
๊ทธ๋ ์ด๋ํ๊ณ , php bin/phpunit --display-deprecations๋ฅผ ์คํํ์ฌ ๋ชจ๋ ๊ฒฝ๊ณ ๋ฅผ ์์ ํ ํ 8.0์ผ๋ก ์ ํํ๋ ๊ฒ์
๋๋ค. 7.4๋ฅผ ๊ฑด๋๋ธ ๊ฒฝ์ฐ ์ ๊ฑฐ๋ API๋ก ์ธํ ๋ฐํ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
PHP 8.4 ํ๋กํผํฐ ํ ๊ณผ Symfony์์์ ํ์ฉ
ํ๋กํผํฐ ํ
(ํด๋์ค ํ๋กํผํฐ์ ์ง์ ์ ์๋๋ get๊ณผ set)์ Symfony ์ํ๊ณ์ ์์ฐ์ค๋ฝ๊ฒ ํตํฉ๋ฉ๋๋ค. Doctrine ์ํฐํฐ๋ ๊ณ์ฐ๋ ํ๋กํผํฐ์ ํ
์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ํผ ํ์
์ ๋ฐ์ดํฐ ๋ณํ์ ํ
์ ํ์ฉํ ์ ์์ต๋๋ค. ์ ํจ์ฑ ๊ฒ์ฌ ์ ์ฝ ์กฐ๊ฑด์ ํ
์ด ์ ์ฉ๋ ํ๋กํผํฐ์์ ์์ ์์ด ๋์ํฉ๋๋ค.
class Product
{
public string $name {
set(string $value) {
$this->name = trim($value);
}
}
public float $price {
set(float $value) {
if ($value < 0) {
throw new \InvalidArgumentException('Price cannot be negative');
}
$this->price = $value;
}
}
public float $priceWithTax {
get => $this->price * 1.20;
}
}ํ๋กํผํฐ ํ ์ผ๋ก ์ธํด getter/setter ์์ ํ์์ฑ์ด ์ค์ด๋ค๊ณ , ๋ถ๋ณ ์กฐ๊ฑด ์ ์ฉ์ด ํ๋กํผํฐ ์ ์ ๋ด๋ถ๋ก ์ด๋ํฉ๋๋ค. Symfony ํผ์์๋ ํธ๋์คํฌ๋จธ ๋ณด์ผ๋ฌํ๋ ์ดํธ๊ฐ ๊ฐ์ํฉ๋๋ค. Doctrine ์ํฐํฐ์์๋ ๊ณ์ฐ ๊ฐ์ด ์์กดํ๋ ๋ฐ์ดํฐ ๊ฐ๊น์ด์ ์์นํ๊ฒ ๋ฉ๋๋ค.
๋ฉด์ ์์ ์ค๋นํด์ผ ํ ์ง๋ฌธ
Symfony 8๋ก ์ธํด ๋ฉด์ ์์ ์ถ์ ๋๋ ๋ด์ฉ์ด ๋ณํํ๊ณ ์์ต๋๋ค. ๋ค์ ์ฃผ์ ๋ค์ด Symfony ๋ฉด์ ์ค๋น์์ ๋น๋ฒํ๊ฒ ๋ฑ์ฅํฉ๋๋ค.
- ๋ ์ด์ง ์ค๋ธ์ ํธ: ๊ณ ์คํธ ์ค๋ธ์ ํธ์ ๋ฒ์ถ์ผ ํ๋ก์์ ์ฐจ์ด๋ฅผ ์ค๋ช
ํ ์ ์๋๊ฐ. Symfony๋ ์ด๋ค ๊ฒฝ์ฐ์ ์ด๋ ๊ฒ์ ์ ํํ๋๊ฐ. PHP 8.4์์
final readonly์๋น์ค์ ๋ ์ด์ง ๋ก๋ฉ์ด ๊ฐ๋ฅํด์ง ์ด์ ๋ ๋ฌด์์ธ๊ฐ. - ๋ฉํฐ์คํ
ํผ:
AbstractFlowType์ ์คํ ๋ณ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋๊ฐ.step_property_path์ ์ญํ ์ ๋ฌด์์ธ๊ฐ. ์กฐ๊ฑด๋ถ ์คํ ์ ์ด๋ป๊ฒ ๋์ํ๋๊ฐ. - ํธ์ถ ๊ฐ๋ฅ ์ปค๋งจ๋: ํธ์ถ ๊ฐ๋ฅ ์ปค๋งจ๋์์
configure()๋ฅผ ๋์ฒดํ๋ ๊ฒ์ ๋ฌด์์ธ๊ฐ.#[MapInput]์ ์ด๋ป๊ฒ ๋์ํ๋๊ฐ.#[Option]๊ธฐ๋ณธ๊ฐ์ ๊ท์น์ ๋ฌด์์ธ๊ฐ. - ์ ์ปดํฌ๋ํธ: JsonStreamer๊ฐ
json_decode()๋ณด๋ค ์ ํธ๋๋ ๊ฒฝ์ฐ๋ ์ธ์ ์ธ๊ฐ. ObjectMapper๊ฐ Serializer๋ก๋ ํด๊ฒฐํ์ง ๋ชปํ๋ ์ด๋ค ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋๊ฐ. - ์ ๊ทธ๋ ์ด๋ ์ ๋ต: 8.0 ์ด์ ์ 7.4๋ก ์ ๊ทธ๋ ์ด๋ํ๋ ๊ฒ์ด ๊ถ์ฅ๋๋ ์ด์ ๋ ๋ฌด์์ธ๊ฐ. 13,202์ค์ ์ง์ ์ค๋จ ์ฝ๋ ์ ๊ฑฐ๊ฐ ํ์ ํธํ์ฑ์ ์์ด ์๋ฏธํ๋ ๋ฐ๋ ๋ฌด์์ธ๊ฐ.
์ด๋ฌํ ์ฃผ์ ๋ค์ ์ค์ ์ฝ๋๋ก ์ฐ์ตํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. Symfony ์ฝ์ ์ปค๋งจ๋ ๋ชจ๋๊ณผ Doctrine ์ฌํ ๋ชจ๋์ด ๊ฐ์ฅ ๋น๋ฒํ๊ฒ ํ ์คํธ๋๋ ์์ญ์ ๋ค๋ฃจ๊ณ ์์ต๋๋ค.
์ฐ์ต์ ์์ํ์ธ์!
๋ฉด์ ์๋ฎฌ๋ ์ดํฐ์ ๊ธฐ์ ํ ์คํธ๋ก ์ง์์ ํ ์คํธํ์ธ์.
๊ฒฐ๋ก
- Symfony 8์ PHP 8.4๋ฅผ ํ์๋ก ํ๋ฉฐ ๋ค์ดํฐ๋ธ ๋ ์ด์ง ์ค๋ธ์ ํธ๋ฅผ ์ ๋ฉด ๋์ ํ์ฌ, DI ์ปจํ ์ด๋์ Doctrine ORM ๋ชจ๋์์ ํ๋ก์ ์ฝ๋ ์์ฑ์ ์ ๊ฑฐ
AbstractFlowType์ด ๋ฉํฐ์คํ ํผ์ฉ ์๋ํํฐ ๋ฒ๋ค์ ๋์ฒดํ๋ฉฐ, ์คํ ๋ณ ์ ํจ์ฑ ๊ฒ์ฌ ๊ทธ๋ฃน๊ณผ ๋ด์ฅ ๋ค๋น๊ฒ์ด์ ๋ฒํผ์ ์ ๊ณต#[Argument],#[Option],#[MapInput]์ ํ์ฉํ ํธ์ถ ๊ฐ๋ฅ ์ปค๋งจ๋๊ฐ ๋ณด์ผ๋ฌํ๋ ์ดํธ๋ฅผ ์ ๊ฑฐํ๊ณ Backed Enum๊ณผ DTO๋ฅผ ์ง์- 3๊ฐ์ ์ ์ปดํฌ๋ํธ(JsonStreamer, JsonPath, ObjectMapper)๊ฐ ์ธ๋ถ ์์กด์ฑ ์์ด JSON ์ฒ๋ฆฌ์ ์ค๋ธ์ ํธ ๋งคํ์ ์ํ
- ์คํ ์ดํธ๋ฆฌ์ค CSRF, ๋ฉ์์ง ์๋ช , ์ํ๋ฆฌํฐ ๋ณดํฐ ๋๋ฒ๊น , 20๊ฐ ์ด์์ ์๋ก์ด ์ ํจ์ฑ ๊ฒ์ฌ ์ ์ฝ ์กฐ๊ฑด์ด ๋ณด์๊ณผ ๊ฐ๋ฐ์ ๊ฒฝํ์ ๊ฐํ
- PHP 8.4์ ํ๋กํผํฐ ํ ์ด Doctrine ์ํฐํฐ, Symfony ํผ, ์ ํจ์ฑ ๊ฒ์ฌ ์ ์ฝ ์กฐ๊ฑด๊ณผ ํฌ๋ช ํ๊ฒ ํตํฉ
- ์ ๊ทธ๋ ์ด๋ ๊ฒฝ๋ก๋ Symfony 7.4๋ฅผ ๊ฑฐ์ณ์ผ ํจ: 8.0์ผ๋ก ์ ํํ๊ธฐ ์ ์ ๋ชจ๋ ์ง์ ์ค๋จ ๊ฒฝ๊ณ ๋ฅผ ์์ ํ ๊ฒ
์ฐ์ต์ ์์ํ์ธ์!
๋ฉด์ ์๋ฎฌ๋ ์ดํฐ์ ๊ธฐ์ ํ ์คํธ๋ก ์ง์์ ํ ์คํธํ์ธ์.
ํ๊ทธ
๊ณต์
๊ด๋ จ ๊ธฐ์ฌ

Symfony 7: API Platform๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค
Symfony 7๊ณผ API Platform 4๋ก ์ ๋ฌธ์ ์ธ REST API๋ฅผ ๊ตฌ์ถํ๋ ์๋ฒฝ ๊ฐ์ด๋์ ๋๋ค. State Provider, Processor, ์ ํจ์ฑ ๊ฒ์ฌ, ์ง๋ ฌํ๋ฅผ ์ค์ ์์ ์ ํจ๊ป ์ค๋ช ํฉ๋๋ค.

Laravel Middleware ์๋ฒฝ ๊ฐ์ด๋: ์ธ์ฆ, ์๋ ์ ํ, ์ปค์คํ ๋ฏธ๋ค์จ์ด ๊ตฌ์ถ
Laravel ๋ฏธ๋ค์จ์ด์ ํ์ดํ๋ผ์ธ ๊ตฌ์กฐ๋ถํฐ ์ธ์ฆ, ์๋ ์ ํ(Rate Limiting), ์ปค์คํ ๋ฏธ๋ค์จ์ด ์์ฑ, ํ๋ก๋์ ํจํด๊น์ง ์ค๋ฌด ์ฝ๋ ์์ ์ ํจ๊ป ์ฒด๊ณ์ ์ผ๋ก ์ ๋ฆฌํฉ๋๋ค.

Laravel๊ณผ PHP ๋ฉด์ ์ง๋ฌธ: 2026๋ ํต์ฌ 25์
Laravel๊ณผ PHP ๋ฉด์ ์์ ๊ฐ์ฅ ์์ฃผ ์ถ์ ๋๋ 25๊ฐ์ง ์ง๋ฌธ์ ์์ธํ ๋ค๋ฃน๋๋ค. Eloquent ORM, ๋ฏธ๋ค์จ์ด, ํ, ํ ์คํธ, ์ํคํ ์ฒ ํจํด์ ๋ํ ์์ธํ ๋ต๋ณ๊ณผ ์ฝ๋ ์์ ๋ฅผ ์ ๊ณตํฉ๋๋ค.