<!-- task=kotlin_wss_tls plan=0 tag=KOTLIN_WSS_TLS -->

# Code Review Reference - KOTLIN_WSS_TLS

## 개요

date=2026-04-26
task=kotlin_wss_tls, plan=0, tag=KOTLIN_WSS_TLS

## 이 파일을 읽는 리뷰 에이전트에게

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료 후 반드시 아래 순서로 아카이브하세요.

1. `CODE_REVIEW.md` → `code_review_0.log` (N = 기존 code_review_*.log 수)
2. `PLAN.md` → `plan_0.log` (M = 기존 plan_*.log 수)
3. PASS인 경우 `complete.log` 작성 후 종료. WARN/FAIL인 경우 새 `PLAN.md` + `CODE_REVIEW.md` 스텁 작성.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [KOTLIN_WSS_TLS-1] WsServer.kt — sslContext 파라미터 추가 및 TLS 활성화 | [x] |
| [KOTLIN_WSS_TLS-2] TlsWsTest.kt — WSS 단위 테스트 추가 | [x] |
| [KOTLIN_WSS_TLS-3] tls_crosstest PLAN.md — Kotlin WSS phase 추가 | [x] |

## 계획 대비 변경 사항

- `WsServer.start()`가 `WebSocketServer.onStart()` 신호를 받을 때까지 최대 5초 대기하도록 안정화했다. 전체 테스트 중 WSS 클라이언트가 서버 리스너 준비 전에 접속해 `websocket open timed out`이 발생한 뒤 확인한 보강이다.
- WSS 테스트는 `freePort()` 대신 `WsServer("127.0.0.1", 0, ...)`로 OS 동적 포트를 사용하고 `server.port()`로 실제 bind 포트를 읽도록 했다. 포트 선점과 bind 사이의 경합을 줄이기 위한 변경이다.
- TLS factory executor를 `WsServer`가 직접 생성해 `stop()`에서 종료 완료를 기다리도록 했다. Java-WebSocket의 SSL factory는 내부 executor에 `shutdown()`만 호출하므로, 연속 WSS 테스트에서 SSL 작업이 남는 경우를 방지한다.
- 테스트 본문은 `try/finally`로 서버와 클라이언트를 정리하고, 클라이언트 disconnect가 서버에 반영된 뒤 서버를 멈추도록 했다. 시작 직후 TLS/WebSocket 핸드셰이크 지연에 대비해 WSS dial은 짧은 재시도 래퍼를 사용했다.

## 주요 설계 결정

- `sslContext`는 기존 trailing lambda 호출을 유지하기 위해 `newClient` 람다 바로 앞에 기본값 `null`로 추가했다.
- TLS 활성화는 `super.start()` 이전에 `DefaultSSLWebSocketServerFactory(sslContext)`를 등록하는 방식으로 Java-WebSocket의 기존 확장 지점을 사용했다.
- `WsServer.port()`는 TCP 서버의 `port()`와 같은 용도로 추가해, port `0`으로 bind된 WebSocket 서버의 실제 포트를 테스트에서 읽을 수 있게 했다.
- WSS 단위 테스트는 `TlsTcpTest.kt`와 동일한 테스트 인증서 로딩 헬퍼 구조를 사용하고, `dialWss()`로 실제 TLS WebSocket 송수신 및 request/response를 검증했다.

## 리뷰어를 위한 체크포인트

- **생성자 파라미터 위치**: `sslContext: SSLContext? = null`이 반드시 `newClient` 람다 앞에 위치해야 trailing lambda 호환이 유지된다. 기존 호출부(`WsTest.kt`, crosstest 4개 파일)가 재컴파일 후에도 오류 없이 동작하는지 확인.
- **`setWebSocketFactory` 호출 순서**: `startedFlag.set(true)` 이후, `super.start()` 이전에 호출됐는지 확인.
- **`DefaultSSLWebSocketServerFactory` import**: `org.java_websocket.server.DefaultSSLWebSocketServerFactory` 경로가 정확한지 확인 (라이브러리 1.5.6에 포함된 클래스).
- **`SSLContext` import 중복 방지**: `WsServer.kt`에 이미 `javax.net.ssl.*` import가 없으면 `SSLContext`를 명시적으로 추가해야 한다.
- **TlsWsTest.kt `createTlsContexts()`**: `TlsTcpTest.kt`의 동일 헬퍼와 구조가 일치하는지 (classpath 리소스 로딩, `createTestSslContexts` 위임) 확인.
- **테스트 격리**: `TlsWsTest.kt` 두 테스트 모두 port `0`으로 동적 포트를 할당하고 `server.port()`로 실제 bind 포트를 사용해 다른 테스트와 포트 충돌이 없는지 확인.
- **tls_crosstest PLAN.md 포트 정확성**: 추가된 Kotlin WSS 포트(29496, 29396, 29400, 29800)가 각 파일 내의 기존 포트(TCP, WS, TLS TCP)와 겹치지 않는지 확인.

## 검증 결과

_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._

### KOTLIN_WSS_TLS-1 중간 검증
```
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle \
  ./gradlew compileKotlin
> Task :checkKotlinGradlePluginConfigurationErrors SKIPPED
> Task :extractIncludeProto UP-TO-DATE
> Task :extractProto UP-TO-DATE
> Task :generateProto UP-TO-DATE
> Task :compileKotlin UP-TO-DATE

BUILD SUCCESSFUL in 1s
4 actionable tasks: 4 up-to-date
```

