# Android 앱 설치/실행 재발 방지 정책

작성일: 2026-07-06
상태: v1.4

목적: Windows Android Studio emulator + WSL/Docker Flutter 환경과 Android 공기계 USB 연결 환경에서 `tdc114plus` 앱을 테스트할 때, 환경값 누락과 ADB 연결 장애로 같은 문제를 반복하지 않도록 설치/실행 정책을 고정한다.

관련 문서:

- `docs/policy_android_studio_wsl_adb_2026-07-03.md`
- `docs/scenario_android_emulator_device_integration_test_2026-07-03.md`
- `scripts/README.md`

## 1. 이번 장애 요약

2026-07-06에 아래 문제가 재현되었다.

- 최신 코드를 빌드한 뒤 `adb install`로 debug APK만 직접 설치했다.
- 그러나 이 앱은 `TDC114_API_BASE` 같은 runtime 값을 `--dart-define`으로 받는다.
- APK 직접 설치 방식은 이 값을 전달하지 못한다.
- 결과적으로 앱은 기본값 `https://sso.example.invalid`를 바라보게 되었고, 직원 목록 API가 실패했다.
- 사용자는 코드가 반영되지 않았거나 필터 버그가 남아 있다고 오해할 수 있었다.

핵심 원인:

- 문제는 코드 수정 자체가 아니라 `설치 방식이 앱의 runtime config 구조와 맞지 않은 것`이었다.

## 2. 필수 원칙

- `tdc114plus` Android 수동 검증 기본 경로는 `flutter run`이다.
- `TDC114_API_BASE`가 필요한 앱 실행은 반드시 `dart-define` 포함 경로로 띄운다.
- `adb install` 또는 APK 파일 직접 설치는 기본 검증 경로로 사용하지 않는다.
- local Baron SSO 검증은 emulator 기준 `http://10.0.2.2:5000`을 사용한다.
- 앱/API 버그 판단 전에는 먼저 `./scripts/api-smoke.sh`로 backend 상태를 확인한다.
- Windows `adb.exe devices`에서 대상 emulator가 `device` 상태로 안정화되기 전에는 WSL/Docker Android 스크립트를 실행하지 않는다.
- 2026-07-08 기준 Windows Android Studio emulator + Docker Flutter 표준 연결 방식은 `ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037`이다.
- `TDC114_ADB_CONNECT_ADDRESS=<WINDOWS_HOST_IP>:<EMULATOR_PORT>` 직접 연결 방식은 Windows ADB 서버 공유 방식이 실패하거나 integration test의 VM service port forwarding 이슈가 확인된 경우에만 보조 경로로 사용한다.
- 2026-07-08 이후 신규 수동 기능점검의 기본 target은 가능하면 Android 공기계 USB 연결 방식으로 전환한다.
- emulator 경로는 보조/fallback으로 보존하되, 물리 키보드 한글 입력이나 emulator portproxy 문제를 해결하기 위해 emulator 설정을 반복하지 않는다.
- 공기계에서는 `10.0.2.2`를 사용할 수 없으므로 `adb reverse tcp:5000 tcp:5000` + `TDC114_API_BASE=http://127.0.0.1:5000`을 우선 사용한다.
- USB 없는 독립형 실기기 검증에서는 `TDC114_API_BASE=https://<staging-or-production-host>`를 사용하고 `TDC114_SKIP_SESSION_BOOTSTRAP=true`로 로컬 bootstrap/mocking을 끈다.
- 2026-07-08 이후 로그인과 데이터 소스를 분리해야 하면 `TDC114_AUTH_API_BASE`, `TDC114_DIRECTORY_API_BASE`, `TDC114_ORGANIZATION_API_BASE`를 별도로 지정할 수 있다. 값을 지정하지 않으면 모두 `TDC114_API_BASE`를 사용한다.

## 3. 허용 경로와 금지 경로

### 3.1 기본 허용 경로

공기계 USB 수동 기능점검을 우선한다.

```bash
ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037 \
TDC114_SMOKE_ENV_FILE=scripts/.env.android-device.local \
TDC114_FLUTTER_DEVICE_ID=<PHYSICAL_DEVICE_ID> \
./scripts/manual-postlogin-run.sh
```

