코딩과 결혼합니다

[Game_Crew] 유저 평점 기능 본문

코딩과 매일매일♥/Game_Crew

[Game_Crew] 유저 평점 기능

코딩러버 2023. 10. 27. 17:09
728x90
게임크루 프로젝트의 핵심 기능 : 유저 평점 기능

WHY?
유저들이 보다 우수한 게임 경험을 가질 수 있도록 돕기 위해 유저들이 게임 파트너를 선택하는 데에 유용한 참고자료를 제공하려 한다.


HOW?
게임을 함께 했던 유저들의 매너, 참여도, 실력, 재미, 친화력 등을 평가하여 종합 점수를 생성한다. 이 평점은 다른 유저들에게 제공되며, 게임 파트너를 선택하는 데 도움을 준다.

'유저 평점'이라는 기능을 도입하여 게임 크루 구성원들 간의 상호 신뢰도를 높이고, 원활한 게임 플레이를 위한 소통과 협력을 촉진하는 역할을 하고자 한다. 이 기능을 통해 게임크루 프로젝트는 유저들에게 더 나은 게임 경험을 제공하게 된다.

 

 

📌유저 평점 등록

    //유저의 평점을 DB에 등록하는 API
    @PostMapping("/rating/{evaluated_user}")
    public MessageResponseDto registrationOfRatings(@RequestBody UserRatingRequestDto userRatingRequestDto,
                                                    @AuthenticationPrincipal UserDetailsImpl userDetails,
                                                    @PathVariable Long evaluated_user){
        if (userRepository.findById(evaluated_user).isEmpty()) throw new CustomException(ErrorMessage.NON_EXISTENT_USER, HttpStatus.BAD_REQUEST, false);

        User evaluator = userDetails.getUser();
        ratingService.registrationOfRatings(userRatingRequestDto, evaluator, evaluated_user);

        return new MessageResponseDto(Message.REGISTRATION_COMPLETED, HttpStatus.OK);
    }

 

JWT 인증이 된 유저는 다른 유저와 게임 후에 평가를 할 수 있다.

evaluator(평가한 사람)은 @AuthenticationPrincipal UserDetailsImpl userDetails 를 통해서 user의 정보를 가져와주고,

evaluatedUser(평가 받은 사람) 는 @PathVariable 로 로그인이 안되어 있는 상태에서도 평가 받을 수 있게 하였다.

원래는 [userId]라는 key값으로 [rating]관련 테이블들과 연관관계를 맺으려 하였지만, 평가 받는 유저는 인증이 필요하지 않기 때문에 Long으로 값을 받아와야 해서 연관관계를 맺어줄 수 없었다. (User 클래스로 가져올 수 없음) 그래서 각각 따로 관리하고 로직을 적절하게 구현함으로써 DB에 저장하고 값을 불러오게끔 하였다.

 

✏️전적 테이블

@Entity
@Getter
@Setter
@NoArgsConstructor
public class RecordOfRatings{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Long id;

    @Column(nullable = false)
    private Long user; // 평가 받은 사람

    @Column(nullable = false)
    private Long evaluator; //평가한 사람

    @Column(nullable = false)
    private int manner;

    @Column(nullable = false)
    private int participation;

    @Column(nullable = false)
    private int gamingSkill;

    @Column(nullable = false)
    private int enjoyable;

    @Column(nullable = false)
    private int sociability;

    @Column(nullable = false)
    private double totalRating;

 

그동안 받았던 점수들을 확인할 수 있도록 RecordOfRatings 테이블을 구성하였다.

어떤 사람(evaluator)이 나(user)에게 점수를 어떻게 줬는지를 알 수 있도록!

 

✏️총 점수 테이블

@Entity
@Getter
@Setter
@NoArgsConstructor
public class TotalRating {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Long id;

    @Column(nullable = false)
    private Long evaluatedUserId; // 평가 받은 사람

    @Column(nullable = false)
    private double totalManner;

    @Column(nullable = false)
    private double totalParticipation;

    @Column(nullable = false)
    private double totalGamingSkill;

    @Column(nullable = false)
    private double totalEnjoyable;

    @Column(nullable = false)
    private double totalSociability;

    @Column(nullable = false)
    private double totalRating;

//...

    public void setTotalManner(double totalManner) {
        this.totalManner = Math.round(totalManner * 2) / 2.0;;
    }

    public void setTotalParticipation(double totalParticipation) {
        this.totalParticipation = Math.round(totalParticipation * 2) / 2.0;;
    }

    public void setTotalGamingSkill(double totalGamingSkill) {
        this.totalGamingSkill = Math.round(totalGamingSkill * 2) / 2.0;;
    }

    public void setTotalEnjoyable(double totalEnjoyable) {
        this.totalEnjoyable = Math.round(totalEnjoyable * 2) / 2.0;;
    }

    public void setTotalSociability(double totalSociability) {
        this.totalSociability = Math.round(totalSociability * 2) / 2.0;;
    }

 

 

특정 유저의 총 점수를 나타내는 테이블로 유저 한 명당 딱 한 번만 DB에 저장될 것이고,set메서드로 계속해서 점수를 업데이트 시켜줄 것이다. 점수는 0.5점을 기준으로 하기 때문에 점수를 반올림해주는 메서드를 멤버변수마다 작성해준다.

 

✏️service

