PHP 언어별 가이드

PHP 언어별 가이드

이 가이드는 Docker로 컨테이너화된 PHP 어플리케이션을 만드는 방법을 알려줘요.

출처: 문서

본문

PHP 언어별 가이드는 Docker를 사용해 컨테이너화된 PHP 어플리케이션을 만드는 방법을 가르쳐줘요. 이 가이드에서 다음을 배우게 돼요:

  • PHP 어플리케이션 컨테이너화하고 실행하기
  • 컨테이너를 사용해 PHP 어플리케이션을 개발할 로컬 환경 구성하기
  • 컨테이너 안에서 PHP 어플리케이션 테스트 실행하기

PHP 언어별 가이드를 모두 마치면, 이 가이드의 예제와 설명을 바탕으로 자신만의 PHP 어플리케이션을 컨테이너화할 수 있게 돼요.

기존 PHP 어플리케이션을 컨테이너화하는 것부터 시작해요.

PHP 어플리케이션 컨테이너화하기

준비 사항 (Prerequisites)

  • 최신 버전의 Docker Desktop을 설치했어야 해요.
  • git 클라이언트가 필요해요. 이 섹션의 예제는 명령줄 기반의 git 클라이언트를 사용하지만, 어떤 클라이언트를 써도 무방해요.

개요 (Overview)

이 섹션에서는 PHP 어플리케이션을 컨테이너화하고 실행하는 과정을 살펴봐요.

샘플 어플리케이션 가져오기

이 가이드에서는 미리 만들어진 PHP 어플리케이션을 사용할 거예요. 이 어플리케이션은 라이브러리 의존성 관리를 위해 Composer를 사용해요. Apache 웹 서버로 어플리케이션을 서빙할 거예요.

터미널을 열고 작업할 디렉토리로 이동한 다음, 다음 명령을 실행해 저장소를 클론해요.

$ git clone https://github.com/docker/docker-php-sample

샘플 어플리케이션은 기본 hello world 어플리케이션이고, 데이터베이스에서 카운터를 증가시키는 어플리케이션이에요. 그리고 이 어플리케이션은 테스트에 PHPUnit을 사용해요.

Docker 자산 만들기

이제 어플리케이션이 준비됐으니, 컨테이너화하는 데 필요한 Docker 자산을 만들 수 있어요.

[!TIP]

Gordon, Docker의 AI 어시스턴트가 프로젝트에 맞는 Docker 자산을 생성해줄 수 있어요. Gordon에게 어플리케이션에 맞춘 Dockerfile, Compose 파일, .dockerignore를 만들어 달라고 요청해보세요.

docker-php-sample 디렉토리에 다음 파일들을 만들어요.

# syntax=docker/dockerfile:1

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/

################################################################################

# Create a stage for installing app dependencies defined in Composer.
FROM composer:lts as deps

WORKDIR /app

# If your composer.json file defines scripts that run during dependency installation and
# reference your application source files, uncomment the line below to copy all the files
# into this layer.
# COPY . .

# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a bind mounts to composer.json and composer.lock to avoid having to copy them
# into this layer.
# Leverage a cache mount to /tmp/cache so that subsequent builds don't have to re-download packages.
RUN --mount=type=bind,source=composer.json,target=composer.json \
    --mount=type=bind,source=composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-dev --no-interaction

################################################################################

# Create a new stage for running the application that contains the minimal
# runtime dependencies for the application. This often uses a different base
# image from the install or build stage where the necessary files are copied
# from the install stage.
#
# The example below uses the PHP Apache image as the foundation for running the app.
# By specifying the "8.2-apache" tag, it will also use whatever happens to be the
# most recent version of that tag when you build your Dockerfile.
# If reproducibility is important, consider using a specific digest SHA, like
# php@sha256:99cede493dfd88720b610eb8077c8688d3cca50003d76d1d539b0efc8cca72b4.
FROM php:8.2-apache as final

