# tdc114plus 화면별/기능별 정책

작성일: 2026-07-07
상태: v1.3

목적: `tdc114plus` 앱의 주요 화면과 기능이 어떤 규칙으로 동작해야 하는지 화면 단위로 고정한다. 구현 변경 시 이 문서를 기준으로 화면 UX와 상태 규칙을 판단한다.

운영 원칙:

- 앱 화면/기능을 수정하기 전에는 반드시 이 문서를 먼저 확인한다.
- 수정 요청이 이 문서의 정책과 일치하는지 먼저 비교한다.
- 문서 정책과 충돌하는 작업이 필요하면, 충돌 항목과 변경 영향 범위를 사용자에게 상세히 설명하고 명시 허락을 받은 뒤 진행한다.
- 정책 변경이 확정된 뒤 구현을 바꾸는 경우, 정책 문서를 먼저 또는 같은 작업 단위 안에서 함께 갱신한다.
- APK 빌드/실기기 설치는 사용자의 명시 허락 후 진행한다.

관련 문서:

- `docs/00_policy_tdc114plus_development_2026-07-02.md`
- `docs/00_contract_tdc114plus_api_2026-07-02.md`
- `docs/policy_android_app_install_execution_2026-07-06.md`

## 1. 공통 원칙

- Baron SSO 로그인/세션 결과를 임의로 변형하지 않는다.
- 가족사/조직 탐색은 Baron SSO `tenant` 계층을 기준으로 진행한다.
- 직원검색 기본 첫 화면은 `본인 회사급 범위`를 기준으로 한다.
- 사용자가 명시적으로 조직 탐색을 시작했을 때만 `전체 -> 회사 -> 하위조직 -> 개인` 단계형 탐색으로 전환한다.
- 실제 API가 화면 정책을 충족하지 못하는 경우, fallback 동작을 문서화하고 필요한 API 보강 이슈를 별도로 작성해 병행 진행한다.
- 화면 정책이 바뀌면 widget test와 정책 문서를 함께 갱신한다.

## 2. AuthGate 화면 정책

대상:

- 앱 첫 진입
- 저장 세션 확인

규칙:

- 저장된 유효 세션이 있으면 `직원검색`으로 이동한다.
- 저장 세션이 없으면 `Baron SSO 로그인` 화면으로 이동한다.
- smoke/manual 실행에서 preauth session seed가 있으면 해당 세션을 그대로 사용한다.

## 3. 로그인 화면 정책

대상:

- `Baron SSO 로그인`

규칙:

- 신규 앱 기본 로그인 UX는 `전화번호 입력 -> 바론SSO 로그인 링크 발송 -> 문자 링크 클릭 -> 승인 완료 -> 앱으로 복귀 -> 앱 진입`이다.
- 로그인 화면은 앱 내부에 전화번호 입력창을 제공한다.
- 로그인 화면은 Baron SSO 화면과 유사한 어두운 카드형 디자인을 사용한다.
- 로그인 화면 상단 중앙에는 `TDC114PLUS` 타이틀을 명확하게 노출한다.
- 전화번호 입력은 `010-0000-0000` 형식의 입력/표현을 기본으로 한다.
- 안내 문구는 짧고 명확하게 유지한다.
- 로그인 화면 본문에서는 `입력하신 전화번호로 바론SSO 로그인 링크가 발송됩니다.` 문구를 중복 노출하지 않는다.
- 로그인 화면은 문자 승인 후 앱으로 돌아오면 자동 로그인된다는 흐름을 우선 안내한다.
- 전화번호 입력 중 키보드가 열려도 `로그인 링크 보내기` 버튼은 사용자가 바로 누를 수 있도록 화면에 계속 노출한다.
- 앱은 링크 발송 후 pending 상태를 유지하며 poll로 승인 완료를 확인한다.
- Baron SSO 공식 정책상 `/api/v1/auth/headless/link/init`은 승인 완료 후 redirect URL 필드를 지원하지 않는다.
- Baron SSO 공식 정책상 문자 링크를 클릭한 모바일 브라우저는 verify-only approver로 동작하며, 승인 완료 후 `sso.hmac.kr/ko/verify-cc` 또는 승인 완료 화면에 머무른다.
- 따라서 앱 화면은 `문자 링크 승인 후 TDC114PLUS 앱으로 돌아오면 자동 로그인됩니다` 흐름을 명확히 안내한다.
- 사용자가 앱으로 돌아오면 앱은 저장된 pendingRef로 poll을 재개하고, 승인 완료가 확인되면 직원검색 화면으로 자동 이동해야 한다.
- 사용자가 문자 링크를 클릭했을 때 앱이 바로 열리려면 Baron SSO 승인 완료 페이지가 앱 링크 또는 callback URL로 redirect하는 별도 정책/기능을 추가해야 한다.
- Baron SSO가 `sso.hmac.kr/ko/verify-cc` 승인 완료 화면에 머무르는 경우, 앱 단독 코드만으로 Chrome을 자동으로 앱으로 전환할 수 없음을 정책상 명확히 둔다.
- OIDC PKCE Hosted Login은 현재 기본 운영 UX가 아니며, 다시 적용하려면 별도 정책 변경 승인이 필요하다.
- 로그인 실패 메시지는 사용자 존재 여부를 과도하게 노출하지 않는다.

