generated from ttipo/project-base
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
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:
@@ -5,7 +5,8 @@
|
||||
|
||||
## 앱 1단계 (2026-11-05 확정 — `App1Login` · `App1MailList` · `App1Reader` · `App1Compose` · `App1Shell`)
|
||||
|
||||
첫 판은 안드로이드 먼저(Play 내부 테스트), 로그인 전체 + 메일 기본 + 새 메일 알림이다. iOS 는 맥 · Apple 개발자 계정을 준비한 뒤.
|
||||
첫 판은 안드로이드 먼저, 로그인 전체 + 메일 기본 + 새 메일 알림이다. iOS 는 맥 · Apple 개발자 계정을 준비한 뒤.
|
||||
나눠 주는 길은 사내 직접 배포다(2026-11-08 — 스토어에 올리지 않는다): 우리 키로 서명한 APK 를 웹메일에 로그인한 직원이 포털의 휴대폰 앱 받기 화면(`<포털>/app` — 웹 보드 `SettingsMobileApp`)에서 받는다. 빌드 · 서명 · 올리기는 `flutter/README.md` "사내 배포".
|
||||
|
||||
### 크기 · 글꼴 (웹과 다른 점)
|
||||
- 누르는 곳 48(웹 44). 예외는 받는 사람 칩의 ✕ 40 하나(밀도 예외 — 사용자 확정).
|
||||
@@ -19,7 +20,8 @@
|
||||
- 탭마다 이동 기록을 따로 지킨다(메일에서 읽던 화면이 설정 탭에 다녀와도 남는다). 읽기 · 쓰기는 탭 막대 없는 전체 화면이다.
|
||||
- 여러 통 고르기 중에는 탭 막대를 숨긴다(잠깐의 상태이고, 다른 탭으로 가면 고른 것을 잃는다). ✕ · 뒤로 가기로 나오면 다시 보인다.
|
||||
- 화면 낭독기 이름은 "앱 메뉴".
|
||||
- **설정 탭**: 내 정보(이름 · 주소) · 알림(새 메일 알림 켬/끔) · 앱 정보(앱 버전 · 오픈소스 라이선스) · 문의 연락처 · [로그아웃]. 비밀번호 · 2단계 인증 같은 계정 설정은 웹에서 한다.
|
||||
- **설정 탭**: 내 정보(이름 · 주소) · 알림(새 메일 알림 켬/끔) · 앱 정보(앱 버전 · [새 버전 받기] · 오픈소스 라이선스) · 문의 연락처 · [로그아웃]. 비밀번호 · 2단계 인증 같은 계정 설정은 웹에서 한다.
|
||||
[새 버전 받기](2026-11-08 — `App1Push` ④ 앱 정보 절)는 끝 아이콘이 바깥으로 여는 아이콘이고, 업데이트 화면의 단추처럼 포털의 휴대폰 앱 받기 화면을 브라우저로 연다(같은 실패 알림).
|
||||
[로그아웃]은 데이터를 지우는 일이 아니라 회색 테두리 단추이고, 누르면 확인 창(처음 초점 [취소]) — 로그아웃하면 이 휴대폰의 새 메일 알림도 오지 않는다고 알린다.
|
||||
|
||||
### 로그인 (`App1Login`) — 네 단계
|
||||
@@ -248,7 +250,7 @@
|
||||
(2차) 자판과 함께 시트가 올라가고 초점 칸이 보이게 스크롤되는지 · 가로 화면 + 자판, 기간 메뉴가 뜨는 자리, 날짜 창의 한국어 머리와 연필(입력) 모드에서 오늘 뒤 날짜를 막는지,
|
||||
뒤로 가기 · 끌어 닫기 · 가림막에서 고친 값을 버리는지와 닫은 뒤 자판, TalkBack([조건] 펼침 · 기간 단추 · 날짜 칸 값과 오류 · 단추 줄 위 알림 · 닫은 뒤 초점), 큰 글자의 날짜 칸 · 여러 줄 조건 줄,
|
||||
바뀐 가림막 색(옮기기 시트 포함), 14단계 서버에서 조건만 · 검색어 + 조건(시간대 · 첨부) · from/to 를 실은 강조.
|
||||
### 새 메일 알림 · 업데이트 · 앱 아이콘 (`App1Push` — 2026-11-07 확정)
|
||||
### 새 메일 알림 · 업데이트 · 앱 아이콘 (`App1Push` — 2026-11-07 확정, ⑩ 업데이트 화면 · ④ 앱 정보 절은 2026-11-08 사내 직접 배포로 고쳐 확정 — 캔버스 v68)
|
||||
- **알림 글은 "새 메일 N통"뿐**이다 — 보낸 사람 · 제목은 Google 서버를 거치지 않게 싣지 않는다(잠금 화면도 수만). 새 메일이 더 오면 같은 알림의 수만 바뀌고 쌓이지 않는다.
|
||||
앱을 보고 있을 때는 띄우지 않는다 — 받은편지함이 보이면 그 자리에서 바로 새 메일을 확인한다. 앱으로 돌아오면 알림창의 새 메일 알림을 지운다.
|
||||
누르면 늘 메일 탭의 받은편지함이다(읽기 · 쓰기 화면 위에 있었어도 — 쓰던 메일은 임시보관함에 마저 저장하고 알린다). 알림 데이터에는 모듈 이름과 종류만 싣는다.
|
||||
@@ -279,20 +281,25 @@
|
||||
- **앱 아이콘**(⑪ — 나안): 남색 #0B3640 바탕 + 흰 ARA 마크. 적응형(배경색 · 앞면 마크 · 안드로이드 13 테마 아이콘용 마크만 담은 그림 한 장 더)이고 마크는 잘리지 않는 가운데 칸 안에 둔다.
|
||||
안드로이드 7(적응형 전)의 아이콘도 같은 모양이다. 삼성 휴대폰은 알림창에도 앱 아이콘을 쓴다.
|
||||
- **업데이트 화면**(⑩): 이 앱의 판이 서버가 정한 최소 판보다 낮으면 로그인 전이든 메일을 보던 중이든 이 화면으로 온다(쓰던 메일은 임시보관함에 저장한다). 뒤로 가기는 앱을 닫는다.
|
||||
[Play 스토어 열기]는 Play 스토어 앱부터, 열리지 않으면 웹 주소. 문의 줄은 로그인 화면과 같은 연락처다. 서버가 최소 판을 내리면 다음 확인(앱으로 돌아올 때 — 1시간마다)에서 원래 화면으로 돌아간다.
|
||||
[새 버전 받기](2026-11-08 사내 직접 배포로 [Play 스토어 열기]를 바꿨다 — 캔버스 v68)는 포털의 휴대폰 앱 받기 화면 `<포털 출처>/app` 을 휴대폰의 브라우저로 연다.
|
||||
포털 출처는 앱의 API 주소(`.env` 의 `API_URL`)에서 `/api` 를 뗀 출처다(같은 주소 배포 — 앱 설정 계약은 받는 곳 주소를 싣지 않는다). 받기 화면은 브라우저의 웹메일 로그인을 쓴다(앱의 토큰은 넘기지 않는다).
|
||||
브라우저를 열지 못하면 "브라우저를 열지 못했습니다. 휴대폰의 브라우저에서 {주소}에 들어가 새 버전을 받아 주세요."(주소는 스킴 없이 — `portal.arasystem.it.kr/app`). 설정 › 앱 정보의 [새 버전 받기]도 같은 화면 · 같은 알림이다.
|
||||
문의 줄은 로그인 화면과 같은 연락처다("새 버전을 받거나 설치하기 어려우신가요?"). 서버가 최소 판을 내리면 다음 확인(앱으로 돌아올 때 — 1시간마다)에서 원래 화면으로 돌아간다.
|
||||
- **로그아웃**: 확인 창이 이 휴대폰에 새 메일 알림도 오지 않는다고 알린다(`App1Shell`). 토큰을 지운 뒤 서버 logout 과 함께 이 휴대폰의 알림을 멈춘다(알림창 지우기 · FCM 등록 풀기 — 서로 기다리지 않는다).
|
||||
서버 줄은 서버가 세션과 함께 지운다. 끊긴 망이라 FCM 등록을 풀지 못했으면 다음 실행 · 다음에 앱으로 돌아올 때 다시 풀고, 그사이 같은 휴대폰에서 다른 직원이 로그인하면 그 등록이 이어받는다(같은 FID 로 올려 서버의 앞 직원 줄이 지워진다).
|
||||
- 글자는 테마 크기로 맞췄다(15.5 → 16 · 14 → 15 · 13.5 → 13 · 13 → 13 — 위 「쓰기」와 같은 정리).
|
||||
- **구현하며 정한 것**(2026-11-07 — 보드에 없는 글 · 경우)
|
||||
- 권하기 줄의 [알림 켜기]를 눌렀는데 새 메일 카테고리만 꺼져 있으면: "휴대폰 설정에서 새 메일 알림이 꺼져 있습니다." + [설정 열기](막혔을 때와 같은 꼴).
|
||||
- 끄는 중의 화면 낭독기 글 "끄는 중"(보드에는 켜는 중만 있다).
|
||||
- 문의 연락처를 정하지 않았거나 받지 못했을 때: 설정 › 문의 "앱이나 계정 문제로 도움이 필요하면 관리자에게 문의해 주세요." · 업데이트 화면 "Play 스토어에 업데이트가 보이지 않나요? 관리자에게 문의해 주세요."(로그인 화면의 문의 줄과 같은 꼴).
|
||||
- 문의 연락처를 정하지 않았거나 받지 못했을 때: 설정 › 문의 "앱이나 계정 문제로 도움이 필요하면 관리자에게 문의해 주세요." · 업데이트 화면 "새 버전을 받거나 설치하기 어려우신가요? 관리자에게 문의해 주세요."(로그인 화면의 문의 줄과 같은 꼴 — 2026-11-08 사내 직접 배포 글로).
|
||||
- 받기 화면 주소를 만들지 못했을 때(앱의 API 주소가 http · https 주소가 아니다 — 그런 판은 서버에 닿지 못해 이 두 화면에 오지 않는다): [새 버전 받기]는 브라우저를 열지 않고 "브라우저를 열지 못했습니다. 관리자에게 문의해 주세요."(2026-11-08).
|
||||
- 서버가 보낼 수 있는지 아직 모르면(앱 설정을 받기 전 · 받지 못함) 쓸 수 있는 것으로 본다 — 막지 않는다. 앱 설정은 앱을 띄우자마자 세션 되살리기와 나란히 묻는다.
|
||||
- 알림의 N 은 앱을 마지막으로 본 때부터 센다 — 앱으로 돌아올 때 · 앱을 보는 중 새 메일 알림을 받을 때 "봤음"을 보낸다(30초에 한 번 · 기다리는 동안 뒤로 가면 그 자리에서). 보는 중에 받은 알림 뒤의 봤음은 받은 때부터 1.5초가 지나기 전에는 보내지 않는다(30초 간격 끝이 더 늦으면 그때 — 그사이 뒤로 가면 그 자리에서 보내 같은 초가 될 수 있다, 받아들였다)(2026-11-08 실기기 — 서버는 봤음의 초와 같은 초에 받은 메일도 세는데 받은 시각이 초 단위라, 곧바로 보내면 열지 않은 그 메일이 다음 알림에 다시 세어져 한 통이 더 많았다).
|
||||
- **실기기 · 실제 Firebase 에서 볼 것**: FID 등록 소식(`onRegistered`)이 자동 초기화를 끈 채 오는지 · 앱 데이터를 지운 뒤 새 FID, 같은 알림을 바꿔 쓰는지(태그 `new-mail`)와 앱으로 돌아올 때 지워지는지,
|
||||
앱을 보는 중에는 시스템 알림이 뜨지 않는지, 뒤에 있을 때 · 꺼져 있을 때 누른 알림이 받은편지함을 여는지, 두 번 거절한 뒤 스위치가 [설정 열기] 쪽으로 바뀌는지,
|
||||
카테고리만 끈 상태를 읽는지 · [설정 열기]가 그 카테고리를 여는지(삼성 · Pixel), 상태 표시줄의 작은 아이콘 굵기 · 삼성 알림창의 앱 아이콘 · 테마 아이콘, 절전(Doze) 중 도착 시간,
|
||||
Play 스토어가 없는 휴대폰의 [Play 스토어 열기], TalkBack(권하기 줄 묶음 · 스위치 이름과 설명 · 켜는 중 알림).
|
||||
[새 버전 받기]가 여는 브라우저(삼성 인터넷 · Chrome — 웹메일에 로그인하지 않은 브라우저면 로그인 뒤 받기 화면으로 돌아오는지)와 받은 APK 를 쓰던 앱 위에 설치했을 때 로그인 · 알림 설정이 남는지,
|
||||
TalkBack(권하기 줄 묶음 · 스위치 이름과 설명 · 켜는 중 알림).
|
||||
- 2026-11-08 갤럭시 S23(Android 16)에서 본 것: 등록 소식(자동 초기화를 끈 채) · 같은 알림 바꿔 쓰기와 앱으로 돌아올 때 지우기 · 보는 중에는 그리지 않음 · 뒤에 있을 때와 꺼져 있을 때 누르면 받은편지함 · 작은 아이콘(잘 보인다) · 홈 화면 배지 · 바꿔 쓴 알림도 다시 소리를 냄 · 로그아웃 때 FCM 풀기(서버 줄은 서버가 지운다). 서버 쪽은 계약 1절 "실기기 · 실제 키로 확인할 것".
|
||||
- 알려진 한계(2026-11-08): FID 로 등록하는 길은 Google Play 서비스 26.12(versionCode 261200000) 이상에서만 탄다 — 그보다 옛 휴대폰은 SDK(firebase-messaging 26.0.1)가 옛 토큰 길로 돌아가 등록 소식(`onRegistered`)의 값이 FID 가 아닐 수 있고, 서버는 FID 모양만 받으므로 켜기가 실패할 수 있다(아직 고치지 않았다 — 등록에 드는 매니페스트 표시는 flutter/README "새 메일 알림").
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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` 를 더했다(크기 · 밀도 예외 토큰은 아니다).
|
||||
|
||||
@@ -289,6 +289,41 @@
|
||||
누르면 늘 메일 탭 받은편지함이다 — `kind` 로 가르지 않는다(`session-ended` 를 눌렀으면 받은편지함의 첫 요청이 세션 끝을 알아 로그인 화면으로 간다).
|
||||
작은 아이콘 · 색은 서버가 싣지 않는다 — 앱 매니페스트의 기본값(`ic_stat_ara` · #0E7C86)이고, 채널 `new_mail`(이름 "새 메일" · 중요도 높음)은 앱이 만든다.
|
||||
|
||||
### 휴대폰 앱 받기 — 사내 배포 (2026-11-08 정함 · 서버)
|
||||
|
||||
> 2026-11-08 사용자가 정했다 — 앱은 스토어(Play 내부 테스트) 없이 **직접 빌드해 서명한 APK 를 포털에 로그인한 직원에게만** 나눠 준다.
|
||||
> 받는 곳은 웹의 설정 › 계정 › 「휴대폰 앱」과 휴대폰 받기 화면 `/app`(보드 `docs/design/webmail/SettingsMobileApp.dc.html`), 앱의 업데이트 화면 · 설정 › 앱 정보 「새 버전 받기」는 바깥 브라우저로 `<포털>/app` 을 연다.
|
||||
> 위 app-config 계약은 그대로다(받는 곳 주소를 싣지 않는다 — 앱이 API_URL 에서 `/api` 를 뗀 출처로 만든다). 서버는 `modules/app-releases` 가 이 절대로 만들었다.
|
||||
|
||||
| 경로 | 응답 |
|
||||
| --- | --- |
|
||||
| `GET /api/app-releases/android/latest` | 200 `{ version: "1.0.0", sizeBytes: 32505856, releasedAt: "2026-11-18T01:23:45.000Z" }` · `Cache-Control: private, no-store` / 404 `RES_001` — 올린 판이 없다 |
|
||||
| `GET /api/app-releases/android/latest/file` (HEAD 도) | 200 APK 바이트 (Range 206 · 416) / 404 `RES_001` — 올린 판이 없다 · 그 파일이 없다 / 429 `REQ_001` — 앞단 동시 연결 상한 |
|
||||
|
||||
- 두 경로 모두 로그인 필요(웹 쿠키 · 앱 Bearer — `JwtAuthGuard`), 없으면 401 `AUTH_001`. 제한된 세션(6절)은 403 `AUTH_010`(허용 목록에 넣지 않는다). 요청 수는 기본 한도(경로마다 IP 당 1분 100회). 새 오류 코드는 없다.
|
||||
- **최신** = 가장 늦게 올린 판(올린 시각 차례 — 버전 번호가 아니다). 옛 APK 를 다시 올리면 그것이 최신이 된다(되돌리기 길 — 단 더 높은 판을 이미 깐 휴대폰은 안드로이드가 낮은 판으로 덮어 깔지 않는다).
|
||||
`releasedAt` 은 올린 때(ISO 8601 UTC), `sizeBytes` 는 파일 바이트 수다.
|
||||
- **파일 응답**은 대용량 첨부 받기(4절 "파일 응답")와 같은 머리글이고 형식만 APK 다 — 서버 `shared/utils/download-headers.ts` 한 곳:
|
||||
`Content-Type: application/vnd.android.package-archive` · `Content-Disposition: attachment; filename="ARA-SYSTEM-<버전>.apk"` · `X-Content-Type-Options: nosniff` ·
|
||||
`Content-Security-Policy: default-src 'none'; sandbox` · `Cache-Control: private, no-store` · `Referrer-Policy: no-referrer` · `X-Robots-Tag: noindex` · `Accept-Ranges: bytes` ·
|
||||
강한 `ETag: "<판 id>"` · `Last-Modified`(올린 때) · `X-Accel-Buffering: no`. Range 하나면 206, 여럿 · 형식 오류면 200 전체, 처음이 크기 이상이면 416(본문 없음 — `Content-Range: bytes */<크기>`),
|
||||
`If-Range` 가 ETag · Last-Modified 와 다르면 200 전체. HEAD 는 머리글만. 실패는 머리글을 쓰기 전에 표준 실패 형식(JSON)이다.
|
||||
파일 이름 · 경로는 서버가 만든 값뿐이다 — 내려받는 이름은 검사한 버전으로 만들고, 디스크의 이름은 UUID 다.
|
||||
- 표에는 판이 있는데 볼륨에 파일이 없거나 크기가 표와 다르면 파일은 404 `RES_001`(정보는 200 그대로)이고 서버가 error 로그를 남긴다 — 운영자가 다시 올린다(운영 도구 「휴대폰 앱 목록」이 "파일 없음"을 보인다).
|
||||
"올린 판이 없음"의 404 는 화면이 설정을 열 때마다 묻는 예상된 상태라 서버 로그는 debug 다.
|
||||
- **동시 연결 상한(앞단 nginx)**: 파일 `…/file`(HEAD 포함)은 대용량 첨부의 앱 안 받기와 같은 그릇으로 센다 — 클라이언트 주소(`X-Forwarded-For` 의 마지막 칸)마다 30 · 서버 전체 200,
|
||||
처리 중인 요청만(4절 "운영 확인"). 넘으면 백엔드에 닿기 전에 429 `REQ_001`(nginx 오류 JSON — `message` 는 빈 값, `traceId` 는 nginx 요청 ID). 정보 경로는 세지 않는다.
|
||||
앞단은 이 응답만 버퍼링 · 압축을 끄고 `X-Accel-Buffering: no` 를 외부 프록시로 넘긴다(느린 휴대폰마다 APK 사본을 임시 파일에 쌓지 않게). 경로는 대소문자 · `%2F` · 겹 `/` 를 가리지 않고 같은 설정을 탄다.
|
||||
- **올리기 — API 가 없다**(서버 B 의 운영 도구 「휴대폰 앱 올리기」, 6절 "운영 도구" — 차례는 mail/README "휴대폰 앱 올리기"):
|
||||
운영자가 `sudo docker cp` 로 백엔드 컨테이너 안에 넣은 APK → 검사(일반 파일 · 이름 `.apk` · zip 머리 `PK\x03\x04` · 1KB~200MB) · SHA-256 → 버전(`숫자.숫자.숫자` — 위 앱 알림 등록의 `appVersion` 과 같은 규칙,
|
||||
app-config 의 최소 판보다 낮으면 올리지 않는다) → 업로드 볼륨(`UPLOAD_DIR`) 아래 `app-releases/<UUID>.apk`(임시 이름에 쓰고 디스크에 내려쓴 뒤 이름을 바꾼다 — 쓰면서 다시 센 SHA-256 이 다르면 올리지 않는다)
|
||||
→ 표 `app_releases` 에 한 줄 → 실행 기록(`ops_audit_log` — `app-release-upload`). 파일은 매일 백업(업로드 볼륨)에 든다.
|
||||
- 표 `app_releases`(마이그레이션 `AddAppReleases`): id(UUID — ETag) · platform(지금 `android`) · version · size_bytes · sha256(소문자 16진수) · file_key(UUID — 디스크 이름) · uploaded_by(운영자 이름) · created_at(올린 때).
|
||||
지우는 명령은 없다 — 옛 판은 표와 볼륨에 남는다. 직원 주소로 나뉜 표가 아니라 퇴사 마무리와 상관없다.
|
||||
- **클라이언트 — 웹**: 404 `RES_001` 은 "아직 받을 수 있는 앱이 없습니다" 칸으로(알림 끔). 내려받기는 첨부처럼 이동 전에 세션을 확인한다(`confirmSession()`).
|
||||
- **클라이언트 — 앱**: 이 API 를 부르지 않는다 — 바깥 브라우저로 `<포털>/app` 을 연다.
|
||||
- **이 단계에서 하지 않는 것**: 앱 안에서 받아 바로 설치(REQUEST_INSTALL_PACKAGES), 최소 판이 아닌 새 판 알림, 아이폰(Ad Hoc · TestFlight), 올린 판 지우기.
|
||||
|
||||
---
|
||||
|
||||
## 2. 메일 (모두 로그인 필요 — 없으면 401 `AUTH_001`)
|
||||
@@ -2355,11 +2390,12 @@ interface FileUploadCreate {
|
||||
|
||||
### 운영 확인 (이 계약이 기대하는 것 — 고치는 일은 release-ops)
|
||||
|
||||
- 앞단 nginx: `/api/file-uploads` 본문 17MB · 읽기 90초. `/api/public-links/` · `/api/messages/*/large-files/` 는 응답 버퍼링을 끄고 `X-Accel-Buffering` 을 외부로 넘긴다
|
||||
- 앞단 nginx: `/api/file-uploads` 본문 17MB · 읽기 90초. `/api/public-links/` · `/api/messages/*/large-files/` · 휴대폰 앱 받기 `/api/app-releases/*/file`(1절 "휴대폰 앱 받기")은 응답 버퍼링을 끄고 `X-Accel-Buffering` 을 외부로 넘긴다
|
||||
(기본값이면 느린 받는 사람마다 임시 파일을 1GB 까지 만든다 — 실시간 알림과 같은 방법). `/s/` 는 `index.html` + `Cache-Control: no-cache` · `X-Robots-Tag: noindex, nofollow` · `Referrer-Policy: no-referrer`.
|
||||
접근 로그의 경로 토큰 가리기, 공개 호스트의 server(위 "공개 호스트").
|
||||
**내려받기 동시 연결 상한**(2026-10-30 리뷰 — 버퍼링을 끈 느린 받기는 연결 하나가 nginx 연결 둘과 백엔드 fd 를 잡아, 토큰 하나로 프론트 nginx 를 바닥낼 수 있다): `limit_conn` 으로
|
||||
공개 받기 IP 마다 10 · 앱 안 받기 IP 마다 30(사무실 NAT) · 서버 전체 공개 받기 400 · 앱 안 받기 200, 넘으면 429 `REQ_001`(nginx 오류 JSON). `worker_connections` 는 이 수와 SSE · 포털을 함께 버티게.
|
||||
공개 받기 IP 마다 10 · 앱 안 받기 IP 마다 30(사무실 NAT) · 서버 전체 공개 받기 400 · 앱 안 받기 200, 넘으면 429 `REQ_001`(nginx 오류 JSON).
|
||||
휴대폰 앱 받기(`/api/app-releases/*/file` — 1절 "휴대폰 앱 받기")는 앱 안 받기와 같은 그릇으로 센다(둘 다 로그인한 직원의 받기 — 합쳐 IP 마다 30 · 전체 200). `worker_connections` 는 이 수와 SSE · 포털을 함께 버티게.
|
||||
전체 상한을 경로마다 따로 둔다(2026-10-30 리뷰 — 처음 판은 두 경로가 500 을 함께 써, 바깥 IP 50개(IPv6 면 /64 하나)로 직원의 앱 안 받기가 모두 429 가 됐다).
|
||||
경로의 대소문자 변형(`/FILES/` · `/api/Messages/…`)도 같은 상한 · 같은 흘려보내기를 탄다(2026-10-30 리뷰 — 처음 판은 글자 하나로 상한 · `X-Accel-Buffering` 넘김을 비켜 갔다).
|
||||
구현(2026-10-30): 클라이언트 주소는 `X-Forwarded-For` 의 마지막 칸(외부 프록시가 이어 붙인 값 — 없으면 접속 주소). 워커마다 `worker_connections` 4096 · 열린 파일 8192.
|
||||
@@ -2873,7 +2909,7 @@ interface FileUploadCreate {
|
||||
- 실행 기록 표(`ops_audit_log`): 언제 · 누가 · 명령 · 대상 · 결과. 비밀은 남기지 않는다.
|
||||
- 표준 입력이 터미널(TTY)이 아니면 운영자 이름 · 비밀번호를 묻기 전에 멈춘다(`docker exec -it` 로 실행). 입력 기록(↑)에 비밀번호를 남기지 않는다.
|
||||
자동 시험만 `OPS_ALLOW_NO_TTY=1` 로 파이프 입력을 받는다 — 사람은 쓰지 않는다.
|
||||
- 명령
|
||||
- 명령 (열세 개 — 메뉴 차례 그대로. 12 · 13 은 2026-11-08 사내 배포 때 더했다)
|
||||
|
||||
| 명령 | 하는 일 |
|
||||
| --- | --- |
|
||||
@@ -2888,6 +2924,8 @@ interface FileUploadCreate {
|
||||
| 현황 | 직원마다 잠금 · 2단계 · 앱 비밀번호 수 · 마지막 로그인(웹메일 · 앱 — 2026-11-06 부터 앱도 센다) · 메일함 사용량 · 이전 중(끝나는 날) |
|
||||
| 정책 · 설정 | 2단계 필수(켜기 · 시작일 · 예외), 문의 연락처 |
|
||||
| 메일 이전 (2026-11-02 더함) | 이전 현황(이전 중인 계정 · 끝나는 때 · 메일함 사용량) · 이전용 비밀번호 다시 만들기(한 사람 또는 이전 중인 계정 모두 — 잃었거나 30일을 늘릴 때) · 이전 마치기(한 사람 또는 이전 중인 계정 모두 → 직원용 임시 비밀번호) |
|
||||
| 휴대폰 앱 올리기 (2026-11-08 더함) | 운영자가 `docker cp` 로 백엔드 컨테이너에 넣은 APK 경로 → 검사(일반 파일 · `.apk` · zip 머리 · 1KB~200MB) · SHA-256 → 버전(파일 이름의 `x.y.z` 가 기본값 · app-config 의 최소 판보다 낮으면 하지 않음 · 지금 최신보다 낮거나 같으면 y/N) → 바뀔 것(버전 · 크기 · SHA-256) y/N → 업로드 볼륨 `app-releases/` 에 임시 이름 → 이름 바꾸기 → 표 → 실행 기록. 끝에 SHA-256 을 한 번 더 보인다(빌드한 파일과 견준다). 실패하면 반쯤 쓴 파일을 남기지 않는다 — 1절 "휴대폰 앱 받기" |
|
||||
| 휴대폰 앱 목록 (2026-11-08 더함) | 최근 판 10개 — 버전 · 크기 · 올린 때 · 올린 이 · SHA-256 앞부분, 맨 위가 직원이 받는 판, 볼륨에 파일이 없으면 "파일 없음"(읽기만 — 기록하지 않는다) |
|
||||
|
||||
- 차례가 있는 명령
|
||||
- 비밀번호 재설정: 앱 비밀번호 지우기(대리 접근) → 웹메일 · 앱 세션 끊기(DB) → 알림 메일 넣기(대리 접근) → 엔진에 임시 비밀번호 + 만료 → 세션 한 번 더 끊기.
|
||||
|
||||
Reference in New Issue
Block a user