์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ—๏ธ Symfony ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜์™€์˜์กด์„ฑ ์ฃผ์ž… ์ปจํ…Œ์ด๋„ˆ ํ™œ์šฉ

๐Ÿ—๏ธ Symfony ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜์™€
์˜์กด์„ฑ ์ฃผ์ž… ์ปจํ…Œ์ด๋„ˆ ํ™œ์šฉ

PHP ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ฐ˜๋“œ์‹œ ์•Œ์•„์•ผ ํ•  Symfony์˜ ํ•ต์‹ฌ ์ฒ ํ•™ ์™„์ „ ์ •๋ณต ๐Ÿš€
๐Ÿ’ฌ ์•ผ ๊ทผ๋ฐ Symfony ์“ฐ๋ฉด ๋ญ๊ฐ€ ์ข‹์•„? ๊ทธ๋ƒฅ Laravel ์“ฐ๋ฉด ์•ˆ ๋ผ?
๐Ÿค– ใ…‹ใ…‹ใ…‹ ๋‘˜ ๋‹ค ์ข‹์€๋ฐ, Symfony๋Š” ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์„œ ์“ธ ์ˆ˜ ์žˆ์–ด์„œ ์ง„์งœ ์œ ์—ฐํ•จ ์ฉ”์–ด. ๊ทธ๋ฆฌ๊ณ  Laravel๋„ ์‚ฌ์‹ค Symfony ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜์ด์•ผ ใ„ทใ„ท

PHP ์ƒํƒœ๊ณ„์—์„œ Symfony๋Š” ๋‹จ์ˆœํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
๋งˆ์น˜ ๋ ˆ๊ณ  ๋ธ”๋ก์ฒ˜๋Ÿผ ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋งŒ ๊ณจ๋ผ์„œ ์กฐ๋ฆฝํ•  ์ˆ˜ ์žˆ๋Š” ์•„ํ‚คํ…์ฒ˜ ์ฒ ํ•™์„ ๊ฐ€์ง„ ํ”Œ๋žซํผ์ด์—์š”.
Laravel, Drupal, Magento ๊ฐ™์€ ์œ ๋ช…ํ•œ PHP ํ”„๋ ˆ์ž„์›Œํฌ๋“ค๋„ Symfony ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค, ์•Œ๊ณ  ๊ณ„์…จ๋‚˜์š”? ๐Ÿ˜ฒ

์˜ค๋Š˜์€ Symfony์˜ ํ•ต์‹ฌ ์ค‘์˜ ํ•ต์‹ฌ์ธ ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜์™€ ์˜์กด์„ฑ ์ฃผ์ž…(DI) ์ปจํ…Œ์ด๋„ˆ์— ๋Œ€ํ•ด ์ง„์งœ ๊นŠ๊ฒŒ ํŒŒํ—ค์ณ๋ณผ๊ฒŒ์š”.
์ฝ”๋“œ๋„ ๋ณด๊ณ , ๊ฐœ๋…๋„ ์žก๊ณ , ์‹ค๋ฌด์—์„œ ์–ด๋–ป๊ฒŒ ์“ฐ๋Š”์ง€๊นŒ์ง€ โ€” ์™„์ „ ํ’€ํŒจํ‚ค์ง€๋กœ ๊ฐ‘๋‹ˆ๋‹ค ใ„ฑใ„ฑ ๐Ÿ”ฅ

Symfony ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ๊ณ„ DI ์ปจํ…Œ์ด๋„ˆ HttpKernel EventDispatcher Routing Console Security Form Validator ๊ฐ ์ปดํฌ๋„ŒํŠธ๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์„ค์น˜ยท์‚ฌ์šฉ ๊ฐ€๋Šฅ (composer require symfony/์ปดํฌ๋„ŒํŠธ๋ช…)

๐Ÿงฉ 1. Symfony ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜๋ž€?

Symfony๋Š” 2005๋…„ Fabien Potencier๊ฐ€ ๋งŒ๋“  PHP ํ”„๋ ˆ์ž„์›Œํฌ์ธ๋ฐ์š”,
์ฒ˜์Œ๋ถ€ํ„ฐ "๋ชจ๋†€๋ฆฌ์‹ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์•„๋‹Œ, ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ์ง‘ํ•ฉ"์œผ๋กœ ์„ค๊ณ„๋์–ด์š”.
์ด๊ฒŒ ์ง„์งœ ํ˜์‹ ์ ์ธ ๋ถ€๋ถ„์ด์—์š” ใ„นใ…‡ ๐Ÿ”ฅ

๐ŸŽฏ ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜์˜ ํ•ต์‹ฌ ์›์น™

๋‹จ์ผ ์ฑ…์ž„ ์›์น™ ๋А์Šจํ•œ ๊ฒฐํ•ฉ ๋†’์€ ์‘์ง‘๋„ ์žฌ์‚ฌ์šฉ์„ฑ

๊ฐ ์ปดํฌ๋„ŒํŠธ๋Š” ํ•˜๋‚˜์˜ ๋ช…ํ™•ํ•œ ์—ญํ• ๋งŒ ๋‹ด๋‹นํ•˜๊ณ ,
๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ์ตœ์†Œํ™”ํ•ด์„œ ๋…๋ฆฝ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.
๐Ÿ“ฆ ์ฃผ์š” Symfony ์ปดํฌ๋„ŒํŠธ ๋ชฉ๋ก
์ปดํฌ๋„ŒํŠธ ์—ญํ•  ํŒจํ‚ค์ง€๋ช…
DependencyInjection ์˜์กด์„ฑ ์ฃผ์ž… ์ปจํ…Œ์ด๋„ˆ symfony/dependency-injection
HttpKernel HTTP ์š”์ฒญ/์‘๋‹ต ์ฒ˜๋ฆฌ symfony/http-kernel
Routing URL ๋ผ์šฐํŒ… symfony/routing
EventDispatcher ์ด๋ฒคํŠธ ์‹œ์Šคํ…œ symfony/event-dispatcher
Console CLI ๋ช…๋ น์–ด ์ฒ˜๋ฆฌ symfony/console
Validator ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ symfony/validator
Form ํผ ์ƒ์„ฑ ๋ฐ ์ฒ˜๋ฆฌ symfony/form
Security ์ธ์ฆ/์ธ๊ฐ€ symfony/security-core
Cache ์บ์‹ฑ ์ถ”์ƒํ™” symfony/cache
Serializer ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” symfony/serializer
๐Ÿ’ฌ ๊ทผ๋ฐ ์ด๊ฑฐ ๋‹ค ์„ค์น˜ํ•ด์•ผ ํ•ด? ๋ฌด๊ฑฐ์šด ๊ฑฐ ์•„๋‹ˆ์•ผ?
๐Ÿค– ใ„ดใ„ด! ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ณจ๋ผ์„œ ์„ค์น˜ํ•˜๋ฉด ๋ผ. composer require symfony/routing ์ด๋Ÿฐ ์‹์œผ๋กœ ๋”ฑ ํ•„์š”ํ•œ ๊ฒƒ๋งŒ! ๊ทธ๋ž˜์„œ ๋งˆ์ดํฌ๋กœ ํ”„๋ ˆ์ž„์›Œํฌ ๋งŒ๋“ค ๋•Œ๋„ Symfony ์ปดํฌ๋„ŒํŠธ ์”€ ใ…‹ใ…‹

์‹ค์ œ๋กœ Symfony ์ปดํฌ๋„ŒํŠธ๋“ค์€ Composer๋ฅผ ํ†ตํ•ด ๊ฐœ๋ณ„์ ์œผ๋กœ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์–ด์š”.
์ „์ฒด Symfony ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์„ค์น˜ํ•˜์ง€ ์•Š์•„๋„, ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋งŒ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒŒ ์ง„์งœ ๊ฐ•์ ์ด์—์š”.

