Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

발생하는 쿼리 확인 후 쿼리 성능 개선 #758

Merged
merged 13 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
public enum AuthExceptionType implements ExceptionType {

NONEXISTENT_REFRESH_TOKEN(300, "갱신 토큰이 존재하지 않습니다."),
UNMATCHED_INFORMATION_BETWEEN_TOKEN(301, "토큰 간의 정보가 일치하지 않습니다.");
UNMATCHED_INFORMATION_BETWEEN_TOKEN(301, "토큰 간의 정보가 일치하지 않습니다."),
INVALID_TOKEN(302, "올바르지 않은 토큰입니다."),
UNSUPPORTED_TOKEN(303, "지원하지 않는 JWT입니다."),
MALFORMED_TOKEN(304, "잘못된 JWT 서명입니다."),
SIGNATURE_TOKEN(305, "토큰의 서명 유효성 검사가 실패했습니다."),
EXPIRED_TOKEN(306, "토큰의 유효기간이 만료되었습니다."),
UNKNOWN_TOKEN(307, "알 수 없는 토큰 유효성 문제가 발생했습니다.");

private final int code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.votogether.domain.category.exception;

import com.votogether.global.exception.ExceptionType;
import lombok.Getter;

@Getter
public enum CategoryExceptionType implements ExceptionType {

NOT_FOUND(800, "해당 카테고리가 존재하지 않습니다."),
EXIST_LIKE_CATEGORY(801, "이미 선호 카테고리에 등록되어 있습니다."),
NOT_LIKE_CATEGORY(802, "해당 카테고리는 선호 카테고리가 아닙니다.");

private final int code;
private final String message;

CategoryExceptionType(final int code, final String message) {
this.code = code;
this.message = message;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import com.votogether.domain.category.dto.response.CategoryResponse;
import com.votogether.domain.category.entity.Category;
import com.votogether.domain.category.exception.CategoryExceptionType;
import com.votogether.domain.category.repository.CategoryRepository;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.MemberCategory;
import com.votogether.domain.member.repository.MemberCategoryRepository;
import com.votogether.global.exception.BadRequestException;
import com.votogether.global.exception.NotFoundException;
import java.util.Comparator;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -32,11 +35,11 @@ public List<CategoryResponse> getAllCategories() {
@Transactional
public void addFavoriteCategory(final Member member, final Long categoryId) {
final Category category = categoryRepository.findById(categoryId)
.orElseThrow(() -> new IllegalArgumentException("해당 카테고리가 존재하지 않습니다."));
.orElseThrow(() -> new NotFoundException(CategoryExceptionType.NOT_FOUND));

memberCategoryRepository.findByMemberAndCategory(member, category)
.ifPresent(ignore -> {
throw new IllegalStateException("이미 선호 카테고리에 등록되어 있습니다.");
throw new BadRequestException(CategoryExceptionType.EXIST_LIKE_CATEGORY);
});

final MemberCategory memberCategory = MemberCategory.builder()
Expand All @@ -50,9 +53,9 @@ public void addFavoriteCategory(final Member member, final Long categoryId) {
@Transactional
public void removeFavoriteCategory(final Member member, final Long categoryId) {
final Category category = categoryRepository.findById(categoryId)
.orElseThrow(() -> new IllegalArgumentException("해당 카테고리가 존재하지 않습니다."));
.orElseThrow(() -> new NotFoundException(CategoryExceptionType.NOT_FOUND));
final MemberCategory memberCategory = memberCategoryRepository.findByMemberAndCategory(member, category)
.orElseThrow(() -> new IllegalArgumentException("해당 카테고리는 선호 카테고리가 아닙니다."));
.orElseThrow(() -> new BadRequestException(CategoryExceptionType.NOT_LIKE_CATEGORY));

memberCategoryRepository.delete(memberCategory);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ public record MemberInfoResponse(
Integer birthYear,

@Schema(description = "작성한 게시글 수", example = "5")
int postCount,
long postCount,

@Schema(description = "투표한 수", example = "10")
int voteCount
long voteCount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.votogether.domain.member.entity;

import com.votogether.domain.common.BaseEntity;
import com.votogether.domain.member.exception.MemberExceptionType;
import com.votogether.global.exception.BadRequestException;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@EqualsAndHashCode(of = {"id"}, callSuper = false)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MemberMetric extends BaseEntity {

private static final int POST_SCORE = 5;
private static final int VOTE_SCORE = 1;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
private Member member;

@Column(nullable = false)
private long postCount;

@Column(nullable = false)
private long voteCount;

@Column(nullable = false)
private long score;

@Builder
private MemberMetric(
final Member member,
final long postCount,
final long voteCount,
final long score
) {
this.member = member;
this.postCount = postCount;
this.voteCount = voteCount;
this.score = score;
}

public void increasePostCount() {
this.postCount += 1;
updateScore();
}

public void decreasePostCount() {
this.postCount -= 1;
updateScore();
}

public void updateVoteCount(final long voteCount) {
if (voteCount < 0) {
throw new BadRequestException(MemberExceptionType.INVALID_VOTE_COUNT);
}
this.voteCount = voteCount;
updateScore();
}

private void updateScore() {
this.score = (postCount * POST_SCORE) + (voteCount * VOTE_SCORE);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public enum MemberExceptionType implements ExceptionType {
ALREADY_ASSIGNED_BIRTH_YEAR(806, "이미 출생년도가 할당되어 있습니다."),
NOT_PASSED_NICKNAME_CHANGING_CYCLE(807, "최소 닉네임 변경주기가 지나지 않았습니다."),
NOT_ALLOWED_INITIAL_NICKNAME_PREFIX(808, "초기 닉네임에 포함된 접두어로 닉네임을 변경할 수 없습니다."),
INVALID_VOTE_COUNT(809, "수정 가능한 투표 수는 0이상이어야 합니다."),
NOT_FOUND_METRIC(810, "메트릭 정보가 존재하지 않습니다."),
;

private final int code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.votogether.domain.member.repository;

import com.votogether.domain.member.entity.MemberMetric;
import java.util.List;

public interface MemberMetricCustomRepository {

List<MemberMetric> getTop10MemberMetrics();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.votogether.domain.member.repository;

import static com.votogether.domain.member.entity.QMember.member;
import static com.votogether.domain.member.entity.QMemberMetric.memberMetric;

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.votogether.domain.member.entity.MemberMetric;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@RequiredArgsConstructor
@Repository
public class MemberMetricCustomRepositoryImpl implements MemberMetricCustomRepository {

private final JPAQueryFactory jpaQueryFactory;

@Override
public List<MemberMetric> getTop10MemberMetrics() {
return jpaQueryFactory.selectFrom(memberMetric)
.innerJoin(memberMetric.member, member).fetchJoin()
.orderBy(memberMetric.score.desc())
.offset(0)
.limit(10)
.fetch();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.votogether.domain.member.repository;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.MemberMetric;
import jakarta.persistence.LockModeType;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface MemberMetricRepository extends JpaRepository<MemberMetric, Long>, MemberMetricCustomRepository {

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT mm FROM MemberMetric mm where mm.member.id = :memberId")
Optional<MemberMetric> findByMemberIdForUpdate(@Param("memberId") final Long memberId);

Optional<MemberMetric> findByMember(final Member member);

long countByScoreGreaterThan(final long score);

void deleteByMember(final Member member);

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import com.votogether.domain.member.dto.response.MemberInfoResponse;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.MemberCategory;
import com.votogether.domain.member.entity.MemberMetric;
import com.votogether.domain.member.entity.vo.Nickname;
import com.votogether.domain.member.exception.MemberExceptionType;
import com.votogether.domain.member.repository.MemberCategoryRepository;
import com.votogether.domain.member.repository.MemberMetricRepository;
import com.votogether.domain.member.repository.MemberRepository;
import com.votogether.domain.post.entity.Post;
import com.votogether.domain.post.entity.comment.Comment;
Expand All @@ -32,6 +34,7 @@ public class MemberService {
private static final Long NICKNAME_CHANGING_CYCLE = 14L;

private final MemberRepository memberRepository;
private final MemberMetricRepository memberMetricRepository;
private final MemberCategoryRepository memberCategoryRepository;
private final PostRepository postRepository;
private final VoteRepository voteRepository;
Expand All @@ -44,7 +47,17 @@ public Member register(final Member member) {
member.getSocialId(),
member.getSocialType()
);
return maybeMember.orElseGet(() -> memberRepository.save(member));
return maybeMember.orElseGet(() -> {
final Member savedMember = memberRepository.save(member);
final MemberMetric memberMetric = MemberMetric.builder()
.member(member)
.postCount(0)
.voteCount(0)
.score(0)
.build();
memberMetricRepository.save(memberMetric);
return savedMember;
});
}

@Transactional(readOnly = true)
Expand All @@ -55,15 +68,15 @@ public Member findById(final Long memberId) {

@Transactional(readOnly = true)
public MemberInfoResponse findMemberInfo(final Member member) {
final int numberOfPosts = postRepository.countByWriter(member);
final int numberOfVotes = voteRepository.countByMember(member);
final MemberMetric memberMetric = memberMetricRepository.findByMember(member)
.orElseThrow(() -> new NotFoundException(MemberExceptionType.NOT_FOUND_METRIC));

return new MemberInfoResponse(
member.getNickname(),
member.getGender(),
member.getBirthYear(),
numberOfPosts,
numberOfVotes
memberMetric.getPostCount(),
memberMetric.getVoteCount()
);
}

Expand Down Expand Up @@ -103,6 +116,7 @@ public void deleteMember(final Member member) {
deleteMemberCategories(member);
deleteReports(member, posts, comments);

memberMetricRepository.deleteByMember(member);
memberRepository.delete(member);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public static PostResponse ofUser(
final List<PostCategory> postCategories,
final PostContentImage postContentImage,
final List<PostOption> postOptions,
final Optional<Vote> vote
final Optional<Vote> vote,
final long commentCount
) {
postOptions.sort(Comparator.comparingInt(PostOption::getSequence));
return new PostResponse(
Expand All @@ -74,7 +75,7 @@ public static PostResponse ofUser(
post.getCreatedAt(),
post.getDeadline(),
calculateImageCount(postContentImage, postOptions),
post.getCommentCount(),
commentCount,
PostVoteResultResponse.ofUser(user, post, postOptions, vote)
);
}
Expand Down Expand Up @@ -115,7 +116,8 @@ public static PostResponse ofGuest(
final Post post,
final List<PostCategory> postCategories,
final PostContentImage postContentImage,
final List<PostOption> postOptions
final List<PostOption> postOptions,
final long commentCount
) {
postOptions.sort(Comparator.comparingInt(PostOption::getSequence));
return new PostResponse(
Expand All @@ -128,7 +130,7 @@ public static PostResponse ofGuest(
post.getCreatedAt(),
post.getDeadline(),
calculateImageCount(postContentImage, postOptions),
post.getCommentCount(),
commentCount,
PostVoteResultResponse.ofGuest(post, postOptions)
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.votogether.domain.post.dto.response.post;

import com.votogether.domain.post.entity.Post;
import com.votogether.domain.post.entity.PostOption;
import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "게시글 간략 정보 응답")
Expand All @@ -24,15 +23,8 @@ public static PostSummaryResponse from(final Post post) {
post.getId(),
post.getWriter().getNickname(),
post.getPostBody().getTitle(),
calculateTotalVoteCount(post)
post.getVoteCount()
);
}

private static long calculateTotalVoteCount(final Post post) {
return post.getPostOptions()
.stream()
.mapToLong(PostOption::getVoteCount)
.sum();
}

}
Loading