- Align protocol documentation (PROTOCOL.md, README.md, VERSIONING.md) - Go: add nonce test, update communicator - Kotlin: update Communicator, TcpClient, TcpServer, add TLS test - Python: update all modules, add certificate test resources - TypeScript: update communicator, tcp/ws clients and servers, add tests - Dart: update communicator, heartbeat mixin, and tests
183 lines
6.1 KiB
Text
183 lines
6.1 KiB
Text
<!-- task=dart_legacy_refactor plan=0 tag=REFACTOR -->
|
|
|
|
# Dart Communicator 레거시 정리
|
|
|
|
## 이 파일을 읽는 구현 에이전트에게
|
|
|
|
각 항목의 체크리스트를 완료 표시하고, 중간 검증 명령을 실행한 뒤 출력을 CODE_REVIEW.md의 `검증 결과` 섹션에 붙여 넣는다. 최종 검증까지 완료한 후 CODE_REVIEW.md의 각 항목을 `[x]`로 체크한다.
|
|
|
|
## 배경
|
|
|
|
Dart `Communicator`는 `send<T>()` 메서드가 `abstract`로 선언되어 모든 서브클래스나 믹스인이 구현해야 한다. Go·Kotlin·Python·TypeScript 4개 언어는 `Communicator.send()`가 직접 `queuePacket`을 호출하는 concrete 구현을 제공한다. Dart의 이 설계 차이는 `HeartbeatMixin`이 `Communicator`에 믹스인되는 초기 구조에서 비롯된 레거시로, `@Deprecated transmitPacket`과 `_LegacyCommunicatorTransport` 데드코드를 동반한다. 테스트의 `_FakeCommunicator`도 동일한 이유로 불필요한 `send()` 오버라이드를 포함한다. 이번 작업으로 Dart를 다른 언어 구현과 동일한 구조로 맞춘다.
|
|
|
|
---
|
|
|
|
## [REFACTOR-1] `communicator.dart` — `send()` concrete 구현 + 레거시 삭제
|
|
|
|
### 문제
|
|
|
|
- `dart/lib/src/communicator.dart:63` — `send()`가 `abstract`여서 서브클래스 모두 구현 강제
|
|
- `dart/lib/src/communicator.dart:47-53` — `@Deprecated transmitPacket` 데드코드
|
|
- `dart/lib/src/communicator.dart:35,37` — `initialize()`의 `transport` 파라미터가 nullable이며, null 시 이미 삭제 대상인 `_LegacyCommunicatorTransport(this)` 폴백을 사용
|
|
- `dart/lib/src/communicator.dart:192-204` — `_LegacyCommunicatorTransport` 클래스 데드코드
|
|
|
|
### 해결 방법
|
|
|
|
1. `initialize()`의 `{Transport? transport}` → `{required Transport transport}` 변경
|
|
2. `_transport = transport ?? _LegacyCommunicatorTransport(this)` → `_transport = transport`
|
|
3. `@Deprecated transmitPacket` 메서드 삭제
|
|
4. `abstract Future<void> send<T>(T data)` → concrete 구현으로 교체
|
|
5. `_LegacyCommunicatorTransport` 클래스 삭제
|
|
|
|
**Before (communicator.dart:33-38):**
|
|
```dart
|
|
void initialize(
|
|
Map<String, GeneratedMessage Function(List<int>)> instanceGenerator,
|
|
{Transport? transport}) {
|
|
_instanceGenerator = instanceGenerator;
|
|
_transport = transport ?? _LegacyCommunicatorTransport(this);
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```dart
|
|
void initialize(
|
|
Map<String, GeneratedMessage Function(List<int>)> instanceGenerator,
|
|
{required Transport transport}) {
|
|
_instanceGenerator = instanceGenerator;
|
|
_transport = transport;
|
|
}
|
|
```
|
|
|
|
**Before (communicator.dart:47-53):**
|
|
```dart
|
|
/// Deprecated compatibility path for subclasses that still override writes.
|
|
///
|
|
/// New clients should pass a [Transport] to [initialize] instead.
|
|
@Deprecated('Pass a Transport to initialize instead.')
|
|
Future<void> transmitPacket(PacketBase base) {
|
|
return Future.error(StateError('transport is not initialized'));
|
|
}
|
|
```
|
|
|
|
**After:** 전체 삭제
|
|
|
|
**Before (communicator.dart:63):**
|
|
```dart
|
|
Future<void> send<T extends GeneratedMessage>(T data);
|
|
```
|
|
|
|
**After:**
|
|
```dart
|
|
Future<void> send<T extends GeneratedMessage>(T data) async {
|
|
if (isAlive) {
|
|
await queuePacket(PacketBase()
|
|
..typeName = data.info_.qualifiedMessageName
|
|
..nonce = ++nonce
|
|
..data = data.writeToBuffer());
|
|
}
|
|
}
|
|
```
|
|
|
|
**Before (communicator.dart:192-204):**
|
|
```dart
|
|
class _LegacyCommunicatorTransport implements Transport {
|
|
final Communicator _communicator;
|
|
|
|
_LegacyCommunicatorTransport(this._communicator);
|
|
|
|
@override
|
|
Future<void> writePacket(PacketBase base) {
|
|
return _communicator.transmitPacket(base);
|
|
}
|
|
|
|
@override
|
|
Future<void> close() async {}
|
|
}
|
|
```
|
|
|
|
**After:** 전체 삭제 (이 클래스 바로 아래의 `abstract class IDataHandler` 이후는 그대로 유지)
|
|
|
|
### 수정 파일 및 체크리스트
|
|
|
|
- `dart/lib/src/communicator.dart`
|
|
- [x] `initialize()` 파라미터: `{Transport? transport}` → `{required Transport transport}`
|
|
- [x] `initialize()` 바디: `_transport = transport ?? _LegacyCommunicatorTransport(this)` → `_transport = transport`
|
|
- [x] `@Deprecated transmitPacket` 메서드 전체 삭제 (docstring 포함)
|
|
- [x] `abstract Future<void> send<T extends GeneratedMessage>(T data)` → concrete 구현으로 교체
|
|
- [x] `_LegacyCommunicatorTransport` 클래스 전체 삭제
|
|
|
|
### 테스트 작성
|
|
|
|
추가 테스트 불필요. 기존 `communicator_test.dart` 6개 테스트가 `send()` 경로를 포함해 충분히 커버한다.
|
|
|
|
### 중간 검증
|
|
|
|
```
|
|
$ cd dart && dart analyze lib/src/communicator.dart
|
|
(No issues found!)
|
|
```
|
|
|
|
---
|
|
|
|
## [REFACTOR-2] `communicator_test.dart` — `_FakeCommunicator.send()` 오버라이드 제거
|
|
|
|
### 문제
|
|
|
|
`dart/test/communicator_test.dart:24-33` — `_FakeCommunicator.send()` 오버라이드가 REFACTOR-1 이후 `Communicator.send()`와 동일한 로직을 중복 구현한다.
|
|
|
|
```dart
|
|
@override
|
|
Future<void> send<T extends GeneratedMessage>(T data) async {
|
|
if (isAlive) {
|
|
await queuePacket(PacketBase()
|
|
..typeName = data.info_.qualifiedMessageName
|
|
..nonce = ++nonce
|
|
..data = data.writeToBuffer());
|
|
}
|
|
return Future.value();
|
|
}
|
|
```
|
|
|
|
### 해결 방법
|
|
|
|
`_FakeCommunicator.send()` 오버라이드 블록 전체 삭제. `Communicator.send()` 기본 구현이 그대로 사용된다.
|
|
|
|
### 수정 파일 및 체크리스트
|
|
|
|
- `dart/test/communicator_test.dart`
|
|
- [x] `_FakeCommunicator.send()` 오버라이드 삭제 (lines 24-33, `@override`부터 닫는 `}` 포함)
|
|
|
|
### 테스트 작성
|
|
|
|
기존 6개 테스트 그대로 사용.
|
|
|
|
### 중간 검증
|
|
|
|
```
|
|
$ cd dart && dart test test/communicator_test.dart
|
|
00:00 +6: All tests passed!
|
|
```
|
|
|
|
---
|
|
|
|
## 수정 파일 요약
|
|
|
|
| 파일 | 항목 |
|
|
|------|------|
|
|
| `dart/lib/src/communicator.dart` | REFACTOR-1 |
|
|
| `dart/test/communicator_test.dart` | REFACTOR-2 |
|
|
|
|
## 의존 관계 및 구현 순서
|
|
|
|
REFACTOR-1 먼저, 이후 REFACTOR-2 진행. REFACTOR-2는 REFACTOR-1이 완료된 상태에서만 컴파일된다.
|
|
|
|
## 최종 검증
|
|
|
|
```
|
|
$ cd dart && dart analyze
|
|
No issues found!
|
|
|
|
$ cd dart && dart test
|
|
00:xx +43: All tests passed!
|
|
```
|