Skip to content

[FEAT] #709: 솝탬프 파트별 랭킹 파트원 수 고려해서 계산하도록 수정#713

Open
kyoooooong wants to merge 8 commits intodevfrom
feat/#709
Open

[FEAT] #709: 솝탬프 파트별 랭킹 파트원 수 고려해서 계산하도록 수정#713
kyoooooong wants to merge 8 commits intodevfrom
feat/#709

Conversation

@kyoooooong
Copy link
Copy Markdown

@kyoooooong kyoooooong commented Apr 12, 2026

Related issue 🛠

Work Description ✏️

파트별 랭킹 점수를 총점 / 현재 기수 파트원 수 기준으로 계산하도록 수정했습니다.

  • 현재 기수의 파트원 수는 auth 스키마의 user_activity_histories에서 조회하도록 했고, is_sopt = true, role = MEMBER 조건으로 일반 파트원 수만 반영되도록 했습니다.
  • 계산 로직은 기존 총점을 먼저 모은 뒤, 파트별 인원 수로 나누어 평균 점수를 만든 다음 이 값을 기준으로 rank를 다시 계산하도록 구성했습니다. 동점인 경우에는 기존처럼 공동 순위가 유지되도록 처리했습니다.
  • 응답 순서는 기존의 기획-디자인-웹-아요-안드-서버순서를 그대로 유지했습니다. 점수 계산 기준만 바꾸고 응답 순서까지 바뀌면 기존 화면 영향이 있을 수 있어, rank 계산과 응답 순서를 분리해서 구현했습니다.
  • 평균 점수는 소수점 둘째 자리까지 반올림해서 내려가도록 수정했고, 테스트도 함께 수정해 평균 점수, 공동 순위, 기존 응답 순서를 검증하도록 반영했습니다.

Related ScreenShot 📷

To Reviewers 📢

@kyoooooong kyoooooong self-assigned this Apr 12, 2026
@kyoooooong kyoooooong added the ✨ Feat 새로운 피쳐 생성 label Apr 12, 2026
@github-actions github-actions bot requested a review from jher235 April 12, 2026 21:39
@kyoooooong kyoooooong marked this pull request as draft April 12, 2026 21:39
@kyoooooong kyoooooong changed the title [FEAT] #709: 솝탬프 로직 수정 [FEAT] #709: 솝탬프 파트별 랭킹 파트원 수 고려해서 계산하도록 수정 Apr 12, 2026
@kyoooooong kyoooooong marked this pull request as ready for review April 12, 2026 22:26
Copy link
Copy Markdown
Member

@jher235 jher235 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다~~
BigDecimal도 생각 못하고 있었는데 꼼꼼하니 좋네요 👍

Comment on lines +16 to +39
public Map<SoptPart, Long> getCurrentGenerationPartMemberCounts(Long generation) {
String sql = """
SELECT part, COUNT(*) AS member_count
FROM auth_prod.user_activity_histories
WHERE generation = ?
AND is_sopt = true
AND role = 'MEMBER'
AND part IN ('IOS', 'ANDROID', 'DESIGN', 'PLAN', 'SERVER', 'WEB')
GROUP BY part
""";

return jdbcTemplate.query(sql, rs -> {
Map<SoptPart, Long> result = new EnumMap<>(SoptPart.class);

while (rs.next()) {
result.put(
SoptPart.valueOf(rs.getString("part")),
rs.getLong("member_count")
);
}

return result;
}, generation);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@table(schema = "auth_prod", name = "user_activity_histories") 로 엔티티 자체를 만들어서 JPA로 사용하는 방식보다 이렇게 JdbcTemplate을 사용하는 방식이 더 빠르게 작업할 수 있으셔서 이렇게 하신건가요??

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 맞습니다! 이번 조회가 auth_prod.user_activity_histories에 대한 단순 집계성 쿼리이고, 최종적으로 필요한 값도 엔티티 자체가 아니라 part별 count 값이라서JPA 엔티티/리포지토리를 추가하는 것보다 JdbcTemplate이 더 적절하다고 판단했습니다.

향후에 해당 값이나 auth의 데이터를 더 자주 활용하게 된다면 그때에 확장을 고려해보는 게 어떨까 해서 이렇게 작성해보았습니다.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금 다시 보면서 생각났는데, auth_prod를 하드 코딩할 경우 dev 환경에서 문제가 발생할 수 있겠네요,,,

Comment on lines +28 to +35
PartScores partScores = new PartScores();
userInfos.forEach(userInfo -> addPartScore(userInfo, partScores));

Map<Part, BigDecimal> averagePoints = calculateAveragePoints(partScores);
Map<Part, Integer> ranks = calculateRanks(averagePoints);

return Part.getPartsByReturnOrder().stream()
.map(part -> PartRank.builder()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

userInfos.forEach(userInfo -> addPartScore(userInfo, partScores)); 에서 foreach로 상태를 변경하는 것이 좀 애매하다면 이렇게 할 수도 있겠네요

    userInfos.stream()
    .filter(userInfo -> SoptPart.toPart(userInfo.getPart()) != null) 
    .collect(Collectors.groupingBy(
        userInfo -> SoptPart.toPart(userInfo.getPart()),
        Collectors.summingInt(SoptampUserInfo::getTotalPoints) 
    ));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 좋은 의견 감사합니다 👀 말씀해주신 방식처럼 groupingBy로 바로 파트별 총점을 만드는 것도 가능할 것 같다고 생각했습니다.

다만 이번에는 기존 PartScores가 총점 누적 역할을 하고 있어서, 그 부분은 유지하고 평균 점수 계산과 rank 계산만 변경하는 쪽은 어떨까 싶었습니다. 또 groupingBy로 바꾸면 SoptPart -> Part 변환과 null 필터링이 collector 안에 함께 들어가게 되어서, 현재처럼 총점 누적 / 평균 점수 계산 / rank 계산 단계를 나누는 구조도 어떨까 하여 이렇게 구성해보았는데, 혹시 재헌님 생각은 어떠실까요?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요게 가독성이 좀 떨어지시면 이 그룹핑 로직 자체를 메서드로 묶어서 처리할 수도 있을 것 같긴해요!
다만 이건 민경님이 보시기에 더 잘 읽힐 것 같은 방향으로 진행하셔도 될 것 같습니다!

public class SoptampPartRankCalculator {

private final List<SoptampUserInfo> userInfos;
private static final BigDecimal ZERO_POINT = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

소수점 몇째 자리까지 사용하는 지를 별도 상수로 빼서 관리해도 좋을 것 같아요! 하단의

            BigDecimal averagePoint = memberCount == 0 ? ZERO_POINT
                : BigDecimal.valueOf(totalScore)
                    .divide(BigDecimal.valueOf(memberCount), 2, RoundingMode.HALF_UP);

같은 부분도 상수로 컨트롤하면 정책 변경에 유연하게 대처하기 좋을 것 같습니다~

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 이부분 반영해두었습니다! 좋은 의견 너무 감사합니다 🙇‍♀️ 518bdbe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feat 새로운 피쳐 생성

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 솝탬프 로직 수정

2 participants