본문 바로가기
개발/프로젝트-식당 웨이팅 앱 FOOD LINE

230603 실전프로젝트8 [Node.js/Nest.js_sort별 목록조회 api 보완]

by 코딩하는짱구 2023. 6. 5.
반응형

✅오늘학습 Keyword

sort별로 나눈 api를 하나로 통합했다. 

@post('/coordinates') 식당목록조회(거리순으로 변경)
@post('/coordinates-name') 이름순으로 조회
@post('/coordinates-waitingcnt') 웨이팅 적은수로 조회
@post('/coordinates-waitingcnt2') 웨이팅 많은수로 조회
@post('/coordinates-rating') 별점순으로 조회

 

✅오늘 겪은 문제

 

기존의 코드

service.ts

//사용자 위치 기반 반경 1km내의 식당 조회
  async findResWithinRadius(
    southWestLatitude: number,
    southWestLongitude: number,
    northEastLatitude: number,
    northEastLongitude: number,
  ): Promise<{ 근처식당목록: Stores[] }> {
    //일단 모든 data를 조회한다
    const restaurants = await this.storesRepository.findAll();
    const restaurantsWithinRadius = restaurants.filter((restaurant) => {
      const withinLatitudeRange =
        Number(restaurant.La) >= southWestLatitude &&
        Number(restaurant.La) <= northEastLatitude;
      const withinLongitudeRange =
        Number(restaurant.Ma) >= southWestLongitude &&
        Number(restaurant.Ma) <= northEastLongitude;
      return withinLatitudeRange && withinLongitudeRange;
    });
  
    // 가까운 순으로 sort
    const userLocation = {
      latitude: southWestLatitude,
      longitude: southWestLongitude,
    };
    restaurantsWithinRadius.forEach((restaurant) => {
      const distance = this.calculateDistance(userLocation, {
        latitude: restaurant.La,
        longitude: restaurant.Ma,
      });
      restaurant.distance = distance;
      // restaurant.distance = distance; // 거리를 추가하여 저장
    });

    // 거리순으로 정렬
    restaurantsWithinRadius.sort(
      (a, b) => (a.distance || 0) - (b.distance || 0),
    );

    console.log(restaurantsWithinRadius);
    return { 근처식당목록: restaurantsWithinRadius };
  }
  
    //사용자 위치 기반 반경 1km내에 식당을 이름순으로 조회
  @Public()
  @Post('/coordinates-name')
  async findResWithName(
    @Body() coordinatesData: any,
  ): Promise<{ 근처식당목록: Stores[] }> {
    console.log(coordinatesData);
    const { swLatlng, neLatlng } = coordinatesData;
    return this.storesService.findResWithName(
      swLatlng.Ma,
      swLatlng.La,
      neLatlng.Ma,
      neLatlng.La,
    );
  }
  //이름순으로 정렬하는 api
  async findResWithName(
    southWestLatitude: number,
    southWestLongitude: number,
    northEastLatitude: number,
    northEastLongitude: number,
  ): Promise<{ 근처식당목록: Stores[] }> {
    //일단 모든 data를 조회한다
    const restaurants = await this.storesRepository.findAll();
    const restaurantsWithinRadiusAndName = restaurants.filter((restaurant) => {
      const withinLatitudeRange =
        Number(restaurant.La) >= southWestLatitude &&
        Number(restaurant.La) <= northEastLatitude;
      const withinLongitudeRange =
        Number(restaurant.Ma) >= southWestLongitude &&
        Number(restaurant.Ma) <= northEastLongitude;
      return withinLatitudeRange && withinLongitudeRange;
    });

    restaurantsWithinRadiusAndName.sort((a, b) => {
      const nameA = a.storeName.toUpperCase();
      const nameB = b.storeName.toUpperCase();
      if (nameA < nameB) {
        return -1;
      }
      if (nameA > nameB) {
        return 1;
      }
      return 0;
    });

    console.log(restaurantsWithinRadiusAndName);
    return { 근처식당목록: restaurantsWithinRadiusAndName };
  }

  //현재 웨이팅수 적은기준으로 정렬하는 api
  async findResWithWaitingCnt(
    southWestLatitude: number,
    southWestLongitude: number,
    northEastLatitude: number,
    northEastLongitude: number,
  ): Promise<{ 근처식당목록: Stores[] }> {
    //일단 모든 data를 조회한다
    const restaurants = await this.storesRepository.findAll();
    const restaurantsWithWaitingCnt = restaurants.filter((restaurant) => {
      const withinLatitudeRange =
        Number(restaurant.La) >= southWestLatitude &&
        Number(restaurant.La) <= northEastLatitude;
      const withinLongitudeRange =
        Number(restaurant.Ma) >= southWestLongitude &&
        Number(restaurant.Ma) <= northEastLongitude;
      return withinLatitudeRange && withinLongitudeRange;
    });

    restaurants.sort((a, b) => a.currentWaitingCnt - b.currentWaitingCnt);

    console.log(restaurantsWithWaitingCnt);
    return { 근처식당목록: restaurantsWithWaitingCnt };
  }

  //현재 웨이팅수 많은기준으로 정렬하는 api
  async findResWithWaitingCnt2(
    southWestLatitude: number,
    southWestLongitude: number,
    northEastLatitude: number,
    northEastLongitude: number,
  ): Promise<{ 근처식당목록: Stores[] }> {
    //일단 모든 data를 조회한다
    const restaurants = await this.storesRepository.findAll();
    const restaurantsWithWaitingCnt = restaurants.filter((restaurant) => {
      const withinLatitudeRange =
        Number(restaurant.La) >= southWestLatitude &&
        Number(restaurant.La) <= northEastLatitude;
      const withinLongitudeRange =
        Number(restaurant.Ma) >= southWestLongitude &&
        Number(restaurant.Ma) <= northEastLongitude;
      return withinLatitudeRange && withinLongitudeRange;
    });

    restaurants.sort((a, b) => b.currentWaitingCnt - a.currentWaitingCnt);

    console.log(restaurantsWithWaitingCnt);
    return { 근처식당목록: restaurantsWithWaitingCnt };
  }

  //rating 높은기준으로 정렬하는 api
  async findResWithRating(
    southWestLatitude: number,
    southWestLongitude: number,
    northEastLatitude: number,
    northEastLongitude: number,
  ): Promise<{ 근처식당목록: Stores[] }> {
    //일단 모든 data를 조회한다
    const restaurants = await this.storesRepository.findAll();
    const restaurantsWithRating = restaurants.filter((restaurant) => {
      const withinLatitudeRange =
        Number(restaurant.La) >= southWestLatitude &&
        Number(restaurant.La) <= northEastLatitude;
      const withinLongitudeRange =
        Number(restaurant.Ma) >= southWestLongitude &&
        Number(restaurant.Ma) <= northEastLongitude;
      return withinLatitudeRange && withinLongitudeRange;
    });

    restaurants.sort((a, b) => b.rating - a.rating);

    console.log(restaurantsWithRating);
    return { 근처식당목록: restaurantsWithRating };
  }

 