### KOTLIN_WSS_TLS-2 중간 검증
```
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle \
  ./gradlew test --tests "*.TlsWsTest"
> Task :checkKotlinGradlePluginConfigurationErrors SKIPPED
> Task :extractIncludeProto UP-TO-DATE
> Task :extractProto UP-TO-DATE
> Task :generateProto UP-TO-DATE
> Task :compileKotlin UP-TO-DATE
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :extractIncludeTestProto UP-TO-DATE
> Task :extractTestProto UP-TO-DATE
> Task :generateTestProto NO-SOURCE
> Task :processTestResources UP-TO-DATE
> Task :compileTestKotlin
> Task :compileTestJava NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test

BUILD SUCCESSFUL in 6s
11 actionable tasks: 2 executed, 9 up-to-date

TEST-com.tokilabs.toki_socket.TlsWsTest.xml:
tests="2" skipped="0" failures="0" errors="0"
```

### 최종 검증
```
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle \
  ./gradlew test
> Task :checkKotlinGradlePluginConfigurationErrors SKIPPED
> Task :extractIncludeProto UP-TO-DATE
> Task :extractProto UP-TO-DATE
> Task :generateProto UP-TO-DATE
> Task :compileKotlin UP-TO-DATE
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :extractIncludeTestProto UP-TO-DATE
> Task :extractTestProto UP-TO-DATE
> Task :generateTestProto NO-SOURCE
> Task :compileTestKotlin UP-TO-DATE
> Task :compileTestJava NO-SOURCE
> Task :processTestResources UP-TO-DATE
> Task :testClasses UP-TO-DATE
> Task :test

BUILD SUCCESSFUL in 17s
11 actionable tasks: 1 executed, 10 up-to-date

Test XML summary:
CommunicatorTest tests=6, HeartbeatTest tests=4, HeartbeatTimerTest tests=3,
TcpTest tests=5, TlsTcpTest tests=2, TlsWsTest tests=2, WsTest tests=4.
Total: 26 tests, 0 failures, 0 errors.
```

---

## 코드리뷰 결과

### 종합 판정: WARN

### 차원별 평가

| 차원 | 판정 | 비고 |
|------|------|------|
| 정확성 | Pass | WsServer 변경, TlsWsTest 모두 정확. latch/executor 개선은 올바른 선택 |
| 완성도 | Pass | 3개 항목 모두 완료 체크. 단, kotlin_dart.kt 변경이 문서화 누락 |
| 테스트 커버리지 | Pass | testWssSendReceive + testWssRequestResponse, try/finally 정리, retry helper 포함 |
| API 계약 | Pass | `sslContext: SSLContext? = null` 위치 올바름, trailing lambda 호환 유지, port() getter 추가 일관성 있음 |
| 코드 품질 | Pass | 디버그 출력 없음, dead code 없음. ExecutorService lifecycle 명확 |
| 계획 편차 | Warn | kotlin_dart.kt 수정이 KOTLIN_WSS_TLS-3 범위 외 변경이며 CODE_REVIEW.md에 기록 없음 |
| 검증 신뢰 | Pass | BUILD SUCCESSFUL, 26 tests = 6+4+3+5+2+2+4 합산 일치 |

### 발견된 문제

- **Suggested** `kotlin/crosstest/kotlin_dart.kt` / `agent-task/tls_crosstest/PLAN.md:450-455`
  KOTLIN_WSS_TLS-3의 계획 범위는 tls_crosstest 문서 수정뿐이었으나 `kotlin_dart.kt`에 실제 구현(TLS_TCP_PORT = 29494, `createServerSslContext()`, `kotlinDir()`, `runTlsTcp()`, main 호출)이 추가됐다. CODE_REVIEW.md 계획 대비 변경 사항에 기록이 없다. 결과적으로 tls_crosstest PLAN.md의 TLS_CROSSTEST-8 체크리스트 kotlin_dart.kt 항목이 이미 완료된 내용을 `[ ]`로 표시하고 있어 tls_crosstest 구현 에이전트가 혼동할 수 있다.

  수정 방법: `agent-task/tls_crosstest/PLAN.md` TLS_CROSSTEST-8의 `kotlin/crosstest/kotlin_dart.kt` 체크리스트에서 이미 완료된 항목(TLS TCP 포트 상수, 인증서 경로, runTlsTcp, main 호출)을 `[x]`로 표시하고, WSS 항목(WSS_PORT = 29496, runWssSendPush/runWssRequests)만 `[ ]`로 남긴다.

- **Nit** `agent-task/tls_crosstest/PLAN.md:39`
  `typescript_kotlin` 행의 WSS가 `—`로 남아 있다. `typescript_kotlin`은 TypeScript 서버 + Kotlin 클라이언트 조합으로, Kotlin WsClient(`dialWss()`)는 항상 WSS를 지원했으므로 Kotlin WsServer TLS 추가와 무관하게 WSS가 가능하다. tls_crosstest 구현 시 TLS_CROSSTEST-10 담당자가 판단할 사항.

### 다음 단계

WARN: 위 Suggested 이슈를 해결하는 새 PLAN.md + CODE_REVIEW.md 스텁을 작성한다.
