728x90
반응형

컬렉션 안에 있는 맛집별로 평균 평점을 구하는 작업을 했다.

처음엔 아무 생각없이 모든 평점을 모아서 평균을 냈는데, 생각해보니

1가지 맛집에 대한 평점만 낸것이어서, 다른 맛집도 추가하여 분리해보았다.

 

맛집ID가 같은 것끼리 분리해야하니, 그룹화가 필요했고,

그룹화 이후 각 포스트 정보는 1개씩만 대표로 가져오게 했다.

 

  async getMyListDetail(collectionId: number, page: string) {
    try {
      let pageNum = Number(page) - 1;
      const myListInOnePage = 1;

      if (isNaN(pageNum) || pageNum < 0) {
        pageNum = 0;
      }

      // 컬렉션 이름과 포스트 정보 가져오기
      const myList = await this.collectionRepository.find({
        relations: {
          collectionItems: {
            post: { images: true, restaurant: true },
          },
        },
        where: {
          id: collectionId,
          // user_id: userId,
          deletedAt: null,
          type: 'myList',
        },
        select: {
          id: true,
          name: true,
          description: true,
          visibility: true,
          collectionItems: {
            id: true,
            post: {
              id: true,
              content: true,
              rating: true,
              images: true,
              restaurant: {
                id: true,
                x: true,
                y: true,
                place_name: true,
                category_group_name: true,
              },
            },
          },
        },
        skip: pageNum * myListInOnePage,
        take: myListInOnePage,
      });

      const [myListDetail] = myList.map((myList) => {
        const groupedPosts = {};
        for (const item of myList.collectionItems) {
          
          const restaurantId = item.post.restaurant.id;
          if (!groupedPosts[restaurantId]) {
            groupedPosts[restaurantId] = { posts: [], sum: 0, count: 0 };
          }
          groupedPosts[restaurantId].posts.push({
            ...item.post,
            restaurant: item.post.restaurant,
            images: item.post.images,
          });
          
          const rating = item.post.rating;
          if (typeof rating === 'number') {
            groupedPosts[restaurantId].sum += rating;
            groupedPosts[restaurantId].count++;
          }
        }

        
        const formattedPosts = [];
        for (const groupId in groupedPosts) {
          const group = groupedPosts[groupId];
          const avgRating =
            group.count > 0 ? (group.sum / group.count).toFixed(1) : null;

          if (group.posts.length > 0) {
            formattedPosts.push({
              id: group.posts[0].id,
              content: group.posts[0].content,
              rating: group.posts[0].rating,
              AvgRating: avgRating,
              images: group.posts[0].images,
              restaurant: group.posts[0].restaurant,
            });
          }
        }

        return {
          id: myList.id,
          name: myList.name,
          description: myList.description,
          visibility: myList.visibility,
          post: formattedPosts,
        };
      });

      return myListDetail;
    } catch (err) {
      console.error(err);
      throw new InternalServerErrorException(
        'Something went wrong while processing your request. Please try again later.',
      );
    }
  }
728x90
반응형
728x90
반응형

요즘 뜨는 맛집리스트와 내 친구의 맛집리스트 기능구현을 했다.

 

요즘 뜨는 맛집리스트는 쿼리문을 작성하는 것까진 쉬웠으나 원하는 값을 뽑아오는건 힘들었다.

추가적으로 내 친구의 맛집리스트는 어떤식으로 데이터를 가져올지 고민을 했다.

 

우선 user에서 정보를 가져와야 할 것 같았다.

내 userId를 통해 내가 팔로잉하고 있는 유저아이디를 찾았다.

해당 유저아이디를 where에 넣어서 유저게시물 정보를 찾아오는 방식으로 구현했다.

(유저아이디 먼저 찾고, 유저아이디를 토대로 게시물 정보 찾기)

 

const followerId = await this.followRepository.find({
        where: {
          follower: { id: userId },
        },
        select: {
          following: { id: true },
        },
        relations: {
          following: true,
        },
      });

      const followingIds = followerId
        .map((f) => f.following.id)
        .filter((id) => !isNaN(id));

      const myListFollwers = await this.collectionItemRepository.find({
        relations: {
          post: {
            user: true,
            images: true,
          },
          collection: {
            user: true,
          },
        },
        where: {
          collection: {
            type: 'myList',
            deletedAt: null,
            user_id: In(followingIds), //팔로워들의 아이디
          },
        },
        select: {
          post: {
            id: true,
            images: { id: true, file_url: true },
            user: {
              id: true,
              nickname: true,
            },
          },
          collection: {
            id: true,
            name: true,
            user: {
              id: true,
              nickname: true,
            },
          },
        },
      });

 

 

 

