<!-- task=flutter_first_portal_migration/01_flutter_portal_scaffold plan=0 tag=FLUTTER_PORTAL -->

# Plan - FLUTTER_PORTAL

## 이 파일을 읽는 구현 에이전트에게

구현 마지막 단계에서 `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 반드시 실제 변경 내용과 검증 출력으로 채운다. 검증 명령을 실행하고 실제 stdout/stderr를 기록한 뒤 active 파일은 그대로 두고 리뷰 준비 상태만 보고한다. `complete.log` 작성, `.md` 파일 로그화, archive 이동은 code-review-skill 전용이다.

## 배경

현재 최상위 Milestone은 Flutter-first Portal 마이그레이션이며, `apps/web`의 Next.js 스캐폴드는 삭제 예정이다. 후속 Docker 교체와 Portal wire 연결이 가능하려면 먼저 Flutter 앱 자체가 제품 UI source of truth로 들어와야 한다. 이 작업은 Flutter Portal의 최소 앱 구조와 검증 진입점만 만든다.

## 분석 결과

### 읽은 파일

- `agent-ops/roadmap/current.md`
- `agent-ops/roadmap/milestones/flutter-first-portal-migration.md`
- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `README.md`
- `docs/architecture.md`
- `.gitignore`
- `Makefile`
- `bin/web.sh`
- `docker-compose.yml`
- `apps/web/README.md`
- `apps/web/package.json`
- `apps/web/src/app/page.tsx`
- `apps/web/src/app/layout.tsx`
- `apps/web/src/app/globals.css`
- `apps/web/src/lib/iop-client/client.ts`
- `apps/web/src/lib/iop-client/connection.ts`
- `apps/web/src/lib/iop-client/types.ts`

### 테스트 커버리지 공백

- Flutter Portal 앱 생성: 기존 테스트 없음. 새 `apps/portal/test/widget_test.dart`가 최소 렌더링과 기본 endpoint 표시를 검증해야 한다.
- Portal 설정 기본값: 기존 TypeScript `getIopClientConfig`만 있고 Flutter 설정 테스트 없음. 새 Dart unit/widget 테스트에서 `IOP_CONTROL_PLANE_HTTP_URL`, `IOP_CONTROL_PLANE_WIRE_URL` 기본값을 검증한다.
- 앱 다중 플랫폼 scaffold: 자동 테스트는 `flutter test`와 `flutter build web`까지만 보장한다. Android/iOS/desktop runner 전체 빌드는 이번 범위에서 제외한다.

### 심볼 참조

- 변경/삭제 심볼 없음. 이 작업은 `apps/portal` 신규 앱과 문서/검증 진입점 추가가 중심이다.

### 분할 판단

분할 정책을 먼저 평가했다. 공유 task group은 `flutter_first_portal_migration`이다.

- `01_flutter_portal_scaffold`: Flutter 앱 source of truth를 만든다. 독립 실행 가능.
- `02_proto_socket_browser_transport`: proto-socket Dart browser transport. 독립 실행 가능.
- `03+01_web_delete_flutter_deploy`: `01` 완료 후 Next.js 제거와 Docker Flutter Web 배포 교체.
- `04_control_plane_ws_endpoint`: Control Plane WS endpoint. 독립 실행 가능.
- `05+01,02,04_portal_wire_contract`: `01`, `02`, `04` 완료 후 Flutter Portal wire 연결.

앱 생성, proto-socket 라이브러리 변경, Docker 배포, Control Plane protocol은 서로 다른 소유 경계와 검증 전략을 가지므로 단일 plan으로 묶지 않는다.

### 범위 결정 근거

- `apps/web` 삭제, `bin/web.sh` 교체, compose/Dockerfile 교체는 `03+01_web_delete_flutter_deploy`에서 한다.
- proto-socket Dart browser transport는 `02_proto_socket_browser_transport`에서 한다.
- Control Plane WS listener와 IOP proto 계약 변경은 `04_control_plane_ws_endpoint`에서 한다.
- Flutter Portal이 실제 Control Plane에 붙는 client 구현은 `05+01,02,04_portal_wire_contract`에서 한다.

### 빌드 등급

Build lane `cloud-G07`, review lane `cloud-G07`. Flutter multi-platform scaffold와 web build 검증이 있고 기존 테스트가 전혀 없어 local보다 cloud가 맞다.

## 구현 체크리스트

- [ ] [FLUTTER_PORTAL-1] `apps/portal` Flutter 앱 scaffold를 생성하고 최소 Portal 화면을 구현한다.
- [ ] [FLUTTER_PORTAL-2] Portal 설정 기본값과 문서, ignore/Makefile 진입점을 Flutter 기준으로 추가한다.
- [ ] 중간 검증과 최종 검증 명령을 실행하고 실제 출력을 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [FLUTTER_PORTAL-1] Flutter 앱 scaffold

문제: Milestone은 Flutter 앱을 UI source of truth로 요구하지만 `agent-ops/roadmap/milestones/flutter-first-portal-migration.md:23`의 대상 앱이 아직 없고, 현재 제품 UI는 `README.md:230`의 legacy `apps/web`뿐이다.

해결 방법: 루트에서 `flutter create apps/portal --project-name iop_portal --platforms=android,ios,linux,macos,windows,web`로 앱을 생성한다. 생성 후 `apps/portal/lib/main.dart`는 최소 운영 포털 화면, health 상태 placeholder, HTTP/Wire endpoint 표시를 가진 Material 앱으로 정리한다.

Before:

```text
agent-ops/roadmap/milestones/flutter-first-portal-migration.md:23
- Flutter 앱을 IOP Portal의 기준 구현으로 선언

