MyStock의 DB 작업이 거의 끝나가던 시점이었다. DB에 데이터를 쌓는 기반을 만들고 화면에 연결하는 과정에서 생각보다 많은 문제를 겪었다. 특히 Daily 데이터를 언제 최신으로 볼 것인지 제대로 정의하지 않은 채 눈앞의 버그를 하나씩 수정하면서 정책이 계속 복잡해졌다. 문제는 코드만 복잡해진 것이 아니었다. GitHub Codex Connector 리뷰에서 새로운 지적이 나오면 그 상황을 보고 즉흥적으로 조건과 정책을 추가했고, 다시 리뷰를 요청했다. 그러면 새로 추가한 정책이 기존 정책과 충돌하면서 또 다른 지적이 나타났다. 코드를 고치고 리뷰를 다시 돌릴수록 끝에 가까워지는 게 아니라 새로운 문제가 계속 나오는 느낌이었다.
Connector 리뷰 정책 변경 필요성
처음에는 리뷰에서 지적된 문제를 바로바로 고치는 게 가장 빠른 방법이라고 생각했다. 지적 하나가 나오면 원인을 확인하고, 그 문제를 막는 조건을 추가하고, 테스트한 뒤 다시 리뷰를 요청했다. 그런데 전체 DB lifecycle에 대한 정확한 요구명세가 없는 상태에서는 그 수정 자체가 새로운 정책이 됐다.
Startup에서는 이렇게 하고, 장중에는 저렇게 하고, 주말에는 다른 조건을 적용하고, coverage가 이 경우에는 충분하고 저 경우에는 부족하다고 판단하는 식이었다. 각 조건에는 이유가 있었지만 전체 기준이 없었다. 결국 리뷰는 구현이 요구사항을 제대로 따르는지 확인하는 과정이 아니라, 다음 요구사항을 발견하고 즉석에서 추가하는 과정처럼 변해버렸다. 이때 겪었던 GitHub Codex Connector의 반복 리뷰 이야기는 별도로 기록해두었다.
이번 DB 작업에서는 같은 방식으로 다시 들어가고 싶지 않았다.
DB 구현보다 우선 정책 결정
Daily 문제를 다시 정리하면서 먼저 바꾼 것은 코드가 아니라 순서였다. 문제가 발생했을 때 바로 조건을 추가하지 않고, MyStock이 실제로 원하는 동작을 먼저 정의하기로 했다. DB가 비어 있는 최초 상태와 이미 사용할 수 있는 데이터가 있는 평상시 실행은 같은 일이 아니었다. 그래서 최초 historical baseline을 구축하는 Initial과, 기존 데이터를 먼저 사용하면서 필요한 최근 구간만 확인하는 Routine을 명확하게 나눴다.
화면에 들어가거나 종목을 선택했다는 이유만으로 긴 historical 데이터를 다시 받지 않는다. DB에 사용할 수 있는 데이터가 있으면 먼저 보여주고, Startup이나 Refresh 같은 정해진 synchronization event에서 필요한 범위만 provider에게 다시 확인한다. Domain마다 어떤 데이터가 저장되고, 어떤 event에서 다시 확인하며, 실패했을 때 기존 데이터를 어떻게 유지할지도 먼저 정리했다. 이번에는 이 정책을 코드 수정의 기준으로 삼았다.
내가 먼저 직접 검증
정책을 정한 뒤에는 실제 사용하면서 느꼈던 이상한 동작부터 확인했다. 시장현황에 들어가면 여전히 3년치 데이터를 다시 받는 것 같은 느낌이 있었다. 투자분석에서 종목을 바꾸면 Daily나 수급, 매매시점 데이터를 다시 가져오는 것처럼 보이기도 했다. 예전 같았으면 일단 provider fetch를 줄이는 조건부터 넣었을지도 모른다. 이번에는 먼저 운영 DB를 read-only로 확인하고 각 화면 event에서 실제 어떤 request가 발생하는지 추적했다. 결과는 한 가지 원인이 아니었다.
Market 데이터는 이미 DB에 충분히 존재했다. 그런데 화면에서는 DB 데이터를 먼저 보여준 뒤에도 선택한 기간, 기본값으로는 3년치를 provider에서 다시 요청하고 있었다. 여기에 정상적인 최근 구간 reconciliation까지 별도로 실행되고 있었다. 반면 Daily의 일반 종목 선택은 이미 DB/RAM-first로 동작하고 있었다. Investor Flow는 일부 종목에 실제 historical baseline이 없었고, Trade marker는 canonical persistence가 없는 runtime 데이터라 첫 조회 자체는 정상적이었다. 다만 같은 종목으로 돌아왔을 때 불필요하게 다시 요청하는 경로는 있었다.
느낌만 보고 하나의 원인으로 묶었다면 또 잘못된 조건을 추가했을 가능성이 컸다.
Initial과 Routine
조사 결과를 바탕으로 최초 DB 구축과 평상시 Startup을 분리했다. DB 파일이 존재한다는 사실만으로 초기화가 끝났다고 판단하지 않았다. 필요한 Domain과 작업 대상별로 실제 baseline이 준비됐는지를 확인하고, 최초 구축이 필요한 데이터만 Initial 대상으로 삼았다.
Initial에서는 Market, Daily, Investor, ETF의 필요한 historical data를 순서대로 준비한다. 성공한 작업은 보존하고, 중간에 실패하거나 사용자가 중단하면 다음 실행에서 남은 작업만 이어서 할 수 있도록 했다.
반대로 baseline이 준비된 뒤의 Routine Startup에서는 마지막으로 정상 저장된 데이터를 먼저 사용한다. Daily와 Market은 최근 구간을 다시 확인하고, Investor와 ETF도 각자 이미 정한 routine policy에 따라 필요한 부분만 갱신한다. Initial과 Routine을 나누고 나니 “DB가 있는데 왜 또 3년치를 받지?”라는 질문에도 답할 수 있게 됐다. 최초 구축이 필요한 상황이 아니라면 긴 historical acquisition을 반복할 이유가 없었다.
Connector 리뷰 정책 재정의
이번에는 GitHub Codex Connector 리뷰를 사용하는 방식도 변경했고 구현 중간에 지적 하나를 받고 바로 수정한 뒤 다시 리뷰를 요청하는 흐름을 반복하지 않기로 했다. 먼저 DB 갱신 정책을 정하고, 그 정책에 따라 구현과 테스트를 진행했다. 기능 구현이 일단 완료된 시점에서 GitHub Codex Connector 리뷰를 시작했다.
리뷰에서 지적이 여러 개 나왔지만 이번에는 지적을 받을 때마다 새로운 정책을 만들지 않았다. 이미 정한 Initial/Routine contract와 Domain별 lifecycle을 기준으로 각각의 지적을 분류했다. 구현이 정책을 빠뜨린 것인지, 특정 UI event가 정해둔 guard를 우회하는 것인지, 실패나 취소 상황에서 기존 계약을 깨는 것인지 확인한 뒤 같은 기준으로 수정했다.
이번 PR에서도 리뷰 검토는 13개까지 생겼고 P1/P2 correctness finding도 있었다. Investor freshness, partial initialization, Account persistence, initialization 중 Analysis acquisition, cancel 이후 동작, Market의 중복 provider fetch처럼 실제로 놓친 경계가 적지 않았다. 하지만 이번에는 돌아갈 기준이 있었다.
Connector Review 검토 및 수정 시간 1시간 18분
정책을 먼저 정했다고 리뷰가 금방 끝난 것은 아니다. 최종 구현이 끝난 뒤 GitHub Codex Connector 리뷰를 시작하고 지적사항을 확인해 수정하고 검증을 마치는 데 약 1시간 18분이 걸렸다. 예전과 비교하면 시간만 놓고 봤을 때 여전히 긴 작업이었다. 차이는 그 시간 동안 무엇을 하고 있었느냐였다.
이전에는 리뷰에서 새로운 상황이 발견될 때마다 요구사항까지 같이 흔들렸다. 하나를 고치면서 새 조건을 만들고, 그 조건이 다른 정책과 충돌하면 다시 방향을 정해야 했다. 이번에는 정책은 이미 정해져 있었다. 리뷰에서 발견된 문제는 새로운 정책을 만드는 출발점이 아니라, 정해둔 정책과 실제 코드가 어긋난 지점이었다.
그래서 여러 지적이 나와도 수정 방향을 다시 고민하는 대신 같은 기준으로 코드를 고치고 regression을 확인할 수 있었다.
GitHub Codex Connector 무한지옥 탈출
이번 작업을 끝내고 나서야 GitHub Codex Connector 리뷰 자체가 무한지옥의 원인은 아니었다는 생각이 들었다. 정확한 요구명세 없이 리뷰에서 발견되는 문제를 따라가며 정책까지 즉흥적으로 만들었던 개발 방식이 문제였다. 리뷰는 꽤 많은 문제를 찾아냈고 이번에도 수정할 것은 많았다. 하지만 먼저 정책을 정하고 나니 리뷰의 역할도 달라졌다.
리뷰가 다음 요구사항을 만드는 과정이 아니라, 구현이 이미 정한 요구사항을 제대로 지키고 있는지 확인하는 과정이 됐다. 1시간 18분이나 걸렸지만 이번에는 끝이 보였다.
무한지옥에서 빠져나온 건 리뷰가 줄어서가 아니라, 돌아갈 요구명세가 생겼기 때문이었다.