# 테넌트 이메일 연동 가이드 > **작성일**: 2026-03-11 > **상태**: 설계 확정 > **관련 정책**: [이메일 발송 정책](../standards/email-policy.md) --- ## 1. 개요 ### 1.1 목적 SAM 멀티테넌시 환경에서 각 테넌트(회사)가 사용하는 다양한 메일 시스템을 SAM과 연동하는 방법을 정의한다. **본사(코드브릿지엑스)가 MNG 관리 화면에서 각 테넌트의 메일 설정을 대행 관리**하는 방식으로 운영한다. ### 1.2 핵심 원칙 | 원칙 | 설명 | |------|------| | 🔴 본사 중앙 관리 | 테넌트 메일 설정은 MNG 관리자(본사)가 등록/수정한다 | | 🔴 프리셋 기반 | 주요 메일 제공자별 SMTP 프리셋을 제공하여 설정 오류를 방지한다 | | 🟡 연결 테스트 필수 | 설정 저장 전 SMTP 연결 테스트를 통과해야 한다 | | 🟡 단계적 확장 | 플랫폼 발송(Phase 1) → SMTP 릴레이(Phase 2) → OAuth2(Phase 3) | | 🟢 가이드 제공 | 테넌트에게 앱 비밀번호 생성 등 사전 준비 절차를 안내한다 | ### 1.3 운영 모델 ``` 본사 (코드브릿지엑스) 테넌트 (고객사) ┌────────────────────────┐ ┌────────────────────────┐ │ MNG 관리 화면 │ │ │ │ ├── 테넌트 목록 │ SMTP 정보 │ 기존 메일 시스템 │ │ ├── 메일 설정 등록 │ ←──── 전달 ──│ (Gmail, Naver 등) │ │ ├── SMTP 프리셋 선택 │ │ │ │ ├── 연결 테스트 │ │ 앱 비밀번호 생성 │ │ └── 발송 현황 모니터링 │ │ (본사 안내에 따라) │ └────────────────────────┘ └────────────────────────┘ ``` > **테넌트는 MNG에 접근하지 않는다.** 본사 관리자가 테넌트로부터 SMTP 정보를 전달받아 MNG에서 설정한다. --- ## 2. 테넌트 메일 환경 현황 ### 2.1 한국 기업 메일 시스템 분류 | 유형 | 주로 사용하는 메일 | 예시 도메인 | 비율 (추정) | |------|-------------------|------------|------------| | 포털 메일 | 네이버, 다음 | `ceo@naver.com` | 소기업 30% | | 호스팅 메일 | 카페24, 가비아 | `admin@company.co.kr` | 중소기업 25% | | Google Workspace | Gmail 인프라 | `hong@company.com` | 중소~중견 20% | | 네이버웍스 | 네이버 인프라 | `hong@company.com` | 중소기업 15% | | Microsoft 365 | Exchange/Outlook | `hong@company.co.kr` | 중견~대기업 8% | | 자체 메일 서버 | On-premise | `hong@company.com` | 대기업 2% | ### 2.2 메일 제공자별 SMTP 정보 | 제공자 | SMTP 호스트 | 포트 | 암호화 | 인증 방식 | 일일 한도 | |--------|------------|------|--------|----------|----------| | Gmail / Google Workspace | `smtp.gmail.com` | 587 | TLS | 앱 비밀번호 | 500건 (무료) / 2,000건 (Workspace) | | 네이버 | `smtp.naver.com` | 587 | TLS | 앱 비밀번호 | 500건 | | 네이버웍스 | `smtp.worksmobile.com` | 587 | TLS | 앱 비밀번호 | 관리자 설정 | | 다음/카카오 | `smtp.daum.net` | 465 | SSL | 앱 비밀번호 | 500건 | | Microsoft 365 | `smtp.office365.com` | 587 | STARTTLS | OAuth2 권장 | 10,000건 | | 카페24 | 업체별 상이 | 587 | TLS | ID/PW | 업체별 상이 | | 가비아 | `smtp.gabia.com` | 587 | TLS | ID/PW | 업체별 상이 | --- ## 3. 연동 방식 (3단계 전략) ### 3.1 Phase 1: 플랫폼 발송 + Reply-To (즉시 적용) ``` ┌─────────────────────────────────────────────────────────┐ │ Phase 1 — 플랫폼 발송 │ │ │ │ SAM 공용 SMTP로 발송하되, 테넌트 브랜딩 적용 │ │ │ │ From: "주일블라인드" │ │ Reply-To: admin@juil-blind.co.kr │ │ Subject: [SAM] 전자서명 요청 │ │ │ │ 테넌트 설정: from_name + reply_to만 입력하면 됨 │ │ provider: 'platform' │ └─────────────────────────────────────────────────────────┘ ``` **장점**: 테넌트가 SMTP 정보를 제공하지 않아도 즉시 사용 가능 **단점**: 발신 주소가 `@sam.codebridge-x.com`으로 보임 (신뢰도 하락 가능) **적합 대상**: 메일 설정이 어렵거나 SMTP 정보를 제공하지 않는 테넌트 ### 3.2 Phase 2: SMTP 릴레이 (핵심 — 대부분의 테넌트) ``` ┌─────────────────────────────────────────────────────────┐ │ Phase 2 — SMTP 릴레이 │ │ │ │ 테넌트의 SMTP 서버를 통해 발송 │ │ │ │ From: "주일블라인드" │ │ Subject: 전자서명 요청 │ │ │ │ 테넌트 설정: SMTP 프리셋 선택 + 계정/비밀번호 입력 │ │ provider: 'smtp' │ │ → 수신자에게는 테넌트 회사 메일에서 온 것처럼 보임 │ └─────────────────────────────────────────────────────────┘ ``` **장점**: 테넌트 도메인으로 발송, 높은 신뢰도 **단점**: 테넌트에게 앱 비밀번호 생성을 요청해야 함 **적합 대상**: SMTP 접근 가능한 대부분의 테넌트 ### 3.3 Phase 3: OAuth2 연동 (고급 — 향후) ``` ┌─────────────────────────────────────────────────────────┐ │ Phase 3 — OAuth2 연동 │ │ │ │ Google / Microsoft 계정 OAuth2 인증 │ │ 비밀번호 저장 없이 토큰 기반 발송 │ │ │ │ provider: 'google_oauth' | 'microsoft_oauth' │ │ → 가장 안전하지만 구현 복잡도 높음 │ └─────────────────────────────────────────────────────────┘ ``` **장점**: 비밀번호 저장 불필요, 보안 최상 **단점**: 구현 복잡도 높음, Google/MS만 지원, Azure AD 등록 필요 **적합 대상**: Google Workspace / Microsoft 365 사용 중견기업 이상 ### 3.4 단계별 구현 우선순위 | Phase | 범위 | 대상 테넌트 | 구현 난이도 | 우선순위 | |-------|------|-----------|------------|---------| | Phase 1 | 플랫폼 발송 + Reply-To | 전체 (기본값) | 낮음 | 🔴 필수 | | Phase 2 | SMTP 릴레이 + 프리셋 | SMTP 정보 제공 가능 | 중간 | 🔴 필수 | | Phase 3 | OAuth2 연동 | Google/MS 365 사용 업체 | 높음 | 🟢 권장 | --- ## 4. SMTP 프리셋 시스템 ### 4.1 프리셋 설정 파일 > **위치**: `/home/aweso/sam/mng/config/mail-presets.php` (MNG) > `/home/aweso/sam/api/config/mail-presets.php` (API) ```php [ 'label' => 'Gmail / Google Workspace', 'host' => 'smtp.gmail.com', 'port' => 587, 'encryption' => 'tls', 'guide_key' => 'gmail', 'daily_limit' => 500, 'notes' => '앱 비밀번호 필요 (2단계 인증 활성화 후 생성)', ], 'naver' => [ 'label' => '네이버 메일', 'host' => 'smtp.naver.com', 'port' => 587, 'encryption' => 'tls', 'guide_key' => 'naver', 'daily_limit' => 500, 'notes' => '네이버 메일 설정 > POP3/SMTP > SMTP 사용 활성화 필요', ], 'naverworks' => [ 'label' => '네이버웍스 (NAVER WORKS)', 'host' => 'smtp.worksmobile.com', 'port' => 587, 'encryption' => 'tls', 'guide_key' => 'naverworks', 'daily_limit' => null, 'notes' => '관리자 콘솔에서 SMTP 허용 설정 필요', ], 'daum' => [ 'label' => '다음/카카오 메일', 'host' => 'smtp.daum.net', 'port' => 465, 'encryption' => 'ssl', 'guide_key' => 'daum', 'daily_limit' => 500, 'notes' => '카카오 계정 > 보안 > 앱 비밀번호 생성', ], 'microsoft365' => [ 'label' => 'Microsoft 365 (Outlook)', 'host' => 'smtp.office365.com', 'port' => 587, 'encryption' => 'tls', 'guide_key' => 'microsoft365', 'daily_limit' => 10000, 'notes' => 'Basic Auth 폐지 주의 — OAuth2 전환 권장', ], 'cafe24' => [ 'label' => '카페24 호스팅 메일', 'host' => '', 'port' => 587, 'encryption' => 'tls', 'guide_key' => 'cafe24', 'daily_limit' => null, 'notes' => 'SMTP 호스트는 호스팅 계정마다 상이 — 수동 입력', ], 'gabia' => [ 'label' => '가비아 호스팅 메일', 'host' => 'smtp.gabia.com', 'port' => 587, 'encryption' => 'tls', 'guide_key' => 'gabia', 'daily_limit' => null, 'notes' => '가비아 메일 관리 > SMTP 설정 확인', ], 'custom' => [ 'label' => '직접 입력 (자체 서버 등)', 'host' => '', 'port' => 587, 'encryption' => 'tls', 'guide_key' => null, 'daily_limit' => null, 'notes' => '호스트, 포트, 암호화 방식을 직접 입력', ], ]; ``` ### 4.2 프리셋 선택 흐름 ``` 본사 관리자가 MNG에서 테넌트 메일 설정 │ ├── 1. 테넌트 선택 │ ├── 2. 발송 방식 선택 │ ├── "SAM 기본" → Phase 1 (from_name + reply_to만 입력) │ └── "자체 SMTP" → Phase 2 (프리셋 선택 진행) │ ├── 3. 프리셋 선택 (자체 SMTP 시) │ ├── [Gmail ▼] 선택 → host/port/encryption 자동 채움 │ ├── [네이버 ▼] 선택 → host/port/encryption 자동 채움 │ └── [직접 입력] 선택 → 모든 필드 수동 입력 │ ├── 4. 계정 정보 입력 │ ├── SMTP 사용자명 (이메일 주소) │ └── SMTP 비밀번호 (앱 비밀번호) │ ├── 5. 연결 테스트 [🔧 테스트] 버튼 │ ├── 성공 → "SMTP 연결 성공 (230ms)" ✅ │ └── 실패 → 에러 메시지 + 트러블슈팅 안내 │ └── 6. 저장 (테스트 통과 시에만 활성화) ``` --- ## 5. MNG 관리 화면 설계 ### 5.1 메뉴 위치 ``` MNG 사이드바 └── 시스템 관리 └── 테넌트 관리 └── 이메일 설정 ← 신규 메뉴 ``` > **라우트**: `GET /system/tenants/{tenant}/mail-config` > **컨트롤러**: `App\Http\Controllers\System\TenantMailConfigController` ### 5.2 화면 구조 — 테넌트 메일 설정 폼 ``` ┌─ 이메일 설정 — [주일블라인드] ──────────────────────────┐ │ │ │ ── 기본 정보 ────────────────────────────────────────── │ │ 발신자명: [주일블라인드 ] │ │ 발신 이메일: [admin@juil-blind.co.kr] │ │ 회신 주소: [admin@juil-blind.co.kr] (선택) │ │ 일일 발송 한도: [500] 건 │ │ │ │ ── 발송 방식 ────────────────────────────────────────── │ │ ● SAM 기본 (플랫폼 SMTP) │ │ 발신 주소: noreply@sam.codebridge-x.com │ │ Reply-To에 위 발신 이메일 자동 적용 │ │ │ │ ○ 자체 SMTP │ │ ┌─ SMTP 설정 ──────────────────────────────────────┐ │ │ │ 프리셋: [Gmail / Google Workspace ▼] │ │ │ │ ℹ️ 앱 비밀번호 필요 (가이드 보기) │ │ │ │ │ │ │ │ 호스트: [smtp.gmail.com ] (자동) │ │ │ │ 포트: [587 ] (자동) │ │ │ │ 암호화: [TLS ▼ ] (자동) │ │ │ │ 사용자명: [admin@juil-blind.co.kr] │ │ │ │ 비밀번호: [•••••••••••••••• ] │ │ │ │ │ │ │ │ [🔧 연결 테스트] │ │ │ │ │ │ │ │ 테스트 결과: │ │ │ │ ✅ SMTP 연결 성공 (응답시간: 230ms) │ │ │ │ ✅ 테스트 메일 발송 완료 │ │ │ │ → admin@juil-blind.co.kr로 전송 │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ ── 브랜딩 (선택) ────────────────────────────────────── │ │ 회사명: [주일블라인드 ] │ │ 로고: [📎 업로드] sam_bi_logo.png │ │ 테마 컬러: [#1a56db] 🎨 │ │ 회사 주소: [서울시 강남구... ] │ │ 연락처: [02-1234-5678 ] │ │ 푸터 문구: [SAM 시스템에서 발송된 메일입니다] │ │ │ │ ── 상태 ─────────────────────────────────────────────── │ │ 설정 상태: ✅ 활성 | 마지막 테스트: 2026-03-11 14:30 │ │ 오늘 발송: 23/500건 | 이번 달 총 발송: 456건 │ │ │ │ [💾 저장] [↩️ 취소] │ └──────────────────────────────────────────────────────────┘ ``` ### 5.3 화면 구조 — 테넌트 목록 (메일 설정 현황) ``` ┌─ 테넌트 이메일 설정 현황 ────────────────────────────────┐ │ │ │ ┌────┬──────────┬──────────┬────────┬───────┬───────┐ │ │ │ ID │ 테넌트명 │ 발송 방식 │ 프리셋 │ 상태 │ 발송 │ │ │ ├────┼──────────┼──────────┼────────┼───────┼───────┤ │ │ │ 1 │ 주일블라인드│ 자체 SMTP│ Gmail │ ✅ 활성│ 23건 │ │ │ │ 2 │ 경동스크린 │ SAM 기본 │ — │ ✅ 활성│ 12건 │ │ │ │ 3 │ 테스트업체 │ 미설정 │ — │ ⚠️ 미설정│ 0건 │ │ │ └────┴──────────┴──────────┴────────┴───────┴───────┘ │ │ │ │ ⚠️ 미설정 테넌트는 플랫폼 기본 SMTP로 발송됩니다 │ └──────────────────────────────────────────────────────────┘ ``` ### 5.4 파일 구조 ``` mng/ ├── app/Http/Controllers/System/ │ └── TenantMailConfigController.php ← 컨트롤러 ├── app/Services/Mail/ │ ├── TenantMailService.php ← 메일 발송 서비스 │ └── SmtpConnectionTester.php ← SMTP 연결 테스트 ├── config/ │ └── mail-presets.php ← SMTP 프리셋 설정 └── resources/views/system/tenant-mail/ ├── index.blade.php ← 테넌트 목록 └── edit.blade.php ← 설정 폼 ``` --- ## 6. SMTP 연결 테스트 ### 6.1 테스트 절차 ``` [🔧 연결 테스트] 클릭 │ ├── 1. SMTP 서버 접속 (TCP 연결) │ └── 실패: "SMTP 서버에 접속할 수 없습니다 (호스트/포트 확인)" │ ├── 2. TLS/SSL 핸드셰이크 │ └── 실패: "암호화 연결 실패 (암호화 방식 확인)" │ ├── 3. 인증 (AUTH LOGIN) │ └── 실패: "인증 실패 (사용자명/비밀번호 확인)" │ ├── 4. 테스트 메일 발송 (선택) │ ├── 수신자: 입력한 from_address (자기 자신에게) │ ├── 제목: "[SAM] SMTP 연결 테스트" │ └── 본문: "이 메일은 SAM 시스템의 SMTP 연결 테스트입니다." │ └── 5. 결과 반환 ├── 응답시간 (ms) ├── 서버 배너 (SMTP EHLO 응답) └── 성공/실패 메시지 ``` ### 6.2 `SmtpConnectionTester` 서비스 > **위치**: `mng/app/Services/Mail/SmtpConnectionTester.php` ```php class SmtpConnectionTester { /** * SMTP 연결 테스트 실행 * * @return array{ * success: bool, * message: string, * response_time_ms: int, * server_banner: string|null, * error_code: string|null * } */ public function test( string $host, int $port, string $encryption, string $username, string $password, ?string $testRecipient = null ): array; } ``` ### 6.3 에러 코드 및 트러블슈팅 | 에러 코드 | 메시지 | 원인 | 해결 방법 | |----------|--------|------|----------| | `CONN_REFUSED` | SMTP 서버 접속 거부 | 호스트/포트 오류, 방화벽 | 호스트/포트 확인, 프리셋 재선택 | | `TLS_FAILED` | TLS 핸드셰이크 실패 | 암호화 방식 불일치 | SSL ↔ TLS 전환 시도 | | `AUTH_FAILED` | 인증 실패 | 비밀번호 오류, 앱 비밀번호 미사용 | 앱 비밀번호 재생성 안내 | | `SMTP_DISABLED` | SMTP 비활성화 | 메일 설정에서 SMTP 미허용 | 메일 서비스 SMTP 활성화 안내 | | `QUOTA_ERROR` | 발송 한도 초과 | 일일 한도 도달 | 시간 경과 후 재시도 | | `TIMEOUT` | 연결 시간 초과 (10초) | 네트워크, 서버 과부하 | 잠시 후 재시도 | --- ## 7. 테넌트 사전 준비 안내 ### 7.1 안내 절차 본사 관리자가 테넌트에게 메일 연동을 위한 사전 준비를 안내한다. ``` 본사 → 테넌트 안내 흐름: │ ├── 1. 테넌트가 사용하는 메일 서비스 확인 │ "현재 회사 메일이 Gmail인가요, 네이버인가요?" │ ├── 2. 해당 서비스의 앱 비밀번호 생성 가이드 전달 │ (SAM 도움말 페이지 링크 또는 PDF) │ ├── 3. 테넌트가 앱 비밀번호 생성 후 본사에 전달 │ ├── SMTP용 이메일 주소 │ └── 앱 비밀번호 (일반 비밀번호 아님) │ └── 4. 본사가 MNG에서 설정 → 연결 테스트 → 완료 통보 ``` ### 7.2 제공자별 앱 비밀번호 생성 안내 #### Gmail / Google Workspace ``` 1. Google 계정 > 보안 > 2단계 인증 활성화 (필수 전제) 2. Google 계정 > 보안 > 앱 비밀번호 3. "앱 선택" → "기타 (맞춤 이름)" → "SAM" 입력 4. 생성된 16자리 비밀번호를 본사에 전달 ``` #### 네이버 메일 ``` 1. 네이버 메일 > 환경설정 > POP3/IMAP 설정 2. "IMAP/SMTP 사용" → "사용함" 체크 3. 네이버 계정 > 보안 설정 > 2단계 인증 활성화 4. "앱 비밀번호 관리" → 앱 이름 "SAM" 입력 → 생성 5. 생성된 비밀번호를 본사에 전달 ``` #### 네이버웍스 ``` 1. 네이버웍스 관리자 콘솔 접속 (관리자 권한 필요) 2. 보안 > 외부 앱 연동 > SMTP 허용 3. 사용할 계정의 이메일/비밀번호를 본사에 전달 (또는 앱 전용 비밀번호 생성) ``` #### 다음/카카오 메일 ``` 1. 다음 메일 > 환경설정 > IMAP/POP > SMTP 사용 체크 2. 카카오 계정 > 보안 > 앱 비밀번호 생성 3. 생성된 비밀번호를 본사에 전달 ``` #### Microsoft 365 ``` 1. Microsoft 365 관리자 센터 > 인증 정책 2. SMTP AUTH 허용 (테넌트 수준 또는 사용자 수준) 3. 해당 사용자 계정의 이메일/비밀번호 전달 ※ Basic Auth 폐지 추세 → OAuth2 연동(Phase 3) 권장 ``` #### 카페24 / 가비아 호스팅 메일 ``` 1. 호스팅 관리 패널에서 SMTP 서버 주소 확인 2. 메일 계정의 이메일/비밀번호를 본사에 전달 (호스팅 메일은 별도 앱 비밀번호 없이 일반 비밀번호 사용) ``` --- ## 8. `tenant_mail_configs` 확장 — `options` JSON > 기존 [이메일 발송 정책](../standards/email-policy.md)의 `options` 구조에 연동 관련 필드를 추가한다. ### 8.1 전체 `options` JSON 구조 ```json { "smtp": { "host": "smtp.gmail.com", "port": 587, "username": "admin@juil-blind.co.kr", "password": "", "encryption": "tls" }, "preset": "gmail", "branding": { "logo_url": "/storage/tenants/1/logo.png", "primary_color": "#1a56db", "company_name": "주일블라인드", "company_address": "서울시 강남구...", "company_phone": "02-1234-5678", "footer_text": "본 메일은 SAM 시스템에서 발송되었습니다." }, "connection_test": { "last_tested_at": "2026-03-11T14:30:00", "last_result": "success", "response_time_ms": 230, "server_banner": "220 smtp.gmail.com ESMTP", "tested_by": "admin@codebridge-x.com" }, "domain_auth": { "spf_verified": false, "dkim_verified": false, "dkim_selector": null, "verified_at": null }, "oauth": { "provider": null, "access_token": null, "refresh_token": null, "expires_at": null } } ``` ### 8.2 `options` 키 설명 | 키 | Phase | 설명 | |-----|-------|------| | `smtp.*` | Phase 2 | 테넌트 SMTP 접속 정보 | | `preset` | Phase 2 | 프리셋 식별자 (`gmail`, `naver`, `custom` 등) | | `branding.*` | Phase 1~2 | 메일 템플릿 브랜딩 정보 | | `connection_test.*` | Phase 2 | 마지막 SMTP 연결 테스트 결과 | | `domain_auth.*` | Phase 2+ | SPF/DKIM 도메인 인증 상태 | | `oauth.*` | Phase 3 | OAuth2 토큰 정보 (향후) | --- ## 9. 도메인 인증 (SPF/DKIM) — Phase 2+ ### 9.1 필요성 Phase 1(플랫폼 발송)에서 테넌트 도메인이 아닌 SAM 도메인으로 발송하면 스팸 분류될 수 있다. 도메인 인증을 통해 SAM이 테넌트 도메인 대신 발송하는 것을 허용한다. ### 9.2 인증 항목 | 인증 | 역할 | 테넌트 조치 | |------|------|------------| | **SPF** | "이 IP에서 보내는 메일은 정상" | 테넌트 DNS에 SAM 발송 IP 추가 | | **DKIM** | 메일 위변조 방지 서명 | SAM이 생성한 TXT 레코드를 DNS에 등록 | | **DMARC** | SPF+DKIM 정책 정의 | 선택 사항 (권장) | ### 9.3 도메인 인증 흐름 (MNG 마법사) ``` 본사 관리자가 MNG에서 도메인 인증 진행 │ ├── 1. 테넌트 도메인 입력 (예: juil-blind.co.kr) │ ├── 2. SAM이 DNS 레코드 자동 생성 │ ├── SPF: "v=spf1 include:sam.codebridge-x.com ~all" │ └── DKIM: sam2026._domainkey.juil-blind.co.kr → 공개키 │ ├── 3. 테넌트 IT 담당자에게 DNS 레코드 추가 요청 │ (안내 메일 또는 텍스트로 전달) │ ├── 4. [인증 확인] 버튼 → DNS 조회로 레코드 확인 │ ├── 성공: domain_auth.spf_verified = true │ └── 실패: "DNS 전파 대기 중 (최대 48시간)" │ └── 5. 인증 완료 시 Phase 1에서도 높은 도달률 확보 ``` > **현실적 판단**: 대부분의 중소기업 테넌트는 DNS 관리 역량이 없으므로, Phase 2(SMTP 릴레이)를 기본으로 권장하고 도메인 인증은 선택 사항으로 제공한다. --- ## 10. API 엔드포인트 ### 10.1 MNG 내부 라우트 | Method | Path | 설명 | |--------|------|------| | `GET` | `/system/tenants/{tenant}/mail-config` | 메일 설정 폼 화면 | | `PUT` | `/system/tenants/{tenant}/mail-config` | 메일 설정 저장 | | `POST` | `/system/tenants/{tenant}/mail-config/test` | SMTP 연결 테스트 | | `GET` | `/system/tenants/mail-config/presets` | 프리셋 목록 JSON | | `GET` | `/system/tenants/mail-config/overview` | 전체 테넌트 설정 현황 | ### 10.2 연결 테스트 API **Request**: ```json POST /system/tenants/1/mail-config/test { "provider": "smtp", "preset": "gmail", "host": "smtp.gmail.com", "port": 587, "encryption": "tls", "username": "admin@juil-blind.co.kr", "password": "xxxx xxxx xxxx xxxx", "send_test_mail": true } ``` **Response (성공)**: ```json { "success": true, "message": "SMTP 연결 성공", "data": { "response_time_ms": 230, "server_banner": "220 smtp.gmail.com ESMTP", "test_mail_sent": true, "test_mail_to": "admin@juil-blind.co.kr" } } ``` **Response (실패)**: ```json { "success": false, "message": "인증 실패 — 앱 비밀번호를 확인하세요", "data": { "error_code": "AUTH_FAILED", "troubleshoot": "Gmail은 앱 비밀번호가 필요합니다. 2단계 인증 활성화 후 앱 비밀번호를 생성하세요.", "guide_url": "/docs/email-setup/gmail" } } ``` --- ## 11. 보안 규칙 ### 11.1 필수 준수 사항 ``` ✅ SMTP 비밀번호는 encrypt()로 암호화 저장 (DB에 평문 금지) ✅ OAuth2 토큰도 encrypt()로 암호화 저장 ✅ 연결 테스트 시 비밀번호는 HTTPS 전송만 허용 ✅ MNG 관리자만 테넌트 메일 설정 접근 가능 (권한: system.tenant.mail) ✅ SMTP 비밀번호는 API 응답/뷰에서 마스킹 처리 (••••••••) ✅ 연결 테스트 결과는 mail_logs가 아닌 options.connection_test에 기록 ``` ### 11.2 금지 사항 ``` ❌ 테넌트가 직접 MNG에서 메일 설정 변경 금지 (본사만 가능) ❌ SMTP 비밀번호를 로그/콘솔에 출력 금지 ❌ 암호화 없이 비밀번호 저장 금지 ❌ 다른 테넌트의 SMTP 설정 조회 금지 (TenantScope 적용) ``` ### 11.3 비밀번호 수명주기 ``` 비밀번호 입력 → encrypt() 암호화 → DB 저장 │ 연결 테스트 시 ← decrypt() 복호화 ────┘ 메일 발송 시 ← decrypt() 복호화 ────┘ 설정 폼 표시 → "••••••••" 마스킹 (원본 미노출) ``` --- ## 12. 구현 체크리스트 ### Phase 1: 플랫폼 발송 (기반) - [ ] `tenant_mail_configs` 마이그레이션 (API) - [ ] `TenantMailConfig` 모델 생성 (API + MNG) - [ ] `TenantMailService` 생성 (MNG) - [ ] MNG 관리 화면 — 기본 설정 폼 (from_name, reply_to) - [ ] 기존 Mailable을 `TenantMailService` 경유로 전환 ### Phase 2: SMTP 릴레이 - [ ] `config/mail-presets.php` 프리셋 설정 파일 생성 - [ ] MNG 관리 화면 — 프리셋 선택 + SMTP 설정 폼 - [ ] `SmtpConnectionTester` 서비스 구현 - [ ] SMTP 연결 테스트 API 구현 - [ ] 프리셋 선택 시 호스트/포트/암호화 자동 채움 (HTMX) - [ ] SMTP 비밀번호 encrypt/decrypt 처리 - [ ] 테넌트별 앱 비밀번호 생성 가이드 페이지 ### Phase 3: OAuth2 연동 (향후) - [ ] Google OAuth2 연동 (Gmail API `gmail.send` scope) - [ ] Microsoft Graph OAuth2 연동 (`Mail.Send` scope) - [ ] OAuth 토큰 자동 갱신 (Refresh Token) - [ ] MNG에서 "Google 연결" / "Microsoft 연결" 버튼 --- ## 관련 문서 - [이메일 발송 정책](../standards/email-policy.md) — 내부 발송 아키텍처, 테이블 설계, 서비스 설계 - [테넌트 DB 구조](../../system/database/tenants.md) — 테넌트 테이블, 데이터 격리 - [options JSON 컬럼 정책](../standards/options-column-policy.md) — options 컬럼 사용 규칙 - [전자서명 기능](../../features/esign/README.md) — 메일 발송이 많은 주요 기능 --- **최종 업데이트**: 2026-03-11