2026๋…„ Symfony์™€ Docker: ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๋ถ€ํ„ฐ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ๊นŒ์ง€

2026๋…„์„ ์œ„ํ•œ ์™„์ „ํ•œ Symfony Docker ์›Œํฌํ”Œ๋กœ. FrankenPHP ๊ฐœ๋ฐœ ํ™˜๊ฒฝ, ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€, ์›Œ์ปค ๋ชจ๋“œ, ์•”ํ˜ธํ™”๋œ ์‹œํฌ๋ฆฟ, ์ด๋ฏธ์ง€ ์ตœ์ ํ™”, ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ํฌํ•จํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

2026๋…„ Symfony Docker ํŠœํ† ๋ฆฌ์–ผ: ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ

์ด Symfony Docker ํŠœํ† ๋ฆฌ์–ผ์€ ์™„์ „ํ•œ ์ปจํ…Œ์ด๋„ˆ ์›Œํฌํ”Œ๋กœ๋ฅผ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค. ์žฌํ˜„ ๊ฐ€๋Šฅํ•œ ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ๊ฒฌ๊ณ ํ•˜๊ฒŒ ๋‹ค๋“ฌ์€ ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค๋ฃจ๋ฉฐ, ๋‘ ๊ฐ€์ง€ ๋ชจ๋‘ FrankenPHP์™€ Symfony 7.4 LTS๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. Symfony๋ฅผ ์ปจํ…Œ์ด๋„ˆ์—์„œ ์‹คํ–‰ํ•˜๋ฉด ๋…ธํŠธ๋ถ๊ณผ ์„œ๋ฒ„ ์‚ฌ์ด์— ๋ฐœ์ƒํ•˜๋˜ ๊ณ ์ „์ ์ธ ํ™˜๊ฒฝ ๋ถˆ์ผ์น˜๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ , ๋งค ๋ฐฐํฌ๊ฐ€ ์ทจ์•ฝํ•œ ์ˆ˜์ž‘์—… ์„œ๋ฒ„ ๋ช…๋ น ์‹œํ€€์Šค๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋Œ€์‹  ๋‹จ์ผ ๋ถˆ๋ณ€ ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ๋ฐฐํฌํ•˜๋Š” ์ผ๋กœ ๋ฐ”๋€๋‹ˆ๋‹ค.

2026๋…„ Symfony Docker ์Šคํƒ

๊ณต์‹ Symfony Docker ํ…œํ”Œ๋ฆฟ์€ ์ด์ œ ๋Ÿฐํƒ€์ž„์œผ๋กœ FrankenPHP๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ์ „ํ†ต์ ์ธ Nginx์™€ PHP-FPM ์กฐํ•ฉ์„ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ผ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ HTTP/2์™€ HTTP/3๋ฅผ ์„œ๋น™ํ•˜๊ณ , ์š”์ฒญ๋งˆ๋‹ค ์ปค๋„์„ ๋‹ค์‹œ ๋งŒ๋“œ๋Š” ๋Œ€์‹  ์š”์ฒญ ์‚ฌ์ด์— ์ปค๋„์„ ๋ถ€ํŒ…๋œ ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๋Š” ์˜๊ตฌ ์›Œ์ปค ๋ชจ๋“œ๋กœ Symfony๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

Symfony๋ฅผ ์œ„ํ•œ Docker Compose ๊ฐœ๋ฐœ ํ™˜๊ฒฝ

์ข‹์€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์€ ํ˜ธ์ŠคํŠธ ๋จธ์‹ ์„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ ๋„ ๋ชจ๋“  ๊ธฐ์—ฌ์ž์—๊ฒŒ ๋™์ผํ•œ PHP ๋ฒ„์ „, ๋™์ผํ•œ ํ™•์žฅ, ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Docker Compose๋Š” ๊ทธ ์Šคํƒ์„ ์„ ์–ธ์ ์œผ๋กœ ๊ธฐ์ˆ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ์˜ˆ์ œ๋Š” ๋กœ์ปฌ Dockerfile๋กœ ๋นŒ๋“œํ•œ FrankenPHP ์ปจํ…Œ์ด๋„ˆ๋ฅผ PostgreSQL 17 ์„œ๋น„์Šค์™€ ์ง์ง€์–ด ๊ธฐ๋ณธ Compose ๋„คํŠธ์›Œํฌ๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

yaml
# compose.yaml
services:
  php:
    build:
      context: .
      target: frankenphp_dev
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/app                    # bind mount: edits on the host appear instantly
      - caddy_data:/data           # persist TLS certificates across restarts
    environment:
      DATABASE_URL: "postgresql://app:app@database:5432/app?serverVersion=17"
    depends_on:
      - database

  database:
    image: postgres:17-alpine
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  caddy_data:
  db_data:

php ์„œ๋น„์Šค์˜ ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ๋Š” ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ๋ฅผ ์ปจํ…Œ์ด๋„ˆ ์•ˆ์œผ๋กœ ๋งคํ•‘ํ•˜๋ฏ€๋กœ, ํŒŒ์ผ ๋ณ€๊ฒฝ์ด ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์‹œ ๋นŒ๋“œํ•˜์ง€ ์•Š์•„๋„ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. FrankenPHP๋Š” ์ฒซ ๋ถ€ํŒ… ์‹œ ๋กœ์ปฌ TLS ์ธ์ฆ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ, ์ด ๋•Œ๋ฌธ์— 443 ํฌํŠธ๋ฅผ ๋…ธ์ถœํ•˜๊ณ  caddy_data๋ฅผ ๋ช…๋ช…๋œ ๋ณผ๋ฅจ์œผ๋กœ ๋‘๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ธ์ฆ์„œ๊ฐ€ docker compose down ์ดํ›„์—๋„ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. DSN์˜ serverVersion=17 ํžŒํŠธ๋Š” Doctrine์ด ์—ฐ๊ฒฐ๋งˆ๋‹ค ๋ฒ„์ „ ๊ฐ์ง€ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์ƒ๋žตํ•˜๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