728x90
반응형
728x90
반응형

오늘 해야할 일은 메인에 관한 정보이다.

이 부분에 대한 정보를 가져와야하는데 우선 어떤 저장소에서 정보를 가져와야할지 고민했다.

고민한 결과 컬렉션아이템이라는 테이블을 사용하는 것이 가장 좋을 것 같았다.

  /* 로직 설명
요즘 뜨는 맛집 리스트 ⇒ 
★ 마이리스트에 있는 게시물 좋아요 총합순. 좋아요총합 설정★
1) 모든 마이리스트를 조회한다.
2) 모든 마이리스트의 게시물의 좋아요 수를 더한다
3) 좋아요 총합이 가장 좋은 마이리스트순으로
    마이리스트 이름과 닉네임과 포스트 이미지를 반환한다.
4) 현재(최신)유행하는 날짜 둘다 받는거로 한다. 날짜설정★
*/

다음은 어떤 방식으로 결과값을 가져와야할지 고민했다.

초기에는 위처럼 작성하면서 쿼리문을 작성해봤다.

 

const myListSumLikes = await this.collectionItemRepository.find({
        relations: {
          post: {
            postLikes: true,
            user: true,
            images: true,
          },
          collection: {
            user: true,
          },
        },
        where: {
          // 컬렉션 타입이 myList 이면서 삭제되지 않은 것을 가져온다
          collection: {
            type: 'myList',
            deletedAt: null,
          },
          post: {
            // 좋아요가 삭제되지 않았고, 1달 이내에 좋아요 업데이트된 게시물만 가져온다
            postLikes: {
              deleted_at: null,
              updated_at: MoreThan(oneMonthAgo),
            },
          },
        },
        select: {
          id: true,
          post: {
            id: true,
            images: { id: true, file_url: true },
            postLikes: {
              id: true,
            },
            user: {
              id: true,
              nickname: true,
            },
          },
          collection: {
            id: true,
            name: true,
          },
        },
      });

예전 같았으면 하루종일 매달려도 완성하지 못했을 코드다.

그런데 테이블간의 관계도 익숙해지고, 쿼리문도 익숙해지다보니 몇십분만에 뚝딱 만들었다.

이후 찾은 값을 어떠한 조건속에서 값을 반환해야하는지 많은 고민을 하고 기능을 구현했다.

 

728x90
반응형
728x90
반응형

