- SETUP_GUIDE.md: Gmail SMTP 설정 방법, Google Groups 발신자 설정 추가 - CURRENT_WORKS.md: 2025-12-01 비밀번호 자동 생성/이메일 발송 작업 이력 - INDEX.md: 작업 이력 및 최종 업데이트일 갱신
617 lines
14 KiB
Markdown
617 lines
14 KiB
Markdown
# MNG 환경 구성 가이드
|
|
|
|
## 📋 개요
|
|
|
|
MNG 프로젝트를 위한 Docker 환경 구성 가이드입니다.
|
|
기존 SAM 프로젝트 (api, admin, react)에 mng 서비스를 추가합니다.
|
|
|
|
---
|
|
|
|
## 🎯 목표 환경
|
|
|
|
```
|
|
SAM/
|
|
├── api/ (api.sam.kr) - 외부 API
|
|
├── admin/ (admin.sam.kr) - Filament 관리자 (deprecated)
|
|
├── react/ (dev.sam.kr) - React 프론트엔드
|
|
├── mng/ (mng.sam.kr) - 새 관리자 패널 ⭐ NEW
|
|
└── docker/
|
|
├── docker-compose.yml
|
|
├── nginx/nginx.conf
|
|
└── mng/ ⭐ NEW
|
|
├── Dockerfile
|
|
├── nginx.conf
|
|
└── supervisord.conf
|
|
```
|
|
|
|
### 도메인 구성
|
|
| 도메인 | 서비스 | 용도 |
|
|
|--------|--------|------|
|
|
| api.sam.kr | api | 외부 클라이언트 API |
|
|
| admin.sam.kr | admin | Filament 관리자 (점차 폐기) |
|
|
| dev.sam.kr | react | React 프론트엔드 |
|
|
| **mng.sam.kr** | **mng** | **새 관리자 패널** ⭐ |
|
|
|
|
---
|
|
|
|
## 📐 Phase 0: 환경 구성 (최초 1회)
|
|
|
|
### Step 1: Laravel 프로젝트 생성
|
|
|
|
```bash
|
|
# SAM 디렉토리로 이동
|
|
cd /Users/hskwon/Works/@KD_SAM/SAM
|
|
|
|
# Laravel 12 프로젝트 생성
|
|
composer create-project laravel/laravel mng
|
|
|
|
cd mng
|
|
|
|
# 필수 패키지 설치
|
|
composer require laravel/sanctum
|
|
composer require darkaonline/l5-swagger
|
|
composer require --dev laravel/pint
|
|
|
|
# NPM 패키지 설치
|
|
npm install -D tailwindcss daisyui @tailwindcss/forms postcss autoprefixer
|
|
npm install htmx.org
|
|
npx tailwindcss init -p
|
|
```
|
|
|
|
### Step 2: 환경 변수 설정
|
|
|
|
**mng/.env** (기본값 수정)
|
|
```env
|
|
APP_NAME=MNG
|
|
APP_ENV=local
|
|
APP_KEY=base64:... (자동 생성됨)
|
|
APP_DEBUG=true
|
|
APP_URL=http://mng.sam.kr
|
|
|
|
DB_CONNECTION=mysql
|
|
DB_HOST=mysql
|
|
DB_PORT=3306
|
|
DB_DATABASE=samdb
|
|
DB_USERNAME=samuser
|
|
DB_PASSWORD=sampass
|
|
|
|
CACHE_DRIVER=file
|
|
QUEUE_CONNECTION=sync
|
|
SESSION_DRIVER=file
|
|
|
|
SANCTUM_STATEFUL_DOMAINS=mng.sam.kr
|
|
|
|
# 이메일 설정 (Gmail SMTP)
|
|
MAIL_MAILER=smtp
|
|
MAIL_HOST=smtp.gmail.com
|
|
MAIL_PORT=587
|
|
MAIL_USERNAME=your-email@gmail.com
|
|
MAIL_PASSWORD=your-app-password
|
|
MAIL_ENCRYPTION=tls
|
|
MAIL_FROM_ADDRESS=your-email@gmail.com
|
|
MAIL_FROM_NAME="${APP_NAME}"
|
|
```
|
|
|
|
### 이메일 설정 (Gmail SMTP)
|
|
|
|
사용자 등록 시 임시 비밀번호 발송, 비밀번호 초기화 등 이메일 기능을 사용하려면 SMTP 설정이 필요합니다.
|
|
|
|
**Gmail 앱 비밀번호 발급:**
|
|
1. Google 계정 → 보안 → 2단계 인증 활성화
|
|
2. 앱 비밀번호 생성 (https://myaccount.google.com/apppasswords)
|
|
3. 생성된 16자리 비밀번호를 `MAIL_PASSWORD`에 입력
|
|
|
|
**Google Groups 이메일 사용 (발신자 변경):**
|
|
- Google Groups 이메일(예: `develop@company.com`)을 발신자로 사용하려면:
|
|
1. Gmail 설정 → 계정 및 가져오기 → 다른 주소에서 메일 보내기
|
|
2. 그룹 이메일 주소 추가 및 인증
|
|
3. `.env`의 `MAIL_FROM_ADDRESS`를 그룹 이메일로 설정
|
|
|
|
**주의사항:**
|
|
- `MAIL_USERNAME`은 실제 Gmail 계정 (SMTP 인증용)
|
|
- `MAIL_FROM_ADDRESS`는 발신자 표시 주소 (Gmail에서 "다른 주소에서 메일 보내기" 설정 필요)
|
|
- 앱 비밀번호는 일반 비밀번호가 아닌 Google에서 생성한 16자리 코드
|
|
|
|
### Step 3: Tailwind + DaisyUI 설정
|
|
|
|
**mng/tailwind.config.js**
|
|
```js
|
|
/** @type {import('tailwindcss').Config} */
|
|
export default {
|
|
content: [
|
|
"./resources/**/*.blade.php",
|
|
"./resources/**/*.js",
|
|
"./resources/**/*.vue",
|
|
],
|
|
theme: {
|
|
extend: {},
|
|
},
|
|
plugins: [
|
|
require('daisyui'),
|
|
require('@tailwindcss/forms'),
|
|
],
|
|
daisyui: {
|
|
themes: ['light', 'dark'],
|
|
},
|
|
}
|
|
```
|
|
|
|
**mng/resources/css/app.css**
|
|
```css
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
```
|
|
|
|
**mng/resources/js/app.js**
|
|
```js
|
|
import './bootstrap';
|
|
import htmx from 'htmx.org';
|
|
|
|
window.htmx = htmx;
|
|
|
|
// HTMX 전역 설정
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// CSRF 토큰 자동 추가
|
|
document.body.addEventListener('htmx:configRequest', (event) => {
|
|
event.detail.headers['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').content;
|
|
});
|
|
|
|
// 에러 처리
|
|
document.body.addEventListener('htmx:responseError', (event) => {
|
|
console.error('HTMX Error:', event.detail);
|
|
alert('오류가 발생했습니다. 다시 시도해주세요.');
|
|
});
|
|
});
|
|
```
|
|
|
|
**mng/vite.config.js** (확인)
|
|
```js
|
|
import { defineConfig } from 'vite';
|
|
import laravel from 'laravel-vite-plugin';
|
|
|
|
export default defineConfig({
|
|
plugins: [
|
|
laravel({
|
|
input: ['resources/css/app.css', 'resources/js/app.js'],
|
|
refresh: true,
|
|
}),
|
|
],
|
|
});
|
|
```
|
|
|
|
### Step 4: Docker 설정 파일 생성
|
|
|
|
#### 4-1. Dockerfile 생성
|
|
|
|
**docker/mng/Dockerfile**
|
|
```dockerfile
|
|
FROM php:8.4-fpm
|
|
|
|
# 시스템 패키지 설치
|
|
RUN apt-get update && apt-get install -y \
|
|
git \
|
|
curl \
|
|
libpng-dev \
|
|
libonig-dev \
|
|
libxml2-dev \
|
|
libzip-dev \
|
|
zip \
|
|
unzip \
|
|
supervisor \
|
|
nginx
|
|
|
|
# PHP 확장 설치
|
|
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
|
|
|
|
# Composer 설치
|
|
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
|
|
|
# Node.js 설치 (Vite 빌드용)
|
|
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
|
apt-get install -y nodejs
|
|
|
|
# 작업 디렉토리 설정
|
|
WORKDIR /var/www/mng
|
|
|
|
# 권한 설정
|
|
RUN chown -R www-data:www-data /var/www/mng
|
|
|
|
# PHP-FPM 포트 노출
|
|
EXPOSE 9000
|
|
|
|
CMD ["php-fpm"]
|
|
```
|
|
|
|
#### 4-2. Nginx 설정 (PHP-FPM 연동)
|
|
|
|
**docker/mng/nginx.conf**
|
|
```nginx
|
|
server {
|
|
listen 9000;
|
|
server_name localhost;
|
|
root /var/www/mng/public;
|
|
index index.php index.html;
|
|
|
|
location / {
|
|
try_files $uri $uri/ /index.php?$query_string;
|
|
}
|
|
|
|
location ~ \.php$ {
|
|
include fastcgi_params;
|
|
fastcgi_pass 127.0.0.1:9000;
|
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 4-3. Supervisor 설정 (선택)
|
|
|
|
**docker/mng/supervisord.conf**
|
|
```ini
|
|
[supervisord]
|
|
nodaemon=true
|
|
|
|
[program:php-fpm]
|
|
command=/usr/local/sbin/php-fpm
|
|
autostart=true
|
|
autorestart=true
|
|
stderr_logfile=/var/log/php-fpm.err.log
|
|
stdout_logfile=/var/log/php-fpm.out.log
|
|
```
|
|
|
|
#### 4-4. PHP 업로드 설정
|
|
|
|
**docker/mng/uploads.ini**
|
|
```ini
|
|
upload_max_filesize = 100M
|
|
post_max_size = 100M
|
|
max_execution_time = 300
|
|
memory_limit = 256M
|
|
```
|
|
|
|
### Step 5: docker-compose.yml 업데이트
|
|
|
|
**docker/docker-compose.yml** (mng 서비스 추가)
|
|
```yaml
|
|
services:
|
|
nginx:
|
|
image: nginx:latest
|
|
ports:
|
|
- "80:80"
|
|
volumes:
|
|
- ../api:/var/www/api
|
|
- ../admin:/var/www/admin
|
|
- ../mng:/var/www/mng # ⭐ NEW
|
|
- ../docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
|
depends_on:
|
|
- api
|
|
- admin
|
|
- mng # ⭐ NEW
|
|
- react
|
|
networks:
|
|
- samnet
|
|
|
|
api:
|
|
build:
|
|
context: .
|
|
dockerfile: ../docker/api/Dockerfile
|
|
volumes:
|
|
- ../api:/var/www/api
|
|
- ../docker/api/nginx.conf:/etc/nginx/conf.d/default.conf
|
|
- ../docker/api/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf
|
|
- ../docker/api/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
|
|
environment:
|
|
- DB_HOST=mysql
|
|
- DB_PORT=3306
|
|
- DB_DATABASE=samdb
|
|
- DB_USERNAME=samuser
|
|
- DB_PASSWORD=sampass
|
|
networks:
|
|
- samnet
|
|
working_dir: /var/www/api
|
|
|
|
admin:
|
|
build:
|
|
context: .
|
|
dockerfile: ../docker/admin/Dockerfile
|
|
volumes:
|
|
- ../admin:/var/www/admin
|
|
- ../docker/admin/nginx.conf:/etc/nginx/conf.d/default.conf
|
|
- ../docker/admin/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf
|
|
- ../docker/admin/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
|
|
environment:
|
|
- DB_HOST=mysql
|
|
- DB_PORT=3306
|
|
- DB_DATABASE=samdb
|
|
- DB_USERNAME=samuser
|
|
- DB_PASSWORD=sampass
|
|
networks:
|
|
- samnet
|
|
working_dir: /var/www/admin
|
|
|
|
# ⭐ NEW: MNG 서비스
|
|
mng:
|
|
build:
|
|
context: .
|
|
dockerfile: ../docker/mng/Dockerfile
|
|
volumes:
|
|
- ../mng:/var/www/mng
|
|
- ../docker/mng/nginx.conf:/etc/nginx/conf.d/default.conf
|
|
- ../docker/mng/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf
|
|
- ../docker/mng/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
|
|
environment:
|
|
- DB_HOST=mysql
|
|
- DB_PORT=3306
|
|
- DB_DATABASE=samdb
|
|
- DB_USERNAME=samuser
|
|
- DB_PASSWORD=sampass
|
|
networks:
|
|
- samnet
|
|
working_dir: /var/www/mng
|
|
|
|
react:
|
|
build:
|
|
context: ..
|
|
dockerfile: docker/react/Dockerfile
|
|
volumes:
|
|
- ../react:/app
|
|
- /app/node_modules
|
|
- /app/.next
|
|
environment:
|
|
- NEXT_PUBLIC_API_BASE_URL=http://api.sam.kr
|
|
- NEXT_PUBLIC_ADMIN_URL=http://admin.sam.kr
|
|
- NEXT_PUBLIC_MNG_URL=http://mng.sam.kr # ⭐ NEW
|
|
- NEXT_PUBLIC_API_KEY=${NEXT_PUBLIC_API_KEY:-}
|
|
- NEXT_PUBLIC_APP_NAME=SAM
|
|
- NODE_ENV=development
|
|
networks:
|
|
- samnet
|
|
working_dir: /app
|
|
|
|
mysql:
|
|
image: mysql:8.0
|
|
restart: always
|
|
environment:
|
|
MYSQL_DATABASE: samdb
|
|
MYSQL_USER: samuser
|
|
MYSQL_PASSWORD: sampass
|
|
MYSQL_ROOT_PASSWORD: root
|
|
volumes:
|
|
- db_data:/var/lib/mysql
|
|
- ../docker/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
|
|
ports:
|
|
- "3306:3306"
|
|
networks:
|
|
- samnet
|
|
|
|
volumes:
|
|
db_data:
|
|
|
|
networks:
|
|
samnet:
|
|
driver: bridge
|
|
```
|
|
|
|
### Step 6: Nginx 메인 설정 업데이트
|
|
|
|
**docker/nginx/nginx.conf** (mng.sam.kr 서버 블록 추가)
|
|
```nginx
|
|
events {}
|
|
|
|
http {
|
|
include /etc/nginx/mime.types;
|
|
default_type application/octet-stream;
|
|
client_max_body_size 100M;
|
|
|
|
# ... 기존 서버 블록들 (dev.haisa.kr, api.sam.kr, admin.sam.kr, dev.sam.kr)
|
|
|
|
# ⭐ NEW: MNG 서버 블록
|
|
server {
|
|
listen 80;
|
|
server_name mng.sam.kr;
|
|
|
|
root /var/www/mng/public;
|
|
index index.php index.html;
|
|
|
|
access_log /var/log/nginx/mng.sam.kr_access.log;
|
|
error_log /var/log/nginx/mng.sam.kr_error.log;
|
|
|
|
# 🛡️ 보안: 악의적 경로 패턴 차단
|
|
if ($request_uri ~* "(\.\.\/|\.\.\\|etc\/passwd|\.env|\.git|\.htaccess|\.sql|@fs\/)") {
|
|
return 403;
|
|
}
|
|
|
|
# 🛡️ 보안: 의심스러운 User-Agent 차단
|
|
if ($http_user_agent ~* "(sqlmap|nikto|nmap|masscan|metasploit|nessus)") {
|
|
return 403;
|
|
}
|
|
|
|
# 정적 자산 처리
|
|
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|map)$ {
|
|
try_files $uri =404;
|
|
access_log off;
|
|
expires 30d;
|
|
add_header Cache-Control "public";
|
|
}
|
|
|
|
# 일반 요청 처리
|
|
location / {
|
|
try_files $uri $uri/ /index.php?$query_string;
|
|
}
|
|
|
|
location ~ \.php$ {
|
|
include fastcgi_params;
|
|
fastcgi_pass mng:9000; # 서비스명 mng
|
|
fastcgi_param SCRIPT_FILENAME /var/www/mng/public$fastcgi_script_name;
|
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 7: hosts 파일 설정
|
|
|
|
**/etc/hosts** (로컬 도메인 추가)
|
|
```bash
|
|
# SAM 프로젝트 도메인
|
|
127.0.0.1 api.sam.kr
|
|
127.0.0.1 admin.sam.kr
|
|
127.0.0.1 dev.sam.kr
|
|
127.0.0.1 mng.sam.kr # ⭐ NEW
|
|
```
|
|
|
|
**macOS/Linux:**
|
|
```bash
|
|
sudo nano /etc/hosts
|
|
# 위 내용 추가 후 저장
|
|
```
|
|
|
|
**Windows:**
|
|
```
|
|
C:\Windows\System32\drivers\etc\hosts
|
|
# 관리자 권한으로 메모장 열어서 추가
|
|
```
|
|
|
|
### Step 8: admin/ 모델 복사
|
|
|
|
```bash
|
|
cd /Users/hskwon/Works/@KD_SAM/SAM
|
|
|
|
# Models 복사
|
|
cp -r admin/app/Models/* mng/app/Models/
|
|
|
|
# Traits 복사
|
|
mkdir -p mng/app/Traits
|
|
cp admin/app/Traits/BelongsToTenant.php mng/app/Traits/
|
|
cp admin/app/Traits/HasAuditLog.php mng/app/Traits/
|
|
|
|
# Filament 의존성 제거 (수동 작업 필요)
|
|
# - form(), table(), canAccessPanel() 등 삭제
|
|
# - FilamentUser 인터페이스 제거
|
|
```
|
|
|
|
### Step 9: Docker 빌드 및 실행
|
|
|
|
```bash
|
|
cd /Users/hskwon/Works/@KD_SAM/SAM/docker
|
|
|
|
# 기존 컨테이너 중지
|
|
docker-compose down
|
|
|
|
# mng 서비스 빌드
|
|
docker-compose build mng
|
|
|
|
# 전체 서비스 시작
|
|
docker-compose up -d
|
|
|
|
# 로그 확인
|
|
docker-compose logs -f mng
|
|
|
|
# mng 컨테이너 접속
|
|
docker-compose exec mng bash
|
|
|
|
# 컨테이너 내부에서 Laravel 설정
|
|
php artisan key:generate
|
|
php artisan migrate
|
|
php artisan storage:link
|
|
|
|
# Vite 빌드 (개발 모드)
|
|
npm run dev
|
|
# 또는 프로덕션 빌드
|
|
npm run build
|
|
```
|
|
|
|
### Step 10: 동작 확인
|
|
|
|
```bash
|
|
# 1. 브라우저에서 확인
|
|
http://mng.sam.kr
|
|
|
|
# 2. API 확인 (예시)
|
|
curl http://mng.sam.kr/api/admin/health
|
|
|
|
# 3. 로그 확인
|
|
docker-compose logs mng
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 문제 해결
|
|
|
|
### 1. "502 Bad Gateway" 에러
|
|
```bash
|
|
# PHP-FPM 상태 확인
|
|
docker-compose exec mng ps aux | grep php-fpm
|
|
|
|
# 재시작
|
|
docker-compose restart mng
|
|
```
|
|
|
|
### 2. 권한 에러
|
|
```bash
|
|
# 컨테이너 내부에서
|
|
docker-compose exec mng bash
|
|
chown -R www-data:www-data /var/www/mng/storage
|
|
chown -R www-data:www-data /var/www/mng/bootstrap/cache
|
|
chmod -R 775 /var/www/mng/storage
|
|
chmod -R 775 /var/www/mng/bootstrap/cache
|
|
```
|
|
|
|
### 3. DB 연결 실패
|
|
```bash
|
|
# MySQL 컨테이너 확인
|
|
docker-compose exec mysql mysql -u samuser -psampass -e "SHOW DATABASES;"
|
|
|
|
# mng .env 확인
|
|
docker-compose exec mng cat .env | grep DB_
|
|
```
|
|
|
|
### 4. Vite 빌드 실패
|
|
```bash
|
|
# 컨테이너 내부에서
|
|
docker-compose exec mng bash
|
|
npm install
|
|
npm run build
|
|
```
|
|
|
|
### 5. HTMX 동작 안함
|
|
- 브라우저 개발자 도구 → Network 탭 확인
|
|
- `HX-Request` 헤더 있는지 확인
|
|
- CSRF 토큰 확인 (meta 태그)
|
|
|
|
---
|
|
|
|
## 📋 환경 구성 체크리스트
|
|
|
|
```
|
|
[ ] Step 1: Laravel 프로젝트 생성 (mng/)
|
|
[ ] Step 2: .env 설정 (DB 정보)
|
|
[ ] Step 3: Tailwind + DaisyUI 설정
|
|
[ ] Step 4: Docker 설정 파일 생성 (Dockerfile, nginx.conf)
|
|
[ ] Step 5: docker-compose.yml 업데이트 (mng 서비스 추가)
|
|
[ ] Step 6: Nginx 메인 설정 업데이트 (mng.sam.kr 서버 블록)
|
|
[ ] Step 7: /etc/hosts 설정 (mng.sam.kr)
|
|
[ ] Step 8: admin/ 모델 복사 및 Filament 제거
|
|
[ ] Step 9: Docker 빌드 및 실행
|
|
[ ] Step 10: 동작 확인 (http://mng.sam.kr)
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 다음 단계
|
|
|
|
환경 구성 완료 후:
|
|
|
|
1. **Phase 1**: 인증 시스템 구현 (로그인 API + Blade)
|
|
2. **Phase 2**: 대시보드 구현 (레이아웃 + HTMX)
|
|
3. **Phase 3**: 사용자 관리 (첫 CRUD 기능)
|
|
|
|
DEV_PROCESS.md 문서를 참고하여 개발을 진행하세요.
|
|
|
|
---
|
|
|
|
**작성일**: 2025-01-20
|
|
**버전**: 1.0
|
|
**환경**: Docker + Laravel 12 + MySQL 8.0 + HTMX + DaisyUI |