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

[머지 금지!] 리뷰용입니다 #455

Open
wants to merge 7 commits into
base: backend
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
@@ -1,9 +1,9 @@
package com.digginroom.digginroom.controller;

import com.digginroom.digginroom.service.RoomService;
import com.digginroom.digginroom.service.dto.RoomRequest;
import com.digginroom.digginroom.service.dto.RoomResponse;
import com.digginroom.digginroom.service.dto.RoomsResponse;
import com.digginroom.digginroom.service.RoomService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.digginroom.digginroom.domain;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class UUIDBaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
Comment on lines +21 to +23
Copy link
Collaborator

Choose a reason for hiding this comment

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

id를 UUID로 하는 이유가 있을까요?

UUID를 기준으로 데이터를 저장하면 클러스터링 인덱스로 인한 장점을 모두 잃어버리는 것 같은데 이 부분을 변경하신 이유가 있으신지 궁금합니다. ㅎㅎ
제가 생각한 단점들은 다음과 같아요.

  • id 기반 범위 탐색을 못한다.
  • 다른 인덱스를 추가해서 조회를 하더라도 항상 랜덤 io로만 데이터를 조회한다.
    • 클러스터링 인덱스를 활용하면 순차 io가 발생해서 조회 성능이 훨씬 개선 됩니다.
  • 기존 레포지토리에서 id 범위탐색을 했던 것을 모두 변경해야한다.
    • 예를 들어 댓글 레포지토리에서는 id를 무한 스크롤에 사용하는데 이를 모두 변경해야합니다. createdAt으로 조회해도 되지만 세컨더리 인덱스를 추가해야하고, 성능 상 이점도 잃어버리게 됩니다.
  • pk의 크기가 커진다. 세컨더리 인덱스는 리프 노드에 pk를 두는 것으로 알고있습니다. uuid가 128비트라고 하면 16바이트를 사용하는데 그러면 데이터가 많아질수록 부담이 클 것 같아요.

cf) 처음 UUID를 도입한 목적이 무엇이신가요? - 땡칠 질문

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

제가 비슷한 다른 작업(페이징)을 하다가 이 질문에 대해 생각나서 결정에 도움이 될까 답을 달아봅니다~

  • id 기반 범위 탐색을 못한다.

기술적으로 사실입니다.
UUID를 도입하게 되어 지금의 정렬 기준을 createdAt + UUID로 대체하게 된다면?
createdAt 및 UUID로 세컨더리 인덱스를 만들고 유지하는 것은 sequential id에 비해 읽기 뿐 아니라 쓰기 성능도 후퇴하게 됩니다.
게다가 id 자체도 크기도 작고 연산도 단순한 숫자 id에 비해서 사전순으로 정렬하는데 더 많은 연산이 필요하므로
sequential id에 비해 몇 배 이상 느려질 가능성이 있습니다.

대신 UUID를 사용하면, 기존에 PK에 의미를 부여(생성 순서대로 정렬된다)하고 의존하던 것을 벗어날 수 있어서 확장성을 늘릴 수 있을 것이라는 생각이 들었습니다.
PK에 의미를 부여하고 의존하게 되면, PK가 관리(락)되는 범위(단일 DB 인스턴스)에서 벗어나기 어렵습니다.
이 경우 DB를 다중화한다면 별도의 동기화가 필요할 거라고 추측이 드네요.

하지만 UUID를 사용하면 DB 복제, 다중화가 용이해져서 인프라 차원에서 학습을 위한 다양한 경험을 할 수 있는 기반이 될 것이라고 생각합니다.

~를 블랙캣과 파워가 얘기하고 싶었던게 아닐까 라는 생각이 들었습니다 ㅋㅋㅋ

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package com.digginroom.digginroom.domain.member;

