From b46909865f3a4363cb1651a790fa067c938662cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 12 Feb 2026 08:49:35 +0900 Subject: [PATCH] =?UTF-8?q?docs:E-Sign=20=EC=9A=B4=EC=98=81/=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=20=EA=B0=80=EC=9D=B4=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- projects/e-sign/operations-guide.md | 940 ++++++++++++++++++++++++++++ 1 file changed, 940 insertions(+) create mode 100644 projects/e-sign/operations-guide.md diff --git a/projects/e-sign/operations-guide.md b/projects/e-sign/operations-guide.md new file mode 100644 index 0000000..0ad9739 --- /dev/null +++ b/projects/e-sign/operations-guide.md @@ -0,0 +1,940 @@ +# 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 기술 스택 + +| 항목 | 버전 | +|------|------| +| PHP | 8.3 | +| Laravel | 11 (API), 11 (MNG) | +| MySQL | 8.0 | +| Nginx | 최신 | +| Docker Compose | v2 | +| Node.js | React 18 (CDN, 빌드 불필요) | + +--- + +## 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' => , + 'name' => '계약 대시보드', + 'url' => '/esign', + 'icon' => 'ti ti-dashboard', + 'sort_order' => 1, + 'is_active' => true, +]); +App\\\\Models\\\\Commons\\\\Menu::create([ + 'tenant_id' => 1, + '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= +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. 확장 가이드 + +### 11.1 v1.1 추가 예정 패키지 + +```bash +# PDF 서명 합성 (FPDI + FPDF) +docker exec sam-api-1 composer require setasign/fpdi setasign/fpdf + +# 서버 +cd /home/webservice/api +composer require setasign/fpdi setasign/fpdf +``` + +### 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= +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*