controller.ts

  //웨이팅카운트 적은순으로 조회
  @Public()
  @Post('/coordinates-waitingcnt')
  async findResWithWaitingCnt(
    @Body() coordinatesData: any,
  ): Promise<{ 근처식당목록: Stores[] }> {
    console.log(coordinatesData);
    const { swLatlng, neLatlng } = coordinatesData;
    return this.storesService.findResWithWaitingCnt(
      swLatlng.Ma,
      swLatlng.La,
      neLatlng.Ma,
      neLatlng.La,
    );
  }

  //웨이팅카운트 많은순으로 조회
  @Public()
  @Post('/coordinates-waitingcnt2')
  async findResWithWaitingCnt2(
    @Body() coordinatesData: any,
  ): Promise<{ 근처식당목록: Stores[] }> {
    console.log(coordinatesData);
    const { swLatlng, neLatlng } = coordinatesData;
    return this.storesService.findResWithWaitingCnt2(
      swLatlng.Ma,
      swLatlng.La,
      neLatlng.Ma,
      neLatlng.La,
    );
  }

  //별점 높은 순으로 조회
  @Public()
  @Post('/coordinates-rating')
  async findResWithRating(
    @Body() coordinatesData: any,
  ): Promise<{ 근처식당목록: Stores[] }> {
    console.log(coordinatesData);
    const { swLatlng, neLatlng } = coordinatesData;
    return this.storesService.findResWithRating(
      swLatlng.Ma,
      swLatlng.La,
      neLatlng.Ma,
      neLatlng.La,
    );
  }

 

 