์Šคํƒ์ด ์‹คํ–‰๋˜๋ฉด ๋ชจ๋“  Symfony ๋ช…๋ น์€ ํ˜ธ์ŠคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ php ์ปจํ…Œ์ด๋„ˆ ์•ˆ์—์„œ ์‹คํ–‰๋˜๋ฉฐ, ์ด๋Š” ์˜ฌ๋ฐ”๋ฅธ PHP ๋ฒ„์ „๊ณผ ํ™•์žฅ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑ, ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํ–‰, ์…ธ ์—ด๊ธฐ ๋ชจ๋‘ docker compose exec๋ฅผ ๊ฑฐ์น˜๋Š” ๋™์ผํ•œ ํŒจํ„ด์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

์ปจํ…Œ์ด๋„ˆ์—์„œ ์ฝ˜์†” ๋ช…๋ น ์‹คํ–‰ํ•˜๊ธฐ

Symfony์™€ Composer ํ˜ธ์ถœ ์•ž์— docker compose exec php๋ฅผ ๋ถ™์ด๋ฉด ์ปจํ…Œ์ด๋„ˆ์˜ PHP ๋Ÿฐํƒ€์ž„์„ ๋Œ€์ƒ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด docker compose exec php bin/console make:entity๋‚˜ docker compose exec php composer require symfony/uid์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ˜ธ์ŠคํŠธ์—๋Š” PHP๋ฅผ ์„ค์น˜ํ•  ํ•„์š”๊ฐ€ ์ „ํ˜€ ์—†์Šต๋‹ˆ๋‹ค.

Symfony ํ”„๋กœ๋•์…˜์„ ์œ„ํ•œ ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ Dockerfile

๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ Dockerfile์€ ๊ณตํ†ต ๋ฒ ์ด์Šค๋ฅผ ๊ณต์œ ํ•œ ๋’ค ๊ฐœ๋ฐœ ํƒ€๊นƒ๊ณผ ํ”„๋กœ๋•์…˜ ํƒ€๊นƒ์œผ๋กœ ๊ฐˆ๋ผ์ง‘๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ์Šคํ…Œ์ด์ง€๋Š” ๋Ÿฐํƒ€์ž„ ์˜์กด์„ฑ๋งŒ ์„ค์น˜ํ•˜๊ณ , Composer ๊ฐœ๋ฐœ ํŒจํ‚ค์ง€๋ฅผ ์ œ๊ฑฐํ•˜๋ฉฐ, ๋นŒ๋“œ ์‹œ์ ์— ์บ์‹œ๋ฅผ ์›Œ๋ฐ์—…ํ•˜์—ฌ ์‹คํ–‰ ์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ฆ‰์‹œ ๊ธฐ๋™๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

dockerfile
# Dockerfile
FROM dunglas/frankenphp:1-php8.4 AS base

WORKDIR /app

# Install the PHP extensions Symfony relies on
RUN install-php-extensions \
    intl \
    opcache \
    pdo_pgsql \
    zip

COPY --from=composer/composer:2-bin /composer /usr/bin/composer

# --- Development target ---
FROM base AS frankenphp_dev
ENV APP_ENV=dev
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"

# --- Production target ---
FROM base AS frankenphp_prod
ENV APP_ENV=prod
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
ENV APP_RUNTIME="Runtime\FrankenPhpSymfony\Runtime"
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

# Layer caching: copy manifests first, install, then copy the source
COPY composer.json composer.lock symfony.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist

COPY . .
RUN composer dump-autoload --no-dev --classmap-authoritative \
    && composer dump-env prod \
    && bin/console cache:warmup

๋‚˜๋จธ์ง€ ์†Œ์Šค๋ณด๋‹ค composer.json๊ณผ ๋ฝ ํŒŒ์ผ์„ ๋จผ์ € ๋ณต์‚ฌํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ ์ตœ์ ํ™”์ž…๋‹ˆ๋‹ค. Docker๋Š” ์˜์กด์„ฑ ์„ค์น˜ ๋ ˆ์ด์–ด๋ฅผ ์บ์‹œํ•˜๋ฉฐ, ๋งค ์ฝ”๋“œ ์ˆ˜์ •์ด ์•„๋‹ˆ๋ผ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งŒ ์ด ๋ ˆ์ด์–ด๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. --classmap-authoritative ํ”Œ๋ž˜๊ทธ๋Š” ํด๋ž˜์Šค๋งต์ด ์™„์ „ํ•˜๋‹ค๊ณ  Composer์— ์•Œ๋ ค์ฃผ๋ฏ€๋กœ, ์˜คํ† ๋กœ๋”๊ฐ€ ๋Ÿฐํƒ€์ž„์— ํŒŒ์ผ์‹œ์Šคํ…œ ์กฐํšŒ๋กœ ๋˜๋Œ์•„๊ฐ€๋Š” ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค. ๋นŒ๋“œ ์ค‘์— ์บ์‹œ๋ฅผ ์›Œ๋ฐ์—…ํ•˜๋ฉด ์ฒซ ํ”„๋กœ๋•์…˜ ์š”์ฒญ์ด ์™„์ „ํžˆ ์ปดํŒŒ์ผ๋œ ์ปจํ…Œ์ด๋„ˆ์— ๋„๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