[Feature] 마이리스트 상세보기

 

 async getMyListDetail(collectionId: number) {
    try {
      // 컬렉션 이름과 포스트 정보 가져오기
      const myList = await this.collectionRepository.find({
        relations: {
          collectionItems: {
            post: { images: true, restaurant: true },
          },
        },
        where: {
          id: collectionId,
          // user_id: userId,
          deletedAt: null,
          type: 'myList',
        },
        select: {
          id: true,
          name: true,
          visibility: true,
          collectionItems: {
            id: true,
            post: {
              id: true,
              content: true,
              rating: true,
              images: true,
              restaurant: {
                id: true,
                x: true,
                y: true,
                place_name: true,
              },
            },
          },
        },
      });

      return myList.map((myList) => ({
        id: myList.id,
        name: myList.name,
        visibility: myList.visibility,
        post: myList.collectionItems.map((item) => ({
          ...item.post,
          restaurant: item.post.restaurant,
          images: item.post.images,
        })),
      }));
    }

위 기능을 만들기 위해선 쿼리문이 가장 문제였다.

테이블과 테이블간의 관계가 너무 복잡했다.

잘하는 팀원의 도움을 받아서 컬렉션 아이템에 있는 post정보,

또 post안에 있는 restaurant 정보는 어떻게 가져오는지 많이 배웠다. 

 

쿼리문은 많이 익숙해졌으나, 가져오는 값이 대부분 객체 형식이다보니

map함수를 통해 값을 찾아야하는데 익숙해지기 어렵다.

 

 

 

728x90
반응형
728x90
반응형
[FIX] : 포스트 수정시에 마이리스트를 변경할 경우 
원래있던 마이리스트에 포스팅은 삭제되는 기능 #76

위 문제를 대해서 몇시간동안 고민했다.

원하는 방법은 입력값을 넣을때 기존값이랑 중복되면 기존값을 유지하고, 새로운 값은 추가한다.

 

하지만 그렇게 하려니 너무 머리아팠다.

나는 아직 초보기 때문에 어쨌든 기능을 만들고 싶었던 결과,

 

	// 1. 입력받은 값으로 컬렉션아이템에 있는 포스트아이디를 모두 찾는다.
      const findPostId = await this.collectionItemRepository.find({
        relations: ['post', 'collection'],
        where: {
          post: { id: postId },
          collection: { type: 'myList' }, //마이리스트 일때만!
        },
      });

      // 2. 컬렉션아이템에서 해당 포스트 아이디로 검색되는거 다지운다.
      await this.collectionItemRepository.remove(findPostId);
      // 3. 입력받은 정보로 모두 넣어준다.
      await this.myListPlusPosting(postId, collectionId);
      return;

무식하지만 간단하게 만들었다.

728x90
반응형
728x90
반응형

오늘 마이리스트 상세 더보기 기능을 만들었다.

기존의 코드를 복사하여 어떻게 짜맞추기하다가 도저히

안되서 튜터님에게 찾아가 질문을 했다.

 

설명하는 도중에 튜터님이 무슨말인지, 무슨로직인지 모르겠다고 얘기해주셨고

나보고 완전 초보이더라도 이해할 수 있게 말로 풀어서 설명하고, 쿼리문을 작성해보라고 하셨다.

 

순간 당황했지만 천천히 작성해봤더니 로직자체가 잘못된 것이었다.

그리고 쿼리문을 작성해보니 원래 사용하던 타입ORM의 원리도 조금 이해가 되었다.

 

결국 튜터님의 조언을 듣고 기존 코드를 모두 지우고

오랜시간 고민했던 코드가 30분만에 해결이 되었다.

 

  /* 로직 설명
      1. 맛집상세리스트 PAGE2에 있는 맛집을 클릭한다. (레스토랑ID)
      2. 콜렉션 아이템에 있는 레스토랑아이디와 콜렉션아이디가 둘다 일치하는 정보를 찾는다.
      3. 레스토랑의 정보와 게시물 정보를 가져온다
      레스토랑 정보: 가게이름, 업종(카페), 주소
      포스팅 정보: 설명, 이미지, 평점 ,좋아요, 댓글 등 
    */
  async getMyListsDetailPost(
    userId: number,
    restaurantId: number,
    collectionId: number,
  ) {
    try {
      //컬렉션아이템에서 맛집아이디에 관한 정보 찾기
      const existRestaurant = await this.collectionItemRepository.find({
        where: {
          restaurant: { id: restaurantId },
          collection: { id: collectionId },
        },
        select: {
          restaurant: {
            id: true,
            category_group_name: true,
            road_address_name: true,
            place_name: true,
          },
          post: {
            content: true,
            rating: true,
          },
        },
        relations: ['restaurant', 'post'],
      });

      return existRestaurant;

 

오랜만에 정말 뿌듯하고 깔끔하게 코드를 작성해서 기분이 좋은 하루였다.

다른 기능구현도 위와 같이 말로 정리하고, 쿼리문으로 먼저 찾아본후 작업해야겠다!

728x90
반응형
728x90
반응형

오늘은 추가로 구현해야 할 기능이 있었다.

마이리스트, 북마크에 포스팅 추가시 중복되면

못들어가게 막아야하는데 해당 값을 가져오질 못해서

몇시간동안 고생했지만 찾을 수 없었다...

튜터님 시간될때 질문을 통해 해결해야 할 것 같다.

 

그전에 이제 모든 코드를 병합하기 위해서

나도 유즈 가드를 달기 시작했다.

  @Get('/collections')
  @UseGuards(AuthAccessGuard)
  @ApiOperation({ summary: 'MyList 전체조회(내꺼)' })
  @ApiResponse({ status: 200, description: 'MyList 전체조회(내꺼) 성공' })
  @ApiResponse({ status: 400, description: 'MyList 전체조회(내꺼) 실패' })
  async getMyListsMe(@CurrentUser() currentUser: any) {
    const myLists = await this.myListService.getMyListsMe(currentUser.id);
    return await myLists;
  }

북마크와 마이리스트에 모두 달았으며,

위의 코드만 알고 있으면 달기편하다.

다른 팀원이 만들었지만 그걸 쉽게 이용할 수 있다니 참 편한 것 같다.

728x90
반응형
728x90
반응형

벌써 3주차가 시작되었다.

이전까지 완료한 것은 마이리스트

오늘은 비슷한 기능을 하는 북마크 기능구현을 시작했다.

 

마이리스트는 시간이 꽤 걸렸지만

기능이 비슷하다보니 하루만에 끝낼 수 있었다.

조금 다른 기능도 있어서 고생좀 했지만

하루만에 CRUD기능을 끝낼 수 있어서 뿌듯한 하루였다.

728x90
반응형

+ Recent posts