- 계약 2절: 서식 본문(bodyHtml)의 허용 목록·값 형식·한도(VAL_001)·지문, 읽기 한도와 정화 전 검사(htmlBody null), 답장·전달 초안 길이 맞춤, 발송 JSON 4MB, 서식 본문 지문의 알려진 한계 - 디자인: 쓰기 화면 입력칸 포커스 규칙, 편집기 툴바·메뉴·단축키·좁은 폭 순서·팔레트, 원문 인용, 본문 이미지 → 첨부 (캔버스 Compose·ComposeEditor 보드와 구현하며 정한 것) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
웹메일 디자인 원본 (1단계)
확정된 디자인 캔버스의 원본이다. 화면 구현의 기준으로 쓴다.
- 캔버스: https://claude.ai/artifact/WJvVsKvXepxn3p75CGJAr2 (확정본: 2026-10-27, 버전 14)
- 화면:
Login(로그인) ·Main(분할 보기: 메일함·목록·본문) ·MainList(목록 보기: 게시판형 표) ·MainListRead(목록 보기에서 연 메일) ·Compose(메일 쓰기 — 서식 편집기) ·ComposeEditor(메일 쓰기 · 편집기 상태: 포커스·툴바·메뉴·팝오버·인용·이미지 첨부) ·States(오류·빈 상태·피드백 모음) - 파일 형식은 캔버스 전용(
<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 구현하며 정함).
-
좁은 폭: 툴바는 줄을 바꾸지 않는다. 묶음째 아래 순서로 더보기에 옮기고, 숨은 묶음은 더보기 맨 위에 툴바 순서대로 들어간다. 더보기 단추에 숫자를 달지 않는다.
- 실행 취소 · 다시 실행 — 쓰기 칸 734px 미만 (Ctrl+Z · Ctrl+Y 는 그대로)
- 글꼴 · 글자 크기 — 639px 미만
- 글자 색 · 배경 색 — 460px 미만
- 링크 — 365px 미만 (주소를 붙여 넣으면 저절로 링크가 된다)
- 글머리 · 번호 목록 — 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 구현하며 정함).
-
링크 팝오버(폭 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 구현하며 정함).
본문 이미지 → 첨부 파일
- 본문에는 이미지를 넣지 않는다(사용자 확정). 툴바에 이미지 버튼이 없다.
- 본문에 붙여 넣거나(Ctrl+V) 끌어다 놓은 이미지는 첨부 파일로 올린다. 끌어다 놓은 다른 파일도 첨부로 올린다(쓰기 화면 어디에 놓아도 같다).
한도는 첨부와 같다 — 넘치면 첨부 목록에 실패로 보인다(
States의 "첨부 업로드").- 클립보드에 글이 함께 있으면 글을 붙여 넣는다 — 워드 · 엑셀에서 복사하면 고른 부분의 그림이 글과 함께 따라오기 때문이다. 글 없이 파일만 있을 때 첨부로 돌린다 (2026-10-27 구현하며 정함 — 캔버스 시안은 그림이 있으면 늘 첨부로 돌린다).
- 이름 없이 붙여 넣은 그림은 "붙여넣은 이미지 N.확장자"로 올린다. 원래 이름이 있는 파일은 그 이름 그대로다.
- 끌어다 놓는 동안 본문 위에 점선 덮개(2px #0E7C86 점선 · 바탕 #F3F8F8)와 "놓으면 첨부 파일로 올립니다"를 보인다. 쓰기 화면이 떠 있으면 파일은 창 어디(앱 레일 위 포함)에 놓아도 첨부로 받고, 덮개도 파일이 창 위에 있는 동안 보인다 — 받지 않는 곳에 놓으면 브라우저가 그 파일을 열어 쓰던 메일을 잃기 때문이다 (2026-10-27 구현하며 정함).
- 이미지를 첨부로 돌렸을 때만 성공 알림 "이미지는 본문 대신 첨부 파일로 올렸습니다."를 띄운다(성공 알림과 같은 모양 · 5초).
구현하며 정한 보완 (2026-10-26 — 캔버스에는 아직 없다)
- 찾지 못한 첨부(보관 시간이 지난 업로드·지워진 원본)는 실패로 표시하고, 전달 원본에는 "빼기"만 둔다(다시 올릴 수 없다).
- 없는 메일함·메일(주소창에 남은 옛 id 등)에는 "다시 시도" 대신 "받은편지함으로"·"목록으로"를 둔다. "다시 시도"는 메일 서버 장애처럼 다시 해 보면 나아질 수 있는 오류에만 쓴다.