1차 수정 후 

service.ts

async searchRestaurants(
    southWestLatitude: number,
    southWestLongitude: number,
    northEastLatitude: number,
    northEastLongitude: number,
    sort?: 'distance' | 'name' | 'waitingCnt' | 'waitingCnt2' | 'rating',
  ): Promise<{ 근처식당목록: Stores[] }> {
    const restaurants = await this.storesRepository.findAll();
    const restaurantsWithinRadius = restaurants.filter((restaurant) => {
      const withinLatitudeRange =
        Number(restaurant.La) >= southWestLatitude &&
        Number(restaurant.La) <= northEastLatitude;
      const withinLongitudeRange =
        Number(restaurant.Ma) >= southWestLongitude &&
        Number(restaurant.Ma) <= northEastLongitude;
      return withinLatitudeRange && withinLongitudeRange;
    });

    if (sort === 'distance') {
      const userLocation = {
        latitude: southWestLatitude,
        longitude: southWestLongitude,
      };

      restaurantsWithinRadius.forEach((restaurant) => {
        const distance = this.calculateDistance(userLocation, {
          latitude: restaurant.La,
          longitude: restaurant.Ma,
        });
        restaurant.distance = distance;
      });

      restaurantsWithinRadius.sort(
        (a, b) => (a.distance || 0) - (b.distance || 0),
      );
    } else if (sort === 'name') {
      restaurantsWithinRadius.sort((a, b) => {
        const nameA = a.storeName.toUpperCase();
        const nameB = b.storeName.toUpperCase();
        if (nameA < nameB) {
          return -1;
        }
        if (nameA > nameB) {
          return 1;
        }
        return 0;
      });
    } else if (sort === 'waitingCnt') {
      restaurantsWithinRadius.sort(
        (a, b) => a.currentWaitingCnt - b.currentWaitingCnt,
      );
    } else if (sort === 'waitingCnt2') {
      restaurantsWithinRadius.sort(
        (a, b) => b.currentWaitingCnt - a.currentWaitingCnt,
      );
    } else if (sort === 'rating') {
      restaurantsWithinRadius.sort((a, b) => b.rating - a.rating);
    }

    console.log(restaurantsWithinRadius);
    return { 근처식당목록: restaurantsWithinRadius };
  }

 

controller.ts

//사용자 위치 기반 반경 1km내의 식당 조회
  @Public()
  @Post('/coordinates')
  async searchRestaurants(
    @Body() coordinatesData: any,
    @Query('sort')
    sort?: 'distance' | 'name' | 'waitingCnt' | 'waitingCnt2' | 'rating',
  ): Promise<{ 근처식당목록: Stores[] }> {
    console.log(coordinatesData);
    const { swLatlng, neLatlng } = coordinatesData;
    const southWestLatitude = swLatlng.La;
    const southWestLongitude = swLatlng.Ma;
    const northEastLatitude = neLatlng.La;
    const northEastLongitude = neLatlng.Ma;

    const restaurants = await this.storesService.searchRestaurants(
      southWestLatitude,
      southWestLongitude,
      northEastLatitude,
      northEastLongitude,
      sort,
    );

    return restaurants;
  }

 

코드는 맞는 것 같은데 계속해서 빈 배열이 출력됐다..

 

해결하기 위한 시도1

console을 찍어보니 