์˜ˆ๋ฅผ ๋“ค์–ด Laravel์€ ๋‚ด๋ถ€์ ์œผ๋กœ symfony/http-foundation, symfony/routing, symfony/console ๋“ฑ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์š”.
Drupal 8 ์ด์ƒ๋„ Symfony ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋Œ€๊ฑฐ ์ฑ„ํƒํ–ˆ๊ณ ์š”. ์ด๊ฒŒ ๋ฐ”๋กœ Symfony๊ฐ€ "PHP ์ƒํƒœ๊ณ„์˜ ์ธํ”„๋ผ"๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค ๐Ÿ’ช


๐Ÿ’‰ 2. ์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection)์ด๋ž€?

์˜์กด์„ฑ ์ฃผ์ž…... ์ด๋ฆ„๋งŒ ๋“ค์œผ๋ฉด ๋ญ”๊ฐ€ ์–ด๋ ต๊ฒŒ ๋А๊ปด์ง€์ฃ ? ใ…‹ใ…‹ใ…‹
๊ทผ๋ฐ ๊ฐœ๋… ์ž์ฒด๋Š” ์ƒ๊ฐ๋ณด๋‹ค ์‹ฌํ”Œํ•ด์š”. ๊ฐ™์ด ์‚ดํŽด๋ด์š”!

๐Ÿšซ ์˜์กด์„ฑ ์ฃผ์ž… ์—†์ด ์ฝ”๋“œ ์งœ๋ฉด?

// โŒ ๋‚˜์œ ์˜ˆ์‹œ - ๊ฐ•ํ•œ ๊ฒฐํ•ฉ(Tight Coupling)
class UserService {
    private $db;
    
    public function __construct() {
        // ๋‚ด๋ถ€์—์„œ ์ง์ ‘ ์ƒ์„ฑ - ์ด๊ฒŒ ๋ฌธ์ œ!
        $this->db = new MySQLDatabase('localhost', 'root', 'password');
    }
    
    public function findUser(int $id): array {
        return $this->db->query("SELECT * FROM users WHERE id = ?", [$id]);
    }
}

// ์‚ฌ์šฉ
$service = new UserService(); // DB ์„ค์ •์ด ํด๋ž˜์Šค ์•ˆ์— ๋ฐ•ํ˜€์žˆ์Œ ใ… ใ… 
โš ๏ธ ์ด ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ 

1. ํ…Œ์ŠคํŠธ ๋ถˆ๊ฐ€ โ€” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์‹œ ์‹ค์ œ DB ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•จ
2. ์œ ์—ฐ์„ฑ ์—†์Œ โ€” DB๋ฅผ PostgreSQL๋กœ ๋ฐ”๊พธ๋ ค๋ฉด ํด๋ž˜์Šค ๋‚ด๋ถ€๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•จ
3. ์žฌ์‚ฌ์šฉ ์–ด๋ ค์›€ โ€” ๋‹ค๋ฅธ DB ์„ค์ •์œผ๋กœ ์žฌ์‚ฌ์šฉ ๋ถˆ๊ฐ€
4. ๋‹จ์ผ ์ฑ…์ž„ ์›์น™ ์œ„๋ฐ˜ โ€” ์„œ๋น„์Šค๊ฐ€ DB ์ƒ์„ฑ๊นŒ์ง€ ๋‹ด๋‹น
โœ… ์˜์กด์„ฑ ์ฃผ์ž…์œผ๋กœ ๊ฐœ์„ ํ•˜๋ฉด?

// โœ… ์ข‹์€ ์˜ˆ์‹œ - ์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection)

// ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜
interface DatabaseInterface {
    public function query(string $sql, array $params = []): array;
}

// ๊ตฌํ˜„์ฒด
class MySQLDatabase implements DatabaseInterface {
    public function __construct(
        private string $host,
        private string $user,
        private string $password
    ) {}
    
    public function query(string $sql, array $params = []): array {
        // MySQL ์ฟผ๋ฆฌ ์‹คํ–‰
        return [];
    }
}

// ์„œ๋น„์Šค - ์™ธ๋ถ€์—์„œ ์˜์กด์„ฑ์„ ์ฃผ์ž…๋ฐ›์Œ!
class UserService {
    public function __construct(
        private DatabaseInterface $db  // ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กด
    ) {}
    
    public function findUser(int $id): array {
        return $this->db->query("SELECT * FROM users WHERE id = ?", [$id]);
    }
}

// ์‚ฌ์šฉ - ์™ธ๋ถ€์—์„œ ์กฐ๋ฆฝ
$db = new MySQLDatabase('localhost', 'root', 'password');
$service = new UserService($db);  // ์ฃผ์ž…!

// ํ…Œ์ŠคํŠธ ์‹œ์—๋Š” Mock ๊ฐ์ฒด ์ฃผ์ž… ๊ฐ€๋Šฅ!
$mockDb = new MockDatabase();
$testService = new UserService($mockDb);  // ํ…Œ์ŠคํŠธ ์‰ฌ์›Œ์ง ใ„ทใ„ท
๐Ÿ’ฌ ์˜ค ์ด์ œ ์ดํ•ด๋จ! ๊ทผ๋ฐ ์˜์กด์„ฑ์ด ๋งŽ์•„์ง€๋ฉด ์ง์ ‘ ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ๋„ ๊ท€์ฐฎ์ง€ ์•Š์•„?
๐Ÿค– ๋งž์•„ ใ…‹ใ…‹ใ…‹ ๊ทธ๋ž˜์„œ DI ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋“ฑ์žฅํ•˜๋Š” ๊ฑฐ์•ผ! ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์•Œ์•„์„œ ๋‹ค ์กฐ๋ฆฝํ•ด์คŒ ใ„ฑใ„ฑ
์˜์กด์„ฑ ์ฃผ์ž…(DI) ๋™์ž‘ ์›๋ฆฌ DI ์ปจํ…Œ์ด๋„ˆ ์„œ๋น„์Šค ๋“ฑ๋ก ยท ์ƒ์„ฑ ยท ๊ด€๋ฆฌ UserService ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง Database ๋ฐ์ดํ„ฐ ์ ‘๊ทผ Mailer ์ด๋ฉ”์ผ ๋ฐœ์†ก Logger ๋กœ๊ทธ ๊ธฐ๋ก ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์˜์กด์„ฑ์„ ์ž๋™์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ณ  ์ฃผ์ž…ํ•ด์คŒ! ๊ฐœ๋ฐœ์ž๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—๋งŒ ์ง‘์ค‘ ๊ฐ€๋Šฅ ๐ŸŽฏ

๐Ÿญ 3. Symfony DI ์ปจํ…Œ์ด๋„ˆ ์™„์ „ ์ •๋ณต

Symfony์˜ DependencyInjection ์ปดํฌ๋„ŒํŠธ๋Š” PHP ์ƒํƒœ๊ณ„์—์„œ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•˜๊ณ  ์„ฑ์ˆ™ํ•œ DI ์ปจํ…Œ์ด๋„ˆ ์ค‘ ํ•˜๋‚˜์˜ˆ์š”.
์„ค์น˜๋ถ€ํ„ฐ ์‹ค์ œ ์‚ฌ์šฉ๊นŒ์ง€ ๋‹จ๊ณ„๋ณ„๋กœ ์‚ดํŽด๋ณผ๊ฒŒ์š”!

๐Ÿ“ฅ ์„ค์น˜

composer require symfony/dependency-injection
composer require symfony/config  # ์„ค์ • ํŒŒ์ผ ๋กœ๋”ฉ์šฉ (์„ ํƒ)
composer require symfony/yaml    # YAML ์„ค์ • ํŒŒ์ผ์šฉ (์„ ํƒ)
๐Ÿ”ง ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ• - PHP ์ฝ”๋“œ๋กœ ์„œ๋น„์Šค ๋“ฑ๋ก

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

// ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ
$container = new ContainerBuilder();

// ์„œ๋น„์Šค ๋“ฑ๋ก - Database
$container->register('database', MySQLDatabase::class)
    ->addArgument('localhost')
    ->addArgument('root')
    ->addArgument('secret_password');

