59 lines
2.4 KiB
Markdown
59 lines
2.4 KiB
Markdown
---
|
|
name: IMX[맥락] IMAP 메일 기능 확장
|
|
description: 왜 이 기능들을 추가하는가, 핵심 결정 근거
|
|
type: context
|
|
---
|
|
|
|
# IMX 맥락 — IMAP 메일 기능 확장
|
|
|
|
## 왜 하는가
|
|
|
|
ERP 시스템에서 메일 확인만 되면 의미가 없음. 거래처 메일 수신 후 바로 답장, 견적서 첨부파일 저장, 담당자 전달까지 워크플로우가 연결되어야 실용적.
|
|
|
|
## 핵심 결정 + 근거
|
|
|
|
### SMTP 서비스 분리 (`userMailSmtpService.ts`)
|
|
- IMAP(수신)과 SMTP(송신)은 프로토콜 자체가 다름
|
|
- 파일 분리로 각각 독립적으로 교체/테스트 가능
|
|
- nodemailer는 이미 설치됨 (추가 의존성 없음)
|
|
|
|
### TipTap v2 선택 (메일 에디터)
|
|
- ProseMirror 기반 → 안정적, 확장 용이
|
|
- `@tiptap/react` 공식 패키지 → Next.js 15 호환
|
|
- Quill보다 번들 크기 작음 (~100KB vs ~200KB)
|
|
- dynamic import (`ssr: false`)로 SSR 문제 회피
|
|
|
|
### 첨부파일 스트리밍
|
|
- 서버에 임시 저장하지 않음 → 디스크 절약, 보안
|
|
- `imapflow client.download()` → `stream/promises pipeline()` → HTTP 응답
|
|
- 대용량 파일도 메모리 부담 없음
|
|
|
|
### 메일 삭제 = Trash 이동
|
|
- 즉시 삭제(`messageDelete`) 대신 Trash 폴더 이동
|
|
- 실수로 삭제 시 복구 가능
|
|
- Gmail/wace.me 모두 Trash 폴더 표준 지원
|
|
|
|
### 폴더 구조 표시
|
|
- `client.list({ statusQuery: { unseen: true } })` 로 미읽음 수 포함
|
|
- 폴더 클릭 시 기존 스트리밍 로직 재활용 (folder 파라미터 추가)
|
|
|
|
### 답장/전달 RFC 준수
|
|
- `inReplyTo`, `references` 헤더 → 메일 클라이언트에서 스레드로 묶임
|
|
- `<blockquote>` 인용 → Gmail/Outlook 모두 올바르게 렌더링
|
|
|
|
## 관련 파일
|
|
|
|
- `backend-node/src/services/userMailImapService.ts` — IMAP 수신 로직
|
|
- `backend-node/src/services/imapConnectionPool.ts` — 커넥션 풀 (건드리지 않음)
|
|
- `backend-node/src/services/mailCache.ts` — TTL 캐시 (건드리지 않음)
|
|
- `frontend/app/(main)/mail/imap/page.tsx` — 메인 UI
|
|
|
|
## 기술 참고
|
|
|
|
- imapflow `client.list()` → statusQuery 옵션으로 unseen 포함
|
|
- imapflow `client.messageMove(seqno, folder)` → UID 기반 이동
|
|
- imapflow `client.download(seqno, partId)` → ReadableStream 반환
|
|
- nodemailer `createTransport({ host, port, secure, auth })` → `sendMail()`
|
|
- RFC 2822 §3.6.4: `In-Reply-To`, `References` 헤더
|
|
- W3C HTML Threading: `<blockquote cite="mid:...">` 권장
|