@extends('layouts.app') @section('title', 'Docker 환경이해') @push('styles') @endpush @section('content')
SAM 프로젝트의 로컬 Docker 환경과 서버 네이티브 환경 — 비개발자도 이해할 수 있는 가이드
로컬 개발 환경 (내 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을 직접 설치하여 운영한다.
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) | Docker 컨테이너 |
|---|---|---|
| 비유 | 건물 통째로 빌리기 | 칸막이 사무실 임대 |
| OS | 각각 별도 OS 설치 | 호스트 OS 커널 공유 |
| 크기 | 수 GB | 수십~수백 MB |
| 시작 시간 | 수 분 | 수 초 |
| 자원 사용 | 무거움 (RAM 많이 차지) | 가벼움 (오버헤드 적음) |
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 |
요청 흐름 5단계 — 브라우저 → Nginx → PHP-FPM → Laravel → MySQL
비유: 식당 주문 과정
손님(브라우저)이 식당에 들어오면 → 안내 데스크(Nginx)가 좌석으로 안내 → 서빙 직원(PHP-FPM)이 주문을 받아 → 주방(Laravel)에서 요리 → 재료 창고(MySQL)에서 재료를 꺼내 요리를 완성한다.
mng.sam.kr 접속
컨테이너 내부 프로세스 구조 — Supervisor가 관리
MNG 컨테이너
API 컨테이너
비유: 식당 주방
하나의 컨테이너 안에 주방장(Supervisor)이 있고, 그 아래 요리사(PHP-FPM), 서빙 직원(Nginx), 설거지 담당(Queue Worker)이 각자 일을 한다. 주방장이 누가 쓰러지면 바로 새 직원을 투입한다(프로세스 자동 재시작).
React (Next.js) 컨테이너
서버에서 빌드 금지! 로컬에서만 빌드
MySQL 컨테이너
samdb: SAM 메인 DBchandj: 레거시 DBdb_data 볼륨에 영구 저장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 |
바인드 마운트 vs Docker 볼륨
비유: 공유 폴더
바인드 마운트는 내 PC 폴더를 컨테이너와 공유하는 것이다. 내 PC에서 파일을 수정하면 컨테이너 안에서도 즉시 반영된다. 마치 클라우드 공유 폴더처럼 양쪽이 항상 같은 파일을 본다.
# docker-compose.yml 예시 volumes: - ../mng:/var/www/mng # 바인드 마운트: 소스코드 - mng_vendor:/var/www/mng/vendor # Docker 볼륨: 의존성
바인드 마운트
호스트 경로 ↔ 컨테이너 경로 연결
코드 수정 → 즉시 반영
Docker 볼륨
Docker가 자체 관리하는 저장소
vendor, node_modules 성능 향상
vendor와 node_modules는 Docker 볼륨으로 격리한다.
이렇게 하면 호스트(WSL)의 파일 시스템 성능 문제를 피하고, 컨테이너 내부에서 빠르게 읽을 수 있다.
| 볼륨명 | 용도 | 특징 |
|---|---|---|
db_data |
MySQL 데이터 | 컨테이너 삭제해도 데이터 유지 |
api_vendor |
API Composer 패키지 | 컨테이너 내부 성능 최적화 |
mng_vendor |
MNG Composer 패키지 | 컨테이너 내부 성능 최적화 |
*_node_modules |
Node.js 패키지 | 호스트와 분리하여 속도 향상 |
주의: Docker 볼륨은 호스트에서 직접 접근 불가
vendor 폴더는 Docker 볼륨 안에 있으므로,
호스트(WSL)에서 직접 수정할 수 없다. 반드시 docker exec로 컨테이너에 들어가서 작업해야 한다.
.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 파일에 넣는 것
.env 파일 주의사항
.env 로드 흐름과 우선순위
Docker Compose의 environment 설정은
프로젝트 .env 파일보다 우선한다.
즉, Docker에서 설정한 DB_HOST가 .env의 DB_HOST를 덮어쓴다.
environment 섹션
.env 파일
.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 은 무시됨
로컬(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 앱
로컬에서는 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 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
마이그레이션은 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/