    public void registrationOfRatings(UserRatingRequestDto userRatingRequestDto, User evaluator, Long evaluated_user) {
        //request ...

        Optional<TotalRating> checkRating = totalRatingRepository.findByEvaluatedUserId(evaluated_user);
        if (checkRating.isEmpty()){
            double totalRating = (double)(manner + participation + gamingSkill + enjoyable + sociability) / 5 ;

            RecordOfRatings ratings = new RecordOfRatings(evaluated_user ,evaluatorId, manner, participation, gamingSkill, enjoyable, sociability, totalRating);
            recordOfRatingsRepository.save(ratings);

            TotalRating totalrating = new TotalRating(evaluated_user, manner, participation, gamingSkill, enjoyable, sociability, totalRating);
            totalRatingRepository.save(totalrating);

 

totalRatingRepository( 총 평점 테이블 )에 입력받은 evaluated_user 가 없다면

recordOfRatingsRepository(전적) 와 totalRatingRepository(총 평점) 에 받은 값들을 그대로 저장해준다.

 

        } else if (checkRating.isPresent()) {
            TotalRating existingTotalRating = checkRating.get();

            //있으면 저장된 점수 + 새로 들어온 점수 더하기 + /2
            double totalManner = (existingTotalRating.getTotalManner() + manner)/2;
            //...
            double total =(totalManner + totalParticipation + totalGamingSkill + totalEnjoyable + totalSociability)/5;

            existingTotalRating.setTotalManner(totalManner);
            //...
            existingTotalRating.setTotalRating(total);

            double totalRating = (double)(manner + participation + gamingSkill + enjoyable + sociability) / 5 ;
            RecordOfRatings ratings = new RecordOfRatings(evaluated_user ,evaluatorId, manner, participation, gamingSkill, enjoyable, sociability, totalRating);
            recordOfRatingsRepository.save(ratings);
        }
    }

 

totalRatingRepository( 총 평점 테이블 )에 입력받은 evaluated_user 가 존재하면
이전의 점수에 새로 입력받은 점수를 합한 뒤 2로 나누어 준다. 그리고 set메서드로 정보를 업데이트 해준다.

마지막으로 RecordOfRatings에도 입력받은 값들을 저장해준다. 이렇게 업데이트 + 전적 저장을 반복한다.


📌유저 평점 조회

    //유저의 평점을 가져오는 API
    @GetMapping("/getRating/{evaluated_user}")
    public UserTotalRatingResponseDto getUserRating(@PathVariable Long evaluated_user){
        return ratingService.getUserRating(evaluated_user);
    }

 

유저의 평점같은 경우에는 비로그인 유저도 확인할 수 있게 하였다. 마찬가지로 @PathVariable 로 평점을 조회하고 싶은 유저의 id를 입력받고

    public UserTotalRatingResponseDto getUserRating(Long evaluated_user) {
        Optional<TotalRating> checkUser= totalRatingRepository.findByEvaluatedUserId(evaluated_user);
        if (checkUser.isEmpty()){
            throw new CustomException(ErrorMessage.NON_EXISTENT_USER, HttpStatus.BAD_REQUEST, false);
        }
        TotalRating existingTotalRating = checkUser.get();
        double totalManner = existingTotalRating.getTotalManner();
        double totalParticipation = existingTotalRating.getTotalParticipation();
        double totalGamingSkill = existingTotalRating.getTotalGamingSkill();
        double totalEnjoyable =existingTotalRating.getTotalEnjoyable();
        double totalSociability = existingTotalRating.getTotalSociability();
        double total = existingTotalRating.getTotalRating();
        return new UserTotalRatingResponseDto(totalManner, totalParticipation, totalGamingSkill, totalEnjoyable, totalSociability, total);
    }

 

totalRatingRepository에서 값을 가져와 프런트로 넘겨준다.


✔️ 문제

위의 방식대로 평점을 낸다면 그동안 꾸준히 좋은 점수를 받다가 나쁜 점수를 한 번 받은 유저와, 그동안 나쁜 점수만 받다가 한 번 좋은 점수를 받은 유저와의 차이가 나지 않게 된다. 계속해서 이전 점수와 새로운 점수를 더해서 2로 나눌 뿐이기 때문이다.

 

✔️ 해결 방안

1. RecordRatingRepository에 평가한 시간을 추가한다.

2. 최근 전적들 최대 10개까지 모아서 더한 후에 평균을 낸다.

3. 10개가 안되는 전적을 가지고 있다면 (모든 점수들의 합 / 전적의 개수)를 해준다.

 

✔️ 문제2

중복되는 코드들이 많이 보인다. 일단은 프로젝트 기간이 얼마 안남았으니 MVP 기능을 끝낸 뒤에 리팩토링을 하여 코드의 가독성과 유지보수성을 높여야 겠다.

 

https://coding-s2-chaewon.tistory.com/215