## 4. 직원검색 화면 정책

대상:

- `직원검색`
- 상단 조직 칩
- 직원/조직도/즐겨찾기 segment

### 4.1 초기 기본값

- 초기 조회 범위는 로그인 사용자의 `회사급 tenantSlug`
- 단, 초기 선택 상태는 가능하면 로그인 사용자의 `본인 팀 뱃지`가 실제 선택되도록 맞춘다.
- 초기 화면은 직원 목록 중심으로 시작
- 앱 최초 진입 시에는 조직 단계 표현에 필요한 상위 조직/내 팀 정보를 먼저 확보한다.
- 최초 직원 목록은 가족사 전체가 아니라 `내 팀` 또는 현재 초기 선택 scope에 필요한 인원만 조회한다.
- 초기 진입 또는 상단 뱃지 선택 시 직원 조회가 가족사 전체 조회로 새지 않도록 한다.
- 하위 조직을 표시해야 하는 단계에서는 기본 직원 목록 조회를 실행하지 않는다. 단, 선택 조직의 직접 소속 인원을 `직접 소속` 구역에 표시하기 위한 제한 조회는 허용한다.
- 자식 조직이 없는 leaf 조직 또는 검색 결과 표시가 필요한 상태에서만 직원 목록을 조회한다.
- 초기 진입 직후 phone/name 재조회로 개인 1명 또는 하위 팀 1개로 자동 축소하지 않음
- 초기 상단 칩에는 로그인 사용자의 `본인 팀 뱃지`를 함께 고정 노출한다.
- 본인 팀 뱃지는 세션의 `department`와 tenant 계층의 조직명을 우선 매칭해 결정한다.
- tenant 계층에 본인 팀 노드가 없더라도, 세션의 `department` 문자열로 본인 팀 뱃지를 synthetic chip 형태로 유지한다.

### 4.2 상단 칩 규칙

- 본인 소속 팀 칩이 있으면 상단 칩의 가장 앞에 노출한다.
- 항상 `전체` 칩이 존재한다.
- `전체` 칩은 본인팀 칩 다음 순서에 노출한다.
- 회사급 칩은 항상 노출한다.
- 본인 소속 팀 칩은 빠른 재선택용으로 유지한다.
- 다른 회사 칩을 선택한 뒤에도 본인 팀 칩은 사라지지 않는다.
- 현재 선택된 하위조직은 칩에서 사라지지 않아야 한다.
- 현재 선택된 칩은 비선택 칩보다 더 강한 배경색과 외곽 표현으로 즉시 구분 가능해야 한다.
- 상단 칩 수가 화면 폭을 넘으면 가로 스크롤로 계속 탐색 가능해야 한다.
- 상단 칩 overflow는 줄바꿈보다 `한 줄 가로 스크롤 + 스크롤 가능 인지성`을 우선한다.

### 4.3 `<전체>` 클릭 규칙

`<전체>`를 클릭하면 단계형 조직 탐색 모드로 전환한다.

진행 규칙:

1. `전체` 선택
   - 가족사/회사 목록 표시
2. 회사 선택
   - 해당 회사의 바로 아래 하위조직 목록 표시
3. 하위조직 선택
   - 자식 조직이 있으면 그 다음 하위조직 목록 표시
   - 자식 조직이 없으면 해당 조직 소속 직원 목록 표시
   - leaf 조직이면 해당 조직 소속 직원 목록으로 진입
4. breadcrumb
   - `전체 > 회사 > 하위조직` 경로를 클릭 가능하게 유지

즉, 상단 칩은 단순 필터가 아니라 조직 탐색 진입점 역할을 겸한다.

추가 표현 규칙:

