์ง์ ๋ IP๋ก๋ถํฐ SMTP๋ฅผ ํตํด ์์ ๋ ๋ฉ์ผ์ ํค๋/๋ณธ๋ฌธ ๊ธฐ๋ฐ์ผ๋ก ๋ถ๋ฅํ์ฌ ๋ด๋น์์๊ฒ ์ ๋ฌํ๊ฑฐ๋ ๋ด๋ถ ๋ก๊ทธ๋ก ๋จ๊ธฐ๋ ์์คํ .
# ๋ก์ปฌ ์คํ (Node.js + Redis ํ์)
npm install
cp .env.example .env
npm run db:migrate
npm run dev # SMTP ์๋ฒ (localhost:2525)
npm run dev:api # API + ๋์๋ณด๋ (localhost:3000)
# Docker ์คํ (Redis ํฌํจ 4๊ฐ ์ปจํ
์ด๋)
docker compose up -d # SMTP :2525 / API+๋์๋ณด๋ :3000| ๊ตฌ๋ถ | ๊ธฐ์ | ๋น๊ณ |
|---|---|---|
| ๋ฐํ์ | Node.js + TypeScript | strict ๋ชจ๋ |
| SMTP ์์ | smtp-server | Nodemailer ํ๋ก์ ํธ |
| ๋ฉ์ผ ํ์ฑ | mailparser | RFC 822 ํธํ |
| ๋ฉ์ผ ๋ฐ์ | nodemailer | ์ ๋ฌ/ํฌ์๋ฉ |
| ๋น๋๊ธฐ ์์ ํ | BullMQ | Redis ๊ธฐ๋ฐ ์์ ํ |
| ๋ฉ์์ง ๋ธ๋ก์ปค | Redis 7 | BullMQ ๋ฐฑ์๋, ๋ก์ปฌ ๋๋ Docker |
| API ์๋ฒ | Hono | ๊ฒฝ๋ Web Framework |
| ๋์๋ณด๋ | React 19 + Vite 7 | Tailwind CSS v4 |
| ORM | Prisma 6 | SQLite โ PostgreSQL/MySQL ์ ํ ๊ฐ๋ฅ |
| ์ค์ ๊ฒ์ฆ | Zod | ๋ฐํ์ ํ๊ฒฝ๋ณ์ ๊ฒ์ฆ |
| ๋ก๊น | Pino | ๊ตฌ์กฐํ JSON ๋ก๊น |
mail-gateway/
โโโ prisma/
โ โโโ schema.prisma # DB ์คํค๋ง ์ ์
โ โโโ migrations/ # ๋ง์ด๊ทธ๋ ์ด์
ํ์คํ ๋ฆฌ
โโโ scripts/
โ โโโ generate-cert.sh # ์์ฒด์๋ช
TLS ์ธ์ฆ์ ์์ฑ
โโโ src/
โ โโโ config/
โ โ โโโ shared.ts # ๊ณตํต ํ๊ฒฝ๋ณ์ (DB, ๋ก๊ทธ, Redis ๋ฑ)
โ โ โโโ smtp.ts # SMTP ์ ์ฉ ํ๊ฒฝ๋ณ์
โ โ โโโ api.ts # API ์ ์ฉ ํ๊ฒฝ๋ณ์
โ โ โโโ index.ts # SMTP ์ค์ ํ์ ํธํ re-export
โ โ โโโ logger.ts # Pino ๋ก๊ฑฐ ์ค์
โ โโโ api/
โ โ โโโ main.ts # API ์๋ฒ ์ํธ๋ฆฌํฌ์ธํธ (Hono)
โ โ โโโ middleware/
โ โ โ โโโ security.ts # API Key ์ธ์ฆ + ์๋ ์ ํ ๋ฏธ๋ค์จ์ด
โ โ โโโ routes/
โ โ โโโ emails.ts # ์ด๋ฉ์ผ ์กฐํ API
โ โ โโโ tenants.ts # ํ
๋ํธ CRUD API
โ โ โโโ rules.ts # ๋ฃฐ CRUD API
โ โ โโโ forward-logs.ts # ์ ๋ฌ ๋ก๊ทธ ์กฐํ API
โ โ โโโ stats.ts # ๋์๋ณด๋ ํต๊ณ API
โ โโโ db/
โ โ โโโ client.ts # Prisma ํด๋ผ์ด์ธํธ ์ฑ๊ธํด
โ โ โโโ repository.ts # ํ
์ด๋ธ๋ณ CRUD ๋ ํฌ์งํ ๋ฆฌ
โ โ โโโ index.ts # ๋ฐฐ๋ด export
โ โโโ smtp/
โ โ โโโ server.ts # SMTP ์์ ์๋ฒ (์์ โ DB ์ ์ฅ โ ํ ๋ฑ๋ก๋ง ์ํ)
โ โ โโโ index.ts
โ โโโ queue/ # BullMQ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๊ณ์ธต
โ โ โโโ connection.ts # IORedis ์ฐ๊ฒฐ ํฉํ ๋ฆฌ (์ฑ๊ธํด)
โ โ โโโ email-queue.ts # BullMQ Queue ํ๋ก๋์ (addEmailJob)
โ โ โโโ email-processor.ts # BullMQ Worker ์ปจ์๋จธ (ํ์ฑโ๋ถ๋ฅโ์ ๋ฌ)
โ โ โโโ index.ts # ๋ฐฐ๋ด export
โ โโโ parser/
โ โ โโโ index.ts # ๋ฉ์ผ ํ์ฑ (ํค๋, ๋ณธ๋ฌธ, ์ฒจ๋ถ)
โ โโโ keyword/
โ โ โโโ index.ts # ํค์๋ ์ถ์ถ ์์ง
โ โโโ auth/
โ โ โโโ index.ts # DKIM/SPF ๊ฒ์ฆ
โ โโโ classifier/
โ โ โโโ index.ts # ๋ฃฐ ๊ธฐ๋ฐ ๋ถ๋ฅ ์์ง (๋ฉํฐ ํ
๋ํธ ์ง์)
โ โโโ retry/
โ โ โโโ index.ts # API ์๋ ์ฌ์๋ ์ง์ (๋ ๊ฑฐ์)
โ โโโ storage/
โ โ โโโ index.ts # ์ฒจ๋ถํ์ผ ๋์คํฌ ์ ์ฅ
โ โโโ forwarder/
โ โ โโโ index.ts # ๋ฉ์ผ ์ ๋ฌ + ์ ๋ฌ ๋ก๊ทธ
โ โโโ rate-limiter/
โ โ โโโ index.ts # IP๋ณ ์๋ ์ ํ
โ โโโ types/
โ โ โโโ index.ts # ๊ณตํต ํ์
์ ์
โ โโโ cli.ts # ๋ฃฐ + ํ
๋ํธ ๊ด๋ฆฌ CLI
โ โโโ worker.ts # ๋
๋ฆฝ ์์ปค ํ๋ก์ธ์ค ์ํธ๋ฆฌํฌ์ธํธ (pm2์ฉ)
โ โโโ index.ts # SMTP ์ํธ๋ฆฌํฌ์ธํธ (๋ด์ฅ ์์ปค ํฌํจ)
โโโ dashboard/ # React ๋์๋ณด๋ (Vite + Tailwind CSS)
โ โโโ src/
โ โ โโโ components/Layout.tsx # ์ฌ์ด๋๋ฐ ๋ ์ด์์
โ โ โโโ pages/ # ํ์ด์ง ์ปดํฌ๋ํธ๋ค
โ โ โโโ lib/api.ts # ํ์
๋ API ํด๋ผ์ด์ธํธ
โ โ โโโ types.ts # ๋์๋ณด๋ ํ์
์ ์
โ โ โโโ App.tsx # React Router ์ค์
โ โโโ vite.config.ts
โ โโโ package.json
โโโ .env.example # ํ๊ฒฝ๋ณ์ ํ
ํ๋ฆฟ
โโโ Dockerfile # SMTP ์๋ฒ Docker ๋น๋
โโโ Dockerfile.api # API + ๋์๋ณด๋ Docker ๋น๋
โโโ docker-compose.yml # Docker Compose ๊ตฌ์ฑ (Redis ํฌํจ)
โโโ .dockerignore # Docker ๋น๋ ์ ์ธ ๋ชฉ๋ก
โโโ tsconfig.json
โโโ package.json
- npm
- Redis 7+ (BullMQ ๋ฉ์์ง ๋ธ๋ก์ปค์ฉ, ๋ก์ปฌ ์ค์น ๋๋ Docker)
npm installcp .env.example .env
# .env ํ์ผ์ ํธ์งํ์ฌ ๊ฐ ์ค์ ์ฃผ์ ํ๊ฒฝ๋ณ์:
| ๋ณ์ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|
DATABASE_URL |
DB ์ฐ๊ฒฐ ๋ฌธ์์ด | file:./data/mail-gateway.db |
REDIS_URL |
Redis ์ฐ๊ฒฐ URL (BullMQ ํ ๋ฐฑ์๋) | redis://127.0.0.1:6379 |
SMTP_HOST |
SMTP ์์ ๋ฐ์ธ๋ ์ฃผ์ | 0.0.0.0 |
SMTP_PORT |
SMTP ์์ ํฌํธ | 2525 |
SMTP_ALLOWED_IPS |
ํ์ฉ IP ๋ชฉ๋ก (์ผํ ๊ตฌ๋ถ, CIDR ์ง์) | 127.0.0.1 |
RELAY_SMTP_HOST |
๋ฐ์ SMTP ์๋ฒ ํธ์คํธ | - |
RELAY_SMTP_PORT |
๋ฐ์ SMTP ํฌํธ | 587 |
RELAY_SMTP_USER |
๋ฐ์ SMTP ์ธ์ฆ ์ฌ์ฉ์ | - |
RELAY_SMTP_PASS |
๋ฐ์ SMTP ์ธ์ฆ ๋น๋ฐ๋ฒํธ | - |
LOG_LEVEL |
๋ก๊ทธ ๋ ๋ฒจ | info |
ATTACHMENT_STORAGE_DIR |
์ฒจ๋ถํ์ผ ์ ์ฅ ๋๋ ํ ๋ฆฌ | ./data/attachments |
SMTP_TLS_ENABLED |
STARTTLS ํ์ฑํ ์ฌ๋ถ | false |
SMTP_TLS_KEY |
TLS ๊ฐ์ธํค ํ์ผ ๊ฒฝ๋ก | - |
SMTP_TLS_CERT |
TLS ์ธ์ฆ์ ํ์ผ ๊ฒฝ๋ก | - |
SMTP_TLS_CA |
TLS CA ์ธ์ฆ์ ํ์ผ ๊ฒฝ๋ก (์ ํ) | - |
RATE_LIMIT_MAX |
์๋์ฐ๋น IP๋ณ ์ต๋ ์ฐ๊ฒฐ ์ | 100 |
RATE_LIMIT_WINDOW_MS |
์๋ ์ ํ ์๋์ฐ (๋ฐ๋ฆฌ์ด) | 60000 |
API_HOST |
API ์๋ฒ ๋ฐ์ธ๋ ์ฃผ์ | 0.0.0.0 |
API_PORT |
API ์๋ฒ ํฌํธ | 3000 |
MAX_MESSAGE_SIZE_MB |
์์ ๋ฉ์ผ ์ต๋ ํฌ๊ธฐ (MB) | 25 |
API_KEY |
API ์ธ์ฆ ํค (๋ฏธ์ค์ ์ ์ธ์ฆ ๋นํ์ฑํ) | - |
CORS_ORIGINS |
CORS ํ์ฉ ์ค๋ฆฌ์ง (์ผํ ๊ตฌ๋ถ, *์ ์ ์ฒด ํ์ฉ) |
* |
API_RATE_LIMIT_MAX |
API ์๋์ฐ๋น IP๋ณ ์ต๋ ์์ฒญ ์ | 200 |
API_RATE_LIMIT_WINDOW_MS |
API ์๋ ์ ํ ์๋์ฐ (๋ฐ๋ฆฌ์ด) | 60000 |
npm run db:migrate# ๊ฐ๋ฐ ๋ชจ๋ โ ๋จ์ผ ํ๋ก์ธ์ค (SMTP ์๋ฒ + ๋ด์ฅ ์์ปค)
npm run dev
# ๊ฐ๋ฐ ๋ชจ๋ โ ํ๋ก์ธ์ค ๋ถ๋ฆฌ (SMTP ์์ ๊ณผ ์ฒ๋ฆฌ๋ฅผ ๋
๋ฆฝ ์คํ)
npm run dev # SMTP ์๋ฒ (์์ + ํ ๋ฑ๋ก)
npm run dev:worker # ์์ปค (ํ ์ฒ๋ฆฌ: ํ์ฑโ๋ถ๋ฅโ์ ๋ฌ)
# ํ๋ก๋์
npm run build
npm start # SMTP ์๋ฒ + ๋ด์ฅ ์์ปค
# ํ๋ก๋์
โ pm2 ํ๋ก์ธ์ค ๋ถ๋ฆฌ (๊ถ์ฅ)
pm2 start dist/index.js --name mail-gateway
pm2 start dist/worker.js --name mail-workerTip
ํ๋ก์ธ์ค ๋ถ๋ฆฌ ๊ถ์ฅ: ํ๋ก๋์ ํ๊ฒฝ์์๋ SMTP ์์ ๊ณผ ์์ปค๋ฅผ ๋ณ๋ ํ๋ก์ธ์ค๋ก ๋ถ๋ฆฌํ๋ฉด, ์ฒ๋ฆฌ ๋ก์ง์ด ์์ ์ฒ๋ฆฌ๋์ ์ํฅ์ ์ฃผ์ง ์์ ์ต๋ ์์ ์ฑ๋ฅ์ ํ๋ณดํ ์ ์์ต๋๋ค. pm2 ๋ฑ์ ํ๋ก์ธ์ค ๊ด๋ฆฌ์๋ก ์์ปค์ ๋ฌด์ค๋จ ์ด์์ ๋ณด์ฅํ์ธ์.
# ๊ฐ๋ฐ ๋ชจ๋ (API hot reload + Vite dev server)
npm run dev:api # API ์๋ฒ (hot reload)
npm run dev --prefix dashboard # ๋์๋ณด๋ (Vite dev, http://localhost:5173)
# ํ๋ก๋์
(API ์๋ฒ๊ฐ ๋์๋ณด๋ ์ ์ ํ์ผ ์๋น)
npm run build # TypeScript ์ปดํ์ผ
npm run build:dashboard # React ๋์๋ณด๋ ๋น๋
npm run start:api # http://localhost:3000 ์์ API + ๋์๋ณด๋ ์๋นnpm run db:studio # Prisma Studio (DB GUI)
npm run db:generate # Prisma Client ์ฌ์์ฑ
npm run db:migrate:prod # ํ๋ก๋์
๋ง์ด๊ทธ๋ ์ด์
์ ์ฉ
npm run cli # ๋ฃฐ ๊ด๋ฆฌ CLI
npm run generate-cert # ์์ฒด์๋ช
TLS ์ธ์ฆ์ ์์ฑ
npm run dev:worker # ์์ปค ๊ฐ๋ฐ ๋ชจ๋ (hot reload)
npm run start:worker # ์์ปค ํ๋ก๋์
์คํDocker๋ก ์ปจํ ์ด๋ํ๋ ํ๊ฒฝ์์ ์คํํ ์ ์์ต๋๋ค. 4๊ฐ์ ์ปจํ ์ด๋(Redis, SMTP ์๋ฒ, BullMQ ์์ปค, API+๋์๋ณด๋)๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
| ์ปจํ ์ด๋ | ์ด๋ฏธ์ง | ์ญํ | ํฌํธ |
|---|---|---|---|
redis |
redis:7-alpine |
BullMQ ๋ฉ์์ง ๋ธ๋ก์ปค | 6379 |
mail-gateway |
๋น๋ (Dockerfile) |
SMTP ์์ โ DB ์ ์ฅ โ ํ ๋ฑ๋ก | 2525 |
mail-worker |
๋น๋ (Dockerfile) |
BullMQ ์์ปค (ํ์ฑโ๋ถ๋ฅโ์ ๋ฌ) | - |
mail-gateway-api |
๋น๋ (Dockerfile.api) |
REST API + React ๋์๋ณด๋ | 3000 |
# ์ด๋ฏธ์ง ๋น๋ ๋ฐ ์คํ (4๊ฐ ์ปจํ
์ด๋)
docker compose up -d
# ๋ก๊ทธ ํ์ธ
docker compose logs -f redis # Redis
docker compose logs -f mail-gateway # SMTP ์๋ฒ
docker compose logs -f mail-worker # BullMQ ์์ปค
docker compose logs -f mail-gateway-api # API + ๋์๋ณด๋
# ์ค์ง
docker compose down.env ํ์ผ์ ์์ฑํ๋ฉด docker-compose.yml์ด ์๋์ผ๋ก ์ฝ์ต๋๋ค:
cp .env.example .env
# .env ํ์ผ์์ ํ์ํ ๊ฐ ์์ ๋๋ docker compose์์ ์ง์ ํ๊ฒฝ๋ณ์๋ฅผ ์ค๋ฒ๋ผ์ด๋:
SMTP_ALLOWED_IPS=10.0.0.0/8 RATE_LIMIT_MAX=200 docker compose up -d| ๋ณผ๋ฅจ | ์ปจํ ์ด๋ ๊ฒฝ๋ก | ์ฉ๋ |
|---|---|---|
mail-data |
/app/data |
SQLite DB + ์ฒจ๋ถํ์ผ ์ ์ฅ (SMTP, ์์ปค, API ๊ณต์ ) |
redis-data |
/data |
Redis ์์ํ ๋ฐ์ดํฐ |
./certs (๋ฐ์ธ๋) |
/app/certs |
TLS ์ธ์ฆ์ (์ฝ๊ธฐ ์ ์ฉ, SMTP๋ง) |
docker-compose.yml์์ PostgreSQL ๊ด๋ จ ์ฃผ์์ ํด์ ํ๊ณ DATABASE_URL์ ๋ณ๊ฒฝํ์ธ์:
# docker-compose.yml์์:
# 1. mail-gateway, mail-worker์ DATABASE_URL์ PostgreSQL URL๋ก ๋ณ๊ฒฝ
# 2. depends_on ์น์
์ฃผ์ ํด์
# 3. postgres ์๋น์ค ์น์
์ฃผ์ ํด์
# 4. pg-data ๋ณผ๋ฅจ ์ฃผ์ ํด์ ๋ํ prisma/schema.prisma์ provider๋ฅผ "postgresql"๋ก ๋ณ๊ฒฝํ ๋ค ์ด๋ฏธ์ง๋ฅผ ๋ค์ ๋น๋ํ์ธ์:
docker compose build --no-cache
docker compose up -d# ์ด๋ฏธ์ง ๋น๋
docker build -t mail-gateway .
docker build -f Dockerfile.api -t mail-gateway-api .
# Redis ์คํ
docker run -d \
--name mail-gateway-redis \
-p 6379:6379 \
-v redis-data:/data \
redis:7-alpine
# SMTP ์๋ฒ ์คํ
docker run -d \
--name mail-gateway \
-p 2525:2525 \
-v mail-data:/app/data \
-e DATABASE_URL=file:/app/data/mail-gateway.db \
-e REDIS_URL=redis://mail-gateway-redis:6379 \
-e SMTP_HOST=0.0.0.0 \
-e SMTP_PORT=2525 \
-e SMTP_ALLOWED_IPS=127.0.0.1 \
-e LOG_LEVEL=info \
--link mail-gateway-redis \
mail-gateway
# BullMQ ์์ปค ์คํ (๋์ผ ์ด๋ฏธ์ง, ๋ค๋ฅธ CMD)
docker run -d \
--name mail-worker \
-v mail-data:/app/data \
-e DATABASE_URL=file:/app/data/mail-gateway.db \
-e REDIS_URL=redis://mail-gateway-redis:6379 \
-e LOG_LEVEL=info \
--link mail-gateway-redis \
mail-gateway \
sh -c 'npx prisma migrate deploy && node dist/worker.js'
# API + ๋์๋ณด๋ ์คํ
docker run -d \
--name mail-gateway-api \
-p 3000:3000 \
-v mail-data:/app/data \
-e DATABASE_URL=file:/app/data/mail-gateway.db \
-e API_HOST=0.0.0.0 \
-e API_PORT=3000 \
-e LOG_LEVEL=info \
mail-gateway-apiSMTP ์๋ฒ์ ๋ณ๋๋ก ์คํ๋๋ REST API ์๋ฒ์ React ๋์๋ณด๋์ ๋๋ค. ๋์ผํ SQLite DB๋ฅผ ๊ณต์ ํ์ฌ ๋ฉ์ผ, ํ ๋ํธ, ๋ฃฐ, ์ ๋ฌ ๋ก๊ทธ๋ฅผ ์น์์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
- API ์๋ฒ: Hono (Node.js) โ
/api/*์๋ํฌ์ธํธ ์ ๊ณต - ๋์๋ณด๋: React 19 + Vite + Tailwind CSS v4 โ SPA
- ํ๋ก๋์ ์๋น: API ์๋ฒ๊ฐ ๋์๋ณด๋ ์ ์ ํ์ผ์ ์ง์ ์๋น (nginx ๋ถํ์)
- ๊ฐ๋ฐ ๋ชจ๋: Vite dev server(ํฌํธ 5173)๊ฐ API ์๋ฒ(ํฌํธ 3000)๋ก ํ๋ก์
| ๋ฉ์๋ | ๊ฒฝ๋ก | ์ค๋ช |
|---|---|---|
GET |
/api/health |
ํฌ์ค์ฒดํฌ |
GET |
/api/emails |
์ด๋ฉ์ผ ๋ชฉ๋ก (ํ์ด์ง๋ค์ด์ , status/tenantId ํํฐ) |
GET |
/api/emails/:id |
์ด๋ฉ์ผ ์์ธ (์ฒจ๋ถํ์ผ, ์ ๋ฌ ๋ก๊ทธ ํฌํจ) |
POST |
/api/emails/:id/retry |
์คํจ ๋ฉ์ผ ์ฌ์๋ (failed ์ํ๋ง) |
GET |
/api/tenants |
ํ ๋ํธ ๋ชฉ๋ก |
GET |
/api/tenants/:id |
ํ ๋ํธ ์์ธ |
POST |
/api/tenants |
ํ ๋ํธ ์์ฑ |
PUT |
/api/tenants/:id |
ํ ๋ํธ ์์ |
DELETE |
/api/tenants/:id |
ํ ๋ํธ ์ญ์ |
GET |
/api/rules |
๋ฃฐ ๋ชฉ๋ก (tenantId ํํฐ) |
GET |
/api/rules/:id |
๋ฃฐ ์์ธ |
POST |
/api/rules |
๋ฃฐ ์์ฑ |
PUT |
/api/rules/:id |
๋ฃฐ ์์ |
DELETE |
/api/rules/:id |
๋ฃฐ ์ญ์ |
GET |
/api/forward-logs |
์ ๋ฌ ๋ก๊ทธ ๋ชฉ๋ก (ํ์ด์ง๋ค์ด์ , emailId/status ํํฐ) |
GET |
/api/stats |
๋์๋ณด๋ ํต๊ณ (totalEmails, emailsByStatus, emailsToday ๋ฑ) |
Note
๐ ์ ์ฒด API ์คํ์ docs/openapi.yaml (OpenAPI 3.0.3) ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์. ์์ฒญ/์๋ต ์คํค๋ง, ํ๋ผ๋ฏธํฐ ์์ธ, ์๋ฌ ์๋ต ํ์์ด ํฌํจ๋์ด ์์ต๋๋ค.
// ์ฑ๊ณต (๋จ์ผ ๊ฐ์ฒด)
{ "data": { ... } }
// ์ฑ๊ณต (๋ชฉ๋ก + ํ์ด์ง๋ค์ด์
)
{ "data": [...], "total": 100, "page": 1, "limit": 20 }
// ์๋ฌ
{ "error": "์๋ฌ ๋ฉ์์ง" }| ๊ฒฝ๋ก | ํ์ด์ง | ์ค๋ช |
|---|---|---|
/ |
Dashboard | ํต๊ณ ์นด๋, ์ํ ๋ถํฌ, ์ต๊ทผ ์ด๋ฉ์ผ |
/emails |
Emails | ์ด๋ฉ์ผ ๋ชฉ๋ก + ์ํ ํํฐ |
/emails/:id |
Email Detail | ์ด๋ฉ์ผ ์์ธ + ์ฒจ๋ถํ์ผ + ์ ๋ฌ ๋ก๊ทธ |
/tenants |
Tenants | ํ ๋ํธ CRUD ๊ด๋ฆฌ |
/rules |
Rules | ๋ฃฐ CRUD ๊ด๋ฆฌ + ํ ๋ํธ ํํฐ |
/forward-logs |
Forward Logs | ์ ๋ฌ ๋ก๊ทธ ์กฐํ |
SMTP ์์ ์๋ฒ์์ STARTTLS๋ฅผ ์ง์ํ์ฌ ํด๋ผ์ด์ธํธ์์ ํต์ ์ ์ํธํํ ์ ์์ต๋๋ค.
๊ฐ๋ฐ/ํ ์คํธ ํ๊ฒฝ์์๋ ์์ฒด์๋ช ์ธ์ฆ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
npm run generate-cert์ด ์คํฌ๋ฆฝํธ๋ certs/ ๋๋ ํ ๋ฆฌ์ key.pem๊ณผ cert.pem์ ์์ฑํฉ๋๋ค.
ํ๋ก๋์ ํ๊ฒฝ์์๋ Let's Encrypt ๋ฑ์ ๊ณต์ธ ์ธ์ฆ์๋ฅผ ์ฌ์ฉํ์ธ์.
SMTP_TLS_ENABLED=true
SMTP_TLS_KEY=./certs/key.pem
SMTP_TLS_CERT=./certs/cert.pem
# SMTP_TLS_CA=./certs/ca.pem # ์ ํ: CA ์ธ์ฆ์ ์ฒด์ธSMTP_TLS_ENABLED=true์ค์ ์ EHLO ์๋ต์ STARTTLS๊ฐ ๊ด๊ณ ๋ฉ๋๋ค.- ํด๋ผ์ด์ธํธ๋ STARTTLS ๋ช ๋ น์ผ๋ก TLS ์ ๊ทธ๋ ์ด๋๋ฅผ ์์ฒญํ ์ ์์ต๋๋ค.
- TLS ํ์ฑํ ์
key์cert๋ ํ์์ด๋ฉฐ, ํ์ผ์ด ์กด์ฌํ์ง ์์ผ๋ฉด ์๋ฒ๊ฐ ์์๋์ง ์์ต๋๋ค. SMTP_TLS_ENABLED=false(๊ธฐ๋ณธ๊ฐ)์ด๋ฉด ํ๋ฌธ SMTP๋ง ์ง์ํฉ๋๋ค.
IP๋ณ ์ฌ๋ผ์ด๋ฉ ์๋์ฐ ๋ฐฉ์์ ์๋ ์ ํ์ด ์ ์ฉ๋ฉ๋๋ค.
RATE_LIMIT_MAX: ์๋์ฐ ๊ธฐ๊ฐ ๋ด IP๋น ์ต๋ ์ฐ๊ฒฐ ํ์ (๊ธฐ๋ณธ๊ฐ: 100)RATE_LIMIT_WINDOW_MS: ์๋์ฐ ๊ธฐ๊ฐ (๊ธฐ๋ณธ๊ฐ: 60000ms = 1๋ถ)- IPv6-mapped IPv4 ์ฃผ์๋ ์๋ ์ ๊ทํ๋ฉ๋๋ค (
::ffff:192.168.1.1โ192.168.1.1) - ์ ํ ์ด๊ณผ ์ SMTP ์ฐ๊ฒฐ์ด ๊ฑฐ๋ถ๋๋ฉฐ, ์๋ฌ ๋ฉ์์ง์ ํจ๊ป ์ฌ์๋ ๊ฐ๋ฅ ์๊ฐ์ด ์ ๋ฌ๋ฉ๋๋ค
API ์๋ฒ์ ๋ํ ๋ณด์ ๊ธฐ๋ฅ์ ๋๋ค.
API_KEYํ๊ฒฝ๋ณ์๋ฅผ ์ค์ ํ๋ฉด ๋ชจ๋ API ์์ฒญ์ ์ธ์ฆ์ด ํ์ํฉ๋๋ค- ์ธ์ฆ ๋ฐฉ์:
x-api-keyํค๋ ๋๋?api_key=์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ /api/health์๋ํฌ์ธํธ๋ ์ธ์ฆ ์์ด ์ ๊ทผ ๊ฐ๋ฅํฉ๋๋คAPI_KEY๊ฐ ์ค์ ๋์ง ์์ผ๋ฉด ์ธ์ฆ์ด ๋นํ์ฑํ๋ฉ๋๋ค (๊ฐ๋ฐ ํ๊ฒฝ์ฉ)
CORS_ORIGINSํ๊ฒฝ๋ณ์๋ก ํ์ฉํ ์ค๋ฆฌ์ง์ ์ค์ ํฉ๋๋ค*: ๋ชจ๋ ์ค๋ฆฌ์ง ํ์ฉ (๊ธฐ๋ณธ๊ฐ)- ์ผํ ๊ตฌ๋ถ์ผ๋ก ์ฌ๋ฌ ์ค๋ฆฌ์ง ์ง์ :
http://localhost:5173,https://admin.example.com
- IP๋ณ ์ฌ๋ผ์ด๋ฉ ์๋์ฐ ๋ฐฉ์์ผ๋ก API ์์ฒญ์ ์ ํํฉ๋๋ค
API_RATE_LIMIT_MAX: ์๋์ฐ๋น IP๋ณ ์ต๋ ์์ฒญ ์ (๊ธฐ๋ณธ๊ฐ: 200)API_RATE_LIMIT_WINDOW_MS: ์๋์ฐ ๊ธฐ๊ฐ (๊ธฐ๋ณธ๊ฐ: 60000ms = 1๋ถ)- ์ ํ ์ด๊ณผ ์
429 Too Many Requests์๋ต๊ณผRetry-Afterํค๋๋ฅผ ๋ฐํํฉ๋๋ค - ์๋ต ํค๋์
X-RateLimit-Limit,X-RateLimit-Remainingํฌํจ
- ๋ฃฐ ์์ฑ/์์ ์
action๊ฐ์forward | log | archive | reject์ค ํ๋์ฌ์ผ ํฉ๋๋ค conditions๋ ์ ํจํ JSON ๋ฐฐ์ด์ด์ด์ผ ํ๋ฉฐ, ๊ฐ ์กฐ๊ฑด์field,operator,value๋ฅผ Zod๋ก ๊ฒ์ฆํฉ๋๋คaction์ดforward์ผ ๋forwardTo๋ ์ ํจํ ์ด๋ฉ์ผ ํ์์ด์ด์ผ ํฉ๋๋ค- ํ
๋ํธ
settings๋ ์ ํจํ JSON ๊ตฌ์กฐ ({ maxMessageSizeBytes?: number })์ฌ์ผ ํฉ๋๋ค - ์ด๋ฉ์ผ/์ ๋ฌ ๋ก๊ทธ์
statusํํฐ๋ ํ์ฉ๋ ๊ฐ๋ง ์ฌ์ฉํ ์ ์์ต๋๋ค - ํ์ด์ง๋ค์ด์
page๋ ์ต๋ 1000์ผ๋ก ์ ํ๋ฉ๋๋ค
์์ ๋ฉ์ผ์ rcptTo ๋๋ฉ์ธ์ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ํธ๋ฅผ ์๋ณํ์ฌ, ํ ๋ํธ๋ณ๋ก ๋ถ๋ฅ ๋ฃฐ์ ๊ฒฉ๋ฆฌํ ์ ์์ต๋๋ค.
- ๋ฉ์ผ ์์ ์ rcptTo์ ๋๋ฉ์ธ(
@๋ค)์ ์ถ์ถ - ๋ฑ๋ก๋ ํ ๋ํธ์ domains ๋ชฉ๋ก๊ณผ ๋งค์นญ
- ํ ๋ํธ๊ฐ ๋งค์นญ๋๋ฉด: ํ ๋ํธ ์ ์ฉ ๋ฃฐ + ๊ธ๋ก๋ฒ ๋ฃฐ์ ํจ๊ป ํ๊ฐ
- ํ ๋ํธ๊ฐ ์์ผ๋ฉด: ๊ธ๋ก๋ฒ ๋ฃฐ๋ง ํ๊ฐ
# ํ
๋ํธ ๋ชฉ๋ก
npm run cli -- tenant list
# ํ
๋ํธ ์์ธ ์กฐํ
npm run cli -- tenant show <name>
# ํ
๋ํธ ์์ฑ
npm run cli -- tenant create acme-corp --domains acme.com,acme.io
# ํ
๋ํธ ์์
npm run cli -- tenant update acme-corp --domains acme.com,acme.io,acme.dev --enabled true
# ํ
๋ํธ ์ญ์
npm run cli -- tenant delete acme-corp
# ํ
๋ํธ ์ ์ฉ ๋ฃฐ ์์ฑ (--tenant ์ต์
)
npm run cli -- create acme-urgent \
--action forward \
--forward-to ops@acme.com \
--priority 100 \
--category urgent \
--tenant acme-corp \
--conditions '[{"field":"subject","operator":"contains","value":"๊ธด๊ธ"}]'๋ถ๋ฅ ๋ฃฐ์ CLI๋ก ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
# ์ ์ฒด ๋ฃฐ ๋ชฉ๋ก
npm run cli -- list
# ํน์ ๋ฃฐ ์์ธ ์กฐํ
npm run cli -- show <name>
# ๋ฃฐ ์์ฑ
npm run cli -- create urgent-forward \
--action forward \
--forward-to ops@example.com \
--priority 100 \
--category urgent \
--conditions '[{"field":"subject","operator":"contains","value":"๊ธด๊ธ"}]'
# ๋ฃฐ ์์
npm run cli -- update urgent-forward --priority 200 --enabled false
# ๋ฃฐ ์ญ์
npm run cli -- delete urgent-forwardSMTP ์์ ๊ณผ ๋ฉ์ผ ์ฒ๋ฆฌ๊ฐ BullMQ ํ๋ฅผ ํตํด ๋น๋๊ธฐ๋ก ๋ถ๋ฆฌ๋์ด ์์ต๋๋ค. SMTP ์๋ฒ๋ ์์ ๊ณผ DB ์ ์ฅ๋ง ์ํํ๊ณ , ์ค์ ์ฒ๋ฆฌ(ํ์ฑ, ๋ถ๋ฅ, ์ ๋ฌ)๋ ๋ณ๋ ์์ปค ํ๋ก์ธ์ค์์ ์คํ๋ฉ๋๋ค.
โโโ SMTP ์๋ฒ ํ๋ก์ธ์ค โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
์์ (SMTP)
โ
โโ IP ํ์ฉ ํ์ธ
โโ ์๋ ์ ํ ํ์ธ
โโ ํ
๋ํธ ๋งค์นญ (rcptTo ๋๋ฉ์ธ ๊ธฐ๋ฐ)
โ
โผ
DB ์ ์ฅ (์ต์ ๋ฐ์ดํฐ)
โ
โโ envelope (mailFrom, rcptTo, remoteIp)
โโ rawMessage (RFC822 ์๋ฌธ)
โโ status = 'received'
โ
โผ
BullMQ ํ ๋ฑ๋ก
โ
โโ emailId๋ฅผ jobId๋ก ์ฌ์ฉ (๋ฉฑ๋ฑ์ฑ ๋ณด์ฅ)
โโ ์ฑ๊ณต โ 250 OK ์๋ต
โโ ์คํจ โ 451 Temporary failure ์๋ต
โโโ Redis (BullMQ ๋ฉ์์ง ๋ธ๋ก์ปค) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโ BullMQ ์์ปค ํ๋ก์ธ์ค โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
์์ปค๊ฐ ํ์์ ์์
์์
โ
โโ ๋ฉฑ๋ฑ์ฑ ํ์ธ (์ด๋ฏธ ์ฒ๋ฆฌ๋ ์ด๋ฉ์ผ ๊ฑด๋๋ฐ๊ธฐ)
โ
โผ
ํ์ฑ (mailparser)
โ
โโ ํค๋, ๋ณธ๋ฌธ, ์ฒจ๋ถํ์ผ ์ถ์ถ
โโ DKIM/SPF ๊ฒ์ฆ
โโ ํค์๋ ์ถ์ถ (textBody ์ฐ์ , htmlBody ํด๋ฐฑ)
โโ DB ์
๋ฐ์ดํธ (ํ์ฑ ๊ฒฐ๊ณผ ์ ์ฅ)
โโ ์ฒจ๋ถํ์ผ ๋์คํฌ ์ ์ฅ
โ
โผ
๋ถ๋ฅ (classifier)
โ
โโ ํ
๋ํธ ์ ์ฉ ๋ฃฐ + ๊ธ๋ก๋ฒ ๋ฃฐ ์กฐํฉ
โโ ์ฐ์ ์์ ์์ผ๋ก ๋ฃฐ ๋งค์นญ
โโ ์กฐ๊ฑด: subject, from, to, cc, body, header
โโ ์ฐ์ฐ์: contains, equals, startsWith, endsWith, regex, notContains
โ
โผ
์ก์
์คํ
โโ forward โ ์ง์ ์ฃผ์๋ก ๋ฉ์ผ ์ ๋ฌ, ์ ๋ฌ ๋ก๊ทธ ๊ธฐ๋ก
โโ log โ ๋ด๋ถ ๋ก๊ทธ๋ง ๋จ๊น
โโ archive โ ๋ณด๊ด ์ฒ๋ฆฌ
โโ reject โ ๊ฑฐ๋ถ ์ฒ๋ฆฌ
๋ฃฐ ์ก์ ์์ธ:
| ์ก์ | ์ค๋ช | ์ด๋ฉ์ผ ์ํ ๋ณ๊ฒฝ |
|---|---|---|
forward |
forwardTo์ ์ง์ ๋ ์ฃผ์๋ก ๋ฉ์ผ์ SMTP ๋ฆด๋ ์ด๋ฅผ ํตํด ์ ๋ฌํฉ๋๋ค. ์ ๋ฌ ๊ฒฐ๊ณผ๋ ForwardLog์ ๊ธฐ๋ก๋ฉ๋๋ค. |
received โ forwarded (์ฑ๊ณต ์) ๋๋ failed (์คํจ ์) |
log |
๋ณ๋ ์ก์ ์์ด ๋ถ๋ฅ ๊ฒฐ๊ณผ๋ง ๊ธฐ๋กํฉ๋๋ค. ๋ฉ์ผ์ DB์ ๋ณด์กด๋ฉ๋๋ค. | received โ classified |
archive |
๋ฉ์ผ์ ๋ณด๊ด ์ฒ๋ฆฌํฉ๋๋ค. ํฅํ ์กฐํ๋ ๊ฐ๋ฅํ์ง๋ง ์ ๋ฌํ์ง ์์ต๋๋ค. | received โ archived |
reject |
๋ฉ์ผ์ ๊ฑฐ๋ถ ์ฒ๋ฆฌํฉ๋๋ค. SMTP ์ธ์ ์์ ๊ฑฐ๋ถ ์๋ต์ ๋ฐํํฉ๋๋ค. | received โ classified |
์๋๋ DB์ ์ ์ฅ๋๋ Email.status ๊ฐ๋ค์ ์๋ฏธ์ ์ ํ ์์ ์ ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ๋ด๋ถ ๋ก์ง๊ณผ ์ ๋ฌ/๋ถ๋ฅ ํ๋ฆ์์ ๋ค์ ๊ท์น์ ๋ฐ๋ฆ ๋๋ค.
| ์ํ | ์๋ฏธ | ์ ํ ์กฐ๊ฑด / ์ค์ ์ฃผ์ฒด |
|---|---|---|
received |
SMTP๋ก ๋ฉ์ผ์ ์์ ํ์ฌ ์ต์ ๋ฐ์ดํฐ(envelope + rawMessage)๋ง DB์ ์ ์ฅ๋ ์ด๊ธฐ ์ํ. ์์ปค๊ฐ ์์ง ํ์ฑ/๋ถ๋ฅ/์ ๋ฌ์ ์ํํ์ง ์์ ์ํ์ ๋๋ค. | SMTP ์๋ฒ๊ฐ ๋ฉ์ผ์ ์์ ํ๊ณ envelope ์ ๋ณด์ rawMessage๋ฅผ DB์ ์ ์ฅํ ์งํ, BullMQ ํ์ ๋ฑ๋กํ ๋ ๊ธฐ๋ณธ์ผ๋ก ์ค์ ํฉ๋๋ค. |
classified |
๋ถ๋ฅ ๋ฃฐ์ ๋งค์นญ๋์ด ์นดํ
๊ณ ๋ฆฌ๊ฐ ์ง์ ๋ ์ํ. ๋ณดํต ์ก์
์ด log ๋๋ reject์ธ ๊ฒฝ์ฐ์ ์ค์ ๋ฉ๋๋ค. |
๋ถ๋ฅ ์์ง์ด ๋ฃฐ์ ๋งค์นญํ๊ณ , ๋ฃฐ์ action์ด log ๋๋ reject์ผ ๋ ๋ถ๋ฅ ์ฝ๋๊ฐ ์ํ๋ฅผ classified๋ก ์
๋ฐ์ดํธํฉ๋๋ค. |
forwarded |
๋ถ๋ฅ ๋ฃฐ์ ๋ฐ๋ผ ์ง์ ๋ ์ฃผ์๋ก SMTP ๋ฆด๋ ์ด๋ฅผ ํตํด ์ ๋ฌ์ด ์๋ฃ๋ ์ํ. | ๋ถ๋ฅ ์์ง์ด forward ์ก์
์ ์ ํํ๊ณ , forwarder๊ฐ SMTP ๋ฆด๋ ์ด์์ ์ฑ๊ณต ์๋ต์ ๋ฐ์์ ๋ ForwardLog์ Email.status๋ฅผ forwarded๋ก ์
๋ฐ์ดํธํฉ๋๋ค. |
failed |
๋ฉ์ผ ์ ๋ฌ ๋๋ ํฌ์๋ฉ ์๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ํ. forwardTo๊ฐ ์๊ฑฐ๋ SMTP ์ ๋ฌ ์คํจ ์ ๋ฐ์ํฉ๋๋ค. | forward ์ก์
์คํ ์ค forwardTo๊ฐ ๋น์ด์๊ฑฐ๋, SMTP ๋ฆด๋ ์ด์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด forwarder๊ฐ ์ํ๋ฅผ failed๋ก ์ค์ ํ๊ณ ForwardLog.error๋ฅผ ๊ธฐ๋กํฉ๋๋ค. |
archived |
๋ถ๋ฅ ๋ฃฐ์ ์ํด ๋ณด๊ด ์ฒ๋ฆฌ๋ ์ํ. ์กฐํ๋ ๊ฐ๋ฅํ์ง๋ง ์ ๋ฌ์ ํ์ง ์์ต๋๋ค. | ๋ถ๋ฅ ์์ง์ด archive ์ก์
์ ์ ํํ๋ฉด Email.status๋ฅผ archived๋ก ์
๋ฐ์ดํธํฉ๋๋ค. |
๊ฐ ์ก์ ์ด ์ค์ ๋ก ์ํํ๋ ๋์๊ณผ ๊ทธ์ ๋ฐ๋ฅธ ์ด๋ฉ์ผ ์ํ ๋ณ๊ฒฝ์ ๋ช ํํ ์ ๋ฆฌํฉ๋๋ค.
| ์ก์ | ์คํ ๋์ | ์ด๋ฉ์ผ ์ํ ๋ณ๊ฒฝ ๋ฐ ๋ก๊ทธ |
|---|---|---|
forward |
forwardTo์ ์ง์ ๋ ์ฃผ์๋ก ๋ฉ์ผ์ SMTP ๋ฆด๋ ์ด๋ฅผ ํตํด ์ ๋ฌํฉ๋๋ค. ์ ๋ฌ ์ ์๋ณธ ๋ฉ์์ง์ ํ์ํ ํค๋๋ฅผ ํฌํจํด ์ ์กํฉ๋๋ค. |
์ ๋ฌ ์๋๋ ForwardLog ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ฉฐ, ์ฑ๊ณต ์ ForwardLog.status=success์ smtpResponse๋ฅผ ์ ์ฅํ๊ณ Email.status=forwarded๋ก ์
๋ฐ์ดํธํฉ๋๋ค. ์คํจ ์ ForwardLog.status=failed, error ํ๋์ ์๋ฌ ๋ฉ์์ง๋ฅผ ์ ์ฅํ๊ณ Email.status=failed๋ก ์
๋ฐ์ดํธํฉ๋๋ค. |
log |
๋ณ๋์ ์ธ๋ถ ์ ๋ฌ ์์ด ๋ถ๋ฅ ๊ฒฐ๊ณผ๋ง ์์คํ ๋ก๊ทธ์ DB์ ๊ธฐ๋กํฉ๋๋ค. | Email.status๋ฅผ classified๋ก ์ค์ ํฉ๋๋ค. ForwardLog๋ ์์ฑ๋์ง ์์ต๋๋ค. |
archive |
๋ฉ์ผ์ ๋ด๋ถ ์ ์ฅ์์ ๋ณด๊ด ์ฒ๋ฆฌํ์ฌ ๊ฒ์/์กฐํ๋ ๊ฐ๋ฅํ๋๋ก ํฉ๋๋ค. ์ ๋ฌ์ ์ํํ์ง ์์ต๋๋ค. | Email.status๋ฅผ archived๋ก ์ค์ ํฉ๋๋ค. ๋ณด๊ด ๊ด๋ จ ๋ฉํ๋ฐ์ดํฐ๋ Tenant.settings ๋๋ ๋ณ๋ ๋ณด๊ด ํ
์ด๋ธ์ ์ ์ฅ๋ ์ ์์ต๋๋ค. |
reject |
๋ฉ์ผ์ ๊ฑฐ๋ถ ์ฒ๋ฆฌํฉ๋๋ค. SMTP ์ธ์ ๋จ๊ณ์์ ๊ฑฐ๋ถ ์๋ต(5xx)์ ๋ฐํํ๋๋ก ํธ๋ฆฌ๊ฑฐํฉ๋๋ค. | ํ์ฌ ๊ตฌํ์์๋ ๊ฑฐ๋ถ๋ฅผ ๋ณ๋ ์ํ๋ก ๋์ง ์๊ณ archived๋ก ์ค์ ํ๋ฉฐ, ๊ฑฐ๋ถ ๋ก๊ทธ๋ฅผ ๋จ๊น๋๋ค. (์ถํ ๋ณ๋ rejected ์ํ ๋์
๊ฐ๋ฅ) |
์กฐ๊ฑด ์ฐ์ฐ์๋ ํ๋ ๊ฐ๊ณผ ๋น๊ตํ๋ ๋ฐฉ์์
๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์์ผ๋ฉฐ, regex๋ case-insensitive ํ๋๊ทธ๋ฅผ ์ ์ฉํฉ๋๋ค. ์ ํจํ์ง ์์ ์ ๊ท์์ ๊ฒฝ๊ณ ๋ก๊ทธ ํ ํด๋น ์กฐ๊ฑด์ false๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค.
| ์ฐ์ฐ์ | ๋์ ๋ฐฉ์ | ์ถ๊ฐ ๋ฉ๋ชจ |
|---|---|---|
contains |
๋์๋ฌธ์ ๋ฌด์, ๋์ ํ๋์ ์ง์ ๋ฌธ์์ด์ด ํฌํจ๋์ด ์์ผ๋ฉด ๋งค์นญ | ๋ด๋ถ์ ์ผ๋ก both values๋ฅผ ์๋ฌธ์ํํด ๋น๊ตํฉ๋๋ค. |
notContains |
๋์๋ฌธ์ ๋ฌด์, ๋์ ํ๋์ ์ง์ ๋ฌธ์์ด์ด ํฌํจ๋์ด ์์ง ์์ผ๋ฉด ๋งค์นญ | contains์ ์ญ ๋
ผ๋ฆฌ์
๋๋ค. |
equals |
๋์๋ฌธ์ ๋ฌด์, ๋์ ํ๋๊ฐ ์ง์ ๋ฌธ์์ด๊ณผ ์ ํํ ์ผ์นํ๋ฉด ๋งค์นญ | ๊ณต๋ฐฑ ๋ฐ ์ ์ฒด ๋ฌธ์์ด ์ผ์น ๊ธฐ์ค์ ๋๋ค. ํธ๋ฆฌ๋ฐ์ ์ ์ฉ๋ ์ ์์ต๋๋ค. |
startsWith |
๋์๋ฌธ์ ๋ฌด์, ๋์ ํ๋๊ฐ ์ง์ ๋ฌธ์์ด๋ก ์์ํ๋ฉด ๋งค์นญ | ์ ๋์ฌ ๊ฒ์ฌ์ ๋๋ค. |
endsWith |
๋์๋ฌธ์ ๋ฌด์, ๋์ ํ๋๊ฐ ์ง์ ๋ฌธ์์ด๋ก ๋๋๋ฉด ๋งค์นญ | ์ ๋ฏธ์ฌ ๊ฒ์ฌ์ ๋๋ค. |
regex |
์ ๊ทํํ์ ๋งค์นญ. ๋์๋ฌธ์ ๋ฌด์ ํ๋๊ทธ(i)๋ฅผ ์ ์ฉํ์ฌ ๋น๊ต. | ์ ๋ฌ๋ ํจํด์ ๋ํด new RegExp(pattern, 'i')๋ก ์์ฑํฉ๋๋ค. ํจํด์ด ์ ํจํ์ง ์์ผ๋ฉด ๊ฒฝ๊ณ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ณ ํด๋น ์กฐ๊ฑด์ false๋ก ํ๊ฐ๋ฉ๋๋ค. ์บก์ฒ ๊ทธ๋ฃน์ ์ฌ์ฉ ๊ฐ๋ฅํ์ง๋ง ์ฑ๋ฅ ์ํฅ์ ์ ์ํ์ธ์. |
๊ฐ ํ๋๊ฐ ๋ฉ์ผ์ ์ด๋์์ ์ถ์ถ๋๋์ง ์ค๋ช ํฉ๋๋ค.
| ํ๋ | ์ถ์ถ ์์น ๋ฐ ํ์ |
|---|---|
subject |
์ด๋ฉ์ผ์ Subject ํค๋์์ ์ถ์ถ๋ ๋ฌธ์์ด. ํ์ฑ ์ ์ธ์ฝ๋ฉ์ ๋์ฝ๋ฉํ ํ ์ฌ์ฉ๋ฉ๋๋ค. |
from |
From ํค๋์ ๊ฐ, ํ์ ์ด๋ฆ๊ณผ ์ด๋ฉ์ผ ์ฃผ์๋ฅผ ํฌํจํ ํ์์ผ๋ก ์ ์ฅ๋ฉ๋๋ค. ์: "Alice alice@example.com". ๋น๊ต ์ ์ ์ฒด ํค๋ ๋ฌธ์์ด์ ์ฌ์ฉํฉ๋๋ค. |
to |
To ํค๋์์ ์ถ์ถ๋ ์์ ์ ๋ชฉ๋ก ๋ฌธ์์ด. ๋ค์ค ์์ ์๋ comma-separated ํ์์ผ๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค. |
cc |
CC ํค๋์์ ์ถ์ถ๋ ์ฐธ์กฐ์ ๋ชฉ๋ก ๋ฌธ์์ด. ๋ค์ค ์ฐธ์กฐ์๋ comma-separated ํ์์ ๋๋ค. |
body |
๋ณธ๋ฌธ ๋ด์ฉ. ์ฐ์ textBody๋ฅผ ์ฌ์ฉํ๋ฉฐ, textBody๊ฐ ์์ผ๋ฉด htmlBody๋ฅผ ํ๋ฐฑ์ผ๋ก ์ฌ์ฉํฉ๋๋ค. HTML์ ์ฌ์ฉํ ๋๋ ํ๊ทธ๋ฅผ ์ ๊ฑฐํ ํ ์คํธ๋ก ๋น๊ตํฉ๋๋ค. |
header |
์์ ํค๋๋ฅผ ์กฐํํฉ๋๋ค. ์กฐ๊ฑด์ headerName ํ๋๋ฅผ ํจ๊ป ์ ๊ณตํด์ผ ํ๋ฉฐ, ๋ด๋ถ์ ์ผ๋ก ํค๋ ์ด๋ฆ์ ์๋ฌธ์๋ก ๋ณํํ์ฌ ์กฐํํฉ๋๋ค. ์: headerName: "x-priority". |
ForwardLog๋ ๋ฉ์ผ ํฌ์๋ฉ ์ ์ ๋ฌ ์๋์ ๊ฒฐ๊ณผ๋ฅผ ๋จ๊น๋๋ค.
| ์ํ | ์๋ฏธ | ๊ธฐ๋ก ํ๋ |
|---|---|---|
pending |
์ ๋ฌ์ด ์์ง ์๋๋์ง ์์๊ฑฐ๋ ์๋ ๋๊ธฐ ์ค์ธ ์ด๊ธฐ ์ํ | DB ์์ฑ ์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ค์ ๋ฉ๋๋ค. |
success |
SMTP ๋ฆด๋ ์ด๋ฅผ ํตํด ๋์์ ์ ์ ์ ๋ฌ๋์ด ์๋ฒ์ ์๋ต ์๋ฌธ์ด ์ ์ฅ๋ ์ํ | smtpResponse ํ๋์ SMTP ์๋ต ์๋ฌธ์ ์ ์ฅํฉ๋๋ค. |
failed |
์ ๋ฌ ์๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ํ | error ํ๋์ ์๋ฌ ๋ฉ์์ง๋ฅผ, attempts์ ์๋ ํ์๋ฅผ ๊ธฐ๋กํฉ๋๋ค. |
ํ ๋ํธ๋ณ ์ค์ ์ Tenant.settings ํ๋์ JSON ๋ฌธ์์ด๋ก ์ ์ฅ๋ฉ๋๋ค. ํ์ฌ ์ฌ์ฉ๋๋ ์ค์ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
{
"maxMessageSizeBytes": 10485760
}์ค๋ช :
- maxMessageSizeBytes: ์ด ํ ๋ํธ์์ ํ์ฉํ๋ ์ต๋ ๋ฉ์์ง ํฌ๊ธฐ(๋ฐ์ดํธ). ์ ์์๋ 10MB์ ๋๋ค. ํ ๋ํธ ์ค์ ์ด ๋น์ด ์์ผ๋ฉด ๊ธ๋ก๋ฒ ํ๊ฒฝ๋ณ์ MAX_MESSAGE_SIZE_MB๋ฅผ ๋ฐํ์ผ๋ก ํ์ฉ ํฌ๊ธฐ๋ฅผ ๊ณ์ฐํฉ๋๋ค.
์ด๋ฏธ ํ๊ฒฝ๋ณ์ ํ์ MAX_MESSAGE_SIZE_MB๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. ๋์๊ณผ ์ฐ์ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
MAX_MESSAGE_SIZE_MB(ํ๊ฒฝ๋ณ์): ๊ธ๋ก๋ฒ ๊ธฐ๋ณธ ์์ ๋ฉ์์ง ํฌ๊ธฐ ์ ํ(MB ๋จ์). ์:25๋ 25MB๋ฅผ ์๋ฏธํฉ๋๋ค.- ํ
๋ํธ๋ณ๋ก
Tenant.settings.maxMessageSizeBytes๊ฐ ์ค์ ๋์ด ์์ผ๋ฉด ํด๋น ๊ฐ์ด ๊ธ๋ก๋ฒ ๊ฐ์ ๋ฎ์ด์๋๋ค. ํ ๋ํธ ์ค์ ๊ฐ์ ๋ฐ์ดํธ ๋จ์์ ๋๋ค.
์ฐ์ ์์: Tenant.settings.maxMessageSizeBytes (์์ผ๋ฉด ์ฌ์ฉ) โ ํ๊ฒฝ๋ณ์ MAX_MESSAGE_SIZE_MB โ ์ฝ๋ ๋ด ๊ธฐ๋ณธ๊ฐ(25MB).
DB์ ClassificationRule ํ
์ด๋ธ์ ์ ์ฅ๋๋ฉฐ JSON ํ์์ ์กฐ๊ฑด ๋ฐฐ์ด๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
{
"field": "subject",
"operator": "contains",
"value": "๊ธด๊ธ"
}| ํ๋ | ์ค๋ช | ์์ ๊ฐ |
|---|---|---|
subject |
๋ฉ์ผ ์ ๋ชฉ | ๊ธด๊ธ, [์๋ฆผ] |
from |
๋ฐ์ ์ (From ํค๋) | admin@example.com |
to |
์์ ์ (To ํค๋) | support@example.com |
cc |
์ฐธ์กฐ (CC ํค๋) | manager@example.com |
body |
๋ณธ๋ฌธ ๋ด์ฉ (text ์ฐ์ , html ํด๋ฐฑ) | ๊ฒฐ์ ์๋ฃ, ์น์ธ ์์ฒญ |
header |
์์ ํค๋ (headerName ํ๋๋ก ํค๋ ์ด๋ฆ ์ง์ ) |
X-Priority: 1 |
| ์ฐ์ฐ์ | ์ค๋ช | ๋์ ์์ธ |
|---|---|---|
contains |
ํฌํจ ์ฌ๋ถ | ๋์๋ฌธ์ ๋ฌด์. ํ๋ ๊ฐ์ ์ง์ ๋ฌธ์์ด์ด ํฌํจ๋๋ฉด ๋งค์นญ |
notContains |
๋ฏธํฌํจ ์ฌ๋ถ | ๋์๋ฌธ์ ๋ฌด์. ํ๋ ๊ฐ์ ์ง์ ๋ฌธ์์ด์ด ํฌํจ๋์ง ์์ผ๋ฉด ๋งค์นญ |
equals |
์ ํํ ์ผ์น | ๋์๋ฌธ์ ๋ฌด์. ํ๋ ๊ฐ์ด ์ง์ ๋ฌธ์์ด๊ณผ ์์ ํ ๋์ผํ๋ฉด ๋งค์นญ |
startsWith |
์ ๋์ฌ ์ผ์น | ๋์๋ฌธ์ ๋ฌด์. ํ๋ ๊ฐ์ด ์ง์ ๋ฌธ์์ด๋ก ์์ํ๋ฉด ๋งค์นญ |
endsWith |
์ ๋ฏธ์ฌ ์ผ์น | ๋์๋ฌธ์ ๋ฌด์. ํ๋ ๊ฐ์ด ์ง์ ๋ฌธ์์ด๋ก ๋๋๋ฉด ๋งค์นญ |
regex |
์ ๊ทํํ์ ๋งค์นญ | ๋์๋ฌธ์ ๋ฌด์ ํ๋๊ทธ(i) ์ ์ฉ. ์ ํจํ์ง ์์ ์ ๊ท์์ ๋ฌด์๋จ |
"์ ๋ชฉ์ '๊ธด๊ธ'์ด ํฌํจ๋ ๋ฉ์ผ์ opsํ์ ์ ๋ฌ":
{
"name": "urgent-to-ops",
"priority": 100,
"conditions": [
{ "field": "subject", "operator": "contains", "value": "๊ธด๊ธ" }
],
"action": "forward",
"forwardTo": "ops@example.com",
"category": "urgent"
}"ํน์ ๋๋ฉ์ธ์์ ์จ ๋ฉ์ผ์ ๋ณด๊ด ์ฒ๋ฆฌ":
{
"name": "archive-newsletters",
"priority": 10,
"conditions": [
{ "field": "from", "operator": "endsWith", "value": "@newsletter.example.com" }
],
"action": "archive",
"category": "newsletter"
}ClassificationRule์ matchMode ํ๋๋ฅผ ์ถ๊ฐํ์ฌ ์กฐ๊ฑด ๋งค์นญ ๋ฐฉ์์ ์ ์ดํ ์ ์์ต๋๋ค.
- ๊ธฐ๋ณธ๊ฐ:
allโ ๋ชจ๋ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋์ด์ผ ๋ฃฐ์ด ๋งค์นญ๋ฉ๋๋ค (AND) any: ํ๋์ ์กฐ๊ฑด๋ง ์ถฉ์กฑ๋์ด๋ ๋ฃฐ์ด ๋งค์นญ๋ฉ๋๋ค (OR)- API์์ ๋ฃฐ ์์ฑ/์์ ์
matchModeํ๋๋ก ์ค์ ํ ์ ์์ต๋๋ค
์์:
{
"name": "notify-if-from-or-subject",
"priority": 50,
"matchMode": "any",
"conditions": [
{ "field": "from", "operator": "contains", "value": "alerts@example.com" },
{ "field": "subject", "operator": "contains", "value": "์ค์" }
],
"action": "forward",
"forwardTo": "ops@example.com"
}ํ์ฌ SQLite๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฉฐ, PostgreSQL ๋๋ MySQL๋ก ์ ํํ ์ ์์ต๋๋ค.
datasource db {
provider = "postgresql" // "mysql"๋ ๊ฐ๋ฅ
url = env("DATABASE_URL")
}# PostgreSQL
DATABASE_URL="postgresql://user:password@localhost:5432/mail_gateway"
# MySQL
DATABASE_URL="mysql://user:password@localhost:3306/mail_gateway"# ๊ธฐ์กด migrations ๋๋ ํ ๋ฆฌ ์ญ์ ํ
rm -rf prisma/migrations
# ์ DB์ ๋ง๋ ๋ง์ด๊ทธ๋ ์ด์
์์ฑ
npm run db:migrate์ ๋ฌ ์คํจ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ BullMQ์ ๋ด์ฅ ์ฌ์๋ ๋ฉ์ปค๋์ฆ์ ํตํด ์๋ ์ฌ์๋ํฉ๋๋ค.
- ์ฌ์๋ ๋ฐฉ์: BullMQ ๋ด์ฅ exponential backoff
- ์ง์ฐ ๊ณ์ฐ:
delay = 60,000ms ร 2^(attempt-1)(1๋ถ, 2๋ถ, 4๋ถ, 8๋ถ, 16๋ถ) - ์ต๋ 5ํ ์ฌ์๋ ํ ์ต์ข ์คํจ ์ฒ๋ฆฌ
- ์ง์ฐ ๊ณ์ฐ:
- ๋ฉฑ๋ฑ์ฑ ๋ณด์ฅ: emailId๋ฅผ BullMQ jobId๋ก ์ฌ์ฉํ์ฌ ๋์ผ ์ด๋ฉ์ผ์ ์ค๋ณต ์์ ์ ๋ฐฉ์งํฉ๋๋ค
- DB ์ฒดํฌํฌ์ธํธ: ์์ปค๊ฐ ์์ ์ฌ์๋ ์ ์ด๋ฏธ ์๋ฃ๋ ๋จ๊ณ(ํ์ฑ, ์ฒจ๋ถํ์ผ ์ ์ฅ ๋ฑ)๋ ๊ฑด๋๋ฐ์ด ๋ถํ์ํ ์ค๋ณต ์์ ์ ๋ฐฉ์งํฉ๋๋ค
- ์๋ ์ฌ์๋:
POST /api/emails/:id/retry์๋ํฌ์ธํธ๋ก ํน์ ์ด๋ฉ์ผ์ ์ฆ์ ์ฌ์๋ํ ์ ์์ต๋๋ค (์ BullMQ ์์ ์ผ๋ก ๋ฑ๋ก)
์์ ๋ ๋ฉ์ผ์ ๋ณธ๋ฌธ์์ ์๋์ผ๋ก ํค์๋๋ฅผ ์ถ์ถํ์ฌ Email ๋ ์ฝ๋์ ์ ์ฅํฉ๋๋ค.
- ์์ค:
textBody์ฐ์ ,textBody๊ฐ ์์ผ๋ฉดhtmlBody๋ฅผ ํ๋ฐฑ์ผ๋ก ์ฌ์ฉํฉ๋๋ค. HTML์ ํ๊ทธ ์ ๊ฑฐ ํ ์ฒ๋ฆฌํฉ๋๋ค - ์๊ณ ๋ฆฌ์ฆ: TF(Term Frequency) ๊ธฐ๋ฐ์ผ๋ก ์์ 10๊ฐ ํค์๋๋ฅผ ์ ์ ํฉ๋๋ค
- ์ธ์ด: ์๋ฌธ๊ณผ ํ๊ธ์ ๋ชจ๋ ์ง์ํ๋ฉฐ, ๋ถ์ฉ์ด(stopwords)๋ฅผ ํํฐ๋งํฉ๋๋ค
- ๊ฒฐ๊ณผ ์ ์ฅ:
Email.keywordsํ๋์ JSON ๋ฐฐ์ด ๋ฌธ์์ด๋ก ์ ์ฅ๋ฉ๋๋ค
์ ํ์ ๊ธฐ๋ฅ์ผ๋ก, mailauth ํจํค์ง ์ค์น ์ DKIM/SPF ๊ฒ์ฆ์ด ํ์ฑํ๋ฉ๋๋ค. ์ค์น๊ฐ ์์ผ๋ฉด graceful fallback์ผ๋ก ๊ฒ์ฆ์ ๋นํ์ฑํํ๊ณ none ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํฉ๋๋ค.
- ํ์ฑํ ๋ฐฉ๋ฒ:
npm install mailauthํ ์๋ฒ ์ฌ์์ - DKIM ๊ฒฐ๊ณผ:
pass,fail,none,error - SPF ๊ฒฐ๊ณผ:
pass,fail,none,softfail,error - ๊ฒฐ๊ณผ ์ ์ฅ:
Email.dkimResult,Email.spfResultํ๋์ ์ ์ฅ๋ฉ๋๋ค
์์ ๋ ๋ชจ๋ ๋ฉ์ผ์ ์๋ณธ ๋ฐ ํ์ฑ ๊ฒฐ๊ณผ ์ ์ฅ.
| ์ปฌ๋ผ | ํ์ | ์ค๋ช |
|---|---|---|
id |
UUID | PK |
mailFrom |
String | SMTP MAIL FROM |
rcptTo |
String | SMTP RCPT TO |
remoteIp |
String | ๋ฐ์ IP |
messageId |
String? | Message-ID ํค๋ (unique) |
subject |
String? | ์ ๋ชฉ |
fromHeader |
String? | From ํค๋ |
toHeader |
String? | To ํค๋ |
ccHeader |
String? | CC ํค๋ |
textBody |
String? | ํ ์คํธ ๋ณธ๋ฌธ |
htmlBody |
String? | HTML ๋ณธ๋ฌธ |
rawMessage |
String | RFC822 ์๋ฌธ |
status |
String | received / classified / forwarded / failed / archived |
category |
String? | ๋ถ๋ฅ ์นดํ ๊ณ ๋ฆฌ |
matchedRule |
String? | ๋งค์นญ๋ ๋ฃฐ ์ด๋ฆ |
dkimResult |
String? | DKIM ๊ฒ์ฆ ๊ฒฐ๊ณผ (pass / fail / none / error) |
spfResult |
String? | SPF ๊ฒ์ฆ ๊ฒฐ๊ณผ (pass / fail / none / softfail / error) |
keywords |
String? | ์๋ ์ถ์ถ๋ ํค์๋ (JSON ๋ฐฐ์ด ๋ฌธ์์ด) |
tenantId |
String? | ์์ ํ ๋ํธ ID |
Email ์ํ (status) ๊ฐ:
| ์ํ | ์ค๋ช |
|---|---|
received |
SMTP๋ก ์์ ์๋ฃ. ์ต์ ๋ฐ์ดํฐ(envelope + rawMessage)๋ง DB ์ ์ฅ๋จ. ์์ปค์ ํ์ฑ/๋ถ๋ฅ ์ |
classified |
์์ปค๊ฐ ํ์ฑ ํ ๋ถ๋ฅ ๋ฃฐ์ ๋งค์นญํ์ฌ ์นดํ
๊ณ ๋ฆฌ ์ง์ ๋จ (์ก์
์ด log์ธ ๊ฒฝ์ฐ) |
forwarded |
์์ปค๊ฐ ๋ถ๋ฅ ๋ฃฐ์ ๋ฐ๋ผ ์ง์ ์ฃผ์๋ก ์ ๋ฌ ์๋ฃ |
failed |
์์ปค์ ๋ฉ์ผ ์ ๋ฌ ์๋ ์ค ์ค๋ฅ ๋ฐ์ |
archived |
์์ปค๊ฐ ๋ถ๋ฅ ๋ฃฐ์ ์ํด ๋ณด๊ด ์ฒ๋ฆฌ๋จ |
| ์ปฌ๋ผ | ํ์ | ์ค๋ช |
|---|---|---|
id |
UUID | PK |
emailId |
String | ์์ ์ด๋ฉ์ผ ID (FK โ Email) |
filename |
String? | ์ฒจ๋ถํ์ผ ์๋ณธ ํ์ผ๋ช |
contentType |
String | MIME ํ์
(์: application/pdf, image/png) |
size |
Int | ํ์ผ ํฌ๊ธฐ (๋ฐ์ดํธ) |
checksum |
String? | SHA-256 ํด์ (๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ์ฉ) |
storagePath |
String? | ๋์คํฌ ์ ์ฅ ๊ฒฝ๋ก |
| ์ปฌ๋ผ | ํ์ | ์ค๋ช |
|---|---|---|
id |
UUID | PK |
name |
String | ๋ฃฐ ์ด๋ฆ (unique) |
description |
String? | ๋ฃฐ ์ค๋ช |
priority |
Int | ์ฐ์ ์์ (๋์์๋ก ๋จผ์ ํ๊ฐ, ๊ธฐ๋ณธ๊ฐ: 0) |
enabled |
Boolean | ํ์ฑํ ์ฌ๋ถ (๊ธฐ๋ณธ๊ฐ: true) |
conditions |
String | JSON ๋ฐฐ์ด โ ๋งค์นญ ์กฐ๊ฑด ๋ชฉ๋ก |
action |
String | ๋งค์นญ ์ ์คํํ ์ก์
: forward | log | archive | reject |
forwardTo |
String? | ์ ๋ฌ ๋์ ์ด๋ฉ์ผ ์ฃผ์ (action์ด forward์ผ ๋ ํ์) |
category |
String? | ๋ถ๋ฅ ์นดํ ๊ณ ๋ฆฌ ํ๊ทธ |
matchMode |
String | ์กฐ๊ฑด ๋งค์นญ ๋ชจ๋: all (AND, ๊ธฐ๋ณธ๊ฐ) | any (OR) |
tenantId |
String? | ์์ ํ ๋ํธ ID (FK โ Tenant, null์ด๋ฉด ๊ธ๋ก๋ฒ ๋ฃฐ) |
| ์ปฌ๋ผ | ํ์ | ์ค๋ช |
|---|---|---|
id |
UUID | PK |
emailId |
String | ๋์ ์ด๋ฉ์ผ ID (FK โ Email) |
forwardTo |
String | ์ ๋ฌ ๋์ ์ด๋ฉ์ผ ์ฃผ์ |
status |
String | ์ ๋ฌ ์ํ: pending (๋๊ธฐ) | success (์ฑ๊ณต) | failed (์คํจ) |
error |
String? | ์คํจ ์ ์๋ฌ ๋ฉ์์ง |
smtpResponse |
String? | SMTP ์๋ฒ ์๋ต ์๋ฌธ |
attempts |
Int | ์ ๋ฌ ์๋ ํ์ (๊ธฐ๋ณธ๊ฐ: 1) |
nextRetryAt |
DateTime? | ๋ค์ ์ฌ์๋ ์์ ์๊ฐ |
๋ฉํฐ ํ ๋ํธ ๊ฒฉ๋ฆฌ ๋จ์.
| ์ปจ๋ผ | ํ์ | ์ค๋ช |
|---|---|---|
id |
UUID | PK |
name |
String | ํ ๋ํธ ์ด๋ฆ (unique) |
domains |
String | JSON ๋ฐฐ์ด (๋๋ฉ์ธ ๋ชฉ๋ก) |
settings |
String? | JSON ์ค์ |
enabled |
Boolean | ํ์ฑํ ์ฌ๋ถ |
์ค์ ์ด์ ์๋๋ฆฌ์ค๋ณ ๊ตฌ์ฑ ๊ฐ์ด๋์ ๋๋ค.
| # | ์๋๋ฆฌ์ค | ์ค๋ช |
|---|---|---|
| 01 | ๊ณ ๊ฐ ์ง์ ๋ฉ์ผ ๋ผ์ฐํ | ์์ ๋ฉ์ผ์ ์ ๋ชฉ/๋ฐ์ ์ ๊ธฐ๋ฐ์ผ๋ก ๋ด๋น ๋ถ์์ ์๋ ์ ๋ฌ |
| 02 | ๋ฉํฐ ํ ๋ํธ SaaS ๋ฉ์ผ ๊ฒฉ๋ฆฌ | ๋๋ฉ์ธ๋ณ ํ ๋ํธ ๋ถ๋ฆฌ, ํ ๋ํธ ์ ์ฉ ๋ฃฐ๋ก ๋ฉ์ผ ๊ฒฉ๋ฆฌ |
| 03 | ๋ด๋ถ ๋ฉ์ผ ๊ฐ์ฌ ๋ฐ ์ปดํ๋ผ์ด์ธ์ค | ๋ชจ๋ ๋ฉ์ผ์ ์์นด์ด๋ธํ๊ณ ํค์๋ ๊ธฐ๋ฐ ์ปดํ๋ผ์ด์ธ์ค ๊ฐ์ฌ |
| 04 | ๋ชจ๋ํฐ๋ง ์๋ฆผ ์ง์ฝ | Grafana, Prometheus ๋ฑ ์๋ฆผ ๋ฉ์ผ์ ๋ถ๋ฅยท์ง์ฝํ์ฌ ์จ์ฝ ํ์ ์ ๋ฌ |
| 05 | ๊ฐ๋ฐ/์คํ ์ด์ง ๋ฉ์ผ ์บก์ฒ | ๋ฆด๋ ์ด ์์ด ๋ฉ์ผ ํธ๋ฉ์ผ๋ก ์ฌ์ฉ, ๋์๋ณด๋์์ ๋ฐ์ก ๋ฉ์ผ ๊ฒ์ฌ |
| 06 | MX ๋ ์ฝ๋ ๊ธฐ๋ฐ ํ๋ก๋์ ๋ฉ์ผ ์์ | DNS MX โ ๋ฆฌ๋ฒ์ค ํ๋ก์ โ Mail Gateway, STARTTLS ํ๋ก๋์ ๊ตฌ์ฑ |
| 07 | ์ํฐํ๋ผ์ด์ฆ ๋ณตํฉ ๋ผ์ฐํ | ๋ณธ์ฌยท์ํ์ฌ ๋ฉํฐ ํ ๋ํธ, ๋ณตํฉ ๋ฃฐ ์ฒด์ธ, ๋๊ท๋ชจ ํธ๋ํฝ ๋์ |