# Your PHP application may require additional PHP extensions to be installed
# manually. For detailed instructions for installing extensions can be found, see
# https://github.com/docker-library/docs/tree/master/php#how-to-install-more-php-extensions
# The following code blocks provide examples that you can edit and use.
#
# Add core PHP extensions, see
# https://github.com/docker-library/docs/tree/master/php#php-core-extensions
# This example adds the apt packages for the 'gd' extension's dependencies and then
# installs the 'gd' extension. For additional tips on running apt-get, see
# https://docs.docker.com/go/dockerfile-aptget-best-practices/
# RUN apt-get update && apt-get install -y \
#     libfreetype-dev \
#     libjpeg62-turbo-dev \
#     libpng-dev \
# && rm -rf /var/lib/apt/lists/* \
#     && docker-php-ext-configure gd --with-freetype --with-jpeg \
#     && docker-php-ext-install -j$(nproc) gd
#
# Add PECL extensions, see
# https://github.com/docker-library/docs/tree/master/php#pecl-extensions
# This example adds the 'redis' and 'xdebug' extensions.
# RUN pecl install redis-5.3.7 \
#    && pecl install xdebug-3.2.1 \
#    && docker-php-ext-enable redis xdebug

# Use the default production configuration for PHP runtime arguments, see
# https://github.com/docker-library/docs/tree/master/php#configuration
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

# Copy the app dependencies from the previous install stage.
COPY --from=deps app/vendor/ /var/www/html/vendor
# Copy the app files from the app directory.
COPY ./src /var/www/html

# Switch to a non-privileged user (defined in the base image) that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
USER www-data
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/

# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
  server:
    build:
      context: .
    ports:
      - 9000:80

# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker compose up`.
#     depends_on:
#       db:
#         condition: service_healthy
#   db:
#     image: postgres
#     restart: always
#     user: postgres
#     secrets:
#       - db-password
#     volumes:
#       - db-data:/var/lib/postgresql/data
#     environment:
#       - POSTGRES_DB=example
#       - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
#     expose:
#       - 5432
#     healthcheck:
#       test: [ "CMD", "pg_isready" ]
#       interval: 10s
#       timeout: 5s
#       retries: 5
# volumes:
#   db-data:
# secrets:
#   db-password:
#     file: db/password.txt
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/

**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.next
**/.cache
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/charts
**/docker-compose*
**/compose.y*ml
!**/composer.json
!**/composer.lock
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
**/vendor
LICENSE
README.md

이제 docker-php-sample 디렉토리에 다음 내용이 있어야 해요.

├── docker-php-sample/
│ ├── .git/
│ ├── src/
│ ├── tests/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── composer.json
│ ├── composer.lock
│ ├── Dockerfile
│ └── README.md

이 파일들에 대해 자세히 알아보려면 다음을 참고해요:

어플리케이션 실행하기

docker-php-sample 디렉토리 안에서 터미널에 다음 명령을 실행해요.

$ docker compose up --build

브라우저를 열고 http://localhost:9000/hello.php에서 어플리케이션을 확인해요. 간단한 hello world 어플리케이션을 볼 수 있어요.

터미널에서 ctrl+c를 눌러 어플리케이션을 중지해요.

어플리케이션을 백그라운드로 실행하기

-d 옵션을 추가하면 터미널에서 분리된 상태로 어플리케이션을 실행할 수 있어요. docker-php-sample 디렉토리 안에서 터미널에 다음 명령을 실행해요.

$ docker compose up --build -d

브라우저를 열고 http://localhost:9000/hello.php에서 어플리케이션을 확인해요. 간단한 hello world 어플리케이션을 볼 수 있어요.

터미널에서 다음 명령을 실행해 어플리케이션을 중지해요.

$ docker compose down

Compose 명령에 대한 자세한 내용은 Compose CLI reference를 참고해요.

PHP 개발에 컨테이너 사용하기

준비 사항 (Prerequisites)

PHP 어플리케이션 컨테이너화를 완료해요.

개요 (Overview)

이 섹션에서는 컨테이너화한 어플리케이션을 위한 개발 환경을 구성하는 방법을 배워요. 여기에는 다음이 포함돼요:

  • 로컬 데이터베이스를 추가하고 데이터를 유지(Persist)하기
  • 데이터베이스와 상호작용할 phpMyAdmin 추가하기
  • 코드를 수정·저장할 때 실행 중인 Compose 서비스를 자동으로 갱신하도록 Compose 구성하기
  • 개발 의존성을 포함하는 개발용 컨테이너 만들기

