주소정제 서비스 내재화 - 4화 ( 슬픈예감 )
단독건물 주소정제 정복 (feat.전라북도와 부천시의 습격)
- 흥미로운 통계
- 주소정제 2.0 설계
- AddressSeachParam 생성 과정
- 휴먼 에러를 보상하기 위한 로직이 몇개 보이는데, 그럼에도 모든 케이스를 커버하긴 힘들 것 같은데?
- 전라북도랑 부천시의 습격 사건이 있었다던데
- 단독건물의 정복 결과는 어떠한가?
![주소정제 내재화 전체 과정](/post-img/refine-address-internalizatipn-4/time_table.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
3화에서는 주소정제 1.0의 실패를 인정 후 전국의 건물 약 1080만개를 DB화 과정에 대한 설명이었습니다.
노가다의 결과로 전국의 건물 DB가 완성이 되었습니다. 👏👏
자 이제 재료는 준비되었고 남은건 잘 설계하고 안전한 랜딩 방법을 계획한 후 개발만 잘하면 되겠지요.
다시 1화의 내용을 복습해보면 건물은 크게 2가지로 나눌수 있습니다.
-
단독건물 : 도로명 기본주소가 곧 1개의 건물 (ex. 전원주택, 한국타이어빌딩)
-
복합건물 : 도로명 기본주소에 여러개의 건물 (ex. 아파트, 101동 102동 … 경비실…근린상가…)
위와 같은 특징을 통해 단독건물의 주소정제의 경우 상세주소를 전혀 참고할 필요없이 정제가 가능함도 알수 있었습니다.
그래서 이번 4화에서는 단독건물 요청에 대한 주소정제 과정을 소개할 예정입니다.
흥미로운 통계
![123344](/post-img/refine-address-internalizatipn-1/building_rate.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
건물 DB가 완성된 후 여러가지 쿼리를 해보다가 건물 타입으로 카운트 쿼리를 때려보니 총 1080만개의 건물 중 아래와 같은 결과를 확인했습니다.
- 복합건물 : 약20만개
- 단독건물 : 나머지
좀처럼 믿기지 않아서 잘못된 데이터가 혹시 있지 않을까 검증도 해봤지만 통계의 신뢰를 흐릴 정도의 양은 아니었습니다. ( 실제 복합건물인데 단독건물 타입으로 저장 되있는 케이스, 그 반대 케이스도 종종 존재)
위 통계가 맞다면 기본주소만으로 주소정제를 하더라도 약 90% 이상의 요청이 정제가능해 보였습니다.
정말 이 결과가 맞을지는 이번화 끝에 설명드릴게요.
주소정제 2.0 설계
주소정제 1.0과 달라지는 부분은 기존에 행안부 주소 조회 api 를 사용하여 건물관리번호를 알아냈던 구간을 건물 DB 조회로 변경하는 내용입니다.
![단독건물 처리 로직](/post-img/refine-address-internalizatipn-4/single_building_algo.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
-
String 타입의 primartAddress, secondaryAddress 를 통해 AddressSeachParam 객체로 변환
-
AddressSeachParam객체에 저장된 변수들 중 도로명 기본주소 아이템만으로 건물DB 리스트조회
-
단 1개의 결과가 나올 때만 단일건물로 간주하고 주소정제 결과 응답 (최대한 보수적으로 정제하자 전략)
-
그 외에 모든 경우는 기존 주소정제1.0 flow를 그대로~
위 과정에서 어떠한 경우라도 Exception이 발생할 경우 기존 외부업체 호출 로직을 태우도록 구현되었습니다. 아무래도 오픈 api의 역할을 직접 구현하다보니 이 구간에서의 안정성을 가장 크게 신경썼습니다.
또한 AddressSeachParam 생성 모든 과정에서 추가된 단위 테스트에도 Exception에 처리에 대한 테스트는 필수로 추가했습니다.
AddressSeachParam 생성 과정
![AddressSearchParam 객체 변환](/post-img/refine-address-internalizatipn-4/address_param_ex.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
AddressSearchParam 객체는 위와 같이 도로명, 지번주소 모두 추출가능한 모든 아이템의 변수를 정의하고 있습니다.
추출 순서는 아래와 같습니다.
- 도로명 : 시/도 -> 시/군/구 -> (법정동 존재하면 ) 법정동 -> 도로명 -> 건물본번 -> 건물부번 -> 나머지
- 지번 : 시/도 -> 시/군/구 -> 법정동 -> 면/리 -> 번 -> 지 -> (산 존재 시) 산 -> 나머지
추출방식은 아래와 같습니다.
- 추출된 항목을 삭제하며 다음단계를 추출
- ex) 경기도 수원시 영통구 삼성로 11 -> 경기도 추출 -> 나머지 수원시 영통구 삼성로 11 만으로 다음단계 추출
생성자 호출
생성자 fullAddress 를 통해 extractAllParams 의 결과로 모든 값들이 추출되어 각 변수에 할당
public AddressSeachParam(String fullAddress) { this.extractAllParams(fullAddress); }
이때 중간에 특정 주소아이템 추출에 실패하더라도, 익셉션을 발생시키지 않습니다.
왜냐하면 '서울특별시' 를 누락한 '강남구 테헤란로 133' 만으로도 주소정제는 가능합니다. (전국에 강남구 테헤란로 133이 유일하다는 조건에서 가능)
그래서 고객요청 주소 string에서 추출 가능한 모든 것을 일단 추출한 후 주소정제 가능여부 판단은 이후에 진행합니다.
시/도 정보 추출
-
요청 주소를 split(" ")결과의 array의 맨 앞의 음절을 사용하여 후보로 지정
여러가지가 나올수 있음 ( 서울, 서울시, 서울특별시 … 어느 경우든 가능) -
후보 단어를 아래 AddressUtil의 replaceSidoName 함수를 통해 공통 규격으로 변환
- 서울, 서울시, 서울특별시 —-> 어느케이스든 '서울특별시'추출
- AddressSearchParam 의 sidoName 에 '서울특별시' 할당
- ( 추출이 성공했을때 )요청 주소에서 맨앞자리 토큰 제거
public static String replaceSidoName(String sido) {
String s = sido + " ";
return s
.replace("세종 ", "세종특별자치시")
.replace("세종시 ", "세종특별자치시")
.replace("서울시 ", "서울특별시")
.replace("서울 ", "서울특별시")
.replace("경기 ", "경기도")
.replace("인천시 ", "인천광역시")
.replace("인천 ", "인천광역시")
.replace("부산시 ", "부산광역시")
.replace("부산 ", "부산광역시")
.replace("울산시 ", "울산광역시")
.replace("울산 ", "울산광역시")
.replace("대전시 ", "대전광역시")
.replace("대전 ", "대전광역시")
.replace("대구시 ", "대구광역시")
.replace("대구 ", "대구광역시")
.replace("광주시 ", "광주광역시")
.replace("광주 ", "광주광역시")
.replace("제주도 ", "제주특별자치도")
.replace("제주 ", "제주특별자치도")
.replace("강원도 ", "강원특별자치도")
.replace("강원 ", "강원특별자치도")
.replace("충남 ", "충청남도")
.replace("충북 ", "충청북도")
.replace("경남 ", "경상남도")
.replace("경북 ", "경상북도")
.replace("전남 ", "전라남도")
.replace("전라북도 ", "전북특별자치도")
.replace("전북 ", "전북특별자치도")
.trim();
}
시/도 추출은 비교적 쉽습니다.
전국 다해봐야 나올수 있는 케이스는 많지 않고 위의 경우만으로 현재 문제없이 동작합니다.
시/군/구 정보 추출
시/군/구는 시/도보다 까다로운 작업이 하나 있습니다.
일단 마음을 열고 한번 생각해 보는 시간이 필요합니다.
-
경기도, 전라북도 같은 '도' 이후에는 화성'시', 전주'시'와 같이 '시' 가 보통 등장합니다.
-
서울특별시, 울산광역시 같은 '특별시'나 '광역시' 뒤에는 강남'구', 북'구' 같은 '구' 정보가 옵니다.
-
근데 또 경기도 수원시, 성남시, 용인시 같은 경우는 '수원시 영통구', '용인시 처인구' 이렇게 한 세트로 두 음절이 '구' 정보로 표기됩니다.
-
약간 맛이가는 상황이라면 부산광역시 뒤에는 기장'군' 처럼 '구' 가 아닌 '군' 이 오기도 합니다. (부산 외에도 몇개 있습니다.)
-
더 맛이가는 상황이라면 세종특별자치시에는 시/군/구 정보가 현재는 존재하지 않습니다.
자 위의 케이스를 보면 뭔가 피곤해지는 느낌인데요.
제가 작성한 시/군/구 추출 알고리즘 입니다.
private String extractSigungu(String payload) {
String[] token = payload.split(" ");
if(token[0].endsWith("시") && token[1].endsWith("구")){
return token[0] + " " + token[1];
} else if(token[0].endsWith("시") || token[0].endsWith("군") || token[0].endsWith("구")){
return token[0];
}
// 부천시 원미구, 수원시 팔달구 케이스 부천 원미구, 수원 장안구 형태로 '시' 를 누락한 케이스
if(token[1].endsWith("구")) {
return token[0] + " " + token[1];
}
return "";
}
-
이미 시/도 는 앞에서 제거된 상태로 payload가 넘어 왔음을 가정
-
한번에 2개 음절을 추출해야하는 '수원시 영통구', '용인시 처인구' 같은 케이스를 가장 먼저 추출
-
1번이 아니라면 첫번째 음절이 '시', '군', '구' 로 끝나면 그대로 추출
만약 고객요청에 별다른 누락이 없다면 위 알고리즘이면 충분합니다.
다만 수원 장안구 (수원시 장안구), 용인 처인구 (용인시 처인구)… 와 같이 '시'글자를 누락한 채로 넘어오는 경우도 종종 보았습니다. 그래서 2번때 음절이 '구'로 끝날때를 처리하는 조건이 마지막에 추가되었습니다.
읍/면/동 정보 추출
- 경상남도 양산시 동면 금오16길 100 (양산이지더원리버포레)
- 경북 울릉군 울릉읍 사동2길 25-37
위와 같이 시/군/구 정보 뒤에는 읍/면/동 정보가 이어져 오는 주소도 존재합니다. 이건 옵셔널값이 아니라 필수입니다.
이런 케이스도 당연히 무시하면 안되곘죠.
이 친구는 좀 더 간단합니다. 아래 알고리즘으로 쉽게 추출이 가능합니다.
알고리즘 필터조건의 마지막에 '가'로 끝나는 경우는 지번주소 케이스입니다.
서울 용산구 한강로2가 419 현대아파트 이 주소는 뭔가 '한강로2가' 로 되있어서 도로명인 것으로 착각을 하게되는 변화구였는데요. 실제 도로명 주소는 한강대로30길 25 입니다.
private String extractDongName(String payload) {
return Arrays.stream(payload.split(" "))
.findFirst()
.filter(s -> s.endsWith("읍") || s.endsWith("면") || s.endsWith("동") || s.endsWith("가"))
.orElse("");
}
도로명 정보 추출
모든 추출 과정에서 가장 큰 지옥을 경험했던 아이템입니다.
일단 도로명의 특징을 다시 살펴볼게요.
-
도로명은 전체 붙여쓰는 것을 원칙
-
'고개', '거리', '번길', '길', '로' 로 끝나는 단어만 존재
-
큰 도로명 뒤에는 이어서 작은 도로명이 붙을 수도 있음 (망포로84번길과 같이 망포로 + 84번길)
도로명을 추출하면서 컬리몰에서부터 요청오는 케이스에서는 문제가 전혀없습니다. 다만 OMS에서는 직접 엑셀로 주소를 업로드를하여 주소정제를 요청하는 클라이언트도 존재하는데 전제적으로 도로명주소 포멧이 잘 지켜지지 않는 경우가 꽤 있었습니다.
예를들면 '가로수로20번길'과 같이 도로명으로 간주되는 postfix (로, 로, 번길)가 3개까지 존재하는 케이스에 만약 띄어쓰기 실수라도 포함되있다면 대부분 정확한 도로명을 추출하지 못했습니다.
또한 도로명뒤에는 바로 건물 본번이 나오는데 가로수로20번길133 과 같이 건물 본번도 도로명에 붙여써버리는 케이스도 생각보다 많이 존재합니다.
그래서
private String extractRoadName(String payload) {
// 다른 언어끼리는 space로 구분 ex( 가로수로20번길 -> 가로수로 20 번길 )
String replacedPayload = RegexUtils.addSpacesBetweenDifferentLanguage(payload);
String extractRoadName = "";
int index = replacedPayload.indexOf("고개 ");
if(index != -1) extractRoadName = replacedPayload.substring(0, index + 2);
index = replacedPayload.indexOf("거리 ");
if(StringUtil.isBlank(extractRoadName) && index != -1) extractRoadName = replacedPayload.substring(0, index + 2);
index = replacedPayload.indexOf("번길 ");
if(StringUtil.isBlank(extractRoadName) && index != -1) extractRoadName = replacedPayload.substring(0, index + 2);
index = replacedPayload.indexOf("길 ");
if(StringUtil.isBlank(extractRoadName) && index != -1) extractRoadName = replacedPayload.substring(0, index + 1);
index = replacedPayload.indexOf("로 ");
if(StringUtil.isBlank(extractRoadName) && index != -1) extractRoadName = replacedPayload.substring(0, index + 1);
if(StringUtil.isBlank(extractRoadName)) return "";
...
}
위는 도로명 추출 함수인데요.
String replacedPayload = RegexUtils.addSpacesBetweenDifferentLanguage(payload);
이 함수의 역할은 명확합니다.
다른 언어(한글, 영어, 숫자…등) 를 전부 space로 구분하여 리턴하는 함수입니다.
이 힘수는 가로수로20번길133과 같이 건물번호를 도로명과 붙여쓰는 요청 오류을 일단 해소하고 추출을 시작하기 위함입니다.
그렇게 되면 가로수로20번길133 -> 가로수로 20 번길 133 으로 구분하고 시작할 수 있겠네요.
이후 도로명을 '고개 ', '거리 ', '번길 ', '길 ', '로 ' 순서대로 존재하는 index를 확인한 후 최종 추출된 글자를 합쳐서 '가로수로20번길' 을 가져올 수 있습니다.
그런데 이렇게만 하면 끝날줄 알았는데
'인천 서구 서로3로 225' 주소에서 '서로' 만 추출되고 '3로' 를 추출 하지 못하는 문제가 있었습니다.
이 경우 extractRoadName 함수를 무조건 2번 호출하는 식으로 처리를 했고 DB에 있는 도로명 전체케이스를 커버할 수 있었습니다.
this.mainRoadName = this.extractRoadName(primaryAddressPayload);
if(StringUtil.isBlank(this.mainRoadName)) return;
primaryAddressPayload = primaryAddressPayload.replaceFirst(this.mainRoadName, "").trim();
this.subRoadName = this.extractRoadName(primaryAddressPayload);
primaryAddressPayload = primaryAddressPayload.replaceFirst(this.subRoadName, "").trim();
this.roadName = (this.mainRoadName + this.subRoadName).replace(" ", "");
위처럼 그냥 딱 2번만 돌려서 this.roadName 값을 할당하면 extractRoadName 함수를 일단 훼손하지 않고 전체케이스를 커버할 수 있었습니다.
그럼 mainRoadName, subRoadName 이 두 변수는 roadName 변수를 할당 후 필요없어지는 변수인데, 왜 굳이 AddressSeachParam의 한 자리를 차지하고 있느냐?
이건 … 다음 블로그에 등장합니다.
외부업체 주소정제에 없는 OMS 주소정제만의 기가막힌 기능이 있는데, 이 기능을 검증하기 위해 사용됩니다. (갈 길이 멀어 여기까지만 정리할게요)
건물번호 ( 건물본번-건물부번 ) 정보 추출
// 건물본번 추출 ( 필수 )
this.buildingNumber = this.getBuildingNumber(buildingNumberToken);
// 건물부번 추출
this.buildingSubNumber = this.getBuildingSubNumber(buildingNumberToken);
...
...
private String getBuildingNumber(String buildingNumberToken) {
String[] split = buildingNumberToken.split("-");
if(split.length == 0) return "";
try {
return String.valueOf(Integer.parseInt(split[0]));
} catch (NumberFormatException ex) {
return "";
}
}
private String getBuildingSubNumber(String buildingNumberToken) {
String[] split = buildingNumberToken.split("-");
if(split.length < 2) return "0";
try {
return String.valueOf(Integer.parseInt(split[1]));
} catch (NumberFormatException ex) {
return "0";
}
}
만약 63-2 와 같이 '-' 로 구분 된 경우는 건물본번 = 63, 건물부번 = 2 로 추출이 되어야 합니다.
만약에 63과 같이 '-' 이 없이 건물본번만 존재하면 건물부번 = 0 으로 추출해야합니다. (행안부 데이터가 건물 부번이 없는경우는 0 임)
휴먼 에러를 보상하기 위한 로직이 몇개 보이는데, 그럼에도 모든 케이스를 커버하긴 힘들 것 같은데?
맞습니다.
도로명의 경우도 휴먼 에러로 요청된 케이스를 무시한다면 훨씬 깔끔한 코드가 나올 수 있습니다.
그런데 OMS 주소정제는 향후 외부 판매처의 주문을 받아야 할 상황도 얼마든지 존재하기 때문에 포멧에 맞지않게 요청되더라도 정제 가능한 수준을 목표로 했습니다.
코드의 가독성 및 유지보수성 vs 찰떡같은 주소 정제 사이에서 최선의 판단을 하려고 고민을 많이 했던 것 같습니다.
그래서 처음에는 운영에서 외부업체 호출 로그중에 가장 많은 빈도가 발생한 케이스를 먼저 처리했습니다.
일부 '아 이런것 까지 해야되나…' 싶은 케이스들도 있었는데요.
그런 경우는 상식선에서 충분히 가능한 휴먼오류냐 아니냐를 판단해서 결정했던 것 같습니다.
그리고 테스트 코드가 굉장히 중요했는데요.
![AddressSearchParam 객체 변환케이스 테스트 코드](/post-img/refine-address-internalizatipn-4/address_param_test_case.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
캡쳐 부분 외에도 비슷한 양 만큼 더 있습니다.
제가 생각하지 못한 변화구들이 계속 등장하더라구요. 그래서 어떤 변화구 요청 주소가 발견될 때마다 그 주소 자체를 테스트 코드에 넣는 방식으로 진행했습니다.
나중에는 주소만 봐도 원인을 알겠더라고요.
전라북도랑 부천시의 습격 사건이 있었다던데
네 행정안전부 공지사항에 아래 2가지 변경이 공지되었습니다.
-
1월 1일 부터 경기도 부천시 -> 부천시 오정구, 부천시 소사구, 부천시 원미구 로 '시군구' 정보가 구체적으로 일괄변경
-
1월 18일부터 전라북도 -> 전북특별자치도 로 '도'명 일괄변경
주소정제 튜닝 과정에서 예상치 못했던 케이스들이 너무 많았기 때문에 왠지 모를 슬픈예감이 계속 들었는데요.
앞서 23년에도 비슷한 큰 변경이 존재했습니다.
-
강원도 -> 강원특별자치도
-
경상북도 군위군 -> 대구광역시 군위군
이렇게 한번에 큰 변경이 발생시 가장 큰 문제는 우리가 가지고있는 건물 DB 데이터와 고객들의 미리 저장된 기존 배송지 사이에 충돌이 발생합니다.
제 기억에 24년까지도 '경상북도 군위군' 으로 요청하는 주소가 존재합니다.
그렇다고 '이젠 경상북도 군위군이 아니고 대구광역시야~! 틀렸어 아웃~!' 으로 응답하면 고객 경험상 좋을게 없겠지요. 배송지 목록에 갑자기 우리집 주소가 '배송불가' 지역으로 보일테니 이게 뭔가 싶으실 겁니다.
제 학창시절의 살던 집도 매곡동 -> 용봉동으로 바뀌었는데, 저는 이사갈때까지 매곡동이라고 쓰고 다녔거든요. (내가 문젠가?)
이런 경우는 당연히 고객분들이 모두 변화를 인지하지 못할 것이라는 가정을 하고 대응을 하는게 맞습니다.
그래서 이번에도 또 행안부 직원과 유선상으로 여러가지 문의를 했습니다.
- (제일 중요한 내용) 건물관리번호는 죽었다 깨어나도 안바뀐다.
- 법정동코드, 행정동코드, 도로명코드, 기관코드, 영문명칭, 텍스트이름 전부 바뀐다.
- 대부분 홈페이지에 나온 날짜에 변경될테지만, 데이터 변경을 한곳에서 하는게 아니고 지자체에서 각각 담당을 하기때문에 시점차이 존재할 수 있다.
일단 평소에는 항상 답변을 애매모호하게 주셨던 직원분께서 건물관리번호 변경에 대한 내용만큼은 아주 강력하게 바뀌지 않는다고 못박아 주셨기 때문에 너무도 다행이라는 생각이 들었습니다.
그리고 아래는 OMS팀의 대응방식입니다.
전라북도특별자치도는 간단하네
- 건물 DB에 전라북도 -> 전라북도특별자치도로 일괄 변경
- 고객은 전라북도, 전라북도특별자치도 2가지 모두 요청올 것으로 생각하고 정제 결과는 전라북도특별자치도 로 통일
- 이후 지자체에서 변경데이터로 동기화 해주기전까지는 법정동코드, 행정동코드, 도로명코드, 기관코드는 과거 데이터 기준으로 응답
부천시는…어느지역이 부천시 원미구냐, 소사구냐 이건 변경데이터를 봐야 알수 있음
- 부천시는 다만 원미구, 소사구, 오정구 로 디테일한 시군구 값으로 변경되었을 뿐 '부천시' 만으로 조회한다고 해서 주소정제 오류가 발생하진 않음 (여지껏 부천시로만 조회했던 상황)
- 건물 DB에 '부천시 원미구' , '부천시 소사구'… 가 존재해도 '부천시' 만으로 조회가 가능하도록 replaceSigunguNameByRule 을 적용해서 like 검색을 하도록 수정
public static String replaceSigunguNameByRule(String sigunguName) {
if(StringUtil.isBlank(sigunguName)) return "";
//24.01.01일 부터 부천시 원미구, 부천시 소사구, 부천시 오정구 로 인입시 부천시로 like 검색 하도록 '부천시' 로 일괄 치환
//고객 기존 주소록 일괄 반영 전까지 해당 함수 유지
if(sigunguName.trim().startsWith("부천")) return "부천시";
if(sigunguName.trim().startsWith("부천시")) return "부천시";
if(sigunguName.trim().startsWith("창원시")) return "창원시";
String[] token = sigunguName.split(" ");
if(token[0].endsWith("시") || token[0].endsWith("군") || token[0].endsWith("구")) {
return sigunguName;
}
if(token.length > 1 && token[1].endsWith("구")) {
return token[0] + "시 " + token[1];
}
return sigunguName;
}
준비는 끝났다!
각 변경이 존재하는 날은 하루종일 로그만 보고있었는데요.
아마 컬리의 모든 임직원분들은 이런 대규모 변경이 있었는지도 모르셨을겁니다. 저희팀은 이날 엄청 긴장하면서 하루를 보냈거든요.
현재 부천시의 경우는 아직도 건물 DB에 달랑 '부천시' 로만 저장되있는 건물이 존재합니다. 그리고 여전히 '부천시' 로 요청하시는 고객주소도 당연히 존재하고 있구요.
1월 18일 전라북도특별자치도 또한 부천시와 마찬가지의 상황이고, 얘는 한번에 변경되야할 건물수가 80만건 정도여서 걱정을 했지만, 조금씩 현행화되어 현재는 거의 대부분 완료된 상태로 보이네요.
정말 다행히도 슬픈예감은 멋지게 빗나가 버렸죠.
다만 앞으로 이런 변경점이 언제든 발생할 수 있다는 생각으로 가끔 한번씩 행정구역 코드 변경안내 페이지를 들여다 보고 있습니다.
다음 블로그에 등장할테지만 현재는 건물의 변동이력 DB를 관리를 하게 되었고, 이런 대규모 변경이 발생하더라도 긴장 할 필요 없도록 다 조치를 취해놓은 상태입니다.
단독건물의 정복 결과는 어떠한가?
AddressSeachParam 생성 알고리즘이 어느정도 완성이되고 단독건물 케이스도 일부 케이스를 제외하고는 외부업체 호출 대부분이 커버되었습니다.
![123344](/post-img/refine-address-internalizatipn-1/building_rate.png)
(그림: 컬리, © 2023. Kurly. All rights reserved.)
진짜 통계만큼 90%가량 호출량이 줄어들었나?
전체 건물의 비율중 단일건물 비율이 98%가 단일건물이니, 얼추 보수적으로 잡더라도 로그의 90%정도는 줄어들 것으로 예상했으나, 실제로는 60%정도의 호출량만 절감이 되었습니다.
사실 통계는 문제가 없지만 통계의 해석에 문제가 있었습니다.
서울 역삼동에 어느 전원주택(단일건물) 혼자살고있는 또치가 일주일에 1번 주문할때
바로 옆 아파트의 101동에서는 1호에 살고있는 둘리와 2호에 살고있는 고길동…아파트에 존재하는 호수만큼 여러건 주문을 넣을 수 있기 때문에, 건물 타입별 비중이 정확히 트래픽 감소와 일치하지는 않았던 것이지요.
이를 깨닫고는 이어서 바로 복합건물 호출량을 때려 잡기에 돌입하였습니다.