사전 조건:

- Windows PowerShell `adb.exe devices`에서 공기계가 `device` 상태다.
- `adb reverse tcp:5000 tcp:5000`이 적용되어 있다.
- `scripts/.env.android-device.local`의 `TDC114_API_BASE`는 `http://127.0.0.1:5000`이다.

### 3.1-B USB 없는 독립형 실기기 허용 경로

staging 또는 production 공개 API에 직접 붙는 실기기 검증은 아래 경로를 사용한다.

```bash
ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037 \
TDC114_SMOKE_ENV_FILE=scripts/.env.android-device.staging.local \
TDC114_FLUTTER_DEVICE_ID=<PHYSICAL_DEVICE_ID> \
./scripts/manual-postlogin-run.sh
```

사전 조건:

- `TDC114_API_BASE`는 `https://...` 공개 HTTPS Baron API 주소다.
- 필요 시 로그인은 staging, 직원/조직 데이터는 production으로 분리 주입할 수 있다.
- `TDC114_SKIP_SESSION_BOOTSTRAP=true`가 설정되어 있다.
- 로그인은 앱의 `Baron SSO로 로그인` 버튼에서 Hosted Login을 열고, App Link callback과 PKCE token 교환으로 완료한다.
- USB는 최초 설치/실행 후 분리 가능해야 한다.

### 3.1-A emulator fallback 허용 경로

emulator를 쓸 경우 아래 경로만 허용한다.

```bash
ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037 \
TDC114_SMOKE_ENV_FILE=scripts/.env.android-emulator.local \
./scripts/manual-postlogin-run.sh
```

용도:

- post-login 상태 수동 기능 점검
- local API base URL 포함 실행
- 실제 phone-login bootstrap 또는 mock fallback 포함 실행

실제 로그인 화면 검증이 필요하면 RP issuer/client/callback 값이 맞는지 확인한 뒤 `Baron SSO로 로그인` 버튼에서 외부 Hosted Login을 연다.

### 3.2 조건부 허용 경로

```bash
ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037 \
TDC114_SMOKE_ENV_FILE=scripts/.env.android-emulator.local \
./scripts/integration_tests.sh
```

용도:

- integration smoke
- post-login 가정 자동 검증

### 3.3 금지 경로

아래 방식은 정책상 기본 검증 경로로 금지한다.

```bash
adb install app-debug.apk
```

금지 이유:

- `TDC114_API_BASE`
- `TDC114_PREAUTH_*`
- `TDC114_SMOKE_USE_MOCK_DIRECTORY`

같은 runtime define 값이 전달되지 않는다.

예외:

- 단순 설치 가능 여부 확인
- 패키지명 확인
- manifest 수준 점검

이 경우에도 `기능 검증용 실행`으로 간주하지 않는다.

## 3-A. 직원검색 초기 기본값 정책

직원검색 첫 진입 시 기본값은 아래처럼 고정한다.

- 초기 조회 범위: 로그인 사용자의 회사급 범위
- 상단 기본 노출 칩: 회사급 칩 + 본인팀 칩
- 금지 동작: 앱 시작 직후 phone/name 재조회로 본인 1명 결과에 맞춰 초기 범위를 다시 팀 또는 개인 단위로 축소하는 로직

정책 이유:

- 첫 진입에서 사용자가 자기 자신만 보이면 디렉터리 앱 기본 UX와 맞지 않는다.
- 조직/회사 단위 탐색이 시작점이어야 하고, 본인팀은 빠른 재선택용 칩으로 유지하면 충분하다.
- `회사급 범위 조회`와 `본인팀 칩 고정 노출`은 서로 다른 정책이므로 둘 다 함께 유지해야 한다.

## 4. 실행 전 체크 순서

Android 기능점검 전에는 아래 순서를 고정한다.

### 4.1 공기계 USB 기본 순서

1. Windows PowerShell에서 `adb.exe devices` 확인
2. 공기계가 `device` 상태인지 확인
3. `./scripts/api-smoke.sh` 통과 확인
4. `adb reverse tcp:5000 tcp:5000` 적용
5. `scripts/check-android-device-env.sh` 통과 확인
6. `manual-postlogin-run.sh` 실행

