본문 바로가기

웹/TypeScript

[TypeScript] TypeScript 처음 사용해보기 (2) (React.js)

Foodie-finder
취향분석을 통한 음식점 추천 - React와 코사인 유사도 알고리즘을 활용한 애플리케이션

TypeScript를 통해 localStorage에서 간단한 작업을 해봤는데, 그것을 포스팅하기에는 너무 간략하다고 생각되어 최근에 심심풀이로 떠올랐던 아이디어를 구현하여 만든 것을 이어 포스팅하게 되었다. 그리고, 그것은 바로 개인 맞춤형 음식점 추천 시스템이다.
거창하진 않지만 간략하게 해당 코드를 설명하자면 사용자의 위치를 기반으로 주변 음식점을 찾을 수 있고, 사용자의 이전 선택을 기반으로 한 추천 목록을 제공하여 새로운 음식점을 찾는 데 도움을 주는 말 그대로 사용자의 취향을 분석하여 음식점을 추천해주는 간단한 프로그램을 React와 TypeScript를 통하여 구현해보았다.

사용 기술

  • React.js: 사용자 인터페이스 구현
  • TypeScript: 타입 검사 및 안정성 제공
  • LocalStorage: 사용자 취향 데이터 저장 (별도의 서버 구축이 없어 DB로 사용)
  • Google Places API: 주변 음식점 정보 제공
  • 코사인 유사도 알고리즘: 사용자의 취향과 음식점의 특징을 비교하여 가장 적합한 음식점을 추천

주요 기능

  • Foodie-Finder의 주요 기능은 사용자의 위치를 기반으로 주변 음식점을 찾아주는 것과 사용자가 과거에 선택한 음식점을 기반으로 새로운 음식점을 추천해주는 기능이다. 이를 위해 사용자의 선택이 LocalStorage에 저장되고, 이 정보는 추후 사용자의 취향을 분석하여 음식점을 추천하는 데 사용된다.
  • 음식점 정보는 Google Maps API를 통해 가져오며, 각 음식점은 별점(rating), 가격(price_level), 음식점 타입(types) 이렇게 3가지의 정보를 가지고 있고, 이러한 정보들은 사용자의 취향과 비교하기 위해 벡터로 변환되고, 코사인 유사도를 계산하는 데 사용된다.
// utils.ts
...
const typeToNumber: { [index: string]: number } = {
    'restaurant': 0,
    'food': 1,
    'point_of_interest': 2,
    'establishment': 3,
    'bar': 4,
    'cafe': 5,
    'meal_takeaway': 6,
    'meal_delivery': 7,
    'bakery': 8,
    // 추가 type 값들이 있다면 추가
};
function convertTypeToNumber(type: string): number {
    return typeToNumber[type] || 0;  // 만약 알려지지 않은 타입이라면 음식점의 기본값인 0을 반환
}

...

export const loadUserVectorFromLocalStorage 
	= (): { price_level: number, rating: number, types: number } => {
    const userVectorString = localStorage.getItem('selectedRestaurant');
    if (userVectorString) {
        return JSON.parse(userVectorString) 
		as { price_level: number, rating: number, types: number };
    }
    return { price_level: 0, rating: 0, types: 0 };
}

export const calculateCosineSimilarity 
	= (userVector: { price_level: number, rating: number, types: number },
     restaurantData: Restaurant[]): Restaurant[] => {
    let userVectorArray = Object.values(userVector);
    return restaurantData.map((restaurant: Restaurant) => {
        // restaurant를 비교할 수 있는 Vector의 형태로 변환
        const restaurantVector = [restaurant.price_level || 0, restaurant.rating
		 || 0, restaurant.types ? convertTypeToAverageNumber(restaurant.types) : 0];
        if (userVector) {
            restaurant.cosineSimilarity 
			= cosineSimilarity(userVectorArray, restaurantVector);
        }
        return restaurant;
    });
}

 

이 코드는 사용자의 취향에 기반한 음식점을 추천하기 위한 기능을 구현하기 위해 작성된 코드의 일부이다.
  • convertTypeToNumber 함수: types의 문자배열의 값들을 숫자로 변환시키는 함수
  • loadUserVectorFromLocalStorage 함수: localStorage에서 사용자의 선호도 벡터를 로드하는 기능을 수행
  • calculateCosineSimilarity 함수: 사용자 벡터와 각각의 음식점 벡터 사이의 코사인 유사도를 계산

TypeScript의 관점에서 작성된 특징

  • 정적 타입 체킹
    • TypeScript의 가장 큰 특징 중 하나는 정적 타입 체킹을 제공한다는 것이다. TypeScript는 JavaScript의 슈퍼셋이므로, JavaScript의 모든 기능을 포함하면서 추가적으로 타입을 부여할 수 있다. 이 코드에서는 { price_level: number, rating: number, types: number }와 같은 타입을 정의하고 있습니다. 이는 사용자의 취향 벡터를 나타내는 객체이며, 이 객체는 price_level, rating, types 세 가지 속성을 갖고, 모두 숫자 타입으로 관리된다. 이렇게 타입을 지정하면, 코드 작성 시 이 변수나 객체가 어떤 속성을 가져야 하는지, 어떤 타입의 값을 가질 수 있는지 명확하게 알 수 있다.
    • 최초의 코드 작성 시에는 typeToNumber 함수를 만들지 않고, types에 API에서 제공받는 문자배열들을 그대로 저장하여 벡터를 구현하려 하였다. 그때는 해당 객체의 속성들을 { price_level: number, rating: number, types: string[] }와 같이 정의하여 관리하였다.
  • 명시적인 반환 타입
    • TypeScript에서는 함수의 반환 타입도 명시적으로 지정할 수 있다. 예를 들어 convertTypeToNumber 함수에서는 반환 타입을 number로 명시하고, loadUserVectorFromLocalStorage 함수에서는 반환 타입을 { price_level: number, rating: number, types: number }로 명시하고 있다. 해당 TypeScript의 특징은 코드의 가독성을 높이며, 예상치 못한 오류를 줄이는데 도움이 될 수 있다.
  • 인터페이스와 타입
    • TypeScript에서는 interface와 type 키워드를 사용해 복잡한 데이터 구조를 정의할 수 있습니다. 이 프로젝트에서는 음식점별 정보를 관리하기 위해 Restaurant interface로 사용하여 음식점 데이터의 구조를 정의하고 있습니다. 이러한 type 정의는 코드의 가독성을 높이고 데이터를 효율적으로 관리할 수 있게 해준다.
  • 모듈화
    • TypeScript는 ES6 모듈 시스템을 따르며, 이 코드에서도 export 키워드를 사용하여 함수들을 내보내고 있습니다. 이렇게 하면 다른 TypeScript 파일에서 이 함수들을 가져와 사용할 수 있게 됩니다

이처럼 TypeScript는 정적 타입 체킹, 명시적인 반환 타입, 인터페이스와 타입 정의, 모듈화 등의 기능을 통해 코드의 안정성, 가독성, 유지 보수성을 높여줄 수 있다.

 

(해당 작업물은 이러한 TypeScript의 특징을 잘 활용하여 사용자의 취향에 맞는 음식점을 추천하는 알고리즘을 구현하였다. TypeScript의 활용 방안과 특징에 대해서 설명하기위해 작성된 포스팅이기 때문에 해당 코드를 구현하였던 자세한 내용에 대해서는 해당 포스팅에서는 다루지 않으려 한다.)