MyStock 리팩토링 Phase 4는 AnalysisController 경계를 정리하는 작업이 될 거라고 생각했다. 그런데 Step 0에서 현재 구조를 다시 확인하자 시작부터 예상이 빗나갔다. Phase 4에서 만들려고 했던 AnalysisController가 이미 존재했다.
Phase 3에 이어 또 이미 끝난 작업을 만났다
이상한 일은 이번이 처음이 아니었다. Phase 3에서는 Account와 Market의 반복 갱신 Timer를 RefreshCoordinator로 분리하려고 했지만, 확인해 보니 그 책임 역시 Phase 2에서 이미 이동된 상태였다. 결국 Phase 3은 production code를 다시 수정하지 않고 기존 구조의 경계와 회귀 동작을 테스트로 확인하는 쪽으로 마무리했다.
Phase 4에서도 비슷했다. 현재 AnalysisController는 stock history와 investment sentiment 요청, history cache, pending/latest 선택, stale request 차단, Worker/QThread retire와 shutdown 중 신규 요청 차단까지 이미 담당하고 있었다. 새 Controller나 AnalysisStore를 추가하면 구조를 개선하는 것이 아니라 이미 존재하는 책임을 다시 만드는 셈이었다. 그래서 이번에도 리팩토링을 위한 리팩토링은 하지 않기로 했다.
새 구조 대신 실제로 남아 있는 gap만 찾았다
Phase 4의 구조적 목표는 이미 상당 부분 충족되어 있었지만 실제 동작을 확인하면서 stock history의 동일 in-flight 요청 처리에는 보완할 부분이 남아 있다는 것을 확인했다.
처음에는 동일 요청의 중복 실행을 막는 정도의 작은 수정으로 보였다. 기존 cache, pending/latest, stale 차단, sentiment 흐름과 QThread lifecycle은 그대로 유지하고 해당 요청 경계만 보완했다. 초기 검증과 관련 GUI 테스트, Cocoa sample 종료 smoke까지 통과했고 실제 앱을 사용해 봤을 때도 큰 문제는 보이지 않았다.
Connector review가 비동기 요청의 모서리를 세 번 찾았다
PR #14에 Connector review를 요청한 뒤에는 비동기 요청 타이밍의 경계 조건이 연달아 발견됐다. 첫 번째 P2 finding은 A → B → A처럼 빠르게 선택이 바뀌는 경우였다. active request와 interruption 상태가 겹치면 중복 pending 또는 중복 emission이 발생할 수 있었다. 수정 후 다시 review를 요청하자 실패 직후 retry가 유실될 수 있는 두 번째 P2 finding이 나왔다. 다시 상태를 보완한 뒤에는 늦게 도착한 성공 결과와 requeued request가 겹치면서 같은 요청이 다시 처리될 수 있는 세 번째 경계 조건이 발견됐다.

세 finding은 모두 Phase 4 범위 안에서 재현 가능한 문제로 판단했고, 범위를 넓히지 않은 채 후속 조치를 진행했다. 최종적으로 active request, interruption, 결과·실패 전달 상태와 pending retry의 관계를 보완했다.
세 번의 수정 후, 최종 Connector review는 “Didn't find any major issues”였다. 최종 검증에서는 Phase 4 계약 9 passed, Phase 2 Analysis 7 passed, Phase 3 Refresh 6 passed, Regression 5 passed를 확인했고 관련 GUI 테스트와 compileall/diff check도 통과했다. 실제 앱을 다시 사용해 봤을 때도 큰 문제는 보이지 않았다.
결과적으로 Phase 4에서는 새로운 Controller를 만들지 않았다. 이미 존재하던 AnalysisController의 경계를 확인하고, 실제로 남아 있던 비동기 요청 상태 전이의 빈틈을 보완하는 것으로 작업을 마무리했다.
그런데 왜 또 고칠 게 없었던 걸까?
Phase 4 자체는 이렇게 끝났지만 작업이 끝나고 나니 코드보다 다른 것이 더 신경 쓰였다. 왜 Phase 4에서 만들려고 했던 AnalysisController가 Phase 2에서 이미 만들어져 있었을까? 한 번이라면 이전 작업에서 범위가 조금 넓어진 것으로 생각할 수도 있었다. 하지만 Phase 3에서도 같은 일이 있었다. RefreshCoordinator 역시 Phase 2에서 이미 구현되어 있었다.
그러고 보니 Phase 2는 다른 Phase보다 유독 오래 걸렸다. 당시에는 리팩토링 작업량이 많아서 그런가 보다 하고 넘어갔는데, Phase 3과 Phase 4에서 연속으로 이미 끝난 작업을 만나고 나니 그냥 넘기기 어려워졌다.
Phase 2에서는 대체 어디까지 작업했던 걸까? 그리고 왜 당시에는 그 사실을 알아차리지 못했을까?
이건 코드를 더 고칠 문제가 아니었다. Phase 2 대화방으로 돌아가 당시 작업 과정을 다시 확인해 보기로 했다.
관련글 : ChatGPT 인수인계 사고 감사착수
