@extends('layouts.app') @section('title', 'Docker 환경이해') @push('styles') @endpush @section('content')
아카데미 Docker 환경

Docker 환경이해

SAM 프로젝트의 로컬 Docker 환경과 서버 네이티브 환경 — 비개발자도 이해할 수 있는 가이드

Docker 컨테이너 비유 일러스트
{{-- ============================================================ --}} {{-- 핵심 안내: 로컬 vs 서버 환경 차이 --}} {{-- ============================================================ --}}
!

핵심: SAM의 로컬과 서버 환경은 다르다

로컬 개발 환경 (내 PC / WSL)

Docker 사용 — PHP, MySQL, Nginx가 모두 컨테이너 안에서 실행된다.

docker exec sam-api-1 php artisan migrate

운영 서버 (114.203.209.83)

Docker 없음 (네이티브) — PHP, MySQL, Nginx가 서버에 직접 설치되어 있다.

cd /home/webservice/api && php artisan migrate

서버에서 Docker를 사용하지 않는 이유: 서버 스펙(2코어, 3.8GB RAM)에서 Docker는 무거워서 성능 저하가 발생한다. 따라서 서버에는 PHP, Nginx, MySQL을 직접 설치하여 운영한다.

{{-- ============================================================ --}} {{-- 1. Docker란? — 왜 Docker를 쓰는가 --}} {{-- ============================================================ --}}

1 Docker란? — 왜 Docker를 쓰는가

컨테이너 비유

Docker는 애플리케이션을 컨테이너라는 격리된 환경에 넣어 실행하는 도구다. 어떤 컴퓨터에서든 동일한 환경으로 프로그램을 돌릴 수 있게 해준다.

비유: 이삿짐 컨테이너

이사할 때 가구를 컨테이너에 통째로 넣어서 옮기면, 어디로 가든 꺼내서 바로 사용할 수 있다. Docker도 마찬가지다. 프로그램과 실행에 필요한 모든 것(PHP, MySQL, 설정 파일 등)을 컨테이너에 넣어두면, 내 PC든 서버든 똑같이 동작한다.

Docker 없이 개발할 때의 문제점:

"내 PC에선 되는데?"

PHP 버전이 달라서
서버에서 오류 발생

의존성 충돌

A 프로젝트는 PHP 7.3
B 프로젝트는 PHP 8.4 필요

설치 지옥

PHP, MySQL, Nginx...
하나하나 설치하다 하루 종일

Docker를 쓰면:

docker compose up 한 줄이면 모든 서비스가 동일한 환경으로 시작된다.

컨테이너 vs 가상머신(VM)

컨테이너 vs VM 비교

컨테이너 vs 가상머신 구조 비교

비교 항목 가상머신 (VM) Docker 컨테이너
비유 건물 통째로 빌리기 칸막이 사무실 임대
OS 각각 별도 OS 설치 호스트 OS 커널 공유
크기 수 GB 수십~수백 MB
시작 시간 수 분 수 초
자원 사용 무거움 (RAM 많이 차지) 가벼움 (오버헤드 적음)
{{-- ============================================================ --}} {{-- 2. SAM Docker 아키텍처 --}} {{-- ============================================================ --}}

2 SAM 로컬 Docker 아키텍처 — 개발 환경 구조

9개 서비스 구성

SAM 전체 아키텍처 다이어그램

SAM Docker 전체 아키텍처 — samnet 브리지 네트워크

로컬 개발 환경에서 SAM은 9개 서비스가 하나의 samnet 브리지 네트워크에서 통신한다. docker-compose.yml 파일로 전체 구성을 관리한다.

(운영 서버에서는 Docker를 사용하지 않고, PHP/Nginx/MySQL이 직접 설치되어 있다.)

서비스 컨테이너명 기술 역할
nginx sam-nginx-1 Nginx 리버스 프록시, SSL, 도메인 라우팅
api sam-api-1 PHP 8.4 + Laravel REST API, DB 마이그레이션
mng sam-mng-1 PHP 8.4 + Laravel 관리자 웹 (HTMX + Blade)
react sam-react-1 Node.js + Next.js 15 고객용 프론트엔드 (React)
sales sam-sales-1 PHP + Laravel 영업 관리
admin sam-admin-1 PHP + Laravel 시스템 관리자 페이지
php73 sam-php73-1 PHP 7.3 레거시(5130) 호환
mysql sam-mysql-1 MySQL 8.0 데이터베이스 (samdb + chandj)
phpmyadmin sam-phpmyadmin-1 phpMyAdmin DB 관리 웹 UI