중단 기준:

- Windows `adb.exe devices`에서 공기계가 `unauthorized`이면 단말 RSA 승인 전까지 진행하지 않는다.
- `adb reverse`가 실패하면 LAN IP 방식으로 바꾸기 전에는 `127.0.0.1:5000` 기준 앱 실행을 하지 않는다.
- 앱에서 HTTP API가 차단되면 debug 전용 cleartext 허용 설정을 먼저 검토한다.

### 4.1-B USB 없는 독립형 실기기 순서

1. Windows PowerShell에서 `adb.exe devices` 확인
2. 공기계 또는 실사용 폰이 `device` 상태인지 확인
3. `TDC114_API_BASE=https://<staging-or-production-host>`로 env를 준비
4. `TDC114_SKIP_SESSION_BOOTSTRAP=true` 상태로 `manual-postlogin-run.sh` 실행
5. 앱 첫 화면에서 `Baron SSO로 로그인` 수행
6. 문자 또는 메일의 링크 승인 완료
7. `직원검색`, `organization/tenants`, `organization/orgchart` 화면 동작 확인
8. 앱이 열린 뒤 USB를 분리하고 같은 동작이 유지되는지 재확인

중단 기준:

- 공개 HTTPS base URL이 확정되지 않았으면 진행하지 않는다.
- Hosted Login, App Link callback, PKCE token 교환이 staging/production 환경에서 준비되지 않았으면 local mode로 되돌리지 않고 RP 설정과 서버 준비 상태를 먼저 맞춘다.
- 승인 링크 수신 채널이 준비되지 않았으면 USB 없는 독립 검증 완료로 간주하지 않는다.

### 4.2 emulator fallback 순서

1. Windows PowerShell에서 `adb.exe devices` 확인
2. 대상 emulator가 `device` 상태인지 확인
3. `./scripts/api-smoke.sh` 통과 확인
4. Windows ADB server `5037 -> 127.0.0.1:5037` portproxy와 방화벽 rule 확인
5. 그 다음 `manual-postlogin-run.sh` 또는 `integration_tests.sh` 실행

중단 기준:

- Windows `adb.exe devices`에서 대상 emulator가 `offline`이면 WSL/Docker 스크립트를 실행하지 않는다.
- Windows `adb.exe devices`가 `device`인데 Docker local ADB 직접 연결이 `offline`이면, 직접 emulator port 연결을 반복하지 않고 `ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037` 방식으로 전환한다.
- Android Studio Device Manager의 표시 이름과 실제 AVD 폴더 이름이 달라도, `C:\Users\user\.android\avd`에 저장된 AVD 수와 Windows `adb.exe devices` 상태를 우선 기준으로 삼는다.
- AVD 프로세스가 `terminated`되거나 Android Studio가 `failed to connect within 5 minutes`를 표시하면 WSL/Docker 확인으로 넘어가지 않고 Windows 단독 emulator 부팅 안정화부터 처리한다.

완료 기준:

- 앱이 local API 기준으로 실행된다
- 직원검색 또는 로그인 화면이 기대한 경로로 열린다

## 5. 포트가 왜 바뀌는가

질문: 코드 수정이 생길 때마다 포트가 바뀌는가?

답: 아니다. `코드 수정 때문에 포트가 바뀌는 것이 아니다.`

포트가 바뀌는 이유는 보통 아래 중 하나다.

- 새 emulator 인스턴스를 띄웠다
- 기존 emulator를 끄고 다른 emulator 번호로 다시 띄웠다
- `emulator-5554`, `5556`, `5558`처럼 여러 인스턴스가 섞였다
- Windows `portproxy`가 그 emulator의 adbd port와 맞지 않았다

즉:

- 코드 변경
- Flutter rebuild
- hot reload

이 자체는 emulator port를 바꾸지 않는다.

## 6. 반복 절차를 줄이는 고정 운영안

반복을 줄이기 위해 아래 운영안을 기본값으로 사용한다.

### 6.1 1대 고정 원칙