로컬 데이터베이스 추가 및 데이터 유지

컨테이너를 사용해 데이터베이스 같은 로컬 서비스를 구성할 수 있어요. 샘플 어플리케이션에 이 작업을 하려면 다음을 수행해야 해요:

  • 데이터베이스에 연결할 확장을 설치하도록 Dockerfile 업데이트하기
  • 데이터베이스 서비스와 데이터를 유지할 볼륨을 추가하도록 compose.yaml 파일 업데이트하기

확장을 설치하도록 Dockerfile 업데이트하기

PHP 확장을 설치하려면 Dockerfile을 업데이트해야 해요. IDE나 텍스트 편집기에서 Dockerfile을 연 다음 내용을 업데이트해요. 다음 Dockerfile은 pdo와 pdo_mysql 확장을 설치하는 새 줄 하나를 포함해요. 주석은 모두 제거됐어요.

# syntax=docker/dockerfile:1

FROM composer:lts as deps
WORKDIR /app
RUN --mount=type=bind,source=composer.json,target=composer.json \
    --mount=type=bind,source=composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-dev --no-interaction

FROM php:8.2-apache as final
RUN docker-php-ext-install pdo pdo_mysql
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --from=deps app/vendor/ /var/www/html/vendor
COPY ./src /var/www/html
USER www-data

PHP 확장 설치에 대한 자세한 내용은 Official Docker Image for PHP를 참고해요.

db를 추가하고 데이터를 유지하도록 compose.yaml 파일 업데이트하기

compose.yaml 파일을 IDE나 텍스트 편집기로 열어요. PostgreSQL 데이터베이스와 볼륨에 대한 지시사항이 이미 주석 처리되어 있을 거예요. 이 어플리케이션에서는 MariaDB를 사용해요. MariaDB에 대한 자세한 내용은 MariaDB Official Docker image를 참고해요.

src/database.php 파일을 IDE나 텍스트 편집기로 열어요. 데이터베이스에 연결하기 위해 환경 변수를 읽는다는 것을 알 수 있어요.

compose.yaml 파일에서 다음을 업데이트해야 해요:

  1. MariaDB에 대한 데이터베이스 지시사항을 주석 해제하고 업데이트해요.
  2. server 서비스에 시크릿(secret)을 추가해 데이터베이스 비밀번호를 전달해요.
  3. server 서비스에 데이터베이스 연결 환경 변수를 추가해요.
  4. 데이터를 유지하도록 볼륨 지시사항을 주석 해제해요.

다음은 업데이트된 compose.yaml 파일이에요. 주석은 모두 제거됐어요.

services:
  server:
    build:
      context: .
    ports:
      - 9000:80
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
    environment:
      - PASSWORD_FILE_PATH=/run/secrets/db-password
      - DB_HOST=db
      - DB_NAME=example
      - DB_USER=root
  db:
    image: mariadb
    restart: always
    user: root
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
    environment:
      - MARIADB_ROOT_PASSWORD_FILE=/run/secrets/db-password
      - MARIADB_DATABASE=example
    expose:
      - 3306
    healthcheck:
      test:
        [
          "CMD",
          "/usr/local/bin/healthcheck.sh",
          "--su-mysql",
          "--connect",
          "--innodb_initialized",
        ]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

[!NOTE]

Compose 파일의 지시사항에 대해 더 알아보려면 Compose file reference를 참고해요.

Compose로 어플리케이션을 실행하기 전에, 이 Compose 파일이 secrets를 사용하고 데이터베이스의 비밀번호를 담을 password.txt 파일을 지정한다는 점에 주의해요. 이 파일은 소스 저장소에 포함돼 있지 않으므로 직접 만들어야 해요.

docker-php-sample 디렉토리에서 db라는 새 디렉토리를 만들고, 그 안에 password.txt 파일을 만들어요. IDE나 텍스트 편집기에서 password.txt를 열고 다음 비밀번호를 추가해요. 비밀번호는 파일에 추가 줄 없이 한 줄이어야 해요.

example