요청의 여정 — 브라우저에서 DB까지

요청 흐름 5단계

요청 흐름 5단계 — 브라우저 → Nginx → PHP-FPM → Laravel → MySQL

비유: 식당 주문 과정

손님(브라우저)이 식당에 들어오면 → 안내 데스크(Nginx)가 좌석으로 안내 → 서빙 직원(PHP-FPM)이 주문을 받아 → 주방(Laravel)에서 요리 → 재료 창고(MySQL)에서 재료를 꺼내 요리를 완성한다.

1 브라우저에서 mng.sam.kr 접속
2 Nginx가 도메인을 보고 MNG 컨테이너로 전달
3 PHP-FPM이 PHP 코드를 실행
4 Laravel이 비즈니스 로직 처리 + DB 조회
5 MySQL에서 데이터를 가져와 HTML로 응답 반환
{{-- ============================================================ --}} {{-- 3. 서비스별 상세 --}} {{-- ============================================================ --}}

3 서비스별 상세 — 각 컨테이너의 역할

MNG / API — PHP + Laravel 컨테이너

Supervisor 내부 프로세스 구조

컨테이너 내부 프로세스 구조 — Supervisor가 관리

MNG 컨테이너

  • PHP 8.4 + Laravel 11
  • Supervisor가 관리하는 프로세스:
  • - Nginx (내부 웹서버)
  • - PHP-FPM (코드 실행)
  • - Queue Worker x2 (백그라운드 작업)

API 컨테이너

  • PHP 8.4 + Laravel 11
  • Supervisor가 관리하는 프로세스:
  • - Nginx (내부 웹서버)
  • - PHP-FPM (코드 실행)
  • - Queue Worker (백그라운드 작업)
  • - Scheduler (크론 작업)

비유: 식당 주방

하나의 컨테이너 안에 주방장(Supervisor)이 있고, 그 아래 요리사(PHP-FPM), 서빙 직원(Nginx), 설거지 담당(Queue Worker)이 각자 일을 한다. 주방장이 누가 쓰러지면 바로 새 직원을 투입한다(프로세스 자동 재시작).

React / MySQL

React (Next.js) 컨테이너

  • Node.js + Next.js 15
  • 고객용 프론트엔드 앱
  • 서버 사이드 렌더링(SSR)
  • 포트 3000에서 실행

서버에서 빌드 금지! 로컬에서만 빌드

MySQL 컨테이너

  • MySQL 8.0
  • 데이터베이스 2개 운영:
  • - samdb: SAM 메인 DB
  • - chandj: 레거시 DB
  • 포트 3306으로 접근
  • 데이터는 db_data 볼륨에 영구 저장

Nginx — 도메인별 라우팅

Nginx 도메인 라우팅 맵

Nginx 도메인 → 서비스 라우팅 맵

외부 Nginx가 도메인 이름을 보고 어떤 컨테이너로 보낼지 결정한다. 모든 요청은 80/443 포트로 들어오고, Nginx가 내부 서비스로 분배한다.

도메인 서비스 내부 포트
mng.sam.kr mng (Laravel) :8080
api.sam.kr api (Laravel) :8080
app.sam.kr react (Next.js) :3000
admin.sam.kr admin (Laravel) :8080
{{-- ============================================================ --}} {{-- 4. 볼륨과 데이터 관리 --}} {{-- ============================================================ --}}

4 볼륨과 데이터 관리

바인드 마운트 — 소스 코드 실시간 동기화

볼륨 마운트 구조

바인드 마운트 vs Docker 볼륨

비유: 공유 폴더

바인드 마운트는 내 PC 폴더를 컨테이너와 공유하는 것이다. 내 PC에서 파일을 수정하면 컨테이너 안에서도 즉시 반영된다. 마치 클라우드 공유 폴더처럼 양쪽이 항상 같은 파일을 본다.

# docker-compose.yml 예시
volumes:
  - ../mng:/var/www/mng          # 바인드 마운트: 소스코드
  - mng_vendor:/var/www/mng/vendor # Docker 볼륨: 의존성

바인드 마운트

호스트 경로 ↔ 컨테이너 경로 연결

코드 수정 → 즉시 반영

Docker 볼륨

Docker가 자체 관리하는 저장소

vendor, node_modules 성능 향상

Docker 볼륨 — 의존성과 데이터 격리