- 상단 회사/고정팀 칩 영역과 하위 경로 칩 영역 사이에는 시각적 구분선을 둔다.
- breadcrumb 영역과 segment 사이의 구분 표식은 선 중심으로 단순하게 유지하고, 중앙 강조 아이콘은 기본 표현으로 두지 않는다.
- breadcrumb에서 현재 선택 중인 하위조직 칩은 비선택 칩보다 더 눈에 띄는 아이콘과 색으로 구분한다.
- leaf 조직 선택 후 직원 목록 진입은 조직명 문자열 비교보다 `선택 tenantSlug` 기준 조회를 우선한다.
- 하위조직 카드의 인원 수는 API 원문의 `memberCount`만 그대로 쓰지 않고, 해당 하위조직 자신과 모든 descendant 조직에 소속된 인원을 합산한 `subtree 인원 수`로 표시한다.
- 선택한 조직 아래에 자식 조직이 하나라도 있으면 하위조직 목록을 우선 표시한다.
- 선택한 조직에 직접 소속 인원이 있으면 하위조직 목록과 섞지 않고 별도 구역으로 표시한다.
- 직접 소속 별도 구역의 제목은 사용자가 현재 선택한 조직을 알 수 있도록 `{선택 조직명} 조직 관리자` 형식으로 표시한다.
- 직접 소속 별도 구역은 하위조직 목록보다 위에 표시한다.
- `디비전장`, `센터장` 등 상위 조직에 직접 매핑되고 하위 팀/부서 소속값이 비어 있는 인원은 `직접 소속` 구역에서 누락 없이 노출한다.
- 자식 조직이 없는 leaf 조직에 도달했을 때만 해당 조직에 직접 소속된 직원 목록을 표시한다.
- 하위조직 목록을 표시하는 동안에는 하위조직 전체 직원 목록을 섞어 표시하지 않는다. 단, 선택 조직에 직접 매핑된 인원 확인을 위한 제한 조회는 허용한다.
- leaf 조직의 직접 소속 인원이 0명이면 `검색 결과 없음` 표시가 가능하다. 단, 이 경우 실제 Baron SSO 조직 데이터상 해당 leaf에 members가 없는지 확인해야 한다.
- 다만 실제 API가 하위 tenant subtree를 제공하지 않는 구간에서는, 회사 하위 상세 트리는 완전 복원하지 못할 수 있으며 이 경우 본인 팀 synthetic chip과 회사 단위 직원 목록을 fallback으로 유지한다.

### 4.4 검색어 입력 규칙

- 검색어가 비어 있으면 조직 탐색 규칙을 우선 적용한다.
- 검색 범위는 항상 현재 선택된 상단 뱃지 scope를 기준으로 한다.
- 검색어가 있으면 현재 선택된 scope 안에서 직원 검색 결과를 우선 표시한다.
- 검색 중에는 drilldown 목록보다 검색 결과가 우선이다.
- 직원검색 입력창은 한글 IME 조합 입력을 막지 않는 일반 텍스트 입력으로 유지한다.
- 가족사 전인원 고정 검색은 기본 정책으로 사용하지 않는다.
- 한글 이름 검색은 한글 1자 이상 입력 시 조회 가능하다.
- 전화번호 검색은 숫자만 추출했을 때, 앞자리 `010`을 제외한 나머지가 2자리 이상일 때 조회 가능하다.
- 최소 입력 조건에 미달하면 조회 요청을 보내지 않고 현재 scope 기본 목록 또는 drilldown 상태를 유지한다.
- 최소 입력 조건 안내 문구를 사용자에게 짧게 노출할 수 있다.

### 4.5 즐겨찾기 규칙

- `즐겨찾기` 뷰는 조직 drilldown보다 즐겨찾기 결과 표시를 우선한다.
- 현재 선택된 scope 안에서 즐겨찾기를 필터링할 수 있다.
- 즐겨찾기 추가/해제는 직원 상세/목록 어느 쪽에서도 동일하게 동작해야 한다.

### 4.6 조직도 인원 정렬 규칙

- 조직도 그룹 내부 인원 정렬은 단순 API 수신 순서에 의존하지 않는다.
- 같은 조직/부서 내부에서는 `isManager == true` 인원을 최우선으로 노출한다.
- `isManager` 값이 없거나 false인 경우에 한해 `position`에 `팀장`이 포함된 인원을 보조적으로 장으로 판단할 수 있다.
- 그 다음 순서는 직급/직위 우선순위를 반영한다.
- 기본 우선순위는 `사장 > 부사장 > 수석(연구원) > 전무 > 상무 > 이사 > 책임(연구원) > 부장 > 선임(연구원) > 과장 > 대리 > 사원`으로 본다.
- 위 기준이 같은 경우 마지막은 이름 가나다순으로 정렬한다.
- `미지정` 부서 그룹이 있더라도 동일한 인원 정렬 규칙을 적용한다.

### 4.7 직원 리스트 스크롤 규칙

- 검색창, 상단 뱃지, breadcrumb, segment 영역은 결과 인원 증가 때문에 함께 밀려 올라가면 안 된다.
- 하단 직원/조직 결과 영역만 독립적으로 세로 스크롤되어야 한다.
- 직원 리스트는 `Expanded` 영역 안의 `ListView` 계열로 구성한다.
- 인원이 많아도 상단 검색/뱃지/조직 탐색 영역은 같은 화면 안에서 유지되어야 한다.