// ์„œ๋น„์Šค ๋“ฑ๋ก - Mailer
$container->register('mailer', SmtpMailer::class)
    ->addArgument('smtp.example.com')
    ->addArgument(587);

// ์„œ๋น„์Šค ๋“ฑ๋ก - UserService (๋‹ค๋ฅธ ์„œ๋น„์Šค์— ์˜์กด)
$container->register('user_service', UserService::class)
    ->addArgument(new Reference('database'))  // database ์„œ๋น„์Šค ์ฐธ์กฐ
    ->addArgument(new Reference('mailer'));   // mailer ์„œ๋น„์Šค ์ฐธ์กฐ

// ์ปจํ…Œ์ด๋„ˆ ์ปดํŒŒ์ผ (์ตœ์ ํ™”)
$container->compile();

// ์„œ๋น„์Šค ๊ฐ€์ ธ์˜ค๊ธฐ
$userService = $container->get('user_service');
// ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์•Œ์•„์„œ database, mailer ์ƒ์„ฑํ•ด์„œ ์ฃผ์ž…ํ•ด์คŒ! ๐ŸŽ‰
โœ… ์ปจํ…Œ์ด๋„ˆ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ

์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading) โ€” ์‹ค์ œ๋กœ ํ•„์š”ํ•  ๋•Œ๋งŒ ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
์‹ฑ๊ธ€ํ†ค ๊ด€๋ฆฌ โ€” ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋น„์Šค๋Š” ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑ๋˜๊ณ  ์žฌ์‚ฌ์šฉ๋จ
์ž๋™ ์™€์ด์–ด๋ง โ€” ํƒ€์ž… ํžŒํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ์˜์กด์„ฑ ์ž๋™ ํ•ด๊ฒฐ
์ปดํŒŒ์ผ ์ตœ์ ํ™” โ€” ์ปดํŒŒ์ผ ์‹œ ์ตœ์ ํ™”๋œ PHP ์ฝ”๋“œ ์ƒ์„ฑ
๐Ÿ“ YAML ์„ค์ • ํŒŒ์ผ๋กœ ์„œ๋น„์Šค ๋“ฑ๋ก

PHP ์ฝ”๋“œ๋กœ ์ง์ ‘ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์ง€๋งŒ, ์‹ค๋ฌด์—์„œ๋Š” YAML ์„ค์ • ํŒŒ์ผ์„ ๋” ๋งŽ์ด ์จ์š”.
ํ›จ์”ฌ ๊น”๋”ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ํŽธํ•˜๊ฑฐ๋“ ์š” ใ…‹ใ…‹


# config/services.yaml

services:
    # ๊ธฐ๋ณธ ์„ค์ •
    _defaults:
        autowire: true      # ์ž๋™ ์™€์ด์–ด๋ง ํ™œ์„ฑํ™”
        autoconfigure: true # ์ž๋™ ์„ค์ • ํ™œ์„ฑํ™”
        public: false       # ๊ธฐ๋ณธ์ ์œผ๋กœ private ์„œ๋น„์Šค

    # ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ „์ฒด ์ž๋™ ๋“ฑ๋ก
    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'

    # ๊ฐœ๋ณ„ ์„œ๋น„์Šค ์„ค์ •
    App\Service\UserService:
        arguments:
            $db: '@App\Repository\UserRepository'
            $mailer: '@App\Service\MailerService'
        tags:
            - { name: 'app.service' }

    # ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฌ์šฉ
    App\Service\SmtpMailer:
        arguments:
            $host: '%mailer.host%'
            $port: '%mailer.port%'

parameters:
    mailer.host: 'smtp.example.com'
    mailer.port: 587
๐Ÿ’ฌ autowire: true ์ด๊ฒŒ ๋ญ์•ผ? ์ž๋™์œผ๋กœ ๋ญ˜ ํ•ด์ค€๋‹ค๋Š” ๊ฑฐ์•ผ?
๐Ÿค– ํƒ€์ž… ํžŒํŠธ ๋ณด๊ณ  ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์•Œ์•„์„œ ๋งž๋Š” ์„œ๋น„์Šค ์ฃผ์ž…ํ•ด์ค˜! ์˜ˆ๋ฅผ ๋“ค์–ด ์ƒ์„ฑ์ž์— DatabaseInterface $db ์žˆ์œผ๋ฉด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ DatabaseInterface ๊ตฌํ˜„์ฒด ์ฐพ์•„์„œ ์ž๋™์œผ๋กœ ๋„ฃ์–ด์คŒ. ์ง„์งœ ํŽธํ•จ ใ„ทใ„ท
๐Ÿค– ์ž๋™ ์™€์ด์–ด๋ง(Autowiring) ์‹ฌํ™”

// PHP 8.1+ ์ƒ์„ฑ์ž ํ”„๋กœํผํ‹ฐ ํ”„๋กœ๋ชจ์…˜๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ
class OrderService {
    public function __construct(
        private UserRepository $userRepository,      // ์ž๋™ ์ฃผ์ž…!
        private ProductRepository $productRepository, // ์ž๋™ ์ฃผ์ž…!
        private MailerInterface $mailer,              // ์ž๋™ ์ฃผ์ž…!
        private LoggerInterface $logger               // ์ž๋™ ์ฃผ์ž…!
    ) {}
    
    public function createOrder(int $userId, array $items): Order {
        $user = $this->userRepository->find($userId);
        // ... ์ฃผ๋ฌธ ์ƒ์„ฑ ๋กœ์ง
        $this->mailer->send(new OrderConfirmationEmail($user));
        $this->logger->info('Order created', ['user_id' => $userId]);
        return $order;
    }
}
// services.yaml์— autowire: true ์„ค์ •๋งŒ ํ•ด๋‘๋ฉด
// ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํƒ€์ž… ํžŒํŠธ ๋ณด๊ณ  ์•Œ์•„์„œ ๋‹ค ์ฃผ์ž…ํ•ด์คŒ! ๐ŸŽ‰

๐Ÿท๏ธ 4. ์„œ๋น„์Šค ํƒœ๊ทธ(Service Tags)์™€ ์ปดํŒŒ์ผ๋Ÿฌ ํŒจ์Šค

Symfony DI ์ปจํ…Œ์ด๋„ˆ์˜ ์ง„์งœ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ ์„œ๋น„์Šค ํƒœ๊ทธ์˜ˆ์š”.
ํŠน์ • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ์„œ๋น„์Šค๋“ค์„ ์ž๋™์œผ๋กœ ์ˆ˜์ง‘ํ•ด์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๊ฑฐ ์•Œ๋ฉด ์ง„์งœ ๋ ˆ๋ฒจ์—… ๋จ ใ„นใ…‡ ๐Ÿ”ฅ

๐Ÿ“Œ ์„œ๋น„์Šค ํƒœ๊ทธ ์‹ค์ „ ์˜ˆ์‹œ - ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ

// 1. ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜
interface PaymentGatewayInterface {
    public function getName(): string;
    public function processPayment(float $amount): bool;
}

// 2. ๊ตฌํ˜„์ฒด๋“ค
class StripeGateway implements PaymentGatewayInterface {
    public function getName(): string { return 'stripe'; }
    public function processPayment(float $amount): bool {
        // Stripe API ํ˜ธ์ถœ
        return true;
    }
}

class PaypalGateway implements PaymentGatewayInterface {
    public function getName(): string { return 'paypal'; }
    public function processPayment(float $amount): bool {
        // PayPal API ํ˜ธ์ถœ
        return true;
    }
}

// 3. ์ˆ˜์ง‘๊ธฐ ์„œ๋น„์Šค
class PaymentGatewayRegistry {
    private array $gateways = [];
    
    public function addGateway(PaymentGatewayInterface $gateway): void {
        $this->gateways[$gateway->getName()] = $gateway;
    }
    
    public function getGateway(string $name): PaymentGatewayInterface {
        return $this->gateways[$name] 
            ?? throw new \InvalidArgumentException("Gateway '$name' not found");
    }
}