password.txt 파일을 저장하고 닫아요.

이제 docker-php-sample 디렉토리에 다음이 있어야 해요.

├── docker-php-sample/
│ ├── .git/
│ ├── db/
│ │ └── password.txt
│ ├── src/
│ ├── tests/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── composer.json
│ ├── composer.lock
│ ├── Dockerfile
│ └── README.md

다음 명령을 실행해 어플리케이션을 시작해요.

$ docker compose up --build

브라우저를 열고 http://localhost:9000/database.php에서 어플리케이션을 확인해요. 새로고침할 때마다 증가하는 카운터와 텍스트가 있는 간단한 웹 어플리케이션을 볼 수 있어요.

터미널에서 ctrl+c를 눌러 어플리케이션을 중지해요.

데이터베이스에 데이터가 유지되는지 확인하기

터미널에서 docker compose rm을 실행해 컨테이너를 제거한 다음 docker compose up을 실행해 어플리케이션을 다시 실행해요.

$ docker compose rm
$ docker compose up --build

브라우저에서 http://localhost:9000/database.php를 새로고침하고 이전 카운트가 여전히 존재하는지 확인해요. 볼륨이 없다면 컨테이너를 제거한 뒤 데이터베이스 데이터가 유지되지 않을 거예요.

터미널에서 ctrl+c를 눌러 어플리케이션을 중지해요.

데이터베이스와 상호작용할 phpMyAdmin 추가하기

compose.yaml 파일을 업데이트하면 어플리케이션 스택에 서비스를 쉽게 추가할 수 있어요.

compose.yaml을 업데이트해 phpMyAdmin용 새 서비스를 추가해요. 자세한 내용은 phpMyAdmin Official Docker Image를 참고해요. 다음은 업데이트된 compose.yaml 파일이에요.

services:
  server:
    build:
      context: .
    ports:
      - 9000:80
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
    environment:
      - PASSWORD_FILE_PATH=/run/secrets/db-password
      - DB_HOST=db
      - DB_NAME=example
      - DB_USER=root
  db:
    image: mariadb
    restart: always
    user: root
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
    environment:
      - MARIADB_ROOT_PASSWORD_FILE=/run/secrets/db-password
      - MARIADB_DATABASE=example
    expose:
      - 3306
    healthcheck:
      test:
        [
          "CMD",
          "/usr/local/bin/healthcheck.sh",
          "--su-mysql",
          "--connect",
          "--innodb_initialized",
        ]
      interval: 10s
      timeout: 5s
      retries: 5
  phpmyadmin:
    image: phpmyadmin
    ports:
      - 8080:80
    depends_on:
      - db
    environment:
      - PMA_HOST=db
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

터미널에서 docker compose up을 실행해 어플리케이션을 다시 실행해요.

$ docker compose up --build

브라우저에서 http://localhost:8080을 열어 phpMyAdmin에 접근해요. 사용자 이름으로 root, 비밀번호로 example을 사용해 로그인해요. 이제 phpMyAdmin을 통해 데이터베이스와 상호작용할 수 있어요.

터미널에서 ctrl+c를 눌러 어플리케이션을 중지해요.

서비스 자동 업데이트

Compose Watch를 사용하면 코드를 수정·저장할 때 실행 중인 Compose 서비스를 자동으로 갱신할 수 있어요. Compose Watch에 대한 자세한 내용은 Use Compose Watch를 참고해요.

compose.yaml 파일을 IDE나 텍스트 편집기로 연 다음 Compose Watch 지시사항을 추가해요. 다음은 업데이트된 compose.yaml 파일이에요.

services:
  server:
    build:
      context: .
    ports:
      - 9000:80
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
    environment:
      - PASSWORD_FILE_PATH=/run/secrets/db-password
      - DB_HOST=db
      - DB_NAME=example
      - DB_USER=root
    develop:
      watch:
        - action: sync
          path: ./src
          target: /var/www/html
  db:
    image: mariadb
    restart: always
    user: root
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
    environment:
      - MARIADB_ROOT_PASSWORD_FILE=/run/secrets/db-password
      - MARIADB_DATABASE=example
    expose:
      - 3306
    healthcheck:
      test:
        [
          "CMD",
          "/usr/local/bin/healthcheck.sh",
          "--su-mysql",
          "--connect",
          "--innodb_initialized",
        ]
      interval: 10s
      timeout: 5s
      retries: 5
  phpmyadmin:
    image: phpmyadmin
    ports:
      - 8080:80
    depends_on:
      - db
    environment:
      - PMA_HOST=db
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

