주소정제 서비스 내재화 - 마지막 화 ( 엔드 게임 )
유종의 미 그리고 회고
![주소정제 내재화 전체 과정](/post-img/refine-address-internalizatipn-6/time_table.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
최초 문제정의 후 약 10개월 간의 여정끝에 단독건물, 복합건물 모두 안전하게 정제 가능했고 외부업체 호출량은 1%도 되지않는 수준으로 개선이 되었습니다.
거의 다 왔지만 외부업체 api를 호출해야만 하는 명확한 상황이 아직 2가지가 남아있었습니다.
- 지번 주소 요청 처리
- LOW 레벨 정제 호출 처리
위 2가지를 해결하지 못하면 계약을 종료할 수 없습니다.
사실 복합건물의 대부분이 처리된 이후는 계약 종료까지 7개월 정도가 남아있었기 때문에 조급한 마음은 없어졌습니다.
마지막화에서는 24년 3월부터 10월까지 약 7개월 동안의 유종의미를 거두는 과정을 소개합니다.
지번 요청을 처리 방법
행안부 공공데이터를 활용하면 가능해 보였습니다.
![행안부 지번주소 제공 스펙](/post-img/refine-address-internalizatipn-6/jibun_address_hang.png)
스펙을 보니 시도, 시군구, 도로명코드, 건물본번, 건물 부번 등 지번주소 DB를 구축하면, 이를 통해 건물 DB의 건물관리번호를 가져올 수는 있습니다.
그러나 지번 주소 제공을 위해 별도의 데이터를 구성하는건 오버엔지니어링 이라고 판단했습니다.
일단 지번주소는 점점 줄어드는 추세 (일 평균 보수적으로 잡아도 20건도 없는 요청 수준 ) 이기 때문에 이것 만큼은 손안대고 코풀기 전략을 시도했습니다.
바로 2화 그럴싸한 계획 편에 등장하는 행안부 도로명 주소조회 api 를 활용하는 방법입니다.
![지번주소로 행안부 도로명 주소 api 호출](/post-img/refine-address-internalizatipn-6/hang_api_jibun.png)
당시 api 안정성의 이유로 결국 사용하지 않았지만, 일 20건 정도의 지번주소 요청만 처리 해 주는 역할로는 아주 찰떡이었습니다.
![AddressSearchParam 객체 변환](/post-img/refine-address-internalizatipn-6/jibun_address_param.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
4화에 단독건물 처리때 AddressSearchParam 객체를 자세히 보셨다면, 객체 안에는 도로명 뿐만 아니라 지번주소 아이템도 포함되어 있음을 보셨을겁니다.
-
지번 요청도 AddressSearchParam 객체로 일단 변환
-
객체에 존재하는 isJibunAddress(), isRoadAddress() getter 함수를 통해 어떤 주소 형태의 요청인지를 구분
-
isJibunAddress true 라면 행안부 도로명 조회 api 를 호출하여 건물관리번호를 통해 건물 DB 조회
-
이후 로직은 도로명 주소 처리와 동일 알고리즘
현재까지도 전혀 문제없이 동작하고 있습니다.
LOW 레벨 (기본 주소 조회부터 실패!) 주소요청 처리 방법
앞서 5화에서 LOW 레벨의 내용을 잠깐 설명했는데요.
아래 LOW 레벨의 발생상황을 예로 들었습니다.
👉 첫번째 예시
- ex) 과거에 컬리몰에 등록된 배송지이며, 현재 건물이 재건축되어 철거상태
잘 생각해보면 철거된 건물이기에 그 동네로 직접 가보더라도 건물을 찾을 수 없습니다. (로드뷰로 철거된 공사현장 이미지가 나옴)
철거된 건물로 판단되면 애초에 외부업체 api를 호출할 필요도 없었다?
네 맞습니다. 이 케이스는 로그상 외부업체 api를 호출해도 어차피 '처리할 수 없는 주소' 응답을 받았습니다.
오히려, 외부업체를 호출 했기 때문에 오배송이 발생한 경우도 있습니다.
OMS 건물 DB에는 철거된 건물 ( reasonCode = 63) 으로 관리되어있고 매일 최신화 합니다. 외부업체 api 응답에서 아직 철거된 건물임을 반영하지 않은 응답은 배송기사가 없어진 건물로 배송을 시도할 수 밖에 없기에 배송실패가 나기도 했습니다.
👉 두번째 예시
- ex) 동탄 2신도시의 특정 도로명이 일괄변경(동탄대로 -> 동탄역로) 되어 고객이 직접 배송지 변경을 해줘야하는 상태
위 케이스는 전라북도와 부천시 습격과 같은 상황입니다.
중학교때 제가 살던 아파트 법정동이 이미 오래전부터 매곡동에서 용봉동으로 바뀌었다는 사실을 이사갈 때 까지도 몰랐던 기억이 납니다.
고객분들 중 본인 거주지역의 행정구역 변경을 즉각적으로 모든 어플리케이션 배송지에 반영하시는 비율이 얼마나 될까요.
사실 없다고 가정하고 이를 해결해야만 합니다.
그래서 현재 OMS 일단위 업데이트를 진행할때 특정 값들이 기존과 달라진 경우 변경이력 DB에서 변경 전 내용을 저장하도록 추가했습니다.
![변경 감지 후 변경이력 DB에 적재](/post-img/refine-address-internalizatipn-6/change_history.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
그래서 최초 메인 건물 DB로 어떤 건물을 찾지 못한경우 기존 LOW 레벨인 경우, 변경 이력 DB를 한번더 조회하여 과거 주소의 요청이라도 건물관리번호를 알아낼 수 있었습니다.
변경이력 DB를 도입한 시점부터는 변경이 있었던 주소들에 대해서도 정상적으로 최신 주소로 정제가 가능했는데, 도입 이전에 변경이 된 건들들은 이력을 가지고 있지 않으니 이를 어떻게 채워줄까 고민이 되었습니다.
![백오피스 변경이력 수정 삭제](/post-img/refine-address-internalizatipn-6/backoffice_address_history.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
매일 발생하는 빈도를 보아하니 발생할 때 마다 수기로 처리하다보면 어느새 빈도가 줄어들다가 결국 끝나지 않을까? 생각이 들었습니다.
그래서 위와 같이 수기대응을 위해 편하게 변경이력을 강제로 추가/수정/삭제 할 수 있는 백오피스 기능을 추가했습니다.
한 3개월쯤 매일 1~2건씩 처리하다보니 점점 양이 줄면서, 지금은 언제 마지막으로 발생했는지 기억도 안나네요.
결국 LOW 레벨도 위 로직을 통해서 처리할 수 있게되었습니다.
그리고 변경 이력 DB 에도 없는 경우는 진짜 '처리할 수 없는 주소' 익셉션을 발생시킵니다.
그리고 현재까지 정상적인 주소를 '처리할 수 없는 주소'로 잘못 응답한 케이스는 보지 못했습니다.
24년 9월 30일을 끝으로 외부업체와의 계약종료
사실 계약 종료를 위한 마지막 버전은 8월 초 약 2달 전에 배포가 되었습니다.
- 8월 한달 간 : '처리할수 없는 주소'로 응답하는 것들 외에 혹시 정상 주소 중 외부 주소정제를 호출하는 케이스가 있는지 점검
- 9월 한달 간 : 외부업체 호출 인터페이스 fade-out 후 서비스 안정성 체크 (보험 없이 oms 주소정제 only)
종료에 앞서 2달간 미리 외부업체 호출 인터페이스가 없는 상황으로 운영이 되었고, 그 어떤 버그나 문제를 발견하지 못했습니다.
이로써 약 1년 간의 여정을 아름답게 마무리 할 수 있게 되었습니다.
회고
어느정도 트래픽이 줄어들었던 시점부터 주변 동료분들이 블로그 써달라는 요청이 있었습니다. 아무래도 최종 목표였던 외부업체와의 계약종료 이후 최소 3개월은 문제없는지 최종 확인은 하고 나대고 싶어서 미루다가 이제야 씁니다.
블로그를 쓰면서 다시 1년 전으로 돌아가봤고 주소정제 내재화 프로젝트에 대한 개인적인 생각을 한번 끄적여볼게요.
난이도
극악의 난이도는 아니었다고 생각합니다.
주소정제 내재화는 시작부터 외부업체 정제 결과라는 좋은 답안지가 있었기에 끝이 명확히 보이는 프로젝트였습니다.
- 외부업체 에서 정제 가능한 주소들은 전부 커버할 수 있으면 된다.
- 외부업체 에서 제공하지 않는 OMS 주소정제만의 기능이 있다면 더 좋겠다 ( HIGH 레벨 케이스 )
- 개발하는 과정에서 여차하면 외부업체라는 보험이자 답안지를 그냥 사용하면 된다.
- 답안지 없이 OMS 주소정제 혼자서 가능하면 개발은 끝이다.
개인적으로 난이도가 어렵다고 생각되는 과제라고 하면 어느 정도의 수준이어야 만족할 수 있는지를 가늠하기 힘든 과제들이라고 생각을 합니다.
아쉬웠던 점
처음부터 건물 DB를 만들고 시작할 껄…
처음에는 인터넷으로 도로명 주소를 쭉 학습했지만, 귀찮더라도 건물 DB를 미리 만들고 여러 쿼리를 때려보면서 도메인 지식을 더 단단하게 만들고 시작했으면 어땠을까하는 생각이 듭니다.
건물 DB가 있고 없고의 차이는 개발 속도, 버그 발생율에서 넘사의 차이가 났습니다.
ex) 건물 DB에 주택, 공장, 업무시설 등 구분 값이 있는데 굉장히 활용도가 높을 것 같지만, 막상 데이터를 까보면 잘못 매핑된 건물이 많아서 주소정제에서 활용을 포기
상세주소 변화구들 중 포기할건 빠르게 포기했어야…
단독건물 정제를 끝낸 후 복합건물을 대응할때 어떤 전략이 있었다기보다
- "대충 이런 케이스가 많네?"
- "현재로직에서 크게 수정없이 가능할 것 같은데?"
두가지 기준을 우선순위로 정제 튜닝을 진행했는데요.
어차피 자유롭게 타이핑을 할 수 있는 UX에서 100% 상세동 추출은 불가능 하니, 포기할 케이스를 미리 정의해놓고 시작하는게 어땠을까 싶습니다.
당장의 외부업체 호출건을 줄이는데 혈안이 되어던 정신상태였던 것 같고 그러다보니 이런 비중이 작고 규칙없는 로직들이 점점 쌓이게 되었습니다.
튜닝 중반쯤 진행하면서 뭔가 잘못된 방향으로 가고있음을 느꼈고 결국 이전 화에 여러차례 등장했던 AddressSearchParam 클래스로 중심 구조를 잡고 시작하는 로직으로 전면 리팩토링을 진행하였습니다.
확인하는 과정이 번거로우니 그 만큼 속도가 더디다
주소정제 개발 초반에는 서버 log와 쿼리에 의존하여 신규 버전 패치 결과도 검증하고 각종 주소관련 CS문의도 대응했는데요.
지도 기반 어플리케이션을 만든 후 번거로웠던 주소관련 대응들에 스트레스가 없어졌던 것 같습니다.
대충 점심먹고 오후 2시 정도에 주소관련 CS 문의 슬랙이 오면 일단 귀찮았는데, 지도기반 백오피스 페이지를 통해 클릭 몇번이면 원인을 확인할 수 도 있고 신속한 대처도 가능해졌습니다.
가장 중요한건 DB접근 권한 등의 이유로 이슈대응에서 항상 빠져나갔던 염태환님이 더이상 그런 핑계를 댈 수 없게 만든게 가장 큰 성과가 아닐까?
건물DB를 후 곧장 지도기반 어플리케이션을 바로 다음 태스크로 진행하는 게 더 적절한 순서였다고 생각합니다.
잘했던 것
- 단독건물, 복합건물 2가지 타입을 처음부터 나눠서 생각한 점
- 1588-0061(7번 도로명주소)유선 전화를 마치 챗지피티 마냥 활용한 점
- 주소정제 정확도 레벨의 개념을 도입 한 것
- 기존 기능의 대체를 넘어 더 나은 엣지있는 기능 ( HIGH 레벨 )도 챙겼다는 점
- 개발 후 5개월 정도에 이미 90%이상의 트래픽을 낮추는데 성공했지만, 나머지 10%를 위해 끝까지 가본 것
마지막
![박수석님 박제](/post-img/refine-address-internalizatipn-6/soosuck_backjae.png)
수석님께서 블로그 편당 스벅상품권을 준다는 확답으로 받고 이걸 한 16화까지 일기라도 쓰면서 질질 끌어볼까 하다가 6화로 끝냅니다.
이상으로 긴글 끝까지 읽어주셔서 감사드립니다.
그리고
- 계약종료 여부를 계속 의심했지만 그대도 끝까지 믿어주신 수석님
- 건물 DB 노가다판에 같이 뛰어들어 주시고 여러가지 변화구를 처리할 아이디어를 주신 OMS 팀원분들 류대환, 서지연, 강완수님
- 개발자 자존심을 열심히 긁어주셔서 지대한 동기부여를 던져주신 염태환 PM님
- 커머스에서 고객 주소에 대해 많은 협업을 같이 진행해 주셨던 주문결제팀의 정영권님
감사드립니다.🎉