## 5. 조직도 화면 정책

대상:

- `조직도` segment

규칙:

- 조직 drilldown 중 자식 조직이 있으면 하위조직 목록을 우선 보여준다.
- 하위조직 목록의 각 카드에는 해당 하위조직 subtree 기준 인원 수를 표시한다.
- leaf 조직에 도달하면 해당 leaf 조직에 직접 소속된 직원 목록/조직도 그룹 표시를 보여준다.
- 비-leaf 조직에 직접 소속된 인원이 있으면 하위조직 목록과 직원 목록을 한 화면에 섞지 않고 `직접 소속` 가상 그룹으로 구분해 표시한다.
- 조직도 화면은 직원검색 화면의 tenant navigation 상태를 공유한다.
- breadcrumb 영역과 직원/조직도/즐겨찾기 segment 사이에는 위아래 영역을 구분하는 시각 표식을 둔다.

## 6. 직원 상세 기능 정책

대상:

- bottom sheet 상세
- 전화/문자 버튼

규칙:

- 프로필 사진 URL이 있으면 직원 프로필 앞에 사진을 우선 노출한다.
- 프로필 사진 URL이 없으면 이니셜 또는 기본 아바타 fallback을 사용한다.
- 사번 기반 이미지 네이밍으로 사진을 연결하려면 직원 DTO에 사번 또는 사번으로 역매핑 가능한 안정 식별자가 있어야 한다.
- 이메일만으로 사번 이미지를 역매핑하는 방식은 별도 매핑 테이블이나 명명 규칙이 확정되기 전까지 기본 정책으로 채택하지 않는다.
- 전화번호가 없으면 전화/문자 버튼은 비활성화한다.
- 직원 상세는 tenantName, department, 직급/직위, 직무를 우선 노출한다.
- 즐겨찾기 토글은 상세에서도 가능해야 한다.

## 7. 구현 시 금지사항

- APK 직접 설치만으로 기능 검증이 끝났다고 판단하지 않는다.
- 조직 탐색 정책과 초기 조회 범위를 같은 규칙으로 섞지 않는다.
- 본인 확인 로직 때문에 초기 scope를 개인 1명으로 자동 축소하지 않는다.
- 하위조직 칩 또는 본인팀 칩이 선택 전후로 사라지도록 두지 않는다.
- 다른 회사를 눌렀다는 이유만으로 본인팀 뱃지를 제거하지 않는다.
- 회사/팀 뱃지 선택 시 가족사 전체 직원 목록을 먼저 가져온 뒤 앱에서만 필터링하는 방식을 기본 구현으로 사용하지 않는다.
- 하위조직 목록을 보여주는 단계에서 직원 목록 조회를 동시에 실행하지 않는다.

## 8. 변경 시 필수 검증

최소 검증:

```bash
./scripts/flutter-docker.sh test test/widget_test.dart test/directory/directory_filters_test.dart
```

변경 후 확인할 항목:

- 로그인 후 기본 범위가 회사급인지
- 로그인 후 본인팀 칩이 기본 노출되는지
- `<전체>` 클릭 시 가족사 목록으로 들어가는지
- 회사 선택 후 하위조직 목록이 나오는지
- leaf 조직 선택 후 직원 목록이 나오는지
- 하위조직 카드의 인원 수가 해당 하위조직 subtree 기준으로 합산되는지
- 자식 조직이 있는 비-leaf 조직에서는 직원 목록보다 하위조직 목록이 우선 표시되는지
- leaf 조직의 직접 소속 인원이 0명일 때만 `검색 결과 없음`이 표시되는지
- 본인팀 칩/선택 조직 칩이 사라지지 않는지
- 선택된 칩이 비선택 칩보다 명확히 구분되는지
- 현재 선택된 breadcrumb 칩이 아이콘과 색으로 명확히 구분되는지
- 칩이 많을 때 가로 스크롤로 끝까지 탐색 가능한지
- 이름 1자 검색과 전화번호 `010 제외 2자리` 검색 규칙이 맞는지
- 최소 입력 미달 시 조회를 남발하지 않고 현재 scope가 유지되는지
- 조직도 그룹 내에서 팀장/직급/이름 순 정렬이 맞는지
- 직원 목록이 많을 때 하단 결과 영역만 스크롤되는지
- 하위조직 목록 표시 중 하위조직 전체 직원 목록이 섞여 나오지 않는지
- 상위 조직 직접 소속 인원이 있으면 `직접 소속` 구역에 누락 없이 표시되는지
- 회사/팀 뱃지 선택 시 가족사 전체 직원 조회로 새지 않는지
- 정렬 기준이 `isManager -> 직급표 -> 이름` 순서와 일치하는지
