2026-02-12 08:49:35 +09:00
|
|
|
# SAM E-Sign 운영/배포 가이드
|
|
|
|
|
|
|
|
|
|
> **프로젝트명**: SAM E-Sign (전자계약 서명 솔루션)
|
|
|
|
|
> **작성일**: 2026-02-12
|
|
|
|
|
> **버전**: v1.0
|
|
|
|
|
> **대상**: 시스템 운영자, DevOps 엔지니어
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 목차
|
|
|
|
|
|
|
|
|
|
1. [시스템 구성](#1-시스템-구성)
|
|
|
|
|
2. [환경 변수](#2-환경-변수)
|
|
|
|
|
3. [배포 절차](#3-배포-절차)
|
|
|
|
|
4. [스토리지 관리](#4-스토리지-관리)
|
|
|
|
|
5. [메일 발송 설정](#5-메일-발송-설정)
|
|
|
|
|
6. [스케줄러 설정](#6-스케줄러-설정)
|
|
|
|
|
7. [모니터링](#7-모니터링)
|
|
|
|
|
8. [백업 및 복구](#8-백업-및-복구)
|
|
|
|
|
9. [보안 운영](#9-보안-운영)
|
|
|
|
|
10. [장애 대응](#10-장애-대응)
|
|
|
|
|
11. [확장 가이드](#11-확장-가이드)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 1. 시스템 구성
|
|
|
|
|
|
|
|
|
|
### 1.1 인프라 아키텍처
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
인터넷
|
|
|
|
|
│
|
|
|
|
|
▼
|
|
|
|
|
┌───────────────┐
|
|
|
|
|
│ Nginx (443) │ sam-nginx-1
|
|
|
|
|
│ SSL 종단점 │ api.sam.kr / mng.sam.kr
|
|
|
|
|
└──────┬────────┘
|
|
|
|
|
│
|
|
|
|
|
┌──────────┴──────────┐
|
|
|
|
|
▼ ▼
|
|
|
|
|
┌──────────────┐ ┌──────────────┐
|
|
|
|
|
│ API (FPM) │ │ MNG (FPM) │
|
|
|
|
|
│ sam-api-1 │ │ sam-mng-1 │
|
|
|
|
|
│ :9000 │ │ :9000 │
|
|
|
|
|
└──────┬───────┘ └──────┬───────┘
|
|
|
|
|
│ │
|
|
|
|
|
└────────┬───────────┘
|
|
|
|
|
▼
|
|
|
|
|
┌──────────────┐ ┌──────────────┐
|
|
|
|
|
│ MySQL 8.0 │ │ phpMyAdmin │
|
|
|
|
|
│ sam-mysql-1 │ │ :8080 │
|
|
|
|
|
│ :3306 │ └──────────────┘
|
|
|
|
|
└──────────────┘
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 1.2 도메인 매핑
|
|
|
|
|
|
|
|
|
|
| 도메인 | 대상 컨테이너 | 용도 | 프로토콜 |
|
|
|
|
|
|--------|-------------|------|---------|
|
|
|
|
|
| `api.sam.kr` | sam-api-1:9000 | E-Sign API 백엔드 | HTTPS |
|
|
|
|
|
| `mng.sam.kr` | sam-mng-1:9000 | E-Sign 관리 화면 + 공개 서명 | HTTPS |
|
|
|
|
|
|
|
|
|
|
### 1.3 E-Sign 관련 컨테이너
|
|
|
|
|
|
|
|
|
|
| 컨테이너 | 역할 | E-Sign 관련 기능 |
|
|
|
|
|
|----------|------|-----------------|
|
|
|
|
|
| **sam-api-1** | API 서버 | 계약 CRUD, OTP, 서명 처리, 메일 발송, PDF 처리 |
|
|
|
|
|
| **sam-mng-1** | 관리 화면 | 대시보드, 계약 생성, 서명 위치 지정, 공개 서명 UI |
|
|
|
|
|
| **sam-mysql-1** | 데이터베이스 | esign_* 4개 테이블 |
|
|
|
|
|
| **sam-nginx-1** | 웹서버 | SSL, 리버스 프록시, 보안 헤더 |
|
|
|
|
|
|
|
|
|
|
### 1.4 기술 스택
|
|
|
|
|
|
2026-02-13 13:02:16 +09:00
|
|
|
| 항목 | 버전 | 비고 |
|
|
|
|
|
|------|------|------|
|
|
|
|
|
| PHP | 8.3 | |
|
|
|
|
|
| Laravel | 11 (API), 11 (MNG) | |
|
|
|
|
|
| MySQL | 8.0 | Multi-tenant |
|
|
|
|
|
| Nginx | 최신 | |
|
|
|
|
|
| Docker Compose | v2 | |
|
|
|
|
|
| React | 18 (CDN + Babel) | 브라우저 트랜스파일링 |
|
|
|
|
|
| FPDI/TCPDF | 2.6 / 6.10 | PDF 서명 합성 (MNG) |
|
|
|
|
|
| LibreOffice | headless (writer-nogui) | DOCX→PDF 변환 (MNG) |
|
|
|
|
|
| GD 확장 | PHP 내장 | 서명 이미지 처리 (MNG) |
|
|
|
|
|
| 나눔 폰트 | fonts-nanum | DOCX 한글 렌더링 (MNG) |
|
|
|
|
|
| PDF.js | CDN | 브라우저 PDF 표시 |
|
|
|
|
|
| signature_pad.js | 4.x | 터치/마우스 서명 캡처 |
|
|
|
|
|
| Lucide | CDN | 아이콘 |
|
2026-02-12 08:49:35 +09:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 2. 환경 변수
|
|
|
|
|
|
|
|
|
|
### 2.1 API 프로젝트 (`/home/aweso/sam/api/.env`)
|
|
|
|
|
|
|
|
|
|
#### 필수 설정
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 애플리케이션
|
|
|
|
|
APP_URL=https://api.sam.kr/
|
|
|
|
|
APP_ENV=production # local → production
|
|
|
|
|
APP_DEBUG=false # true → false (운영)
|
|
|
|
|
APP_TIMEZONE=Asia/Seoul
|
|
|
|
|
APP_LOCALE=ko
|
|
|
|
|
|
|
|
|
|
# 데이터베이스
|
|
|
|
|
DB_CONNECTION=mysql
|
|
|
|
|
DB_HOST=sam-mysql-1 # Docker 네트워크
|
|
|
|
|
DB_PORT=3306
|
|
|
|
|
DB_DATABASE=samdb
|
|
|
|
|
DB_USERNAME=samuser
|
|
|
|
|
DB_PASSWORD=sampass
|
|
|
|
|
|
|
|
|
|
# 큐 (메일 비동기 발송)
|
|
|
|
|
QUEUE_CONNECTION=database
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### E-Sign 관련 설정
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 메일 (서명 요청/OTP 발송용)
|
|
|
|
|
MAIL_MAILER=smtp
|
|
|
|
|
MAIL_HOST=smtp.gmail.com
|
|
|
|
|
MAIL_PORT=587
|
|
|
|
|
MAIL_USERNAME=shine1324@gmail.com
|
|
|
|
|
MAIL_PASSWORD=<앱 비밀번호>
|
|
|
|
|
MAIL_ENCRYPTION=tls
|
|
|
|
|
MAIL_FROM_ADDRESS=shine1324@gmail.com
|
|
|
|
|
MAIL_FROM_NAME="SAM E-Sign"
|
|
|
|
|
|
|
|
|
|
# 파일 저장
|
|
|
|
|
FILESYSTEM_DISK=local
|
|
|
|
|
FILE_MAX_SIZE=20480 # 20MB (KB 단위)
|
|
|
|
|
|
|
|
|
|
# 인증 토큰
|
|
|
|
|
SANCTUM_ACCESS_TOKEN_EXPIRATION=120 # 2시간
|
|
|
|
|
SANCTUM_REFRESH_TOKEN_EXPIRATION=10080 # 7일
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 환경별 차이
|
|
|
|
|
|
|
|
|
|
| 설정 | 로컬 (Docker) | 서버 (운영) |
|
|
|
|
|
|------|-------------|-------------|
|
|
|
|
|
| APP_ENV | local | production |
|
|
|
|
|
| APP_DEBUG | true | **false** |
|
|
|
|
|
| DB_HOST | sam-mysql-1 | 127.0.0.1 |
|
|
|
|
|
| MAIL_MAILER | log (테스트) | smtp (실제 발송) |
|
|
|
|
|
| QUEUE_CONNECTION | sync | database |
|
|
|
|
|
|
|
|
|
|
### 2.2 MNG 프로젝트 (`/home/aweso/sam/mng/.env`)
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 애플리케이션
|
|
|
|
|
APP_URL=https://mng.sam.kr
|
|
|
|
|
APP_ENV=production
|
|
|
|
|
APP_DEBUG=false
|
|
|
|
|
|
|
|
|
|
# 데이터베이스 (API와 동일 DB 공유)
|
|
|
|
|
DB_HOST=sam-mysql-1
|
|
|
|
|
DB_DATABASE=samdb
|
|
|
|
|
DB_USERNAME=samuser
|
|
|
|
|
DB_PASSWORD=sampass
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> MNG는 화면 렌더링만 담당하므로 E-Sign 관련 별도 환경변수는 없습니다.
|
|
|
|
|
> API 호출은 프론트엔드(React)에서 `api.sam.kr`로 직접 요청합니다.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 3. 배포 절차
|
|
|
|
|
|
|
|
|
|
### 3.1 최초 배포 (신규 설치)
|
|
|
|
|
|
|
|
|
|
#### Step 1: 마이그레이션 실행
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 로컬 (Docker)
|
|
|
|
|
docker exec sam-api-1 php artisan migrate
|
|
|
|
|
|
|
|
|
|
# 서버
|
|
|
|
|
cd /home/webservice/api
|
|
|
|
|
php artisan migrate
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
마이그레이션 결과 확인:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 4개 테이블 생성 확인
|
|
|
|
|
docker exec sam-api-1 php artisan migrate:status | grep esign
|
|
|
|
|
|
|
|
|
|
# 기대 결과:
|
|
|
|
|
# Ran 2026_02_12_100000_create_esign_contracts_table
|
|
|
|
|
# Ran 2026_02_12_110000_create_esign_signers_table
|
|
|
|
|
# Ran 2026_02_12_120000_create_esign_sign_fields_table
|
|
|
|
|
# Ran 2026_02_12_130000_create_esign_audit_logs_table
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Step 2: 스토리지 디렉토리 생성
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 로컬 (Docker)
|
|
|
|
|
docker exec sam-api-1 mkdir -p storage/app/esign
|
|
|
|
|
|
|
|
|
|
# 서버
|
|
|
|
|
cd /home/webservice/api
|
|
|
|
|
mkdir -p storage/app/esign
|
|
|
|
|
chmod 775 storage/app/esign
|
|
|
|
|
chown -R www-data:www-data storage/app/esign
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Step 3: 라우트 캐시 갱신
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 로컬
|
|
|
|
|
docker exec sam-api-1 php artisan route:cache
|
|
|
|
|
docker exec sam-mng-1 php artisan route:cache
|
|
|
|
|
|
|
|
|
|
# 서버
|
|
|
|
|
cd /home/webservice/api && php artisan route:cache
|
|
|
|
|
cd /home/webservice/mng && php artisan route:cache
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Step 4: 메뉴 등록
|
|
|
|
|
|
|
|
|
|
메뉴 시더 실행 금지. tinker로 수동 등록합니다.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 상위 메뉴 생성
|
|
|
|
|
ssh sam-server "cd /home/webservice/mng && php artisan tinker --execute=\"
|
|
|
|
|
\\\$parent = App\\\\Models\\\\Commons\\\\Menu::create([
|
|
|
|
|
'tenant_id' => 1,
|
|
|
|
|
'parent_id' => null,
|
|
|
|
|
'name' => '전자계약 (E-Sign)',
|
|
|
|
|
'url' => '/esign',
|
|
|
|
|
'icon' => 'ti ti-file-certificate',
|
|
|
|
|
'sort_order' => 90,
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
]);
|
|
|
|
|
echo 'Parent ID: ' . \\\$parent->id;
|
|
|
|
|
\""
|
|
|
|
|
|
|
|
|
|
# 하위 메뉴 생성 (parent_id를 위에서 출력된 값으로 교체)
|
|
|
|
|
ssh sam-server "cd /home/webservice/mng && php artisan tinker --execute=\"
|
|
|
|
|
App\\\\Models\\\\Commons\\\\Menu::create([
|
|
|
|
|
'tenant_id' => 1,
|
|
|
|
|
'parent_id' => <PARENT_ID>,
|
|
|
|
|
'name' => '계약 대시보드',
|
|
|
|
|
'url' => '/esign',
|
|
|
|
|
'icon' => 'ti ti-dashboard',
|
|
|
|
|
'sort_order' => 1,
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
]);
|
|
|
|
|
App\\\\Models\\\\Commons\\\\Menu::create([
|
|
|
|
|
'tenant_id' => 1,
|
|
|
|
|
'parent_id' => <PARENT_ID>,
|
|
|
|
|
'name' => '새 계약 생성',
|
|
|
|
|
'url' => '/esign/create',
|
|
|
|
|
'icon' => 'ti ti-file-plus',
|
|
|
|
|
'sort_order' => 2,
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
]);
|
|
|
|
|
\""
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Step 5: 라우트 확인
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# API 라우트 확인 (16개)
|
|
|
|
|
docker exec sam-api-1 php artisan route:list --path=esign
|
|
|
|
|
|
|
|
|
|
# MNG 라우트 확인 (8개)
|
|
|
|
|
docker exec sam-mng-1 php artisan route:list --path=esign
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3.2 업데이트 배포
|
|
|
|
|
|
|
|
|
|
코드 변경 사항을 반영할 때:
|
|
|
|
|
|
|
|
|
|
#### 로컬 (Docker)
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 1. 코드 pull
|
|
|
|
|
cd /home/aweso/sam/api && git pull
|
|
|
|
|
cd /home/aweso/sam/mng && git pull
|
|
|
|
|
|
|
|
|
|
# 2. 의존성 업데이트 (composer.json 변경 시)
|
|
|
|
|
docker exec sam-api-1 composer install --no-dev --optimize-autoloader
|
|
|
|
|
docker exec sam-mng-1 composer install --no-dev --optimize-autoloader
|
|
|
|
|
|
|
|
|
|
# 3. 마이그레이션 (API에서만)
|
|
|
|
|
docker exec sam-api-1 php artisan migrate
|
|
|
|
|
|
|
|
|
|
# 4. 캐시 갱신
|
|
|
|
|
docker exec sam-api-1 php artisan config:cache
|
|
|
|
|
docker exec sam-api-1 php artisan route:cache
|
|
|
|
|
docker exec sam-api-1 php artisan view:cache
|
|
|
|
|
docker exec sam-mng-1 php artisan config:cache
|
|
|
|
|
docker exec sam-mng-1 php artisan route:cache
|
|
|
|
|
docker exec sam-mng-1 php artisan view:cache
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 서버 (운영)
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# API 프로젝트
|
|
|
|
|
cd /home/webservice/api
|
|
|
|
|
git pull
|
|
|
|
|
composer install --no-dev --optimize-autoloader
|
|
|
|
|
php artisan migrate --force
|
|
|
|
|
php artisan config:cache
|
|
|
|
|
php artisan route:cache
|
|
|
|
|
php artisan view:cache
|
|
|
|
|
|
|
|
|
|
# MNG 프로젝트 (마이그레이션 없음)
|
|
|
|
|
cd /home/webservice/mng
|
|
|
|
|
git pull
|
|
|
|
|
composer install --no-dev --optimize-autoloader
|
|
|
|
|
php artisan config:cache
|
|
|
|
|
php artisan route:cache
|
|
|
|
|
php artisan view:cache
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3.3 배포 체크리스트
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
□ API: git pull 완료
|
|
|
|
|
□ MNG: git pull 완료
|
|
|
|
|
□ API: composer install 완료
|
|
|
|
|
□ MNG: composer install 완료
|
|
|
|
|
□ API: php artisan migrate 완료 (신규 마이그레이션 시)
|
|
|
|
|
□ API: php artisan config:cache
|
|
|
|
|
□ MNG: php artisan config:cache
|
|
|
|
|
□ API: route:list --path=esign 로 16개 라우트 확인
|
|
|
|
|
□ MNG: route:list --path=esign 로 8개 라우트 확인
|
|
|
|
|
□ 브라우저: mng.sam.kr/esign 접속 확인
|
|
|
|
|
□ API: esign 테이블 4개 존재 확인
|
|
|
|
|
□ 메일: 테스트 서명 요청 발송 → 이메일 수신 확인
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 4. 스토리지 관리
|
|
|
|
|
|
|
|
|
|
### 4.1 파일 저장 구조
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
storage/app/esign/
|
|
|
|
|
└── {tenant_id}/
|
|
|
|
|
├── originals/ # 원본 PDF
|
|
|
|
|
│ └── {contract_id}/
|
|
|
|
|
│ └── {hash}.pdf
|
|
|
|
|
├── signatures/ # 서명 이미지
|
|
|
|
|
│ └── {signer_id}/
|
|
|
|
|
│ └── {timestamp}.png
|
|
|
|
|
└── signed/ # 서명 완료 PDF (v1.1)
|
|
|
|
|
└── {contract_id}/
|
|
|
|
|
└── {contract_code}_signed.pdf
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4.2 용량 산정
|
|
|
|
|
|
|
|
|
|
| 파일 유형 | 평균 크기 | 월 100건 기준 |
|
|
|
|
|
|----------|----------|--------------|
|
|
|
|
|
| 원본 PDF | 2MB | 200MB |
|
|
|
|
|
| 서명 이미지 | 50KB x 2인 | 10MB |
|
|
|
|
|
| 서명 완료 PDF | 2.5MB (v1.1) | 250MB |
|
|
|
|
|
| **월 합계** | | **약 460MB** |
|
|
|
|
|
| **연간 합계** | | **약 5.5GB** |
|
|
|
|
|
|
|
|
|
|
### 4.3 스토리지 모니터링
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 전체 E-Sign 스토리지 용량 확인
|
|
|
|
|
du -sh /home/webservice/api/storage/app/esign/
|
|
|
|
|
|
|
|
|
|
# 테넌트별 용량 확인
|
|
|
|
|
du -sh /home/webservice/api/storage/app/esign/*/
|
|
|
|
|
|
|
|
|
|
# 디스크 여유 공간 확인
|
|
|
|
|
df -h /home/webservice/
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4.4 파일 정리 정책
|
|
|
|
|
|
|
|
|
|
| 항목 | 보관 기간 | 근거 |
|
|
|
|
|
|------|----------|------|
|
|
|
|
|
| 완료된 계약 PDF | 5년 | 전자상거래법 |
|
|
|
|
|
| 서명 이미지 | 5년 | 법적 증거 |
|
|
|
|
|
| 취소/만료 계약 PDF | 1년 | 운영 판단 |
|
|
|
|
|
| 감사 로그 | 영구 | 삭제 불가 |
|
|
|
|
|
|
|
|
|
|
### 4.5 권한 설정
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# API 스토리지 디렉토리 권한
|
|
|
|
|
chown -R www-data:www-data /home/webservice/api/storage/app/esign/
|
|
|
|
|
chmod -R 775 /home/webservice/api/storage/app/esign/
|
|
|
|
|
|
|
|
|
|
# 보안: 웹에서 직접 접근 차단 (Nginx에서 처리)
|
|
|
|
|
# storage 디렉토리는 public 경로에 포함되지 않음
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 5. 메일 발송 설정
|
|
|
|
|
|
|
|
|
|
### 5.1 발송 시점
|
|
|
|
|
|
|
|
|
|
| 이벤트 | 수신자 | 메일 내용 |
|
|
|
|
|
|--------|--------|----------|
|
|
|
|
|
| 서명 요청 발송 | 첫 번째 서명자 | 계약 제목, 서명 링크, 기한 |
|
|
|
|
|
| OTP 발송 | 해당 서명자 | 6자리 인증코드, 5분 유효 |
|
|
|
|
|
| 리마인더 | 미서명 서명자 | 서명 독촉, 남은 기한 |
|
|
|
|
|
| 서명 완료 알림 | 다음 서명자 | "상대방이 서명했습니다", 서명 링크 |
|
|
|
|
|
| 계약 완료 | 양쪽 서명자 | 완료 안내, 다운로드 링크 |
|
|
|
|
|
|
|
|
|
|
### 5.2 SMTP 설정
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# API .env (현재 Gmail SMTP)
|
|
|
|
|
MAIL_MAILER=smtp
|
|
|
|
|
MAIL_HOST=smtp.gmail.com
|
|
|
|
|
MAIL_PORT=587
|
|
|
|
|
MAIL_USERNAME=shine1324@gmail.com
|
|
|
|
|
MAIL_PASSWORD=<Gmail 앱 비밀번호>
|
|
|
|
|
MAIL_ENCRYPTION=tls
|
|
|
|
|
MAIL_FROM_ADDRESS=shine1324@gmail.com
|
|
|
|
|
MAIL_FROM_NAME="SAM E-Sign"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.3 Gmail 앱 비밀번호 발급
|
|
|
|
|
|
|
|
|
|
1. Google 계정 → 보안 → 2단계 인증 활성화
|
|
|
|
|
2. 앱 비밀번호 생성 (항목: 메일, 기기: 기타 → "SAM API")
|
|
|
|
|
3. 생성된 16자리 비밀번호를 `.env`의 `MAIL_PASSWORD`에 설정
|
|
|
|
|
|
|
|
|
|
### 5.4 메일 발송 테스트
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# tinker를 이용한 메일 발송 테스트
|
|
|
|
|
docker exec sam-api-1 php artisan tinker --execute="
|
|
|
|
|
Mail::raw('E-Sign 메일 테스트', function(\$m) {
|
|
|
|
|
\$m->to('test@example.com')->subject('SAM E-Sign 테스트');
|
|
|
|
|
});
|
|
|
|
|
echo 'Mail sent';
|
|
|
|
|
"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.5 메일 발송 실패 시
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 메일 로그 확인
|
|
|
|
|
docker exec sam-api-1 tail -50 storage/logs/laravel.log | grep -i mail
|
|
|
|
|
|
|
|
|
|
# 큐 실패 작업 확인
|
|
|
|
|
docker exec sam-api-1 php artisan queue:failed
|
|
|
|
|
|
|
|
|
|
# 실패 작업 재시도
|
|
|
|
|
docker exec sam-api-1 php artisan queue:retry all
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.6 운영 환경 권장 사항
|
|
|
|
|
|
|
|
|
|
| 항목 | 현재 | 운영 권장 |
|
|
|
|
|
|------|------|----------|
|
|
|
|
|
| SMTP 서비스 | Gmail | AWS SES / Mailgun / SendGrid |
|
|
|
|
|
| 일 발송 한도 | 500건 (Gmail) | 무제한 (전문 서비스) |
|
|
|
|
|
| 발송 모드 | 동기(sync) | **비동기(database/redis)** |
|
|
|
|
|
| 큐 워커 | 미실행 | `php artisan queue:work` 상시 실행 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 6. 스케줄러 설정
|
|
|
|
|
|
|
|
|
|
### 6.1 E-Sign 관련 예정 스케줄 작업
|
|
|
|
|
|
|
|
|
|
> 현재 v1.0에서는 스케줄러 미구현. v1.1에서 추가 예정.
|
|
|
|
|
|
|
|
|
|
| 작업 | 실행 주기 | 설명 | 상태 |
|
|
|
|
|
|------|----------|------|------|
|
|
|
|
|
| 계약 만료 처리 | 매일 01:00 | expires_at 초과 계약 → expired 상태 변경 | v1.1 예정 |
|
|
|
|
|
| 자동 리마인더 | 매일 09:00 | 만료 3일 전 서명자에게 알림 | v1.1 예정 |
|
|
|
|
|
| 임시 파일 정리 | 매일 03:30 | 7일 이상 된 임시 파일 삭제 | 기존 설정 |
|
|
|
|
|
|
|
|
|
|
### 6.2 기존 SAM 스케줄러 (참고)
|
|
|
|
|
|
|
|
|
|
API 프로젝트의 `routes/console.php`에 이미 등록된 작업:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
02:00 일간 통계 집계
|
|
|
|
|
03:10 감사 로그 정리 (13개월 보관)
|
|
|
|
|
03:30 임시 파일 정리 (7일 이상)
|
|
|
|
|
03:40 휴지통 파일 삭제 (30일 이상)
|
|
|
|
|
03:50 공유 링크 정리
|
|
|
|
|
05:00 DB 백업 상태 확인
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 6.3 크론탭 설정
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 서버 크론탭 (이미 설정된 경우 확인만)
|
|
|
|
|
crontab -l
|
|
|
|
|
|
|
|
|
|
# Laravel 스케줄러 크론 (1분마다 실행)
|
|
|
|
|
* * * * * cd /home/webservice/api && php artisan schedule:run >> /dev/null 2>&1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 7. 모니터링
|
|
|
|
|
|
|
|
|
|
### 7.1 헬스체크
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# API 서버 동작 확인
|
|
|
|
|
curl -s https://api.sam.kr/api/health | jq .
|
|
|
|
|
|
|
|
|
|
# MNG 서버 동작 확인
|
|
|
|
|
curl -s -o /dev/null -w "%{http_code}" https://mng.sam.kr/esign
|
|
|
|
|
|
|
|
|
|
# MySQL 연결 확인
|
|
|
|
|
docker exec sam-api-1 php artisan tinker --execute="DB::connection()->getPdo(); echo 'DB OK';"
|
|
|
|
|
|
|
|
|
|
# E-Sign 테이블 확인
|
|
|
|
|
docker exec sam-api-1 php artisan tinker --execute="
|
|
|
|
|
echo 'contracts: ' . \App\Models\ESign\EsignContract::withoutGlobalScopes()->count();
|
|
|
|
|
echo 'signers: ' . \App\Models\ESign\EsignSigner::withoutGlobalScopes()->count();
|
|
|
|
|
"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 7.2 로그 모니터링
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# API 에러 로그 실시간 감시
|
|
|
|
|
docker exec sam-api-1 tail -f storage/logs/laravel.log | grep -i "error\|exception"
|
|
|
|
|
|
|
|
|
|
# MNG 에러 로그 실시간 감시
|
|
|
|
|
docker exec sam-mng-1 tail -f storage/logs/laravel.log | grep -i "error\|exception"
|
|
|
|
|
|
|
|
|
|
# E-Sign 관련 로그만 필터
|
|
|
|
|
docker exec sam-api-1 grep -i "esign\|e-sign" storage/logs/laravel.log | tail -50
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 7.3 주요 모니터링 지표
|
|
|
|
|
|
|
|
|
|
| 지표 | 확인 방법 | 임계치 |
|
|
|
|
|
|------|----------|--------|
|
|
|
|
|
| API 응답 시간 | Nginx access log | > 3초 경고 |
|
|
|
|
|
| 에러 발생률 | Laravel log | 시간당 10건 이상 경고 |
|
|
|
|
|
| 디스크 사용량 | `df -h` | 80% 이상 경고 |
|
|
|
|
|
| 메일 발송 실패 | `queue:failed` | 1건 이상 즉시 확인 |
|
|
|
|
|
| DB 연결 수 | `SHOW STATUS LIKE 'Threads_connected'` | 80% 이상 경고 |
|
|
|
|
|
|
|
|
|
|
### 7.4 계약 현황 모니터링
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 상태별 계약 통계
|
|
|
|
|
docker exec sam-api-1 php artisan tinker --execute="
|
|
|
|
|
\$stats = \App\Models\ESign\EsignContract::withoutGlobalScopes()
|
|
|
|
|
->selectRaw('status, count(*) as cnt')
|
|
|
|
|
->groupBy('status')
|
|
|
|
|
->pluck('cnt', 'status');
|
|
|
|
|
print_r(\$stats->toArray());
|
|
|
|
|
"
|
|
|
|
|
|
|
|
|
|
# 만료 임박 계약 (3일 이내)
|
|
|
|
|
docker exec sam-api-1 php artisan tinker --execute="
|
|
|
|
|
\$expiring = \App\Models\ESign\EsignContract::withoutGlobalScopes()
|
|
|
|
|
->whereIn('status', ['pending', 'partially_signed'])
|
|
|
|
|
->where('expires_at', '<=', now()->addDays(3))
|
|
|
|
|
->count();
|
|
|
|
|
echo '만료 임박: ' . \$expiring . '건';
|
|
|
|
|
"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 8. 백업 및 복구
|
|
|
|
|
|
|
|
|
|
### 8.1 백업 대상
|
|
|
|
|
|
|
|
|
|
| 대상 | 방법 | 주기 |
|
|
|
|
|
|------|------|------|
|
|
|
|
|
| DB (esign_* 4개 테이블) | mysqldump | 일간 |
|
|
|
|
|
| 원본 PDF 파일 | rsync / 파일 복사 | 일간 |
|
|
|
|
|
| 서명 이미지 | rsync / 파일 복사 | 일간 |
|
|
|
|
|
| .env 설정 파일 | 수동 백업 | 변경 시 |
|
|
|
|
|
|
|
|
|
|
### 8.2 DB 백업
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# E-Sign 테이블만 백업
|
|
|
|
|
docker exec sam-mysql-1 mysqldump -u samuser -psampass samdb \
|
|
|
|
|
esign_contracts esign_signers esign_sign_fields esign_audit_logs \
|
|
|
|
|
> /home/aweso/sam/db_backup/esign_$(date +%Y%m%d).sql
|
|
|
|
|
|
|
|
|
|
# 전체 DB 백업 (기존 방식 활용)
|
|
|
|
|
docker exec sam-mysql-1 mysqldump -u samuser -psampass samdb \
|
|
|
|
|
> /home/aweso/sam/db_backup/samdb_$(date +%Y%m%d).sql
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 8.3 파일 백업
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# E-Sign 스토리지 백업
|
|
|
|
|
rsync -av /home/webservice/api/storage/app/esign/ \
|
|
|
|
|
/backup/esign/$(date +%Y%m%d)/
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 8.4 복구 절차
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 1. DB 복구
|
|
|
|
|
docker exec -i sam-mysql-1 mysql -u samuser -psampass samdb \
|
|
|
|
|
< /home/aweso/sam/db_backup/esign_20260212.sql
|
|
|
|
|
|
|
|
|
|
# 2. 파일 복구
|
|
|
|
|
rsync -av /backup/esign/20260212/ \
|
|
|
|
|
/home/webservice/api/storage/app/esign/
|
|
|
|
|
|
|
|
|
|
# 3. 권한 복원
|
|
|
|
|
chown -R www-data:www-data /home/webservice/api/storage/app/esign/
|
|
|
|
|
|
|
|
|
|
# 4. 캐시 클리어
|
|
|
|
|
cd /home/webservice/api && php artisan cache:clear
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 9. 보안 운영
|
|
|
|
|
|
|
|
|
|
### 9.1 Nginx 보안 설정 (적용 완료)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
# SSL/TLS
|
|
|
|
|
- TLSv1.2, TLSv1.3만 허용
|
|
|
|
|
- HSTS: max-age=31536000 (1년)
|
|
|
|
|
|
|
|
|
|
# 보안 헤더
|
|
|
|
|
- X-Frame-Options: SAMEORIGIN
|
|
|
|
|
- X-Content-Type-Options: nosniff
|
|
|
|
|
- X-XSS-Protection: 1; mode=block
|
|
|
|
|
|
|
|
|
|
# 경로 차단
|
|
|
|
|
- ../, .env, .git, .htaccess, .sql 패턴 차단 (403)
|
|
|
|
|
- sqlmap, nikto, nmap 등 스캐너 User-Agent 차단 (403)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 9.2 E-Sign 보안 포인트
|
|
|
|
|
|
|
|
|
|
| 항목 | 보호 방법 | 점검 주기 |
|
|
|
|
|
|------|----------|----------|
|
|
|
|
|
| access_token | 128자 랜덤, API 응답에 미포함 ($hidden) | 코드 리뷰 시 |
|
|
|
|
|
| OTP 코드 | 6자리, 5분 만료, 5회 제한 | 배포 시 |
|
|
|
|
|
| 파일 접근 | Controller 스트리밍만 허용, 직접 경로 차단 | 배포 시 |
|
|
|
|
|
| 문서 무결성 | SHA-256 hash_equals() (타이밍 공격 방지) | 코드 리뷰 시 |
|
|
|
|
|
| 테넌트 격리 | BelongsToTenant 글로벌 스코프 | 배포 시 |
|
|
|
|
|
| 감사 로그 | SoftDeletes 미적용, 삭제 API 없음 | 분기 점검 |
|
|
|
|
|
|
|
|
|
|
### 9.3 정기 보안 점검
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 1. 비정상 접근 시도 확인
|
|
|
|
|
docker exec sam-nginx-1 grep "403\|401" /var/log/nginx/access.log | tail -20
|
|
|
|
|
|
|
|
|
|
# 2. OTP 시도 횟수 초과 확인
|
|
|
|
|
docker exec sam-api-1 php artisan tinker --execute="
|
|
|
|
|
\$blocked = \App\Models\ESign\EsignSigner::withoutGlobalScopes()
|
|
|
|
|
->where('otp_attempts', '>=', 5)
|
|
|
|
|
->count();
|
|
|
|
|
echo 'OTP 차단 서명자: ' . \$blocked . '명';
|
|
|
|
|
"
|
|
|
|
|
|
|
|
|
|
# 3. 만료된 토큰으로 접근 시도 감사
|
|
|
|
|
docker exec sam-api-1 php artisan tinker --execute="
|
|
|
|
|
\$suspicious = \App\Models\ESign\EsignAuditLog::withoutGlobalScopes()
|
|
|
|
|
->where('action', 'like', '%failed%')
|
|
|
|
|
->where('created_at', '>=', now()->subDays(7))
|
|
|
|
|
->count();
|
|
|
|
|
echo '최근 7일 실패 이벤트: ' . \$suspicious . '건';
|
|
|
|
|
"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 9.4 SSL 인증서 관리
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 인증서 만료일 확인
|
|
|
|
|
openssl x509 -enddate -noout -in /etc/nginx/ssl/sam.kr.crt
|
|
|
|
|
|
|
|
|
|
# 인증서 갱신 후 Nginx 재시작
|
|
|
|
|
docker exec sam-nginx-1 nginx -s reload
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 10. 장애 대응
|
|
|
|
|
|
|
|
|
|
### 10.1 장애 유형별 대응
|
|
|
|
|
|
|
|
|
|
#### API 서버 응답 없음
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 1. 컨테이너 상태 확인
|
|
|
|
|
docker ps | grep sam-api
|
|
|
|
|
|
|
|
|
|
# 2. PHP-FPM 프로세스 확인
|
|
|
|
|
docker exec sam-api-1 ps aux | grep php-fpm
|
|
|
|
|
|
|
|
|
|
# 3. 에러 로그 확인
|
|
|
|
|
docker exec sam-api-1 tail -50 storage/logs/laravel.log
|
|
|
|
|
|
|
|
|
|
# 4. 컨테이너 재시작
|
|
|
|
|
docker restart sam-api-1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### DB 연결 실패
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 1. MySQL 컨테이너 확인
|
|
|
|
|
docker ps | grep sam-mysql
|
|
|
|
|
|
|
|
|
|
# 2. MySQL 프로세스 확인
|
|
|
|
|
docker exec sam-mysql-1 mysqladmin -u samuser -psampass ping
|
|
|
|
|
|
|
|
|
|
# 3. 연결 수 확인
|
|
|
|
|
docker exec sam-mysql-1 mysql -u samuser -psampass -e \
|
|
|
|
|
"SHOW STATUS LIKE 'Threads_connected';"
|
|
|
|
|
|
|
|
|
|
# 4. MySQL 재시작
|
|
|
|
|
docker restart sam-mysql-1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 메일 발송 실패
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 1. SMTP 연결 테스트
|
|
|
|
|
docker exec sam-api-1 php artisan tinker --execute="
|
|
|
|
|
try {
|
|
|
|
|
\$transport = new \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
|
|
|
|
|
'smtp.gmail.com', 587, true
|
|
|
|
|
);
|
|
|
|
|
echo 'SMTP 연결 가능';
|
|
|
|
|
} catch (\Exception \$e) {
|
|
|
|
|
echo 'SMTP 에러: ' . \$e->getMessage();
|
|
|
|
|
}
|
|
|
|
|
"
|
|
|
|
|
|
|
|
|
|
# 2. 큐 실패 작업 확인
|
|
|
|
|
docker exec sam-api-1 php artisan queue:failed
|
|
|
|
|
|
|
|
|
|
# 3. 실패 작업 재시도
|
|
|
|
|
docker exec sam-api-1 php artisan queue:retry all
|
|
|
|
|
|
|
|
|
|
# 4. 로그에서 메일 에러 확인
|
|
|
|
|
docker exec sam-api-1 grep "Swift_Transport\|Mail\|smtp" storage/logs/laravel.log | tail -20
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 서명 링크 접속 불가
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 1. 토큰 유효성 확인
|
|
|
|
|
docker exec sam-api-1 php artisan tinker --execute="
|
|
|
|
|
\$signer = \App\Models\ESign\EsignSigner::withoutGlobalScopes()
|
|
|
|
|
->where('access_token', '<토큰값>')
|
|
|
|
|
->first();
|
|
|
|
|
if (\$signer) {
|
|
|
|
|
echo 'Status: ' . \$signer->status;
|
|
|
|
|
echo ', Expires: ' . \$signer->token_expires_at;
|
|
|
|
|
echo ', Contract: ' . \$signer->contract->status;
|
|
|
|
|
} else {
|
|
|
|
|
echo 'Token not found';
|
|
|
|
|
}
|
|
|
|
|
"
|
|
|
|
|
|
|
|
|
|
# 2. Nginx 로그에서 해당 요청 확인
|
|
|
|
|
docker exec sam-nginx-1 grep "esign/sign" /var/log/nginx/access.log | tail -10
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### PDF 업로드 실패
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 1. 스토리지 디렉토리 권한 확인
|
|
|
|
|
docker exec sam-api-1 ls -la storage/app/esign/
|
|
|
|
|
|
|
|
|
|
# 2. 디스크 여유 공간 확인
|
|
|
|
|
docker exec sam-api-1 df -h /var/www/api/storage/
|
|
|
|
|
|
|
|
|
|
# 3. PHP 업로드 설정 확인
|
|
|
|
|
docker exec sam-api-1 php -i | grep -i "upload_max_filesize\|post_max_size"
|
|
|
|
|
|
|
|
|
|
# 4. Nginx 업로드 크기 확인
|
|
|
|
|
docker exec sam-nginx-1 grep "client_max_body_size" /etc/nginx/nginx.conf
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 10.2 긴급 복구 체크리스트
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
□ 장애 내용 파악 (에러 메시지, 발생 시점, 영향 범위)
|
|
|
|
|
□ 에러 로그 확인 (API, MNG, Nginx, MySQL)
|
|
|
|
|
□ 컨테이너 상태 확인 (docker ps)
|
|
|
|
|
□ 네트워크 확인 (도메인, DNS, SSL)
|
|
|
|
|
□ 서비스 재시작 (필요 시)
|
|
|
|
|
□ DB 연결 확인
|
|
|
|
|
□ 캐시 클리어 (config:cache, route:cache)
|
|
|
|
|
□ 복구 확인 (브라우저 접속 테스트)
|
|
|
|
|
□ 장애 보고서 작성 (원인, 조치, 재발 방지)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 11. 확장 가이드
|
|
|
|
|
|
2026-02-13 13:02:16 +09:00
|
|
|
### 11.1 현재 설치된 패키지 (구현 완료)
|
2026-02-12 08:49:35 +09:00
|
|
|
|
|
|
|
|
```bash
|
2026-02-13 13:02:16 +09:00
|
|
|
# FPDI/TCPDF (PDF 서명 합성) - MNG 프로젝트에 설치됨
|
|
|
|
|
# setasign/fpdi: ^2.6, tecnickcom/tcpdf: ^6.10
|
2026-02-12 08:49:35 +09:00
|
|
|
|
2026-02-13 13:02:16 +09:00
|
|
|
# LibreOffice (DOCX→PDF 변환) - MNG Docker 컨테이너에 설치됨
|
|
|
|
|
# libreoffice-writer-nogui, fonts-nanum, fonts-nanum-extra
|
|
|
|
|
|
|
|
|
|
# GD 확장 (서명 이미지 처리) - MNG Docker 컨테이너에 설치됨
|
2026-02-12 08:49:35 +09:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 11.2 큐 워커 설정 (운영 권장)
|
|
|
|
|
|
|
|
|
|
메일 발송을 비동기로 처리하려면 큐 워커를 상시 실행해야 합니다.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Supervisor 설정 (/etc/supervisor/conf.d/esign-worker.conf)
|
|
|
|
|
[program:esign-worker]
|
|
|
|
|
process_name=%(program_name)s_%(process_num)02d
|
|
|
|
|
command=php /home/webservice/api/artisan queue:work database --sleep=3 --tries=3 --max-time=3600
|
|
|
|
|
autostart=true
|
|
|
|
|
autorestart=true
|
|
|
|
|
stopasleepy=false
|
|
|
|
|
numprocs=2
|
|
|
|
|
user=www-data
|
|
|
|
|
redirect_stderr=true
|
|
|
|
|
stdout_logfile=/home/webservice/api/storage/logs/worker.log
|
|
|
|
|
stopwaitsecs=3600
|
|
|
|
|
|
|
|
|
|
# Supervisor 적용
|
|
|
|
|
supervisorctl reread
|
|
|
|
|
supervisorctl update
|
|
|
|
|
supervisorctl start esign-worker:*
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 11.3 S3 스토리지 전환 (선택)
|
|
|
|
|
|
|
|
|
|
로컬 스토리지에서 AWS S3로 전환할 경우:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 패키지 설치
|
|
|
|
|
composer require league/flysystem-aws-s3-v3 "^3.0"
|
|
|
|
|
|
|
|
|
|
# .env 설정 추가
|
|
|
|
|
AWS_ACCESS_KEY_ID=<키>
|
|
|
|
|
AWS_SECRET_ACCESS_KEY=<시크릿>
|
|
|
|
|
AWS_DEFAULT_REGION=ap-northeast-2
|
|
|
|
|
AWS_BUCKET=sam-esign
|
|
|
|
|
FILESYSTEM_DISK=s3
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 11.4 운영 환경 SMTP 전환 (권장)
|
|
|
|
|
|
|
|
|
|
Gmail SMTP는 일 500건 제한이 있으므로, 운영 환경에서는 전문 메일 서비스를 권장합니다.
|
|
|
|
|
|
|
|
|
|
**AWS SES 예시:**
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# .env 설정
|
|
|
|
|
MAIL_MAILER=ses
|
|
|
|
|
AWS_ACCESS_KEY_ID=<키>
|
|
|
|
|
AWS_SECRET_ACCESS_KEY=<시크릿>
|
|
|
|
|
AWS_DEFAULT_REGION=ap-northeast-2
|
|
|
|
|
MAIL_FROM_ADDRESS=esign@sam.kr
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**SendGrid 예시:**
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# .env 설정
|
|
|
|
|
MAIL_MAILER=smtp
|
|
|
|
|
MAIL_HOST=smtp.sendgrid.net
|
|
|
|
|
MAIL_PORT=587
|
|
|
|
|
MAIL_USERNAME=apikey
|
|
|
|
|
MAIL_PASSWORD=<SendGrid API Key>
|
|
|
|
|
MAIL_FROM_ADDRESS=esign@sam.kr
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 부록: 명령어 모음
|
|
|
|
|
|
|
|
|
|
### 일상 운영
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 서비스 상태 확인
|
|
|
|
|
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep sam
|
|
|
|
|
|
|
|
|
|
# 전체 재시작
|
|
|
|
|
cd /home/aweso/sam && docker compose restart
|
|
|
|
|
|
|
|
|
|
# 개별 재시작
|
|
|
|
|
docker restart sam-api-1
|
|
|
|
|
docker restart sam-mng-1
|
|
|
|
|
|
|
|
|
|
# 캐시 전체 클리어
|
|
|
|
|
docker exec sam-api-1 php artisan optimize:clear
|
|
|
|
|
docker exec sam-mng-1 php artisan optimize:clear
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 데이터 조회
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 계약 건수
|
|
|
|
|
docker exec sam-mysql-1 mysql -u samuser -psampass samdb \
|
|
|
|
|
-e "SELECT status, COUNT(*) AS cnt FROM esign_contracts GROUP BY status;"
|
|
|
|
|
|
|
|
|
|
# 최근 감사 로그
|
|
|
|
|
docker exec sam-mysql-1 mysql -u samuser -psampass samdb \
|
|
|
|
|
-e "SELECT action, ip_address, created_at FROM esign_audit_logs ORDER BY id DESC LIMIT 20;"
|
|
|
|
|
|
|
|
|
|
# 서명 대기 건수
|
|
|
|
|
docker exec sam-mysql-1 mysql -u samuser -psampass samdb \
|
|
|
|
|
-e "SELECT COUNT(*) AS pending FROM esign_contracts WHERE status IN ('pending','partially_signed');"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 트러블슈팅
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Laravel 로그 실시간
|
|
|
|
|
docker exec sam-api-1 tail -f storage/logs/laravel.log
|
|
|
|
|
|
|
|
|
|
# Nginx 접근 로그
|
|
|
|
|
docker exec sam-nginx-1 tail -f /var/log/nginx/access.log
|
|
|
|
|
|
|
|
|
|
# PHP 설정 확인
|
|
|
|
|
docker exec sam-api-1 php -i | grep -E "memory_limit|upload_max|post_max|max_execution"
|
|
|
|
|
|
|
|
|
|
# 라우트 목록
|
|
|
|
|
docker exec sam-api-1 php artisan route:list --path=esign --columns=method,uri,name
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
*이 문서는 SAM E-Sign v1.0 운영/배포 가이드입니다. 최종 업데이트: 2026-02-12*
|