FrankenPHP ์›Œ์ปค ๋ชจ๋“œ์™€ Symfony ๋Ÿฐํƒ€์ž„

์ „ํ†ต์ ์ธ PHP-FPM์€ Symfony ์ปค๋„์„ ๋ถ€ํŒ…ํ•˜๊ณ , ์š”์ฒญ ํ•˜๋‚˜๋ฅผ ์ฒ˜๋ฆฌํ•œ ๋’ค, ๋ชจ๋“  ๊ฒƒ์„ ํ๊ธฐํ•ฉ๋‹ˆ๋‹ค. ์›Œ์ปค ๋ชจ๋“œ๋Š” ์ปค๋„๊ณผ ์˜์กด์„ฑ ์ฃผ์ž… ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์š”์ฒญ ์‚ฌ์ด์— ์‚ด์•„ ์žˆ๊ฒŒ ์œ ์ง€ํ•˜๋ฉฐ, ์—ฌ๊ธฐ์„œ ๋Œ€๋ถ€๋ถ„์˜ ์ง€์—ฐ ์‹œ๊ฐ„ ์ ˆ๊ฐ์ด ๋‚˜์˜ต๋‹ˆ๋‹ค. runtime/frankenphp-symfony ํŒจํ‚ค์ง€๋Š” Symfony Runtime ์ปดํฌ๋„ŒํŠธ์™€ ํ‘œ์ค€ public/index.php ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์„ ํ†ตํ•ด ์ด๋ฅผ ๋™์ž‘์‹œํ‚ต๋‹ˆ๋‹ค.

public/index.phpphp
use App\Kernel;

require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

return function (array $context) {
    return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};

Dockerfile์— ์„ค์ •๋œ FRANKENPHP_CONFIG="worker ./public/index.php" ๋ผ์ธ์ด ์ด ๋ฃจํ”„๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์žฌ์‚ฌ์šฉ๋˜๋ฏ€๋กœ, ์š”์ฒญ๋ณ„ ์ƒํƒœ๋ฅผ ๋ณด์œ ํ•˜๋Š” ์„œ๋น„์Šค๋Š” ์š”์ฒญ ์‚ฌ์ด์— ์Šค์Šค๋กœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Symfony๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์„œ๋น„์Šค๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ, ์ปค์Šคํ…€ ์ƒํƒœ ์œ ์ง€ ์„œ๋น„์Šค๋Š” Symfony\Contracts\Service\ResetInterface๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  kernel.reset ํƒœ๊ทธ๋ฅผ ๋ถ™์—ฌ์•ผ ๋ˆ„์ ๋œ ์ƒํƒœ๊ฐ€ ๋‹ค์Œ ์š”์ฒญ์œผ๋กœ ์ƒˆ์–ด ๋‚˜๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์žฅ์‹œ๊ฐ„ ์‹คํ–‰๋˜๋Š” Messenger ์›Œ์ปค๊ฐ€ ์š”๊ตฌํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๊ทœ์œจ์ด๋ฉฐ, ๊ทธ ๋ณด์ƒ์€ ๋™์ผํ•œ ํ•˜๋“œ์›จ์–ด์—์„œ PHP-FPM๋ณด๋‹ค ๋ช‡ ๋ฐฐ ๋†’์€ ์š”์ฒญ ์ฒ˜๋ฆฌ๋Ÿ‰์ž…๋‹ˆ๋‹ค.

๋กœ์ปฌ ๊ฐœ๋ฐœ์—์„œ๋Š” --watch ํ”Œ๋ž˜๊ทธ๋กœ FrankenPHP๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ํŒŒ์ผ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ์›Œ์ปค๊ฐ€ ์ž๋™์œผ๋กœ ์žฌ์‹œ์ž‘๋˜๋ฏ€๋กœ, ์›Œ์ปค ๋ชจ๋“œ๊ฐ€ ํŽธ์ง‘-์ƒˆ๋กœ๊ณ ์นจ ๋ฃจํ”„๋ฅผ ๋ฐฉํ•ดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Symfony์—์„œ FrankenPHP์™€ PHP-FPM ๋น„๊ต

FrankenPHP์™€ ๊ณ ์ „์ ์ธ Nginx + PHP-FPM ์Šคํƒ ์ค‘ ๋ฌด์—‡์„ ์„ ํƒํ•˜๋А๋ƒ๋Š” Dockerfile๊ณผ ๋Ÿฐํƒ€์ž„ ๋™์ž‘์„ ๋ชจ๋‘ ์ขŒ์šฐํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ํ‘œ๋Š” Symfony ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ์˜ ์‹ค์งˆ์ ์ธ ์ฐจ์ด๋ฅผ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

