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

Feature : 라벨 기능 구현 #18

Merged
merged 44 commits into from
Nov 28, 2024
Merged

Feature : 라벨 기능 구현 #18

merged 44 commits into from
Nov 28, 2024

Conversation

yeonsu00
Copy link
Collaborator

@yeonsu00 yeonsu00 commented Nov 25, 2024

📢 기능 설명

  • 라벨 기능 추가
    • 전체 라벨 조회
    • 라벨 이미지 저장
    • 사용자 소유 라벨 조회
    • 선착순 라벨 뽑기

연결된 issue

#1

✅ 체크리스트

  • PR 제목 규칙 잘 지켰는가?
  • 추가/수정사항을 설명하였는가?
  • 이슈넘버를 적었는가?

ETC

  • 유저 관련 로직은 추후 수정할 예정입니다.

@yeonsu00 yeonsu00 linked an issue Nov 25, 2024 that may be closed by this pull request
4 tasks
public static List<Label> toLabels(List<LabelEntity> entities) {
return entities.stream()
.map(LabelEntity::to)
.collect(Collectors.toList());
Copy link
Collaborator

@l2yujw l2yujw Nov 25, 2024

Choose a reason for hiding this comment

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

collect(Collectors.toList());
아래걸로 대체가 됐었던거 같아요
.toList()

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵 수정하겠습니다~

Copy link
Collaborator

@kahyun0255 kahyun0255 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다~ 🥳🤩

FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403"),

// 라벨 에러
INVALID_LABEL(HttpStatus.BAD_REQUEST, "LABEL400"),
Copy link
Collaborator

Choose a reason for hiding this comment

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

LABEL 4000 어때요 아래가 4001이니까

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넘 좋아요

assertNotNull(dtos, "Response DTO 리스트는 null이 될 수 없음");
assertEquals(2, dtos.size(), "DTO 리스트 사이즈는 2");
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

엔터처리 좀 부탁드립니다 👽

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵ㅎㅎㅎ

Comment on lines 13 to 14
@NoArgsConstructor
@AllArgsConstructor
Copy link
Collaborator

@kahyun0255 kahyun0255 Nov 25, 2024

Choose a reason for hiding this comment

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

이거 NoArgsConstructor와 AllArgsConstructor는 지양해야 할 Annotaion이라서
@NoArgsConstructor@NoArgsConstructor(access=accessLevel.PROTECTED) 이걸로, @AllArgsConstuctor은 최대한 지양하고, Builder를 써야한다는데 저는 @AllArgsConstuctor(access=accessLevel.PRIVATE) 요렇게 합니다 맞는지는 모르겠어요ㅎㅎ

참고 : https://velog.io/@rosa/Lombok-지양해야-할-annotation, https://kcode-recording.tistory.com/342,

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

오호 첨 알았어요! 감사합니다~

public static List<LabelResponseDTO> toResponseDTOList(List<Label> labels) {
return labels.stream()
.map(label -> new LabelResponseDTO(label.getImageUrl()))
.collect(Collectors.toList());
Copy link
Collaborator

Choose a reason for hiding this comment

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

다시 찾아보니까 현재 collect(Collectors.toList())는 Java 8 방식에서 사용됐었고
Stream.toList() Java16 부터 생겼다고해요
더 간결하고, 내부적으로 최적화시켜놨다고 합니다!
차이점은 전자는 가변이고 후자는 불변이라고 하네요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

그런 차이가 있었군요 감사합니다!

Copy link
Collaborator

@hgh1472 hgh1472 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다🔥

return imageUrl;
}

public static List<LabelResponseDTO> toResponseDTOList(List<Label> labels) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Label 자신이 DTO로 변환되는 식으로 해도 나쁘지 않을 것 같아요!

public LabelResponseDTO 메서드명() {
}

이런 식으로 작성해서 서비스에서는

List<LabelResponseDTO> response = labels.stream().map(Label::toDTO).toList();

해도 괜찮을 것 같아요! 서비스 입장에서 Label의 메서드를 가져와서 이용한다기 보다는, 라벨한테 DTO로 변환하라고 시키는 느낌은 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

오 이 방법이 더 좋은 것 같아요! 수정하겠습니다~

@yeonsu00 yeonsu00 changed the title Feature/#1 label Feature: 라벨 기능 구현 Nov 27, 2024
@yeonsu00 yeonsu00 self-assigned this Nov 27, 2024
@yeonsu00 yeonsu00 changed the title Feature: 라벨 기능 구현 Feature : 라벨 기능 구현 Nov 27, 2024
Copy link
Collaborator

@hgh1472 hgh1472 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다! 기능적으로 문제되는건 없는 것 같아서 리뷰단 내용들은 추후에 리팩토링 할 때 확인해주셔도 될 것 같아요!!

User user = UserEntity.toUser(userEntity);

//2. 유저가 해당 라벨을 이미 가지고 있다면 예외 처리
List<UserLabel> userLabel = labelRepository.findUserLabelByUserAndLabel(user, label);
Copy link
Collaborator

Choose a reason for hiding this comment

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

라벨과 유저를 통해 조회하면 최대 1개만 나오지 않을까 싶은데 List로 받은 이유가 궁금해요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

혹시 모를 상황을 대비했습니다..ㅎㅎ 혹시 2개 이상이 될 수 있으니까? 그래서 59번째줄에서 리스트의 사이즈를 검사해주고 있어요!
하지만 로직상 2개 이상이 될 상황이 없어서 굳이 리스트로 받지 않아도 될 것 같네요!

return new LabelResponseDTO(this.labelId, this.imageUrl);
}

public boolean isOwnedCountValid() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