README.md:230
| `apps/web` | 삭제 예정인 legacy Next.js Web Portal 스캐폴드. 장기 UI는 Flutter-first Portal과 Flutter Web 산출물로 전환한다 |
```

After:

```dart
import 'package:flutter/material.dart';

void main() {
  runApp(const IopPortalApp());
}

class IopPortalApp extends StatelessWidget {
  const IopPortalApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'IOP Portal',
      home: const PortalHomePage(),
    );
  }
}
```

수정 파일 및 체크리스트:

- [ ] `apps/portal/pubspec.yaml`: package name, SDK bounds, Flutter dependency를 생성 상태에서 검토한다.
- [ ] `apps/portal/lib/main.dart`: 최소 UI와 안정적인 위젯 key/text를 둔다.
- [ ] `apps/portal/test/widget_test.dart`: 앱 title, endpoint 표시, 기본 상태를 검증한다.
- [ ] 생성된 platform runner 파일은 Flutter 기본 구조를 유지하고 임의로 삭제하지 않는다.

테스트 작성: 작성한다. `apps/portal/test/widget_test.dart`에서 `IOP Portal`, `Control Plane`, `Wire` 텍스트가 렌더링되는지 검증한다.

중간 검증:

```bash
cd apps/portal && flutter test
```

기대 결과: 모든 Flutter 테스트 통과.

### [FLUTTER_PORTAL-2] 설정과 진입점 문서

문제: `.gitignore:15`는 Next.js 산출물만 무시하고, `Makefile:1`에는 Flutter Portal 검증 진입점이 없다. `apps/web/README.md:17`은 Node/npm을 전제로 해 Flutter-first 흐름과 충돌한다.

해결 방법: `apps/portal`에 README를 추가하고, `String.fromEnvironment` 기반 설정 클래스를 둔다. 루트 `.gitignore`에는 Flutter build/cache 산출물을 추가하고, `Makefile`에는 `portal-test`, `portal-build-web` target을 추가한다.

Before:

```makefile
Makefile:1
.PHONY: all build tidy test test-e2e test-openai-ollama proto clean
```

After:

```makefile
.PHONY: all build tidy test test-e2e test-openai-ollama proto portal-test portal-build-web clean

portal-test:
	cd apps/portal && flutter test

portal-build-web:
	cd apps/portal && flutter build web \
		--dart-define=IOP_CONTROL_PLANE_HTTP_URL=http://localhost:9080 \
		--dart-define=IOP_CONTROL_PLANE_WIRE_URL=ws://localhost:19080/portal
```

수정 파일 및 체크리스트:

- [ ] `apps/portal/README.md`: Flutter Portal 실행/테스트/build 명령과 endpoint env를 기록한다.
- [ ] `apps/portal/lib/portal_config.dart`: `IOP_CONTROL_PLANE_HTTP_URL`, `IOP_CONTROL_PLANE_WIRE_URL` 기본값을 둔다.
- [ ] `apps/portal/test/portal_config_test.dart`: 기본값을 검증한다.
- [ ] `.gitignore`: `apps/portal/.dart_tool/`, `apps/portal/build/`, `apps/portal/.flutter-plugins*`, `apps/portal/.packages`를 무시한다.
- [ ] `Makefile`: `portal-test`, `portal-build-web` target을 추가한다.

테스트 작성: 작성한다. `apps/portal/test/portal_config_test.dart`에서 기본 HTTP URL이 `http://localhost:9080`, Wire URL이 `ws://localhost:19080/portal`인지 검증한다.

중간 검증:

```bash
make portal-test
```

기대 결과: `cd apps/portal && flutter test`가 실행되고 통과.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/portal/**` | FLUTTER_PORTAL-1, FLUTTER_PORTAL-2 |
| `.gitignore` | FLUTTER_PORTAL-2 |
| `Makefile` | FLUTTER_PORTAL-2 |
| `README.md` | FLUTTER_PORTAL-2 |

## 최종 검증

```bash
command -v flutter
flutter --version
make portal-test
make portal-build-web
git diff --check
```

기대 결과: `command -v flutter`가 경로를 출력하고, Flutter 테스트와 web build가 통과하며 whitespace 오류가 없다. Flutter build cache output은 허용하지 않는다. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
