docs: 휴대폰 앱 사내 배포를 계약 · 운영 · 앱 · 디자인 문서와 스킬에 적는다
CI / Frontend (lint · type-check · test · build) (pull_request) Successful in 12m41s
CI (Flutter) / Flutter (analyze · test) (pull_request) Successful in 22m13s
CI / Backend (lint · type-check · test · e2e · build) (pull_request) Successful in 13m13s
CI / Infra (compose · Dockerfile · nginx · 운영 스크립트) (pull_request) Successful in 2m13s
CI / 에러 코드 SSOT 동기화 검사 (pull_request) Successful in 11m5s
CI (Flutter) / Flutter (analyze · test) (push) Successful in 21m49s
CI / Backend (lint · type-check · test · e2e · build) (push) Successful in 13m13s
CI / Frontend (lint · type-check · test · build) (push) Successful in 12m54s
CI / Infra (compose · Dockerfile · nginx · 운영 스크립트) (push) Successful in 1m47s
CI / 에러 코드 SSOT 동기화 검사 (push) Successful in 10m26s

계약 1절 "휴대폰 앱 받기"와 6절 운영 도구 명령, 서버 B 에 APK 올리는 차례와 직원 안내(링크 없이),
앱의 서명 키 · release 빌드 · 올리기 전 확인, 웹 화면 절, 스킬 표, App1Shell 보드의 앱 정보 줄.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit was merged in pull request #22.
This commit is contained in:
2026-11-08 23:29:06 +09:00
parent 7c70862647
commit ff1948b7df
10 changed files with 312 additions and 36 deletions
+1
View File
@@ -164,6 +164,7 @@ input::placeholder,textarea::placeholder{color:#8A94A3}
<span style="font-size: 15.5px">앱 버전</span>
<span style="font-size: 14px; color: #667085; font-variant-numeric: tabular-nums">1.0.0</span>
</div>
<button type="button" style="width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 52px; box-sizing: border-box; padding: 0 12px 0 20px; border: 0; background: transparent; font: inherit; font-size: 15.5px; color: #171C26; text-align: left; cursor: pointer"><span>새 버전 받기</span><span aria-hidden="true" style="display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; color: #667085"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg></span></button>
<button type="button" style="width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 52px; box-sizing: border-box; padding: 0 12px 0 20px; border: 0; background: transparent; font: inherit; font-size: 15.5px; color: #171C26; text-align: left; cursor: pointer"><span>오픈소스 라이선스</span><span aria-hidden="true" style="display: inline-flex; color: #667085"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 18 15 12 9 6"></polyline></svg></span></button>
</section>
<div aria-hidden="true" style="height: 1px; margin: 8px 0 0; background: #EEF0F3"></div>
+87
View File
@@ -1091,6 +1091,79 @@ API 와 서버 규칙은 계약 2절 "실시간 알림".
본문 강조의 다음 · 이전 이동, 검색어 줄임말(from: 같은 꼴), 대화째 끌어다 놓기, 첨부 파일 안의 글 찾기, 끝이 바뀐 말 찾기(형태소 분석).
같은 대화 칸 머리의 [모두 펼치기]와 펼친 줄 머리의 [답장]도 다음 후보로 둔다(2026-11-07 사용자 관점 점검에서 나온 제안 — 사용자가 미루기로 정함). 지금은 줄마다 펼치고, [답장]은 펼친 본문 아래에만 있다.
## 휴대폰 앱 받기 (2026-11-08 확정 — `SettingsMobileApp`)
안드로이드 앱(ARA SYSTEM — `flutter/`)은 스토어(Play)에 올리지 않고, 직접 빌드해 서명한 APK 를 **웹메일에 로그인한 직원만** 받게 한다.
PC 에서는 설정 › 계정 › 휴대폰 앱(A)이 QR 코드로 휴대폰에 넘기고, 휴대폰 브라우저는 앱 틀 밖의 받기 화면 `/app`(B)을 연다.
앱의 업데이트 화면 · 설정 › 앱 정보의 [새 버전 받기]도 `/app` 을 연다(앱은 API_URL 에서 `/api` 를 뗀 출처 + `/app` — 같은 주소 배포).
데이터는 서버 두 경로다(로그인 필요 · 제한된 세션은 막힘 — 계약 1절 "휴대폰 앱 받기 — 사내 배포"): 판 정보 `GET /api/app-releases/android/latest`
(`{ version, sizeBytes, releasedAt }` — 가장 늦게 올린 판, 없으면 404 `RES_001`)와 파일 `GET /api/app-releases/android/latest/file`
(APK — `attachment; filename="ARA-SYSTEM-<버전>.apk"` · no-store · Range). 판을 올리는 것은 서버의 운영 도구다(웹에 올리기 화면은 없다).
글의 원본은 화면 상수 `frontend/src/shared/constants/mobile-app.ts` 이고 아래는 옮겨 적은 것이다. 문구는 위 「화면 문구 — 사용자에게 말하듯」을 따른다.
새 크기 토큰 · 밀도 예외는 없다(글자 크기 17 하나만 토큰으로 더했다 — 문서 끝 "휴대폰 앱 받기 구현하며 정한 보완"). 뱃지 · 최신 · NEW 같은 꼬리표는 달지 않는다.
### 설정 › 계정 › 휴대폰 앱 (`SettingsMobileApp` A)
- **메뉴**: 계정 묶음의 메일 프로그램 연결 다음 · 로그인 기록 앞 「휴대폰 앱」(아이콘은 이미 있는 휴대폰 그림 `smartphone`). 주소 `/settings/account/mobile-app`, 탭 제목 "휴대폰 앱 · ARA SYSTEM".
- 머리 "휴대폰 앱" + "안드로이드 휴대폰에 ARA SYSTEM 앱을 설치하면 메일을 읽고 보내고, 새 메일 알림도 받을 수 있습니다." 콘텐츠 열은 다른 설정 화면과 같은 800.
- **받을 앱 있음(A1)** — 「안드로이드 앱」(14 굵게) 아래 테두리 칸(모서리 10):
- 앱 줄(안쪽 16 · 사이 14): 앱 그림 48 · "ARA SYSTEM"(14 · 600) / "버전 1.0.0 · 올린 날 2026년 11월 18일 · 크기 31 MB"(12.5 회색 · 숫자 고정폭) ·
오른쪽 [앱 내려받기](채운 단추 44 · 아이콘 15 — 화면 낭독은 크기까지 "앱 내려받기 (31 MB)").
- 가는 선 아래 QR 줄(사이 20): QR 160(흰 상자 · 안쪽 6 · 테두리) + 「휴대폰으로 받기」(13.5 · 600) /
"휴대폰 카메라로 QR 코드를 찍어 주세요. 휴대폰 브라우저에서 웹메일에 로그인하면 바로 앱을 받을 수 있습니다." /
가는 선 아래 "QR 코드를 찍을 수 없으면 휴대폰 브라우저의 주소 칸에 아래 주소를 직접 넣어 주세요." + 주소 "portal.arasystem.it.kr/app"(14 · 600) /
"PC에서 받은 파일은 휴대폰으로 옮겨야 설치할 수 있습니다."(12.5 회색).
QR 은 2단계 인증 켜기와 같은 `QrCodeImage`(160)이고, 지금 주소의 출처에 `/app` 을 붙인 주소만 담는다 — 비밀이 아니다(그림 이름 "휴대폰에서 앱을 받는 주소의 QR 코드").
- 「설치 방법」 — 번호 줄(청록 굵은 숫자 · 동그라미 없음, 2단계 인증 「켜는 차례」와 같은 꼴). 2번의 파일 이름은 받은 판의 버전이다:
1. 휴대폰에서 [앱 내려받기]를 눌러 파일을 받아 주세요. 브라우저가 이 파일을 받을지 한 번 더 물으면 그대로 받아 주세요.
2. 받은 ‘ARA-SYSTEM-1.0.0.apk’ 파일을 열어 주세요. 처음 설치할 때는 휴대폰이 알 수 없는 앱의 설치를 허용할지 묻습니다. 허용한 뒤 돌아와 설치를 이어 가 주세요.
3. Play 스토어를 거치지 않은 앱이라 Play 프로텍트가 경고할 수 있습니다. 웹메일에서 받은 ARA SYSTEM 앱이면 설치를 이어 가 주세요.
4. 설치한 앱을 열고 웹메일과 같은 메일 주소와 비밀번호로 로그인해 주세요. 앱 비밀번호는 만들지 않으셔도 됩니다.
- 「새 버전이 나오면」 — "새 버전도 같은 곳에서 받아 설치하면 됩니다. 쓰던 앱을 지우지 않고 설치하면 로그인과 설정이 그대로 남습니다." / "꼭 필요한 업데이트가 있으면 앱이 먼저 알려 드립니다."
- 아이폰 안내 상자(#F3F8F8 · 안내 아이콘): **"아이폰용 앱은 아직 없습니다"** / "아이폰에서는 브라우저로 웹메일을 쓰거나, 기본 메일 앱에 회사 메일을 연결해 주세요. 연결 방법은 메일 프로그램 연결에서 볼 수 있습니다."
— 「메일 프로그램 연결」이 그 설정 화면 링크다.
- **받을 앱 없음(A2 — 404 `RES_001`, 알림 없음)**: 「안드로이드 앱」 아래 테두리 상자에 휴대폰 아이콘 24 · "아직 받을 수 있는 앱이 없습니다"(14 · 600) /
"앱이 준비되면 이곳에서 받아 설치할 수 있습니다." + 가는 선 아래 문의 줄. QR · 설치 방법 · 새 버전 · 아이폰 안내는 모두 뺀다.
- **문의 줄**: 로그인 화면의 문의 줄과 같은 꼴이다(연락처는 13단계 `GET /api/support-contact`). 연락처가 있으면 "언제 받을 수 있는지 궁금하신가요?" 다음 줄에 연락처,
없거나 받지 못하면 대신 "언제 받을 수 있는지는 관리자에게 문의해 주세요."
- **좁은 폭(휴대폰 — 480 이하)**: 설정 메뉴가 위로 올라가고(지금 설정 셸), QR 줄은 빼며, [앱 내려받기]는 이름 아래 전체 폭으로 둔다.
### 휴대폰 받기 화면 `/app` (`SettingsMobileApp` B)
- **독립 화면**: 앱 레일 · 상단 바가 없다(인쇄 화면처럼 앱 틀 밖의 보호 화면 — `meta.requiresAuth`). 탭 제목 "휴대폰 앱 받기 · ARA SYSTEM".
- **로그인하지 않았을 때**: 지금의 로그인 화면으로 갔다가(`?redirect=/app`) 로그인을 마치면 이 화면으로 돌아온다 — 로그인 화면은 새로 그리지 않았다.
새 비밀번호 정하기나 2단계 인증 켜기 단계가 걸린 직원도 그 단계를 마친 뒤 돌아온다. 이미 로그인해 둔 휴대폰 브라우저는 바로 열린다.
- **브랜드 띠**(#0B3640 — 로그인 브랜드 패널을 줄인 꼴): 흰 로고 120 · "휴대폰 앱 받기"(20 · 흰 굵게) ·
"안드로이드 휴대폰에 설치하면 메일을 읽고 보내고, 새 메일 알림도 받을 수 있습니다."(14 · #A3C2C6). 내용 바탕은 #F8F9FB, 본문 14.
- **받을 앱 있음(B1)**:
- 카드(테두리 · 모서리 12 · 안쪽 16): 앱 그림 52 · "ARA SYSTEM"(17 굵게) / "안드로이드 앱"(13 회색) → 버전 · 올린 날 · 크기(이름 칸 56 · 13) →
[앱 내려받기](전체 폭 · 높이 48 · 글 15 · 아이콘 18). 휴대폰에서도 [앱 내려받기]까지 첫 화면에 들어온다.
- 「설치 방법」(15 굵게 — 줄 사이 10 · 줄 높이 1.55):
1. [앱 내려받기]를 눌러 파일을 받아 주세요. 브라우저가 한 번 더 물으면 그대로 받아 주세요.
2. 받은 파일을 열어 주세요. 처음에는 알 수 없는 앱의 설치를 허용할지 묻습니다. 허용한 뒤 설치를 이어 가 주세요.
3. Play 프로텍트가 경고해도 이 화면에서 받은 ARA SYSTEM 앱이면 설치를 이어 가 주세요.
4. 설치한 앱을 열고 웹메일과 같은 메일 주소와 비밀번호로 로그인해 주세요.
- 설치 방법 아래 한 줄(13 · #4A5363) "새 버전도 이 화면에서 받아 설치하면 됩니다. 쓰던 앱을 지우지 않고 설치하면 로그인과 설정이 그대로 남습니다." — 앱의 [새 버전 받기]로 온 직원이 쓰던 앱을 먼저 지우지 않게.
- 아이폰 안내 상자(설정과 같다) → 가는 선 아래 문의 줄(13): 연락처가 있으면 "설치가 잘 되지 않나요?" 다음 줄에 연락처, 없으면 "설치가 잘 되지 않으면 관리자에게 문의해 주세요."
- **받을 앱 없음(B2)**: 테두리 상자에 휴대폰 아이콘 28 · "아직 받을 수 있는 앱이 없습니다"(17) / "앱이 준비되면 이 화면에서 받을 수 있습니다." + 문의 줄(설정의 받을 앱 없음과 같은 글).
설치 방법과 아이폰 안내는 뺀다.
- **넓은 화면(480 초과 — B3)**: 띠는 화면 폭 전체로 두고, 로고(150) · 제목(22) · 내용은 가운데 칸 480 에 맞춘다. 내용은 휴대폰과 같다.
### 두 화면 공통
- **[앱 내려받기]**: 첨부처럼 `<a href download>` 이동이고, 누르기 직전에 세션을 확인한다(`confirmSession`) — 세션이 끝났으면 세션 만료 창이 뜨고 받지 않는다.
받는 파일 이름은 서버가 정한다("ARA-SYSTEM-1.0.0.apk").
- **앱 정보를 받지 못하면**: 칸(카드) 자리에 사전 문구와 [다시 시도]를 둔다. 받는 동안은 자리만 둔다.
- **앱 그림**: 남색 바탕에 흰 ARA 마크 — 홈 화면 아이콘 `apple-touch-icon.png` 를 둥글게 깎은 것이라 새 그림이 들지 않는다.
- **크기**: 설정의 [앱 내려받기] 44, `/app` 의 [앱 내려받기] 48(전체 폭) — 44 보다 크니 밀도 예외가 아니다. 새 크기 토큰은 없다(48 은 `--space-48`).
### 구현 때 맞출 것 · 처음 판에서 미룬 것
- 받기와 설치 때 뜨는 창의 글은 브라우저와 제조사마다 다르다. 갤럭시 S23 에서 삼성 인터넷과 Chrome 으로 받기 경고, 알 수 없는 앱 설치 허용, Play 프로텍트 경고를
한 번씩 거쳐 보고 차례 글을 맞춘다(첫 판 구현 때는 아직 하지 않았다 — 맞춘 글은 화면 상수와 이 절을 함께 고친다).
- 서명 키가 다른 판(시험판)이 깔린 휴대폰은 새 판을 덮어 설치할 수 없다 — 그때는 앱을 지운 뒤 설치하고 로그인을 다시 한다.
- 미룬 것: 앱 안에서 받아 바로 설치, 최소 판이 아닌 새 판 알림, 아이폰(Ad Hoc · TestFlight), Google 개발자 인증(2027 전 세계 — 그 전에 등록할지 정한다).
## 구현하며 정한 보완 (2026-10-26 — 캔버스에는 아직 없다)
- 찾지 못한 첨부(보관 시간이 지난 업로드·지워진 원본)는 실패로 표시하고, 전달 원본에는 "빼기"만 둔다(다시 올릴 수 없다).
@@ -1487,3 +1560,17 @@ API 와 서버 규칙은 계약 2절 "실시간 알림".
- **알려진 한계**: 여러 통인 줄의 마지막 메일만 읽으면 다음 맞추기까지 줄이 굵은 채다. 마지막 메일이 아닌, 보내기를 기다리는 메일은 풀린 뒤에도 다음 맞추기까지 고를 수 없다(계약 2절).
분할 보기 머리는 [비우기] 단추가 있는 휴지통 · 스팸메일함, 세 자리 이상 안 읽은 수, "받은편지함"보다 긴 메일함 이름, 1120~1220 폭에서는 1440 에서도 개수 글이 잘린다(title 로 다 보인다).
분할 보기 도구 줄의 "N개 선택됨"은 세 자리부터 두 줄로 넘친다(메일 목록의 "123통 선택됨"도 같다).
### 휴대폰 앱 받기 구현하며 정한 보완 (2026-11-08 — 캔버스에는 아직 없다)
데이터 · 경로는 위 "휴대폰 앱 받기" 머리말(서버와 맞춘 약속 — 계약 1절 "휴대폰 앱 받기 — 사내 배포")과 같다.
- **올린 날**은 다른 설정 화면처럼 브라우저 시간대의 날짜다("2026년 11월 18일" — 2단계 인증의 "…부터 켜짐"과 같은 표기). 대용량 첨부의 끝나는 날(한국 날짜 — `kst-date.ts`)과 달리 직원만 보는 날이다.
크기는 첨부와 같은 표기("31 MB")이고, 읽을 수 없는 값(빈 버전 · 날짜가 아님 · 음수 크기)은 그 칸째 뺀다.
- **받지 못함**은 다른 설정 화면의 불러오기 실패와 같은 모양(`MailLoadError` — 빨간 아이콘 · 사전 문구 · 문의 코드 · [다시 시도])을 칸 자리에 둔다(`/app` 은 카드 자리). 알림은 띄우지 않는다.
받는 동안은 빈 자리(aria-busy)다 — 다른 설정 화면과 같다.
- 앱 정보는 들어올 때마다 새로 받는다(운영자가 새 판을 올렸을 수 있다). 세션이 바뀌면(로그아웃 · 만료 · 다른 계정) 늦게 온 응답을 버리고 받아 둔 값도 지운다.
- **문의 줄**은 로그인 화면의 `LoginSupportLine` 을 그대로 쓴다. `/app` 은 본문이 14 라 문의 줄 글만 13 이다(`size="base"`).
- **앱 그림**의 모서리는 한 변의 1/4 이다(48 은 12, 52 는 13 — 보드 값).
- **폭 기준**: 설정의 QR 줄을 빼는 좁은 폭은 480 이하(공개 내려받기 화면의 좁은 폭과 같은 값), `/app` 의 넓은 화면은 그 반대(481 이상)다.
- 글자 크기 17(`/app` 의 앱 이름 · 받을 앱이 없을 때의 제목)은 토큰 `--font-size-title-md` 를 더했다(크기 · 밀도 예외 토큰은 아니다).