vendornode_modules는 Docker 볼륨으로 격리한다. 이렇게 하면 호스트(WSL)의 파일 시스템 성능 문제를 피하고, 컨테이너 내부에서 빠르게 읽을 수 있다.

볼륨명 용도 특징
db_data MySQL 데이터 컨테이너 삭제해도 데이터 유지
api_vendor API Composer 패키지 컨테이너 내부 성능 최적화
mng_vendor MNG Composer 패키지 컨테이너 내부 성능 최적화
*_node_modules Node.js 패키지 호스트와 분리하여 속도 향상

주의: Docker 볼륨은 호스트에서 직접 접근 불가

vendor 폴더는 Docker 볼륨 안에 있으므로, 호스트(WSL)에서 직접 수정할 수 없다. 반드시 docker exec로 컨테이너에 들어가서 작업해야 한다.

{{-- ============================================================ --}} {{-- 5. 환경 변수 (.env) 관리 --}} {{-- ============================================================ --}}

5 환경 변수 (.env) 관리

.env 파일의 역할과 구조

.env 파일은 애플리케이션의 환경 설정을 저장하는 파일이다. DB 접속 정보, API 키, 앱 이름 등 환경마다 달라지는 값을 코드 밖에서 관리한다.

비유: 매장별 설정 카드

프랜차이즈 매장마다 전화번호, 주소, 영업시간이 다르다. 매장 설정 카드(.env)에 적어두면, 같은 매뉴얼(코드)로도 매장마다 다르게 운영할 수 있다. 코드를 바꾸지 않고도 DB 주소, 비밀번호 등을 변경 가능하다.

# .env 파일 예시
APP_NAME=SAM
APP_ENV=local
APP_DEBUG=true

# 데이터베이스 설정
DB_HOST=sam-mysql-1
DB_PORT=3306
DB_DATABASE=samdb
DB_USERNAME=samuser
DB_PASSWORD=sampass

# 메일 설정
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com

.env 파일에 넣는 것

  • - DB 접속 정보 (호스트, 비밀번호)
  • - API 키, 시크릿 키
  • - 앱 환경 (local / production)
  • - 메일, 캐시, 큐 설정

.env 파일 주의사항

  • - Git에 커밋하면 안 됨 (.gitignore)
  • - 비밀번호가 있으므로 외부 공유 금지
  • - 서버와 로컬 값이 다름
  • - 수정 후 캐시 클리어 필요

Docker environment vs .env 우선순위

.env 로드 흐름

.env 로드 흐름과 우선순위

Docker Compose의 environment 설정은 프로젝트 .env 파일보다 우선한다. 즉, Docker에서 설정한 DB_HOST가 .env의 DB_HOST를 덮어쓴다.

1 최우선: docker-compose.yml environment 섹션
2 중간: 프로젝트 .env 파일
3 기본값: .env.example (참조용, 실제 로드 안 됨)
# docker-compose.yml에서 설정한 환경 변수 (최우선)
environment:
  - DB_HOST=sam-mysql-1    # ← 이 값이 .env의 DB_HOST를 덮어씀
  - DB_PORT=3306
  - DB_DATABASE=samdb

# 프로젝트 .env 파일의 DB_HOST=127.0.0.1 은 무시됨

로컬 vs 서버 .env 차이점

로컬 vs 서버 환경 비교

로컬(Docker) vs 서버(직접 설치) 환경 비교

설정 항목 로컬 (Docker) 서버 (운영)
APP_ENV local production
APP_DEBUG true false
DB_HOST sam-mysql-1 (컨테이너명) 127.0.0.1 (로컬호스트)
DB_PASSWORD sampass (개발용) **** (운영용 복잡 비밀번호)
APP_URL https://mng.sam.kr https://mng.sam.kr
실행 방법 docker exec 직접 명령 실행

핵심 차이: DB_HOST

로컬에서는 Docker 네트워크 안에서 컨테이너 이름(sam-mysql-1)으로 DB에 접근하고, 서버에서는 MySQL이 같은 서버에 직접 설치되어 있으므로 127.0.0.1로 접근한다.

서버 환경 구조 (Docker 없음)

운영 서버에는 Docker가 설치되어 있지 않다. 서버 스펙이 제한적이라(2코어/3.8GB RAM) Docker의 오버헤드를 감당하기 어렵기 때문이다.