# services.yaml - ํƒœ๊ทธ ์„ค์ •
services:
    App\Payment\StripeGateway:
        tags:
            - { name: 'app.payment_gateway' }
    
    App\Payment\PaypalGateway:
        tags:
            - { name: 'app.payment_gateway' }
    
    App\Payment\PaymentGatewayRegistry:
        # ํƒœ๊ทธ๋œ ์„œ๋น„์Šค๋“ค์„ ์ž๋™์œผ๋กœ ์ฃผ์ž…!

// 4. ์ปดํŒŒ์ผ๋Ÿฌ ํŒจ์Šค - ํƒœ๊ทธ๋œ ์„œ๋น„์Šค ์ž๋™ ์ˆ˜์ง‘
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class PaymentGatewayCompilerPass implements CompilerPassInterface {
    public function process(ContainerBuilder $container): void {
        if (!$container->has(PaymentGatewayRegistry::class)) {
            return;
        }
        
        $registryDef = $container->findDefinition(PaymentGatewayRegistry::class);
        
        // 'app.payment_gateway' ํƒœ๊ทธ๊ฐ€ ๋ถ™์€ ๋ชจ๋“  ์„œ๋น„์Šค ์ฐพ๊ธฐ
        $taggedServices = $container->findTaggedServiceIds('app.payment_gateway');
        
        foreach ($taggedServices as $id => $tags) {
            // ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ์ž๋™์œผ๋กœ ์ถ”๊ฐ€
            $registryDef->addMethodCall('addGateway', [new Reference($id)]);
        }
    }
}

// Kernel ๋˜๋Š” Bundle์— ๋“ฑ๋ก
class AppKernel extends Kernel {
    protected function build(ContainerBuilder $container): void {
        $container->addCompilerPass(new PaymentGatewayCompilerPass());
    }
}
๐Ÿ’ก PHP 8 ์†์„ฑ(Attribute)์œผ๋กœ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ!

Symfony 6.x๋ถ€ํ„ฐ๋Š” PHP 8 Attribute๋ฅผ ํ™œ์šฉํ•ด์„œ ํƒœ๊ทธ๋ฅผ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”.

use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;

#[AutoconfigureTag('app.payment_gateway')]
class StripeGateway implements PaymentGatewayInterface {
    // ...
}

// ๋˜๋Š” AsTaggedItem ์‚ฌ์šฉ
use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;

#[AsTaggedItem('app.payment_gateway', priority: 10)]
class StripeGateway implements PaymentGatewayInterface {
    // ...
}

โš™๏ธ 5. ํ™˜๊ฒฝ๋ณ„ ์„ค์ •๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ด€๋ฆฌ

์‹ค๋ฌด์—์„œ๋Š” ๊ฐœ๋ฐœ(dev), ํ…Œ์ŠคํŠธ(test), ์šด์˜(prod) ํ™˜๊ฒฝ๋งˆ๋‹ค ๋‹ค๋ฅธ ์„ค์ •์ด ํ•„์š”ํ•˜์ฃ .
Symfony๋Š” ์ด๊ฑธ ์ •๋ง ์šฐ์•„ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ด์š” ๐Ÿ˜Ž

๐ŸŒ ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ํŒŒ์ผ ๊ตฌ์กฐ

config/
โ”œโ”€โ”€ services.yaml          # ๊ณตํ†ต ์„œ๋น„์Šค ์„ค์ •
โ”œโ”€โ”€ services_dev.yaml      # ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์ „์šฉ
โ”œโ”€โ”€ services_test.yaml     # ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ์ „์šฉ
โ”œโ”€โ”€ services_prod.yaml     # ์šด์˜ ํ™˜๊ฒฝ ์ „์šฉ
โ”œโ”€โ”€ packages/
โ”‚   โ”œโ”€โ”€ framework.yaml
โ”‚   โ”œโ”€โ”€ doctrine.yaml
โ”‚   โ””โ”€โ”€ dev/
โ”‚       โ””โ”€โ”€ web_profiler.yaml  # ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ๋กœ๋“œ
โ””โ”€โ”€ routes/
    โ”œโ”€โ”€ annotations.yaml
    โ””โ”€โ”€ dev/
        โ””โ”€โ”€ web_profiler.yaml

# config/services.yaml - ๊ณตํ†ต ์„ค์ •
parameters:
    app.name: 'My Application'
    app.version: '1.0.0'
    
services:
    App\Service\UserService:
        arguments:
            $appName: '%app.name%'  # ํŒŒ๋ผ๋ฏธํ„ฐ ์ฐธ์กฐ

---
# config/services_dev.yaml - ๊ฐœ๋ฐœ ํ™˜๊ฒฝ
parameters:
    app.debug: true
    
services:
    # ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์„œ๋น„์Šค
    App\Service\DebugMailer:
        decorates: App\Service\MailerService  # ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด!

---
# config/services_prod.yaml - ์šด์˜ ํ™˜๊ฒฝ
parameters:
    app.debug: false
๐Ÿ” ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์—ฐ๋™

# .env ํŒŒ์ผ
DATABASE_URL="mysql://user:password@localhost:3306/mydb"
MAILER_DSN="smtp://user:pass@smtp.example.com:587"
APP_SECRET="your-secret-key-here"

# config/services.yaml์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ฌ์šฉ
parameters:
    database_url: '%env(DATABASE_URL)%'
    
services:
    App\Repository\DatabaseConnection:
        arguments:
            $dsn: '%env(DATABASE_URL)%'
            # ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํ”„๋กœ์„ธ์„œ ์‚ฌ์šฉ
            $port: '%env(int:DATABASE_PORT)%'  # ์ •์ˆ˜๋กœ ๋ณ€ํ™˜
            $debug: '%env(bool:APP_DEBUG)%'    # ๋ถˆ๋ฆฌ์–ธ์œผ๋กœ ๋ณ€ํ™˜
๐ŸŽฏ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํ”„๋กœ์„ธ์„œ ์ข…๋ฅ˜

env(string:VAR) ๋ฌธ์ž์—ด (๊ธฐ๋ณธ๊ฐ’)
env(int:VAR) ์ •์ˆ˜ ๋ณ€ํ™˜
env(float:VAR) ์‹ค์ˆ˜ ๋ณ€ํ™˜
env(bool:VAR) ๋ถˆ๋ฆฌ์–ธ ๋ณ€ํ™˜
env(json:VAR) JSON ํŒŒ์‹ฑ
env(base64:VAR) Base64 ๋””์ฝ”๋”ฉ
env(resolve:VAR) ๋‹ค๋ฅธ ํŒŒ๋ผ๋ฏธํ„ฐ ์ฐธ์กฐ ํ•ด๊ฒฐ

๐ŸŽจ 6. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด๊ณผ ์„œ๋น„์Šค ๋Œ€์ฒด(Decoration)

Symfony DI ์ปจํ…Œ์ด๋„ˆ์˜ ๋˜ ๋‹ค๋ฅธ ํ‚ฌ๋Ÿฌ ๊ธฐ๋Šฅ! ๋ฐ”๋กœ ์„œ๋น„์Šค ๋ฐ์ฝ”๋ ˆ์ด์…˜์ด์—์š”.
๊ธฐ์กด ์„œ๋น„์Šค๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์–ด์š”. ๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™(OCP) ์™„๋ฒฝ ๊ตฌํ˜„ ใ„ทใ„ท


// ์›๋ณธ ์„œ๋น„์Šค
class UserRepository {
    public function find(int $id): ?User {
        // DB์—์„œ ์‚ฌ์šฉ์ž ์กฐํšŒ
        return $this->db->findOne('users', $id);
    }
}

// ์บ์‹ฑ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ
class CachedUserRepository {
    public function __construct(
        private UserRepository $inner,  // ์›๋ณธ ์„œ๋น„์Šค ์ฃผ์ž…
        private CacheInterface $cache
    ) {}
    