- 수동 검증용 emulator는 한 번에 1대만 켠다.
- 기본 장비는 당일 Windows `adb.exe devices`에서 `device`로 확인된 emulator 1대다.
- 다른 emulator가 `offline`으로 남아 있거나 여러 인스턴스가 섞이면 Android Studio/ADB 상태를 먼저 정리한다.

효과:

- 어떤 emulator가 현재 대상인지 혼선이 줄어든다.
- `offline`/`refused` 원인 추적이 쉬워진다.

### 6.2 Windows ADB 서버 공유 원칙

- Docker/WSL Flutter는 기본적으로 Windows ADB server를 공유한다.
- 표준 환경변수는 `ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037`이다.
- Windows 관리자 PowerShell에서 `0.0.0.0:5037 -> 127.0.0.1:5037` portproxy와 방화벽 rule을 유지한다.
- Windows `adb.exe devices`에서 `emulator-5562 device`처럼 정상으로 보이면, Docker Flutter도 같은 Windows ADB server를 통해 장치를 인식해야 한다.
- 직접 emulator adbd port 연결이 `offline`으로 반복되면 해당 방식은 중단하고 Windows ADB server 공유 방식만 사용한다.

효과:

- Docker container 내부 ADB key 불일치로 인한 `offline`/`unauthorized` 반복을 줄인다.
- Windows에서 이미 정상 승인된 ADB 세션을 그대로 사용한다.

### 6.3 명시 emulator portproxy 원칙

- 수동 검증 포트는 Windows `adb.exe devices`에서 확인한 emulator port에 맞춘다.
- 예: Windows 대상이 `emulator-5562`이면 `5562 -> 127.0.0.1:5562` portproxy와 방화벽 규칙을 확인한다.
- `TDC114_ADB_CONNECT_ADDRESS=<WINDOWS_HOST_IP>:<EMULATOR_PORT>`는 보조 경로로만 명시한다.
- shutdown/startup 스크립트는 과거 고정 포트를 임의로 disconnect하지 않는다.

효과:

- 실제 대상 포트와 스크립트 포트가 어긋나는 일을 줄인다.

### 6.4 APK 직접 설치 금지

- 코드 수정 후 수동 검증은 항상 `flutter run` 또는 `manual-postlogin-run.sh`
- APK 직접 설치는 설치 확인용 보조 수단으로만 사용

효과:

- runtime define 누락 사고를 막는다.

### 6.5 같은 세션 재사용

- emulator를 켠 뒤 가능한 한 끄지 않는다.
- `flutter run` 세션이 살아 있을 때는 hot reload/hot restart를 우선한다.
- 큰 설정 변경이 아닐 때는 재설치보다 같은 세션 재사용을 우선한다.

효과:

- rebuild/install 시간과 ADB 재연결 횟수가 줄어든다.

## 7. 표준 명령

### 7.1 post-login 수동 기능점검

```bash
TDC114_SMOKE_ENV_FILE=scripts/.env.android-emulator.local \
ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037 \
./scripts/manual-postlogin-run.sh
```

### 7.2 integration smoke

```bash
TDC114_SMOKE_ENV_FILE=scripts/.env.android-emulator.local \
ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037 \
./scripts/integration_tests.sh
```

### 7.3 backend 상태 확인

```bash
./scripts/api-smoke.sh
```

## 8. 내일부터의 운영 기준

- Android 수동 검증 기본 target은 USB 연결 공기계다.
- Android emulator는 fallback target으로 보존한다.
- Docker/WSL 기본 연결은 `ADB_SERVER_SOCKET=tcp:<WINDOWS_HOST_IP>:5037`로 명시한다.
- `TDC114_ADB_CONNECT_ADDRESS=<WINDOWS_HOST_IP>:<EMULATOR_PORT>`는 직접 emulator port 연결이 필요한 예외 상황에만 사용
- 실행 스크립트 기본값은 `manual-postlogin-run.sh`
- `adb install`은 기능검증용 실행으로 인정하지 않음

이 기준을 어기면 다시 아래 오판이 발생할 수 있다.

- 코드 미반영으로 오해
- API 장애로 오해
- 필터 버그 미수정으로 오해
