docs: [email] 테넌트 이메일 연동 가이드 추가
- 테넌트 메일 연동 기술문서 신규 작성 (SMTP 프리셋, MNG 관리 화면, 연결 테스트) - 기존 email-policy.md에 연동 가이드 참조 추가 - INDEX.md에 이메일 연동 문서 등록
This commit is contained in:
4
INDEX.md
4
INDEX.md
@@ -16,7 +16,8 @@
|
||||
| Git 커밋 | `dev/standards/git-conventions.md` | 커밋 메시지, 브랜치 전략 |
|
||||
| 품질 검증 | `dev/standards/quality-checklist.md` | 코드 품질 체크리스트 |
|
||||
| Swagger | `dev/guides/swagger-guide.md` | API 문서 작성법 |
|
||||
| 이메일 | `dev/standards/email-policy.md` | 멀티테넌시 이메일 정책 |
|
||||
| 이메일 정책 | `dev/standards/email-policy.md` | 멀티테넌시 이메일 발송 아키텍처 |
|
||||
| 이메일 연동 | `dev/guides/tenant-email-integration-guide.md` | 테넌트 메일 연동, SMTP 프리셋, MNG 관리 |
|
||||
| 품목관리 | `rules/item-policy.md` | 품목 정책 |
|
||||
| 단가관리 | `rules/pricing-policy.md` | 원가/판매가, 리비전 |
|
||||
| 견적관리 | `features/quotes/README.md` | 견적 시스템, BOM 계산 |
|
||||
@@ -167,6 +168,7 @@ DB 도메인별:
|
||||
| [erp-api-detail.md](dev/guides/erp-api-detail.md) | ERP API 상세 |
|
||||
| [item-master-guide.md](dev/guides/item-master-guide.md) | 품목기준관리 구조 |
|
||||
| [claude-code-to-slack.md](dev/guides/claude-code-to-slack.md) | Claude Code → 슬랙 붙여넣기 가이드 |
|
||||
| [tenant-email-integration-guide.md](dev/guides/tenant-email-integration-guide.md) | 테넌트 이메일 연동 (SMTP 프리셋, MNG 관리 화면, 연결 테스트) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
741
dev/guides/tenant-email-integration-guide.md
Normal file
741
dev/guides/tenant-email-integration-guide.md
Normal 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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user