    public function find(int $id): ?User {
        $cacheKey = "user_{$id}";
        
        // ์บ์‹œ์—์„œ ๋จผ์ € ์ฐพ๊ธฐ
        if ($this->cache->has($cacheKey)) {
            return $this->cache->get($cacheKey);
        }
        
        // ์—†์œผ๋ฉด ์›๋ณธ์—์„œ ๊ฐ€์ ธ์™€์„œ ์บ์‹œ์— ์ €์žฅ
        $user = $this->inner->find($id);
        $this->cache->set($cacheKey, $user, 3600);
        
        return $user;
    }
}

# services.yaml - ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์„ค์ •
services:
    App\Repository\UserRepository: ~  # ์›๋ณธ ์„œ๋น„์Šค
    
    App\Repository\CachedUserRepository:
        decorates: App\Repository\UserRepository  # ๋ฐ์ฝ”๋ ˆ์ด์…˜!
        arguments:
            $inner: '@.inner'  # ์›๋ณธ ์„œ๋น„์Šค ์ž๋™ ์ฐธ์กฐ
๐Ÿ’ฌ ์ด๊ฑฐ ์“ฐ๋ฉด ๋ญ๊ฐ€ ์ข‹์•„?
๐Ÿค– ์›๋ณธ ์ฝ”๋“œ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ์บ์‹ฑ, ๋กœ๊น…, ํŠธ๋žœ์žญ์…˜ ๊ฐ™์€ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด! ๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋นผ๊ณ  ์›๋ณธ๋งŒ ์“ธ ์ˆ˜๋„ ์žˆ๊ณ . ์ง„์งœ ์œ ์—ฐํ•จ ใ„นใ…‡ ๐Ÿ”ฅ

๐Ÿ›๏ธ 7. ์‹ค์ „ ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด - ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜

Symfony ์ปดํฌ๋„ŒํŠธ์™€ DI ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ™œ์šฉํ•œ ์‹ค์ „ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์‚ดํŽด๋ณผ๊ฒŒ์š”.
์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์„ ๋งŒ๋“ ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์„ค๊ณ„ํ•ด๋ณผ๊ฒŒ์š”! ๐ŸŽฏ

๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์กฐ ๐Ÿ–ฅ๏ธ ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋ ˆ์ด์–ด (Presentation Layer) Controller ยท Twig Template ยท API Response ยท Form โš™๏ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ์ด์–ด (Application Layer) Command ยท Query ยท Handler ยท DTO ยท Use Case ๐ŸŽฏ ๋„๋ฉ”์ธ ๋ ˆ์ด์–ด (Domain Layer) Entity ยท Value Object ยท Domain Service ยท Repository Interface ๐Ÿ”ง ์ธํ”„๋ผ ๋ ˆ์ด์–ด (Infrastructure Layer) Doctrine ORM ยท Mailer ยท Cache ยท External API ยท Queue ๐Ÿญ DI ์ปจํ…Œ์ด๋„ˆ ๋ชจ๋“  ๋ ˆ์ด์–ด์˜ ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•˜๊ณ  ์กฐ๋ฆฝ
๐Ÿ“ ์‹ค์ „ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ

src/
โ”œโ”€โ”€ Controller/           # ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋ ˆ์ด์–ด
โ”‚   โ”œโ”€โ”€ UserController.php
โ”‚   โ””โ”€โ”€ OrderController.php
โ”‚
โ”œโ”€โ”€ Application/          # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ์ด์–ด
โ”‚   โ”œโ”€โ”€ Command/
โ”‚   โ”‚   โ”œโ”€โ”€ CreateUserCommand.php
โ”‚   โ”‚   โ””โ”€โ”€ CreateUserHandler.php
โ”‚   โ””โ”€โ”€ Query/
โ”‚       โ”œโ”€โ”€ GetUserQuery.php
โ”‚       โ””โ”€โ”€ GetUserHandler.php
โ”‚
โ”œโ”€โ”€ Domain/               # ๋„๋ฉ”์ธ ๋ ˆ์ด์–ด
โ”‚   โ”œโ”€โ”€ Entity/
โ”‚   โ”‚   โ””โ”€โ”€ User.php
โ”‚   โ”œโ”€โ”€ Repository/
โ”‚   โ”‚   โ””โ”€โ”€ UserRepositoryInterface.php  # ์ธํ„ฐํŽ˜์ด์Šค๋งŒ!
โ”‚   โ””โ”€โ”€ Service/
โ”‚       โ””โ”€โ”€ UserDomainService.php
โ”‚
โ””โ”€โ”€ Infrastructure/       # ์ธํ”„๋ผ ๋ ˆ์ด์–ด
    โ”œโ”€โ”€ Repository/
    โ”‚   โ””โ”€โ”€ DoctrineUserRepository.php   # ๊ตฌํ˜„์ฒด
    โ”œโ”€โ”€ Mailer/
    โ”‚   โ””โ”€โ”€ SymfonyMailer.php
    โ””โ”€โ”€ Cache/
        โ””โ”€โ”€ RedisCache.php
๐Ÿ”— ์‹ค์ „ ์ฝ”๋“œ - CQRS ํŒจํ„ด๊ณผ DI ์ปจํ…Œ์ด๋„ˆ

// Command - ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๋ช…๋ น
class CreateUserCommand {
    public function __construct(
        public readonly string $email,
        public readonly string $name,
        public readonly string $password
    ) {}
}

// Handler - ๋ช…๋ น ์ฒ˜๋ฆฌ
class CreateUserHandler {
    public function __construct(
        private UserRepositoryInterface $userRepository,
        private PasswordHasherInterface $passwordHasher,
        private EventDispatcherInterface $eventDispatcher,
        private LoggerInterface $logger
    ) {}
    
    public function __invoke(CreateUserCommand $command): User {
        // ์ด๋ฉ”์ผ ์ค‘๋ณต ํ™•์ธ
        if ($this->userRepository->existsByEmail($command->email)) {
            throw new UserAlreadyExistsException($command->email);
        }
        
        // ์‚ฌ์šฉ์ž ์ƒ์„ฑ
        $user = new User(
            email: $command->email,
            name: $command->name,
            passwordHash: $this->passwordHasher->hash($command->password)
        );
        
        $this->userRepository->save($user);
        
        // ์ด๋ฒคํŠธ ๋ฐœํ–‰
        $this->eventDispatcher->dispatch(new UserCreatedEvent($user));
        
        $this->logger->info('User created', ['email' => $command->email]);
        
        return $user;
    }
}

# services.yaml - ์ž๋™ ์™€์ด์–ด๋ง์œผ๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ!
services:
    _defaults:
        autowire: true
        autoconfigure: true
    
    App\:
        resource: '../src/'
    
    # ์ธํ„ฐํŽ˜์ด์Šค โ†’ ๊ตฌํ˜„์ฒด ๋ฐ”์ธ๋”ฉ
    App\Domain\Repository\UserRepositoryInterface:
        alias: App\Infrastructure\Repository\DoctrineUserRepository
    
    App\Domain\Service\PasswordHasherInterface:
        alias: App\Infrastructure\Security\BcryptPasswordHasher
๐Ÿ’ฌ ์™€ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Controller์—์„œ Handler ์ง์ ‘ ์•ˆ ๋งŒ๋“ค์–ด๋„ ๋˜๋Š” ๊ฑฐ์•ผ?
๐Ÿค– ๋งž์•„! Controller์—์„œ ๊ทธ๋ƒฅ ํƒ€์ž… ํžŒํŠธ๋กœ ๋ฐ›์œผ๋ฉด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์•Œ์•„์„œ ๋‹ค ๋งŒ๋“ค์–ด์„œ ์ฃผ์ž…ํ•ด์คŒ. ์ง„์งœ ๋งˆ๋ฒ• ๊ฐ™์Œ ใ…‹ใ…‹ใ…‹ ๊ทธ๋ฆฌ๊ณ  ๋‚˜์ค‘์— DB ๋ฐ”๊ฟ”๋„ ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„์ฒด๋งŒ ๋ฐ”๊พธ๋ฉด ๋˜๋‹ˆ๊นŒ ์œ ์ง€๋ณด์ˆ˜๋„ ์‰ฌ์›Œ!