| ํ•ญ๋ชฉ | FrankenPHP ์›Œ์ปค ๋ชจ๋“œ | Nginx + PHP-FPM | |--------|------------------------|-----------------| | ์ปจํ…Œ์ด๋„ˆ | ํ•˜๋‚˜ | ๋‘˜ (์›น ์„œ๋ฒ„ + PHP) | | ์ปค๋„ ๋ถ€ํŒ… | ์›Œ์ปค๋‹น ํ•œ ๋ฒˆ | ์š”์ฒญ๋‹น ํ•œ ๋ฒˆ | | HTTP/3 | ๋‚ด์žฅ | ์ถ”๊ฐ€ ์„ค์ • ํ•„์š” | | TLS | ์ž๋™ (Caddy) | ์ˆ˜๋™ ์ธ์ฆ์„œ ์„ค์ • | | ์ƒํƒœ ์ฒ˜๋ฆฌ | ์š”์ฒญ ์‚ฌ์ด์— ์ดˆ๊ธฐํ™” ํ•„์š” | ์š”์ฒญ๋ณ„ ๊ฒฉ๋ฆฌ | | ์ฝœ๋“œ ์Šคํƒ€ํŠธ ๋น„์šฉ | ๋ถ€ํŒ… ์‹œ ํ•œ ๋ฒˆ ์ง€๋ถˆ | ๋งค ์š”์ฒญ๋งˆ๋‹ค ์ง€๋ถˆ |

์›Œ์ปค ๋ชจ๋“œ๋Š” ๊ฐ’๋น„์‹ผ ์ปค๋„ ๋ฐ ์ปจํ…Œ์ด๋„ˆ ์ปดํŒŒ์ผ ๋‹จ๊ณ„๋ฅผ ์ˆ˜์ฒœ ๊ฑด์˜ ์š”์ฒญ์— ๊ฑธ์ณ ๋ถ„์‚ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฒ˜๋ฆฌ๋Ÿ‰์—์„œ ์šฐ์œ„๋ฅผ ์ ํ•ฉ๋‹ˆ๋‹ค. PHP-FPM์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ „์—ญ ๋ณ€์ˆ˜์— ์˜์กดํ•˜๊ฑฐ๋‚˜ ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ „์ œ๋กœ ํ•˜๋Š” ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์—ฌ์ „ํžˆ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ์ฝ”๋“œ๋Š” ์žฌ์‚ฌ์šฉ๋˜๋Š” ์›Œ์ปค์—์„œ ๊นจ์ง€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์›Œ์ปค ๋ชจ๋“œ์˜ ์ƒํƒœ ๋ˆ„์ˆ˜

์š”์ฒญ ๋ฐ์ดํ„ฐ๋ฅผ ํ”„๋ผ์ด๋น— ์†์„ฑ์— ์บ์‹œํ•˜๋Š” ์„œ๋น„์Šค๋Š” ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์œผ๋ฉด ํ•œ ์š”์ฒญ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์Œ ์š”์ฒญ์— ๊ทธ๋Œ€๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜์—์„œ ์›Œ์ปค ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•˜๊ธฐ ์ „์— ์‹ฑ๊ธ€ํ„ด, ์ด๋ฒคํŠธ ๊ตฌ๋…์ž, ๊ทธ๋ฆฌ๊ณ  ๋ณด์•ˆ ํ† ํฐ์ด๋‚˜ ํ˜„์žฌ ์š”์ฒญ์„ ๋ณด์œ ํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ์„ ์ ๊ฒ€ํ•˜์‹ญ์‹œ์˜ค.

Symfony ๋ฉด์ ‘ ์ค€๋น„๊ฐ€ ๋˜์…จ๋‚˜์š”?

์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ, flashcards, ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์—ฐ์Šตํ•˜์„ธ์š”.

ํ”„๋กœ๋•์…˜์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜์™€ ์‹œํฌ๋ฆฟ ๊ด€๋ฆฌํ•˜๊ธฐ

Symfony๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ์„ค์ •์„ ์ฝ์–ด๋“ค์ด๋ฉฐ, ํ”„๋กœ๋•์…˜์—์„œ ์ด๋ฅผ ๊ณต๊ธ‰ํ•˜๋Š” ๊ฒฌ์‹คํ•œ ๋ฐฉ๋ฒ•์ด ๋‘ ๊ฐ€์ง€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ๋‚˜ Compose ํŒŒ์ผ์„ ํ†ตํ•ด ์ผ๋ฐ˜ ๋ณ€์ˆ˜๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ๋Š” Symfony์˜ ์•”ํ˜ธํ™”๋œ ์‹œํฌ๋ฆฟ ๋ณผํŠธ๋กœ, ๋ฏผ๊ฐํ•œ ๊ฐ’์„ ์•”ํ˜ธ๋ฌธ์œผ๋กœ ์ €์žฅ์†Œ์— ์ €์žฅํ–ˆ๋‹ค๊ฐ€ ๋Ÿฐํƒ€์ž„์— ๋‹จ์ผ ๊ฐœ์ธ ํ‚ค๋กœ ๋ณตํ˜ธํ™”ํ•ฉ๋‹ˆ๋‹ค.

bash
# Generate the keypair; commit the public key, keep the private key out of the image
php bin/console secrets:generate-keys

# Encrypt a value into config/secrets/prod/
php bin/console secrets:set DATABASE_URL

# In production, expose only the decryption key to the container
export SYMFONY_DECRYPTION_SECRET="$(cat config/secrets/prod/prod.decrypt.private.php)"

