이벤트 소싱에서 모델 정보의 진실 원천을 턴컨텍스트로 이전
목차
이벤트 소싱 시스템에서 데이터를 읽어오는 경로를 바꾸는 작업이었다. 원래는 response_item에서 모델 정보를 가져오던 걸 turn_context events로 변경했다.
왜 이 변경이 필요했나
response_item에서 모델을 읽는 방식은 겉으로는 문제없어 보였다. 응답 결과물에 모든 정보가 담겨 있으니까. 하지만 이벤트 소싱 아키텍처를 진짜로 이해하려면, 데이터의 진실의 원천(source of truth)이 어디인지를 명확히 해야 한다. 우리 시스템에서 turn_context events가 진정한 상태 변화의 기록이라면, 그 이벤트로부터 모든 정보를 유도해야 한다. response_item은 단순히 그 이벤트들의 파생물일 뿐이다.
이렇게 설계하는 이유는:
- 감사 추적(audit trail) 관점에서 명확함: 언제 어떤 모델이 선택되었는지가 이벤트에 남음
- 재현 가능성: 나중에 로그를 다시 읽을 때도 같은 데이터를 얻을 수 있음
- 스키마 진화: response_item 구조가 바뀌어도 turn_context는 안정적일 가능성 높음
코드 레벨에서의 변경
codex-log-reader 모듈이 핵심이다. 이건 로그 데이터를 파싱해서 read model로 변환하는 인프라 레이어다. 테스트 파일(test.ts)과 구현 파일(ts) 모두 수정된 걸 보면, 단순 버그 수정보다는 동작 방식 자체를 리팩터링한 것 같다.
// 변경 전 (가정)
const model = responseItem.model_name;
// 변경 후
const model = extractModelFromTurnContextEvents(turnContextEvents);
이렇게 바뀌면서 테스트도 함께 수정됐을 것. response_item을 모킹하던 테스트들을 turn_context events 기반으로 다시 쓰는 작업이 필요하기 때문이다.
팀 관점에서의 의사결정
같은 기능을 두 가지 방식으로 구현할 수 있을 때, "더 느린 방식"을 택하는 건 항상 리스크다. 특히 로그 리더처럼 데이터 파이프라인의 핵심 부분이면 더욱 그렇다. 그래서 이 변경을 제안할 때는 단순히 "맞는 방식"이라는 주장보다, 구체적인 문제 시나리오를 먼저 공유했을 것 같다.
예를 들면:
- 기존 방식에서 response_item 구조가 변경되면서 모델 필드가 누락된 케이스
- 감사팀에서 "이 응답은 어느 모델로 생성됐는가"를 물었을 때 response_item만으로는 답하기 어려운 상황
- 재처리 시나리오에서 turn_context는 있는데 response_item만 손상된 경우
코드리뷰 단계에서도 이런 배경을 함께 설명해야 팀원들이 "왜 추가 파싱 비용을 감수하는가"를 이해할 수 있다.
패키지 변경의 의미
package.json, package-lock.json까지 수정된 건 아마도:
- 이벤트 파싱 로직을 더 안전하게 하기 위해 새로운 유틸 라이브러리 도입
- 또는 테스트 프레임워크 버전 업그레이드 (더 정확한 모킹을 위해)
의존성 변경은 "버전 충돌", "번들 크기", "보안 패치" 같은 변수들이 얽혀 있다. 이 정도 규모의 변경이면 lock 파일까지 영향을 주는 게 자연스럽다.
회고
이 작업을 통해 배운 점은, 정확성과 명확성이 때론 성능이나 편의성보다 먼저라는 거다. 특히 이벤트 소싱 같은 패러다임에선 더욱 그렇다. 초기엔 "그냥 response_item에서 꺼내면 되지 않냐"고 생각했을 수 있지만, 시스템이 커지고 데이터 수정 요청이 들어오고, 감사 추적이 중요해지는 순간 그 선택이 수십 배의 비용으로 돌아온다.
비슷한 상황에서 다시 만난다면, "진실의 원천이 어디인가"를 먼저 정의하고 설계하는 습관을 더 일찍 기르는 게 낫겠다는 생각이 든다. 그리고 팀과 리뷰할 때도 단순히 "이렇게 바꿔야 한다"가 아니라, "왜 이 구조가 나중에 문제가 될 수 있는지"를 먼저 공유하는 게 설득력 있다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.