โšก 8. DI ์ปจํ…Œ์ด๋„ˆ ์„ฑ๋Šฅ ์ตœ์ ํ™”

DI ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํŽธ๋ฆฌํ•˜๊ธด ํ•œ๋ฐ, ์ž˜๋ชป ์“ฐ๋ฉด ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์–ด์š”.
Symfony๋Š” ์ด ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ๊ฒŒ์š”!

๐Ÿš€ ์ปจํ…Œ์ด๋„ˆ ์ปดํŒŒ์ผ๊ณผ ์บ์‹ฑ

// Symfony๋Š” ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ปดํŒŒ์ผํ•ด์„œ ์ตœ์ ํ™”๋œ PHP ํŒŒ์ผ๋กœ ์ €์žฅ
// var/cache/prod/App_KernelProdContainer.php ๊ฐ™์€ ํŒŒ์ผ์ด ์ƒ์„ฑ๋จ

// ์ปดํŒŒ์ผ๋œ ์ปจํ…Œ์ด๋„ˆ๋Š” ์ด๋Ÿฐ ํ˜•ํƒœ
class App_KernelProdContainer extends Container {
    // ๋ชจ๋“  ์„œ๋น„์Šค ์ •์˜๊ฐ€ ์ตœ์ ํ™”๋œ PHP ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜๋จ
    protected function getUserService(): UserService {
        return $this->services['App\\Service\\UserService'] 
            ??= new UserService(
                $this->getDoctrineUserRepositoryService(),
                $this->getSymfonyMailerService()
            );
    }
}

// ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์ด ์บ์‹œ๋œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉ
// โ†’ ๋งค ์š”์ฒญ๋งˆ๋‹ค YAML ํŒŒ์‹ฑ ์—†์Œ, ์ฆ‰์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ!
๐Ÿ’ค ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading) ์„œ๋น„์Šค

# services.yaml - ๋ฌด๊ฑฐ์šด ์„œ๋น„์Šค๋Š” lazy ์„ค์ •
services:
    App\Service\HeavyReportGenerator:
        lazy: true  # ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋  ๋•Œ๋งŒ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
        
    App\Service\ExternalApiClient:
        lazy: true

// PHP 8.4+ ์—์„œ๋Š” Lazy Ghost ๊ฐ์ฒด ์ง€์›
// ์‹ค์ œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์ „๊นŒ์ง€ ์ดˆ๊ธฐํ™” ์•ˆ ๋จ

use Symfony\Component\DependencyInjection\Attribute\Lazy;

class OrderController {
    public function __construct(
        #[Lazy] private HeavyReportGenerator $reportGenerator,
        // ์ด ์„œ๋น„์Šค๋Š” ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ๋•Œ๋งŒ ์ดˆ๊ธฐํ™”๋จ!
    ) {}
    
    public function index(): Response {
        // reportGenerator๋Š” ์—ฌ๊ธฐ์„œ ์ดˆ๊ธฐํ™” ์•ˆ ๋จ
        return new Response('Hello');
    }
    
    public function generateReport(): Response {
        // ์—ฌ๊ธฐ์„œ ์ฒ˜์Œ ์‚ฌ์šฉ๋  ๋•Œ ์ดˆ๊ธฐํ™”๋จ
        $report = $this->reportGenerator->generate();
        return new Response($report);
    }
}
โšก ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ฒดํฌ๋ฆฌ์ŠคํŠธ

โœ… ์šด์˜ ํ™˜๊ฒฝ์—์„œ APP_ENV=prod ์„ค์ •
โœ… php bin/console cache:warmup์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ ์‚ฌ์ „ ์ปดํŒŒ์ผ
โœ… ๋ฌด๊ฑฐ์šด ์„œ๋น„์Šค์— lazy: true ์„ค์ •
โœ… ๋ถˆํ•„์š”ํ•œ ์„œ๋น„์Šค ์ž๋™ ๋“ฑ๋ก ์ œ์™ธ (exclude ์„ค์ •)
โœ… OPcache ํ™œ์„ฑํ™”๋กœ ์ปดํŒŒ์ผ๋œ ์ปจํ…Œ์ด๋„ˆ ์บ์‹ฑ
โœ… ์„œ๋น„์Šค๋ฅผ public: false๋กœ ์„ค์ •ํ•ด ์ตœ์ ํ™” ํ—ˆ์šฉ

๐Ÿงช 9. ํ…Œ์ŠคํŠธ ์นœํ™”์ ์ธ DI ํ™œ์šฉ

DI ์ปจํ…Œ์ด๋„ˆ์˜ ๊ฐ€์žฅ ํฐ ์žฅ์  ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์ด์—์š”.
์˜์กด์„ฑ์„ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…ํ•˜๋‹ˆ๊นŒ ํ…Œ์ŠคํŠธ ์‹œ Mock ๊ฐ์ฒด๋กœ ์‰ฝ๊ฒŒ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”!

๐Ÿ”ฌ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ - Mock ์ฃผ์ž…

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;

class CreateUserHandlerTest extends TestCase {
    private UserRepositoryInterface&MockObject $userRepository;
    private PasswordHasherInterface&MockObject $passwordHasher;
    private EventDispatcherInterface&MockObject $eventDispatcher;
    private CreateUserHandler $handler;
    
    protected function setUp(): void {
        // Mock ๊ฐ์ฒด ์ƒ์„ฑ
        $this->userRepository = $this->createMock(UserRepositoryInterface::class);
        $this->passwordHasher = $this->createMock(PasswordHasherInterface::class);
        $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
        
        // ์ง์ ‘ ์˜์กด์„ฑ ์ฃผ์ž…! (์ปจํ…Œ์ด๋„ˆ ์—†์ด๋„ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ)
        $this->handler = new CreateUserHandler(
            $this->userRepository,
            $this->passwordHasher,
            $this->eventDispatcher,
            new NullLogger()  // ์‹ค์ œ ๋กœ๊ฑฐ ๋Œ€์‹  Null ๋กœ๊ฑฐ
        );
    }
    
    public function testCreateUserSuccessfully(): void {
        // Given
        $command = new CreateUserCommand('test@example.com', 'Test User', 'password123');
        
        $this->userRepository
            ->expects($this->once())
            ->method('existsByEmail')
            ->with('test@example.com')
            ->willReturn(false);  // ์ด๋ฉ”์ผ ์ค‘๋ณต ์—†์Œ
        
        $this->passwordHasher
            ->expects($this->once())
            ->method('hash')
            ->willReturn('hashed_password');
        
        $this->userRepository
            ->expects($this->once())
            ->method('save');
        
        $this->eventDispatcher
            ->expects($this->once())
            ->method('dispatch')
            ->with($this->isInstanceOf(UserCreatedEvent::class));
        
        // When
        $user = ($this->handler)($command);
        
        // Then
        $this->assertEquals('test@example.com', $user->getEmail());
        $this->assertEquals('Test User', $user->getName());
    }
    
