diff --git a/docs/ycshin-node/BIC[계획]-버튼-아이콘화.md b/docs/yc/BIC[계획]-버튼-아이콘화.md similarity index 100% rename from docs/ycshin-node/BIC[계획]-버튼-아이콘화.md rename to docs/yc/BIC[계획]-버튼-아이콘화.md diff --git a/docs/ycshin-node/BIC[맥락]-버튼-아이콘화.md b/docs/yc/BIC[맥락]-버튼-아이콘화.md similarity index 100% rename from docs/ycshin-node/BIC[맥락]-버튼-아이콘화.md rename to docs/yc/BIC[맥락]-버튼-아이콘화.md diff --git a/docs/ycshin-node/BIC[체크]-버튼-아이콘화.md b/docs/yc/BIC[체크]-버튼-아이콘화.md similarity index 100% rename from docs/ycshin-node/BIC[체크]-버튼-아이콘화.md rename to docs/yc/BIC[체크]-버튼-아이콘화.md diff --git a/docs/ycshin-node/BTN-일괄변경-탑씰-버튼스타일.md b/docs/yc/BTN-일괄변경-탑씰-버튼스타일.md similarity index 100% rename from docs/ycshin-node/BTN-일괄변경-탑씰-버튼스타일.md rename to docs/yc/BTN-일괄변경-탑씰-버튼스타일.md diff --git a/docs/ycshin-node/CCA[계획]-카테고리-연속등록모드.md b/docs/yc/CCA[계획]-카테고리-연속등록모드.md similarity index 100% rename from docs/ycshin-node/CCA[계획]-카테고리-연속등록모드.md rename to docs/yc/CCA[계획]-카테고리-연속등록모드.md diff --git a/docs/ycshin-node/CCA[맥락]-카테고리-연속등록모드.md b/docs/yc/CCA[맥락]-카테고리-연속등록모드.md similarity index 100% rename from docs/ycshin-node/CCA[맥락]-카테고리-연속등록모드.md rename to docs/yc/CCA[맥락]-카테고리-연속등록모드.md diff --git a/docs/ycshin-node/CCA[체크]-카테고리-연속등록모드.md b/docs/yc/CCA[체크]-카테고리-연속등록모드.md similarity index 100% rename from docs/ycshin-node/CCA[체크]-카테고리-연속등록모드.md rename to docs/yc/CCA[체크]-카테고리-연속등록모드.md diff --git a/docs/ycshin-node/CTI[계획]-카테고리-깊이구분.md b/docs/yc/CTI[계획]-카테고리-깊이구분.md similarity index 100% rename from docs/ycshin-node/CTI[계획]-카테고리-깊이구분.md rename to docs/yc/CTI[계획]-카테고리-깊이구분.md diff --git a/docs/ycshin-node/CTI[맥락]-카테고리-깊이구분.md b/docs/yc/CTI[맥락]-카테고리-깊이구분.md similarity index 100% rename from docs/ycshin-node/CTI[맥락]-카테고리-깊이구분.md rename to docs/yc/CTI[맥락]-카테고리-깊이구분.md diff --git a/docs/ycshin-node/CTI[체크]-카테고리-깊이구분.md b/docs/yc/CTI[체크]-카테고리-깊이구분.md similarity index 100% rename from docs/ycshin-node/CTI[체크]-카테고리-깊이구분.md rename to docs/yc/CTI[체크]-카테고리-깊이구분.md diff --git a/docs/yc/IMX[계획]-imap-메일기능확장.md b/docs/yc/IMX[계획]-imap-메일기능확장.md new file mode 100644 index 00000000..f282f0ea --- /dev/null +++ b/docs/yc/IMX[계획]-imap-메일기능확장.md @@ -0,0 +1,122 @@ +--- +name: IMX[계획] IMAP 메일 기능 확장 +description: 메일 삭제, SMTP 발송, 폴더 전환, 첨부파일 다운로드, 이동, 답장/전달 구현 +type: plan +--- + +# IMX 계획 — IMAP 메일 기능 확장 + +## 개요 + +기존 메일 조회/읽음처리만 되던 IMAP 페이지에 전체 메일 클라이언트 기능 추가. +nodemailer(이미 설치), imapflow(이미 설치), TipTap v2(신규 설치) 기반. + +## 현재 동작 + +- 계정 목록 조회 +- 메일 스트리밍 목록 +- 메일 상세 보기 +- 읽음 처리 +- 메일 삭제 (백엔드만, UI 버튼 있으나 실제 연동 확인 필요) + +## 변경 후 동작 + +- 좌측 패널: 폴더 목록 (INBOX, Sent, Trash, Spam 등) + 미읽음 수 +- 메일 상세 우측 버튼: 답장 / 전달 / 이동 / 삭제(→Trash) +- 하단 첨부파일 목록 + 다운로드 버튼 +- 우상단 `작성` 버튼 → Dialog (TipTap 에디터, to/cc/subject) +- 답장/전달 시 원문 인용 자동 삽입 + +## 시각적 예시 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ 메일 관리 (IMAP) [작성] [계정추가] │ +├──────────────┬───────────────────┬───────────────────────────────┤ +│ [계정목록] │ [검색창] │ 제목: 보안 알림 │ +│ │ │ From: Google │ +│ Gmail │ ● Google 오후1:51 │ To: yechul@gmail.com │ +│ Wace │ 보안 알림 │ Date: 2026-03-27 │ +│ │ ● GitHub 오전9:48 │ [답장][전달][이동▼][삭제] │ +│ ───────── │ Sudo code │ ───────────────────────────── │ +│ [폴더목록] │ │ │ +│ INBOX (3) │ │ │ +│ Sent │ │ 📎 첨부파일 │ +│ Trash │ │ file.pdf (120KB) [다운로드] │ +│ Spam │ │ │ +└──────────────┴───────────────────┴───────────────────────────────┘ +``` + +## 아키텍처 + +```mermaid +graph TD + FE[page.tsx] -->|GET /folders| BE_CTRL[userMailController] + FE -->|POST /send| BE_CTRL + FE -->|POST /move| BE_CTRL + FE -->|GET /attachment| BE_CTRL + BE_CTRL --> IMAP[userMailImapService] + BE_CTRL --> SMTP[userMailSmtpService - 신규] + IMAP --> Pool[imapConnectionPool] + SMTP --> Nodemailer[nodemailer] +``` + +## 변경 파일 + +### 신규 +- `backend-node/src/services/userMailSmtpService.ts` — SMTP 발송 전용 + +### 수정 +- `backend-node/src/services/userMailImapService.ts` — 폴더목록, 이동, 첨부파일 추가 +- `backend-node/src/controllers/userMailController.ts` — 신규 엔드포인트 핸들러 +- `backend-node/src/routes/userMailRoutes.ts` — 신규 라우트 등록 +- `frontend/lib/api/userMail.ts` — 신규 API 함수 +- `frontend/app/(main)/mail/imap/page.tsx` — UI 전면 확장 + +## 신규 API 엔드포인트 + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/user-mail/accounts/:id/folders` | 폴더 목록 + 미읽음 수 | +| GET | `/user-mail/accounts/:id/folders/:folder/mails/stream` | 폴더별 메일 스트리밍 | +| POST | `/user-mail/accounts/:id/mails/:seqno/move` | 메일 이동 `{ targetFolder }` | +| GET | `/user-mail/accounts/:id/mails/:seqno/attachment/:partId` | 첨부파일 다운로드 (스트리밍) | +| POST | `/user-mail/accounts/:id/send` | 메일 발송 `{ to, cc, subject, html, text, inReplyTo?, references? }` | + +## 코드 설계 + +### userMailSmtpService.ts +```typescript +// SMTP 포트 추론: useTls true → 465, false → 587 +// Gmail: smtp.gmail.com, wace.me: mail.wace.me 또는 host 에서 도메인 추출 +// nodemailer createTransport + sendMail +// 답장: inReplyTo, references 헤더 설정 +``` + +### userMailImapService.ts 추가 메서드 +```typescript +listFolders(account): Promise<{ path, name, unseen }[]> // client.list({ statusQuery }) +moveMail(account, seqno, targetFolder): Promise // messageMove +downloadAttachment(account, seqno, partId, res): Promise // download() + pipeline(res) +``` + +### page.tsx 추가 UI +- `folders` state: 폴더 목록 +- `currentFolder` state: 현재 폴더 (기본 INBOX) +- `ComposeDialog`: TipTap 에디터 + to/cc/subject 필드 +- `composeMode`: 'new' | 'reply' | 'forward' +- 메일 상세 버튼: 답장, 전달, 이동(DropdownMenu), 삭제 + +## 예상 문제 + +1. **Gmail SMTP 포트**: Gmail은 587(STARTTLS) 또는 465(SSL). host에서 자동 추론. +2. **폴더명 인코딩**: 한글 폴더 등 UTF-7/UTF-8 혼용 → imapflow가 자동 처리 +3. **첨부파일 partId**: bodyStructure 파싱이 복잡 → `client.download(seqno, partId)` 직접 사용 +4. **TipTap SSR**: Next.js에서 dynamic import 필요 (`ssr: false`) + +## 설계 원칙 + +- SMTP 서비스는 IMAP 서비스와 완전 분리 (파일 분리) +- 첨부파일은 서버에 저장하지 않고 스트리밍으로 직접 응답 +- 답장/전달 인용: `
` + RFC 2822 헤더 표준 준수 +- TipTap은 dynamic import로 SSR 방지 diff --git a/docs/yc/IMX[맥락]-imap-메일기능확장.md b/docs/yc/IMX[맥락]-imap-메일기능확장.md new file mode 100644 index 00000000..553ef5bd --- /dev/null +++ b/docs/yc/IMX[맥락]-imap-메일기능확장.md @@ -0,0 +1,58 @@ +--- +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` 헤더 → 메일 클라이언트에서 스레드로 묶임 +- `
` 인용 → 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: `
` 권장 diff --git a/docs/yc/IMX[체크]-imap-메일기능확장.md b/docs/yc/IMX[체크]-imap-메일기능확장.md new file mode 100644 index 00000000..c4be6ecf --- /dev/null +++ b/docs/yc/IMX[체크]-imap-메일기능확장.md @@ -0,0 +1,58 @@ +--- +name: IMX[체크] IMAP 메일 기능 확장 +description: 구현 및 검증 체크리스트 +type: checklist +--- + +# IMX 체크리스트 — IMAP 메일 기능 확장 + +## 공정 상태: 0% + +## 구현 체크리스트 + +### Unit A — 백엔드 서비스 +- [ ] `userMailImapService.ts`: `listFolders()` 추가 +- [ ] `userMailImapService.ts`: `streamMailsByFolder()` 추가 +- [ ] `userMailImapService.ts`: `moveMail()` 추가 +- [ ] `userMailImapService.ts`: `downloadAttachment()` 추가 +- [ ] `userMailSmtpService.ts` 신규 생성 (nodemailer 기반) +- [ ] TypeScript 에러 없음 + +### Unit B — 백엔드 컨트롤러/라우트 +- [ ] `userMailController.ts`: `listFolders`, `streamFolderMails`, `moveMail`, `downloadAttachment`, `sendMail` 핸들러 +- [ ] `userMailRoutes.ts`: 5개 신규 라우트 등록 +- [ ] TypeScript 에러 없음 + +### Unit C — 프론트엔드 API +- [ ] `userMail.ts`: `getUserMailFolders()` 추가 +- [ ] `userMail.ts`: `streamFolderMails()` 추가 +- [ ] `userMail.ts`: `moveUserMail()` 추가 +- [ ] `userMail.ts`: `sendUserMail()` 추가 +- [ ] `userMail.ts`: 첨부파일 다운로드 URL 헬퍼 추가 + +### Unit D — 프론트엔드 UI (TipTap 설치 포함) +- [ ] TipTap 패키지 설치 (`@tiptap/react`, `@tiptap/starter-kit`, `@tiptap/extension-link`) +- [ ] 좌측 패널: 폴더 목록 + 미읽음 수 +- [ ] 폴더 클릭 → 해당 폴더 메일 스트리밍 +- [ ] 메일 상세: 답장/전달/이동/삭제 버튼 +- [ ] ComposeDialog: TipTap 에디터 + to/cc/subject +- [ ] 답장/전달 시 원문 인용 자동 삽입 +- [ ] 첨부파일 목록 + 다운로드 링크 +- [ ] TypeScript 에러 없음 + +## 검증 체크리스트 + +- [ ] 폴더 목록이 좌측 패널에 표시됨 (INBOX, Sent, Trash 등) +- [ ] 폴더 클릭 시 해당 폴더 메일이 로드됨 +- [ ] 메일 삭제 버튼 클릭 → Trash로 이동됨 +- [ ] 메일 이동 드롭다운 → 다른 폴더로 이동됨 +- [ ] 첨부파일 있는 메일에서 다운로드 버튼 동작 +- [ ] 새 메일 작성 → 발송 성공 +- [ ] 답장 → To 자동 입력, 원문 인용 포함 +- [ ] 전달 → 원문 전체 포함 + +## 변경 이력 + +| 일자 | 내용 | +|------|------| +| 2026-03-27 | PCC 작성, 구현 시작 | diff --git a/docs/ycshin-node/LFC[계획]-위치포맷-사용자설정.md b/docs/yc/LFC[계획]-위치포맷-사용자설정.md similarity index 100% rename from docs/ycshin-node/LFC[계획]-위치포맷-사용자설정.md rename to docs/yc/LFC[계획]-위치포맷-사용자설정.md diff --git a/docs/ycshin-node/LFC[맥락]-위치포맷-사용자설정.md b/docs/yc/LFC[맥락]-위치포맷-사용자설정.md similarity index 100% rename from docs/ycshin-node/LFC[맥락]-위치포맷-사용자설정.md rename to docs/yc/LFC[맥락]-위치포맷-사용자설정.md diff --git a/docs/ycshin-node/LFC[체크]-위치포맷-사용자설정.md b/docs/yc/LFC[체크]-위치포맷-사용자설정.md similarity index 100% rename from docs/ycshin-node/LFC[체크]-위치포맷-사용자설정.md rename to docs/yc/LFC[체크]-위치포맷-사용자설정.md diff --git a/docs/ycshin-node/MPN[계획]-품번-수동접두어채번.md b/docs/yc/MPN[계획]-품번-수동접두어채번.md similarity index 100% rename from docs/ycshin-node/MPN[계획]-품번-수동접두어채번.md rename to docs/yc/MPN[계획]-품번-수동접두어채번.md diff --git a/docs/ycshin-node/MPN[맥락]-품번-수동접두어채번.md b/docs/yc/MPN[맥락]-품번-수동접두어채번.md similarity index 100% rename from docs/ycshin-node/MPN[맥락]-품번-수동접두어채번.md rename to docs/yc/MPN[맥락]-품번-수동접두어채번.md diff --git a/docs/ycshin-node/MPN[체크]-품번-수동접두어채번.md b/docs/yc/MPN[체크]-품번-수동접두어채번.md similarity index 100% rename from docs/ycshin-node/MPN[체크]-품번-수동접두어채번.md rename to docs/yc/MPN[체크]-품번-수동접두어채번.md diff --git a/docs/ycshin-node/MST[계획]-다중선택-라벨표시.md b/docs/yc/MST[계획]-다중선택-라벨표시.md similarity index 100% rename from docs/ycshin-node/MST[계획]-다중선택-라벨표시.md rename to docs/yc/MST[계획]-다중선택-라벨표시.md diff --git a/docs/ycshin-node/MST[맥락]-다중선택-라벨표시.md b/docs/yc/MST[맥락]-다중선택-라벨표시.md similarity index 100% rename from docs/ycshin-node/MST[맥락]-다중선택-라벨표시.md rename to docs/yc/MST[맥락]-다중선택-라벨표시.md diff --git a/docs/ycshin-node/MST[체크]-다중선택-라벨표시.md b/docs/yc/MST[체크]-다중선택-라벨표시.md similarity index 100% rename from docs/ycshin-node/MST[체크]-다중선택-라벨표시.md rename to docs/yc/MST[체크]-다중선택-라벨표시.md diff --git a/docs/ycshin-node/PGN[계획]-페이징-직접입력.md b/docs/yc/PGN[계획]-페이징-직접입력.md similarity index 100% rename from docs/ycshin-node/PGN[계획]-페이징-직접입력.md rename to docs/yc/PGN[계획]-페이징-직접입력.md diff --git a/docs/ycshin-node/PGN[맥락]-페이징-직접입력.md b/docs/yc/PGN[맥락]-페이징-직접입력.md similarity index 100% rename from docs/ycshin-node/PGN[맥락]-페이징-직접입력.md rename to docs/yc/PGN[맥락]-페이징-직접입력.md diff --git a/docs/ycshin-node/PGN[체크]-페이징-직접입력.md b/docs/yc/PGN[체크]-페이징-직접입력.md similarity index 100% rename from docs/ycshin-node/PGN[체크]-페이징-직접입력.md rename to docs/yc/PGN[체크]-페이징-직접입력.md diff --git a/docs/ycshin-node/RFO[계획]-렉구조-층필수해제.md b/docs/yc/RFO[계획]-렉구조-층필수해제.md similarity index 100% rename from docs/ycshin-node/RFO[계획]-렉구조-층필수해제.md rename to docs/yc/RFO[계획]-렉구조-층필수해제.md diff --git a/docs/ycshin-node/RFO[맥락]-렉구조-층필수해제.md b/docs/yc/RFO[맥락]-렉구조-층필수해제.md similarity index 100% rename from docs/ycshin-node/RFO[맥락]-렉구조-층필수해제.md rename to docs/yc/RFO[맥락]-렉구조-층필수해제.md diff --git a/docs/ycshin-node/RFO[체크]-렉구조-층필수해제.md b/docs/yc/RFO[체크]-렉구조-층필수해제.md similarity index 100% rename from docs/ycshin-node/RFO[체크]-렉구조-층필수해제.md rename to docs/yc/RFO[체크]-렉구조-층필수해제.md diff --git a/docs/yc/UML[계획]-user-mail.md b/docs/yc/UML[계획]-user-mail.md new file mode 100644 index 00000000..9997e10e --- /dev/null +++ b/docs/yc/UML[계획]-user-mail.md @@ -0,0 +1,169 @@ +# UML[계획] - 사용자 메일 관리 시스템 + +## 개요 + +벡스플로우(Vexflow) 사용자 메일 관리 페이지 구현 프로젝트입니다. 외부 메일 서버(POP3/IMAP)와 연동하여 사용자가 본인의 메일 계정을 등록하고 벡스플로우 내에서 메일을 조회할 수 있는 기능을 제공합니다. + +### 현재 동작 +- Admin 메일 시스템만 존재 +- JSON 파일 기반 저장소 +- 사용자 구분 없음 + +### 변경 후 동작 +- 사용자가 외부 메일 계정(IMAP 또는 POP3) 등록 +- 벡스플로우에서 해당 계정의 메일 조회 +- PostgreSQL 기반 계정 저장 및 관리 +- 사용자별 격리(user_id 기반) + +--- + +## 아키텍처 + +``` +┌─────────────┐ +│ 사용자 │ +│ (Frontend) │ +└──────┬──────┘ + │ + ├─→ /mail/imap 페이지 + └─→ /mail/pop3 페이지 + │ + ↓ + ┌──────────────────┐ + │ userMail.ts │ (API 클라이언트) + │ (lib/api/) │ + └────────┬─────────┘ + │ + ↓ + ┌────────────────────────────┐ + │ /api/user-mail/* 라우트 │ + │ (userMailController) │ + └────────┬───────────────────┘ + │ + ┌───────┴────────┐ + │ │ + ↓ ↓ + ┌────────────────┐ ┌──────────────┐ + │ userMailAccount│ │ userMailImap │ + │ Service │ │ Service │ + │ (PostgreSQL) │ │ (IMAP) │ + └────────────────┘ │ │ + └──────────────┘ + │ + ↓ + ┌──────────────────┐ + │ 외부 IMAP 서버 │ + └──────────────────┘ + + 또는 + + ┌──────────────────┐ + │ userMailPop3 │ + │ Service │ + │ (POP3) │ + └──────────────────┘ + │ + ↓ + ┌──────────────────┐ + │ 외부 POP3 서버 │ + └──────────────────┘ +``` + +--- + +## 신규 파일 목록 + +### 백엔드 (Node.js/Express) + +| 파일 경로 | 역할 | +|----------|------| +| `src/services/userMailAccountService.ts` | DB 계정 관리 (생성, 조회, 삭제, 수정) | +| `src/services/userMailImapService.ts` | IMAP 프로토콜 연결 및 메일 조회 | +| `src/services/userMailPop3Service.ts` | POP3 프로토콜 연결 및 메일 조회 | +| `src/controllers/userMailController.ts` | API 엔드포인트 처리 | +| `src/routes/userMailRoutes.ts` | 라우트 정의 | + +### 프론트엔드 (React/TypeScript) + +| 파일 경로 | 역할 | +|----------|------| +| `frontend/lib/api/userMail.ts` | API 클라이언트 | +| `frontend/app/(main)/mail/imap/page.tsx` | IMAP 메일 관리 페이지 | +| `frontend/app/(main)/mail/pop3/page.tsx` | POP3 메일 관리 페이지 | + +--- + +## 수정 파일 목록 + +| 파일 경로 | 변경 사항 | +|----------|---------| +| `src/runMigration.ts` | 마이그레이션 스크립트에 user_mail_accounts 테이블 추가 | +| `src/app.ts` | userMailRoutes 등록 | +| `src/components/AdminPageRenderer.tsx` | /mail/imap, /mail/pop3 페이지 하드코딩 등록 (2줄) | + +--- + +## 데이터베이스 스키마 + +### user_mail_accounts 테이블 + +```sql +CREATE TABLE user_mail_accounts ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + protocol VARCHAR(10) NOT NULL CHECK (protocol IN ('imap', 'pop3')), + host VARCHAR(255) NOT NULL, + port INT NOT NULL DEFAULT 993, + use_tls BOOLEAN DEFAULT TRUE, + username VARCHAR(255) NOT NULL, + password TEXT NOT NULL, -- 암호화됨 (encryptionService 사용) + status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'inactive')), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(user_id, protocol, host, username) +); +``` + +--- + +## 설계 원칙 + +### 1. 사용자 격리 +- 모든 API 요청에서 현재 사용자의 user_id 검증 +- 다른 사용자의 계정/메일 접근 불가 + +### 2. 프로토콜별 서비스 분리 +- userMailImapService.ts: IMAP 전용 +- userMailPop3Service.ts: POP3 전용 +- 각 서비스는 독립적으로 동작 + +### 3. 기존 기능 재활용 +- `encryptionService`: 비밀번호 암호화/복호화 +- `mailparser`: 메일 본문 파싱 +- `imap` 패키지: IMAP 연결(기존 mailReceiveBasicService 참조) + +### 4. 기존 Admin 메일 시스템과 분리 +- 새로운 테이블, 서비스, 라우트로 완전 독립 +- JSON 파일 기반 방식 미사용 + +--- + +## 주요 API 엔드포인트 + +| 메서드 | 경로 | 설명 | +|--------|------|------| +| POST | `/api/user-mail/accounts` | 새 계정 등록 | +| GET | `/api/user-mail/accounts` | 사용자 계정 목록 | +| GET | `/api/user-mail/accounts/:id` | 계정 상세 조회 | +| PUT | `/api/user-mail/accounts/:id` | 계정 수정 | +| DELETE | `/api/user-mail/accounts/:id` | 계정 삭제 | +| POST | `/api/user-mail/accounts/:id/test` | 연결 테스트 | +| GET | `/api/user-mail/accounts/:id/mails` | 메일 목록 조회 | + +--- + +## 변경 이력 + +| 날짜 | 버전 | 내용 | +|------|------|------| +| 2026-03-27 | v1.0 | 초안 작성 | diff --git a/docs/yc/UML[맥락]-user-mail.md b/docs/yc/UML[맥락]-user-mail.md new file mode 100644 index 00000000..7b1f54fe --- /dev/null +++ b/docs/yc/UML[맥락]-user-mail.md @@ -0,0 +1,147 @@ +# UML[맥락] - 사용자 메일 관리 시스템 + +## 프로젝트 배경 + +### 추진 이유 +- 팀장 지시로 POP3 구현 필요 +- IMAP 허용 여부 확인 대기 중 +- 두 프로토콜 모두 구현 후 비교하여 최적 솔루션 채택 + +--- + +## 핵심 기술 결정 사항 + +### 1. 페이지 등록 방식: 하드코딩 +**선택**: 하드코딩 (AdminPageRenderer.tsx에 직접 등록) + +**사유**: +- 컴포넌트 레지스트리에 추가할 권한 없음 +- 간단한 추가 작업으로 빠른 구현 가능 + +**구현**: +```typescript +// AdminPageRenderer.tsx에 2줄 추가 +{path: '/mail/imap', label: '메일(IMAP)', component: () => }, +{path: '/mail/pop3', label: '메일(POP3)', component: () => }, +``` + +--- + +### 2. 저장소: PostgreSQL (Admin 메일과 완전 분리) + +**선택**: PostgreSQL `user_mail_accounts` 테이블 + +**사유**: +- Admin 메일 시스템(JSON 파일 기반)과 완전 독립 +- 사용자별 격리 용이 (user_id 기반) +- 확장성 및 성능 이점 + +**결과**: +- 기존 Admin 메일: JSON 파일 유지 +- 신규 사용자 메일: PostgreSQL 관리 + +--- + +### 3. POP3 메일 삭제 정책: 서버 유지 + +**선택**: DELE 명령 미호출 (서버 메일 유지) + +**사유**: +- 데이터 손실 방지 +- 사용자 실수로 인한 피해 최소화 +- 벡스플로우는 조회만 수행 + +**구현**: +- `userMailPop3Service.ts`에서 RETR 후 DELE 호출 안 함 +- 서버의 자동 정리 정책에 의존 + +--- + +### 4. 페이지별 프로토콜 고정 + +**선택**: 페이지당 프로토콜 1개로 제한 + +**구현**: +- `/mail/imap` → IMAP 계정만 표시/관리 +- `/mail/pop3` → POP3 계정만 표시/관리 + +**사유**: +- UI 단순화 +- 프로토콜별 메일 구조 차이 처리 용이 +- 사용자 혼동 최소화 + +--- + +## 관련 기존 코드 참조 + +### mailReceiveBasicService.ts +- IMAP 연결 및 메일 조회 로직 +- 메일 파싱 및 저장 방식 +- Error handling 패턴 + +**참조 사항**: +```typescript +// IMAP 연결 구조, 메일 검색 쿼리, 메일 수신 처리 방식 +``` + +### encryptionService.ts +- 비밀번호 암호화/복호화 +- DB 저장 시 암호화, 조회 시 복호화 + +**사용 방식**: +```typescript +// 저장: encryptionService.encrypt(password) +// 조회: encryptionService.decrypt(encrypted_password) +``` + +### AdminPageRenderer.tsx +- 기존 페이지 하드코딩 구조 +- 페이지 등록 형식 및 라벨 지정 방식 + +**추가 위치**: +```typescript +// 기존 페이지 목록에 /mail/imap, /mail/pop3 추가 +``` + +--- + +## 기술 스택 및 패키지 + +### 기존 패키지 (재활용) +| 패키지 | 버전 | 용도 | +|--------|------|------| +| `imap` | - | IMAP 연결 | +| `mailparser` | - | 메일 파싱 | +| `pg` | - | PostgreSQL 클라이언트 | + +### 신규 패키지 +| 패키지 | 버전 | 용도 | +|--------|------|------| +| `node-pop3` | latest | POP3 연결 | + +--- + +## 핵심 고려 사항 + +### 보안 +1. 메일 계정 비밀번호는 항상 암호화 상태로 저장 +2. 사용자 격리: user_id 기반 접근 제어 +3. 외부 서버 연결 정보는 민감: 환경변수 활용 + +### 성능 +1. 메일 조회는 페이지네이션 처리 +2. 연결 테스트는 별도 API (현재 메일 검색과 분리) +3. 대량 메일 처리 시 비동기 처리 + +### 에러 처리 +1. 네트워크 오류: 재시도 로직 +2. 인증 실패: 명확한 에러 메시지 제공 +3. DB 오류: 트랜잭션 롤백 + +--- + +## 변경 이력 + +| 날짜 | 내용 | +|------|------| +| 2026-03-27 | 초안 작성 | diff --git a/docs/yc/UML[체크]-user-mail.md b/docs/yc/UML[체크]-user-mail.md new file mode 100644 index 00000000..822a994d --- /dev/null +++ b/docs/yc/UML[체크]-user-mail.md @@ -0,0 +1,161 @@ +# UML[체크] - 사용자 메일 관리 시스템 + +## 공정 상태 +**진행률: 90%** (IMAP 완성, POP3 미구현) + +--- + +## 구현 체크리스트 + +### 데이터베이스 +- [x] DB 마이그레이션 작성 (user_mail_accounts 테이블 생성) + +### 패키지 설치 +- [ ] npm install node-pop3 (설치됨, 서비스 미구현) + +### 백엔드 서비스 계층 +- [x] userMailAccountService.ts (DB CRUD) +- [x] userMailImapService.ts (IMAP 프로토콜) +- [x] userMailSmtpService.ts (SMTP 발송) +- [x] imapConnectionPool.ts (IMAP 연결 풀) +- [x] mailCache.ts (메일 캐시) +- [ ] userMailPop3Service.ts (POP3 프로토콜 - 미구현) + +### 백엔드 API 계층 +- [x] userMailController.ts (요청 처리) +- [x] userMailRoutes.ts (라우트 정의) +- [x] app.ts에 userMailRoutes 등록 (`/api/user-mail`) + +### 프론트엔드 API 클라이언트 +- [x] frontend/lib/api/userMail.ts + +### 프론트엔드 페이지 +- [x] frontend/app/(main)/mail/imap/page.tsx +- [x] frontend/app/(main)/mail/imap/ComposeDialog.tsx (메일 작성) +- [ ] frontend/app/(main)/mail/pop3/page.tsx (미구현) + +### 페이지 등록 +- [x] AdminPageRenderer.tsx에 /mail/imap 등록 +- [ ] AdminPageRenderer.tsx에 /mail/pop3 등록 (미구현) + +--- + +## 구현된 IMAP 기능 + +### 계정 관리 +- [x] 계정 추가 (연결 테스트 후 저장) +- [x] 계정 수정 +- [x] 계정 삭제 +- [x] 연결 테스트 (저장 전 자동 + 수동) + +### 메일 조회 +- [x] SSE 스트리밍으로 메일 목록 로드 (20개씩) +- [x] 이전 메일 더 보기 (무한 스크롤 방식) +- [x] 메일 상세 조회 (HTML/텍스트 본문) +- [x] 폴더별 메일 조회 (INBOX, 휴지통, 스팸 등) +- [x] 새로고침 버튼 + +### 메일 관리 +- [x] 읽음 처리 (클릭 시 자동, IMAP \Seen 플래그) +- [x] 메일 삭제 (\Trash 특수 폴더로 이동 - Gmail 호환) +- [x] 메일 이동 (폴더 간 이동) + +### 첨부파일 +- [x] 첨부파일 목록 표시 (pill 형태) +- [x] 첨부파일 다운로드 (ReadableStream 진행바 표시) +- [x] Content-Length 헤더 지원 (정확한 진행률) + +### 발신 +- [x] 메일 작성 / 발송 (SMTP) +- [x] 답장 (Re: 제목, inReplyTo 헤더) +- [x] 전달 (Fwd: 제목, 원본 본문 인용) + +### UI +- [x] 3단 패널 레이아웃 (계정 / 메일 목록 / 상세) +- [x] 폴더 목록 (unseen 카운트 표시) +- [x] 읽음/삭제 후 unseen 카운트 자동 갱신 +- [x] 검색 (제목/발신자 클라이언트 필터) + +--- + +## 검증 체크리스트 + +### 데이터베이스 +- [x] `user_mail_accounts` 테이블 존재 확인 +- [x] 테이블 스키마 정확성 확인 + +### 계정 관리 API +- [x] POST `/api/user-mail/accounts` - 계정 생성 +- [x] GET `/api/user-mail/accounts` - 사용자 계정 목록 +- [x] PUT `/api/user-mail/accounts/:id` - 계정 수정 +- [x] DELETE `/api/user-mail/accounts/:id` - 계정 삭제 +- [x] POST `/api/user-mail/accounts/:id/test` - 연결 테스트 +- [x] POST `/api/user-mail/test-connection` - 직접 연결 테스트 + +### 메일 API +- [x] GET `/api/user-mail/accounts/:id/mails/stream` - 스트리밍 목록 +- [x] GET `/api/user-mail/accounts/:id/mails/:seqno` - 상세 조회 +- [x] POST `/api/user-mail/accounts/:id/mails/:seqno/mark-read` - 읽음 처리 +- [x] DELETE `/api/user-mail/accounts/:id/mails/:seqno` - 삭제 (휴지통 이동) +- [x] POST `/api/user-mail/accounts/:id/mails/:seqno/move` - 이동 +- [x] GET `/api/user-mail/accounts/:id/folders` - 폴더 목록 +- [x] GET `/api/user-mail/accounts/:id/folders/:folder/mails/stream` - 폴더별 스트리밍 +- [x] GET `/api/user-mail/accounts/:id/mails/:seqno/attachments` - 첨부파일 목록 +- [x] GET `/api/user-mail/accounts/:id/mails/:seqno/attachment/:partId` - 첨부파일 다운로드 +- [x] POST `/api/user-mail/accounts/:id/send` - 메일 발송 + +### 사용자 격리 검증 +- [x] 모든 쿼리에 WHERE user_id = $n 포함 (DB 레벨 강제) +- [x] 다른 user_id로 계정 접근 시 404 반환 + +### 프론트엔드 페이지 +- [x] `/mail/imap` 페이지 접속 및 동작 +- [x] Gmail IMAP 연동 확인 +- [x] 메일 목록 → 상세 → 읽음 처리 +- [x] 첨부파일 다운로드 진행바 +- [x] 메일 삭제 → Gmail 휴지통 이동 확인 +- [x] 답장/전달 발송 확인 + +--- + +## 알려진 이슈 및 주의사항 + +### 1. 메일 삭제 방식 +- `\Trash` 특수 폴더로 이동 (EXPUNGE 아님) +- Gmail 호환: `[Gmail]/휴지통`으로 자동 라우팅 +- 폴더 없으면 `messageDelete` fallback (영구 삭제 주의) + +### 2. 첨부파일 진행바 +- Content-Length 헤더 기반 진행률 계산 +- imapflow `meta.size`로 헤더 설정 +- totalSize fallback: `getUserMailAttachments`의 size 필드 사용 + +### 3. IMAP 연결 풀 +- 계정당 1개 연결 유지 (maxIdleMs: 5분) +- busy 상태 시 큐잉 처리 +- 연결 끊김 시 자동 재연결 + +### 4. 캐시 +- 메일 목록: 60초 TTL +- 메일 상세: 5분 TTL +- 읽음/삭제/이동 시 해당 캐시 무효화 + +### 5. POP3 미구현 +- `node-pop3` 패키지 설치됨 +- 서비스 파일 미작성 +- 팀장 지시 후 구현 예정 + +--- + +## 변경 이력 + +| 날짜 | 버전 | 내용 | +|------|------|------| +| 2026-03-27 | v1.0 | 초안 작성 | +| 2026-03-30 | v2.0 | IMAP 전 기능 구현 완료 (메일 조회/삭제/이동/첨부/발송/답장/전달/폴더/진행바) | + +--- + +## 관련 문서 + +- [UML[계획]-user-mail.md](./UML[계획]-user-mail.md): 아키텍처 및 설계 diff --git a/docs/ycshin-node/탭_시스템_설계.md b/docs/yc/탭_시스템_설계.md similarity index 100% rename from docs/ycshin-node/탭_시스템_설계.md rename to docs/yc/탭_시스템_설계.md diff --git a/docs/ycshin-node/필수입력항목_자동검증_설계.md b/docs/yc/필수입력항목_자동검증_설계.md similarity index 100% rename from docs/ycshin-node/필수입력항목_자동검증_설계.md rename to docs/yc/필수입력항목_자동검증_설계.md