본문 바로가기

프로젝트/세컨핸드

[Trouble Shooting] 지역 설정 기능 구현 과정에서 겪은 문제

- 중고거래 프로젝트에서 선택된 지역을 기반으로 해당 지역에 등록된 상품을 보여주거나 사용자가 상품을 등록할 때 지역을 선택하는 기능의 구현이 필요했고, 사용자의 위치를 기반으로 주변 법정동을 검색하는 로직을 작성했다.

- 최초에는 지역을 6개만 DB에 저장하고 진행했지만, 사용자의 위치기반으로 지역을 설정하고 검색하는 방법에 대해서 시도해보고 싶다는 생각이 들었다.

 

시도한 방법

1. 사용자의 위치(위도, 경도)를 기준으로 외부 지도 API를 통해 사용자 근방의 행정기관(각 행정구역마다 동사무소, 읍사무소 등은 꼭 한개씩 있을 것이라고 판단)을 찾고 해당 행정기관의 주소를 찾는다.

2. 행정기관의 주소들을 이용해 중복이 없는 법정동 주소를 찾는다.

3. 사용자 주변의 법정동 주소들을 API 요청을 통해 DB와 일치하는 주소의 idx값을 얻는다.

 

// 브라우저를 통해 사용자의 위치(위도, 경도를 확인하는 함수
const getCurrentUserLocation = async (): Promise<{
  latitude: number;
  longitude: number;
}> => {
  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        resolve({
          latitude: position.coords.latitude,
          longitude: position.coords.longitude,
        });
      },
      (error) => {
        reject(error);
      }
    );
  });
};
// 사용자의 위치(위도, 경도)를 통해 카카오 맵 API를 사용하는 함수
const searchPlaceByType = async (latitude, longitude, placeType, radius) => {
  const response = await fetch(
    `${KAKAO_MAP_API_URL}query=${placeType}&x=${longitude}&y=${latitude}&radius=${radius}`,
    {
      headers: {
        Authorization: `KakaoAK ${KAKAO_MAP_API_KEY}`,
      },
    }
  );
  const data = await response.json();
  return data;
};
// 사용자의 위치(위도, 경도)를 기준으로 주변 행정기관(행정복지센터, 읍사무소, 면사무소) 검색 함수
const getNearLocationNames = async (): Promise<nearPlaceData[] | null> => {
  try {
    const { latitude, longitude } = await getCurrentUserLocation();

	// 전국의 지역이 정상적으로 검색되는지 여부를 확인하기 위한 예제 위치(위도, 경도)
    // 서울
    // const latitude = 37.5665;
    // const longitude = 126.978;

    // 수원
    // const latitude = 37.2636;
    // const longitude = 127.0286;

    // 제주도(리)
    // const latitude = 33.3931;
    // const longitude = 126.6047;

    // 강원도
    // const latitude = 38.0296;
    // const longitude = 128.6075;

    // 경북
    // const latitude = 36.6646;
    // const longitude = 129.1211;

    // 세종시
    const latitude = 36.5382;
    const longitude = 127.2545;
    let radius = 0;

    const placeTypes = ['행정복지센터', '읍사무소', '면사무소'];
    let nearLocationNameList = null;

	// 사용자 위치 주변에 행정기관 검색하는 로직
    for (const type of placeTypes) {
      radius += 10000;
      if (type === '면사무소') {
        radius = 20000;
      }
      const nearLocationName = await searchPlaceByType(
        latitude,
        longitude,
        type,
        radius
      );
      if (nearLocationName && nearLocationName.documents.length > 0) {
        nearLocationNameList = nearLocationName.documents;
        break;
      }
    }

    return nearLocationNameList;
  } catch (error) {
    console.error('Error:', error);
    return null;
  }
};
// DB에 저장된 지역과 일치하는 문자열을 만들기 위한 함수
const convertLocationName = async () => {
  const nearLocationNameList = await getNearLocationNames();

  const convertedLocationNameList = new Set<string>();
  const locationNameList: locationData[] = [];

  if (nearLocationNameList !== null) {
    for (const placeName of nearLocationNameList) {
      const { address_name } = placeName;
      const city = address_name.split(' ')[0];

      if (city === '세종특별자치시') {
        const convertedLocationName = address_name
          .split(' ')
          .slice(0, 2)
          .join(' ');
        convertedLocationNameList.add(convertedLocationName);
        continue;
      }

      if (city === '제주특별자치도') {
        const convertedLocationName = address_name
          .split(' ')
          .slice(0, 3)
          .join(' ');
        convertedLocationNameList.add(convertedLocationName);
        continue;
      }

      if (city === '강원특별자치도') {
        const convertedLocationName = address_name
          .split(' ')
          .slice(0, 3)
          .join(' ');
        convertedLocationNameList.add(`${convertedLocationName}`);
        continue;
      }

      if (city === '경기') {
        const convertedLocationName = address_name
          .split(' ')
          .slice(1, 4)
          .join(' ');
        convertedLocationNameList.add(`경기도 ${convertedLocationName}`);
        continue;
      }

      const convertedLocationName = address_name
        .split(' ')
        .slice(1, 3)
        .join(' ');
      convertedLocationNameList.add(
        `${CITY_FULL_NAME[city]} ${convertedLocationName}`
      );
    }
  }

  [...convertedLocationNameList].forEach((locationName) => {
    locationNameList.push({ locationString: locationName });
  });

  return locationNameList;
};

문제정의

- 하지만 많은 시행착오 끝에 프론트에서 사용자의 위치와 외부 지도 API를 이용하여 주변의 법정동을 찾고, 찾아낸 지역명을 DB와 일치시키는게 현실적으로 불가능하다는 것을 깨달았다.

 

1. 행정구역의 개편이 있는 경우, 지도 API에서의 주소명과 DB에 저장된 주소명이 일치하지 않는다.

ex) 세종특별시의 경우, DB에 저장한 시점 이후에 행정구역 개편이 있었던 것을 추정된다.

 

 

2. 대한민국의 모든 주소를 DB에서 정확히 일치하게 관리하는 것이 어렵다.

ex) 행정표준코드관리시스템 홈페이지에서 다운받은 지역에는 "경상북도 영양군 영양읍"이 없지만, 지도 API를 통해 검색한 결과 "경상북도 영양군 영양읍"은 존재하기 때문에 에러가 발생했다.

 

 

해결방안

- 프론트, 백엔드 회의 결과, 우리 팀이 생각하는 가장 이상적인 지역설정 및 검색의 로직은 사용자의 위치(위도, 경도)를 프론트에서 API를 통해 전송하면 DB에서 사용자 위치(위도, 경도)를 기준으로 설정 범위 이내의 지역을 검색하는 로직이었다.

- 하지만 DB 설정 등 시간이 오래 걸리고 작업이 복잡한 관계로 최초의 방법인 6개의 지역만 DB에 저장하고 사용하는 방법으로 일단 진행하였다.

아쉬운 점

- 처음 고민했던 로직의 문제점을 조금 더 빨리 파악했다면 다양한 방법으로 시도를 했을 것 같다.