    public function testThrowsExceptionForDuplicateEmail(): void {
        $command = new CreateUserCommand('existing@example.com', 'User', 'pass');
        
        $this->userRepository
            ->method('existsByEmail')
            ->willReturn(true);  // ์ด๋ฉ”์ผ ์ค‘๋ณต!
        
        $this->expectException(UserAlreadyExistsException::class);
        
        ($this->handler)($command);
    }
}
๐ŸŒ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ - ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ ํ™œ์šฉ

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class UserServiceIntegrationTest extends KernelTestCase {
    private UserService $userService;
    
    protected function setUp(): void {
        // Symfony ํ…Œ์ŠคํŠธ ์ปค๋„ ๋ถ€ํŒ…
        self::bootKernel(['environment' => 'test']);
        
        // ์ปจํ…Œ์ด๋„ˆ์—์„œ ์„œ๋น„์Šค ๊ฐ€์ ธ์˜ค๊ธฐ
        $this->userService = static::getContainer()->get(UserService::class);
    }
    
    public function testFindUser(): void {
        // ์‹ค์ œ ํ…Œ์ŠคํŠธ DB ์‚ฌ์šฉ (test ํ™˜๊ฒฝ)
        $user = $this->userService->findUser(1);
        $this->assertNotNull($user);
    }
}
๐Ÿ’ฌ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ์‹ค์ œ ์ด๋ฉ”์ผ ๋ฐœ์†ก๋˜๋ฉด ์–ด๋–กํ•ด?
๐Ÿค– ใ…‹ใ…‹ใ…‹ ๊ทธ๋ž˜์„œ test ํ™˜๊ฒฝ์—์„œ๋Š” services_test.yaml์—์„œ Mailer๋ฅผ NullMailer๋กœ ๊ต์ฒดํ•ด! ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํ™˜๊ฒฝ๋ณ„๋กœ ๋‹ค๋ฅธ ๊ตฌํ˜„์ฒด ์ฃผ์ž…ํ•ด์ฃผ๋‹ˆ๊นŒ ์ง„์งœ ํŽธํ•จ. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ ๊ฐœ๋ฐœํ•  ๋•Œ ์ด๋Ÿฐ ์„ค์ • ํ•„์ˆ˜์ž„ ใ„นใ…‡

# config/services_test.yaml - ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ์ „์šฉ
services:
    # ์‹ค์ œ ๋ฉ”์ผ๋Ÿฌ ๋Œ€์‹  Null ๋ฉ”์ผ๋Ÿฌ ์‚ฌ์šฉ
    App\Service\MailerInterface:
        alias: App\Service\NullMailer
    
    # ์‹ค์ œ ๊ฒฐ์ œ ๊ฒŒ์ดํŠธ์›จ์ด ๋Œ€์‹  Mock ์‚ฌ์šฉ
    App\Payment\PaymentGatewayInterface:
        alias: App\Payment\MockPaymentGateway
    
    # ์™ธ๋ถ€ API ํด๋ผ์ด์–ธํŠธ Mock
    App\Api\ExternalApiClient:
        alias: App\Api\MockExternalApiClient

๐Ÿ’Ž 10. ์‹ค๋ฌด ํŒ๊ณผ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค
๐Ÿ’ฌ ๊ทผ๋ฐ DI ์ปจํ…Œ์ด๋„ˆ ์“ฐ๋‹ค ๋ณด๋ฉด ์‹ค์ˆ˜ํ•˜๊ธฐ ์‰ฌ์šด ๋ถ€๋ถ„์ด ์žˆ์–ด?
๐Ÿค– ใ…‡ใ…‡ ์ง„์งœ ๋งŽ์Œ ใ…‹ใ…‹ใ…‹ ๋Œ€ํ‘œ์ ์ธ ์•ˆํ‹ฐํŒจํ„ด๋“ค ์•Œ๋ ค์ค„๊ฒŒ!
๐Ÿšซ ์•ˆํ‹ฐํŒจํ„ด 1 - ์„œ๋น„์Šค ๋กœ์ผ€์ดํ„ฐ ํŒจํ„ด

// โŒ ๋‚˜์œ ์˜ˆ - ์„œ๋น„์Šค ๋กœ์ผ€์ดํ„ฐ ํŒจํ„ด (์•ˆํ‹ฐํŒจํ„ด!)
class UserController {
    public function __construct(
        private ContainerInterface $container  // ์ปจํ…Œ์ด๋„ˆ ์ž์ฒด๋ฅผ ์ฃผ์ž…
    ) {}
    
    public function createUser(): Response {
        // ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ง์ ‘ ๊บผ๋‚ด ์”€ - ์˜์กด์„ฑ์ด ์ˆจ๊ฒจ์ง!
        $userService = $this->container->get(UserService::class);
        $mailer = $this->container->get(MailerInterface::class);
        // ...
    }
}

// โœ… ์ข‹์€ ์˜ˆ - ํ•„์š”ํ•œ ์„œ๋น„์Šค๋งŒ ์ง์ ‘ ์ฃผ์ž…
class UserController {
    public function __construct(
        private UserService $userService,
        private MailerInterface $mailer
    ) {}
    // ์˜์กด์„ฑ์ด ๋ช…ํ™•ํ•˜๊ฒŒ ๋“œ๋Ÿฌ๋‚จ!
}
๐Ÿšซ ์•ˆํ‹ฐํŒจํ„ด 2 - ๋„ˆ๋ฌด ๋งŽ์€ ์˜์กด์„ฑ

// โŒ ๋‚˜์œ ์˜ˆ - ์ƒ์„ฑ์ž์— ์˜์กด์„ฑ์ด ๋„ˆ๋ฌด ๋งŽ์Œ (๋‹จ์ผ ์ฑ…์ž„ ์›์น™ ์œ„๋ฐ˜ ์‹ ํ˜ธ!)
class GodService {
    public function __construct(
        private UserRepository $userRepo,
        private OrderRepository $orderRepo,
        private ProductRepository $productRepo,
        private MailerInterface $mailer,
        private CacheInterface $cache,
        private LoggerInterface $logger,
        private EventDispatcherInterface $dispatcher,
        private PaymentGateway $payment,
        private ShippingService $shipping,
        private NotificationService $notification
        // ์ด๋ ‡๊ฒŒ ๋งŽ์œผ๋ฉด ํด๋ž˜์Šค ๋ถ„๋ฆฌ ์‹ ํ˜ธ!
    ) {}
}

// โœ… ์ข‹์€ ์˜ˆ - ์ฑ…์ž„ ๋ถ„๋ฆฌ
class OrderService {
    public function __construct(
        private OrderRepository $orderRepo,
        private PaymentService $paymentService,  // ๊ฒฐ์ œ ๊ด€๋ จ ๋ฌถ์Œ
        private ShippingService $shippingService  // ๋ฐฐ์†ก ๊ด€๋ จ ๋ฌถ์Œ
    ) {}
}
โœ… ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค ๋ชจ์Œ
๐ŸŽฏ Symfony DI ์ปจํ…Œ์ด๋„ˆ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

1. ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•˜๋ผ
๊ตฌ์ฒด ํด๋ž˜์Šค ๋Œ€์‹  ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•ด์„œ ๊ต์ฒด ๊ฐ€๋Šฅ์„ฑ ํ™•๋ณด

2. ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ๊ธฐ๋ณธ์œผ๋กœ
Setter ์ฃผ์ž…๋ณด๋‹ค ์ƒ์„ฑ์ž ์ฃผ์ž…์ด ๋ถˆ๋ณ€์„ฑ ๋ณด์žฅ์— ์œ ๋ฆฌ

3. ์„œ๋น„์Šค๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ private
public: false๊ฐ€ ๊ธฐ๋ณธ๊ฐ’ โ€” ์ปจํ…Œ์ด๋„ˆ ์ตœ์ ํ™”์— ๋„์›€

4. autowire + autoconfigure ํ™œ์šฉ
๋ฐ˜๋ณต์ ์ธ ์„ค์ • ์ค„์ด๊ณ  ์ปจ๋ฒค์…˜ ๊ธฐ๋ฐ˜ ์ž๋™ํ™”

5. ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ๋ถ„๋ฆฌ
dev/test/prod ํ™˜๊ฒฝ๋งˆ๋‹ค ์ ์ ˆํ•œ ๊ตฌํ˜„์ฒด ์‚ฌ์šฉ

6. ์ปดํŒŒ์ผ๋Ÿฌ ํŒจ์Šค๋กœ ํ™•์žฅ
ํƒœ๊ทธ ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค ์ˆ˜์ง‘์œผ๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ ์•„ํ‚คํ…์ฒ˜ ๊ตฌํ˜„
๐Ÿ” ๋””๋ฒ„๊น… ๋„๊ตฌ ํ™œ์šฉ

# ๋“ฑ๋ก๋œ ์„œ๋น„์Šค ๋ชฉ๋ก ํ™•์ธ
php bin/console debug:container