다음 명령을 실행해 Compose Watch로 어플리케이션을 실행해요.

$ docker compose watch

브라우저를 열고 http://localhost:9000/hello.php에서 어플리케이션이 실행 중인지 확인해요.

로컬 머신의 어플리케이션 소스 파일에 가한 어떤 변경이든, 이제 실행 중인 컨테이너에 즉시 반영돼요.

hello.php를 IDE나 텍스트 편집기로 열고 문자열 Hello, world!를 Hello, Docker!로 수정해요.

hello.php의 변경 사항을 저장하고 몇 초 동안 어플리케이션이 동기화될 때까지 기다려요. 브라우저에서 http://localhost:9000/hello.php를 새로고침하고 업데이트된 텍스트가 나타나는지 확인해요.

터미널에서 ctrl+c를 눌러 Compose Watch를 중지해요. 터미널에서 docker compose down을 실행해 어플리케이션을 중지해요.

개발용 컨테이너 만들기

지금 시점에서 컨테이너화한 어플리케이션을 실행하면 Composer는 개발 의존성을 설치하지 않아요. 이 작은 이미지는 프로덕션에 좋지만, 개발할 때 필요한 도구와 의존성이 부족하고 tests 디렉토리도 포함하지 않아요. 다단계 빌드(multi-stage builds)를 사용하면 같은 Dockerfile에서 개발용과 프로덕션용 빌드 스테이지를 모두 만들 수 있어요. 자세한 내용은 Multi-stage builds를 참고해요.

Dockerfile에서 다음을 업데이트해야 해요:

  1. deps 스테이지를 두 개의 스테이지로 나눠요. 하나는 프로덕션용(prod-deps)이고 하나는 개발 의존성을 설치하는 스테이지(dev-deps)예요.
  2. 공통 base 스테이지를 만들어요.
  3. 개발용 새 development 스테이지를 만들어요.
  4. 새 prod-deps 스테이지에서 의존성을 복사하도록 final 스테이지를 업데이트해요.

다음은 변경 전과 후의 Dockerfile이에요.

변경 전

# syntax=docker/dockerfile:1

FROM composer:lts as deps
WORKDIR /app
RUN --mount=type=bind,source=composer.json,target=composer.json \
    --mount=type=bind,source=composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-dev --no-interaction

FROM php:8.2-apache as final
RUN docker-php-ext-install pdo pdo_mysql
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --from=deps app/vendor/ /var/www/html/vendor
COPY ./src /var/www/html
USER www-data

변경 후

# syntax=docker/dockerfile:1

FROM composer:lts as prod-deps
WORKDIR /app
RUN --mount=type=bind,source=./composer.json,target=composer.json \
    --mount=type=bind,source=./composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-dev --no-interaction

FROM composer:lts as dev-deps
WORKDIR /app
RUN --mount=type=bind,source=./composer.json,target=composer.json \
    --mount=type=bind,source=./composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-interaction

FROM php:8.2-apache as base
RUN docker-php-ext-install pdo pdo_mysql
COPY ./src /var/www/html

FROM base as development
COPY ./tests /var/www/html/tests
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
COPY --from=dev-deps app/vendor/ /var/www/html/vendor

FROM base as final
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --from=prod-deps app/vendor/ /var/www/html/vendor
USER www-data

compose.yaml 파일을 업데이트해 development 스테이지를 대상으로 하라는 지시사항을 추가해요.

다음은 compose.yaml 파일의 업데이트된 부분이에요.

services:
  server:
    build:
      context: .
      target: development
      # ...

이제 컨테이너화한 어플리케이션이 개발 의존성을 설치할 거예요.

다음 명령을 실행해 어플리케이션을 시작해요.

$ docker compose up --build

