Files
arasystem/docs/design/webmail
ttipo adb992e6d8 docs: 12단계 계정 보안 — 계약 5절 · 디자인 보드 넷 · README
2단계 인증(인증 앱 TOTP) · 앱 비밀번호 · 로그인 기록을 엔진(Stalwart 0.16.27) 실험 결과에 맞춰 정했다.
엔진이 코드 확인 · 메일 프로그램 막기 · 앱 비밀번호를 맡고, 서버는 첫 코드 확인 · 다시 쓰기 막기 · 실패 한도 · 기록을 맡는다.
사용자 결정(2026-10-31): 비밀번호를 바꾸면 앱 비밀번호도 지우고, 끊은 세션은 바로 막는다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 10:57:14 +09:00
..

웹메일 디자인 원본 (1~6단계)

확정된 디자인 캔버스의 원본이다. 화면 구현의 기준으로 쓴다.

  • 캔버스: https://claude.ai/artifact/WJvVsKvXepxn3p75CGJAr2 (확정본: 2026-10-28, 버전 31)
  • 화면: Login(로그인) · Main(분할 보기: 메일함·목록·본문) · MainList(목록 보기: 게시판형 표) · MainListRead(목록 보기에서 연 메일) · Compose(메일 쓰기 — 서식 편집기) · ComposeEditor(메일 쓰기 · 편집기 상태: 포커스·툴바·메뉴·팝오버·인용·이미지 첨부) · States(오류·빈 상태·피드백 모음) · 2단계: Settings(설정 · 메일 서명) · ComposeAssist(메일 쓰기 · 임시 저장 · 받는 사람 자동 완성 · 서명) — 아래 "2단계" · 3단계: Search(메일 검색 · 분할 보기) · BulkSelect(여러 통 선택 · 목록 보기) · ListStates(새 메일 · 검색 · 여러 통 처리 상태) — 아래 "3단계" · 4단계: SettingsAccount(설정 · 비밀번호) · SettingsVacation(설정 · 부재중 자동 응답) · MailboxManage(메일함 관리 · 휴지통 비우기) · MailActions4(스팸 · 원문 · 오래 걸리는 처리) — 아래 "4단계" · 5단계: SettingsNotify(설정 · 알림) · MailRealtime(실시간 새 메일 · 바탕화면 알림 상태) — 아래 "5단계" · 6단계: SettingsFilter(설정 · 필터 — 고치기 창) · MailFilterStates(메일 필터 상태 · 메일에서 바로 만들기) — 아래 "6단계"
  • 파일 형식은 캔버스 전용(<x-dc> 마크업 + DCLogic 클래스)이다. 그대로 쓰지 말고 구조·간격·색·문구의 기준으로만 본다. Compose 의 툴바·메뉴는 눌러 볼 수 있는 시안이다 — 버튼 상태만 바뀌고 본문 글자에 서식이 입혀지지는 않는다.

캔버스 안의 자산 주소

캔버스는 자산을 /_blob/<id> 로 참조한다. 저장소에서는 아래 파일에 대응한다.

캔버스 주소 저장소 파일 용도
/_blob/16067b9aa5137abf808e3dbef30c42e0 Pretendard Variable (npm pretendard 패키지로 자체 호스팅) 전체 글꼴
/_blob/2b9ef4349333debc969432c859eaf1bf frontend/src/assets/brand/logo-ara-white.png 왼쪽 앱 레일 로고(가로 48px)
/_blob/3a2f4115a8f90207faa7d3a51cd9aec2 frontend/src/assets/brand/logo-arasystem-white.png 로그인 브랜드 패널 로고(가로 240px)

