Trablock의 검색창에서는 도시 이름을 검색할 수 있습니다. 도시를 검색할 때 하단에 자동 완성이 뜨는데, 구글 Places 자동 완성 API는 검색어와 유사한 결과를 최대 5개 제공합니다. 서로 다르지만 동일한 이름을 가진 도시를 구별하기 위해 해당 도시의 주소를 함께 제공합니다.
type Suggestion = {
placePrediction: {
place: string;
placeId: string;
text: {
text: string;
matches?: TextMatch[];
};
structuredFormat: {
mainText: {
text: string;
matches?: TextMatch[];
};
secondaryText: {
text: string;
matches?: TextMatch[];
};
};
types: PlacesType[];
};
};
type GetGooglePlacesAutocompleteResponse = {
suggestions?: Suggestion[];
};
위 타입에서 text와 structuredFormat을 통해 결과를 확인할 수 있습니다. 하지만 문제가 있었습니다. 국가, 도시, 언어에 따라 통일되지 않은 결과물이 나타났습니다. 예를 들어, 서울과 Seoul로 각각 검색할 경우 동일한 도시이지만 표시되는 언어가 달라 해당 API 내에서 동일한 이름으로 처리할 방법이 없었습니다. 또한 특정 국가나 도시는 주소 표시 포맷, 언어 등이 다른 경우가 있어 일관된 도시 주소 처리에 어려움이 있었습니다. 자동 완성 API에는 이 문제를 해결하기 위한 상세 옵션이나 기능이 부족했습니다.
위 문제를 보완하기 위해 장소 세부정보 API를 추가로 연결했습니다. 장소 세부정보 API는 자동완성 API보다 일관된 언어 옵션과 더 자세한 정보를 제공해 도시 이름을 포맷팅하는 일에 용이합니다.
type AddressComponent = {
longText: string;
shortText: string;
types: PlacesType[];
languageCode: string;
};
type GetGooglePlacesDetailResponse = {
id: string;
primaryType: string;
name: string;
formattedAddress: string;
addressComponents: AddressComponent[];
displayName: { text: string; };
internationalPhoneNumber?: string;
nationalPhoneNumber?: string;
websiteUri?: string;
location?: Location;
photos?: Photo[];
};
자동완성 API에서 받아온 placeId를 장소 세부정보 API에 전달합니다. 받아온 데이터를 필터링해서 도시 주소을 추출합니다.
function createCityItem(placeId: string, res: PlaceResult): Location | undefined {
const { formattedAddress, addressComponents } = res;
const newCity = addressComponents[0].longText;
if (!newCity) return;
const newCountry = addressComponents.find((item) => item.types.includes('country'))?.longText;
if (!newCountry) return;
// 필터링한 주소 배열
// Array.from(new Set([...]))으로 중복 요소 제거
const filteredAddressComponents = Array.from(
new Set(
addressComponents
.filter(({ longText, types }) => {
if (!types.includes('political')) return false;
if (types.includes('administrative_area_level_4' || 'administrative_area_level_5')) return false;
if (longText === newCity) return false;
if (!formattedAddress.includes(longText)) return false;
return true;
})
.map((item) => item.longText)
.reverse()
)
);
const newAddress = filteredAddressComponents.join(' ');
return {
place_id: placeId,
address: newAddress,
city: removeLocationSuffix(newCity, newCountry)
};
}
formattedAddress는 자동완성 API와 마찬가지로 대표성을 가지는 주소를 나타내지만 국가, 언어 등에 따라 포맷이 달라집니다. addressComponents는 행정구역, 장소 타입 등에 따라 주소를 분리해 저장하지만, types가 나타내는 정보가 국가마다 차이가 있기 때문에 대표성을 가진 주소를 필터링 하는 것에는 어려움이 있습니다.
따라서 두 정보를 결합해 도시 주소를 포맷팅했습니다. addressComponents에서 먼저 도시와 국가 이름을 추출하고, 전체 구성 요소에서 types를 이용해 도시 주소가 될 수 있는 요소를 필터링합니다. 필터링된 요소 중 formattedAddress에 포함된 요소를 다시 필터링해서 하나의 주소로 합치는 과정을 거쳐 대표성을 가지면서 일관된 포맷을 가지는 도시 주소를 얻을 수 있었습니다.
마지막으로 removeLocationSuffix() 유틸 함수를 이용해 특정 국가의 도시에 붙는 행정구역 접미사를 제거헤 순수한 도시 이름을 완성했습니다.