브라우저를 열고 http://localhost:9000/hello.php에서 어플리케이션을 확인해요. 간단한 "Hello, Docker!" 어플리케이션을 여전히 볼 수 있어요.

터미널에서 ctrl+c를 눌러 어플리케이션을 중지해요.

어플리케이션이 동일해 보이지만, 이제 개발 의존성을 활용할 수 있어요. 다음 섹션으로 넘어가 Docker로 테스트를 실행하는 방법을 배워보세요.

컨테이너에서 PHP 테스트 실행하기

준비 사항 (Prerequisites)

이 가이드의 이전 섹션을 전부 완료해요 — PHP 어플리케이션 컨테이너화부터 시작해서요.

개요 (Overview)

테스트는 현대 소프트웨어 개발의 필수적인 부분이에요. 테스트는 개발 팀마다 서로 다른 의미를 가질 수 있어요. 단위 테스트, 통합 테스트, end-to-end 테스트가 있죠. 이 가이드에서는 개발할 때와 빌드할 때 Docker에서 단위 테스트를 실행하는 방법을 살펴봐요.

로컬에서 개발할 때 테스트 실행하기

샘플 어플리케이션에는 이미 tests 디렉토리 안에 PHPUnit 테스트가 있어요. 로컬에서 개발할 때 Compose를 사용해 테스트를 실행할 수 있어요.

docker-php-sample 디렉토리에서 다음 명령을 실행해 컨테이너 안에서 테스트를 실행해요.

$ docker compose run --build --rm server ./vendor/bin/phpunit tests/HelloWorldTest.php

다음과 같은 내용의 출력을 볼 수 있어요.

Hello, Docker!PHPUnit 9.6.13 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 00:00.003, Memory: 4.00 MB

OK (1 test, 1 assertion)

명령에 대해 더 알아보려면 docker compose run을 참고해요.

빌드할 때 테스트 실행하기

빌드할 때 테스트를 실행하려면 Dockerfile을 업데이트해야 해요. 테스트를 실행하는 새 테스트 스테이지를 만들어요.

다음은 업데이트된 Dockerfile이에요.

# syntax=docker/dockerfile:1

FROM composer:lts as prod-deps
WORKDIR /app
RUN --mount=type=bind,source=./composer.json,target=composer.json \
    --mount=type=bind,source=./composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-dev --no-interaction

FROM composer:lts as dev-deps
WORKDIR /app
RUN --mount=type=bind,source=./composer.json,target=composer.json \
    --mount=type=bind,source=./composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-interaction

FROM php:8.2-apache as base
RUN docker-php-ext-install pdo pdo_mysql
COPY ./src /var/www/html

FROM base as development
COPY ./tests /var/www/html/tests
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
COPY --from=dev-deps app/vendor/ /var/www/html/vendor

FROM development as test
WORKDIR /var/www/html
RUN ./vendor/bin/phpunit tests/HelloWorldTest.php

FROM base as final
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --from=prod-deps app/vendor/ /var/www/html/vendor
USER www-data

다음 명령을 실행해 test 스테이지를 대상으로 지정해 이미지를 빌드하고 테스트 결과를 확인해요. 빌드 출력을 보려면 --progress plain을, 테스트가 항상 실행되도록 하려면 --no-cache를, test 스테이지를 대상으로 하려면 --target test를 포함하세요.

$ docker build -t php-docker-image-test --progress plain --no-cache --target test .

다음과 같은 내용의 출력을 볼 수 있어요.

#18 [test 2/2] RUN ./vendor/bin/phpunit tests/HelloWorldTest.php
#18 0.385 Hello, Docker!PHPUnit 9.6.13 by Sebastian Bergmann and contributors.
#18 0.392
#18 0.394 .                                                                   1 / 1 (100%)
#18 0.395
#18 0.395 Time: 00:00.003, Memory: 4.00 MB
#18 0.395
#18 0.395 OK (1 test, 1 assertion)

요약 (Summary)

이 섹션에서는 Compose를 사용해 로컬에서 개발할 때 테스트를 실행하고, 이미지를 빌드할 때 테스트를 실행하는 방법을 배웠어요.

관련 정보:

더 알아보기 (Learn more)