# ํŠน์ • ์„œ๋น„์Šค ์ƒ์„ธ ์ •๋ณด
php bin/console debug:container App\Service\UserService

# ์ž๋™ ์™€์ด์–ด๋ง ๊ฐ€๋Šฅํ•œ ํƒ€์ž… ๋ชฉ๋ก
php bin/console debug:autowiring

# ํŠน์ • ํƒ€์ž…์œผ๋กœ ํ•„ํ„ฐ๋ง
php bin/console debug:autowiring Logger

# ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋ชฉ๋ก
php bin/console debug:event-dispatcher

# ๋ผ์šฐํŒ… ์ •๋ณด
php bin/console debug:router

๐Ÿ†• 11. PHP 8 Attribute๋กœ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ

Symfony 6.x๋ถ€ํ„ฐ๋Š” PHP 8์˜ Attribute๋ฅผ ์ ๊ทน ํ™œ์šฉํ•ด์„œ
YAML ์„ค์ • ์—†์ด๋„ ์„œ๋น„์Šค๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ง„์งœ ๊ฐœํŽธํ•จ ใ„ทใ„ท ๐Ÿ”ฅ


use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
use Symfony\Component\DependencyInjection\Attribute\When;

class NotificationService {
    public function __construct(
        // ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ง์ ‘ ์ฃผ์ž…
        #[Autowire('%env(NOTIFICATION_API_KEY)%')]
        private string $apiKey,
        
        // ํŠน์ • ์„œ๋น„์Šค ๋ช…์‹œ์  ์ฃผ์ž…
        #[Autowire(service: 'app.primary_mailer')]
        private MailerInterface $mailer,
        
        // ํƒœ๊ทธ๋œ ์„œ๋น„์Šค๋“ค ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋กœ ์ฃผ์ž…
        #[TaggedIterator('app.notification_channel')]
        private iterable $channels
    ) {}
}

// ํŠน์ • ํ™˜๊ฒฝ์—์„œ๋งŒ ํ™œ์„ฑํ™”
#[When(env: 'dev')]
class DebugNotificationService extends NotificationService {
    // ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๋Š” ๊ตฌํ˜„์ฒด
}

// ์„œ๋น„์Šค์— ํƒœ๊ทธ ์ž๋™ ์ถ”๊ฐ€
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;

#[AutoconfigureTag('app.notification_channel', ['priority' => 10])]
class EmailNotificationChannel implements NotificationChannelInterface {
    public function send(string $message): void {
        // ์ด๋ฉ”์ผ ๋ฐœ์†ก
    }
}

#[AutoconfigureTag('app.notification_channel', ['priority' => 5])]
class SmsNotificationChannel implements NotificationChannelInterface {
    public function send(string $message): void {
        // SMS ๋ฐœ์†ก
    }
}
๐Ÿ†• Symfony 7.x ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ๋“ค

#[AutowireDecorated] ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ž๋™ ์™€์ด์–ด๋ง
#[AsAlias] ์„œ๋น„์Šค ๋ณ„์นญ ์„ค์ •
#[AsDecorator] ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์„ ์–ธ
#[Exclude] ์ž๋™ ๋“ฑ๋ก์—์„œ ์ œ์™ธ
#[Lazy] ์ง€์—ฐ ๋กœ๋”ฉ ์„ค์ •
#[Target] ๋™์ผ ํƒ€์ž… ์—ฌ๋Ÿฌ ์„œ๋น„์Šค ๊ตฌ๋ถ„

๐ŸŽ“ 12. ์ •๋ฆฌ ๋ฐ ํ•™์Šต ๋กœ๋“œ๋งต
Symfony DI ํ•™์Šต ๋กœ๋“œ๋งต 1๋‹จ๊ณ„ ์˜์กด์„ฑ ์ฃผ์ž… ๊ฐœ๋… ์ดํ•ด 2๋‹จ๊ณ„ ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ณธ ์„œ๋น„์Šค ๋“ฑ๋ก 3๋‹จ๊ณ„ ์ž๋™ ์™€์ด์–ด๋ง YAML ์„ค์ • 4๋‹จ๊ณ„ ์„œ๋น„์Šค ํƒœ๊ทธ ์ปดํŒŒ์ผ๋Ÿฌ ํŒจ์Šค 5๋‹จ๊ณ„ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด ์„œ๋น„์Šค ๋Œ€์ฒด 6๋‹จ๊ณ„ ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜ CQRS ํŒจํ„ด 7๋‹จ๊ณ„ ํ…Œ์ŠคํŠธ ์ „๋žต Mock ํ™œ์šฉ 8๋‹จ๊ณ„ ์„ฑ๋Šฅ ์ตœ์ ํ™” ์šด์˜ ๋ฐฐํฌ ๐ŸŽฏ ์ตœ์ข… ๋ชฉํ‘œ ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๊ณ  ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ๊ธ‰ PHP ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ

Symfony ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜์™€ DI ์ปจํ…Œ์ด๋„ˆ๋Š” ์ฒ˜์Œ์—๋Š” ๋ณต์žกํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ์–ด์š”.
๊ทผ๋ฐ ํ•œ ๋ฒˆ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๊ณ  ๋‚˜๋ฉด ์ฝ”๋“œ ํ’ˆ์งˆ, ์œ ์ง€๋ณด์ˆ˜์„ฑ, ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์ด ์—„์ฒญ๋‚˜๊ฒŒ ์˜ฌ๋ผ๊ฐ€์š”.

ํŠนํžˆ ํŒ€ ํ”„๋กœ์ ํŠธ๋‚˜ ์žฅ๊ธฐ ์šด์˜ ์„œ๋น„์Šค์—์„œ๋Š” ์ด๋Ÿฐ ์•„ํ‚คํ…์ฒ˜๊ฐ€ ์ง„์งœ ๋น›์„ ๋ฐœํ•ด์š”.
์žฌ๋Šฅ๋„ท์ฒ˜๋Ÿผ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์ด ๊ณ„์† ์ถ”๊ฐ€๋˜๋Š” ํ”Œ๋žซํผ์„ ๊ฐœ๋ฐœํ•  ๋•Œ๋„ Symfony์˜ ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์ ‘๊ทผ๋ฒ•์ด ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค ๐Ÿ’ช

์ฒ˜์Œ์—๋Š” ์˜์กด์„ฑ ์ฃผ์ž… ๊ฐœ๋… โ†’ ๊ธฐ๋ณธ ์ปจํ…Œ์ด๋„ˆ ์‚ฌ์šฉ โ†’ ์ž๋™ ์™€์ด์–ด๋ง โ†’ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ ์ˆœ์„œ๋กœ ์ฐจ๊ทผ์ฐจ๊ทผ ์ตํ˜€๊ฐ€์„ธ์š”.
์–ด๋А ์ˆœ๊ฐ„ "์•„ ์ด๋ž˜์„œ Symfony๊ฐ€ PHP ์ƒํƒœ๊ณ„์˜ ์ธํ”„๋ผ๊ตฌ๋‚˜!" ํ•˜๋Š” ์ˆœ๊ฐ„์ด ์˜ฌ ๊ฑฐ์˜ˆ์š” ใ…‹ใ…‹ใ…‹ ๐ŸŽ‰

๐Ÿ“š ์ถ”๊ฐ€ ํ•™์Šต ์ž๋ฃŒ

๊ณต์‹ ๋ฌธ์„œ โ€” symfony.com/doc/current/service_container.html
Symfony Cast โ€” symfonycasts.com (์˜์ƒ ๊ฐ•์˜, ์ง„์งœ ์ข‹์Œ)
PHP-DI โ€” php-di.org (Symfony ์™ธ ๋‹ค๋ฅธ DI ์ปจํ…Œ์ด๋„ˆ ๋น„๊ต ํ•™์Šต)
PSR-11 โ€” Container Interface ํ‘œ์ค€ ์ŠคํŽ™ ์ดํ•ด
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.