Laravel 서비스 클래스의 계층형 카테고리 매핑에서 실패가 얼마나 나는지 세려고 했다. map()이 MappingResult 객체 하나만 돌려주고 있었다.
Table of contents
Open Table of contents
배경 — 무엇을 세려 했나
채널마다 카테고리 체계가 달라 우리 것을 ESM·SSG 쪽 코드로 옮기는 매핑이 있었다. 매핑이 안 되면 상품 등록 API가 거절한다.
어느 카테고리에서 얼마나 실패하는지를 알아야 어디부터 보완할지 순서를 정할 수 있었다.
원인 — 결과 객체의 입도
MappingResult에는 success와 code 둘뿐이었고, 실패했을 때 어느 단계에서 걸렸는지는 담고 있지 않았다.
매핑은 대분류·중분류·소분류 세 단계를 거친다. minor 실패와 major 부재는 보완 방법이 다르다.
결과 객체를 그대로 세면 실패 건수만 나오고 어디를 고쳐야 할지는 안 나왔다.
선택지와 대가
방법이 셋이었다. 결과 객체에 단계 정보를 추가하거나, 매핑 함수 안에서 직접 계측을 남기거나, 실패 케이스를 별도 테이블에 적는 것이다.
MappingResult를 바꾸는 쪽은 호출자 전부가 영향을 받는데, 부르는 곳이 여섯이었고 그중 둘은 Jenkins가 도는 외부 배치였다.
map() 안에서 계측하는 쪽은 호출자를 건드리지 않는 대신, 그 메서드가 계측 책임까지 지고 PHPUnit 테스트에서도 카운터가 함께 오른다.
별도 테이블은 분석이 자유로운 대신 매핑마다 MySQL INSERT가 생긴다.
고른 것과 하지 않은 것
map() 안에서 계측하는 쪽을 골랐다. MappingResult는 매핑 결과를 전달하는 계약이지 계측 통로가 아니라고 봤다.
계측은 Prometheus 카운터만 올리도록 했다. stage="major"|"middle"|"minor" 라벨로 갈라 세고 개별 케이스는 남기지 않는다.
방어 코드를 넣는 것은 하지 않았다. 매핑 실패 시 기본 카테고리로 넘기자는 이야기가 있었는데, 그러면 잘못된 카테고리로 등록되고 그것이 실패보다 나쁘다. 실패는 실패로 두었다.
이어받을 사람을 위한 것
Grafana 대시보드에 뜨는 stage 라벨의 뜻을 문서에 적었다. 라벨만 보고 단계를 알아야 쓸모가 있다.
MappingResult를 계측용으로 쓰지 않기로 한 이유도 함께 적었다. 나중에 누가 거기 필드를 추가하려 할 때 근거가 있어야 한다.
정리
- 무엇을 셀지 정하면 필요한 입도가 따라 나온다
- 결과 객체가 전달하는 것과 계측이 필요한 것은 입도가 다르다
- 세 단계 매핑에서 어느 단계 실패인지를 알아야 보완 순서가 정해진다
- 결과 객체 확장·함수 내 계측·별도 테이블 셋을 놓고 봤다
- 결과 객체를 바꾸면 호출자 여섯 곳이 영향을 받는다
- 결과 객체는 계약이지 계측 통로가 아니라고 보고 함수 안에서 계측했다
- 실패 시 기본 카테고리로 넘기는 방어는 하지 않았다 — 잘못된 등록이 더 나쁘다
- 계측 라벨의 뜻과 이 결정의 근거를 함께 문서에 남겼다