async searchRestaurants(
    southWestLatitude: number,
    southWestLongitude: number,
    northEastLatitude: number,
    northEastLongitude: number,
    sortBy?: 'distance' | 'name' | 'waitingCnt' | 'waitingCnt2' | 'rating',
  ): Promise<{ 근처식당목록: Stores[] }> {
    const restaurants = await this.storesRepository.findAll();
    //전체 레스토랑까지 잘 불러와짐

    const restaurantsWithinRadius = restaurants.filter((restaurant) => {
      const withinLatitudeRange =
        Number(restaurant.La) >= southWestLatitude &&
        Number(restaurant.La) <= northEastLatitude;
      const withinLongitudeRange =
        Number(restaurant.Ma) >= southWestLongitude &&
        Number(restaurant.Ma) <= northEastLongitude;
      return withinLatitudeRange && withinLongitudeRange;
    });

    console.log(restaurantsWithinRadius, '필터링된 레스토랑');

 

repository에서 findAll()메서드는 잘 작동하고 있었다.

전체 레스토랑은 잘 불러와지지만 필터링부분에서 문제가 생겨 빈 배열이 반환되는 것 같았다.

이 밖에 console로 controller에서 요청되는 좌표가 잘 찍히는지도 확인했다.

 

 

 

해결하기 위한 시도2

뒷번호의 레스토랑엔 좌표가 설정되어 있지 않을 수도 있어서 조회 순서를 storeId로 정렬하고 배열 앞 3가지를 불러와봄.

  async searchRestaurants(
    southWestLatitude: number,
    southWestLongitude: number,
    northEastLatitude: number,
    northEastLongitude: number,
    sortBy?: 'distance' | 'name' | 'waitingCnt' | 'waitingCnt2' | 'rating',
  ): Promise<{ 근처식당목록: Stores[] }> {
    const restaurants = await this.storesRepository.findAll();
    //전체 레스토랑까지 잘 불러와짐
    console.log(
      restaurants[0],
      restaurants[1],
      restaurants[2],
      'findAll 메서드의 작동 잘 됌',
    );

반환값

3000 포트로 연결되었습니다.
{
  swLatlng: { La: 127.09, Ma: 37.585 },
  neLatlng: { La: 127.097, Ma: 37.59 }
}
실행중
Stores {
  storeId: 456137,      
  storeName: '카페하루',
  description: 'string',
  category: '기타',     
  maxWaitingCnt: 0,
  currentWaitingCnt: 0,
  La: '0',
  Ma: '0',
  address: '대전광역시 대덕구 대덕대로 1593, 1층 (석봉동)',
  distance: null,
  oldAddress: null,
  cycleTime: 60,
  tableForTwo: 0,
  tableForFour: 6,
  rating: 0,
  createdAt: 2023-05-31T10:12:52.625Z,
  updatedAt: 2023-05-31T10:12:52.625Z
} Stores {
  storeId: 456141,
  storeName: '1988지지미',
  description: 'string',
  category: '기타',
  maxWaitingCnt: 0,
  currentWaitingCnt: 0,
  La: '0',
  Ma: '0',
  address: '인천광역시 서구 서곶로315번길 18, 2동1층일부 (심곡동)',
  distance: null,
  oldAddress: null,
  cycleTime: 60,
  tableForTwo: 2,
  tableForFour: 5,
  rating: 0,
  createdAt: 2023-05-31T10:12:52.689Z,
  updatedAt: 2023-05-31T10:12:52.689Z
} Stores {
  storeId: 456145,
  storeName: '명륜진사갈비 간석점',
  description: 'string',
  category: '식육(숯불구이)',
  maxWaitingCnt: 0,
  currentWaitingCnt: 0,
  La: '0',
  Ma: '0',
  address: '인천광역시 남동구 석정로 465, 1층 전부호 (간석동)',
  distance: null,
  oldAddress: null,
  cycleTime: 60,
  tableForTwo: 6,
  tableForFour: 1,
  rating: 0,
  createdAt: 2023-05-31T10:12:52.753Z,
  updatedAt: 2023-05-31T10:12:52.753Z
} findAll 메서드의 작동 잘 됌
127.09
[] 필터링된 레스토랑
[] 정렬된 레스토랑

La, Ma가 불러와지지 않고있었다..DB에도 정상적으로 저장되어 있는데 왜 안불러와질까요.. 

console 로 찍어보니 좌표안에 해당되는 값이 전부 false를 반환하고 있었다, 즉 좌표내에 해당하는 가게를 읽을 수 없는 문제인데..

 

해결하기 위한 시도3

알고보니 request 값이 잘못 되어있었다. 

아마 프로젝트 중간에 뭔가 수정이 들어가서 그런 것 같은데  Ma, La값을 바꿔 넣어주니 정상작동 됐다.

 

Latitude : 위도 

Ma : 경도 (Longitude, Lng)

 

swLatlng : Southwest Latitude and Longitude 남서쪽 경도와 위도

neLatlng : Northeast Latitude and Longitude 북동쪽 경도와 위도

{
  "swLatlng": {
    "Ma": 127.090,
    "La": 37.585
  },
  "neLatlng": {
    "Ma": 127.097,
    "La": 37.590
  }
}

이 정보는 사각형 영역을 나타내는 좌표값이다. 

남서쪽 좌표와 북동쪽 좌표를 사용하여 사각형 영역을 정의, 해당 영역 내의 식당을 검색하는 용도로 사용된다.

 

 

최종코드

위와 같이 코드를 작성하고나니 distance 항목이 distance로 sort할때만 계산되어 출력되는 문제가 생겼다. 

distance값을 전역에서 계산해서 각 sort결과 값에 모두 출력되게 수정했다.

  async searchRestaurants(
    southWestLatitude: number,
    southWestLongitude: number,
    northEastLatitude: number,
    northEastLongitude: number,
    sortBy?: 'distance' | 'name' | 'waitingCnt' | 'waitingCnt2' | 'rating',
  ): Promise<{ 근처식당목록: Stores[] }> {
    const restaurants = await this.storesRepository.findAll();

    const restaurantsWithinRadius = restaurants.filter((restaurant) => {
      const withinLatitudeRange =
        Number(restaurant.La) >= southWestLatitude &&
        Number(restaurant.La) <= northEastLatitude;
      const withinLongitudeRange =
        Number(restaurant.Ma) >= southWestLongitude &&
        Number(restaurant.Ma) <= northEastLongitude;
      // console.log(withinLatitudeRange, withinLongitudeRange);
      return withinLatitudeRange && withinLongitudeRange;
    });

    // 거리 계산 로직
    const calculateDistance = (
      source: { latitude: number; longitude: number },
      target: { latitude: number; longitude: number },
    ): number => {
      const latDiff = Math.abs(source.latitude - target.latitude);
      const lngDiff = Math.abs(source.longitude - target.longitude);
      const approximateDistance = Math.floor(
        latDiff * 111000 + lngDiff * 111000,
      );
      return approximateDistance;
    };

    //user위치에 따른 거리값을 모든 sort조건에 포함시켜준다
    const userLocation = {
      latitude: southWestLatitude,
      longitude: southWestLongitude,
    };
    restaurantsWithinRadius.forEach((restaurant) => {
      const distance = calculateDistance(userLocation, {
        latitude: Number(restaurant.La),
        longitude: Number(restaurant.Ma),
      });
      restaurant.distance = distance;
    });

    //정렬로직모음
    restaurantsWithinRadius.sort((a, b) => {
      if (sortBy === 'distance') {
        return (a.distance || 0) - (b.distance || 0);
      } else if (sortBy === 'name') {
        const nameA = a.storeName.toUpperCase();
        const nameB = b.storeName.toUpperCase();
        if (nameA < nameB) {
          return -1;
        }
        if (nameA > nameB) {
          return 1;
        }
        return 0;
      } else if (sortBy === 'waitingCnt') {
        return a.currentWaitingCnt - b.currentWaitingCnt;
      } else if (sortBy === 'waitingCnt2') {
        return b.currentWaitingCnt - a.currentWaitingCnt;
      } else if (sortBy === 'rating') {
        return b.rating - a.rating;
      }
      return 0;
    });

    return { 근처식당목록: restaurantsWithinRadius };
  }

 

✅오늘 알게된 점 및 추후 학습 계획

코드 에러 잡는데는 디버깅이 최고지만 아무리 console을 찍어봐도 해결이 안되는 문제는 팀단위로 해결하는게 빠르다! 

내가 모르는 곳에서 수정이 들어가있을 수도 있기 때문에.. 

 

반응형