pile·
백엔드·마켓컬리마켓컬리 Hello World·

주소정제 서비스 내재화 - 4화 ( 슬픈예감 )

컬리 주소정제 내재화 시리즈 4화로, 3화에서 만든 전국 약 1,080만 건물 DB를 바탕으로 단독 건물 주소정제를 구현한다. 문자열 주소를 파싱하는 AddressSearchParam 추출 알고리즘, 도로명 추출의 난점, 부천·전북 행정구역 변경 대응을 다루며, 기대 90%였던 외부 호출 감축이 실제 60%에 그친 이유를 밝힌다.

핵심 포인트
  • 복합 건물 약 20만 대 단독 건물 약 1,060만(98%)이지만 트래픽은 건물 수 비율을 따르지 않는다.
  • 정제 2.0은 기본 도로명 주소로 내부 DB를 조회해 정확히 1건 매칭 시에만 결과 반환, 나머지는 외부 API로 폴백한다.
  • 주소 문자열을 시도→시군구→법정동→도로명→건번호로 단계 추출하는 알고리즘을 Java로 구현했다.
  • 건물관리번호는 절대 안 바뀌지만 법정동코드·도로코드·행정코드는 바뀐다는 행안부 원칙을 활용했다.
  • 부천 분구·전북특별자치도 개편을 무중단 대응했고, 단독 건물 내재화로 외부 호출 약 60%를 감축했다.
상세 정리
  • 통계 발견: 복합 건물 약 20만, 단독 건물 약 1,060만(98%). 단, 아파트 한 주소가 수백 주문을 내므로 건물 분포가 트래픽 감축과 직결되지 않는다.
  • 설계(정제 2.0): 문자열을 AddressSearchParam 객체로 변환하고 기본 도로명 주소로만 건물 DB를 조회해 정확히 1건이면 정제 반환(보수적), 그 외는 외부 API로 폴백한다.
  • 추출 순서: 도로명은 시도→시군구→법정동(있으면)→도로명→건물본번→건물부번→나머지, 지번은 시도→시군구→법정동→읍면동→본번→부번→산(있으면)→나머지다.
  • 시도 추출: replaceSidoName()이 서울·서울시 변형을 표준형(서울특별시)으로 정규화하고 전북특별자치도·강원특별자치도 등을 매핑한다.
  • 시군구 추출: 토큰 분해 후 시+구 조합을 처리한다. 부천시 원미구는 시를 생략한 부천 원미구 같은 엣지 케이스에 대응한다.
  • 법정동 추출: 첫 토큰이 읍/면/동/가로 끝나면 채택한다. 가는 지번을 식별한다(한강로2가는 도로명 한강대로30길).
  • 도로명 추출(최난): 도로명은 고개/거리/번길/길/로로만 끝난다. 가로수로20번길133처럼 붙여 쓴 입력을 addSpacesBetweenDifferentLanguage()로 가로수로 20 번길 133으로 띄워 파싱한다.
  • 이중 도로명: 인천 서구 서로3로는 서로와 3로를 잡으려 추출을 두 번 호출한다(mainRoadName+subRoadName). 중간 변수는 OMS 전용 검증 기능에 재사용한다.
  • 건번호 추출: 63-2는 본번 63·부번 2, 63은 부번 기본값 0(행안부 데이터 기준). 파싱 실패는 빈값·0을 반환한다.
  • 설계 철학: 정확성보다 관용성 우선. 가독성·유지보수 대 찰떡 정제를 저울질하며 프로덕션 로그의 빈발 오류부터 처리하고, 발견한 변형은 영구 테스트 케이스로 축적한다.
  • 행정구역 대란: 1/1 부천시 3개 구 분구, 1/18 전북에서 전북특별자치도로. 2023년 강원특별자치도·경북 군위군에서 대구 군위군 선례가 있었다.
  • 핵심 원칙: 행안부 담당자 확인으로 건물관리번호는 죽었다 깨어나도 안 바뀐다. 반면 법정동·행정·도로코드, 영문·한글 명칭은 지자체별 시점차로 바뀐다.
  • 대응: 전북은 DB에 두 표기를 수용하고 표준형을 반환, 부천은 replaceSigunguNameByRule()로 부천·창원 시작이면 부천시·창원시로 like 조회한다. 두 전환일 모두 무장애.
  • 결과: 건물 분포로 기대한 90% 감축이 실제 약 60%였다. 단독 건물은 주문 빈도가 낮고 복합 건물이 물량을 좌우하기 때문이다. 남은 40%는 5화 복합 건물에서 다룬다.
왜 읽나자유 입력 한글 주소를 시도·시군구·도로명·건번호로 파싱하고 행정구역 개편까지 견디는 주소 파서를 설계하는 엔지니어에게 Java 구현 레퍼런스.
마켓컬리
마켓컬리 Hello World 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 백엔드·github-engGitHub Engineering·

    조기 종료를 없애야 벡터화된다 — 메모리 속도 소스 코드 케이스 폴딩

    GitHub의 코드 검색 엔진 Blackbird는 480TB 이상의 소스 코드를 인덱싱하기 전 모든 바이트에 case folding을 적용한다. 이 글은 Rust로 구현한 case folding을 메모리 대역폭 한계(45+ GiB/s)까지 끌어올린 두 가지 반직관적 최적화를 상세히 다룬다. 핵심은 루프 조기 종료(break) 제거로 LLVM 벡터화를 유도하고, UTF-8을 디코딩하지 않고 바이트 공간 산술만으로 fold를 수행하는 것이다.

    #rust#unicode#simd+2
  2. 백엔드·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    트랜잭션 스크립트에서 숙소 메타 + 가격 계산 모듈로 — 전시 아키텍처 개선기 (2/3)

    여기어때 전시개발팀이 숙소 상세(PDP) API를 해부한 결과, 코드상으로는 DB 호출 3번처럼 보이던 요청이 실제로는 MongoDB $lookup 체인으로 컬렉션을 19회 접근하는 구조였다. 이 트랜잭션 스크립트 방식의 핵심 문제는 "aggregation이 I/O를 가린다"는 점으로, 독립적인 쿼리 10개가 단일 파이프라인에 직렬화되어 병렬화 기회를 잃고, 가격 때문에 거의 안 바뀌는 이미지까지 매 요청마다 읽어야 하는 읽기 증폭이 발생했다. V3에서는 "조회 시점 조립"을 "쓰기 시점 사전 조립"으로 전환하고, 화면별로 복제되던 가격 계산 로직을 goodsprice 단일 모듈로 수렴했다. 4개 API(PLP/PDP/RDP/ILP)의 반복 마이그레이션은 Claude Code skill로 절차를 고정하고 쉐도잉 + 동일성 검증으로 안전망을 마련하는 방식으로 진행됐다.

    #architecture#migration#caching+2