라벨을 생성할 때, 이 부분을 검증해도 괜찮을 것 같아요!
이미 다 개수가 소진된 라벨이라면, 라벨을 가져오는 데에서부터 예외를 발생시키는 것도 나쁘지않다 생각합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

라벨 도메인 생성 시 검증하면 전체 라벨 조회할 때 선착순 마감 예외가 발생하게 돼서 일단은 서비스에서 검증하도록 했습니다! 나중에 바꿔볼게요!

//3. label에 최대 인원수와 소유 인원수를 비교한다.
//3-1. 소유 인원수가 최대 인원수를 넘지 않는다면, 소유 인원수 업데이트 후 UserLabel에 저장
if (label.isOwnedCountValid()) {
labelRepository.updateOwnedCount(label);
Copy link
Collaborator

Choose a reason for hiding this comment

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

리포지토리 내에서 라벨의 ownedCount를 상승시켜주고 반영시키는 것 같은데, 상승시키는 건 라벨이 행동하게 하는건 어떨까요?
ownedCount를 증가시키는건 도메인 객체에게 부여하고, 이를 DB에 반영하는 것만 리포지토리에 책임을 주는 것도 괜찮을 것 같아요!

Comment on lines +32 to +40
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
@Cascade(CascadeType.PERSIST)
private UserEntity user;

@ManyToOne
@JoinColumn(name = "label_id", nullable = false)
@Cascade(CascadeType.PERSIST)
private LabelEntity label;
Copy link
Collaborator

Choose a reason for hiding this comment

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

이거 제 기억 이슈이긴한데 직접참조 허용하는쪽으로 했었나요??

Comment on lines +19 to +20
@PersistenceContext
private EntityManager entityManager;
Copy link
Collaborator

Choose a reason for hiding this comment

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

EntityManager 안쓰고 있어서 지워도 될 것 같아요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

userEntity를 담을 때 사용하고 있어요! 이 부분은 유저 로직 구현하면 바꿀 예정입니다!!

Comment on lines +75 to +76
LabelEntity labelEntity = entityManager.find(LabelEntity.class, label.getLabelId());
UserEntity userEntity = entityManager.find(UserEntity.class, user.getUserId());
Copy link
Collaborator

Choose a reason for hiding this comment

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

JpaRepository랑 EntityManager 둘 다 필드로 존재하는데, EntityManager를 사용하신 이유가 궁금합니다!

Comment on lines 79 to 81
entityManager.persist(userLabelEntity);

userLabelJpaRepository.save(userLabelEntity);
Copy link
Collaborator

Choose a reason for hiding this comment

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

persist를 통해 영속상태가 되고, 트랜잭션이 끝나면 insert 쿼리를 날려줄 것 같아요! persist나 save 둘 중 하나만 해도 괜찮을 것 같아요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

헉 그렇군요! 감사합니다~

Copy link
Collaborator

@kahyun0255 kahyun0255 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다~~ 코드 예쁜데여?? 🐬

Comment on lines 16 to 22
public Long getUserId() {
return userId;
}

public String getEmail() {
return email;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Getter로 대체해도 될 것 같아용

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

헉 그렇네요! 감사합니당

@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "user")
Copy link
Collaborator

Choose a reason for hiding this comment

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

저희 끝에 tb붙이는거 아니었나요 저만 붙이는거였나요ㅋㅋㅋㅋㅋㅋㅋㅋ

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ㅋㅋㅋㅋㅋㅋ그런 말 했던 거 기억나는 거 같아요..ㅎㅎㅎㅎㅎ 한번 얘기해봅시다

this.ownedCount++;
}

public LabelResponseDTO toLabelResponseDTO() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

DTO에 from을 넣고 LabelResponseDTO.from(label)

LabeResponseDTO 안에

public static LabelResponseDTO from(Label label) {
    return new LabelResponseDTO(
        label.getLabelId(),
        label.getImageUrl()
    )
}

이렇게 해서 DTO에게 변환을 위임해주는건 어떤가요

LabelEntity labelEntity = labelJpaRepository.findByIdWithLock(labelId)
.orElseThrow(() -> new InvalidLabelException("라벨 ID " + labelId + " 에 해당하는 라벨이 존재하지 않습니다."));
entityManager.persist(labelEntity);
return LabelEntity.toLabel(labelEntity);
Copy link
Collaborator

Choose a reason for hiding this comment

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

return labelJpaRepository.findByIdWithLock(labelId)
                .map(LabelEntity::toLabel)
                .orElseThrow(() -> new InvalidLabelException("라벨 ID " + labelId + " 에 해당하는 라벨이 존재하지 않습니다."))

스트림 쓰면 깔끔해지긴 해요! 취향차이 일 것 같습니다!

@Transactional
public List<LabelResponseDTO> findAllLabels() {
List<Label> labels = labelRepository.findAllLabels();
return labels.stream().map(Label::toLabelResponseDTO).toList();
Copy link
Collaborator

Choose a reason for hiding this comment

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

제가 제안드린대로 한다면 LabelResponseDTO::from 이 들어가게 됩니다!


//2. 유저가 해당 라벨을 이미 가지고 있다면 예외 처리
List<UserLabel> userLabel = labelRepository.findUserLabelByUserAndLabel(user, label);
if (userLabel.size() >= 1) throw new DuplicateLabelException("이미 발급받은 라벨입니다.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

건하님 의견대로 가면 userLabel.size() >= 1 보다는 label 안에 검증을 넣어서 isXXX 형식으로 받아오는 것도 괜찮을거 같아요

Copy link

sonarcloud bot commented Nov 28, 2024

@yeonsu00 yeonsu00 merged commit cdde41f into develop Nov 28, 2024
2 checks passed
@l2yujw l2yujw deleted the feature/#1-label branch November 28, 2024 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature: 라벨 기능 추가
4 participants