Dockerfile์˜ composer dump-env prod ๋‹จ๊ณ„๋Š” .env ํŒŒ์ผ๋“ค์„ ์ตœ์ ํ™”๋œ ๋‹จ์ผ .env.local.php๋กœ ์ปดํŒŒ์ผํ•˜์—ฌ, ๋ถ€ํŒ… ์‹œ dotenv ํŒŒ์ผ์„ ํŒŒ์‹ฑํ•˜๋Š” ๋Ÿฐํƒ€์ž„ ๋น„์šฉ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์— ์„ค์ •๋œ ๋ณ€์ˆ˜๋Š” ์—ฌ์ „ํžˆ ์ปดํŒŒ์ผ๋œ ๊ธฐ๋ณธ๊ฐ’์„ ์žฌ์ •์˜ํ•˜๋ฏ€๋กœ, ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ๊ฐ€ ์ œ๊ณตํ•œ ์‹œํฌ๋ฆฟ์ด ํ•ญ์ƒ ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค. Symfony ๋ฐฐํฌ ๊ฐ€์ด๋“œ๋Š” ์ „์ฒด ์šฐ์„ ์ˆœ์œ„ ์ˆœ์„œ๋ฅผ ๋ฌธ์„œํ™”ํ•˜์ง€๋งŒ, ์‹ค์šฉ์ ์ธ ๊ทœ์น™์€ ๋‹จ์ˆœํ•ฉ๋‹ˆ๋‹ค. APP_SECRET์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž๊ฒฉ ์ฆ๋ช…์„ ์ ˆ๋Œ€ ์ด๋ฏธ์ง€์— ๊ตฝ์ง€ ๋ง๊ณ , ์ปจํ…Œ์ด๋„ˆ ์‹œ์ž‘ ์‹œ์ ์— ์ฃผ์ž…ํ•˜์‹ญ์‹œ์˜ค.

Symfony ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€ ์ตœ์ ํ™”

๋‘ ๊ฐ€์ง€ OPcache ์„ค์ •์ด ํ”„๋กœ๋•์…˜ ์„ฑ๋Šฅ ๊ฒฉ์ฐจ์˜ ๋Œ€๋ถ€๋ถ„์„ ์ฐจ์ง€ํ•ฉ๋‹ˆ๋‹ค. ํƒ€์ž„์Šคํƒฌํ”„ ๊ฒ€์ฆ ๋น„ํ™œ์„ฑํ™”์™€ ํ”„๋ฆฌ๋กœ๋”ฉ ํ™œ์„ฑํ™”์ž…๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋Š” ๋ถˆ๋ณ€์ด๋ฏ€๋กœ ์†Œ์Šค๊ฐ€ ๋Ÿฐํƒ€์ž„์— ๋ฐ”๋€Œ์ง€ ์•Š์œผ๋ฉฐ, ๋”ฐ๋ผ์„œ OPcache๊ฐ€ ํŽธ์ง‘ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋ ค๊ณ  ํŒŒ์ผ์„ stat ํ•  ์ด์œ ๊ฐ€ ์ „ํ˜€ ์—†์Šต๋‹ˆ๋‹ค. ํ”„๋ฆฌ๋กœ๋”ฉ์€ ํ•œ ๊ฑธ์Œ ๋” ๋‚˜์•„๊ฐ€ ์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ Symfony์™€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํด๋ž˜์Šค๋ฅผ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ์— ํ•œ ๋ฒˆ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

ini
; docker/php/prod.ini
opcache.enable=1
opcache.preload=/app/var/cache/prod/App_KernelProdContainer.preload.php
opcache.preload_user=root
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0     ; immutable image: never re-check the filesystem
realpath_cache_size=4096K
realpath_cache_ttl=600

Symfony๋Š” ์œ„์— ๋‚˜์—ด๋œ preload.php ํŒŒ์ผ์„ cache:warmup ์ค‘์— ์ƒ์„ฑํ•˜๋ฏ€๋กœ, ๋นŒ๋“œ ์ดํ›„ ์ด๋ฏธ์ง€ ์•ˆ์— ์ด ํŒŒ์ผ์ด ์ด๋ฏธ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. OPcache ํ”„๋ฆฌ๋กœ๋”ฉ์€ ์‹œ์ž‘ ์‹œ ์ด ํด๋ž˜์Šค๋“ค์„ ๋งํฌํ•˜๊ณ , ํ•ด๋‹น ํด๋ž˜์Šค๊ฐ€ ํ•„์š”ํ•œ ์ฒซ ์š”์ฒญ์—์„œ ์ปดํŒŒ์ผ ๋‹จ๊ณ„๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค. validate_timestamps=0 ์„ค์ •์€ ์ƒˆ ๋ฆด๋ฆฌ์Šค๊ฐ€ ๊ณง ์ƒˆ ์ด๋ฏธ์ง€๋ฅผ ์˜๋ฏธํ•˜๋Š” ๋ถˆ๋ณ€ ๋ฐฐํฌ์—์„œ๋งŒ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋ณ€ ํ˜ธ์ŠคํŠธ์—์„œ๋Š” ์˜ค๋ž˜๋œ ์ฝ”๋“œ๋ฅผ ์ œ๊ณตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. max_accelerated_files๋ฅผ ์‹ค์ œ ํŒŒ์ผ ์ˆ˜๋ณด๋‹ค ํฌ๊ฒŒ ์žก์œผ๋ฉด ํ”„๋ ˆ์ž„์›Œํฌ ์ „์ฒด๊ฐ€ ์ถ•์ถœ ์—†์ด ์บ์‹œ๋œ ์ƒํƒœ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

.dockerignore๋กœ Symfony ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ค„์ด๊ธฐ