# 운영 서버 구조 (네이티브 설치)
운영 서버 (114.203.209.83)
├── Nginx          ← 웹서버 (직접 설치)
├── PHP-FPM        ← PHP 처리 (직접 설치)
├── MySQL          ← DB (직접 설치)
├── /home/webservice/mng  ← MNG 앱
├── /home/webservice/api  ← API 앱
└── /home/webservice/react ← React 앱
{{-- ============================================================ --}} {{-- 6. 실전 명령어 & 트러블슈팅 --}} {{-- ============================================================ --}}

6 실전 명령어 & 트러블슈팅

로컬(Docker) vs 서버(네이티브) 명령어 비교

로컬에서는 docker exec를 통해 컨테이너 안에서 명령을 실행하고, 서버에서는 Docker 없이 직접 명령을 실행한다.

작업 로컬 (Docker) 서버 (네이티브)
artisan 실행 docker exec sam-api-1 php artisan ... cd /home/webservice/api && php artisan ...
composer docker exec sam-api-1 composer install cd /home/webservice/api && composer install
마이그레이션 docker exec sam-api-1 php artisan migrate cd /home/webservice/api && php artisan migrate
캐시 클리어 docker exec sam-mng-1 php artisan config:clear cd /home/webservice/mng && php artisan config:clear
로그 확인 docker logs sam-api-1 --tail 50 tail -50 /home/webservice/api/storage/logs/laravel.log

자주 쓰는 로컬 Docker 명령어

명령어 치트시트

Docker 명령어 치트시트

안전한 명령어 (자유롭게 사용)

# 컨테이너 상태 확인
docker ps

# 컨테이너 로그 확인
docker logs sam-api-1 --tail 50

# 컨테이너 안에서 명령 실행
docker exec sam-api-1 php artisan --version
docker exec sam-mng-1 php artisan route:list

# 캐시 클리어 (설정 변경 후 필수)
docker exec sam-api-1 php artisan config:clear
docker exec sam-api-1 php artisan cache:clear
docker exec sam-mng-1 php artisan config:clear

주의가 필요한 명령어

# 모든 서비스 시작
docker compose up -d

# 특정 서비스 재시작
docker compose restart api

# 의존성 설치 (composer.json 변경 시)
docker exec sam-api-1 composer install

# DB 마이그레이션 (반드시 API에서만!)
docker exec sam-api-1 php artisan migrate

SAM 프로젝트 주의사항

마이그레이션은 API에서만

MNG에서 마이그레이션을 실행하면 안 된다. DB 스키마 관리는 API 프로젝트가 담당한다.

docker exec sam-mng-1 php artisan migrate — 금지!

메뉴 시더 실행 금지

메뉴 시더를 실행하면 부서별 권한 설정이 초기화된다. 메뉴 변경은 tinker로 개별 처리한다.

php artisan db:seed --class=MngMenuSeeder — 금지!

서버에서 npm run build 금지

서버 메모리 부족으로 빌드 실패. React 빌드는 반드시 로컬에서 수행한다.

서버(2코어/3.8GB) → 빌드 시 OOM 위험

올바른 작업 흐름

로컬에서 코드 수정 → git commit → git push → 서버에서 git pull → composer install → config:clear

코드 변경은 항상 로컬에서! 서버는 배포만!

서버에서는 Docker 없이 직접 명령 실행 (php artisan, composer)

문제 발생 시 대처법

"컨테이너가 안 뜬다"

# 1. 상태 확인
docker ps -a  # 종료된 컨테이너도 표시

# 2. 에러 로그 확인
docker logs sam-api-1 --tail 100

# 3. 서비스 재시작
cd /home/aweso/sam/docker && docker compose restart api

"설정을 변경했는데 반영이 안 된다"

# .env 변경 후 반드시 캐시 클리어
docker exec sam-api-1 php artisan config:clear
docker exec sam-api-1 php artisan cache:clear
docker exec sam-api-1 php artisan view:clear

Laravel은 설정을 캐시하므로, .env 변경 후 캐시를 비워야 새 값이 적용된다.

"Class not found 에러"

# Composer 오토로드 재생성
docker exec sam-api-1 composer dump-autoload

# 또는 패키지 전체 재설치
docker exec sam-api-1 composer install

"Permission denied 에러"

# storage/logs 권한 확인
docker exec sam-api-1 ls -la storage/logs/

# 권한 수정 (컨테이너 내부)
docker exec sam-api-1 chmod -R 775 storage/
docker exec sam-api-1 chown -R www-data:www-data storage/
@include('components.academy-glossary', ['domain' => 'docker-environment']) @endsection