import com.digginroom.digginroom.domain.BaseEntity;
import com.digginroom.digginroom.domain.Genre;
import com.digginroom.digginroom.domain.member.vo.Nickname;
import com.digginroom.digginroom.domain.member.vo.Password;
import com.digginroom.digginroom.domain.member.vo.Provider;
import com.digginroom.digginroom.domain.room.Room;
import com.digginroom.digginroom.exception.MemberException.DuplicatedFavoriteException;
import com.digginroom.digginroom.exception.MemberException.EmptyFavoriteException;
import com.digginroom.digginroom.exception.MemberException.FavoriteExistsException;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import lombok.AccessLevel;
Expand All @@ -29,16 +27,10 @@ public class Member extends BaseEntity {
private Password password;
@Enumerated(value = EnumType.STRING)
private Provider provider;
@Getter(AccessLevel.NONE)
private boolean hasFavorite = false;
@Embedded
private Nickname nickname;
@Embedded
private final ScrapRooms scrapRooms = new ScrapRooms();
@Embedded
private final DislikeRooms dislikeRooms = new DislikeRooms();
@Embedded
private final MemberGenres memberGenres = new MemberGenres(this);

private Member(final String username, final Password password, final Provider provider, final Nickname nickname) {
this.username = username;
Expand Down Expand Up @@ -66,70 +58,20 @@ public static Member guest() {

public void scrap(final Room room) {
scrapRooms.scrap(room);
adjustMemberGenreWeight(room, WeightFactor.SCRAP);
}

public void unscrap(final Room room) {
scrapRooms.unscrap(room);
adjustMemberGenreWeight(room, WeightFactor.UNSCRAP);
}

public void dislike(final Room room) {
dislikeRooms.dislike(room);
adjustMemberGenreWeight(room, WeightFactor.DISLIKE);
}

public void undislike(final Room room) {
dislikeRooms.undislike(room);
adjustMemberGenreWeight(room, WeightFactor.UNDISLIKE);
}

public void markFavorite(final List<Genre> genres) {
if (hasFavorite) {
throw new FavoriteExistsException();
}
if (hasDuplicate(genres)) {
throw new DuplicatedFavoriteException();
}
if (genres.isEmpty()) {
throw new EmptyFavoriteException();
}

for (Genre genre : genres) {
memberGenres.adjustWeightBy(genre, WeightFactor.FAVORITE);
}
hasFavorite = true;
}

private boolean hasDuplicate(final List<Genre> genres) {
return new HashSet<>(genres).size() != genres.size();
}

private void adjustMemberGenreWeight(final Room room, final WeightFactor weightFactor) {
Genre superGenre = room.getTrack().getSuperGenre();
memberGenres.adjustWeightBy(superGenre, weightFactor);
}

public boolean hasFavorite() {
return hasFavorite;
}

public boolean hasScrapped(final Room pickedRoom) {
return scrapRooms.hasScrapped(pickedRoom);
}

public List<MemberGenre> getMemberGenres() {
return memberGenres.getAll();
}

public List<Room> getScrapRooms() {
return scrapRooms.getScrapRooms();
}

public List<Room> getDislikeRooms() {
return dislikeRooms.getDislikeRooms();
}

public String getNickname() {
return nickname.getNickname();
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.digginroom.digginroom.domain.member;
package com.digginroom.digginroom.domain.member.vo;

import jakarta.persistence.Embeddable;
import java.util.UUID;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.digginroom.digginroom.domain.member;
package com.digginroom.digginroom.domain.member.vo;

import com.digginroom.digginroom.util.PasswordEncoder;
import jakarta.persistence.Embeddable;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.digginroom.digginroom.domain.member;
package com.digginroom.digginroom.domain.member.vo;

import java.util.Arrays;
import java.util.Optional;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.digginroom.digginroom.domain.recommend;

import com.digginroom.digginroom.domain.Genre;
import com.digginroom.digginroom.domain.member.Member;
import com.digginroom.digginroom.domain.member.MemberGenre;
import com.digginroom.digginroom.domain.room.Room;
import com.digginroom.digginroom.exception.RecommendException.NoRecommendableRoomException;
import com.digginroom.digginroom.exception.RecommendException.UnderBoundWeightException;
import com.digginroom.digginroom.membergenre.domain.MemberGenre;
import com.digginroom.digginroom.repository.RoomRepository;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
Expand All @@ -18,8 +17,7 @@ public class RoomRecommender {

private final RoomRepository roomRepository;

public Room recommend(final Member member) {
List<MemberGenre> memberGenres = member.getMemberGenres();
public Room recommend(final List<MemberGenre> memberGenres) {
Genre recommenedGenre = recommendGenre(memberGenres);
return recommendRoom(recommenedGenre);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.digginroom.digginroom.controller;
package com.digginroom.digginroom.membergenre.controller;

import com.digginroom.digginroom.controller.Auth;
import com.digginroom.digginroom.membergenre.service.MemberGenreService;
import com.digginroom.digginroom.membergenre.service.dto.MemberGenresResponse;
import com.digginroom.digginroom.service.dto.FavoriteGenresRequest;
import com.digginroom.digginroom.service.dto.MemberDetailsResponse;
import com.digginroom.digginroom.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -14,21 +15,22 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/member")
public class MemberOperationController {
public class MemberGenreController {

private final MemberService memberService;
private final MemberGenreService memberGenreService;

@PostMapping("/favorite-genres")
public ResponseEntity<Void> markFavorite(
@Auth final Long memberId,
@RequestBody final FavoriteGenresRequest favoriteGenresRequest
) {
memberService.markFavorite(memberId, favoriteGenresRequest);
memberGenreService.markFavorite(memberId, favoriteGenresRequest);
return ResponseEntity.ok().build();
}

@GetMapping("/me")
public ResponseEntity<MemberDetailsResponse> showMemberDetails(@Auth final Long memberId) {
return ResponseEntity.ok().body(memberService.getMemberDetails(memberId));
@GetMapping("/genres")
public ResponseEntity<MemberGenresResponse> showMemberDetails(@Auth final Long memberId) {
boolean hasFavorite = memberGenreService.hasFavorite(memberId);
return ResponseEntity.ok().body(new MemberGenresResponse(hasFavorite));
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
package com.digginroom.digginroom.domain.member;
package com.digginroom.digginroom.membergenre.domain;

import com.digginroom.digginroom.domain.BaseEntity;
import com.digginroom.digginroom.domain.Genre;
import com.digginroom.digginroom.domain.UUIDBaseEntity;
import com.digginroom.digginroom.membergenre.domain.vo.WeightFactor;
import com.digginroom.digginroom.membergenre.domain.vo.WeightStatus;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MemberGenre extends BaseEntity {
public class MemberGenre extends UUIDBaseEntity {

@Enumerated(value = EnumType.STRING)
private Genre genre;
private Long memberId;
private int weight;
@ManyToOne
private Member member;

public MemberGenre(final Genre genre, final Member member) {
public MemberGenre(final Genre genre, final Long memberId) {
this.genre = genre;
this.weight = WeightStatus.DEFAULT.getValue();
this.member = member;
this.memberId = memberId;
}

public boolean isSameGenre(final Genre genre) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.digginroom.digginroom.membergenre.domain;

import com.digginroom.digginroom.domain.Genre;
import com.digginroom.digginroom.membergenre.domain.vo.WeightFactor;

public interface MemberGenreEvent {

Genre getGenre();

Long getMemberId();

WeightFactor getWeightFactor();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.digginroom.digginroom.membergenre.domain;

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberGenreRepository extends JpaRepository<MemberGenre, Long> {

List<MemberGenre> findByMemberId(final Long memberId);

boolean existsByMemberId(Long memberId);
}
Loading
Loading