.dockerignore ํŒŒ์ผ์ด ์—†์œผ๋ฉด ๋นŒ๋“œ๊ฐ€ ์กฐ์šฉํžˆ ๋น„๋Œ€ํ•ด์ง‘๋‹ˆ๋‹ค. ์ด ํŒŒ์ผ์ด ์—†์œผ๋ฉด COPY . . ๋‹จ๊ณ„๊ฐ€ ๋กœ์ปฌ vendor ๋””๋ ‰ํ„ฐ๋ฆฌ, ๊ฐœ๋ฐœ์šฉ var/cache, ๋…ธ๋“œ ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ, ๊ทธ๋ฆฌ๊ณ  .git ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์ด๋ฏธ์ง€์™€ ๋นŒ๋“œ ์ปจํ…์ŠคํŠธ๋กœ ์‹ค์–ด ๋‚˜๋ฆ…๋‹ˆ๋‹ค. ์ด๊ฒƒ๋“ค์„ ์ œ์™ธํ•˜๋ฉด ์ด๋ฏธ์ง€๊ฐ€ ์ž‘์•„์ง€๊ณ , ๋นŒ๋“œ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฐ๋ชฌ์œผ๋กœ ์—…๋กœ๋“œํ•˜๋Š” ์†๋„๊ฐ€ ๋นจ๋ผ์ง€๋ฉฐ, ๊ฐœ๋ฐœ ์‚ฐ์ถœ๋ฌผ์ด ๋ฐฉ๊ธˆ ์„ค์น˜ํ•œ ํ”„๋กœ๋•์…˜ ์˜์กด์„ฑ์„ ๋ฎ์–ด์“ฐ๋Š” ์ผ์„ ๋ง‰์•„์ค๋‹ˆ๋‹ค.

gitignore
# .dockerignore
/.git/
/vendor/
/node_modules/
/var/
/.env.local
/.env.*.local
/tests/
/docker/
compose*.yaml
Dockerfile

/vendor/๋ฅผ ๋ฌด์‹œํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ์Šคํ…Œ์ด์ง€๋Š” composer install --no-dev๋ฅผ ์‹คํ–‰ํ•˜๋ฏ€๋กœ, ํ˜ธ์ŠคํŠธ์˜ ๊ฐœ๋ฐœ์šฉ vendor ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์‹ค์–ด ๋‚˜๋ฅด๋ฉด ๊ทธ ๋‹จ๊ณ„๊ฐ€ ์™„์ „ํžˆ ๋ฌด์˜๋ฏธํ•ด์ง‘๋‹ˆ๋‹ค. /var/๋ฅผ ์ œ์™ธํ•˜๋ฉด ๊ฐœ๋ฐœ ์บ์‹œ์™€ ๋กœ๊ทธ๊ฐ€ ์ด๋ฏธ์ง€์—์„œ ๋น ์ ธ, ๋นŒ๋“œ ์‹œ์ ์˜ cache:warmup์ด ๊นจ๋—ํ•œ ํ”„๋กœ๋•์…˜ ์บ์‹œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ์™€ Alpine ๊ธฐ๋ฐ˜ ํ™•์žฅ์„ ๊ฒฐํ•ฉํ•˜๋ฉด, ๊ตฐ๋”๋”๊ธฐ ์—†๋Š” Symfony ์ด๋ฏธ์ง€๋Š” ๋ณดํ†ต 150 MB๋ฅผ ๋„‰๋„‰ํžˆ ๋ฐ‘๋•๋‹ˆ๋‹ค.

Symfony ์ปจํ…Œ์ด๋„ˆ ๋ฐฐํฌ์™€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํ–‰

ํ”„๋กœ๋•์…˜ Compose ํŒŒ์ผ์€ ๋กœ์ปฌ์—์„œ ๋นŒ๋“œํ•˜๋Š” ๋Œ€์‹  ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์˜ ์‚ฌ์ „ ๋นŒ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•˜๋ฉฐ, ์‘๋‹ตํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ๋กœ๋งŒ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ๊ฐ€ ํŠธ๋ž˜ํ”ฝ์„ ๋ผ์šฐํŒ…ํ•˜๋„๋ก ํ—ฌ์Šค ์ฒดํฌ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์‹œํฌ๋ฆฟ์€ ์ด๋ฏธ์ง€ ๋ ˆ์ด์–ด๊ฐ€ ์•„๋‹ˆ๋ผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ๋„์ฐฉํ•ฉ๋‹ˆ๋‹ค.

yaml
# compose.prod.yaml
services:
  php:
    image: registry.example.com/app:${TAG}
    environment:
      APP_ENV: prod
      APP_SECRET: ${APP_SECRET}
      DATABASE_URL: ${DATABASE_URL}
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost/health"]
      interval: 10s
      timeout: 3s
      retries: 3
    restart: unless-stopped

ํ—ฌ์Šค ์ฒดํฌ๋Š” /health ๋ผ์šฐํŠธ๋ฅผ curl ํ•˜๋ฏ€๋กœ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ด ๋ผ์šฐํŠธ๋ฅผ ๋…ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  200์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ตœ์†Œํ•œ์˜ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์ฒดํฌ๋ฅผ ๋น ๋ฅด๊ฒŒ ์œ ์ง€ํ•˜๊ณ , ์ผ์‹œ์ ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์žฅ์• ๊ฐ€ ์ผ์–ด๋‚˜๋„ ์ปจํ…Œ์ด๋„ˆ๋ฅผ unhealthy๋กœ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ค€๋น„ ์ƒํƒœ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค๋ฉด ๋‘ ๋ฒˆ์งธ ์‹ฌ์ธต ํ”„๋กœ๋ธŒ๊ฐ€ ๊ฐ€๋ฒผ์šด ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ผ์ด๋ธŒ๋‹ˆ์Šค ์—”๋“œํฌ์ธํŠธ ์ž์ฒด๋Š” ์ž๋ช…ํ•˜๊ฒŒ ๋‹จ์ˆœํ•œ ์ƒํƒœ๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

