Skip to content

HBEA300_메신저 ​

(Hereby) 직원 앱 > 메신저

앱직원 앱 Hereby Works (포트 4002)
대상 사용자조건1 [EMPLOYEE] (직원 인증 후 전원)
프로토타입TBU
관련 정책[[HBAD280_공지]] 게시 소비
Statusreview
Owner카나

개요 ​

직원 앱 메신저 탭. 서브탭 3종: "대화" / "연락처" / "그룹 대화". 공지는 별도 서브탭이 아니라 "대화" 리스트 상단 "공지 n" 접이식 섹션으로 노출된다(구 "공지" 서브탭 대체). 대화 원천은 conversations / messages / conversation_members. "대화" 상단 "대화 검색..." 입력으로 대화를 검색한다. 공지는 조직 게시([[HBAD280_공지]] 게시 소비)를 수신 표시하며, 공지 상세에서 "확인했어요" 실행 시 "확인 완료 {상대시간}"으로 전환되고 GA/Admin 공지 "확인 내역"에 write-back된다. 메신저 탭에 AI 표면 없음 = greenfield — ESS AI는 신규 설계 영역이며 현재 챗봇/어시스턴트 핀 미존재.

문서 히스토리 ​

날짜작성자팀변경 내용
2026-07-06카나PM최초 작성
2026-07-10카나PM라이브 기능 검토 반영 (서브탭 개편·공지 섹션 이동)
2026-07-13카나PM버튼 행동 명세 보강
2026-07-21카나PMGC700 깊이 재작성 (공지 확인 cross-app write-back 반영)
2026-07-21카나PM셀 스타일 확정판 적용 (콸 승인)
2026-09-17카나PM미확인 채팅 탭 알림(제목·파비콘)·채팅 FAB 통통·공지 더보기 토글 미확인 카운트 반영(#767, 5912818·b0378d1·5ec7856) — 채팅 위젯·직원앱 메신저 공용 마크업
2026-09-18카나PM연락처 팀별 그룹핑(팀 머리 체크박스·「전체 선택」·미배정 묶음·대표 소속 기준 보조 설명) + DS 체크박스 「일부 선택」(indeterminate) 신설 반영(8319cec, #792) — 커밋 근거, 라이브 실사 이월(TBU)
2026-09-25카나PM낮 스윕 — 관리자 패널 공용 메신저 위젯에서 팀별 그룹핑·팀 머리 체크박스·「전체 선택」·「미배정」 묶음 실측(그룹 대화 모드 관찰만, 대화 미생성). 직원앱(Works) 자체 화면 실사는 계속 이월
2026-10-07카나PMPR #1061 반영(MED-903·908): 「나와의 대화」 self chat(ㄴ34 — 고정 줄 없음·아바타 → 내 프로필 시트 진입·fromMe 검색)·1:1 방 되살리기(ㄴ35) · PR #1035 반영(0d780c29·2f03c1e8): 연락처 프로필 선행 화면(ㄴ33, 전화 공개 settings.contactSharing.phone 기본 꺼짐)·여러 줄 입력줄·캡슐 보내기 숨김(ㄴ36) — 커밋 근거, 라이브 실사 이월(TBU)
2026-10-08카나PM10/7 머지분 반영 — PR #1071(MED-941) 내 메시지 삭제·자리 표시·첨부 내려받기 거부(ㄴ37·ㄴ38) · PR #1075(MED-944) 답글·인용 박스·원문 작성자 알림·보낸 사람 이름 성+이름(ㄴ39, 9374718b 입력칸 초점 수정 포함) · PR #1076(MED-943) @멘션 후보·멘션 링크·나 지목 줄 강조·언급 알림(ㄴ40) · PR #1077(MED-942) 단체방 알림 끄기·목록 종 꺼짐 아이콘(ㄴ41) · PR #1083 앱 셸 연동(ㄴ42: 스크롤 시 헤더 날짜 줄 접힘·목록 유리 뒤 통과·앱 모드 메신저 자체 스와이프 끔) · PR #1090/#1093 화면 캡처 제한 대상(ㄴ43). Data I/O 갱신. ⚠️ E2EE·대화 잠금(#1095-#1108·#1112)·권한 미리보기 세션 메신저 차단(4c578a68)은 미머지 — 미반영. 커밋 근거, 라이브 실사 이월(TBU)
2026-10-08카나PM낮 스윕 라이브 실사(웹 /employee-app·test group 단체방) — 메시지 메뉴(ㄴ37 "답글/삭제")·답글 바(ㄴ39)·@멘션 후보 팝업(ㄴ40 "전체 — 방 참여자 전원에게 알림")·알림 끄기 줄 원문(ㄴ41) 실측(전송·삭제·토글은 미실행). 삭제 확인 모달·멘션 말풍선·꺼짐 상태 UI는 TBU 유지
2026-10-09카나PMPR #1120 방 내 메시지 검색(ㄴ44, 라이브 실사) · E2EE 스택 develop 합류 반영(ㄴ45 — #1095~#1114, 암호화 조직 한정 Rule, 실사 대기) · 야간 실사 재확인: 메시지 메뉴 「답글/삭제」·@멘션 「전체」·알림 끄기 원문(ㄴ37·39·40·41 유지). ⚠ 미머지 #1124(재연결 리싱크) 미반영
2026-10-10카나PM(10/9 머지분) PR #1124 반영: 복귀·재연결 시 밀린 메시지 재동기화·PING/PONG·새 1:1 방 단일 경로·나간 1:1 상대 되살리기(ㄴ46 신설 — 동작성, 화면 변화 없음) · PR #1134/#1135 반영: 대화 잠금 모달 잘림·IndexedDB 프록시 저장 수정(ㄴ47 신설) — 커밋·PR 본문 근거
2026-10-13카나PM(10/10~12 머지분) 연락처 재편: 팀 필터·접힌 시작·트리 순서(30 보강, #1139 — 10/13 부분 실증) · 읽음 표시(ㄴ48, #1140) · 복구 코드 이미지 저장(ㄴ49, #1151) · 암호화 목록 미리보기·1:1 정리(ㄴ50, #1177~#1190) · 본문 주소 링크화(ㄴ51, #1155) · E2EE 안정화 묶음 비고(ㄴ45 보강, #1153~#1189) · 대화 잠금 설정 게이트 모달 10/13 실측(ㄴ47 비고) — 메신저 내부 실사는 E2EE 키 게이트로 불가(키 생성 보류), 원문은 커밋·게이트 모달만 채록
2026-10-14카나PM(10/13 머지분) 텔레그램식 메시지 묶음·보낸 사람 아바타·아바타 메뉴(ㄴ52, #1220 — 프로덕션 배포) · 목록 프로필 사진 일괄 캐시(ㄴ53, #1238) · 보고 있는 방 푸시 생략·방 바로가기 메뉴(ㄴ54, f4c68fd — 프로드) · 말풍선 줄바꿈 ds-bubble--plain·라이트박스 뒤로·폰 Enter=줄바꿈 번복(ㄴ55, #1219·0f5bfac) · 「대화의 처음이에요」 구분선(ㄴ56, ffc824c) · 기기 캐시 첨부/메시지 델타 동기화·epoch 봉투 수정(ㄴ57, #1227·#1218) — 커밋 근거, 메신저 내부 실사는 E2EE 키 게이트 지속으로 전부 이월(콸 키 생성 판단 대기) - 카나/PM
2026-10-15카나PM(10/14 머지분) 키 상태 띠 sticky 고정·재키 멤버 epoch 자동 회전(ㄴ58 신설, #1242·#1244 — 앱 QA 10/14 후속) · 안전 번호 모달 안내문 보강(ㄴ45 비고, 21d955c) — 커밋 근거, E2EE 키 게이트 지속으로 실사 이월
2026-10-16카나PM(10/15 머지분, #1249 556cf9e) 복구 코드 폐지·상대 키 변경 자동 수용(ㄴ59 신설) — 설정 = 「키 만들기」 한 번, 복구 코드·PNG 저장·안전 번호 모달·키 변경 띠·멤버 배지 제거. ㄴ49 폐지 표기, ㄴ45·ㄴ58 소급 갱신. 커밋 근거, E2EE 키 게이트로 실사 이월
2026-10-20카나PM(10/19 머지분, #1251 5047f5b d78152f) 키 자동 생성·봉투 실시간 전달(ㄴ60 신설) — 키 없으면 묻지 않고 자동 생성(게이트 모달 폐지, 「대화 잠금 키를 만드는 중이에요…」 띠), 전송 전 봉투 채움으로 실시간 「읽을 수 없음」 결함 해소. ㄴ45 게이트 모달 비고 소급. 가이드(#1254 머지, docs/ko messaging.md)와 ㄴ59 교차 점검 — 「첫 설정 = 키 만들기·복구 코드 절차 폐지」 서술 일치 확인. 커밋 근거 — 자동 생성으로 메신저 진입 자체가 키 생성(계정 상태 변경)이 됨 → 실사는 콸 판단 대기 지속
2026-10-22카나PM(10/21 머지분 — 전부 packages/components·api 경유 = 간접 보고, 사양 행 반영 대기 지속) ① #1289(feat/messenger-reactions-followups) 반응 후속 머지: 체크마크 반응 추가·모바일 버블 텍스트 선택 금지(d8f38924)·선택 복사 모달 + 메시지 메뉴 위 분리 반응 알약(e4b81f6a — 미머지였던 ac2926c 재구현, 구 브랜치 stale)·반응 수신 알림(작성자에게 푸시, d76f316e)·반응 알림을 일반 채팅 푸시와 동급 처리(방 음소거 존중·알림함 행 미생성, 5ebeafc3) ② #1293(daaa687c) 방별 작성 초안 유지: 기기 로컬에 방마다 입력 텍스트·답장 대상·대기 파일 보존 ③ #1302 수신확인 처리 perf(간접). 실사는 진입=키 생성(콸 판단 대기)으로 계속 보류 — 10/20 적체분과 함께 사양 행 일괄 반영 예정
2026-10-21카나PM(10/20 머지분 — 전부 packages/components 공유 컴포넌트 경유 = 간접 보고, 사양 행 반영 대기) 용구 메신저 대량 머지 8건: #1286(97a1a57 외) 이모지 반응 6종(👍❤️😂😮😢🙏)·모바일 더블탭 기본 반응·메시지 메뉴 재편(빠른 반응 줄 + 답글/복사/보내기(전달)/고정/삭제)·고정 메시지 헤더 띠·반응 알림(cdb8bf2)·체크 반응 추가(0287d7f) · #1278 스택: 이미지 뷰어(핀치/더블탭 줌·좌우 스와이프·암호화 첨부 복호 다운로드, e3716ce)·백그라운드 업로드(즉시 말풍선+진행률·취소·다시 보내기, 554a7c9)·첨부 용량 제한+기기 인코딩(08246e2)·사진 즉시 전송(845e6d0) · #1281 푸시 대화별 묶음(Android MessagingStyle) · E2EE 신뢰 체인 수정 4건(#1285·#1283·#1279·#1274)·방 열기 캐시(#1271)·chat-lock 웹 랩 키 폴백(#1270). employee-app 직접 변경은 40c345d(#1267) 글로벌 미읽음 배지가 페이지네이션 목록에서 유지되는 fix뿐. 미머지 후속: ac2926c 선택 복사 모달·분리 반응 알약(feat/messenger-reactions-menu 팁). 실사는 진입=키 생성(콸 판단 대기)으로 전부 보류 — 사양 행(ㄴ61~ 예상)은 실사 또는 콸 지시 후 반영

Data I/O ​

Server → Client ​

변수엔티티.컬럼값 없을 때비고
$conversations.lastMessageAt / lastMessageContentEmpty ("아직 대화가 없어요...")"대화" 서브탭 대화 목록
$employees (조직원)리스트 렌더 (조직원 상시 존재)"연락처" 서브탭. "미배정 n명" 그룹 헤더 + 전 직원 리스트(이니셜 아바타)
$employees (팀 미배정 필터) count"0명" (추정)"미배정 270명" 그룹 헤더 관찰값
$조직 게시 수신 ([[HBAD280]])섹션 헤더 카운트 0 → 섹션 hide (추정)"대화" 상단 "공지 n" 접이식 섹션. 항목 = 제목 + 날짜
$게시.category / date / title / body / 본인 확인상태—공지 상세(서브화면). 미확인 시 "확인했어요" 버튼, 확인 후 "확인 완료 {상대시간}"
$conversation_members.unreadCount"0"하단 탭바 "메신저 0" 배지
$messages.deletedAt · messages.deletedById (마이그레이션 1955)NULL = 살아 있는 메시지지운 메시지는 목록에 자리로 남고 본문·첨부·액션은 서버가 비워 보냄(maskDeletedMessage) (#1071)
$messages.replyToMessageId (마이그레이션 1957, FK 없음) → 요약없음 = 일반 메시지(인용 박스 hide)원문을 통째로 싣지 않고 요약만(buildReplySummary) (#1075)
$messages.mentions jsonb { all, users:[{employeeId,name}] } (마이그레이션 1958)없음 = 멘션 없음(본문 글자 그대로)지운 메시지는 멘션 메타도 가림 (#1076)
$conversation_members.mutedAt (마이그레이션 1956)NULL = 알림 켜짐conversation.list 응답·목록 갱신 이벤트에 muted (#1077)

Client → Server ​

액션전달값tRPC procedure권한 토큰
대화 검색클라이언트 필터 or sdk.conversation.search() (추정)conversation:read.self (추정)
대화 시작sdk.conversation.create() (추정)conversation:write.self (추정)
그룹 만들기{ memberEmployeeIds[] } ("n명 선택")sdk.conversation.createGroup() (추정)conversation:write.self (추정)
메시지 전송sdk.message.send() (추정)message:write.self (추정)
공지 확인sdk.announcement.confirm() (추정)announcement:confirm.self (추정)
내 메시지 삭제message.delete (보낸 사람 본인만, 재삭제는 alreadyDeleted: true 멱등 성공)message.write (보내기와 같은 권한)
답글 보내기message.send · sendWithAttachments (replyToMessageId 선택)message.write
@멘션 보내기{ conversationId, content, mentions: { all, employeeIds } }message.send · sendWithAttachments (mentions 선택, 서버 resolveMentions가 방 활성 멤버로 거름)message.write
단체방 알림 끄기/켜기conversation.setMuted(방 멤버 본인)
멘션 후보 읽기getMembers (방을 열 때 한 번)-

외부 통합 ​

외부 시스템데이터동기/비동기polling 주기
GA/Admin 공지 "확인 내역"공지 확인 이벤트 write-back (announcementId + employeeId + 확인시각)cross-app write (즉시 반영)[[HBAD280_공지]] "확인 내역" 소비
[[HBAD280_공지]] 게시조직 게시 → 메신저 공지 섹션 수신 표시소비 (read)TBU

화면 플로우 ​

메신저 탭에 AI 어시스턴트/챗봇 핀 미존재 = greenfield. ESS AI 진입 표면은 신규 설계 영역이며 self scope RBAC 자동 반영 컨셉(OQ).


메신저 메인 사양표 ​

번호항목/기능명타입사양 및 기능 설명연관/영향 범위비고
00화면 진입 조건-조건1 [EMPLOYEE] 인증 후 하단 탭바 "메신저" 선택 시 진입[[HBEA110]] Shell
01초기 상태Output"대화" 서브탭 활성 상태로 대화 목록 렌더conversations
ㄴ01-Eㄴ 대화 없음Outputconversations 0건 시 Empty 노출 (문구는 -E 행)conversations
10서브탭Tab• "대화" / "연락처" / "그룹 대화" 3서브탭 간 전환 기능. 구 "공지" 서브탭 제거 → "대화" 상단 섹션으로 이동
• 클릭 → 해당 서브 리스트 전환
구 공지 → 대화 섹션
15미확인 채팅 알림 (탭·파비콘·FAB)Behavior• 아직 확인하지 않은 새 채팅이 있으면 사용자를 화면 밖에서도 부르는 알림 동작. 판정 = 미확인 합계가 아니라 미확인 수가 늘어난 방을 표시하고, 그 방들의 미확인이 모두 0이 되면 끈다(합계로 켜면 일부러 안 읽은 방 하나 때문에 알림이 굳고, 합계가 줄 때 끄면 옛 방만 읽어도 꺼짐). 페이지를 열 때 이미 쌓여 있던 미확인·공지는 대상이 아니다
 - CASE01 [탭 알림]: 브라우저 탭 제목을 **"신규 메시지가 도착했습니다."**로 바꾸고 파비콘을 primary·dark 0.5초 교차로 깜빡임(ic-message-fill + 빨간 점). 사파리는 SVG 파비콘 미표시라 64px PNG로 그림(캔버스 없으면 SVG 폴백). 확인하면 원래 제목·파비콘으로 복원(그사이 화면이 제목을 바꿨으면 덮지 않음)
 - CASE02 [채팅 FAB 통통]: .ds-fab--bounce로 2초 주기 무한 반복(transform만). 패널이 열려 있으면 멈추고, 마우스를 올리면 그 자리에 멈춤. 패널을 한 번 열어 보면 다음 새 메시지 전까지 안 튐. prefers-reduced-motion이면 튀지 않음(탭 제목·파비콘 알림은 읽을 때까지 유지)
 - CASE03 [읽음 판정]: 방이 실제로 보일 때만 읽음 처리. 네이티브 앱(employee-app)은 hereby:app-state로 백그라운드 동안 열린 방도 「보고 있지 않음」으로 치고, 포그라운드 복귀 시 읽음으로 넘김
conversation_members.unreadCount · utils/unread-arrivals · utils/tab-attention신설 (코드 반영: 5912818·b0378d1, #767, 2026-09-17) · 채팅 위젯·직원앱 공용 · --ds-fab-dodge 비킴 계약
20대화 (서브탭)List• 대화 목록을 보여주는 서브탭. "대화 검색..." 입력 + "공지 n" 접이식 섹션 + 대화 리스트(예: "test 6월 30일") 구성
• 클릭 → "대화" 리스트 전환
conversations / messages
ㄴ21ㄴ 대화 검색Input• 대화 리스트를 텍스트로 거르는 입력 필드. "대화 검색..." placeholder
• 입력 → 대화 리스트 필터. 빈 입력 시 전체 표시
conversationsTBU: 결과 0건 문구
ㄴ22ㄴ 공지 섹션Section• 전 직원 수신 공지를 접이식으로 노출하는 섹션. "공지 n"(관찰값 "공지 1"), 구 "공지" 서브탭 대체
• 헤더 클릭 → 섹션 접기/펼치기 토글
[[HBAD280_공지]] 게시 소비TBU: 공지 0건 시 섹션 hide (추정)
ㄴㄴ22-1ㄴㄴ 공지 항목ListItem• 개별 공지 1건. 항목 = 제목 + 날짜
• 클릭 → 공지 상세(50) 전환
게시
ㄴㄴ22-2ㄴㄴ 더보기 토글Button• 접힘 상태에서 상위 3건(noticePreviewCount)만 보이고, 공지가 3건을 넘으면 목록 하단에 노출되는 더보기 토글
 - CASE01 [접힘·미확인 공지 있음]: "더보기" 대신 원문 "확인하지 않은 {n}개의 공지가 있습니다." + 빨간 점(●) 표시. {n} = 전체 미확인 수(제목 옆 배지 noticeUnreadCount와 동일 값 — 숨은 공지만 세면 두 숫자가 어긋남)
 - CASE02 [접힘·미확인 없음]: "더보기" 그대로
 - CASE03 [펼침]: "더보기 닫기"
• 클릭 → 전체/미리보기 토글
[[HBAD280_공지]] 게시 소비신설 (코드 반영: 5ec7856, #767, 2026-09-17) · 채팅 위젯·직원앱 메신저 공용 마크업 · 위 3건 모두 확인돼도 아래 미확인 놓침 방지
30연락처 (서브탭)List• 조직원을 열람하고 대화를 시작하는 서브탭. 연락처가 팀별로 묶여 팀 그룹 헤더 아래 직원 리스트(이니셜 아바타)가 나열됨(9/18). 팀은 id로 묶음(이름은 조직 안에서 겹칠 수 있음 — 다른 부서의 같은 이름 병동 등), 소속 없는 직원은 「미배정」 묶음
• 클릭 → "연락처" 리스트 전환
• (10/10 머지, #1139, MED-962) 찾기 재편: ① 처음 들어오면 팀 묶음 전부 접힘, 내 소속 팀만 맨 위에 펼침(펼침 상태는 기기 저장 hereby:expandedTeams) ② 검색 칸 아래 「부서(팀)로 거르기」 팀 피커(1개 선택) — 고른 팀+하위 팀만 펼쳐 보이고 미배정 묶음 제외, 칩 ×로 전체 복귀 ③ 팀 순서 = 관리자 팀 관리 트리 순서(조상→자손, 형제는 지정 순서→이름순). 그룹 대화 선택 모드에서도 동일
employees · team_members · groupContactsByTeam팀별 그룹핑(코드 반영: 8319cec, #792) · 구버전: 전원 「미배정」 한 묶음·역할 빈칸으로 뜨던 조인 버그 동반 수정 · 2026-09-25 실사(관리자 패널 공용 메신저 위젯 기준): 팀 그룹 헤더("간호부 21명"·"병동1 8명"… + 팀별 「단체」 버튼) + 「미배정」 묶음("미배정 230명", 단체 버튼 없음) + 직원 행 이니셜 아바타·역할 라벨 병기·검색 placeholder "이름 또는 역할로 검색..." 실측 — 직원앱(Works) 자체 화면 실사는 미완(공용 DS 컴포넌트 실측)
ㄴ31ㄴ 팀/미배정 그룹 헤더Text팀별 그룹 헤더(팀명 + 인원수) + 팀 미배정 직원을 묶는 「미배정」 헤더. 검색 결과 줄의 소속·역할 보조 설명은 대표 소속(isPrimary) 기준employees · team_members팀 그룹 헤더 신설(8319cec) · 구 "미배정 270명" 단일 헤더 대체
ㄴ48읽음 표시 (읽음 영수증)Badge• (10/10 머지, #1140, MED-963) 단체방: 내 메시지 시각 옆 안 읽은 사람 수(주황 숫자, 1 이상일 때만) — 모두 읽으면 사라짐. 숫자 클릭 → 「읽음 상태」 모달(안 읽은 사람/읽은 사람 이름). 1:1: 내 메시지에 상대가 읽으면 「읽음」(숫자 없음)
• 판정: 멤버별 lastReadAt vs 메시지 시각(메시지별 저장 없음, 새 칸·마이그레이션 없음) — 보낸 사람·보는 본인은 항상 읽음, 보내는 중·실패·시스템 메시지는 표시 없음. 실시간: markRead가 방 구독 kind:"read" 이벤트 발행(뒤로 가는 시각 무시). 암호화 방도 동일(읽음=메타데이터). 배포 즉시 과거 메시지에도 적용
message.list readStates · conversation_members.lastReadAt신설 (#1140) · E2EE 키 게이트로 실사 TBU
ㄴ49복구 코드 이미지 저장 (폐지)Button• [폐지 — 10/15 머지 556cf9e #1249: 복구 코드 자체 폐지(ㄴ59)] (10/10 머지, #1151) 대화 잠금 설정 2단계(코드 표시)·관리 모달 「복구 코드 다시 발급」 결과의 「복사」 옆 「이미지로 저장」 — 코드(4+3 두 줄)·이름·조직·발급 시각·주의 문구가 담긴 세로 PNG. 웹=브라우저 다운로드, 앱=공유 시트(사진·파일에 저장, 네이티브 1.0.6 필요 #1200). 파일명에 날짜만(코드 미포함)utils/recovery-code-image · hereby:save-file신설 (#1151) · 폐지 (556cf9e #1249, 2026-10-15 — recovery-code-image 유틸 삭제) · 실사 불요
ㄴ50암호화 방 목록 미리보기·1:1 정리Rule• (10/11~12 머지, #1177~#1181·#1188·#1190) 암호화 방 목록 미리보기: 기기 키로 못 풀면 「암호화된 대화」 고정 문구 폐기 — 서버에서 내 세대 봉투를 한 번 가져와 재시도, 그래도 없으면 안 읽은 메시지 있음 "새 메시지가 도착했어요" / 없음 "대화를 열어 확인해 보세요". 첫 목록 로드·실시간 갱신에서도 복호(새 메시지 미복호 시 옛 미리보기 비움). 시스템 공지 평문 마지막 메시지는 그대로 표시(#1178)
• 1:1 방: 나가도 「퇴장했습니다」 시스템 메시지 없음(#1190) · 1:1도 방 단위 「알림 끄기」 허용(나와의 대화·공식 알림 제외, 거절 문구 "이 대화방은 알림을 끌 수 없어요") · 목록 리로드 실패·깨진 방 1개가 있어도 목록 유지(#1181)
conversation.setMuted · 세대 키 sync신설 — 커밋 근거, 실사 TBU
ㄴ51본문 주소 링크화Rule• (10/11 머지, #1155) 메시지 본문의 http(s) 주소가 밑줄 링크(.ds-bubble__link)로 표시(끝 문장부호·닫는 괄호 제외, 멘션 치환과 병행) — PC 웹(admin·TM 메신저)과 직원 앱 공통. 클릭: 네이티브 웹뷰=시스템 브라우저, 웹=새 탭renderMessageHtml linkifyEscapedText신설 (#1155)
ㄴ52텔레그램식 메시지 묶음·보낸 사람 아바타Rule• (10/13 머지, #1220 — 프로덕션 배포) 같은 사람이 5분 안에 이어 보낸 메시지는 이름 생략(묶음, isMessageContinuation)
• 받은 말풍선: 보낸 사람 이름을 말풍선 안 첫 줄에 사람마다 고정 색조로 표시 · 시각·읽음 영수증은 말풍선 안 오른쪽 아래로 이동
• 아바타(프로필 사진 또는 색조 바탕+첫 글자)는 묶음의 마지막 말풍선 왼쪽 아래 한 번 — 누르면 메뉴: 프로필 보기 · 1:1 채팅 · 멘션하기
• 실시간 이벤트에 senderProfilePicture 포함 — 새 메시지도 사진 즉시 표시 · 사진 갱신 규칙 = 사람마다 가장 최근 메시지의 사진 경로 적용(#1238 2a89661 — 옛 캐시가 이기던 결함 수정)
message grouping · [[HBEA110]]신설 (#1220·#1238, 텔레그램 참조 — 사용자 요청) · E2EE 키 게이트로 실사 불가(이월) · 미머지 잔여: 아바타 → 프로필 직행(81b56ba, 브랜치 팁)
ㄴ53목록 프로필 사진Rule• (10/13 머지, #1238) 사진 있는 사람은 이니셜 대신 사진 — 1:1 대화방 목록의 상대 · 연락처 목록
• 서버가 contacts·conversation.list 멤버에 profilePicture 키를 싣고, 클라이언트는 키별 URL을 40ms 묶음 일괄 요청(storage.getProfilePictureUrls 1회·동시 4개) 후 기기 첨부 캐시(IndexedDB, 보호 키 래핑)에 저장 — 다음부터 네트워크 없이 Blob URL
• 사진 키는 업로드마다 새로 생김 → 「캐시에 없는 키 = 새 사진」이 갱신 규칙. 말풍선 아바타(ㄴ52)도 같은 캐시
storage.getProfilePictureUrls · 첨부 캐시신설 (#1238) · E2EE 게이트 실사 이월
ㄴ54보고 있는 방 푸시 생략·방 바로가기 메뉴Rule• (10/13 머지, #12xx f4c68fd — 프로덕션 배포) 방이 열려 있고 화면에 보이는 동안 15초마다 conversation.setViewing(Redis 45초 TTL) → 보내기 팬아웃이 그 멤버의 푸시만 생략(목록·안 읽음·알림함은 그대로) — iOS가 앱이 떠 있어도 배너를 띄워 서버 차단 방식 채택
• 대화 목록 행 우클릭·길게 누름 → 바로가기 메뉴(.ds-menu): 읽음으로 표시 · 알림 끄기/켜기 · 대화방 나가기(conversationMenuItems 순수 규칙)
conversation.setViewing신설 (f4c68fd) · E2EE 게이트 실사 이월
ㄴ55말풍선 줄바꿈·입력 Enter 규칙Rule• (10/13 머지, #1219 — 프로덕션 배포) 말풍선에 ds-bubble--plain — 입력한 줄바꿈 그대로 표시(종전 여러 줄이 한 줄로 붙던 결함, 인용 박스는 영향 없음) · 사진 라이트박스는 최상층 — 헤더 뒤로·Android 백·ESC가 먼저 닫음
• (0f5bfac) 휴대폰 키보드(pointer: coarse)의 Enter = 줄바꿈, 보내기는 화면 버튼만(10/8 「PC처럼 Enter 보내기」 결정을 사용자 요청으로 번복) — PC는 Enter 보내기·Shift+Enter 줄바꿈 유지. 판정 shouldSendOnEnter 단일, enterkeyhint = 손가락 enter/그 외 send. 메신저·AI 입력줄 공통
ds-bubble--plain신설 (#1219·0f5bfac)
ㄴ56「대화의 처음이에요」 구분선Output• (10/13 머지, ffc824c) 「이전 메시지」를 눌러 더 없으면 버튼 자리에 「대화의 처음이에요」 구분선(사용자 요청)신설 (ffc824c) · E2EE 게이트 실사 이월
ㄴ57기기 캐시 (첨부·메시지)Rule• (10/13 머지, #1227 731d1fb·842035a) 첨부 캐시: 한 번 받은 파일은 재다운로드 없음 — 암호화 방 파일은 암호문 그대로, 평문 방 파일은 기기 보호 키로 감싸 IndexedDB 저장(파일 8MB·직원당 100MB·LRU 퇴출), 로그아웃 시 삭제
• 메시지 캐시: 방별 캐시를 기기 보호 키로 감싸 저장 — 방 진입 시 캐시 먼저 그리고 서버엔 캐시 이후 델타(after)만 질의, 과거는 before 커서. 메신저 로컬 저장소(계정 키 묶음·방 세대 키·검색 색인)도 같은 키로 래핑(예전 평문 행은 읽을 때 재래핑)
• (비고) epoch 봉투 upsert 수정(#1218 00c7afb) — 키 재생성 직후 옛 방 「읽을 수 없음」 지속 결함 해소
IndexedDB attachments · chat store신설 (#1227·#1218) · 동작성 — 화면 변화 없음
ㄴ32ㄴ 직원 항목ListItem• 연락처 리스트의 개별 직원 행. hover·틴트 배경은 패널 좌우 끝까지 꽉 찬 띠(공지·메시지 행과 같은 규칙)
• 클릭 → 해당 직원과 대화 시작("대화" 서브탭 전환)
conversationsTBU: self scope 열람 범위
ㄴ33연락처 프로필 (1:1 전 단계)Card• (10/7, #1035, 0d780c29) 연락처를 누르면 바로 1:1 대신 연락처 프로필(사진·소속·역할·직무·직군·직위·이메일 + "1:1 채팅"·"전화하기")이 먼저 열림
• 전화번호는 병원이 관리자 「병원 설정 → 메신저 연락처 공개」에서 켰을 때만 서버가 내보냄(settings.contactSharing.phone, 기본 꺼짐)
• 정보 줄 x-for key = 칸 이름(값 기준)
employee.contactProfile · services/contact-profile · ic-phone신설 (#1035) · 라벨 원문 TBU(실사 대기)
ㄴ34나와의 대화 (self chat)Rule• (10/7, #1061, 443cb189·def08756·889b24b3, MED-903) 「나와의 대화」(ConversationType.SELF, 직원당 하나, conversation.ensureSelf가 찾거나 만듦). 목록 맨 위 고정·전용 줄 없음(10/6 사용자 결정) — 진입 = ①앱 상단(오른쪽 위) 아바타 → 내 프로필 시트(사진·이름·소속 + "나에게 채팅" · "프로필 사진 변경" · "내 정보") ②연락처에서 내 이름 검색 시 「나」 표시로 맨 위 → 프로필의 채팅 버튼 라벨 "나에게 채팅"
• 나가기·해체·초대·위임·내보내기는 서버가 거부하고 화면은 버튼 숨김. 메시지 검색에 「내가 보낸 메시지만」(fromMe) — 키워드 없이도 내 메시지 모아보기
• 연락처 목록에 내 프로필 줄은 만들지 않음(889b24b3)
conversation.ensureSelf · 메신저 스토어 pendingSelfChat신설 (#1061) · 시트 원문 TBU(실사 대기)
ㄴ351:1 방 되살리기 (재입장)Rule• (10/7, #1061, MED-908) 나갔던 사람이 연락처에서 같은 사람을 다시 열면 create가 같은 방을 돌려주며 멤버십 leftAt 해제(종전: 화면에만 방이 뜨고 서버는 "Not a member"로 보내기·나가기 모두 차단). 이미 나간 방의 나가기는 오류 대신 no-op 성공(left:false, 멱등)conversation.create신설 (#1061)
ㄴ36입력줄 (여러 줄 textarea)Input• (10/7, #1035, 0d780c29) 메신저 입력칸 = 여러 줄(textarea) — 최대 5줄(화면 설정 「크게」 4줄)에서 멈춤. 휴대폰 줄바꿈 키 = 줄바꿈, PC Enter = 전송(shouldSendOnEnter). 직원 앱은 캡슐형 입력줄 마크업
• 캡슐형 입력줄의 보내기 버튼은 비활성이면 자리째 숨김(:disabled — 2f03c1e8, PC 위젯·관리자 AI 패널은 그대로)
—신설 (#1035)
ㄴ37메시지 메뉴 (⋯ · 우클릭 · 길게 누르기)Menu• (10/8, #1071·#1075, MED-941·944) 말풍선 바깥쪽(내 것은 왼쪽, 상대 것은 오른쪽)에 hover로 드러나는 ⋯ 버튼(aria "메시지 메뉴") · PC 우클릭 · 모바일 길게 누르기(500ms)가 같은 메뉴 하나(.ds-menu, 딤 없음)를 그 자리 좌표에 엶
• 항목: "답글"(누구 메시지에나) · "삭제"(빨간 글자, 내 메시지에만)
 - CASE01 [내 메시지]: "답글" + "삭제"
 - CASE02 [상대 메시지]: "답글"만
 - CASE03 [보내는 중·실패·임시 id 메시지, 지운 메시지]: 삭제 대상 아님(canDeleteMessage)
• 닫기: 바깥 누름(mousedown·touchstart) · ESC — ESC는 공용 ESC 층(registerEscLayer)에 올려 메뉴만 닫히고 채팅 패널은 유지
messenger-content.html · Messenger.ts openMessageMenu · esc-guard신설 (#1071, #1075에서 상대 메시지로 확장) · 채팅 위젯·직원앱 공용 · 라이브 실사 확인(2026-10-08 낮, 웹 /employee-app·내 메시지): 버튼 aria "메시지 메뉴", 메뉴 항목 "답글"·"삭제"(CASE01) 실측 · TBU: 상대 메시지 CASE02·실기기 길게 누르기와 텍스트 선택 콜아웃 충돌 여부
ㄴ38내 메시지 삭제Button• (10/8, #1071, 57a2132a, MED-941) 메뉴 "삭제" → 확인 창 "이 메시지를 삭제할까요?
대화 참여자 모두에게 「삭제된 메시지입니다」로 보여요." → 확인 시 그 메시지가 참여자 전원 화면에서 자리 표시 "삭제된 메시지입니다"(기울임·옅게)로 바뀜 — 행은 지우지 않음(soft delete). 보낸 지 오래된 메시지도 삭제 가능
 - CASE01 [실시간]: 상대·내 다른 기기 화면도 새로고침 없이 바뀜(같은 구독에 삭제 이벤트)
 - CASE02 [마지막 메시지였음]: 대화 목록 미리보기만 "삭제된 메시지입니다", 목록 순서(lastMessageAt) 유지
 - CASE03 [첨부 메시지]: 첨부는 자리 표시로 가려지고 내려받기 요청은 "삭제된 메시지의 파일이에요."로 거부(저장소 파일은 지우지 않음)
• 검색 결과에 지운 메시지는 안 나옴. 안 읽음 수는 손대지 않음
• 실패: 토스트 "메시지 삭제에 실패했어요" · 서버 거부 "내가 보낸 메시지만 삭제할 수 있어요." / "안내 메시지는 삭제할 수 없어요." / "메시지를 찾을 수 없어요."
message.delete · storage.getDownloadUrl · messages.deletedAt/deletedById신설 (#1071) · 방금 보낸 메시지도 서버 id로 치환돼 바로 삭제 가능 · TBU: 이미지 첨부 썸네일 자리 표시 실사
ㄴ39답글 (원문 인용)Feature• (10/8, #1075, c69fa72a, MED-944) 메뉴 "답글" → 입력줄 위에 답글 바: ↩ 아이콘 + "{name}에게 답글" + 원문 미리보기(사진 "[사진]" · 파일 "[파일]") + ✕(aria "답글 취소")
• 보낸 답글 말풍선 안 인용 박스: 원문 작성자 + 원문 앞 80자(넘으면 …)
 - 인용 박스 누름 → 원문으로 스크롤 + 잠시 강조(.msg-anchor-flash, 이제 채팅 위젯에서도 강조)
 - CASE01 [원문이 나중에 지워짐]: 박스 내용이 "삭제된 메시지입니다"(기울임)로 바뀜 — 실시간 삭제 이벤트에도 함께 바뀜
• 서버 거부: "답글 대상 메시지를 찾을 수 없어요." · "삭제된 메시지에는 답글을 달 수 없어요." · "안내 메시지에는 답글을 달 수 없어요."
• 알림: 원문 작성자에게만 "{보낸 사람}님이 회원님의 메시지에 답글을 남겼습니다"(알림 목록 + 푸시) — 방 알림을 꺼 두어도 감, 본인 메시지에 단 답글은 알리지 않음, 원문 작성자는 일반 메시지 푸시에서 빠져 중복 없음. 알림을 누르면 그 대화방이 열림(관리자·인사 채팅 위젯도 알림 벨에서 방을 엶)
• 실시간·푸시의 보낸 사람 이름 = 성+이름(예: 「관리 김」 → 「김관리」)
• 보내기·답글 뒤 입력칸 초점 유지(9374718b — 종전엔 초점 이동이 조용히 무시됨)
message.send(replyToMessageId) · NotificationType.MESSAGE · [[HBEA110_Shell·탭바·알림]] ㄴ33-2신설 (#1075) · 스레드가 아니라 원문을 가리키는 일반 메시지 · 라이브 실사 확인(2026-10-08 낮, 웹): 메뉴 "답글" → 입력줄 위 답글 바 "{이름}에게 답글" + 원문 미리보기 + 「답글 취소」(aria) 실측(전송은 미실행) · TBU: 인용 박스·알림 동선·실기기 모양
ㄴ40@멘션Feature• (10/8, #1076, c4b64cac, MED-943) 입력칸에 @ 입력 → 입력줄 위 후보 팝업(.ds-menu): 방 참여자(@ 아이콘) + 단체방이면 맨 위 "전체"(그룹 아이콘, 보조 "방 참여자 전원에게 알림"). 이어 친 글자로 좁힘, 방향키·Tab·Enter로 고르고 ESC로 닫음. 이메일 속 @는 후보를 띄우지 않음
• 고르면 @이름 이 들어가고 커서는 그 뒤, 초점 유지
• 후보에서 고른 사람만 멘션 — 손으로 친 @아무개는 멘션 아님(동명이인 구분 불가). 보낼 때 본문에 @이름이 남아 있는 사람만 멘션
• 보낸 말풍선: @이름이 굵게·밑줄 링크(받은 말풍선은 브랜드색) → 누르면 그 사람 연락처 프로필(ㄴ33)
• 나를 지목한 메시지 줄은 옅은 브랜드 면으로 강조
• 알림: "{보낸 사람}님이 회원님을 언급했습니다" / @전체면 "{보낸 사람}님이 전체를 언급했습니다"(방 전원, 보낸 본인 제외) — 방 알림을 꺼 두어도 감. 답글 알림을 이미 받는 원문 작성자는 답글 알림 하나만
 - CASE01 [1:1 방]: "전체" 후보 없음, @전체 무시
• 본문의 HTML은 글자로 보임(전체 이스케이프 후 멘션만 감쌈)
message.send(mentions) · messages.mentions · ds-mention · msg-row--mentions-me신설 (#1076) · @전체 보낼 수 있는 사람 제한 없음(티켓 결정) · 라이브 실사 확인(2026-10-08 낮, 웹·단체방): @ 입력 → 후보 팝업 맨 위 "전체"(보조 "방 참여자 전원에게 알림") + 방 참여자 목록 실측(전송은 미실행) · TBU: 멘션 링크·강조 줄·실기기 IME 팝업 위치
ㄴ41단체방 알림 끄기Toggle• (10/8, #1077, 5359194a, MED-942) 단체 채팅방 대화방 설정(드로어)에 줄 하나: 종 아이콘 + "알림 끄기"(꺼져 있으면 "알림 켜기") + 보조 "꺼도 안 읽은 수는 표시되고, 나를 언급하거나 내 메시지에 답글이 달리면 알림이 와요" + 오른쪽 라벨 "알림 켜짐"/"알림 꺼짐"
• 누름 → 즉시 전환(컨펌 없음). 실패 토스트 "알림 설정을 바꾸지 못했어요"
 - CASE01 [꺼짐]: 새 메시지 푸시·브라우저 알림 없음, 대화 목록 방 이름 옆 종 꺼짐 아이콘(title "알림 꺼짐"), 안 읽은 수는 그대로 셈, 앱 아이콘 배지(전체 안 읽은 수)에도 포함
 - CASE02 [1:1 · 나와의 대화 · 공식 안내]: 줄 없음(서버도 "단체 채팅방만 알림을 끌 수 있어요."로 거부)
• 예외: @멘션(ㄴ40)·내 메시지 답글(ㄴ39) 알림은 꺼도 옴
conversation.setMuted · conversation_members.mutedAt · ic-bell-off신설 (#1077) · 그룹·필수 팀방·채널만 허용 · 라이브 실사 확인(2026-10-08 낮, 웹·단체방 대화 설정): 줄 "알림 끄기" + 보조 원문 일치("꺼도 안 읽은 수는 표시되고, 나를 언급하거나 내 메시지에 답글이 달리면 알림이 와요") + 상태 라벨 "알림 켜짐" 실측(토글은 미조작) · 같은 드로어에 "대화방 이름 (나에게만 보임)"·「초대하기」·「내보내기」·「방장 위임」·「단톡방 해체」 확인 · TBU: 꺼짐 상태 UI·실기기 푸시 미수신
ㄴ42앱 셸 연동 (헤더 접힘·스와이프)Rule• (10/8, #1083, fd5bf280·920aee91) 직원 앱 메신저 탭: 대화·연락처 목록을 아래로 스크롤하면 헤더 날짜 줄이 스크롤 양의 0.6배로 접혀 탭 줄만 상단에 남고, 목록도 유리 탭 줄 뒤로 지나감(목록 data-app-scroll-root). 상세는 [[HBEA110_Shell·탭바·알림]] ㄴ14
• 앱(iOS)에서는 메신저 자체 스와이프를 끄고 셸의 왼쪽 가장자리 스와이프(뒤로)만 사용 — 두 번 물러나지 않게
[[HBEA110_Shell·탭바·알림]]신설 (#1083) · TBU: 실기기 긴 목록 스크롤 끊김 여부
ㄴ43화면 캡처 제한 대상Rule• (10/8, #1090·#1093) 메신저 탭 전체(section = messenger)는 조직 「화면 캡처 제한」이 켜져 있으면 캡처 보호 대상(네이티브 iOS·Android 앱만). 스크린샷 시 토스트 "화면이 캡처되었어요." — 상세는 [[HBEA110_Shell·탭바·알림]] 50
• 웹 검은 안내막은 #1093으로 제거 — 채팅 UI는 항상 보임
screen-capture-guard.ts · settings.screenCaptureProtection신설 (#1090·#1093)
ㄴ44방 내 메시지 검색Feature• (10/8, #1120, 57275641·b24278c4) 대화방 헤더 「검색」 버튼 → 헤더가 유리 검색칸(placeholder "대화 내용 검색", 포커스 선 없음) + 「취소」로 전환, 입력줄 자리에 검색 막대(순번/건수 「{i}/{n}」 · ▲▼ 이동)로 이 방 안의 결과를 최신순으로 짚음
• 분모 {n} = 전체 건수(서버 전체 + 기기 색인 합) — ▲로 결과 끝에 닿으면 서버에서 다음 100건 이어 받음(offset 페이징)
• 암호화 방은 이 기기 색인(searchIndex)을 방 범위로 먼저 거른 뒤 한도 적용(타 방 결과에 밀리는 결함 선제 수정)
메시지 검색 · 기기 로컬 색인신설 (#1120) · 라이브 실사 2026-10-09(검색칸 원문·「1/1」 순번 실측, ▲▼·페이징은 데이터 부족 TBU)
ㄴ46실시간 재동기화·새 방 단일 경로Rule• (10/9 머지, #1124, 10/8 온보딩 현장 버그 대응) 끊김 구간 메시지 되살리기: 앱 복귀(포그라운드)·소켓 재연결 때 대화 목록과 열린 방의 밀린 메시지를 서버에서 다시 읽어 채움(종전엔 구독만 다시 걸어 빈 틈이 남았고 검색에서만 보임). 연결 유지 PING/PONG 활성화로 반쪽 연결 감지
• 연락처에서 1:1 시작(startConversation) 시 서버 방 내용을 읽고 구독까지 거는 단일 경로로 통일 — 상대가 먼저 만든 방의 첫 메시지가 보이고 답장이 실시간 수신, 암호화 조직은 첫 세대 키 처리 포함
• 1:1 상대가 방을 나가 있어도 목록의 방에서 바로 보낸 메시지가 상대 목록에 다시 뜸(되살리기를 send 경로에도 적용)
• (비고) 운영 v0.22.1의 다중 API 인스턴스 간 이벤트 유실은 develop의 #1099(Redis pub/sub 팬아웃)가 해소 — 아직 운영 미배포(PR 본문 명시)
messenger-store · Messenger.ts · messenger-helpers신설 (#1124) · 동작성 수정 — 화면 요소 변화 없음, 실사 비대상
ㄴ47대화 잠금 모달 수정 2건Fix• (10/9 머지, #1134·#1135) 잠금 안내 모달 첫 줄 상단 잘림 수정(#1134) · 잠금 키 저장을 평문 객체로 바꿔 IndexedDB가 Alpine 프록시를 거부하던 문제 수정(#1135 — 잠금 설정이 조용히 실패하던 결함)messenger-content.html · chat lock store신설 (#1134·#1135) · 암호화 조직 한정 UI — 실사 TBU(ㄴ45와 동일 사유)
ㄴ58키 상태 띠 sticky·재키 멤버 자동 복구Rule• (10/14 머지, #1242·#1244) 키 없음·서명 불신·세대 새로 엶·「○○ 님의 키가 바뀌었어요 · 안전 번호 확인」 띠를 sticky 묶음 하나로 메시지 목록 위에 고정 — 종전엔 메시지 흐름 속 맨 위에 있어 스크롤 밖으로 밀려 누를 수 없었음(앱 QA 10/14: 재설정한 사람은 지난 대화를, 상대는 새 메시지를 계속 못 읽음) → 10/15(556cf9e #1249, ㄴ59): 「키가 바뀌었어요 · 안전 번호 확인」 띠는 폐지(상대 키 자동 수용) — 키 없음·세대·rekeyed_epoch 안내 띠는 유지
• 계정 키를 다시 만든(재키) 멤버는 현재 세대 봉투가 옛 version으로만 남아 보낼 수 없었고, 다른 멤버 기기는 바뀐 키를 안전 번호로 확인한 뒤에야 봉투를 주므로 「잠시 뒤 다시 보내 주세요」가 사실상 무기한이던 결함 수정 — sync가 「어느 세대든 옛 version 봉투」를 재키로 판정해 스스로 rotate로 다음 세대를 열어 바로 보냄(방 위 안내 띠 messenger.lock.rekeyed_epoch). 봉투가 아예 없는 새 멤버는 그대로 대기, 지난 세대는 다른 멤버 기기가 확인 뒤 채워 주면 읽힘
• 방 키 동기화 실패·키 미보유 결과는 console.warn으로 남겨 CS 리포트에서 원인 추적
conversation-key-manager · messenger-content.html신설 (#1242·#1244, 16c2d23·ad1e209) · E2EE 키 게이트로 실사 이월 · 키 변경 띠 10/15 폐지(556cf9e #1249)
ㄴ59복구 코드 폐지·상대 키 자동 수용Rule• (10/15 머지, #1249 556cf9e — 2026-10-15 결정) 사용자가 복구 수단을 직접 관리하지 않음. 대화 잠금 설정 = 「키 만들기」 한 번으로 완료 — 복구 코드 표시 단계·서버 백업·「복구 코드 다시 발급」·이미지(PNG) 저장 전부 제거(ㄴ49 폐지)
• 키 분실 복구 = 다른 기기 승인만. 승인할 기기가 없으면 새로 시작 — 지난 대화는 다른 멤버가 방을 열어 봉투를 다시 주는 방에서만 복원
• 상대 공개키가 바뀌면 안전 번호 확인 없이 새 키를 고정값으로 자동 수용(E9 보류 폐지) — 키 변경 띠·멤버 배지·안전 번호 모달 제거(ㄴ45·ㄴ58 소급)
• register의 backup 파라미터 = 옛 클라이언트 호환 선택값. 서버 이관 키·회사 복구 봉투(KMS)는 유지. 직원 가이드·기능 문서(docs/ko) 동반 갱신
chat-key.router · chat-key-manager · peer-key-directory신설 (556cf9e, #1249) · E2EE 키 게이트로 실사 이월 — 설정 플로우 단순화 검증은 키 생성 필요(콸 판단 대기) · 10/19 #1251(ㄴ60): 「키 만들기」 버튼조차 없이 자동 생성으로 단순화
ㄴ60키 자동 생성·봉투 실시간 전달Rule• (10/19 머지, #1251 5047f5b — feat/chat-key-auto-create-and-refresh) 암호화 조직에서 키 없는 기기가 메신저에 들어오면 묻지 않고 바로 키를 만든다 — 게이트 모달(ㄴ45 실측분 「키 만들기」) 대신 목록 위에 잠깐 보이는 info 띠 「대화 잠금 키를 만드는 중이에요…」(스피너, data-testid chat-lock-creating)만 노출. 완료 시 토스트(설정 완료 문구), 실패 시 setup 상태로 돌아가 띠의 「설정하기」로 재시도
• 키 생성 직후 열어 둔 방이 있으면 세대 키를 즉시 동기화하고 「읽을 수 없음」이던 메시지를 재복호 — 새로고침 없이 읽고 보냄
• 보내기 전에 빈 봉투를 채움(fillRoomKeyEnvelopes 선행) — 종전엔 보낸 뒤 채워서, 키를 막 만든 상대에게 실시간 메시지가 봉투보다 먼저 닿아 「읽을 수 없음」으로 남던 결함(새로고침해야 풀림) 수정. 서버도 실시간 봉투 요청/전달 이벤트(requestEnvelopes, message-events) 추가
Messenger.ts · messenger-store · conversation-key.router · message.router신설 (#1251, 5047f5b·d78152f) · ⚠ 실사 보류 — 자동 생성이라 메신저 진입만으로 실사 계정에 키가 만들어짐(상태 변경). 콸 판단 대기(기존 E2EE 키 게이트 이월 건과 동일 결론이나 사유가 「게이트 차단」→「진입=생성 부작용」으로 바뀜)
ㄴ45종단간 암호화 (E2EE) — 암호화 조직 한정Rule• (10/8, E2EE 스택 #1095~#1114 develop 합류) 조직별로 system-admin이 켜는 일방향 암호화([[HBSA210]], 한 번 켜면 끔 불가). 켜진 조직에서만 아래 사용자 가시 기능 활성:
 - 채팅 잠금 키 설정·새 기기 승인 플로우(#1102) — 복구 코드 발급은 10/15 폐지(556cf9e #1249, ㄴ59): 설정 = 「키 만들기」 한 번, 복구 코드·서버 백업·재발급 없음
 - 메시지·첨부·이미지 섬네일 기기 암복호(#1104·#1106), 기존 평문 대화 서버 이관(#1107)
 - 상대 키 고정(TOFU)·키 변경 시 방 상단 알림 + 안전 번호(60자리) 대조 모달 「확인했어요」(#1112) — 모달에 상대가 번호를 보는 위치 안내 한 줄 추가: 상대는 방 멤버 목록의 방패 아이콘에서 같은 번호를 보며, 번호가 같으면 「확인했어요」를 누르라고 안내(10/14, 21d955c #1244) → 10/15 폐지(556cf9e #1249, ㄴ59): 상대 공개키 변경 시 안전 번호 확인 없이 새 키 자동 수용(E9 보류 폐지) — 키 변경 알림·멤버 배지·안전 번호 모달 제거
 - 푸시: 「이름 + 본문」 형식 옵션 개방 — 웹 서비스 워커/Android·iOS 네이티브가 기기에서 복호해 본문 표시, 못 풀면 고정 문구(#1105·70fa9baa·80cf6955)
 - 복구: 법원·수사기관/내부 점검용 관리자 「대화 복구」 요청 — 다른 관리자 승인 + hereby 운영자 2인 심사(#1108, admin 신화면 HBAD310 채번 제안)
• CASE01 [암호화 OFF 조직]: 기존 평문 동작 그대로
chat-crypto · conversation key epochs · [[HBSA210_Organizations (목록·상세·설정·역할·관리자)]]신설 (10/8 develop 합류) · 10/12 프로덕션 배포(#1187→#1195, v0.23) · okbest dev 조직 10/13 현재 암호화 켜짐 — 미설정 기기 진입 시 게이트 모달 「대화 잠금 설정」 실측: "이 조직의 대화는 내 기기에서 암호화돼요. 지금 이 기기에 대화 잠금 키를 만들어요." + info "대화와 파일은 회사 자료로 보관되며, 법적 요청이 있을 때 정해진 절차에 따라 열람될 수 있어요." + 「키 만들기」(닫기 불가 — 메신저 전면 게이트) → 10/19 #1251(ㄴ60): 게이트 모달 폐지 — 키 없으면 묻지 않고 자동 생성, 「키를 만드는 중」 띠로 대체. 키 생성은 실사 계정 상태 변경이라 보류 → 내부 플로우 실사 TBU · (10/10~12 안정화 머지 #1153·#1156~#1161·#1164~#1169·#1175·#1182·#1184·#1188·#1189) 기기 승인 앱 전역 감시·확인 다이얼로그 필수(#1175)·강퇴 시 키 회전·재초대 재입장(#1156·#1159)·구독 복구(#1157)·마이그레이션 세대 키 서버 보존(#1165)·자기 세대 키 재개방(#1188)·브라우저 알림이 휴대폰 알림 설정 따름(#1184) 등 — 동작성 수정, 화면 변화 최소
40그룹 대화 (서브탭)Flow• 다중 멤버 그룹 대화를 생성하는 서브탭. 상세 흐름은 서브화면 표 참조
• 클릭 → 멤버 다중 선택 모드 전환
[[HBEA310_그룹 만들기]] (분리 권고)신규 서브탭
50공지 상세 진입Detail• 공지 항목을 상세 서브화면으로 여는 진입점(아래 H3)
• 클릭 → 공지 상세 서브화면 전환
[[HBAD280_공지]]서브화면 표 분리
60하단 탭바Tab• 앱 최상위 탭 전환 바. "근무" / "메신저" / "결재" / "더보기" + "메신저 0" 배지
• 클릭 → 해당 탭 전환
[[HBEA110]]Shell 공통
-E대화 EmptyText대화 0건 시 "아직 대화가 없어요 연락처에서 대화를 시작해보세요" 노출, 연락처로 유도conversationsEmpty 원문

공지 상세 사양표 (서브화면) ​

번호항목/기능명타입사양 및 기능 설명연관/영향 범위비고
00진입 조건-"대화" 서브탭 "공지 n" 섹션 항목 클릭 시 진입메신저(50)
10헤더Text"공지사항" 표기
20카테고리 배지Badge게시 category 값 배지[[HBAD280]]TBU: 카테고리 문구(데이터 의존)
30날짜Text게시 날짜 표기게시.date
40제목/본문Text공지 제목 + 본문 표기게시TBU: 본문 미존재 공지 처리 미관찰
50확인 버튼Button• 본인 공지 확인 상태를 기록하는 버튼. 조건1 [EMPLOYEE] 본인 확인
• 클릭 → 즉시 실행(컨펌 없음), 성공: "확인 완료 {상대시간}"(예: "확인 완료 방금") 전환 + GA/Admin 공지 "확인 내역" write-back
 - CASE01 [미확인]: "확인했어요" 노출
 - CASE02 [확인 완료]: "확인 완료 {상대시간}" 노출(상대시간 원천 = 확인시각)
[[HBAD280]] "확인 내역" cross-app검증 완료. TBU: confirm 실패 UX(무반응이면 버그 후보)

그룹 만들기 플로우 사양표 (서브화면) ​

번호항목/기능명타입사양 및 기능 설명연관/영향 범위비고
00진입 조건-메신저 "그룹 대화" 서브탭 진입 시 멤버 다중 선택 모드메신저(40)
10멤버 선택List• 그룹에 넣을 조직원을 고르는 리스트. 원천 = employees. 팀별로 묶여 노출(9/18)
• 항목 클릭 → 멤버 선택 토글
employees · team_members팀별 그룹핑(코드 반영: 8319cec, #792) · TBU: 선택 가능 범위(전 직원 vs 팀)
ㄴ11ㄴ 팀 머리 체크박스Checkbox• 팀 그룹 헤더의 체크박스로 그 팀 전원을 한꺼번에 선택/해제하는 기능. 그룹 대화 선택·초대하기 공통
 - CASE01 [전원 선택]: 체크 표시
 - CASE02 [일부만 선택]: 「일부 선택」(indeterminate) 가로 막대 표시 — 채움·색은 「선택됨」과 같고 글리프만 다름(9/18 DS 신규)
 - CASE03 [일부 선택 상태에서 클릭]: 전부 선택(해제 아님). 전부 선택된 상태에서 클릭할 때만 그 묶음 해제 — 다른 묶음의 선택은 유지
DS ds-checkbox indeterminate신설 (코드 반영: 8319cec, #792) · 연차 촉진 표 머리 전체 선택도 같은 「일부 선택」 모양 적용 · 2026-09-25 실사(관리자 메신저 그룹 대화 모드): 팀 그룹 헤더 체크박스 실측(indeterminate 상태는 미재현 — 선택 없이 관찰만)
ㄴ12ㄴ 전체 선택Checkbox• 목록 위(검색창 아래, 목록 스크롤 밖)의 "전체 선택" — 보이는(검색 필터 통과) 사람을 모두 선택/해제. 일부 선택 시 indeterminate 막대locale messenger.select_all "전체 선택"신설 (코드 반영: 8319cec, #792) · 2026-09-25 실사(관리자 메신저): 그룹 대화 모드 목록 위 "전체 선택 270명" 체크박스 + 헤더 "0명 선택"·그룹 만들기·[취소] 실측
20선택 카운트Text선택 인원 실시간 "n명 선택" 표기
30그룹 만들기Button• 선택한 멤버로 그룹 대화를 생성하는 버튼
• 클릭 → 그룹 conversations 생성 후 대화로 전환
그룹 conversationsTBU: 0명 시 비활성 여부·생성 실패 UX·멤버 상한
40취소Button• 선택 모드를 종료하는 버튼
• 클릭 → 선택 모드 종료(대화 서브탭 복귀)

AI 표면 보조표 ​

항목현황비고
AI 챗봇/어시스턴트 핀미존재 (0)greenfield — 신규 설계 영역
ESS AI 진입 후보메신저 탭 단체방 영구 핀 / 별도 AI 탭 (OQ)self scope RBAC 자동 반영 컨셉

권한·상태 분기 요약 ​

시나리오트리거결과 화면비고
CASE01 [대화 없음]"대화" 서브탭 진입, conversations 0건Empty "아직 대화가 없어요..."연락처 유도
CASE02 [대화 있음]대화 존재대화 리스트 + "공지 n" 섹션
CASE03 [공지 미확인]공지 상세 진입"확인했어요" 버튼
CASE04 [공지 확인 완료]"확인했어요" 클릭"확인 완료 {상대시간}" + GA/Admin "확인 내역" write-backcross-app
조건1 [EMPLOYEE]전 직원 인증메신저 전 기능 (연락처 열람 범위·그룹 생성 권한은 OQ)역할 세분 미관찰

Open Question ​

  • 대화 검색 결과 0건 Empty 문구 = TBU (별도 카피 미관찰)
  • 그룹 대화 생성 권한 범위 및 멤버 상한 = TBU/OQ
  • 연락처 열람 범위(전 직원 vs 본인 팀만 — self scope 반영 여부) = TBU/OQ
  • 공지 상세 카테고리 배지 문구·본문 Empty 처리 = TBU (데이터 의존)
  • 공지 "확인했어요" confirm 실패 시 UX(토스트·롤백) = TBU (무반응이면 버그 후보)
  • ESS AI 핀 배치 (메신저 단체방 영구 핀 / 별도 AI 탭) = TBU/OQ — 메신저 탭 AI 표면 greenfield 유지
  • "미배정 n명" 외 팀별 그룹 헤더 존재 여부 = TBU (캡처에 미배정 그룹만 관찰)