크라우니메일 기능 7종 구현 (2026-07-27)
개요
mail.crowny.org(9610) 프론트(
pages/앱v3.html)와 백엔드(
크라우니메일서버.한선)에 요청된 7개 기능을 구현·검증했다. 모든 항목은 컴파일 로그·curl 응답·grep 결과로 실증했다(목업 아님).
항목별 결과
1. 예약발송 날짜/시간 선택 — 완료
pages/앱v3.html: state.schedOpen/schedDate/schedTime 신설, onScheduleClick()이 date/time 모달을 열고 confirmSchedule()이 ISO(when)로 변환해 mode:'schedule'로 서버 개별 발송(수신자별 반복 호출).
- 예약 라벨은
fmtSchedLabel(iso)로 실제 선택값 기반 표시(sched 필드에 하드코딩 제거).
- 백엔드
메일목록v3(크라우니메일서버.한선)에 "when" 필드를 리스트 JSON에 추가 → 프론트 loadBoxFromServer가 이를 sched로 매핑.
- 검증: curl로
/api/mail/send mode=schedule when=2026-08-01T09:00:00 → 성공(scheduled:true), 이후 /api/mail/list?box=scheduled에서 "when":"2026-08-01T09:00:00" 그대로 회수 확인.
2. 연락처 등록 UI + API — 완료
- 백엔드:
POST /api/contacts/add {name,email} 신설(_이메일형식유효 재사용, 중복 시 409, CONTACT_ADD 감사기록 + 원장 AUDIT).
- 프론트: 설정 화면에 "연락처 추가" 카드(
renderAddContactCard) + submitAddContact().
- 검증: curl
/api/contacts/add 최초 201, 재호출 시 409 {"error":"이미 등록된 연락처입니다"} 확인.
3. 빠른추가 하드코드 버튼 제거 — 완료
sugWrap(홍길동/이도현/@yjin/partner@ext.co.kr 데모 칩 4개 + 안내문구) 전체 삭제.
- 검증:
grep "홍길동','홍길동" → 0건.
4. 맘 지갑 연동 준비 — 완료(브릿지 단계, 잔여 이슈 있음)
- wallet.crowny.org:9410 지갑서버.한선 실제 API 확인:
GET /api/wallet/:userId, POST /api/wallet/send{from,to,asset,amount}, POST /api/wallet/credit. 자산: CRN/FNC/CRM(맘=CRM).
- 백엔드에
_지갑서버조회(email), _지갑서버송금(from,to,amount) 신설 — 체계() + curl, _쉘안전()으로 이스케이프. 메일 발송 시 mam>0이면 로컬 원장 차감 후 지갑서버로도 송금 시도, 실패(무응답/잔액부족 등)는 WALLET_BRIDGE_FAIL 원장 이벤트로 기록하고 로컬 원장은 이미 SSOT로 유지(폴백).
GET /api/wallet에 walletServer: "bridged"|"local-only" 필드 추가.
- 프론트 마모달 문구를 실제 연동 상태(
state.walletBridged) 반영 + "지갑 앱 열기" 실링크 추가.
- 검증:
/api/wallet → {"mam":118,"walletServer":"bridged"}(조회 성공). mam 포함 발송 시 지갑서버 송금 시도 → 잔액부족으로 실패 → 원장에 WALLET_BRIDGE_FAIL 기록 확인(로컬 폴백 정상 동작).
- 잔여 이슈: 지갑서버와 메일 로컬 원장이 서로 다른 잔고 테이블(사전 동기화 없음) — 완전 정합(선입금/동기화 배치)은 미구현. 실사용 전 두 원장 합산 정책 결정 필요.
5. 멀티 수신자 발송 버그 수정 + 맘 수신자 지정 — 완료(실버그 재현·수정 확인)
- 재현: 서버 재기동 후 curl
/api/mail/send {"to":"gildong@crowny.org, dohyun@crowny.org", ...} → {"error":"받는이 형식이 올바르지 않습니다(로컬파트@도메인, 공백 없음)"} (400). 원인: 백엔드 _이메일형식유효가 공백 포함 시 무조건 거부, 프론트가 addrs.join(', ')로 여러 주소를 한 필드에 합쳐 보냄.
- 수정:
doSend()를 수신자별 개별 /api/mail/send 호출 루프로 변경. 맘>0 + 수신자 2명 이상이면 state.coMamTo 미지정 시 발송 차단, 마모달에 수신자 선택 칩 UI 추가.
- 검증: 개별 호출(gildong, dohyun 각각) → 둘 다
201/queued:true 성공. 콤마+공백 결합 방식은 여전히 400(백엔드 로직 자체는 안 건드림 — 프론트가 아예 그 경로를 타지 않도록 수정).
6. 명함집/캔버스 연동 준비 — 완료(브릿지 단계, SSO 미완)
- name.crowny.org:9621
GET /api/cards 실제 확인 — CORS 오픈(Access-Control-Allow-Origin:*), 인증 불필요, 실 데이터 반환. canvas.crowny.org:9620은 헬스만 확인(OK canvas 9620), 공개 API 라우트 없음(SSO 전제).
- 사이드바 "명함집"/"캔버스 문서함", 레일 버튼, 명함 모달, 슬래시커맨드의 placeholder(
state.boundary "SSO로 이동합니다" 안내) 전부 실제 window.open('https://name.crowny.org'|'https://canvas.crowny.org')로 교체.
- 명함 모달에
fetchNamecardBridge(handle) 추가 — name.crowny.org /api/cards를 실 fetch로 조회해 handle 매칭 여부를 실시간 배지로 표시(연동 성공/미존재/실패 3분기).
- 잔여 이슈: SSO 미구현이라 완전 연동(로그인 상태 공유, 명함 편집 등 쓰기 동작)은 안 됨. canvas는 공개 API가 없어 "문서함 클릭 → 실제 도메인 이동"까지만 구현, 첨부 문서 개별 API 연동은 못함(엔드포인트 부재).
7. "미읽음" → "안읽음" 라벨 변경 — 완료
grep -n "미읽음" 전수 검토 후 perl -pi -e 's/미읽음/안읽음/g'로 일괄 치환(라벨 텍스트 + state.chip==='안읽음' 필터 키 + CSS 클래스 .안읽음점까지 함께 변경, 필터 깨짐 없음).
- 검증:
grep -c 미읽음 → 0, grep -c 안읽음 → 7.
변경 파일
/Users/ef/crowny-mail/pages/앱v3.html
/Users/ef/crowny-mail/크라우니메일서버.한선
/Users/ef/crowny-mail/크라우니메일서버.toau (재컴파일 배포본, 백업: 크라우니메일서버.toau.bak-07281)
검증 요약
- 컴파일:
hanseonc_high 3회 재실행 전부 에러 없이 .toau 생성(마지막 61520 큐브).
- 서버 재기동: PID 교체 후
/health 200 확인, 이후 curl로 로그인/발송/예약/연락처추가/지갑조회 전부 실동작 확인.
- 프론트:
node -e "new Function(...)" 로 스크립트 블록 파싱 에러 없음 확인.
잔여 이슈 (요약)
- 지갑서버-메일 원장 이중화: 사전 자금 동기화/정합 배치 없음 — 실사용 전 설계 필요.
- 명함집/캔버스 SSO 미구현 — 현재는 "실 도메인 링크 + 읽기전용 브릿지 조회"까지만, 완전연동(세션 공유)은 별도 작업.
- canvas.crowny.org는 공개 API가 없어 문서 첨부 단위 연동은 미구현(링크 이동만 가능).