src/Controller/HealthController.phpphp
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;

final class HealthController extends AbstractController
{
    #[Route('/health', name: 'health', methods: ['GET'])]
    public function __invoke(): JsonResponse
    {
        return new JsonResponse(['status' => 'ok']);
    }
}

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ถ€ํŒ… ์•ˆ์ด ์•„๋‹ˆ๋ผ, ์ƒˆ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํŠธ๋ž˜ํ”ฝ์„ ๋ฐ›๊ธฐ ์ „์— ๋ณ„๊ฐœ์˜ ๋‹จ๊ณ„๋กœ ์‹คํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ผํšŒ์„ฑ ์ปจํ…Œ์ด๋„ˆ์—์„œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•˜๋ฉด, ์—ฌ๋Ÿฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆํ”Œ๋ฆฌ์นด๊ฐ€ ๋ณ‘๋ ฌ๋กœ ์‹œ์ž‘๋˜๋”๋ผ๋„ ๋ฆด๋ฆฌ์Šค๋งˆ๋‹ค ์ •ํ™•ํžˆ ํ•œ ๋ฒˆ์”ฉ ์Šคํ‚ค๋งˆ๊ฐ€ ์ตœ์‹  ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

bash
# deploy.sh
set -euo pipefail

docker compose -f compose.prod.yaml pull

# Apply migrations once, in an isolated container
docker compose -f compose.prod.yaml run --rm php \
  bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration

# Start replicas and wait for the health check to pass
docker compose -f compose.prod.yaml up -d --wait

--wait ํ”Œ๋ž˜๊ทธ๋Š” ๋ชจ๋“  ์„œ๋น„์Šค๊ฐ€ healthy๋ฅผ ๋ณด๊ณ ํ•  ๋•Œ๊นŒ์ง€ ๋ธ”๋กœํ‚นํ•˜์—ฌ, ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ์— ๋ฐœ์‚ฌ ํ›„ ๋ฐฉ์น˜์‹ ์‹œ์ž‘์ด ์•„๋‹ˆ๋ผ ์ง„์งœ ์„ฑ๊ณต ์‹ ํ˜ธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ Kubernetes๋‚˜ Docker Swarm ๊ฐ™์€ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ์˜ ๋กค๋ง ์—…๋ฐ์ดํŠธ์™€ ๊ฒฐํ•ฉํ•˜๋ฉด ๋ฌด์ค‘๋‹จ ๋ฆด๋ฆฌ์Šค๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ƒˆ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํ—ฌ์Šค ์ฒดํฌ๋ฅผ ํ†ต๊ณผํ•  ๋•Œ๊นŒ์ง€ ๊ธฐ์กด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ณ„์† ์„œ๋น™ํ•ฉ๋‹ˆ๋‹ค. ์ด ์›Œํฌํ”Œ๋กœ๊ฐ€ ๋Œ€์ƒ์œผ๋กœ ์‚ผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒ„์ „์„ ๋” ํญ๋„“๊ฒŒ ๋ณด๋ ค๋ฉด, Symfony ํ”Œ๋žซํผ ๊ฐœ์š”์™€ Symfony 8 ๋ฐ PHP 8.4 ๊ธฐ๋Šฅ ๊ฐ€์ด๋“œ๊ฐ€ ํ˜„์žฌ ๋ฆด๋ฆฌ์Šค ๋ผ์ธ์—์„œ ๋ฌด์—‡์ด ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

  • ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ Dockerfile์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ ์ด๋ฏธ์ง€์™€ ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€๊ฐ€ ํ•˜๋‚˜์˜ ๋ฒ ์ด์Šค๋ฅผ ๊ณต์œ ํ•˜๊ฒŒ ํ•˜๋˜, ํ”„๋กœ๋•์…˜ ํƒ€๊นƒ์€ Composer ๊ฐœ๋ฐœ ์˜์กด์„ฑ ์—†์ด ๋ฐฐํฌํ•˜์‹ญ์‹œ์˜ค
  • composer.json๊ณผ ๋ฝ ํŒŒ์ผ์„ ์†Œ์Šค๋ณด๋‹ค ๋จผ์ € ๋ณต์‚ฌํ•˜์—ฌ ์˜์กด์„ฑ ์„ค์น˜ ๋ ˆ์ด์–ด๋ฅผ ์ฝ”๋“œ ๋ณ€๊ฒฝ์— ๊ฑธ์ณ ์บ์‹œ๋œ ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜์‹ญ์‹œ์˜ค
  • FrankenPHP๋ฅผ ์›Œ์ปค ๋ชจ๋“œ๋กœ ์‹คํ–‰ํ•˜์—ฌ ๋ถ€ํŒ…๋œ ์ปค๋„์„ ์š”์ฒญ ์‚ฌ์ด์— ์žฌ์‚ฌ์šฉํ•˜๊ณ , ์ƒํƒœ ์œ ์ง€ ์„œ๋น„์Šค๋Š” ResetInterface๋กœ ์ดˆ๊ธฐํ™”ํ•˜์—ฌ ์ƒํƒœ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜์‹ญ์‹œ์˜ค
  • ๋นŒ๋“œ ์‹œ์ ์— Symfony ์บ์‹œ๋ฅผ ์›Œ๋ฐ์—…ํ•˜๊ณ  dump-env prod๋กœ .env๋ฅผ ์ปดํŒŒ์ผํ•˜์—ฌ, ์‹คํ–‰ ์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”๋œ ์ƒํƒœ๋กœ ๊ธฐ๋™๋˜๊ฒŒ ํ•˜์‹ญ์‹œ์˜ค
  • ํ”„๋กœ๋•์…˜์—์„œ OPcache ํ”„๋ฆฌ๋กœ๋”ฉ์„ ํ™œ์„ฑํ™”ํ•˜๊ณ  validate_timestamps=0์„ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค. ์ด๋Š” ์ด๋ฏธ์ง€๊ฐ€ ๋ถˆ๋ณ€์ด๊ธฐ ๋•Œ๋ฌธ์— ์ •ํ™•ํžˆ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค
  • APP_SECRET, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž๊ฒฉ ์ฆ๋ช…, ๋ณผํŠธ ๋ณตํ˜ธํ™” ํ‚ค๋ฅผ ์ด๋ฏธ์ง€ ๋ ˆ์ด์–ด ๋ฐ–์— ๋‘๊ณ , ์ปจํ…Œ์ด๋„ˆ ์‹œ์ž‘ ์‹œ์ ์— ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์ฃผ์ž…ํ•˜์‹ญ์‹œ์˜ค
  • Doctrine ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ƒˆ ๋ ˆํ”Œ๋ฆฌ์นด๊ฐ€ ํŠธ๋ž˜ํ”ฝ์„ ๋ฐ›๊ธฐ ์ „์— ์ผํšŒ์„ฑ ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ ์šฉํ•˜๊ณ , up -d --wait๋กœ ํ—ฌ์Šค ์ฒดํฌ์— ๋กค์•„์›ƒ์„ ๊ฑธ์–ด๋‘์‹ญ์‹œ์˜ค