로고 원본은 docs/brand/logo_arasystem_white.png 다. 흰 로고라 어두운 브랜드 바탕(#0B3640) 위에만 둔다.

디자인 원칙 (사용자 확정)

  • 안 읽음 점·개수 알약·코드 태그 같은 뱃지를 쓰지 않는다 — 굵은 글자와 평범한 숫자로 구분한다.
  • 왼쪽 전역 앱 레일(메일·캘린더·파일·ERP·주소록·설정)에 모듈을 계속 붙인다. 지금은 메일만 동작한다.
  • 정보 밀도가 높은 데스크톱 화면 — 기본 13px. 목록에 본문 미리보기를 넣지 않는다.
    • 분할 보기 행: 2줄(보낸 사람·시간 / 제목+첨부 클립), 약 50px
    • 목록 보기 행: 1줄 표, 38px. 칸 = 별 32 · 보낸 사람 168 · 제목(나머지) · 날짜 84 · 크기 64 (px, 칸 사이 12). 폭 1100px 미만에서 크기 칸 숨김, 600px 미만에서 보낸 사람·날짜 축소, 더 좁으면 가로 스크롤. 캔버스는 파서 제약 때문에 grid 로 그렸지만 구현은 실제 <table> 로 한다.
  • 클릭 영역은 44px 이 기본이다. 아래만 밀도를 위한 의도된 예외다(사용자 확정).
    • 목록 표의 별 버튼(폭 32px) · "분할 | 목록" 전환(바깥 36px)
    • 메일 쓰기 편집기(2026-10-27): 툴바 아이콘 버튼 40×44(새로고침·이전/다음 버튼과 같은 크기) · 메뉴 항목 높이 40(메일함 항목과 같다) · 색 견본 칸 40×40 · 표 고르기 격자 칸 22(간격 3, 키보드는 화살표)
  • 보기 방식: 목록 머리의 "분할 | 목록" 전환. 선택은 사용자별로 기억한다.
  • 콘텐츠 열(읽기·쓰기 공통): 최대 폭 800px, 좌우 여백 32px, 위 20px·아래 36px, 본문 글줄 최대 680px, 영역 안 왼쪽 정렬. 분할 보기 읽기·목록 보기 읽기·쓰기 세 곳이 같은 값을 쓰고, 툴바 첫 아이콘도 같은 32px 선에서 시작한다.
  • 브랜드 컬러 "바다 청록": 바탕 #0B3640, 강조 #0E7C86. 파생 색은 각 화면 원본의 인라인 스타일을 따른다.

메일 쓰기 (2026-10-27 확정 — Compose · ComposeEditor)

위에서부터 머리(제목·닫기) → 받는 사람(→ 참조·숨은참조) → 제목 → 본문 툴바 → 본문 → 첨부 → 보내기 줄이다. 모두 콘텐츠 열 안에 있다. 첨부 안내문은 "파일당 최대 20MB · 합계 25MB · 최대 20개 · 문서, 이미지, 압축 파일" 이다(용량은 서버 값, 개수는 계약 상수).

글자 입력칸 포커스 — 한 가지 규칙

항목 값
대상 받는 사람 · 참조 · 숨은참조 · 제목 · 본문 편집기
위치 그 칸의 아래 hairline 자리. 본문은 편집 영역 아래(첨부 영역 위)
두께 · 색 2px --color-brand(#0E7C86). 1px 선의 색을 바꾸고 안쪽 그림자 1px 를 더한다 — 높이가 변하지 않는다
모서리 없음(곧은 선)
범위 행 전체 폭 — 원래 hairline 과 같은 길이(콘텐츠 열, 최대 800px). 이름표 아래까지 이어진다
언제 글자 입력 요소(input·편집 영역)에 포커스가 있을 때. 마우스로 눌러도 보인다(:focus)
대비 흰 바탕 4.9:1 · 원래 선 #EEF0F3 4.3:1 · 오류 띠 #FEF3F2 4.5:1 (모두 3:1 이상)
  • 버튼 고리와의 관계: 같은 색·같은 2px 이고 모양만 다르다. 누르는 것(버튼·링크)은 전역 :focus-visible 고리(바깥 2px, 버튼 모서리를 따른다), 쓰는 곳은 선이다. 행 안의 버튼(받는 사람 토큰 ✕ · "참조 · 숨은참조")에 포커스가 가면 고리만 보이고 행의 선은 회색 그대로다 — 한 번에 하나만 보인다.

  • 테두리가 있는 입력칸(링크 팝오버·로그인)은 상자가 있으므로 버튼처럼 고리를 쓴다.

  • 구현(tokens.css 변수):

    .compose-field:has(:is(input, .ProseMirror):focus) {
      border-bottom-color: var(--color-brand);
      box-shadow: inset 0 calc(var(--border-width) * -1) 0 var(--color-brand);
    }
    .compose-field :is(input, .ProseMirror):focus-visible { outline: none; }
    

    .compose-field 는 받는 사람·참조·숨은참조 행, 제목 행, 본문 편집 영역에 붙인다. 편집 영역이 아래 hairline 을 가지므로 첨부 영역의 위 선은 없앤다.

본문 서식 편집기 (TipTap)

  • 툴바: 한 줄 · 높이 52(위아래 4) · 왼쪽 여백 20 + 버튼 안 12 → 첫 아이콘이 32px 선. 버튼 사이 2, 묶음 사이 세로선 1×20 #E6E8EC(좌우 4).

    • 아이콘 버튼 40×44 · 모서리 8 · 아이콘 16(선 1.75) · #344054. 글꼴 108×44 · 글자 크기 56×44(글자 + ▾).
    • 상태: 올림 #F2F4F7 · 켜짐(aria-pressed) #E3F2F3 + 안쪽 1px #0E7C86 + 아이콘 #0A5F67(옅은 바탕만으로는 3:1 이 안 돼 선을 둔다) · 비활성 50% · 포커스는 전역 고리 · 메뉴 열림(aria-expanded)은 올림과 같은 바탕.
    • role="toolbar" — Tab 한 번에 들어가 화살표로 옮긴다. 본문에서 Alt+F10 → 툴바, Esc → 본문.
  • 툴바 기능(800px): 실행 취소 · 다시 실행 | 글꼴 ▾ · 글자 크기 ▾ | 굵게 · 기울임 · 밑줄 | 글자 색 · 배경 색 | 글머리 목록 · 번호 목록 | 링크 | 더보기

  • 더보기(늘): 취소선 | 왼쪽 · 가운데 · 오른쪽 정렬 | 내어쓰기 · 들여쓰기 | 인용 · 표 넣기 › · 가로줄 | 서식 지우기

    • 항목 높이 40 · 아이콘 16 · 이름 · 단축키(12px #667085) · 켜진 것은 오른쪽 ✓. 값을 고르는 것(글꼴·크기·색·표)은 › 로 한 단계 더 연다.
    • 단축키: 굵게 Ctrl+B · 기울임 Ctrl+I · 밑줄 Ctrl+U · 취소선 Ctrl+Shift+S · 글머리 Ctrl+Shift+8 · 번호 Ctrl+Shift+7 · 정렬 Ctrl+Shift+L/E/R · 내어쓰기 Ctrl+[ · 들여쓰기 Ctrl+] · 인용 Ctrl+Shift+B · 링크 Ctrl+K · 서식 지우기 Ctrl+\ · 실행 취소 Ctrl+Z · 다시 실행 Ctrl+Y
    • 맥은 Ctrl 대신 ⌘ 이고 표기도 기호로 보인다(⌘B · ⇧⌘S). 다시 실행은 맥 관례대로 ⇧⌘Z 로 적는다(⌘Y 도 된다). 내어쓰기 · 들여쓰기 · 서식 지우기는 할 일이 없어도 키를 삼킨다 — 맥의 ⌘[ 가 브라우저 "뒤로"가 되어 쓰던 메일을 잃지 않게 (2026-10-27 구현하며 정함).
  • 좁은 폭: 툴바는 줄을 바꾸지 않는다. 묶음째 아래 순서로 더보기에 옮기고, 숨은 묶음은 더보기 맨 위에 툴바 순서대로 들어간다. 더보기 단추에 숫자를 달지 않는다.

    1. 실행 취소 · 다시 실행 — 쓰기 칸 734px 미만 (Ctrl+Z · Ctrl+Y 는 그대로)
    2. 글꼴 · 글자 크기 — 639px 미만
    3. 글자 색 · 배경 색 — 460px 미만
    4. 링크 — 365px 미만 (주소를 붙여 넣으면 저절로 링크가 된다)
    5. 글머리 · 번호 목록 — 312px 미만

    굵게 · 기울임 · 밑줄과 더보기는 늘 남는다. 폭은 지금 크기로 셈한 참고값이다 — 구현은 툴바의 실제 폭을 재어(ResizeObserver) 이 순서로 넘긴다. Compose 보드의 좁은 폭 메뉴는 색 · 목록을 취소선 뒤에 그렸지만, 이 문서의 순서(숨은 묶음 전부를 맨 위에)가 기준이다 — 구현도 이 순서다. 휴대폰에서는 더보기 메뉴가 화면 높이에 맞춰 안에서 스크롤된다.

  • 글꼴: 맑은 고딕(기본) · 굴림 · 돋움 · 바탕 | Arial · Verdana · Georgia · Times New Roman · Courier New. 목록의 이름은 그 글꼴로 보인다.

    • 본문 기본 글꼴은 맑은 고딕 스택 'Malgun Gothic', '맑은 고딕', 'Apple SD Gothic Neo', sans-serif 다 — 편집기 안 모양을 받는 사람이 보는 모양과 같게 한다. 화면 UI 는 Pretendard 그대로다. 맥에서는 Apple SD 산돌고딕 Neo 로 보인다.
    • "맑은 고딕(기본)"을 고르면 글꼴 지정을 지운다.
  • 글자 크기(px): 12 · 13 · 14(기본) · 16 · 18 · 20 · 24 · 32. 제목 스타일(제목 1·2·3)은 두지 않는다. 고른 글자의 크기가 섞이면 "–".

  • 색 팔레트(8색씩 · 견본 칸 40×40, 보이는 색 26 · 고른 색에 ✓ · 고른 색 이름을 글자로도 보인다):

    • 글자 색: 기본 #1F2733 · 회색 #667085 · 빨강 #B42318 · 주황 #B54708 · 초록 #067647 · 청록 #0E7C86 · 파랑 #175CD3 · 보라 #6941C6 — 모두 흰 바탕 4.5:1 이상. "기본"은 글자 색을 지운다.
    • 배경 색: 없음 · 노랑 #FFF1A8 · 주황 #FDE4CC · 분홍 #FBDCE3 · 초록 #D9F2DF · 하늘 #D6E8FB · 보라 #E9E0FA · 회색 #E9EBEF — 본문 글자(#1F2733)가 10:1 이상 남는다. "없음"은 배경 색을 지운다.
  • 붙여 넣은 글 · 원문 인용의 서식: 보낼 수 있는 형식(계약 2절 "서식 본문")만 받고 나머지는 버린다 — 색 이름(red) · Calibri 같은 서식은 기본 모양으로 보인다. 편집기에 보이는 모양이 곧 받는 사람이 보는 모양이다 (2026-10-27 구현하며 정함). 보이지 않을 만큼 작은 글자(8px 미만)와 거의 투명한 글자 색(알파 0.3 미만)도 그 서식을 버린다 — 그렇게 숨긴 글은 드러난다. 바탕과 같은 색(흰 바탕 위 흰 글자 등)은 바탕을 알 수 없어 막지 않는다 (2026-10-27 사용자 확정).

  • 링크 팝오버(폭 300): 주소 · 표시할 글자(비우면 주소). 새 링크는 [취소] [넣기], 이미 있는 링크는 [링크 빼기] … [취소] [적용]. Enter = 적용, Esc = 닫고 본문으로. 주소는 http(s) · mailto 만 받는다.

  • 표 넣기(더보기 ›): 8열 × 6행 격자에서 고른다("3행 × 4열"). 표 안의 행·열 추가·삭제는 다음 단계.

  • 메뉴·팝오버 공통: 흰 바탕 · 테두리 #E6E8EC · 모서리 10 · 그림자 --shadow-toast · 툴바 아래 선 바로 밑에서 연다. Esc 로 닫는다.

  • 빈 본문: 안내 문구 "내용을 입력하세요" · #667085(4.9:1). 커서는 문구 앞에 선다.

  • 문단: 문단 사이 여백은 0 — Enter 한 번이 한 줄이다(지금 textarea 와 같다). 줄 간격은 1.75 이고, 보내는 본문도 같은 값이다 (감싸는 div 의 line-height — 받는 사람이 편집기와 같은 모양으로 본다, 2026-10-27 구현하며 정함). 여러 칸 띄어쓰기 · 줄 앞 공백도 받는 쪽에서 그대로 보이게 보낸다. 탭은 다음 8칸 자리까지의 공백이 된다(편집 영역의 탭 간격과 같다, 2026-10-27).

답장 · 전달의 원문 인용

  • 원문은 인용 막대(왼쪽 2px #D0D5DD · 막대와 글 사이 14) 안에 둔다. 원문 글은 #4A5363, 내 글과 24 띄운다.
  • 머리는 답장 · 전달 모두 "원본 메일"(12.5px 굵게)이고, 그 아래에 항목 이름(#667085) · 값 표를 둔다.
    • 답장: 보낸 사람 · 날짜 · 제목 — 커서는 맨 위 첫 줄
    • 전달: 보낸 사람 · 날짜 · 받는 사람 · 참조 · 제목 — 커서는 받는 사람 칸
  • 막대는 회색이다 — 2px 브랜드색 선은 포커스 표시라 겹치지 않게 한다. 툴바의 "인용"도 같은 막대를 머리 없이 쓴다.
  • 초안이 한도(제목 255자 · 본문 텍스트 100,000자 · 서식 본문 500,000자 — 계약 2절)를 넘으면 인용을 줄이고 인용 끝에 "(원문이 길어 이하 생략)"을 둔다(2026-10-26 구현하며 정함 — 캔버스에는 그리지 않았다).
    • 처음 맞출 때 내 글 몫으로 한도의 5%를 남긴다. 보내기 직전에 다시 재서 넘으면 인용을 더 줄이고, 편집기에도 줄인 모양을 보인다(실행 취소로 되돌릴 수 있다).
    • 내 글만으로 넘으면 보내지 않는다 — 본문 아래(첨부 위)에 받는 사람 오류와 같은 빨간 띠로 알리고 본문에 초점을 준다. 제목이 255자를 넘으면 제목 아래에 같은 띠를 둔다 (2026-10-27 구현하며 정함).
    • 본문 띠의 문구는 전용 코드 MAIL_007("본문이 너무 길어 보낼 수 없습니다. 내용을 줄인 뒤 다시 보내 주세요.")다 — 서버가 같은 코드를 돌려줘도 같은 띠를 보인다. 제목 띠는 VAL_001 그대로다 (제목 칸은 255자에서 더 입력되지 않는다, 2026-10-27 사용자 확정).
  • 원문이 서버의 읽기 한도에서 잘렸으면(isBodyTruncated) 클라이언트가 줄이지 않았어도 인용 끝에 같은 생략 표시를 둔다.
  • 인용할 원문은 읽기 화면과 같은 규칙으로 고른다 — HTML 이 없거나 공백뿐이면 글자 본문(textBody)이다. 인용은 그림을 빼므로, 그림을 빼고 남는 글자가 없으면(그림만 있는 뉴스레터 — 그림을 감싼 레이아웃 표 포함) 그때도 글자 본문으로 인용한다 (2026-10-27 리뷰 반영).
  • 내가 보낸 메일에 답장하면 받는 사람은 그 메일의 받는 사람이다 — 답장은 원래 받는 사람, 전체 답장은 원래 받는 사람과 참조(둘 다 나는 뺀다). 나에게만 보낸 메일이면 나에게 답장한다 (2026-10-27 사용자 확정 — 전에는 받는 사람 칸이 비었다). 이때 회신 주소(Reply-To)는 보지 않는다. 받는 사람 없이 참조만 있던 메일이면 답장은 나에게, 전체 답장은 나에게 + 원래 참조로 간다.

나가기 전 확인 (2026-10-27 사용자 확정 — 캔버스에는 아직 없다)

  • 쓰기 화면을 연 뒤 받는 사람 · 제목 · 본문 · 첨부를 바꿨으면, 화면을 떠나기 전에 묻는다 — 답장 · 전달로 처음 채운 내용은 "바꿈"이 아니다.
    • 앱 안 이동(앱 레일 · 메일함 · 뒤로/앞으로 · 쓰기 화면의 취소 · 닫기 ✕)은 확인 대화상자를 띄운다 — 세션 만료 대화상자와 같은 모양이다. 제목 "보내지 않은 메일이 있습니다", 글 "이 화면을 나가면 쓰던 내용이 사라집니다.", 단추 [계속 쓰기](처음 초점 · Esc 와 같다) [나가기]. 단추는 오른쪽 정렬이고, 계속 쓰기는 채운 기본 단추 · 나가기는 "취소"와 같은 보조 단추다. 계속 쓰기를 고르면 주소와 방문 기록이 그대로다.
    • "바꿈"에는 아직 주소 칩이 되지 않은 받는 사람 입력 글자, 첨부 목록의 모든 변화(사전 검사에 걸린 파일 포함)도 든다. 지운 · 실행 취소로 처음 모양에 돌아오면 바꾸지 않은 것으로 본다. 방문 기록(뒤로 · 앞으로)으로 쓰기 화면끼리 옮겨 갈 때도 묻는다 (쓰기 화면에서는 메일함 패널의 "메일 쓰기"가 "메일 쓰는 중"으로 바뀌어 눌리지 않는다).
    • 대화상자가 떠 있는 동안 세션이 끝나면 대화상자를 닫고 그 이동을 취소한다(세션 만료 대화상자가 그 뒤에 뜬다).
    • 알려진 한계(vue-router): 뒤로 가기로 연 확인 창이 떠 있는 동안 주소창에는 가려던 주소가 보인다(계속 쓰기를 고르면 돌아온다). 그 사이 뒤로 가기를 두 번 누르면 방문 기록이 한 칸 어긋날 수 있다.
    • 탭 닫기 · 새로고침 · 다른 사이트로 가기는 브라우저 기본 확인창을 띄운다(문구는 브라우저가 정한다).
  • 보낸 뒤와 세션이 끝났을 때(로그아웃 · 만료 · 다른 탭의 로그아웃)는 묻지 않는다 — 세션 만료 대화상자와 겹치지 않는다.
  • 보내는 중에도 묻지 않는다 — 이미 보내기를 눌렀기 때문이다(Gmail 과 같다). 보내는 중에 화면을 떠나면 결과는 알림으로만 알린다: 성공은 평소의 성공 알림, 실패는 그 오류의 사전 문구 알림이다(받는 사람 · 첨부 · 본문 길이 오류처럼 평소 칸 아래에 보이던 것도). 떠난 화면이나 새로 연 쓰기 화면은 그 결과로 바뀌지 않는다 (2026-10-27 리뷰 반영). 단 보내는 중에 탭을 닫거나 새로고침하면 브라우저 기본 확인창은 뜬다 — 요청이 끊길 수 있고 결과를 알릴 화면이 남지 않기 때문이다.
  • 맥의 ⌘[ 를 본문 밖에서 눌러 브라우저 "뒤로"가 되어도 이 확인에 걸린다.
  • 2단계(임시 저장)부터는 저장된 채로는 묻지 않는다 — 저장하지 못한 내용이 있을 때만 이 확인을 띄우고 제목 · 글이 바뀐다 (아래 "2단계 › 임시 저장"). 단추 · 키 · 묻지 않는 경우는 그대로다.

본문 이미지 → 첨부 파일

  • 본문에는 이미지를 넣지 않는다(사용자 확정). 툴바에 이미지 버튼이 없다. 서명 그림만 예외다 (아래 "2단계 › 서명").
  • 본문에 붙여 넣거나(Ctrl+V) 끌어다 놓은 이미지는 첨부 파일로 올린다. 끌어다 놓은 다른 파일도 첨부로 올린다(쓰기 화면 어디에 놓아도 같다). 한도는 첨부와 같다 — 넘치면 첨부 목록에 실패로 보인다(States 의 "첨부 업로드").
    • 클립보드에 글이 함께 있으면 글을 붙여 넣는다 — 워드 · 엑셀에서 복사하면 고른 부분의 그림이 글과 함께 따라오기 때문이다. 글 없이 파일만 있을 때 첨부로 돌린다 (2026-10-27 구현하며 정함 — 캔버스 시안은 그림이 있으면 늘 첨부로 돌린다).
    • 이름 없이 붙여 넣은 그림은 "붙여넣은 이미지 N.확장자"로 올린다. 원래 이름이 있는 파일은 그 이름 그대로다.
  • 끌어다 놓는 동안 본문 위에 점선 덮개(2px #0E7C86 점선 · 바탕 #F3F8F8)와 "놓으면 첨부 파일로 올립니다"를 보인다. 쓰기 화면이 떠 있으면 파일은 창 어디(앱 레일 위 포함)에 놓아도 첨부로 받고, 덮개도 파일이 창 위에 있는 동안 보인다 — 받지 않는 곳에 놓으면 브라우저가 그 파일을 열어 쓰던 메일을 잃기 때문이다 (2026-10-27 구현하며 정함).
  • 이미지를 첨부로 돌렸을 때만 성공 알림 "이미지는 본문 대신 첨부 파일로 올렸습니다."를 띄운다(성공 알림과 같은 모양 · 5초).

2단계 — 임시 저장 · 서명 · 받는 사람 자동 완성 (2026-10-27 확정 — Settings · ComposeAssist)

임시 저장

  • 자동 저장: 바꾼 내용이 있으면 입력이 3초 멈출 때(10초에 한 번까지)와 화면을 떠날 때 임시보관함에 저장한다. 아무것도 쓰지 않은 쓰기 화면은 저장하지 않는다. 저장은 한 번에 하나씩이고, 보내기 전에는 진행 중인 저장이 끝나기를 기다린다.
  • 아래 단추 줄: [보내기] [임시 저장] 뒤에 상태 글(12px) — "저장하는 중…"(#667085) · "오후 3:12에 임시 저장됨"(✓ #0E7C86) · 실패 "임시 저장하지 못했습니다"(#B42318) + [다시 시도]. 「취소」는 없앤다 — 닫기(✕)가 저장한 채 닫는다.
    • 자동 저장이 실패하면 알림 없이 그 자리에만 보이고 조금 뒤 다시 시도한다. 「임시 저장」을 눌러 실패하면 사전 문구 알림도 띄운다.
    • 같은 저장본을 다른 창 · 기기에서 먼저 고쳤거나 지웠으면(MAIL_008, 휴지통으로 옮겼으면 PUT 의 404 RES_001) 막지 않고 지금 내용을 새 저장본으로 남긴다 — 상태 글 "다른 곳에서 바뀐 저장본이 있어 새로 저장했습니다 · 오후 3:14"(정보 아이콘, #7A5200). 임시보관함에 두 통이 남는다.
  • 삭제: 쓰기 화면 머리의 휴지통 단추(44 · "이 메일 삭제") — 임시 저장본을 휴지통으로 옮기고 닫는다. 알림 "임시 저장한 메일을 휴지통으로 옮겼습니다."
    • [되돌리기](임시보관함으로 되돌린다). 저장본이 아직 없으면 그냥 닫는다.
  • 임시보관함 목록: 보낸 사람 자리에 받는 사람(없으면 "(받는 사람 없음)"), 제목이 없으면 "(제목 없음)" — 둘 다 #667085. 시각은 마지막으로 저장한 때. 누르면 읽기 화면이 아니라 쓰기 화면에서 이어 쓴다. 보내면 임시보관함에서 사라진다.
  • 나가기 확인: 저장된 채로는 묻지 않는다. 떠나기 전에 남은 변경을 먼저 저장하고, 저장하지 못한 내용이 있을 때만 제목 "저장하지 못한 내용이 있습니다", 글 "이 화면을 나가면 마지막으로 바꾼 내용이 사라집니다."로 묻는다.

서명

  • 설정 › 메일 › 서명 — 앱 레일의 "설정"에서 연다. 왼쪽 패널(208 · #F8F9FB)의 "메일" 묶음에 "서명", 오른쪽은 콘텐츠 열(최대 800).
  • 서명 칸: 굵게 · 기울임 · 밑줄 · 글자 색 · 링크 · 그림. 글꼴은 쓰기 화면 본문과 같은 맑은 고딕 14px · 줄 간격 1.75.
  • 그림: 그림 단추 · 붙여 넣기 · 끌어다 놓기로 넣는다 — PNG · JPG · GIF, 한 장 300KB 이하, 3장까지. 그림을 고르면 칸 아래에 크기 줄(작게 96px · 보통 144px · 원본 — 최대 600px · 그림 빼기). 메일 안에 함께 실어 보내므로 받는 쪽에서 외부 이미지처럼 막히지 않는다. 지금 있는 흰 로고는 흰 메일 바탕에서 보이지 않으니 서명에는 색이 있는 로고를 쓴다.
  • 서명을 넣을 메일: 새 메일 · 답장 · 전달(인용한 원문 위) — 둘 다 기본으로 켠다. 미리 보기에서 [새 메일 | 답장]을 바꿔 본다.
  • [저장] → "저장했습니다. 새로 여는 쓰기 화면부터 들어갑니다."(#067647).
  • 쓰기 화면을 열 때 서명을 보통 글로 넣어 둔다 — 새 메일은 본문 끝(빈 줄 하나 뒤), 답장 · 전달은 인용 위. 그 메일에서 고치거나 지울 수 있고 임시 저장에도 남는다. 임시 저장본을 이어 쓸 때는 서명을 다시 넣지 않는다. 본문에 붙여 넣은 다른 그림은 지금처럼 첨부로 올린다.
  • 서명 그림처럼 본문 안에 그림으로 보인 부분은 첨부로 보이지 않는다 — 읽기 화면의 첨부 목록과 목록의 첨부 클립에서 뺀다 (받은 메일의 본문 그림도 같다). 전달할 때는 원문 그림을 잃지 않게 원본 첨부로 함께 붙는다 (인용 본문에서는 그림이 빠지기 때문이다).

받는 사람 자동 완성

  • 받는 사람 · 참조 · 숨은참조에서 한 글자 이상 치면 칸 바로 아래에 추천 목록(최대 8개) — "사내"(웹메일에 로그인한 적 있는 직원) · "최근 주고받은 주소" 두 묶음(묶음 이름 12px #667085 굵게). 줄은 높이 40 · 이름(맞는 글자 굵게) + 주소(12px #4A5363), 고른 줄은 #E3F2F3.
  • 이미 넣은 주소는 빼고, 초성으로도 찾는다(ㄷㅇ → 도윤). 목록 아래 안내 "↑↓ 고르기 · Enter · Tab 넣기 · Esc 닫기".
  • 친 글자가 완전한 메일 주소면 추천을 미리 고르지 않는다 — Enter 는 친 주소를 그대로 넣고("Enter 는 친 주소를 그대로 넣습니다"), 화살표로 고른 뒤에만 추천이 들어간다. 엉뚱한 사람이 받는 사람에 들어가지 않게 하기 위해서다.
  • 쓰기 화면을 열 때 추천 목록을 한 번 받아 두고 치는 대로 바로 거른다 — 모자랄 때만 서버에 다시 묻는다.

3단계 — 새 메일 · 검색 · 여러 통 처리 (2026-10-27 확정 — Search · BulkSelect · ListStates)

API 와 서버 규칙은 계약 2절 "새 메일 확인" · "검색" · "여러 통 한꺼번에".

새 메일

  • 화면이 보이는 동안 1분마다(탭으로 돌아오면 바로 한 번) 바뀐 것이 있는지 묻고, 바뀌었으면 메일함 숫자와 보이는 목록을 맞춘다 — 고른 메일 · 열린 메일 · 스크롤 위치는 그대로다. 검색 결과는 저절로 바꾸지 않는다. 확인이 실패해도 알리지 않는다.
  • 맨 위를 보고 있으면 새 메일이 그냥 위에 생긴다(굵은 글자 = 안 읽음). 목록을 내려 본 채면 머리 아래에 알림 줄 "새 메일 N통 · 맨 위로"(높이 40 · 바탕 #F3F8F8 · #0A5F67 굵게 · 위 화살표)를 띄운다 — 누르면 맨 위로 가고 줄은 사라진다.
  • 탭 제목: 받은편지함에 안 읽은 메일이 있으면 앞에 "(N) " — "(3) 메일 · ARA SYSTEM". 소리 · 데스크톱 알림은 하지 않는다.

검색

  • 상단 검색 칸이 메일 화면에서는 "메일 검색"이다. Enter 로 찾고, 치는 동안에는 찾지 않는다. 검색 중인 칸은 테두리가 강조색(1px 테두리 + 1px 그림자)이고 끝에 ✕(40 · "검색어 지우기" — Esc 와 같다).
  • 결과는 보던 보기 방식(분할 · 목록) 그대로 목록 자리에 보인다.
    • 머리(52): [←](40×44 · "검색 닫기") · "검색 결과"(15px 굵게) · "‘회의’ · 6통"(12px #667085) · 보기 전환.
    • 그 아래 범위 줄(48 · 바탕 #FBFBFC): "찾을 곳" + [그 메일함 | 전체 메일함](36 — 설정 화면의 전환과 같은 밀도 예외)
      • 전체일 때 "휴지통 · 스팸메일함은 빼고 찾습니다"(12px #667085).
  • 전체 메일함에서 찾으면 행에 메일함을 보인다 — 분할 보기는 첫 줄 시각 앞에 "보낸편지함 ·"(12px #667085), 목록 보기 표는 보낸 사람 뒤에 메일함 칸(96). 보낸편지함 · 임시보관함의 메일은 보낸 사람 자리에 "받는 사람: 이름".
  • 검색 결과에서 연 메일의 읽기 화면은 받는 사람 아래에 "메일함 받은편지함" 한 줄(12.5px #667085)을 더한다.
  • 결과 없음: 검색 아이콘 · "‘…’에 맞는 메일이 없습니다" · 한계 한 줄 "낱말 단위로 찾습니다. 붙여 쓴 말의 일부(‘서버이전’의 ‘서버’)나 제목 맨 앞의 말머리([공지] · Re:)로는 찾지 못합니다." · 한 메일함에서 찾았으면 [전체 메일함에서 찾기].
  • 찾는 동안은 지금의 목록 뼈대를 보인다.

여러 통 처리

  • 목록 보기 표: 맨 앞에 고르기 칸(32 — 별 칸과 같은 밀도 예외). 상자 16px — 비었을 때 테두리 #8A94A3 1.5px, 고르면 #0E7C86 채움에 흰 체크. 머리의 상자는 불러온 메일 모두 고르기 · 풀기(일부만 골랐으면 – 표시). 고른 행 바탕 #E8F4F5(마우스를 올리면 #DDEFF0). 칸 = 고르기 32 · 별 32 · 보낸 사람 168 · 제목(나머지) · 날짜 84 · 크기 64 (칸 사이 12). 좁아질 때는 지금 규칙 그대로(1100 미만 크기 칸 숨김 · 600 미만 보낸 사람 112 · 날짜 72).
  • 분할 보기: 행에 마우스를 올리거나 초점이 가면 왼쪽 36px 에 고르기 상자가 나타나고, 하나라도 고르면 모든 행에 보인다. 행을 누르면 지금처럼 메일을 연다. Ctrl(⌘)+클릭은 하나씩, Shift+클릭은 범위, Esc 는 선택 풀기 (두 보기 공통). 여러 통을 고르는 동안 본문 칸은 열어 둔 메일을 그대로 보인다.
  • 도구 줄: 하나라도 고르면 목록 머리(52)가 도구 줄로 바뀐다(바탕 #F3F8F8) — "N통 선택됨"(14px 굵게) · 읽음 표시 · 안 읽음 표시 · 중요 표시(고른 것이 모두 중요면 "중요 해제") | 이동 ▾ · 삭제(휴지통에서는 "영구 삭제", #B42318) … 오른쪽 끝 "선택 해제"(✕). 단추는 높이 44 · 아이콘 + 이름이고, 폭 1100 미만이거나 분할 보기의 목록 칸에서는 아이콘만(40×44 — 이름은 툴팁 · aria-label).
  • 이동 메뉴(항목 높이 40 — 메뉴 항목 밀도 예외): 지금 메일함 · 임시보관함 · 휴지통을 뺀 메일함.
  • 알림(오른쪽 아래 · 되돌리기가 있으면 10초): "메일 N통을 보낸편지함으로 옮겼습니다." [되돌리기] · "메일 N통을 휴지통으로 옮겼습니다." [되돌리기] · "메일 N통을 영구 삭제했습니다." · 일부만 처리했으면 뒤에 "1통은 그사이 옮겨졌거나 지워져 그대로 두었습니다." (메일함 이름의 받침에 맞춰 '으로/로'). 읽음 · 중요 표시는 목록이 바로 바뀌므로 알리지 않는다.
  • 영구 삭제 확인: 휴지통에서만. 제목 "메일 N통을 영구 삭제할까요?" · 글 "휴지통에서 지운 메일은 되돌릴 수 없습니다." · [영구 삭제](#B42318 채움) [취소] — 열리면 「취소」에 초점이 간다.
  • 메일함을 바꾸거나 검색하면 선택을 푼다.

4단계 — 계정 · 메일 설정 (2026-10-28 확정 — SettingsAccount · SettingsVacation · MailboxManage · MailActions4)

API 와 서버 규칙은 계약 1절 "PUT /api/auth/password", 2절 "메일함 관리" · "부재중 자동 응답" · "스팸 · 원문".

설정 메뉴

  • 왼쪽 메뉴를 두 묶음으로: 계정 › 비밀번호 / 메일 › 서명 · 부재중 자동 응답. 묶음 이름은 12px #667085 굵게, 항목은 지금 서명 항목과 같은 모양(40 · 아이콘 16).

비밀번호

  • 칸 셋(최대 폭 420): 지금 비밀번호 · 새 비밀번호 · 새 비밀번호 확인. 칸 높이 44, 앞의 두 칸 끝에 보기/숨기기 단추(44 · 눈 아이콘, aria-pressed).
  • 새 비밀번호 아래 안내 "8자 이상 · 지금 비밀번호와 달라야 합니다. 영문 · 숫자 · 기호를 섞으면 더 안전합니다." (12.5px #667085).
  • 오류는 그 칸 아래 빨간 글(#B42318, 아이콘 alert-circle) · 칸 테두리도 빨강 — "지금 비밀번호가 올바르지 않습니다." · "새 비밀번호가 서로 다릅니다."
  • [비밀번호 바꾸기] → "비밀번호를 바꿨습니다. 다른 기기 · 브라우저의 로그인은 끊었습니다."(#067647), 세 칸을 비운다.
  • 아래 안내 상자(바탕 #F3F8F8): 다른 기기의 웹메일 로그인이 끊기는 것과 메일 프로그램(IMAP)에 새 비밀번호를 다시 넣어야 하는 것.

부재중 자동 응답

  • 부재중 자동 응답 사용(44). 끄면 아래 칸을 흐리게(쓴 글은 남긴다).
  • 기간: (•) 끌 때까지 계속 / ( ) 기간 정하기 → 시작 · 끝 날짜 칸(44). "시작일 0시부터 끝나는 날 자정까지 답합니다 (이 브라우저의 시간대 기준)."
  • 제목(44, 최대 폭 520) · 내용(여러 줄, 글자만 — "글자만 보냅니다 (서식 · 그림 없음). 서명은 붙지 않습니다.").
  • [저장] → "저장했습니다. 10월 30일(금)부터 11월 3일(화)까지 자동으로 답합니다." / "끌 때까지 자동으로 답합니다." / 끄면 "자동 응답을 껐습니다 — 써 둔 글은 남겨 둡니다."
  • 미리 보기 "보낸 사람이 받는 답장"(보낸 사람 · 제목 · 내용) + "같은 사람에게 매번 보내지는 않습니다(메일 엔진 규칙)."
  • 켜져 있는 동안 메일 화면의 메일함 패널 아래(사용량 위)에 안내(바탕 #FFF7E6 · #7A5200, 높이 44 이상): "부재중 자동 응답 켜짐" / "11월 3일까지 · 설정" — 누르면 이 설정으로.

메일함 관리 (메일함 패널)

  • 기본 메일함 아래에 "내 메일함"(12px #667085 굵게) 묶음 — 하위 메일함은 16px 씩 들여 쓴다(지금 규칙). 묶음 끝에 + 새 메일함.
  • 각 메일함 행 오른쪽에 ⋯ 단추(32×40 — 별 칸과 같은 밀도 예외). 마우스를 올리거나 초점이 가면 보이고, 메뉴가 열린 동안은 계속 보인다. 숫자(안 읽음)는 ⋯ 앞에 그대로.
  • ⋯ 메뉴(항목 40): 모두 읽음으로 표시(안 읽은 메일이 있을 때) · 하위 메일함 만들기(받은편지함 · 내 메일함) · 이름 바꾸기 · 지우기(빨강 — 내 메일함만) · 휴지통 비우기 / 스팸메일함 비우기(빨강 — 그 메일함에서만).
  • 대화상자(공용 모양): 새 메일함 · "‘프로젝트’ 안에 새 메일함" · 메일함 이름 바꾸기 — 이름 칸(44, 64자) + 안내 "64자까지. 같은 자리에 같은 이름은 쓸 수 없습니다." (같은 이름이면 그 자리에 빨강 "같은 이름의 메일함이 이미 있습니다."). 지우기 — "‘거래처’ 메일함을 지울까요?" "안의 메일 12통은 휴지통으로 옮깁니다. 지운 메일함은 되돌릴 수 없습니다." [지우기](빨강 채움) [취소]. 하위 메일함이 있으면 "‘프로젝트’ 메일함을 지울 수 없습니다" "하위 메일함이 있습니다. 하위 메일함을 먼저 지워 주세요." [확인].
  • 휴지통 · 스팸메일함 목록 머리 오른쪽에 [휴지통 비우기] / [스팸메일함 비우기](빨강 글자 · 44). 확인 "휴지통의 메일 21통을 모두 영구 삭제할까요?" "지운 메일은 되돌릴 수 없습니다." [영구 삭제] [취소].
  • 비우기 · 지우기 대화상자는 열리면 「취소」에 초점이 간다.

스팸 · 원문 · 오래 걸리는 처리

  • 읽기 화면 도구 줄: 답장 · 전체 답장 · 전달 | 스팸 신고 · 삭제 · ⋯(더 보기 40×44) — ⋯ 메뉴에 "원문 내려받기 (.eml)". 스팸메일함에서는 「스팸 신고」 자리에 「스팸 아님」(#0A5F67 굵게, 받은편지함 아이콘).
  • 여러 통 도구 줄: 이동 ▾ 과 삭제 사이에 「스팸 신고」(스팸메일함에서는 「스팸 아님」).
  • 오래 걸리는 처리(모두 읽음 · 비우기 · 메일함 지우기): 알림이 "받은편지함의 메일을 읽음으로 표시하는 중 · 600 / 1,234통"(돌아가는 아이콘)으로 남았다가 끝나면 "받은편지함의 메일 1,234통을 읽음으로 표시했습니다." 처리하는 동안 다른 일을 해도 된다.
  • 알림 문구: "메일 2통을 스팸메일함으로 옮겼습니다." [되돌리기] · "메일 1통을 받은편지함으로 옮겼습니다 (스팸 아님)." [되돌리기] · "‘협력사’ 메일함을 만들었습니다." · "‘거래처’ 메일함을 지웠습니다. 안의 메일 12통은 휴지통에 있습니다." · "휴지통을 비웠습니다 — 메일 21통." (비우기 · 지우기는 되돌리기 없음)

5단계 — 실시간 새 메일 · 바탕화면 알림 (2026-10-28 확정 — SettingsNotify · MailRealtime)

API 와 서버 규칙은 계약 2절 "실시간 알림".

설정 › 메일 › 알림

  • 메뉴의 "메일" 묶음 끝에 「알림」(종 아이콘).
  • 새 메일이 오면 바탕화면 알림 보내기(44). 켜면 브라우저가 허락을 묻는다. 칸 아래 안내(12.5px): 묻기 전 "켜면 브라우저가 알림을 허락할지 한 번 묻습니다 — 「허용」을 눌러 주세요." · 허용됨 "이 브라우저에서 알림이 허용되어 있습니다."(#067647, 체크 아이콘) · 막힘(칸을 끄고 빨강 #B42318) "브라우저가 이 사이트의 알림을 막고 있습니다. 주소창 왼쪽의 사이트 정보(자물쇠) › 알림을 「허용」으로 바꾼 뒤 이 화면을 새로 고쳐 주세요." · 지원하지 않음 "이 브라우저는 바탕화면 알림을 지원하지 않습니다. 새 메일은 그래도 바로 목록에 나타납니다."
  • 알림에 보일 것: [x] 메일 제목도 보이기 — "끄면 보낸 사람 이름만 보입니다 — 화면을 다른 사람과 함께 보는 자리라면 꺼 두세요." (알림을 끄면 흐리게)
  • [시험 알림 보내기](보조 단추 44 · 종 아이콘) — 알림 "ARA 웹메일" / "알림이 잘 옵니다. 새 메일이 오면 이렇게 알려 드립니다."
  • 실시간 연결: ● 초록 "연결되어 있습니다 — 새 메일이 오면 바로 목록에 나타납니다." / 끊겼을 때 "연결할 수 없어 1분마다 확인합니다." (저절로 다시 잇는다)
  • 아래 안내 상자(바탕 #F3F8F8): 이 브라우저에만 적용 · 웹메일 탭이 하나라도 열려 있어야 · 웹메일 화면을 보고 있을 때는 보내지 않음.

목록 위에서 한 번 권하기

  • 알림을 켜지도 닫지도 않은 사람에게 메일 화면의 목록 머리 아래에 40 줄(바탕 #F3F8F8 — 새 메일 알림 줄과 같은 높이, 밀도 예외): 종 아이콘 + "바탕화면 알림을 켜 두면 다른 창을 보는 동안에도 새 메일을 알려 드립니다." [알림 켜기](강조 글자 굵게) [✕ 닫기].
  • [알림 켜기] → 브라우저가 묻는다 → 허용: 알림 "바탕화면 알림을 켰습니다." / 막힘: 오류 알림 "브라우저가 알림을 막고 있습니다. 푸는 방법은 설정 › 알림에 있습니다." [설정 열기].
  • [✕]는 다시 보이지 않는다(브라우저 · 사용자마다). 브라우저가 이미 막았거나 지원하지 않으면 보이지 않는다.

바탕화면 알림

  • 모양은 운영체제 · 브라우저가 그린다 — 정하는 것은 제목 · 내용 · 아이콘(ARA 마크, 바탕 #0B3640 둥근 사각)뿐이다.
  • 한 통: 제목 = 보낸 사람(이름, 없으면 주소), 내용 = 메일 제목. 여러 통: "새 메일 3통" / "박서준 · 이도윤 외 1명". 제목 숨김: 내용 "새 메일이 왔습니다".
  • 새 알림은 앞의 알림을 바꿔 쓴다(쌓이지 않는다). 누르면 웹메일 탭으로 와서 그 메일을 연다(여러 통이면 받은편지함).
  • 받은편지함에 새로 배달된 안 읽은 메일만(내가 보낸 메일 · 임시 저장 · 다른 메일함으로 옮겨진 메일은 아님), 웹메일을 보고 있지 않을 때만(보이고 초점이 있는 탭이 없을 때), 탭이 여러 개여도 한 번.

실시간 반영

  • 화면 모양은 그대로다 — 새 메일이 1초 안에 목록 · 메일함 숫자 · 탭 제목 "(N)" 에 나타난다(지금은 최대 1분). 목록을 내려 보고 있으면 지금처럼 "새 메일 N통 · 맨 위로" 줄.
  • 다른 탭을 보는 동안에도 탭 제목의 "(N)" 이 바로 바뀐다.
  • 실시간 연결이 끊기면 화면에는 알리지 않고 1분 확인으로 돌아간다(설정 › 알림의 연결 상태로 볼 수 있다).

6단계 — 메일 필터 (2026-10-28 확정 — SettingsFilter · MailFilterStates)

받은 메일을 조건에 따라 메일함으로 옮기고, 읽음 · 중요로 표시하고, 동료에게 전달한다. 규칙과 엔진 쪽 사실은 계약 2절 "메일 필터".

설정 › 메일 › 필터

  • 메뉴는 메일 묶음의 서명 · 부재중 자동 응답 · 필터 · 알림 (깔때기 아이콘). 머리는 "필터" + 설명 한 줄 + 오른쪽 [새 필터](브랜드 색).
  • 목록은 테두리 상자 하나에 행 60 — 순서 번호 · 켜기 스위치(36×20) · 이름(굵게)과 요약 한 줄 · ▲ ▼(32×40, 밀도 예외 — 메일함 ⋯ 와 같다). 행을 누르면 고친다. 요약은 "보낸 사람에 ‘@hanbit-partners.co.kr’ 포함 → 옮기기: 거래처" — 조건은 " 그리고 " / " 또는 ", 할 일은 " · ". 이름이 없으면 조건이 이름 자리에 온다. 꺼 둔 규칙은 글자를 흐리게. 옮길 메일함이 지워진 규칙은 빨간 안내 한 줄 "옮길 메일함이 지워져 받은편지함으로 들어옵니다 — 눌러서 고치세요."
  • 목록 아래 "필터 N개 · 50개까지 만들 수 있습니다." 와 안내 상자(차례로 적용 · 옮기면 멈춤 / 스팸 제외 / 사내 전달만 · 한 곳 · 사본 남김 / 부재중과 함께 동작).

고치기 창 (모달 620)

  • 이름(선택) → "어떤 메일에"(조건이 모두 · 하나라도 · 모든 메일) → 조건 줄(어디에서 116 · 포함/포함하지 않음 132 · 글 · ✕ 36, 5개까지 [조건 더하기]) → "할 일"(메일함으로 옮기기 + 메일함 고르기 · 읽음으로 표시 · 중요 표시 · 동료에게 전달 + 사내 주소) → 안내 띠 → [이 필터 지우기](고칠 때만, 왼쪽) · [저장] [취소].
  • 메일함 고르기에는 받은편지함 · 내 메일함 · 스팸메일함 · 휴지통(보낸편지함 · 임시보관함 없음 — 보관함은 구현하며 더했다, 아래 "6단계 구현하며 정한 보완"). 지워진 메일함이면 "(지워진 메일함) — 다른 메일함을 고르세요"가 골라진 채로.
  • 잘못은 [저장]을 누를 때 칸 아래 빨간 글로만 — 빈 조건 · ${ · 바깥 주소 전달 · 할 일 없음 (MailFilterStates 의 모음).

상태 · 들어오는 길 (MailFilterStates)

  • 필터가 없을 때 · 다른 곳에서 먼저 바꿨을 때(목록 위 노란 줄 + [다시 불러오기]) · 동작하지 않을 때(+ [다시 켜기]) · 50개를 다 만들었을 때 · 알림 문구.
  • 읽기 화면 ⋯ 메뉴에 「이런 메일로 필터 만들기」(원문 내려받기 아래) — 설정 › 필터의 새 필터 창이 보낸 사람의 주소로 채워져 열린다.
  • 켜기 · 끄기 · 순서 바꾸기는 알리지 않는다(목록이 바로 바뀐다). 저장 "필터를 저장했습니다.", 지우기 "‘거래처 메일’ 필터를 지웠습니다. [되돌리기]".

7단계 — 이미 받은 메일에 필터 적용 (2026-10-29 확정 — MailFilterApply · MailFilterApplyStates)

필터를 저장할 때 받은편지함에 이미 있는 메일에도 그 필터 하나를 적용한다. 6단계의 두 한계(고치기 창의 나가기 확인 없음 · 부재중 화면이 꺼진 규칙을 모름)도 함께 고친다. 규칙은 계약 2절 "이미 받은 메일에 적용".

고치기 창 — 「받은편지함에 이미 있는 메일에도 적용」

  • 할 일 아래, 6단계의 안내 띠 자리에 회색 상자 하나: 체크 칸(이름 굵게) + 칸 아래 설명(12.5). 상자 아래 한 줄 "스팸으로 판정되어 들어오는 메일에는 적용하지 않습니다."
  • 창을 열 때마다 꺼져 있다 — 필터의 설정이 아니라 이번 저장에 한 번 쓴다(읽기 화면에서 만드는 새 필터도 같다). 목록의 행에는 단추를 더하지 않는다 — 있는 필터는 창을 열어 고르고 저장한다.
  • 설명은 "저장한 뒤 받은편지함에서 조건에 맞는 메일을 찾아 바로 적용합니다." — 전달이 있는 필터면 " 전달은 새로 오는 메일에만 합니다."를 더한다.
  • 고를 수 없는 두 경우(칸을 끄고 이름을 흐리게, 설명이 까닭): 전달만 하는 필터 "전달만 하는 필터는 이미 받은 메일에 할 일이 없습니다 — 전달은 새로 오는 메일에만 합니다." · 이 탭에서 다른 필터를 적용하는 중 "다른 필터를 받은편지함에 적용하는 중입니다. 끝난 뒤에 고를 수 있습니다."
  • 목록 머리의 설명과 안내 상자의 "새로 오는 메일에만"을 바꾼다(MailFilterApply 보드의 글 — 이미 받은 메일 줄을 더하고, 전달 줄에 "새로 오는 메일만").

저장한 뒤 — 진행 · 결과 알림

  • "필터를 저장했습니다." 대신 4단계의 진행 알림: "‘거래처 메일’ 필터를 받은편지함에 적용하는 중 · 4,200 / 8,412통 확인"(확인한 수 / 받은편지함 메일 수 — 최근 메일부터). 끝나면 그 자리에서 "‘거래처 메일’ 필터를 받은편지함의 메일 37통에 적용했습니다." + 옮기는 필터면 [거래처 보기](그 메일함으로) — 읽음 · 중요만 하는 필터는 단추 없음, 맞는 메일이 없으면 "받은편지함에 ‘거래처 메일’ 필터에 맞는 메일이 없습니다."
  • 멈추면 "‘거래처 메일’ 필터 — 8,412통 가운데 4,200통까지 확인하고 멈췄습니다(12통에 적용). 다시 시도해 주세요." + [다시 시도](남은 메일부터 이어서). 처음 요청부터 실패하면 "필터는 저장했지만 받은편지함에 적용하지 못했습니다. 다시 시도해 주세요." + [다시 시도]. 다른 창 · PC 에서 적용 중이면 "다른 창에서 필터를 받은편지함에 적용하는 중입니다. 끝난 뒤 다시 시도해 주세요."
  • 그동안 다른 화면으로 가서 메일을 읽고 써도 이어 한다. 탭을 닫으면 멈춘다(이어 하지 않는다). 세션이 바뀌면 멈추고 알림을 닫는다.

저장하지 않고 닫을 때 (6단계 한계를 고친다)

  • 창을 연 뒤 바꾼 것이 있으면 [취소] · Esc · 다른 화면으로 가기에 확인 — 서명 · 부재중 설정의 나가기 확인과 같은 모양: "저장하지 않은 필터가 있습니다" / "창을 닫으면 고친 내용이 사라집니다." [계속 고치기](채운 단추 · 처음 초점 · Esc) [닫기]. 다른 화면으로 가려다 물으면 "이 화면을 나가면 고친 내용이 사라집니다." [나가기]. 탭 닫기 · 새로고침은 브라우저 확인창.
  • 읽기 화면에서 채워 연 새 필터는 채운 그대로면 묻지 않는다(바꾼 것은 창을 연 때와 견준다).

부재중 자동 응답 화면 — 엔진에서 규칙이 꺼졌을 때 (6단계 한계를 고친다)

  • 자동 응답을 켜 두었는데(저장한 값) 엔진에서 우리 규칙이 꺼져 있으면(isActive: false) 머리 아래 노란 줄(필터 화면의 줄과 같은 모양): "켜 둔 자동 응답이 지금은 동작하지 않습니다. 메일 서버의 규칙이 다른 곳에서 바뀌었습니다 — 아래 [저장]을 누르면 다시 켜집니다."
  • 단추는 없다 — 저장 단추는 늘 누를 수 있다. 꺼 두었으면 보이지 않는다. 저장하면 필터도 함께 다시 켜진다.

8단계 — 주소록 (2026-10-29 확정 — Contacts · ContactsEdit · ContactsMail)

레일의 주소록 모듈을 켠다(app-modules.ts 의 contacts). 내 주소록(연락처 · 그룹)과 사내 목록(직원 · 공용 주소)이 있고, 메일 쓰기 · 읽기 화면과 이어진다. 규칙은 계약 3절 "주소록". 셸 · 색 · 단추 · 창 · 알림은 메일 모듈과 같다.

주소록 화면 (Contacts)

  • 자리: 레일 · 상단 바 · 모듈 패널(208) · 목록(384) · 상세 — 메일의 분할 보기와 같다. 상단 바의 찾기 칸은 지금 보는 목록 안에서 찾는다 ("이름 · 주소 · 회사 · 전화번호 — 초성도 됩니다 (ㅂㅈㅎ)").
  • 패널: [연락처 추가](채운 단추) → 「내 주소록」 전체 연락처(수) → 「그룹」 그룹마다 한 줄(수 · ⋯ 32×40 — 메일함 ⋯ 와 같은 밀도 예외) + [새 그룹] → 「사내」 직원(수) · 공용 주소(수).
  • 목록 머리: 제목 · "N명"(공용 주소는 "N개", 찾으면 "찾은 결과 N명"). 그룹이면 오른쪽에 [구성원 넣기] [그룹에 메일 쓰기]. 사내면 머리 아래 회색 줄 — 직원 "메일 서버에 계정이 있는 직원입니다 — 이름과 주소만 보입니다." · 공용 주소 "여러 사람이 함께 받는 사내 주소입니다."
  • 목록 줄(56): 아바타 32(이름 첫 글자 — 공용 주소는 회색) · 이름(굵게) + 회사 · 부서(작게) / 대표 주소. 고른 줄은 청록 바탕. 이름 차례(한국어), 100명씩 받고 끝에 [더 보기].
  • 상세 머리 줄: [메일 쓰기] [고치기] … [⋯] — 사내는 [메일 쓰기] [내 주소록에 추가]. 아래로 아바타 48 · 이름(20) · "회사 · 부서 · 직책"(없으면 "소속 없음", 사내는 "사내 직원" · "공용 주소").
  • 상세 칸(왼쪽 이름 88): 이메일(주소마다 링크 — 누르면 그 주소로 새 메일, 옆에 종류) · 전화(번호 · 종류) · 소속 · 직책 · 그룹(누르면 그 그룹의 목록) · 메모(줄바꿈 그대로). 빈 칸은 줄째 뺀다. 사내 상세 아래: "사내 목록은 메일 서버의 계정에서 옵니다 — 이름과 주소만 보이고 여기서 고칠 수 없습니다. 전화번호 · 메모를 적어 두려면 내 주소록에 추가하세요."
  • 빈 상태: 찾은 것이 없으면 "‘…’에 맞는 연락처가 없습니다"(사내는 "주소가") + "이름 · 주소 · 회사 · 전화번호의 일부나 이름의 초성으로 찾습니다." · 고른 것이 없으면 "연락처를 고르세요".

연락처 추가 · 고치기 (ContactsEdit)

  • 모달 620 — 필터 고치기 창과 같은 틀이고 나가기 확인도 같다("저장하지 않은 연락처가 있습니다" / [계속 고치기] [닫기]).
  • 칸: 이름(100자) · 이메일(종류 회사 · 개인 · 기타 + 주소 + ✕, [이메일 더하기] 5개까지 — "받는 사람 칸에는 맨 위 주소가 먼저 나옵니다") · 전화번호(휴대전화 · 회사 · 집 · 기타 + 번호 + ✕, 5개까지) · 회사 · 부서 · 직책(한 줄에 셋) · 메모(1,000자 — 셈 "38 / 1,000") · 그룹(체크 칸). 맨 아래 "이름이나 이메일 가운데 하나는 있어야 합니다." · [저장] [취소], 고치기만 왼쪽에 [이 연락처 지우기](빨간 글).
  • 잘못은 [저장]을 누를 때 칸 아래 빨간 글로만(첫 잘못된 칸에 초점, 알림 없음) — "이메일 형식이 올바르지 않습니다." 등.
  • 같은 주소: 이메일 칸을 떠날 때 내 주소록에 같은 주소(다른 연락처)가 있으면 칸 아래 갈색 글 "이미 주소록에 있는 주소입니다 — 강서윤(거래처). 그래도 저장할 수 있습니다. [그 연락처 보기]" — 막지 않는다.
  • 지우기는 확인 없이: 창을 닫고 "‘박지훈’ 연락처를 지웠습니다." + 되돌리기.

그룹 (ContactsEdit)

  • 새 그룹 · 이름 바꾸기: 모달 400, 칸 하나(50자) · [만들기] / [저장] [취소]. 같은 이름이면 칸 아래 "같은 이름의 그룹이 있습니다. 다른 이름을 써 주세요."(CONTACT_001 사전 문구 — 알림 없이. 보드는 앞 문장만). 그룹은 100개까지.
  • 그룹 ⋯: 이름 바꾸기 · 그룹에 메일 쓰기 · 그룹 지우기(빨간 글). 지우기는 확인 없이 "‘거래처’ 그룹을 지웠습니다 — 연락처 4명은 그대로 있습니다." + [되돌리기].
  • 연락처 ⋯(상세): 주소 복사 · 연락처 지우기.
  • 구성원 넣기: 모달 620 — 「내 연락처 | 사내 직원」 전환(36 — .segmented 밀도 예외) + 찾기(초성), 체크 줄 48(이름 · 회사 · 주소). 이미 구성원인 연락처는 보이지 않는다. "N명 고름" · [넣기] [취소]. 사내 직원을 고르면 내 주소록에도 이름 · 주소로 추가한다(같은 주소의 연락처가 이미 있으면 그 연락처를 넣는다). 끝나면 "2명을 ‘거래처’ 그룹에 넣었습니다."
  • 빈 그룹: "‘…’ 그룹에 아직 아무도 없습니다" · "구성원을 넣으면 그룹 이름으로 한 번에 메일을 보낼 수 있습니다." + [구성원 넣기].
  • 그룹에 넣기 · 빼기는 연락처 고치기 창의 「그룹」으로, 여러 사람을 한꺼번에는 [구성원 넣기]로.

상태 (ContactsEdit)

  • 처음(연락처 없음): "아직 연락처가 없습니다" · "자주 메일을 주고받는 사람을 넣어 두면 받는 사람 칸에서 바로 찾습니다." · [연락처 추가] · "메일을 읽다가 ⋯ › 「보낸 사람을 주소록에 추가」로도 넣을 수 있습니다."
  • 사내 목록을 받지 못함: "사내 목록을 불러오지 못했습니다" · "잠시 후 다시 시도해 주세요. 내 주소록은 그대로 쓸 수 있습니다." · [다시 시도].
  • 한도: 연락처가 3,000개면 [연락처 추가]를 끄고(aria-disabled) 아래에 "연락처는 3,000개까지 둘 수 있습니다. 쓰지 않는 연락처를 지운 뒤 추가해 주세요."
  • 알림: "연락처를 저장했습니다." · "‘…’ 연락처를 지웠습니다." · "‘…’ 그룹을 지웠습니다 — 연락처 N명은 그대로 있습니다." · "N명을 ‘…’ 그룹에 넣었습니다." (단추가 있으면 10초)

메일과 함께 (ContactsMail)

  • ���는 사람 추천: 묶음은 주소록 → 그룹 → 사내 → 최근 주고받은 주소(모두 합쳐 최대 8개). 주소록은 주소마다 한 줄 — 대표 주소는 "주소 · 회사", 그 밖의 주소는 "주소 · 개인"처럼 종류. 사내는 이제 로그인한 적 있는 직원이 아니라 메일 서버에 계정이 있는 직원 · 공용 주소 모두다. 여러 묶음에 있는 주소는 앞 묶음에 한 번만.
  • 그룹 줄: 아이콘 + 그룹 이름(맞는 글자 굵게) + "N명 · 고르면 한꺼번에 넣습니다". 고르면 구성원의 대표 주소를 칩으로 넣고 칸 아래 "‘거래처’ 그룹의 4명을 받는 사람에 넣었습니다." 이메일이 없는 구성원 · 이미 넣은 주소는 빼고, 뺀 것이 있으면 "주소가 없는 1명은 뺐습니다"를 덧붙인다.
  • 주소록에서 메일 쓰기: 연락처 · 그룹 · 사내의 [메일 쓰기]와 주소 링크 → 새 쓰기 화면, 받는 사람을 채워서(그룹은 구성원 모두 — 받는 사람 칸). 쓰다 만 메일은 임시 저장 규칙대로 남는다.
  • 읽기 화면 ⋯: 둘째 묶음(「이런 메일로 필터 만들기」 위)에 「보낸 사람을 주소록에 추가」 → 이름 · 주소를 채운 연락처 추가 창. 이미 내 주소록에 있으면 「주소록에서 보기」(그 연락처를 고른 주소록). 사내 직원의 주소도 「보낸 사람을 주소록에 추가」. 보낸 사람 주소가 없는 메일은 항목이 없고, 연락처가 3,000개면 항목을 끄고 이름 아래에 까닭 한 줄.

9단계 — 보내기 취소 · 예약 발송 · 외부 이미지 · 인쇄 · 보낸 사람 차단 (2026-10-29 확정 — MailReadTools · MailPrint · MailSendLater · SettingsSendRead)

작고 자주 쓰는 다섯 가지를 한 번에 더한다. 계약은 docs/mail/api-contract.md 2절의 9단계 절들. 새 아이콘: 인쇄(printer) · 시계(clock) · 차단(원 + 빗금).

읽기 화면 — 외부 이미지 · ⋯ 메뉴 · 차단 (MailReadTools)

  • 외부 이미지 줄: 지금의 노란 안내 줄("외부 이미지를 차단했습니다. …") 오른쪽에 [이미지 보기](36 — 밀도 예외 --size-notice-action). 누르면 그 메일만 이미지를 보이고 안내 줄은 사라진다. 다시 열면 설정대로다. 받지 못한 이미지 자리는 비어 있다. 메일 안의 배경 그림은 보이지 않는다(알려진 한계).
  • 설정에 따라(설정 › 메일 › 보내기 · 읽기 › 외부 이미지): 늘 묻기(처음 값) · 주소록과 사내 직원은 늘 보기 · 늘 보기. 처음부터 보이면 안내 줄이 없다. 스팸메일함의 메일은 늘 묻는다.
  • ⋯ 메뉴: 「인쇄」(Ctrl+P 표시) → 「원문 내려받기 (.eml)」 → 구분선 → 「보낸 사람을 주소록에 추가」/「주소록에서 보기」 → 「이런 메일로 필터 만들기」 → 「보낸 사람 차단」(빨간 글). 이미 차단한 보낸 사람이면 마지막 항목이 「보낸 사람 차단 풀기」. 사내 직원 · 내 주소에는 차단 항목이 없다. 차단한 주소가 500개면 항목을 끄고 이름 아래에 까닭.
  • 차단 확인 창(모달 400): 제목 "보낸 사람 차단", 설명 "앞으로 오는 메일을 스팸메일함으로 보냅니다. 부재중 자동 응답도 보내지 않습니다." 고르기 둘 — 「이 주소만」(주소) · 「이 도메인 전체」(@도메인) — 여러 사람이 쓰는 메일 서비스(gmail.com · naver.com 등)는 도메인을 고를 수 없게 끄고 그 아래에 까닭. 체크 「이 메일도 스팸메일함으로 옮기기」(처음 켜짐), 안내 "차단한 주소는 설정 › 메일 › 차단한 주소에서 풀 수 있습니다." — [차단](처음 초점 — 되돌릴 수 있는 일) [취소].
  • 차단한 뒤: 알림 "‘주소’ 을 차단하고 이 메일을 스팸메일함으로 옮겼습니다." + [되돌리기](차단을 풀고 메일을 받은편지함으로). 스팸메일함에서 차단한 보낸 사람의 메일을 열면 회색 안내 줄 "차단한 보낸 사람의 메일입니다." + [차단 풀기].

인쇄 화면 (MailPrint)

  • ⋯ › 인쇄(또는 읽기 화면에서 Ctrl+P)는 새 탭에 인쇄 화면을 열고 브라우저의 인쇄 창을 바로 띄운다. 앱의 레일 · 메일함 · 목록 · 도구 줄은 인쇄하지 않는다.
  • 화면에만 보이는 위 줄: "인쇄 화면" + [인쇄](다시 띄우기) [닫기](탭 닫기). 인쇄 창을 닫아도 탭은 남는다.
  • 인쇄하는 것: 머리 줄(「ARA SYSTEM 메일」 · 인쇄한 직원의 이름 <주소>) → 제목 → 보낸 사람 · 받는 사람 · 참조 · 날짜(읽기 화면에 보이는 것 — 숨은참조는 넣지 않는다) → 첨부(이름 · 크기만) → 가는 선 → 본문(읽기 화면과 같은 정화). 쪽 번호 · 날짜는 브라우저의 머리글 · 바닥글 설정을 따른다.
  • 외부 이미지는 읽기 화면에서 보이던 대로. 본문은 인쇄 폭(A4)에 맞춰 높이를 다시 잰 뒤 인쇄한다 — 긴 메일은 여러 쪽으로 이어지고 끝에 빈 쪽이 생기지 않는다 (2026-10-29 확인: 지금 화면을 그대로 인쇄하면 앱 틀까지 찍히고, 화면 폭에서 잰 본문 높이 때문에 끝에 빈 쪽이 10장 생겼다).
  • 못 열 때: 메일을 찾을 수 없으면 "메일을 찾을 수 없습니다" + [닫기], 받지 못하면 [다시 시도] — 인쇄 창은 띄우지 않는다. 세션이 끝났으면 로그인 화면으로.

보내기 취소 · 예약 발송 (MailSendLater)

  • [보내기 ▾]: 쓰기 화면의 [보내기]를 둘로 나눈다 — 왼쪽은 지금의 [보내기], 오른쪽 ▾(44 — 보드는 40, 밀도 예외를 새로 만들지 않는다)는 메뉴 「예약 발송…」(시계 아이콘). 보내기 단축키는 없다(쓰기 화면에 Ctrl+Enter 묶음이 없다 — 2026-10-30 확인). 보내는 중 · 저장 중 · 첨부를 올리는 중에는 두 단추를 함께 끈다(지금의 [보내기]와 같은 규칙).
  • 예약 창(모달 400): 제목 "예약 발송". 고르기 넷 — 「내일 오전」 9:00 · 「내일 오후」 1:00 · 「다음 주 월요일」 9:00 · 「직접 고르기」(날짜 칸 + 30분 단위 시각) — 줄마다 오른쪽에 날짜 · 요일 · 시각. 오늘 오후가 남았으면 「오늘 오후」 6:00 이 첫 줄. 안내 "이 PC 의 시간(한국 표준시)으로 보냅니다 · 7일 안에서 고를 수 있습니다." [예약] [취소]. 칸 아래 오류: "지금부터 5분 뒤 이후의 시각을 고르세요." · "7일 안의 시각만 고를 수 있습니다." 받는 사람 확인은 지금처럼 보내는 순간(없는 주소면 받는 사람 칸의 띠).
  • 보낸 뒤 알림(오른쪽 아래): "메일을 보냈습니다." + [보내기 취소] + 아래 남은 시간 막대(설정한 초 — 마우스를 올려도 멈추지 않는다) → 시간이 지나면 저절로 닫힌다. 일부가 받지 못하면 빨간 알림 "보냈지만 1명은 받지 못합니다 — 주소 (까닭)" + [보내기 취소](주소를 고쳐 다시 보낼 수 있게). [보내기 취소] → "보내기를 취소했습니다. 쓰던 메일로 돌아왔습니다." + 그 메일로 쓰기 화면(받는 사람 · 제목 · 본문 · 첨부 그대로). 늦게 누르면 "이미 보낸 메일이라 취소할 수 없습니다." 예약 → "10월 30일 (금) 오전 9:00 에 보냅니다." + [예약됨 보기]. 로그아웃 · 다른 계정 로그인 때 알림은 닫는다 — 메일은 그대로 보내진다.
  • 「예약됨」: 메일함 패널의 보낸편지함 아래(예약한 메일이 있을 때만, 시계 아이콘, 오른쪽에 통 수). 목록은 받는 사람 · 제목 · 보낼 시각(청록 글 — "10월 30일 (금) 오전 9:00") 차례, 고르는 칸 · 여러 통 처리는 없다. 보낸 메일은 저절로 빠지고 보낸편지함에 남는다.
  • 예약한 메일을 열면: 머리 아래 청록 안내 줄 "10월 30일 (금) 오전 9:00 에 보낼 메일입니다." + [고치기](예약을 취소하고 쓰기 화면으로) [예약 취소](보내지 않고 임시보관함으로 — 알림 "예약을 취소하고 임시보관함으로 옮겼습니다." + [고치기]). 답장 · 전달 · 스팸 신고 단추는 없다. [삭제]는 예약을 취소한 뒤 휴지통으로(확인 창).
  • 보낸편지함: 아직 보내지 않은 메일은 날짜 칸이 "예약 · 10월 30일 오전 9:00"(청록) — 보내기 취소를 기다리는 동안은 "보내는 중".

설정 · 보내기 · 읽기 · 차단한 주소 (SettingsSendRead)

  • 메뉴: 메일 묶음에 「보내기 · 읽기」(필터 아래) · 「차단한 주소」를 더한다 — 서명 · 부재중 자동 응답 · 필터 · 보내기 · 읽기 · 차단한 주소 · 알림.
  • 보내기 · 읽기: 고르는 대로 저장한다(저장 단추 없음 — 머리 오른쪽에 "저장했습니다"). 서버에 저장되어 어느 PC 에서나 같다.
    • 「보내기 취소」 — 눌림 전환(36 — .segmented) 끔 · 5초 · 10초(처음) · 20초 · 30초 + "[보내기]를 누른 뒤 이 시간 동안 [보내기 취소]로 되돌릴 수 있습니다. 그동안 메일은 메일 서버에서 기다리므로 창을 닫아도 보내집니다."
    • 「외부 이미지」 — 라디오 셋(늘 묻기 · 주소록 · 사내 직원의 메일은 늘 보기 · 늘 보기, 줄마다 아래 설명) + 회색 상자 "외부 이미지는 사내 메일 서버가 대신 받아 보여 줍니다 — 보낸 사람에게 이 PC 의 주소는 드러나지 않습니다. 다만 이미지를 받으면 보낸 사람은 메일을 열어 본 것을 알 수 있습니다."
  • 차단한 주소: 머리 "차단한 주소" + "3 / 500" + 설명 "이 주소에서 오는 메일은 스팸메일함으로 갑니다. 부재중 자동 응답도 보내지 않습니다. 「@도메인」은 그 도메인에서 오는 메일 모두입니다."
    • 더하기 줄: 칸 "차단할 주소"(placeholder "kim@example.com 또는 @example.com") + [차단]. 칸 아래 오류: "메일 주소나 @도메인 형식으로 써 주세요." · "이미 차단한 주소입니다." · "내 주소 · 사내 도메인은 차단할 수 없습니다." · "gmail.com 처럼 여러 사람이 쓰는 메일 서비스는 도메인으로 막을 수 없습니다 — 주소로 막아 주세요." · "500개까지 차단할 수 있습니다."
    • 목록: 줄마다 주소(굵게) + 아래 작은 글 "10월 29일 차단"(도메인은 "도메인 전체 · …") + 차단 풀기. 누르면 바로 풀고 알림 "‘주소’ 차단을 풀었습니다." + [되돌리기]. 이미 받은 메일은 옮기지 않는다. 비었으면 "차단한 주소가 없습니다. 메일의 ⋯ › 보낸 사람 차단으로도 막을 수 있습니다."

11단계 — 대용량 첨부 (2026-10-30 확정 — ComposeLargeFiles · LargeFileBlock · MailReadLargeFiles · SharePage · SettingsLargeFiles)

메일에 담지 못하는 큰 파일을 링크로 보낸다. 파일은 회사 서버에 조각으로 올리고(끊겨도 이어서), 보낼 때 서버가 본문에 내려받기 안내(덩이)를 넣는다. 받는 사람은 로그인 없이 내려받기 화면 files.arasystem.it.kr/s/{토큰} 에서 받는다. 보관은 보낸 때(예약 발송은 보낼 시각)부터 30일째 날 23:59:59(한국 시간)까지 — 받는 사람 모두 같은 날이다. 한도는 파일당 2GB · 메일 한 통에 링크 파일 10개 · 직원마다 동시 보관 10GB(올리는 중 · 임시 저장 포함) — 모두 운영 설정이다. 제품 · 기술 결정은 2026-10-30 확정본과 같은 날 정한 보완([링크 복사]는 읽기 화면에서만 · 보내지 않은 파일 지우기 · 전달은 인용 속 링크로 · 첨부처럼 올리는 규칙 — 아래 각 절)을 따른다. 데이터 · 규칙은 계약 4절(docs/mail/api-contract.md "대용량 첨부 — 11단계")이 정하고, 아래 문구가 새 에러 코드(FILE_003~FILE_008 · MAIL_016 — 계약 4절 "에러 코드")의 사전 문구 초안이다. 새 아이콘: 멈춤(pause) · 이어 올리기(play) · 경고 삼각형(alert-triangle). 새 토큰은 없다 — 밀도 예외도 있는 값(40 · 36)을 다시 쓴다.

쓰기 화면 — 첨부 칸 (ComposeLargeFiles)

  • 링크로 바뀌는 때: 파일 하나가 20MB 를 넘을 때, 메일에 담은 파일의 합계를 25MB 넘게 만들 때(고른 차례대로 그 파일부터), 메일에 담지 못하는 형식(mp4 · mov · dwg · dxf · psd · ai · 7z)일 때 저절로 링크로 보낸다. 묻는 창은 없다 — 합계 때문에 바뀌면 칸 아래 ⓘ 한 줄 "‘회의자료_전체.pptx’(9.4 MB)는 메일에 담으면 합계가 25MB 를 넘어 링크로 보냅니다."(role=status). 받는 사람이 사내뿐이어도 같은 규칙이다. 메일에 담은 파일도 줄의 링크 단추(40 · 「링크로 보내기」 — 마우스를 올리면 같은 글)로 바꿀 수 있다 — 이 쓰기 화면에서 고른 파일에만 있다(전달 원본 · 저장본에서 이어 온 첨부에는 없다 — 처음 판). 링크 → 메일에 담기 단추는 없다(빼고 다시 고르면 한도 안에서 담긴다 — 아래 "처음 판에서 미룬 것").

  • 안내문: "파일당 최대 20MB · 합계 25MB · 최대 20개 · 더 큰 파일은 링크로 보냅니다 (파일당 2GB · 10개까지)".

  • 두 묶음: 링크 파일이 있으면 칸을 「메일에 담는 파일 N개」(+ "합계 18.1 MB / 25 MB") · 「링크로 보내는 파일 N개」(+ "받는 사람은 보낸 날부터 30일 동안 내려받습니다 — 오늘 보내면 11월 29일(일)까지")로 나눈다(12.5 굵게 + 12 회색). 일반 첨부만 있으면 지금처럼 묶음 이름 없이 — 링크 파일만 있어도 「링크로 보내는 파일 N개」 머리는 둔다(기간 안내 · 보관 경고의 자리, 보드대로). 보관 사용량이 80%를 넘으면 링크 묶음 머리 오른쪽에 "보관 8.4 / 10 GB"(갈색 굵게).

  • 링크 파일 줄: 아이콘이 링크(청록 #0A5F67), 두 줄 — 이름 · 단추(40) / 크기 · 상태(12px, 아이콘 선만큼 26px 들여), 올리는 동안 3px 막대. ✕ 는 「빼기」 — 올리던 것을 멈추고 서버의 조각도 지운다.

    상태 둘째 줄 단추 · 모양
    올리는 중 "올리는 중 · 420 MB / 1.2 GB" + 오른쪽 "약 3분 남음"(처음 몇 초는 "시간 계산 중") [멈춤] ✕ · 청록 막대
    멈춤 "멈춤 · 420 MB / 1.2 GB 올림" [이어 올리기] ✕ · 회색 막대 — 그 파일만 멈춘다
    잠깐 끊김 "연결이 끊겨 다시 잇는 중… · 420 MB / 1.2 GB 올림"(갈색) 누를 것 없음 — 2 · 5 · 15 · 30초 간격으로 저절로, 받은 조각은 그대로
    오래 끊김 "올리지 못하고 멈췄습니다 — 올린 420 MB 는 남아 있습니다."(빨강) 빨간 줄 · 다시 시도 ✕ — 저절로 다시 잇기를 1분 넘게 못 했을 때
    다 올림 · 검사 중 "612 MB · 보안 검사 중 — 끝나지 않아도 보낼 수 있습니다" ✕
    검사 끝 깨끗하면 크기만 "1.2 GB" · 못 했으면 "860 MB · 검사하지 못함 — 암호가 걸린 압축 파일 등"(까닭은 응답에 없다 — 암호 압축 · 한도 밖 · 검사기 없음을 함께 쓴다) ✕
    위험 "위험한 내용이 발견되어 보낼 수 없습니다. 이 파일을 빼 주세요."(경고 삼각형) 빨간 줄 · ✕
  • 올리기 전 검사(지금 첨부 사전 검사와 같은 결 — 넘으면 올리지 않고 빨간 줄, 그 파일만 빠지고 보내기는 막지 않는다): "파일 하나는 2GB 까지 보낼 수 있습니다." · "링크로 보내는 파일은 메일 한 통에 10개까지입니다." · "대용량 보관 한도(10GB)를 넘어 올릴 수 없습니다. 보내지 않은 파일을 지우거나 링크를 끊으면 바로 공간이 생깁니다. [대용량 첨부 보기]" · "지금은 서버에 공간이 모자라 올릴 수 없습니다. 잠시 후 다시 시도해 주세요." · 형식은 FILE_001 사전 문구. 링크로 보낼 수 있는 형식 = 일반 첨부 목록 + mp4 · mov · dwg · dxf · psd · ai · 7z. 실행 파일(exe · msi · bat · cmd · js · vbs · scr 등)과 디스크 이미지(iso · img · vhd)는 막는다. 파일 고르기 창은 두 목록을 합친 형식만.

  • 다시 열 때: 브라우저는 고른 파일을 스스로 다시 읽지 못한다. 새로고침 · 창을 닫은 뒤 저장본을 열면 노란 줄 "올리다 멈춘 파일 — 같은 파일을 다시 고르면 이어서 올립니다 · 420 MB / 1.2 GB 올림" + 파일 고르기 ✕. 다른 파일을 고르면 그 줄 안에 "고른 파일이 멈춘 파일과 다릅니다(이름 · 크기). 같은 파일을 골라 주세요."(이름 · 크기 · 고친 시각으로 맞춘다 — 서버는 마지막 조각 뒤 24시간 기다린다). 다 올린 파일은 저장본에 붙어 "1.2 GB · 이 임시 저장본에 11월 29일까지 남습니다"(올린 날부터 30일), 지나면 빨간 줄 "기간이 지나 지운 파일입니다 — 빼고 다시 올려 주세요." 설정 › 대용량 첨부에서 지웠으면 같은 모양에 글만 "설정 › 대용량 첨부에서 지운 파일입니다 — 빼고 다시 올려 주세요."

  • 다 올릴 때까지 보내기를 막는다: [보내기]와 ▾ 를 disabled 가 아니라 aria-disabled 로 끄고(초점은 받는다) [임시 저장] 오른쪽에 까닭 글(ⓘ 12.5 — 두 단추의 aria-describedby). 누르면 보내지 않고 까닭을 읽어 준다(role=status — 보내기 단축키는 앱에 없다). 「예약 발송…」도 같다. 임시 저장은 그대로 된다. 까닭 글 — 올리는 중 · 다시 잇는 중 "파일 2개를 올리는 중입니다 — 다 올리면 보낼 수 있습니다." · 멈춤 · 오래 끊김 · 다시 열어 멈춘 파일 "다 올리지 못한 파일이 있습니다 — 이어 올리거나 빼 주세요." · 위험 · 지운 파일(기간 지남 · 설정에서 지움) "보낼 수 없는 파일이 있습니다 — 빨간 줄의 파일을 빼 주세요."

  • 나가기 확인: 올리는 중이면 "올리는 중인 파일이 있습니다" / "이 화면을 나가면 올리기가 멈춥니다. 임시보관함에서 이 메일을 다시 열고 같은 파일을 고르면 이어서 올립니다." [계속 쓰기](처음 초점 · Esc) [나가기]. 떠나기 전 임시 저장은 지금처럼 먼저 하고, 올리는 중인 파일은 저장본에 "올리다 멈춘 파일"로 남긴다. 다 올린 뒤에는 이 확인이 없다.

본문 덩이 — 바깥 메일 프로그램에서 (LargeFileBlock)

  • 자리: 내 글 · 서명 아래, 인용 위(쓰기 화면이 알려 준 경계). 인용이 없으면 맨 끝. 서버가 정화 · 서명 그림 · 중복 확인을 마친 뒤 넣는다 — 쓰기 화면의 편집기에는 보이지 않는다.
  • 내용(위에서부터): "대용량 첨부 2개 (합계 1.8 GB)"(14 굵게) → "2026년 11월 29일(일)까지 내려받을 수 있습니다 (한국 시간)" → 파일마다 이름 · 크기(회색, 오른쪽) → "내려받기 https://files.arasystem.it.kr/s/…"(주소를 글자 그대로 보이는 링크 — 단축 주소 · 단추 모양 없음. 파일 이름은 링크가 아니다 — 메일 한 통에 링크 하나) → "내려받을 때 로그인이나 비밀번호를 묻지 않습니다."(12 회색) → "Large files: download by Nov 29, 2026 (KST) — no login or password needed."(12 회색).
  • 모양: 테두리 1px #D0D5DD 표 + 글자, 안 여백 12 · 16, 맑은 고딕 14 · 13 · 12. 보내는 서식의 허용 목록 안에서만(table · td colspan · a, 테두리 · 여백 · 색 · 크기 · 굵기 · 정렬 · 줄바꿈 막기) — 둥근 모서리 · 그림 · 아이콘 · 뱃지 없음. 폭은 내용에 맞춘다(주소 줄이 가장 길다 — 좁은 화면에서는 받는 프로그램이 줄을 끊는다).
  • 텍스트 대체 본문: 같은 자리에 대시 줄로 가른 같은 내용 — 파일은 "- 이름 (크기)", 주소는 "내려받기: 주소".
  • 보이지 않는 표시 <!--ara-large-files:v1--> … <!--/ara-large-files--> — 보내는 정화가 주석을 지우므로 사용자가 흉내 낼 수 없다. 보내기 취소로 되돌린 메일은 덩이를 떼고 링크 파일로 돌려준다. 나중에 링크를 끊어도 본문의 날짜 · 주소는 그대로다(누르면 내려받기 화면이 "공유 멈춤").
  • 답장 · 전달: 링크 파일을 첨부 칸에 다시 싣지 않는다 — 인용 속 덩이는 표째 글(링크)로 그대로 가고 링크는 처음 기간까지 열린다. 인용으로 옮기면 표시가 지워진다. 전달 쓰기 화면은 칸 아래 한 줄로만 알린다(아래 "읽기 화면 › 전달 쓰기 화면"). 바깥 사람의 답장에 인용된 덩이도 우리 웹메일에서 본문 그대로다 — 첨부 칸으로 올리지 않고 접지도 않는다.

읽기 화면 (MailReadLargeFiles)

  • 첨부처럼 올리는 규칙: 주소가 우리 내려받기 주소이고 서버에 있는 링크이며, 링크 주인이 그 메일의 보낸 사람과 같을 때만 첨부 칸으로 올린다 — 바깥 사람 답장의 인용 속 링크 · 전달된 원래 링크는 본문 글이다.
  • 사내 받는 사람: 링크 파일을 첨부 칸에 일반 첨부와 함께 보인다 — 이름 아래 "1.2 GB · 11월 29일까지". 아이콘 · 다운로드는 일반 첨부와 같고, 내려받기 화면을 거치지 않고 바로 받는다. 「첨부 파일 N개」는 둘을 합한 수.
  • 본문의 덩이는 접는다: 그 자리에 접기 줄(높이 40 · 12.5 · 바탕 #F6F7F9) "본문의 대용량 첨부 안내 (접어 둠) — 파일은 아래 첨부 칸에 있습니다" + "펼치기". 본문 틀 안에서 스크립트 없이 열고 닫는다. 메일을 다시 열면 접혀 있고, 인쇄 화면은 펼친 채 인쇄한다.
  • 보낸 사람 자신: 파일마다 "1.2 GB · 3회 받음 · 11월 29일까지"(받지 않았으면 "아직 받지 않음"), 첨부 칸 아래 한 줄 "링크로 보낸 파일 2개 — 받는 사람 모두 같은 링크로 받습니다. 끊으면 둘 다 받을 수 없습니다." + [링크 복사] [링크 끊기](36 — 차단한 주소의 [차단 풀기]와 같은 밀도 예외, [링크 끊기]는 빨간 글 테두리 단추). [링크 복사]는 이 메일 본문의 덩이에서 주소를 읽어 복사한다 — "링크를 복사했습니다."(못 하면 "링크를 복사하지 못했습니다."). 설정 목록에는 없다(서버는 링크를 해시로만 안다). 받은 수는 처음부터 받기 시작한 수뿐(화면 열기 · 이어 받기 · 내가 받은 것은 빼고)이고, 누가 받았는지는 남기지 않는다.
  • 링크 끊기 확인(alertdialog 400): "대용량 첨부 링크를 끊을까요?" / "받는 사람 모두 이 메일의 파일 2개(설계도_v3.dwg · 현장영상_1029.mp4)를 더는 내려받을 수 없습니다. 이미 받은 파일은 되돌릴 수 없고, 끊은 링크는 다시 열 수 없습니다." 링크 끊기 [취소](처음 초점 · Esc). 끊으면 "링크를 끊었습니다. 받는 사람은 더 이상 내려받을 수 없습니다." — 되돌리기는 없다. 쓰던 보관 공간은 바로 비워진다.
  • 기간 지남 · 공유 멈춤: 흐리게(이름 #667085 · 아이콘 #98A2B3 — 투명도가 아니라 글자색) + "기간 지남 — 11월 29일까지였습니다" · "공유 멈춤 — 보낸 사람이 링크를 끊었습니다", 내려받기 단추 없음. 보낸 사람 자신에게는 받은 수가 남고 끊은 날이 보인다 — "1.2 GB · 3회 받음 · 공유 멈춤 — 10월 31일에 끊었습니다"(한국 날짜 · 응답의 revokedAt, 없으면 "공유 멈춤 — 링크를 끊었습니다"). 끝난 뒤 90일이 지나면 첨부 칸에서 빠지고 덩이는 그냥 본문이 된다.
  • 보안 검사: 검사 중이면 [다운로드]를 aria-disabled(title · 까닭 연결) + "보안 검사 중 — 끝나면 받을 수 있습니다". 위험은 빨간 줄 "위험 — 보안 검사에서 막았습니다. 받을 수 없습니다"(단추 없음). 검사하지 못한 파일은 받을 수 있고 글로만 알린다.
  • 전달 쓰기 화면: 원래 메일의 덩이는 인용 속 글(링크)로 그대로 간다 — 첨부 칸에 다시 싣지 않고 칸 아래 ⓘ 한 줄로만 알린다 "원래 메일의 대용량 첨부 2개는 인용 속 링크로 함께 갑니다 — 11월 29일까지". 공유가 멈췄거나 기간이 지난 링크면 갈색 한 줄 "인용 속 ‘도면_초안.dwg’ 링크는 공유가 멈춰 받을 수 없습니다."("… 기간이 지나 받을 수 없습니다.", 파일이 여럿이면 "‘설계도_v3.dwg’ 외 1개") — 보내기는 막지 않는다. 전달하는 사람이 새 큰 파일을 더하면 그것은 이 메일의 새 링크다(첨부 칸의 「링크로 보내는 파일」 — 전달하는 사람의 보관 한도에 잡힌다). 받는 쪽에서 인용 속 원래 링크는 본문 글이고, 내려받기 화면의 보낸 사람 · 끊을 수 있는 사람은 처음 보낸 사람(링크 주인)이다. 답장도 같다.
  • 목록의 클립 아이콘은 지금처럼 메일에 담긴 첨부만 본다 — 링크 파일만 있는 메일은 클립이 없다(아래 "처음 판에서 미룬 것").

내려받기 화면 (SharePage)

  • 독립 화면: 앱 레일 · 상단 바 · 로그인 메뉴가 없다. 머리 바(#0B3640, 60)에 흰 ARA SYSTEM 로고(88) + "파일 내려받기", 바탕 #F6F7F9 에 가운데 열 640. 탭 제목 "김아라 님이 보낸 파일 — ARA SYSTEM".
  • 정상: "김아라 님이 보낸 파일 2개 (1.8 GB)"(20) → "보낸 사람 김아라 ara.kim@arasystem.it.kr · ARA SYSTEM"(로그인한 계정의 값 — 보낸 사람이 바꿀 수 없다) → "2026년 11월 29일(일)까지 내려받을 수 있습니다 (한국 시간) · 30일 남음" → 파일 목록(줄마다 아이콘 36 · 이름 · "1.2 GB · 보안 검사 · 이상 없음" · [내려받기](채운 단추 44)) → "내려받다 끊기면 브라우저의 다운로드 목록에서 다시 시도하세요 — 받은 곳부터 이어서 받습니다." → 청록 상자 "이 화면은 로그인이나 비밀번호를 묻지 않습니다. 묻는 화면이 나오면 닫고 보낸 사람에게 확인해 주세요." → "받을 이유가 없는 파일이면 postmaster@arasystem.it.kr 로 알려 주세요."(운영 설정 — 처음 값 postmaster@메일 도메인) → 영어 한 줄.
  • 메일 제목 · 본문 · 받는 사람, 내려받은 수 · 남은 수는 보이지 않는다. [내려받기]는 형식과 상관없이 늘 저장이고, 누르기 직전에 상태를 한 번 더 묻는다. noindex · no-referrer.
  • 상태(모든 상태에서 머리 · 영어 한 줄은 그대로. 신고 주소 줄은 링크 정보를 받은 상태에서만 — 없는 링크 · 요청이 너무 많음 · 오류 응답에는 그 값이 없다):
    • 검사 중 — 모두 검사 중이면 제목 "보안 검사 중입니다" / "파일을 검사하고 있습니다. 몇 분 뒤 이 화면을 다시 열어 주세요." + [다시 확인]. 일부면 그 줄만 "보안 검사 중" + [내려받기] aria-disabled.
    • 위험 — "내려받을 수 없는 파일입니다" / "보안 검사에서 위험한 내용이 발견되어 막았습니다. 보낸 사람에게 알렸습니다." 그 줄은 빨강 "위험 — 막았습니다", 단추 없음.
    • 검사하지 못함 — 제목은 그대로, 줄에 갈색 "검사하지 못함 — 암호가 걸린 압축 파일 등 안을 볼 수 없는 파일입니다. 보낸 사람을 아는 경우에만 여세요." + [내려받기](받을 수 있다).
    • 횟수 초과(파일마다 운영 설정 — 처음 값 500번, 넘은 파일만) — 그 줄에 "내려받을 수 있는 횟수를 넘었습니다. 보낸 사람에게 다시 보내 달라고 요청해 주세요."(숫자는 쓰지 않는다 — 한도는 운영 설정이고 응답에 없다)
    • 만료 — "내려받을 수 있는 기간이 지났습니다" / "2026년 11월 29일까지 받을 수 있었던 파일입니다. 파일은 지워져 다시 받을 수 없습니다. 필요하면 보낸 사람(김아라, ara.kim@arasystem.it.kr)에게 다시 보내 달라고 요청해 주세요."
    • 회수 — "보낸 사람이 공유를 멈췄습니다" / "필요하면 보낸 사람(김아라, ara.kim@arasystem.it.kr)에게 문의해 주세요." 파일 이름도 보이지 않는다.
    • 없는 링크(모르는 주소 · 끝난 지 90일이 지나 기록을 지운 링크) — "찾을 수 없는 링크입니다" / "메일의 주소가 끝까지 복사되었는지 확인해 주세요." 보낸 사람도 보이지 않는다.
    • 잠시 막힘(같은 IP 의 요청이 너무 많음) — "요청이 너무 많습니다" / "잠시 뒤 다시 시도해 주세요." + [다시 시도].
  • 좁은 폭(휴대폰 360): 머리 52 · 로고 76, 보낸 사람 주소는 이름 아래 줄로, 줄마다 [내려받기]가 이름 아래 전체 폭(44).

설정 › 메일 › 대용량 첨부 (SettingsLargeFiles)

  • 메뉴: 메일 묶음의 차단한 주소 아래 「대용량 첨부」 — 서명 · 부재중 자동 응답 · 필터 · 보내기 · 읽기 · 차단한 주소 · 대용량 첨부 · 알림.
  • 머리 "대용량 첨부" + "메일에 링크로 보낸 큰 파일입니다. 받는 사람은 보낸 날부터 30일 동안 내려받고, 링크를 끊으면 바로 받을 수 없습니다."
  • 사용량 상자: "보관 사용량 6.2 / 10 GB"(메일함 사용량과 같은 표기) + 4px 막대(90% 를 넘으면 빨강 — 메일함 사용량과 같다) + "보낸 링크 4.3 GB · 보내지 않은 파일 1.9 GB(임시 저장 · 올리다 멈춤). 기간이 지나거나, 링크를 끊거나, 보내지 않은 파일을 지우면 바로 줄어듭니다."
  • 보내지 않은 파일(사용량 상자 바로 아래 — 공간을 바로 비울 수 있는 곳이라 보낸 링크보다 위): 「보내지 않은 파일 3개」 + "1.9 GB · 임시 저장한 메일에 실렸거나 올리다 멈춘 파일 — 지우면 바로 공간이 생깁니다". 없으면 묶음째 보이지 않는다.
    • 줄(52): 아이콘 · 이름 / (올리다 멈춘 파일은 앞에 갈색 "올리다 멈춤") · 크기(멈춘 파일은 "1.2 GB" / "420 MB 올림") · 실린 저장본의 제목 · 링크는 보이지 않는다(2026-10-30 — 저장본 id 는 저장할 때마다 바뀌고 엔진 밖에서도 지워져 서버가 따라가기 무겁다, 계약 4절 LargeFile 에 없음) · 남는 기한 "11월 29일까지 남음" / "올린 뒤 30일"(올리다 멈춘 파일은 이어 올리기 규칙대로 "내일 오후 2:41까지 남음" / "마지막으로 올린 뒤 24시간") · [지우기](36, 빨간 글).
    • [지우기] → 확인 창(alertdialog 400) "보내지 않은 파일을 지울까요?" / "‘시공도면_v4.dwg’(620 MB) 파일을 지웁니다. 이 파일을 실은 임시 저장본이 있으면 열 때 첨부 칸에 ‘지운 파일’로 보입니다. 지운 파일은 되돌릴 수 없습니다." 지우기 [취소](처음 초점 · Esc) → "‘시공도면_v4.dwg’ 파일을 지웠습니다." — 줄이 사라지고 사용량이 바로 준다. 그 저장본을 쓰기 화면에서 열면 첨부 칸에 빨간 줄 "설정 › 대용량 첨부에서 지운 파일입니다 — 빼고 다시 올려 주세요."(쓰기 화면의 "기간이 지나 지운 파일"과 같은 모양 · 다른 글).
  • 보낸 링크: 「보낸 링크 6개」 + "메일마다 한 덩이 · 최근에 보낸 차례". 메일(링크)마다 한 덩이 — 링크가 메일 한 통에 하나라 [링크 끊기]는 덩이에 한 번이다(파일 줄마다 두면 같은 메일의 다른 파일까지 끊긴다).
    • 머리: 메일 제목(청록 링크 → 보낸편지함의 그 메일, 메일을 지웠으면 글자만) / "10월 30일 (금) 보냄 · 파일 2개 · 1.8 GB" · 오른쪽 "11월 29일까지" / "30일 남음"(3일 이하면 갈색 굵게) · 링크 끊기.
    • 파일 줄(44 · 바탕 #FBFBFC): 아이콘 · 이름 · 크기 · "3회 받음" / "마지막 오늘 오후 2:41"(받지 않았으면 "아직 받지 않음") · 상태.
    • 상태 글: 사용 중 · 검사 중(회색) · 위험(빨강 굵게 + "내려받기를 막음", 줄 바탕 #FEF3F2) · 기간 지남 · 공유 멈춤. 검사하지 못한 파일은 상태 아래 갈색 "검사하지 못함". 뱃지 없이 글자만.
    • 끝난 링크는 덩이째 흐리게 — 머리 오른쪽 "공유 멈춤 / 10월 22일에 끊음" · "기간 지남 / 10월 25일까지였습니다", 단추 없음. 90일 뒤 목록에서 빠진다.
    • 최근에 보낸 차례 · 20개씩 [더 보기].
  • [링크 복사]는 두지 않는다 — 서버는 링크를 해시로만 알아 원래 주소를 모른다. 주소는 보낸 메일의 읽기 화면(보낸 사람 자신)에서 복사한다.
  • [링크 끊기] → 읽기 화면과 같은 확인 창 · 같은 알림(창 글에 메일 제목). 끊는 동안 그 덩이의 [링크 끊기]를 막고, 끝나면 초점을 덩이 머리로.
  • 빈 상태: 사용량 "0 / 10 GB" + "링크로 보낸 파일이 없습니다" / "메일 쓰기에서 20MB 가 넘는 파일을 고르면 링크로 보냅니다. 받는 사람은 보낸 날부터 30일 동안 내려받을 수 있습니다." 보내지 않은 파일이 있으면 그 묶음은 위에 그대로 있다. 목록을 받지 못하면 지금의 목록 오류와 같은 모양.
  • 위험이 나오면 빨간 알림 "올린 대용량 파일 ‘견적_요청서.zip’에서 위험한 내용이 발견되어 막았습니다." + [대용량 첨부 보기] — 웹메일을 열어 둔 동안 실시간 알림(large-file)으로 바로 띄운다. 그때 열려 있지 않았으면 알림은 없고 목록 · 첨부 칸의 상태(위험)로만 안다 — 이벤트에 보냈는지가 없어 "보낸 파일 · 내려받기를 막았습니다"로 나누지 않는다(계약 4절 "악성 검사"의 문구). 같은 메일의 다른 파일은 그대로 받을 수 있다.

처음 판에서 미룬 것

  • 링크 → 메일에 담기 되돌리기 단추(제품 조사 3.1) — 빼고 다시 고르면 한도 안에서 메일에 담긴다.
  • 링크 파일만 있는 메일의 목록 클립 아이콘 — 목록의 클립은 지금처럼 메일에 담긴 첨부만 본다.
  • 전달 원본 · 저장본에서 이어 온 첨부의 「링크로 보내기」 — 이 쓰기 화면에서 고른 파일에만.
  • 확정본이 미룬 것: 기간 고르기 · 연장, 다시 보내기, 받는 사람 확인 코드 · 비밀번호, 사내만, 받는 사람별 링크 · 기록, 처음 받음 · 만료 전 알림, 미리 보기, zip 모두 받기, 폴더 올리기, 관리자 화면 · 퇴사자 처리, 보낸 메일을 지울 때 파일도, 모바일 앱, 파일 저장소 연동.

12단계 — 계정 보안 (2026-10-31 확정 — LoginTwoFactor · SettingsTwoFactor · SettingsAppPasswords · SettingsLoginHistory)

2단계 인증(인증 앱 TOTP) · 앱 비밀번호 · 로그인 기록. 직원이 스스로 켠다 — 관리자가 전원에게 강제하는 것은 13단계다. 데이터 · 규칙은 계약 5절(docs/mail/api-contract.md "계정 보안 — 12단계")이 정하고, 새 에러 코드(AUTH_006~AUTH_009 — 계약 5절 "에러 코드")의 사전 문구 초안도 거기 있다. 설정 메뉴의 계정 묶음: 비밀번호 · 2단계 인증 · 앱 비밀번호 · 로그인 기록(새 항목 아이콘: 방패(체크) · 열쇠 · 시계(되감기)). 새 토큰은 없다 — 키 [복사] · 표의 [지우기] · [로그아웃] · 안내 상자 안 단추는 차단한 주소의 [차단 풀기]와 같은 밀도 예외 36(--size-notice-action)이다. 각 보드 위쪽은 실제 크기 화면(설정 셸은 SettingsAccount 와 같다 — 메뉴 208 · 콘텐츠 열 800), 아래는 상태 카드다.

로그인 — 코드 단계 (LoginTwoFactor)

  • 누가 오나: 비밀번호가 맞고 2단계 인증을 켠 직원만. 켜지 않은 직원은 지금처럼 바로 메일함으로 간다. 비밀번호가 틀리면 지금처럼 「이메일 또는 비밀번호가 올바르지 않습니다.」 — 2단계를 켰는지는 비밀번호가 맞은 뒤에야 드러난다.
  • 틀은 로그인 화면과 같다. 위에서부터:
    • 제목 "2단계 인증" / "비밀번호를 확인했습니다. 휴대폰 인증 앱에 표시된 코드를 넣어 주세요."
    • 주소 + [다른 계정으로 로그인] — 처음 화면으로(주소 칸은 비움), 확인한 비밀번호는 버린다.
    • 칸 "인증 앱에 표시된 6자리 코드" — 숫자 6자리만, 붙여 넣은 글의 띄어쓰기 · 하이픈은 지운다. inputmode="numeric" · autocomplete="one-time-code".
    • [확인] → "인증 앱을 쓸 수 없나요? 관리자에게 문의해 주세요." → "Outlook · 휴대폰 메일 앱은 이 코드를 묻지 않습니다. 설정 › 앱 비밀번호에서 만든 비밀번호로 연결합니다."
  • 상태
    • 코드 틀림 — 칸 아래 빨간 글(AUTH_006 사전 문구 "코드가 올바르지 않습니다. 휴대폰의 날짜와 시간이 「자동」으로 맞춰져 있는지 확인해 주세요."). 칸을 비우고 초점은 그 칸, 알림은 없다.
    • 시도 과다 — 위쪽 빨간 상자(로그인 오류와 같은 자리 · 같은 모양)에 REQ_001 사전 문구. 코드 실패는 비밀번호 틀림과 따로 센다(같은 주소 15분 5회 · 하루 20회). 칸 · 단추는 그대로 둔다.
    • 확인 중 — 「확인하는 중…」(로그인 단추와 같은 모양 · aria-busy), 칸은 읽기만. 맞으면 받은편지함으로 — 로그인 전에 가려던 주소가 있으면 그곳으로.
  • 이 화면에 10분 넘게 머물면 [확인] 때 「다시 로그인해 주세요」와 함께 처음 화면으로 간다. 새로고침도 처음 화면이다(비밀번호는 화면 메모리에만 있다).
  • 로그인 기록에는 코드까지 맞으면 「성공 · 2단계 인증」, 틀리면 「실패 — 코드 틀림」으로 남는다.

설정 › 계정 › 2단계 인증 (SettingsTwoFactor)

  • 머리 "2단계 인증" + "로그인할 때 비밀번호와 함께 휴대폰 인증 앱의 코드를 확인합니다. 비밀번호가 새어 나가도 휴대폰이 없으면 로그인할 수 없습니다."
  • 상태는 글자로만(뱃지 · 스위치 없음) — 테두리 상자 하나:
    • 「꺼짐」(회색 굵게) / "지금은 로그인할 때 비밀번호만 확인합니다." + [2단계 인증 켜기]
    • 「켜짐 — 2026년 10월 31일부터」(초록 #067647 굵게 — 켠 때를 모르면 「켜짐」) / "로그인할 때마다 비밀번호와 인증 앱의 코드를 확인합니다." + [2단계 인증 끄기]
  • 꺼져 있을 때 상자 아래:
    • 갈색 경고 "켜면 메일 프로그램은 앱 비밀번호로 다시 연결해야 합니다" / "Outlook · 썬더버드 · 휴대폰 메일 앱은 웹메일 비밀번호로 새로 연결할 수 없고, 이미 연결해 둔 프로그램도 다음에 다시 연결할 때 막힙니다. 켜기 전에 앱 비밀번호를 만들어 두면 편합니다."(앱 비밀번호 링크)
    • 「켜는 차례」 1 · 2 · 3(청록 굵은 숫자 — 동그라미 없음): 인증 앱 설치(Google · Microsoft Authenticator 처럼 6자리 코드를 만드는 앱) → [2단계 인증 켜기]를 눌러 QR 찍기 → 코드와 지금 비밀번호를 넣으면 켜진다.
  • 켜져 있을 때 상자 아래: "휴대폰을 바꿀 때는 2단계 인증을 끈 뒤 새 휴대폰으로 다시 켜 주세요 — 끌 때 지금 휴대폰의 코드가 필요합니다." · "휴대폰을 잃어버렸거나 인증 앱을 지웠으면 관리자에게 문의해 주세요." · "메일 프로그램은 앱 비밀번호로 연결합니다."
  • 켜기 창(모달 620 — wide, 필터 고치기 창과 같은 폭 · 처음 초점은 코드 칸)
    • ① "인증 앱으로 QR 코드를 찍어 주세요" / "인증 앱에서 [+] → 「QR 코드 스캔」을 고르고 이 그림을 찍습니다." + QR 그림
    • "찍을 수 없으면 이 키를 입력하세요" + 32자 키(4자씩 띄움, 고정폭) + [복사](36 — 「키를 복사했습니다.」) + "앱에는 「ARA SYSTEM (ara.kim@arasystem.it.kr)」로 저장됩니다."
    • ② "앱에 표시된 6자리 코드" ③ "지금 비밀번호" → [켜기] [취소]
    • 칸 아래 오류(알림 없이, 첫 잘못된 칸에 초점): 코드 AUTH_006 · 지금 비밀번호 AUTH_005("지금 비밀번호가 올바르지 않습니다."). 둘 다 틀렸으면 코드부터 — 서버가 코드를 먼저 본다. 틀린 코드는 지우고 그 칸에 초점을 둔다. QR · 키는 그대로라 다시 찍지 않아도 된다. 비밀번호 틀림은 로그인 실패와 함께, 코드 틀림은 코드 실패로 따로 센다.
    • 켜는 동안 「켜는 중…」 · [취소]. 맞으면 창을 닫고 화면이 「켜짐」이 되며, 위에 안내 상자 "2단계 인증을 켰습니다" / "메일 프로그램(Outlook · 썬더버드 · 휴대폰 메일 앱)은 다시 연결할 때 앱 비밀번호가 필요합니다." 앱 비밀번호 만들기 — 이 화면을 떠나면 사라진다.
    • 키는 창을 열 때마다 새로 만든다(10분 안에 켠다 — 지나면 AUTH_008 로 처음부터). 켜지 않고 닫으면 그 키는 버린다 — 앱에 먼저 저장한 항목은 지워 달라고 안내한다. QR · 키는 이 창에만 보이고 로그에 남기지 않는다.
    • 켜도 다른 곳의 웹메일 로그인은 끊지 않는다 — 끊으려면 로그인 기록에서.
  • 끄기 창(alertdialog 400 · 처음 초점 「취소」, Esc 도 같다): "2단계 인증을 끌까요?" / "끄면 비밀번호만으로 로그인합니다. 만들어 둔 앱 비밀번호는 그대로 남습니다." + 지금 비밀번호 · 인증 앱 코드 → 끄기 [취소]. 끄면 「2단계 인증을 껐습니다.」 — 화면은 「꺼짐」. 틀리면 켜기 창과 같은 칸 아래 글이다.
  • 비밀번호 화면(설정 › 비밀번호)
    • 2단계 인증을 켠 직원: 지금 양식 맨 아래에 「인증 앱 코드」 칸 + "코드는 30초마다 바뀝니다 — 다른 칸을 다 쓴 뒤 넣어 주세요." 머리 설명은 "웹메일에 로그인하는 비밀번호입니다. 2단계 인증이 켜져 있어 메일 프로그램은 앱 비밀번호로 연결합니다." 다른 탭에서 켜서 AUTH_007 이 오면 2단계 상태를 다시 받아 코드 칸을 보인다.
    • 모든 직원: 단추 아래 "바꾸면 이 브라우저를 뺀 다른 기기 · 브라우저의 웹메일 로그인이 끊깁니다." "앱 비밀번호도 모두 지워집니다 — 메일 프로그램은 새 앱 비밀번호로 다시 연결해 주세요."

설정 › 계정 › 앱 비밀번호 (SettingsAppPasswords)

  • 머리 "앱 비밀번호" + "Outlook · 썬더버드 · 휴대폰 메일 앱 같은 메일 프로그램에 넣는 비밀번호입니다. 프로그램마다 따로 만들어 두면, 기기를 잃었을 때 그것만 지울 수 있습니다." 오른쪽 "3 / 5개" + [앱 비밀번호 만들기].
  • 표(구현은 실제 table): 이름 · 만든 날 · 마지막 사용 · [지우기](36, 빨간 글). 최근에 만든 것이 위, 긴 이름은 말줄임. 마지막 사용은 "오늘 오전 9:12 · IMAP · 211.186.95.252" — 쓰지 않았으면 "아직 쓰지 않음"(회색, 오래 쓰지 않은 줄은 지워도 된다는 신호).
  • 메일 프로그램 설정값 상자: 받는 메일(IMAP) mx1.arasystem.it.kr · 포트 993 · SSL/TLS / 보내는 메일(SMTP) mx1.arasystem.it.kr · 포트 465 · SSL/TLS / 사용자 이름 = 메일 주소 전체 / 비밀번호 = 여기서 만든 앱 비밀번호 — 웹메일 비밀번호가 아닙니다. 호스트 · 포트는 화면 상수 하나에 둔다(전 직원 전환 때 다시 본다).
  • 아래 안내: "앱 비밀번호는 메일 읽기 · 보내기(IMAP · SMTP)에만 쓰입니다. 웹메일 로그인에는 쓸 수 없습니다." · "2단계 인증을 켜면 메일 프로그램은 앱 비밀번호로만 연결합니다. 켜지 않아도 만들어 쓸 수 있습니다." · "웹메일 비밀번호를 바꾸면 앱 비밀번호도 모두 지워집니다 — 메일 프로그램마다 새로 만들어 주세요." · "휴대폰이나 PC 를 잃어버렸으면 그 기기의 앱 비밀번호를 바로 지워 주세요."
  • 5개를 모두 만들었을 때: [앱 비밀번호 만들기]는 aria-disabled + 머리 아래 한 줄 "5개까지 만들 수 있습니다. 쓰지 않는 앱 비밀번호를 지운 뒤 만들어 주세요." — 누르면 창을 열지 않고 까닭을 읽어 준다(role=status).
  • 빈 상태: "0 / 5개" + "만든 앱 비밀번호가 없습니다" / "Outlook · 휴대폰 메일 앱에서 메일을 보려면 앱 비밀번호를 만들어 주세요. 2단계 인증을 켰다면 메일 프로그램은 앱 비밀번호로만 연결합니다." 설정값 상자 · 아래 안내는 그대로. 목록을 받지 못하면 지금의 목록 오류와 같은 모양(사전 문구 + [다시 시도]) — 그동안 [만들기]는 막는다(개수를 모르므로).
  • 만들기 창(400 · 처음 초점 이름 칸): 「이름」 + "어느 프로그램 · 기기에서 쓰는지 알아볼 이름입니다. 40자까지." → [만들기] [취소]. 칸 아래: "이름을 넣어 주세요." · "같은 이름의 앱 비밀번호가 있습니다."(앞뒤 공백을 지운 값으로 화면이 먼저 본다) · 그사이 다른 창에서 5개를 채웠으면 AUTH_009 사전 문구.
  • 만든 결과 창(620 · 처음 초점 [복사]): "앱 비밀번호를 만들었습니다" / "‘회사 PC 아웃룩’ — 메일 프로그램의 비밀번호 칸에 아래 값을 넣어 주세요."
    • 값은 고정폭으로 4자씩 떨어뜨려 보이지만 띄어쓰기는 없다 — 복사는 40자 그대로. "40자입니다. 보기 좋게 4자씩 띄웠지만 띄어쓰기는 없습니다."
    • 경고 "이 창을 닫으면 다시 볼 수 없습니다. 잊어버리면 지우고 새로 만들어 주세요." + 설정값 요약 + [닫기].
    • 복사 「앱 비밀번호를 복사했습니다.」 / 못 하면 「앱 비밀번호를 복사하지 못했습니다. 값을 직접 골라 복사해 주세요.」
    • [닫기] · Esc 로 닫으면 목록 맨 위에 새 줄이 생기고 값은 화면 메모리에서도 지운다. 복사하지 않고 닫아도 다시 묻지 않는다.
  • 지우기 확인(alertdialog 400 · 처음 초점 「취소」): "‘휴대폰 메일 앱’ 앱 비밀번호를 지울까요?" / "이 비밀번호로 연결한 메일 프로그램은 다음 연결부터 로그인하지 못합니다. 지운 비밀번호는 되돌릴 수 없습니다." 지우기 [취소] → 「‘휴대폰 메일 앱’ 앱 비밀번호를 지웠습니다.」 — 줄이 빠지고 개수가 준다(되돌리기 없음).

설정 › 계정 › 로그인 기록 (SettingsLoginHistory)

  • 머리 "로그인 기록" + "최근 90일 동안의 로그인입니다. 모르는 기록이 있으면 바로 비밀번호를 바꾸고, 2단계 인증이 꺼져 있으면 켜 주세요."(두 링크).
  • 로그인된 브라우저: 「로그인된 브라우저」 + "웹메일 3곳" · 오른쪽 다른 곳 모두 로그아웃.
    • 줄마다 PC · 휴대폰 아이콘(16) · 브라우저 · 운영체제 / IP / 마지막 활동 / 로그아웃. 맨 위는 지금 이 브라우저 — 단추 대신 「지금 이 브라우저」 글자만. 모르면 "알 수 없는 브라우저".
    • 아래 "웹메일 로그인만 보입니다. 메일 프로그램은 앱 비밀번호를 지워 끊습니다."(링크)
    • 줄의 [로그아웃]은 묻지 않고 바로 끊는다 → 「‘Edge · Windows’의 로그인을 끊었습니다.」(되돌리기 없음). 끊은 줄은 빠지고, 다른 곳이 없으면 [다른 곳 모두 로그아웃]을 그리지 않는다.
    • [다른 곳 모두 로그아웃] → 확인(alertdialog 400 · 처음 초점 「취소」) "다른 곳의 로그인을 모두 끊을까요?" / "이 브라우저를 뺀 2곳(Edge · Windows, Safari · iPhone)의 웹메일 로그인을 끊습니다. 끊긴 곳은 다음 동작에서 바로 로그인 화면으로 돌아갑니다." "메일 프로그램은 끊기지 않습니다 — 앱 비밀번호를 지워 끊어 주세요." [모두 로그아웃](청록 주 단추 — 데이터를 지우는 일이 아니다) [취소] → 「다른 곳의 로그인 2곳을 끊었습니다.」
    • 끊긴 브라우저는 다음 요청에서 바로 로그인 화면으로 간다(열린 실시간 알림도 30초 안에) — 계약 5절 "로그인된 브라우저".
  • 최근 로그인: 「최근 로그인」 + "최근 90일 · 웹메일과 메일 프로그램" · 오른쪽 전환 전체 · 실패만. 표: 시각 · 방식 · IP · 기기 · 결과.
    • 방식: "웹메일" / "메일 프로그램 · IMAP" · "메일 프로그램 · SMTP" + 앱 비밀번호로 들어왔으면 아래 줄 "앱 비밀번호 ‘회사 PC 아웃룩’"(지운 앱 비밀번호도 그 이름 그대로).
    • 기기: 웹메일은 "Chrome · Windows", 메일 프로그램은 "—". 나라 · 지역은 보이지 않는다.
    • 결과: 「성공」 · 「성공 · 2단계 인증」 · 「실패 — 비밀번호 틀림」 · 「실패 — 코드 틀림」 · 「실패 — 앱 비밀번호 필요」(2단계를 켠 계정에 메일 프로그램이 웹메일 비밀번호로 연결하려 함). 실패는 빨간 굵은 글자만 — 뱃지 없음.
    • 최근 순서 · 20개씩 더 보기. 「실패만」이면 "최근 90일 · 실패 4번", [더 보기]도 실패만 이어서.
    • 빈 상태(실패만): "최근 90일 동안 실패한 로그인이 없습니다" / "[전체]를 누르면 성공한 로그인도 봅니다." 기록은 이 기능을 배포한 날부터 쌓인다.
  • 목록을 받지 못하면 지금의 목록 오류와 같은 모양(사전 문구 + [다시 시도]).

처음 판에서 미룬 것

복구 코드, 이 브라우저 기억, 문자 · 메일 코드, 보안 키 · 패스키, 관리자가 2단계 인증을 강제하기(13단계), 켜진 채 휴대폰 바꾸기, 앱 비밀번호 이름 바꾸기 · 만료, 새 기기 로그인 알림 메일, 계정 변경 기록(비밀번호 바꾸기 · 2단계 켜기 등), 로그인 기록의 나라 · 지역.

구현하며 정한 보완 (2026-10-26 — 캔버스에는 아직 없다)

  • 찾지 못한 첨부(보관 시간이 지난 업로드·지워진 원본)는 실패로 표시하고, 전달 원본에는 "빼기"만 둔다(다시 올릴 수 없다).
  • 없는 메일함·메일(주소창에 남은 옛 id 등)에는 "다시 시도" 대신 "받은편지함으로"·"목록으로"를 둔다. "다시 시도"는 메일 서버 장애처럼 다시 해 보면 나아질 수 있는 오류에만 쓴다.
  • 읽기 화면의 본문 안내(2026-10-27 사용자 확정) — "외부 이미지를 차단했습니다"와 같은 모양(아이콘 alert-circle)의 안내 줄을 본문 위에 둔다. 순서는 생략 → 잘림 → 외부 이미지 차단이다. "표시할 수 없음"을 보일 때는 잘림 안내를 숨기고, 공백뿐인 글자 본문은 없는 것으로 본다:
    • 서버가 HTML 을 정화 한도 때문에 비웠을 때(isHtmlBodyOmitted): 글자 본문이 있으면 "본문이 복잡해 서식 없이 글자만 표시합니다.", 없으면 "본문이 너무 크거나 복잡해 표시할 수 없습니다."
    • 보여 주는 본문이 읽기 한도(2MB)에서 잘렸을 때(isBodyTruncated): "본문이 길어 앞부분만 표시합니다."

2단계 구현하며 정한 보완 (2026-10-27 — 캔버스에는 아직 없다)

  • 임시 저장
    • 쉬지 않고 쓰는 동안에도 처음 바꾼 때(또는 마지막 저장을 시작한 때)부터 30초가 지나면 저장한다 — 멈춤을 기다리다 오래 쓴 글을 잃지 않게.
    • 저장할 때마다 주소를 /mail/compose?draft=<새 id> 로 맞춘다 — 새로고침하거나 다시 열면 마지막 저장본을 이어 쓴다. 떠나기 전 저장은 주소를 바꾸지 않는다(가던 이동이 취소되므로). 대신 같은 탭에서 옛 ?draft= 로 돌아오면(뒤로 가기) 마지막 저장본을 연다 — 저장본이 바뀐 차례는 탭 메모리에만 두고 로그아웃 · 세션 만료 때 지운다.
    • 자동 저장 실패 가운데 메일 서버 장애·용량·요청 과다는 15초 → 30초 → 60초 뒤 저절로 다시 한다. 그 밖의 실패는 내용을 고치면 다시 저장한다.
    • 「임시 저장」은 아직 한 번도 저장하지 않았으면 바꾼 것이 없어도 저장한다 (빈 메일 · 미리 채운 답장 포함).
    • 형식이 틀린 주소 · 사전 검사에 걸린 첨부는 저장하지 않는다 (칸에는 남는다). 올리는 중인 첨부는 "저장하지 못한 내용"으로 친다.
    • 찾지 못한 첨부(MAIL_004)가 남아 있는 동안은 저장하지 않는다(「임시 저장」 · [다시 시도] 포함) — 첨부가 빠진 판으로 멀쩡한 저장본을 덮지 않게. 그 첨부를 빼면 다시 저장한다.
    • 본문이 한도를 넘으면 보내기처럼 인용을 줄여 저장한다(편집기 화면은 그대로). 인용을 다 줄여도 넘으면 저장하지 않고 보내기와 같은 안내를 본문 아래에 보이며, 내용을 고칠 때까지 다시 시도하지 않는다.
    • 다른 메일 프로그램이 만든 저장본에 우리가 실을 수 없는 첨부(허용 목록 밖 · 개수 · 합계 초과)가 있으면, 첫 저장은 원본을 지우지 않고 새 저장본으로 남긴다 — 원본은 첨부째 임시보관함에 그대로 있다.
    • 떠나기 전 저장은 5초까지만 기다린다. 끝나지 않으면 "저장하지 못한 내용이 있습니다"로 묻고, 묻는 사이에 저장이 끝나면 그대로 떠난다.
    • 보내는 중에 화면을 떠났는데 발송이 실패하면, 보내려던 내용을 임시 저장본으로 한 번 남긴다 (실패 알림은 그대로).
    • 알려진 한계: 떠나기 전 저장이 첫 저장이었던 새 메일 · 답장 · 전달은, 뒤로 가기로 돌아오면 새 양식이 열린다 (저장본은 임시보관함에 있다).
    • 휴지통 단추는 저장본을 휴지통 메일함으로 옮긴다(삭제 요청이 아니다 — 다른 창에서 먼저 휴지통으로 옮긴 것을 영구 삭제하지 않게). [되돌리기]로 되돌리면 "임시보관함으로 되돌렸습니다." 알림을 띄운다. 휴지통 메일함을 알 수 없으면 지우지 않고 실패로 알린다. 이미 없는 저장본이면 알림 없이 닫는다. 단추가 있는 알림은 10초 보인다. 알림은 마우스 · 초점이 그 위에 있는 동안 사라지지 않고, 놓으면 그 알림의 시간을 처음부터 다시 센다.
    • 이어 쓰는 저장본의 제목 줄: 답장 대상이 있으면 답장, 제목이 Fwd:·FW:·전달: 로 시작하면 전달, 그 밖은 새 메일.
  • 서명
    • 서명을 바꾸고 저장하지 않은 채 떠나면 묻는다 — 제목 "저장하지 않은 서명이 있습니다", 글 "이 화면을 나가면 바꾼 서명이 사라집니다.", [계속 고치기] [나가기].
    • 서명 그림 3장은 서명 칸에 있는 그림 수로 센다. 미리 보기의 답장 원문 자리는 예시 이름 대신 "(보낸 사람)" 같은 괄호 글이다.
    • 설정 화면의 미리 보기 전환 단추와 그림 크기 줄(작게 · 보통 · 원본 · 그림 빼기, 36px)은 목록의 보기 전환처럼 밀도 예외다 (캔버스 확정값).
  • 받는 사람 자동 완성: Tab 은 고른 추천을 넣고 그 칸에 머문다(고른 것이 없으면 원래 Tab). 화살표는 끝에서 처음으로 돌고, 마우스를 올린 줄이 골라진다. 초성 · 자모만 친 글자는 서버에 묻지 않는다(받아 둔 목록에서만 거른다).

3단계 구현하며 정한 보완 (2026-10-27 — 캔버스에는 아직 없다)

  • 읽기 화면의 삭제: 휴지통 밖의 메일은 휴지통으로 옮기고(알림 "메일 1통을 휴지통으로 옮겼습니다." + [되돌리기]), 휴지통의 메일은 단추가 「영구 삭제」가 되어 여러 통과 같은 확인 대화상자를 거친다 — 그사이 다른 창이 휴지통으로 옮긴 메일을 확인 없이 영구 삭제하지 않게 (웹 화면은 삭제 요청 DELETE 를 쓰지 않는다 — 계약 2절 "여러 통 한꺼번에").
  • 검색
    • 검색 화면의 탭 제목은 "메일 검색"이다 — "(3) 메일 검색 · ARA SYSTEM".
    • 낱말이 8개를 넘으면 찾지 않고 알린다(서버 한도와 같다). 같은 검색어로 다시 Enter 를 누르면 보이는 채로 다시 찾고 고른 것을 푼다.
    • 주소로 바로 들어와 검색을 시작한 곳을 모르면, 닫을 때 그 범위의 메일함으로 간다(범위가 전체면 받은편지함).
    • 검색 화면 안에서 다시 찾거나 범위를 바꾸면 방문 기록을 쌓지 않는다(메일함에서 시작한 첫 검색만 쌓인다).
    • 전체 메일함 결과에서 다른 메일함으로 옮긴 메일은 결과에 남기고 메일함 표시만 바꾼다(휴지통 · 스팸메일함으로 옮기면 뺀다).
  • 여러 통 처리
    • 목록 보기의 「선택 해제」는 폭 1100 미만에서도 이름을 남긴다(분할 보기의 목록 칸에서는 아이콘만).
    • 요청이 통째로 실패하면 고른 것을 그대로 둔다(다시 시도할 수 있게). 열어 둔 메일이 옮겨져 목록에서 빠지면 목록 주소로 바꾼다.
    • 영구 삭제 확인은 공용 대화상자 폭(360)을 쓴다.
    • 「되돌리기」는 이 화면이 알던 메일함 한 곳으로 되돌리고, 로그아웃 · 다른 계정 로그인 뒤에는 움직이지 않는다 (계약 2절 "알려진 한계").

4단계 구현하며 정한 보완 (2026-10-28 — 캔버스에는 아직 없다)

  • 설정: /settings 는 계정 › 비밀번호로 연다(앱 레일의 "설정").
  • 비밀번호
    • 8자 미만이거나 지금 비밀번호와 같으면 새 비밀번호 아래 안내 글이 그대로 빨간 오류로 바뀐다.
    • 서버의 거절은 칸 아래에만 보이고 알림은 띄우지 않는다 — 「지금 비밀번호가 올바르지 않습니다.」는 지금 비밀번호 칸, 엔진 규칙(VAL_001)은 새 비밀번호 칸.
    • 보기 단추는 이름을 바꾸지 않고 눌림 상태(aria-pressed)로 알린다(ARIA 권고).
    • 요청은 30초까지 기다린다(서버가 엔진을 두 번 부른다 — 계약 1절).
  • 부재중 자동 응답
    • 날짜는 하나만 골라도 된다 — 시작만이면 그날부터 끌 때까지, 끝만이면 지금부터 그날까지.
    • 꺼 둔 채 저장할 때 기간이 맞지 않으면(흐린 칸이라 고칠 수 없다) 날짜를 비워 저장한다 — 끄기는 늘 저장되게. 다시 열면 "끌 때까지 계속"이다.
    • 저장 글은 올해가 아니면 연도를 붙인다("2027년 1월 3일(일)"). 날짜가 하나면 "…부터 자동으로 답합니다." · "…까지 자동으로 답합니다."
    • 메일함 패널 안내: 끝나는 시각이 지나면 숨긴다. 아직 시작 전이면 "10월 30일 ~ 11월 3일 · 설정"(끝이 없으면 "10월 30일부터 · 설정"), 끝이 없으면 "끌 때까지 · 설정". 화면을 띄워 둔 채 시각이 지나도 1분 안에 다시 맞춘다.
    • 바꾸고 저장하지 않은 채 떠나면 묻는다 — 제목 "저장하지 않은 자동 응답이 있습니다", 글 "이 화면을 나가면 바꾼 내용이 사라집니다.", [계속 고치기] [나가기]. 저장하는 중에 떠났는데 서버가 거절하면 알림으로 알린다(저장 단추 옆 글이 보이지 않으므로).
  • 메일함 관리
    • 할 일이 없는 메일함(보낸편지함, 안 읽은 메일이 없는 임시보관함, 빈 휴지통 등)에는 ⋯ 를 그리지 않는다. 빈 휴지통 · 스팸메일함에는 비우기 항목도 목록 머리 단추도 없다.
    • 하위 메일함은 받은편지함 · 내 메일함 아래에만 만든다(계약은 보관함 아래도 받는다 — 다른 메일 프로그램이 만든 것을 그대로 보인다).
    • 대화상자는 공용 폭 360(캔버스는 400). "지울 수 없습니다"의 [확인]은 단추가 하나라 주 단추 모양이다(캔버스는 보조 단추 — 세션 만료 대화상자와 같게).
    • 이름을 바꾸지 않고 [바꾸기]를 누르면 요청 없이 닫는다. 이름에는 / 와 제어 문자(줄바꿈 등)를 쓸 수 없다.
    • 분할 보기의 목록 머리(384)에서는 「휴지통 비우기」 · 「스팸메일함 비우기」가 아이콘만이다(40×44 — 이름은 툴팁). 보기 전환 · 새로고침과 함께 이름까지 넣으면 넘친다(3단계 도구 줄과 같은 규칙).
    • ⋯ 메뉴는 보이는 영역 안에 다 들게 연다 — 패널 아래쪽 행에서는 위로 연다.
    • 보던 메일함을 지우면 받은편지함으로 간다. 비우기 · 지우기가 끝나면 초점은 메일함 패널의 지금 메일함(지웠으면 받은편지함)으로 간다 — 누른 단추가 사라지므로.
  • 오래 걸리는 처리
    • 하는 중의 글: "‘거래처’ 메일함을 지우는 중 · 12 / 40통" · "휴지통을 비우는 중 · 600 / 1,234통" (빈 메일함 지우기처럼 셀 메일이 없으면 수를 뺀다).
    • 도중에 실패하면 "1,234통 가운데 600통까지 처리했습니다 — 다시 시도해 주세요."로 바뀐다(까닭은 오류 알림으로 함께). 하나도 못 했으면 오류 알림만 남는다.
    • 다른 탭에서 먼저 끝냈으면 실패로 보이지 않는다 — "받은편지함에 안 읽은 메일이 없습니다." · "휴지통이 이미 비어 있습니다.", 이미 없는 메일함 지우기는 지운 것으로 본다.
    • 같은 메일함의 처리는 한 번에 하나다(다른 탭에서 같은 메일함을 처리하는 중이면 "요청이 너무 많습니다"). 진행 알림은 로그아웃 · 세션 만료 때 닫힌다.
    • 휴지통을 비우는 동안은 메일함을 지울 수 없고, 메일함을 지우는 동안은 휴지통을 비울 수 없다 — 비우기는 그동안 휴지통에 들어온 메일도 지우므로, 지우는 메일함의 메일이 휴지통에 들어가자마자 영구 삭제되지 않게(계약 2절 "메일함 관리").
    • 목록 머리 「비우기」의 확인은 연 그 메일함만 비운다 — 확인 창이 열린 채 다른 메일함으로 가면(뒤로 가기 등) 창을 닫는다.
    • 서버가 처리 수 0 을 세 번 연달아 주면 멈추고 실패로 알린다(같은 요청을 끝없이 보내지 않게).
  • 스팸: 「되돌리기」는 원래 메일함으로 옮기며 표시를 반대로 바꾼다 — 원래 아무 표시도 없던 메일은 "스팸 아님" 표시가 남는다(계약 2절 "스팸 · 원문").

5단계 구현하며 정한 보완 (2026-10-28 — 캔버스에는 아직 없다)

  • 알림을 막았을 때의 알림은 보드의 어두운 바탕 대신 기존 오류 알림 모양(흰 카드)을 쓴다. [설정 열기]는 브랜드 색.
  • "웹메일을 보고 있음" 은 같은 사용자로 로그인한 이 앱의 탭 가운데 보이고 초점이 있는 탭이 있다는 뜻이다 — 설정 화면을 보고 있어도 보내지 않는다. 두 번째 모니터에 웹메일을 띄워 두고 다른 창에서 일하면 보낸다(권하기 문구 "다른 창을 보는 동안에도"와 맞게).
  • 설정 › 알림
    • 허락은 했는데 알림을 꺼 둔 상태: 칸은 비우고 안내는 초록 "이 브라우저에서 알림이 허용되어 있습니다."(보드에 없는 상태).
    • 실시간을 쓸 수 없는 브라우저(Web Locks · BroadcastChannel · 스트림 읽기 가운데 하나라도 없음)는 바탕화면 알림도 "지원하지 않습니다"로 본다 — 알림은 실시간으로만 온다.
    • 연결 상태 글: 처음 잇는 중 "연결하는 중입니다…", 끊김 "연결할 수 없어 1분마다 확인합니다." 아래에 "저절로 다시 잇습니다. 회사 방화벽 · 프록시가 오래 열린 연결을 끊으면 이렇게 됩니다.", 지원하지 않음 "이 브라우저는 실시간 연결을 지원하지 않습니다."
    • 주소는 /settings/mail/notify, 탭 제목은 "메일 알림"(머리글은 "알림").
    • 켜기 칸에서 허락을 받은 결과는 칸 아래 안내로만 보인다(화면 알림은 목록 위 권하기 줄에서만 — 설정 화면에서 [설정 열기]는 어색하다).
    • 연결이 끊긴 동안은 "연결할 수 없어 1분마다 확인합니다." 아래에 "연결될 때까지 바탕화면 알림은 오지 않습니다."를 더한다.
    • 알림을 만들 수 없는 브라우저(휴대폰 Chrome 은 new Notification() 을 막는다)는 처음 실패할 때 "지원하지 않습니다"로 바꾼다.
    • 한국어 안내 글은 낱말 가운데서 줄을 바꾸지 않는다(word-break: keep-all, 긴 주소는 overflow-wrap: anywhere).
  • 목록 위 권하기 줄은 메일함 목록에만 둔다(검색 결과에는 두지 않는다). 줄이 사라지면 초점을 목록 칸으로 옮긴다. 분할 보기의 좁은 목록 칸(384)에서는 짧은 글 "바탕화면 알림으로 새 메일 받기"를 쓴다(긴 글은 툴팁) — 긴 글은 잘려 끝까지 읽을 수 없었다.
  • 바탕화면 알림
    • "외 N명"은 서버가 보낸 새 메일(최대 5통)에서 겹치지 않는 보낸 사람(주소 기준)으로 센다.
    • 시험 알림도 새 메일 알림과 같은 자리(tag)를 쓴다. 누르면 창만 앞으로 온다.
    • 알림을 눌렀을 때 받은편지함을 아직 모르면 메일함을 받아 본 뒤 열고, 그래도 모르면 메일 첫 화면으로 간다. 누르기 전에 로그아웃 · 다른 계정 로그인이 있었으면 이동하지 않는다.
    • 아이콘은 192px PNG(frontend/src/assets/brand/notification-icon.png — ARA 마크를 #0B3640 둥근 사각 가운데에).
    • 사용자가 웹메일로 돌아오면(어느 탭이든 보이고 초점이 생기면) 띄운 알림을 닫는다 — 알림 센터에 지난 알림이 남지 않게.
    • 허락을 묻는 동안 단추 · 칸은 눌러도 다시 묻지 않을 뿐 초점을 잃지 않는다(aria-disabled). 막힘 · 지원 안 함으로 칸이 꺼지면 초점을 안내 글로 옮긴다.
  • 탭 제목 "(N)" 은 지금처럼 메일 화면에서만 붙인다(메일 밖 화면에는 받은편지함 수의 기준이 없다). 메일 밖에 있는 동안 생긴 바뀜은 남겨 두었다가 메일 화면으로 돌아오면 맞춘다.
  • 실시간 반영 시간: 실시간이 부른 목록 맞추기는 1초 모아서 한 번 한다(한 통을 읽어도 바뀜이 오므로 — 연달은 바뀜에 목록을 여러 번 받지 않게). 그래서 새 메일은 "1초 안"이 아니라 2초 안팎에 목록에 나타난다.

발송 · 부재중 결함 수정 (6단계 앞, 2026-10-28 — 캔버스에는 없다)

  • 받는 사람 일부가 받지 못했을 때(없는 주소 · 받는 쪽 거절 · 지금은 받을 수 없음): 보낸 것으로 처리해 쓰기 화면을 닫고, "보냈습니다" 알림 대신 오류 알림 모양(흰 카드)에 "보냈지만 N명은 받지 못했습니다 — 주소 (까닭)"와 [보낸편지함 보기](보냈다는 알림과 같은 링크) — 두 명까지 주소 · 까닭을 적고 나머지는 " 외 N명". 까닭 문구 "없는 주소" · "받는 쪽이 거절" · "지금은 받을 수 없음".
  • 모두 받지 못했을 때: 보내지 않은 것이다 — 없는 주소 · 거절(MAIL_010)은 받는 사람 오류(MAIL_002)와 같은 안내 띠 · 같은 초점으로, 지금은 받을 수 없음(MAIL_011)은 오류 알림으로. 쓰던 메일은 그대로 둔다.
  • 부재중 자동 응답 길이: 제목 150자 · 내용 600자(입력칸 한도(maxlength)도 이 값, 켤 때 검사 — 꺼 둔 채로는 4단계 한도까지 저장된다) — 엔진이 바이트로 막아 한글로는 4단계의 200자 · 4,000자가 저장되지 않았다(계약 2절 "부재중 자동 응답").

6단계 구현하며 정한 보완 (2026-10-28 — 캔버스에는 아직 없다)

  • 되돌리기: 지우기 전 목록을 통째로 되돌리지 않고, 지운 필터만 지운 자리에 다시 넣어 저장한다 — 10초 안에 다른 필터를 켜거나 옮긴 것이 사라지지 않게(보드는 "지우기 직전의 목록 그대로"). 자리는 앞 · 뒤 필터로 기억한다(앞 필터 뒤 → 없으면 뒤 필터 앞 → 없으면 지운 때의 차례) — 여러 개를 어떤 차례로 되돌려도 원래 차례가 된다. 여러 알림의 [되돌리기]는 누른 차례대로, 돌고 있는 저장이 끝난 뒤에 보낸다. 다시 넣으면 50개를 넘을 때는 보내지 않고 50개 안내를 오류 알림으로. 필터 화면을 떠난 뒤에 실패하면(다른 곳에서 바뀜 포함) 알림으로 알린다.
  • 다른 곳에서 바뀜: 고치기 창 안(단추 줄 위)에도 목록 위 줄과 같은 문구와 [다시 불러오기](36 — 밀도 예외)를 둔다 — 창이 모달이라 목록 위 줄을 누를 수 없다. 창에서 다시 불러오면 창과 쓴 내용은 그대로이고 초점은 [저장]으로 간다. 다음 [저장]은 다시 받은 목록에 넣는다 — 같은 필터의 자리, 그사이 다른 곳에서 지워졌으면 맨 아래(그때 50개를 넘으면 창 안에 50개 안내). 안내는 다시 받기 전까지 남는다(칸을 고쳐도). 다시 받은 목록에 이미 없는 필터에서 [이 필터 지우기]를 누르면 보내지 않고 창을 닫는다. 목록 위 줄의 [다시 불러오기]는 보드대로 창이 닫혀 있을 때 쓴다.
  • 켜기 스위치: 이름은 "‘…’ 필터 사용" 하나 — 켜짐 · 꺼짐은 aria-checked 가 알린다(보드는 이름을 끄기 · 켜기로 바꿨다). 그림은 36×20, 누르는 곳은 40×44(밀도 예외).
  • 누를 수 없는 단추는 aria-disabled(누른 단추 · 창이 돌아올 단추가 초점을 잃지 않게): 맨 위 ▲ · 맨 아래 ▼, 저장 중의 스위치 · ▲▼, 창의 [저장] · [이 필터 지우기], 50개일 때의 [새 필터](까닭 글을 aria-describedby). 마우스를 올려도 모양이 바뀌지 않는다. 메뉴의 꺼진 항목도 모두 그렇다 — 편집기 메뉴 · 이동 · 메일함 ⋯ · 읽기 화면 ⋯ 에서 화살표로 가서 초점은 받되 고를 수 없다(WAI-ARIA 메뉴). 이름 아래 까닭 글은 aria-describedby.
  • 저장은 한 번에 하나: 저장이 도는 동안 누른 다른 행의 스위치 · ▲▼ 는 보내지 않는다(모양은 그대로, 커서만 바뀐다). 창으로 저장할 때 켜짐은 그 순간 목록의 값이다 — 창은 켜기를 고치지 않으므로, 창을 연 뒤 실패해 되돌려진 켜기가 창의 저장으로 살아나지 않게.
  • 앞뒤 공백: 이름 · 찾을 글 · 전달 주소는 앞뒤 공백을 빼고 보낸다(서버도 뺀 값으로 저장한다).
  • 문구(보드에 없던 것): "내 주소로는 전달할 수 없습니다." · "전달할 주소를 입력해 주세요." · "전달할 주소 형식이 올바르지 않습니다." (@ 앞은 RFC 5322 dot-atom · 64자 — 서버의 검사보다 좁게 봐서, 화면이 통과시킨 주소를 서버가 거절하지 않게). 길이 · 제어 문자 · 이름 오류는 사전의 VAL_001 문구. 이름 없는 필터의 이름(조건으로 만든 것)은 알림 · 단추 이름에서 50자로 자르고 "…"를 붙인다. 작은따옴표가 든 이름은 큰따옴표로 묶는다.
  • 읽기 화면 ⋯: 50개인지 알려고 메뉴를 열 때 필터를 알림 없이 받는다(받아 둔 것이 없거나 50개일 때). 결과가 오기 전에는 항목을 켜 둔다. 50개면 항목을 끄고 이름 아래에 까닭 한 줄. 보낸 사람 주소가 없는 메일에는 항목이 없다. ?from= 으로 왔는데 50개면 창을 열지 않고 주소의 쿼리만 지운다.
  • 화면 폭: 공용 설정 틀 그대로 최대 800 · 묶음 사이 22(보드는 880 · 18 — 다른 설정 화면과 맞춘다). 머리의 [새 필터]는 틀의 actions 자리. 안내 상자 700, 창 620.
  • 메일함: 받아 둔 메일함 목록을 보이며 뒤에서 다시 받는다(다른 탭에서 지웠을 수 있다). 요약에 메일함 이름이 들어가 필터와 메일함을 둘 다 받은 뒤 목록을 그린다. 옮길 곳 고르기는 받은편지함 → 내 메일함 → 보관함 → 스팸메일함 → 휴지통(보관함과 그 아래 — 보드에 없었다. 서버가 받고, mailcow 에서 옮겨 온 계정에 있다). 지워진 메일함은 "(지워진 메일함)…"이 꺼진 채 골라져 보이고, 그대로 저장해도 막지 않는다(서버가 받는다 — 받은편지함에 들어온다).
  • 초점: 조건 더하기 → 새 줄의 "어디에서", 조건 빼기 → 이웃 줄의 찾을 글, 필터 지우기 → 다음 행(마지막이면 앞 행), 목록 위 [다시 불러오기] · [다시 켜기] · ?from= 으로 연 창의 [취소] · 이미 없는 필터 지우기 → 머리의 [새 필터], 창 안 [다시 불러오기] → [저장].
  • 그 밖: [다시 켜기]가 성공해도 알림은 없다(줄이 사라진다). 목록 아래 줄은 보드 글 그대로 "… 행을 누르면 고칩니다."까지. 전달 칸은 type=email · 254자이고, 보기 글(받는 사람 칸 · 전달 칸)은 로그인한 직원 주소의 도메인으로 만든다. 저장하는 사이 화면을 떠나면 칸 안에 보일 거절을 알림으로 띄운다(부재중 화면과 같다). 목록에 aria-busy 를 걸지 않는다(칸마다 aria-disabled).
  • 하지 않은 것: 고치기 창에 쓰다 만 채로 떠날 때 묻기(나가기 확인) — 보드에 없다. 창을 연 채 브라우저 뒤로 가기를 하면 쓴 것을 잃는다(알려진 한계 — 7단계에서 고쳤다, 위 "7단계").

7단계 구현하며 정한 보완 (2026-10-29 — 캔버스에는 아직 없다)

  • 「…에도 적용」 줄 높이: 할 일 칸과 같은 44(보드는 40) — 고치기 창의 밀도 예외는 조건 칸 · 단추 40 뿐이라 새 예외를 만들지 않는다.
  • 고를 수 없는 경우를 넓혔다(보드는 전달만 · 적용 중 둘): 옮길 메일함이 지워짐 · 받은편지함으로 옮기기만 하는 필터도 이미 받은 메일에 할 일이 없다(서버도 옮기지 않는다). 까닭은 이 차례로 하나만 — 이 탭에서 적용 중 → 지워진 메일함 → 받은편지함으로 옮기기만 → 전달만. 할 일을 아직 하나도 고르지 않았으면 막지 않는다(저장할 때 칸 아래 "할 일을 하나 이상 골라 주세요."). 고를 수 없게 되면 골라 둔 것을 쓰지 않고, 다시 고를 수 있게 되면 돌아온다. 같은 판정으로 저장한 뒤에도 시작하지 않는다 — 그때는 "필터를 저장했습니다."
  • 꺼 둔 필터도 적용한다 — 사용자가 이번 저장에 고른 일이다. 적용 칸만 고르고 다른 칸을 고치지 않아도 바꾼 것으로 본다(나가기 확인 · [저장]).
  • 전체 수: 첫 응답 전에는 받은편지함의 메일 수(메일함을 아직 받지 않았으면 수를 뺀 문구), 그 뒤로는 "확인 + 남은 수"(최근 메일부터라 그사이 온 메일 · 옮겨진 메일로 조금 바뀔 수 있다).
  • 실패 알림: 요청은 인터셉터 알림을 끄고 보내고, 응답이 온 때 세션이 그대로일 때만 사전 문구로 알린다(세션 만료 대화상자는 그대로) — 그래서 처음 요청부터 실패하면 사전 문구 알림과 진행 알림의 결과 "필터는 저장했지만 …"가 함께 보인다(4단계 오래 걸리는 처리와 같다). 다른 창 · PC 에서 적용 중(429)은 결과 문구 하나만. 도중의 429 는 이어 할 자리가 있으니 [다시 시도]를 붙인다(처음 요청의 429 는 보드처럼 단추 없음).
  • 이 탭에서 적용 중에 [다시 시도]: 시작하지 않고 "다른 필터를 받은편지함에 적용하는 중입니다. 끝난 뒤 다시 시도해 주세요." + 같은 자리 · 같은 수의 [다시 시도].
  • 나아가지 못한 응답(확인 0 인데 이어 할 자리가 있다)이 세 번 이어지면 멈춘다(MAIL_FILTER_RUN_MAX_STALLS) — 같은 요청을 끝없이 보내지 않게.
  • 맞추기: 끝났거나 이번에 나아간 뒤 멈췄으면 개수 · 사용량을 다시 받고, 보이는 목록이 받은편지함이나 옮긴 메일함이면 그 목록도 맞춘다(실시간 연결이 없는 탭에서 1분까지 옛 개수가 보이지 않게).
  • 닫기 확인과 고치기 창: 브라우저가 Esc 를 거듭 누르면 고치기 창을 막지 못하고 스스로 닫는다 — [계속 고치기]면 창을 다시 띄우고 초점은 닫히기 전에 있던 칸으로 돌려준다. 닫기 확인이 떠 있는 동안 다른 화면으로 가려 하면(뒤로 가기) 같은 확인 창이 "이 화면을 나가면 …" [나가기]로 바뀐다. 저장 · 지우기를 보내는 중에는 묻지 않는다(결과는 알림으로).
  • 부재중: 메일함 패널 아래의 부재중 안내는 그대로 둔다(저장한 값으로 보인다 — 동작하지 않음은 설정 화면에서만 알린다).

8단계 구현하며 정한 보완 (2026-10-29 — 캔버스에는 아직 없다)

  • 받는 사람 추천: 주소록 줄의 설명은 주소 종류로 정한다 — 회사 주소는 회사 이름(없으면 "회사"), 개인 주소는 "개인", 그 밖은 "기타". 계약에 대표 표시가 없어 보드("대표 주소는 회사")와 두 경우에서 다르다 — 대표 주소가 개인이면 "· 개인", 대표가 아닌 회사 주소에는 회사 이름이 나온다. 구성원이 0명인 그룹은 추천하지 않는다. 초성 · 조합 중 글자도 받아 둔 것으로 모자라면 서버에 묻는다(서버가 같은 맞추기를 쓴다 — 계약 2절). 그룹을 고른 뒤 칸 아래 글(role=status)은 불러오는 중 "‘G’ 그룹의 구성원을 불러오는 중…" → "‘G’ 그룹의 N명을 받는 사람에 넣었습니다." / "‘G’ 그룹에서 받는 사람에 더 넣을 주소가 없습니다." + 뺀 것이 있으면 "주소가 없는 N명 · 이미 넣은 N명은 뺐습니다." — 칸에 다시 치면 지운다. 그룹을 이어 고르면 그룹마다 따로 받아 끝나는 대로 넣는다 — 칸 아래 글은 받는 중인 그룹이 있으면 마지막으로 고른 그룹의 "불러오는 중", 없으면 마지막에 끝난 그룹의 결과.
  • 쓰기 화면을 받는 사람을 채워 열기: 새 메일에만 쓰고(라우터 상태 composeTo — 연 직원의 주소를 함께 싣고, 지금 직원과 다르면 쓰지 않는다), 잘못된 주소는 버린다. 채운 받는 사람은 "바꾼 것"이 아니다 — 손대지 않으면 자동 저장 · 나가기 확인이 없다. 초점은 제목 칸으로. 새 탭으로 연 쓰기 링크(가운데 클릭)는 빈 쓰기 화면이다 — 라우터 상태는 새 탭으로 가지 않는다(한계).
  • 그룹: [그룹에 메일 쓰기]는 받는 동안 바쁜 표시(aria-busy)이고 거듭 누르면 무시한다. 받는 사이 다른 목록 · 화면으로 갔거나 세션이 바뀌면 쓰기 화면으로 가지 않는다. 주소가 있는 구성원이 없으면 오류 알림 "‘G’ 그룹에는 메일 주소가 있는 구성원이 없습니다." — 빈 그룹이면 목록 머리 단추와 그룹 ⋯ 의 같은 항목이 함께 꺼지고(aria-disabled) 까닭은 "아직 구성원이 없습니다.". 그룹이 100개면 [새 그룹]을 끄고 "그룹은 100개까지 만들 수 있습니다.". [되돌리기]는 서버가 기억한 원본으로 다시 만든다(계약 3절 "되돌리기").
  • 구성원 넣기 창: 서버가 이미 구성원인 사람을 빼고 준다(연락처 · 사내 둘 다, [더 보기]도 — excludeGroupId). 한 번에 100명까지 — 다 고르면 나머지 칸을 끄고 알린다(role=status). 전환은 눌림 단추 둘(role=group + aria-pressed — 보드의 radiogroup 대신, 새 .segmented--outline). 목록은 높이 18rem 안에서 넘기고 [더 보기]가 있다. 아바타는 없다(보드대로). 넣은 뒤 알림의 수는 서버가 넣었다고 한 수(addedCount)다 — 0명이면 "‘G’ 그룹에 새로 넣은 사람이 없습니다. 이미 구성원이거나 그룹에 넣을 수 없는 연락처입니다."(다른 프로그램이 uid 없이 만든 카드 — 계약 3절).
  • 연락처 창(추가 · 고치기):
    • [고치기]는 그 연락처를 새로 받아 연다(받는 동안 aria-busy, 거듭 누르면 무시, 받는 사이 다른 연락처를 골랐으면 열지 않는다 — 다른 탭 · 휴대폰에서 바꾼 것을 덮지 않게). 찾을 수 없으면(RES_001) 알림 없이 목록에서 빼고 상세가 "찾을 수 없음" + [목록으로]를, 그 밖의 실패는 알림만 띄운다(창을 열지 않는다).
    • 저장은 바꾼 칸만 보낸다 — 보낼 값으로 견주어 앞뒤 공백 · 빈 줄 · 제어 문자 · 그룹의 차례는 바꾼 것이 아니다. 바꾼 것이 없으면 보내지 않고 닫는다. 나가기 확인도 같은 기준이다.
    • 검사도 바꾼 칸만 — 손대지 않은 남의 값(21번째 주소 · "내선"이 든 번호 · 긴 메모 · 100자 넘는 한 줄 칸)은 그대로 둔다. 이메일 · 전화번호는 고치면 그 칸 전부를 본다. 보드에 없던 문구: "이메일은 20개까지 넣을 수 있습니다. 몇 개를 빼 주세요." · "전화번호는 20개까지 넣을 수 있습니다. 몇 개를 빼 주세요."([더하기] 아래 — 초점은 그 묶음의 첫 줄) · "100자까지 쓸 수 있습니다."(그 칸 아래) · "같은 주소를 두 번 넣었습니다." · 전화번호 형식 · "메모는 1,000자까지 쓸 수 있습니다."(고쳤을 때만).
    • 이메일은 서버보다 좁게 본다 — @ 앞은 dot-atom 64자, 도메인 칸마다 앞뒤 하이픈 없이 63자 · 끝 점 없음 · 마지막 칸은 글자 2개 이상이거나 xn--, 한글 도메인은 받는다, 254자(필터의 전달 주소와 같은 검사). 이메일 칸은 type="text" + inputmode="email" — 브라우저가 한글 도메인을 punycode 로 바꾸지 않게(받는 사람 칸과 같다).
    • 같은 주소 경고의 괄호는 그 연락처의 첫 그룹(없으면 회사)이다. 칸을 고치면 경고를 내리고, 채워 연 창(읽기 화면 · 사내 목록)은 열 때 바로 본다. 늦게 뜨는 안내는 늘 있는 role=status 영역 안에 뜬다.
    • 필수 안내("이름이나 이메일 가운데 하나는…")는 빨간 글로 바뀌고 이름 칸에 초점을 둔다. 서버의 형식 거절(VAL_001)은 단추 줄 위에 사전 문구로. 저장 중에 창을 닫았으면(또는 다시 열었으면) 창 안에 보일 거절도 알림으로 알린다(그룹 이름 창도 같다).
  • 보이지 않는 제어 문자는 거절하지 않고 고친다 — 한 줄 칸의 탭 · 제어 문자는 띄어쓰기로, 메모의 CR LF · CR 은 줄바꿈으로. 열 때 · 보낼 때 고치고, 고친 것만으로는 바꾼 것이 아니다(표에서 붙여 넣은 탭 · 다른 프로그램의 CR LF 는 사용자에게 보이지 않아 고칠 수 없기 때문).
  • 크기 · 제목: 그룹 체크 줄 · [더 보기] · [새 그룹]은 44(보드 40 — 밀도 예외를 새로 만들지 않는다). 패널의 그룹 ⋯ 는 마우스를 올리거나 초점이 갈 때 보인다(메일함 행과 같다). 제목 수준은 메일 화면과 같다 — 패널 묶음 이름은 글 + aria-labelledby, 목록 제목은 h1, 상세 이름은 h2.
  • 알림 문구(보드에 없던 것): "연락처를 되돌렸습니다." · "그룹을 되돌렸습니다." · "‘X’ 그룹을 만들었습니다." · "그룹 이름을 바꿨습니다." · "주소를 복사했습니다." / "주소를 복사하지 못했습니다." · 구성원 없는 그룹을 지웠으면 "‘X’ 그룹을 지웠습니다.". 서버가 원본을 기억하지 못한 지우기(표 없음)는 같은 문구를 [되돌리기] 없이 띄운다.
  • 지운 뒤: 보던 연락처를 지우면 그 목록으로(고른 것 없이, 초점은 목록), 보던 그룹을 지우면 전체 연락처로. 이미 지워진 것(RES_001)은 지운 것으로 본다. 주소 복사는 대표 주소만(주소가 없으면 항목을 끄고 까닭).
  • 목록 맞추기: 만들기 · 고치기 · 되돌리기는 응답을 서버의 차례(frontend compareContacts — backend contact-search.ts 와 짝)로 받은 쪽 안의 자리에 넣는다(받은 쪽 끝보다 뒤면 넣지 않고 수만 고친다). 응답으로 자리를 알 수 없을 때만 첫 쪽을 한 번 다시 받는다 — 찾은 목록에 새 연락처가 생겼을 때 · 구성원 넣기로 보는 그룹이 바뀌었을 때 · 사내 직원을 넣어 전체 목록에 새 연락처가 생겼을 때.
  • 찾기: 목록을 바꾸면 찾기를 푼다. 사내 목록은 칸 안내 · 빈 문구가 따로("사내 직원이 없습니다" · "공용 주소가 없습니다"). Enter 는 바로 찾고, Esc · ✕ 는 푼다. 글자와 자모가 섞인 조합 중 글은 보내지 않는다(초성만이면 보낸다). 찾는 글은 254자까지(서버와 같다).
  • 상세: 연락처를 찾을 수 없으면(RES_001) [목록으로], 그 밖의 실패는 [다시 시도]. 목록을 받지 못해도 고른 연락처는 따로 받는다. 주소로 연 사내 항목은 그 항목 하나를 받는다. [메일 쓰기]는 주소가 없으면 꺼지고(aria-disabled) 까닭 "메일 주소가 없는 연락처입니다.", 사내 [내 주소록에 추가]는 연락처가 3,000개면 꺼지고 3,000개 안내.
  • 읽기 화면 ⋯: 원문 아래에 구분선을 둔다(보드대로). 「주소록에서 보기」 아이콘은 책. 보낸 사람이 바뀌면(다른 메일) 다시 판단한다 — 254자 넘는 주소면 묻지 않고 "없음"으로. 연락처 추가 창은 메일 레이아웃에 둔다 — 메일을 바꾸거나 목록으로 돌아가도 쓴 그대로 남고, 메일 화면을 떠날 때만 창의 나가기 확인이 묻는다. 닫으면 ⋯ 가 아직 있을 때만 초점을 돌려준다. 채우는 주소 종류는 회사.
  • 창의 자리: 주소록의 연락처 · 그룹 · 구성원 창은 주소록 화면의 틀에 둔다 — 주소록 안에서 옮겨 다녀도 닫히지 않고, 주소록을 떠나면 나가기 확인을 묻고, 세션이 바뀌면 닫는다(메일 화면의 창도 같다).
  • 서버 쪽에서 생긴 한계(계약 3절): 엔진이 만든 뒤 uid 를 바꾸지 못하게 해서 다른 프로그램이 uid 없이 만든 카드는 그룹에 넣을 수 없다 — 고치기 창에서 그 저장은 창 안 사전 문구. 다른 프로그램이 넣은 값이 우리 규칙 밖이면 그 칸을 고칠 때만 그 값도 규칙에 맞춰야 한다(다른 칸은 그대로 고칠 수 있다).

9단계 구현하며 정한 보완 (2026-10-29 — 캔버스에는 아직 없다)

  • 외부 이미지: [이미지 보기]는 받는 동안 꺼진다(aria-disabled · aria-busy). 받지 못하면 인터셉터가 알리고 줄은 그대로다. 그 메일의 본문 칸만 바꾸고(받는 사이 다른 메일을 열었으면 버린다), 줄이 사라져 초점을 잃으면 본문 틀로 옮긴다. 본문 틀의 CSP 는 보일 때만 그림에 우리 출처의 중계 경로(/api/mail-images/)를 연다 — 경로까지 좁힌다(2026-10-29 리뷰). 중계 주소로 인정하는 모양은 /api/mail-images/<base64url 한 칸> 뿐이다(짝: backend TOKEN_PATTERN). 답장 · 전달 인용에는 중계 그림도 실리지 않는다.
  • 인쇄: 라우트 mail-print(앱 틀 없음 · 로그인 필요), 탭 제목은 메일 제목(인쇄 머리글 · PDF 이름으로 쓰인다). window.open 은 누름 처리 안에서 바로 부른다(팝업 차단을 피하려고).
    • Ctrl+P(⌘P)는 메일이 다 열려 있을 때만 가로챈다 — 본문 틀 안에서 눌러도 같고, 한글 조합 중 · 다른 수식키 · 불러오는 중이면 브라우저 인쇄 그대로다. 라틴 글자가 아닌 배열은 code === 'KeyP' 로도 잡는다.
    • 종이 210mm · 여백 16mm 15mm 는 @page 와 같은 값이다(둘을 함께 고친다). 본문 틀의 load(그림까지) · 글꼴 준비 · 한 프레임 뒤에 인쇄 창을 한 번만 띄운다 — 그사이 다시 받기 시작했으면 띄우지 않고, [인쇄]는 다 잰 뒤에만 눌린다. [닫기]로 닫히지 않는 탭(주소로 바로 들어온 탭)은 메일 화면으로 간다.
    • 첨부는 목록에 보이는 것만 "이름 (크기)", 날짜는 "2026년 10월 26일 (월) 오전 10:42". 받기 실패는 화면이 그린다(알림 없음). 문구: "(제목 없음)" · "(보낸 사람 없음)" · 받는 중 "메일을 불러오는 중".
  • 보내기 취소 알림: 마감은 설정한 초를 알면 "지금 + 그 초 − 1초", 모르면 min(sendAt, 지금 + 30초)이다(설정은 메일 화면에 들어올 때 받아 둔다 — 아는 값이 0 인데 서버가 붙잡았으면 sendAt). 이 PC 시계로 이미 지났으면 지금처럼 [보낸편지함 보기] 알림이다. 마감이 있는 알림은 마우스 · 초점으로 멈추지 않고, 알림이 넘쳐도 먼저 닫히지 않는다. 막대는 <progress>(aria-hidden)다.
    • 알림 단추는 한 번만 돈다(닫히는 전환 중에 두 번 눌러도 — takeAction). 같은 메일의 취소는 요청 하나로 묶는다. 초점이 든 알림이 닫히면 알림에 들어오기 전 요소로(없으면 본문 영역) 돌려준다.
    • 쓰기 화면으로 가지 못하면(그 화면의 나가기 확인에서 머묾 등) 그 자리에 두고 "보내기를 취소하고 임시보관함으로 옮겼습니다." + [고치기].
    • 일부 거절 문장은 붙잡은 발송이면 "받지 못합니다"(현재형)다. 예약에서 일부 거절: "예약했지만 N명은 받지 못합니다 — 주소 (까닭)" + [예약됨 보기].
  • [보내기 ▾]: ▾ 이름 "보내기 방법 더 보기", 메뉴 이름 "보내기 방법". 메뉴는 아래가 모자라면 위로 연다(usePopupPlacement — 메일함 ⋯ 과 같은 규칙). 「예약 발송…」은 [보내기]와 같은 사전 검사를 먼저 한다(걸리면 창을 열지 않고 그 칸으로).
  • 예약 창: 「오늘 오후」 6:00 은 5분 뒤 이후일 때만, 「다음 주 월요일」은 오늘 다음의 첫 월요일(오늘이 월요일이면 7일 뒤)이다. 예약할 수 없는 시각은 빼고, 같은 시각이 겹치면 앞의 것만 둔다. 직접 고르기는 오늘 ~ 7일 뒤 · 30분 단위, 처음 값은 내일 오전 9:00, 검사는 [예약]을 누를 때(role=alert). 시간대 이름을 모르면 "이 PC 의 시간으로 보냅니다 · 7일 안에서 고를 수 있습니다.". 창이 열린 동안 보낼 수 없게 되거나 세션이 바뀌면 닫는다. 서버가 sendAt 을 VAL_001 로 거절하면 창을 다시 열어 그 시각을 「직접 고르기」에 채우고 까닭(5분 · 7일 문구)을 보인다 — 이 화면의 검사로는 맞는 시각(PC 시계가 어긋남)이면 가까운 한도로 본다. 쓰기 화면을 이미 떠났으면 알림으로.
  • 「예약됨」: 수는 GET /scheduled-messages?limit=1 의 total 이다 — 메일 화면에 들어올 때 · 예약하거나 취소한 뒤 · 새 메일 확인이나 실시간 알림이 바뀜을 알릴 때 다시 묻는다. 예약됨을 보는 동안은 0통이어도 패널 줄을 남긴다(숫자 없이). 패널 줄은 보낸편지함(과 그 하위) 바로 아래이고 ⋯ 는 없다.
    • 목록: 분할 보기 2줄(받는 사람 · 시각 / 제목), 목록 보기 한 줄 세 칸(받는 사람 150 · 제목 · 보낼 시각 150). 고르는 칸 · 별 · 여러 통 처리 · 비우기 · 알림 권하기 줄은 없다. 빈 상태 "예약한 메일이 없습니다" / "쓰기 화면의 [보내기 ▾] › 예약 발송…으로 보낼 시각을 정할 수 있습니다.".
    • [고치기] · [예약 취소]는 하는 동안 두 단추를 함께 막고, 성공해 줄이 사라지면 초점을 옮긴다. [고치기] 뒤 "예약을 취소했습니다. 고친 뒤 다시 보내 주세요." — 쓰기 화면으로 가지 못하면 "예약을 취소하고 임시보관함으로 옮겼습니다." + [고치기]. [예약 취소] 뒤에는 삭제처럼 다음 메일(분할) · 목록(목록 보기)으로.
  • 엔진이 발송을 풀 때: 새 메일 확인 · 실시간 알림의 상태는 바뀌지 않으므로(계약 2절) 보이는 목록 · 열린 메일 · 가장 이른 예약의 시각 + 2초에 셋을 알림 없이 다시 받고, 시각이 지난 채 남으면 5초마다 3번 더 본다(usePendingSendRefresh). 검색 결과는 기다리는 메일이 보일 때만, 열린 메일은 pendingSend 만 고친다. MAIL_014 를 받아도 바로 같은 셋을 다시 받는다.
  • 붙잡힌 메일(보내는 중 · 예약) — 취소로만 보낸편지함을 떠난다(2026-10-29 리뷰, 계약 2절 MAIL_015):
    • 목록(분할 · 표 · 검색)의 고르기 칸은 꺼지고(aria-disabled) title 이 "보내기를 기다리는 메일은 고를 수 없습니다 — 열어서 취소하거나 삭제해 주세요." — 하나 · 범위 · 모두 고르기가 건너뛰고 여러 통 처리에도 들지 않는다(고른 뒤 붙잡히면 고른 것에서 뺀다). 보낸편지함 표의 날짜 칸은 title 에도 같은 글.
    • 읽기 화면은 두 종류 모두 스팸 신고 · 이동 · ⋯ 의 차단 항목이 없다. [삭제]는 확인 "보내지 않은 메일을 삭제할까요?" / "보내지 않고 휴지통으로 옮깁니다." [삭제] [취소](처음 초점 「취소」) → 발송 취소 → 휴지통 → "보내지 않고 휴지통으로 옮겼습니다.". 창을 연 채 발송이 풀리면 창을 닫는다(보낸 메일의 영구 삭제 확인으로 바뀌지 않게).
    • 서버도 막는다(MAIL_015 — 화면이 막지 못한 때만 보인다): 여러 통 처리는 그 메일만 남기고(failedIds · skippedIds — 지금처럼), 메일함 지우기 · 비우기는 붙잡힌 메일만 남으면 진행 알림이 멈추고 인터셉터 알림 "보내기를 기다리는 메일은 옮기거나 지울 수 없습니다. 먼저 보내기를 취소해 주세요."(IMAP 으로 옮긴 메일에서만 생긴다 — 4단계 처리의 실패와 같은 모양).
  • 기다린 뒤 다음 메일로: 삭제 · 스팸 · 차단 · 예약 취소처럼 응답을 기다린 뒤 옮기는 일은 그사이에도 같은 목록 · 같은 메일을 보고 있을 때만 한다(3 · 4단계의 단추도 함께 고쳤다 — 2026-10-29 리뷰).
  • 차단:
    • 항목이 없는 때: 보낸 사람이 없을 때 · 내 주소 · 사내 도메인(그 하위 포함). 차단 목록은 메일 화면에 들어올 때 알림 없이 받고, 못 받았으면 ⋯ 를 열 때 다시 받는다(모르는 동안은 「보낸 사람 차단」). 500개면 항목을 끄고(aria-disabled) "차단한 주소는 500개까지 둘 수 있습니다. 쓰지 않는 차단을 풀어 주세요.".
    • 창: 고르기 묶음 이름 "차단할 범위", 도메인을 고를 수 없는 까닭 "여러 사람이 쓰는 메일 서비스라 도메인으로 막을 수 없습니다.". 「이 메일도 스팸메일함으로 옮기기」는 이미 스팸메일함이면 숨긴다.
    • 옮겼으면 삭제처럼 다음으로 간다. [되돌리기]는 차단을 풀고 원래 메일함으로 되돌린다(보드의 "받은편지함" 대신 — 받은편지함 밖에서 막았을 수 있다). 도메인으로 막힌 보낸 사람의 「차단 풀기」는 그 도메인 차단을 푼다. 풀기가 404 면(이미 풀림) 알림 없이 푼 것으로 본다. 차단 · 풀기 뒤 세션이 바뀌었으면 옮기기 · 알림을 하지 않는다.
    • 조사는 objectParticle 로 — 영문 주소는 "을(를)", 따옴표 뒤에 붙여 쓴다(보드의 "‘주소’ 을" 과 다르다). 문구: 옮기지 않은 차단 "‘주소’을(를) 차단했습니다." · 되돌리기 뒤 "‘주소’ 차단을 풀고 메일을 되돌렸습니다."(옮기지 않았던 차단이면 "‘주소’ 차단을 풀었습니다.") · 풀기의 되돌리기 뒤 "‘주소’을(를) 다시 차단했습니다.".
  • 설정 › 보내기 · 읽기: 머리 설명 "고르는 대로 저장합니다. 이 설정은 어느 PC 에서나 같습니다.", "저장했습니다"는 머리 오른쪽(SettingsPageFrame 의 title-aside 슬롯). 칸마다 저장은 한 번에 하나 — 보내는 동안 고른 값은 기억했다가 앞 저장이 끝난 뒤 보낸 값 · 서버의 답과 다르면 보낸다. "저장했습니다"는 다 끝나고 마지막이 성공했을 때. 끝내 실패하면 서버가 확인한 값으로 되돌린다.
  • 설정 › 차단한 주소: 검사 차례는 형식 → 내 주소 · 사내 도메인 → 메일 서비스 도메인 → 이미 차단 → 500개. 서버 거절(VAL_001 · MAIL_013)은 칸 아래 사전 문구(알림 없음). 성공하면 칸을 비우고 칸 아래에 "‘…’을(를) 차단했습니다."(role=status — 비어 있어도 영역은 늘 둔다). Enter 는 [차단]과 같다(isImeComposing). 줄을 풀면 초점은 다음 줄 → 앞 줄 → 칸. 엔진 규칙이 꺼져 있으면(isActive: false) 머리 아래 노란 줄(단추 없음) "지금은 차단이 동작하지 않습니다. 메일 서버의 규칙이 다른 곳에서 바뀌었습니다 — 주소를 차단하거나 풀면 다시 켜집니다." — 차단 · 풀기가 성공하면 켜진 것으로 본다(서버가 다시 켠다 — 계약 2절).
  • 새 토큰 · 아이콘: --color-on-brand-track · --color-on-brand-progress · --color-on-brand-divider · --color-warning-border, 아이콘 printer · clock · block(원 + 빗금).

11단계 구현하며 정한 보완 (2026-10-30 — 캔버스에는 아직 없다)

데이터 · 값은 계약 4절 "올리기 › 클라이언트"의 "구현하며 정함"이 같은 내용이다.

  • 올리기 차례: 고르자마자 서버에 올리기를 모두 만든다(임시 저장본에 바로 실려 나가기 확인 글이 그대로 맞다). 조각은 파일 3개까지 함께 보내고, 나머지 줄은 오른쪽에 "차례를 기다리는 중"(처음 몇 초의 "시간 계산 중" 자리).
  • 끝내기: 조각을 다 보냈으면 끝내기를 기다리는 동안 오른쪽 글은 "마무리하는 중"이다. 끝내기가 오래 진행 중이면 2 · 5 · 15 · 30초 간격으로 다시 묻고, 1분 넘게 그대로면 "오래 끊김"처럼 멈추고 [다시 시도].
  • 서버가 올리기를 잊었을 때(404): 멈춤 + "서버에서 올리던 파일을 찾을 수 없습니다 — 다시 시도하면 처음부터 올립니다." — [다시 시도]를 누를 때만 처음부터 올린다(사전 RES_001 문구로는 [다시 시도]의 뜻을 알 수 없어 화면 글로 둔다). 고른 파일이 없는 줄(저장본에서 이어 온 것)이나 보내기의 MAIL_016 뒤 다시 물어 404 면 "서버에서 찾을 수 없는 파일입니다 — 빼고 다시 올려 주세요."
  • 서버 거절 문구: 서버가 준 코드(FILE_001 · FILE_002 · FILE_005 · FILE_006 · VAL_001 · FILE_003)는 사전 문구다. 위 "올리기 전 검사"의 화면 글은 서버에 묻기 전(크기 · 개수 · 보관량)에만 쓴다 — "지금은 서버에 공간이 모자라 …"는 쓰지 않고 FILE_006 사전 문구.
  • 「링크로 보내기」: 대용량 사전 검사(개수 · 보관량)를 먼저 보고 통과할 때만 일반 첨부에서 뺀다. 걸리면 첨부는 그대로 두고 그 글을 오류 알림으로(보관량이면 + [대용량 첨부 보기]).
  • 다시 고르기: 이름 · 크기 · 고친 시각이 다르면 줄 안에 알리고 기다린다(보드). 같은데 맨 앞 조각의 SHA-256 이 다르거나 서버가 맨 앞 조각을 아직 못 받았으면 앞 올리기를 지우고 새로 올린다(계약). crypto.subtle 이 없는 곳(https · localhost 밖)에서는 지문 없이 보내고 다시 고르기는 늘 새 올리기다 — 운영은 https 라 해당 없다.
  • 인용 경계: 다시 연 답장 · 전달 저장본(보내기 취소로 되돌린 초안 포함)은 "맨 위 인용의 첫 자식이 ‘원본 메일’ 머리 문단 + 머리 표"로 원문 인용을 알아본다 — 그래야 덩이가 인용 위에 들어가고, 저장본에서 돌지 않던 인용 줄이기도 함께 돈다(2026-10-30 리뷰).
  • 검사 상태 다시 묻기: 쓰기 화면 · 읽기 화면은 검사 중인 파일이 있는 동안 15 · 30 · 60초 간격으로 최대 30번, 내려받기 화면은 20번. 읽기 화면은 막힌 [다운로드]를 누르면 한 번 바로 묻고, 메일을 바꾸거나 닫거나 세션이 바뀌면 멈춘다.
  • 보내기 직전 인용 줄이기: 서버가 넣을 덩이 몫(파일 수 · 이름 길이로 어림)을 더해 잰다.
  • 설정 › 대용량 첨부: 보낸 링크 머리의 메일 제목이 없으면(지웠거나 읽지 못함 — subject: null) 글자만, 빈 제목은 메일이 있는 것이라 링크다. [지우기]가 FILE_003(그사이 보내져 링크에 실림)이면 목록 · 사용량을 다시 받고 사전 문구로 알린다.
  • 내려받기 화면: 보낸 사람 이름이 비어 있으면 주소를 쓴다. 불러오는 동안은 role="status" "불러오는 중"(자리 막대는 aria-hidden).