docs: [email] 테넌트 이메일 연동 가이드 추가

- 테넌트 메일 연동 기술문서 신규 작성 (SMTP 프리셋, MNG 관리 화면, 연결 테스트)
- 기존 email-policy.md에 연동 가이드 참조 추가
- INDEX.md에 이메일 연동 문서 등록
This commit is contained in:
김보곤
2026-03-11 22:59:30 +09:00
parent 65b6a27479
commit 73d64d4b03
3 changed files with 745 additions and 1 deletions

View File

@@ -0,0 +1,741 @@
# 테넌트 이메일 연동 가이드
> **작성일**: 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: "주일블라인드" <noreply@sam.codebridge-x.com> │
│ 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: "주일블라인드" <admin@juil-blind.co.kr> │
│ 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
<?php
return [
'gmail' => [
'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": "<encrypted>",
"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

View File

@@ -400,6 +400,7 @@ app(TenantMailService::class)->send(
## 관련 문서
- [테넌트 이메일 연동 가이드](../guides/tenant-email-integration-guide.md) — MNG에서 테넌트 메일 설정, SMTP 프리셋, 연결 테스트
- [테넌트 DB 구조](../../system/database/tenants.md)
- [전자서명 기능](../../features/esign/README.md)
- [급여관리 기능](../../features/finance/payroll.md)