์—ฐ์Šต์„ ์‹œ์ž‘ํ•˜์„ธ์š”!

๋ฉด์ ‘ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์™€ ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์ง€์‹์„ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

ํƒœ๊ทธ

#symfony
#docker
#frankenphp
#php
#deployment
#devops

๊ณต์œ 

๊ด€๋ จ ๊ธฐ์‚ฌ

Symfony 8 ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ๊ณผ PHP 8.4 ๋ ˆ์ด์ง€ ์˜ค๋ธŒ์ ํŠธ

Symfony 8 ์™„๋ฒฝ ๊ฐ€์ด๋“œ: PHP 8.4 ๋ ˆ์ด์ง€ ์˜ค๋ธŒ์ ํŠธ, ๋ฉ€ํ‹ฐ์Šคํ… ํผ, 2026๋…„ ๋ฉด์ ‘ ๋Œ€๋น„๊นŒ์ง€

Symfony 8์€ PHP 8.4๋ฅผ ํ•„์ˆ˜๋กœ ์š”๊ตฌํ•˜๋ฉฐ ๋„ค์ดํ‹ฐ๋ธŒ ๋ ˆ์ด์ง€ ์˜ค๋ธŒ์ ํŠธ, AbstractFlowType, ํ˜ธ์ถœ ๊ฐ€๋Šฅ ์ปค๋งจ๋“œ ๋“ฑ ๋‹ค์ˆ˜์˜ ์‹ ๊ธฐ๋Šฅ์„ ํƒ‘์žฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฃผ์š” ๊ธฐ๋Šฅ์„ ์ฝ”๋“œ ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ๋ถ„์„ํ•˜๊ณ  2026๋…„ ๋ฉด์ ‘ ๋Œ€๋น„ ํฌ์ธํŠธ๋ฅผ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Symfony์—์„œ Doctrine ORM ๊ด€๊ณ„ - ์™„๋ฒฝ ๊ฐ€์ด๋“œ

Doctrine ORM: Symfony์—์„œ ๊ด€๊ณ„ ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

Symfony์˜ Doctrine ORM ๊ด€๊ณ„์— ๋Œ€ํ•œ ์™„๋ฒฝ ๊ฐ€์ด๋“œ. OneToMany, ManyToMany, ๋กœ๋”ฉ ์ „๋žต, ๊ทธ๋ฆฌ๊ณ  ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ๋ฅผ ํ†ตํ•œ ์„ฑ๋Šฅ ์ตœ์ ํ™”.

Symfony์™€ PHP ์ธํ„ฐ๋ทฐ ์งˆ๋ฌธ - ์™„์ „ ๊ฐ€์ด๋“œ

Symfony ์ธํ„ฐ๋ทฐ ์งˆ๋ฌธ: 2026๋…„ Top 25

๊ฐ€์žฅ ๋งŽ์ด ๋ฌป๋Š” Symfony ์ธํ„ฐ๋ทฐ ์งˆ๋ฌธ 25์„ . ์•„ํ‚คํ…์ฒ˜, Doctrine ORM, ์„œ๋น„์Šค, ๋ณด์•ˆ, ํผ, ํ…Œ์ŠคํŠธ๋ฅผ ์ƒ์„ธํ•œ ๋‹ต๋ณ€๊ณผ ์ฝ”๋“œ ์˜ˆ์ œ๋กœ ๋‹ค๋ฃน๋‹ˆ๋‹ค.