diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/entity/Mentoring.java b/back/src/main/java/com/back/domain/mentoring/mentoring/entity/Mentoring.java index c8ccd00b..af7e476d 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/entity/Mentoring.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/entity/Mentoring.java @@ -52,7 +52,12 @@ public void updateTags(List tags) { if (tags != null) { tags.forEach(tag -> - this.mentoringTags.add(new MentoringTag(this, tag)) + this.mentoringTags.add( + MentoringTag.builder() + .mentoring(this) + .tag(tag) + .build() + ) ); } } diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/entity/Tag.java b/back/src/main/java/com/back/domain/mentoring/mentoring/entity/Tag.java index 6520868d..b4271246 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/entity/Tag.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/entity/Tag.java @@ -11,7 +11,7 @@ @Getter @NoArgsConstructor public class Tag extends BaseEntity { - @Column(length = 50, nullable = false, unique = true) + @Column(length = 50, nullable = false) private String name; @Builder diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/service/MentoringService.java b/back/src/main/java/com/back/domain/mentoring/mentoring/service/MentoringService.java index b177a08b..e3487cc8 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/service/MentoringService.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/service/MentoringService.java @@ -24,7 +24,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Set; +import java.util.Map; import java.util.stream.Collectors; @Service @@ -123,33 +123,51 @@ private List getOrCreateTags(List tagNames) { return new ArrayList<>(); } - // 기존 태그 조회 - List existingTags = tagRepository.findByNameIn(tagNames); - - Set existingNames = existingTags.stream() - .map(Tag::getName) - .collect(Collectors.toSet()); + // 1. 동일한 사용자의 태그 중복 제거 + List distinctNames = tagNames.stream() + .map(String::trim) + .filter(name -> !name.isEmpty()) + .distinct() + .toList(); - // 신규 태그 생성 - List newTags = createNewTags(tagNames, existingNames); + // 2. 기존 태그 조회 - MySQL collation에 의해 대소문자 무시 + Map tagMap = tagRepository.findByNameIn(distinctNames).stream() + .collect(Collectors.toMap( + Tag::getName, + tag -> tag, (t1, t2) -> t1) + ); - // 기존 태그 + 신규 태그 - List allTags = new ArrayList<>(existingTags); - allTags.addAll(newTags); + return buildTagList(distinctNames, tagMap); + } - return allTags; + /** + * 기존 태그 + 신규 태그 + * - 정확히 일치하는 건 재사용 (대소문자 구분) + * - 없을 경우 신규 태그 생성 + */ + private List buildTagList(List distinctNames, Map tagMap) { + List result = new ArrayList<>(); + for (String name : distinctNames) { + Tag tag = tagMap.getOrDefault(name, createTagSafely(name)); + if (tag != null) { + result.add(tag); + } + } + return result; } - private List createNewTags(List tagNames, Set existingNames) { - List newTags = tagNames.stream() - .filter(name -> !existingNames.contains(name)) - .map(name -> Tag.builder().name(name).build()) - .toList(); + private Tag createTagSafely(String name) { + // 생성 전 재조회로 중복 방지 + Tag existing = tagRepository.findByNameIn(List.of(name)).stream() + .filter(tag -> tag.getName().equals(name)) + .findFirst() + .orElse(null); - if (!newTags.isEmpty()) { - tagRepository.saveAll(newTags); + if (existing != null) { + return existing; } - return newTags; + + return tagRepository.save(Tag.builder().name(name).build()); } diff --git a/back/src/main/java/com/back/domain/mentoring/reservation/service/ReservationService.java b/back/src/main/java/com/back/domain/mentoring/reservation/service/ReservationService.java index 793bd01e..72184e3b 100644 --- a/back/src/main/java/com/back/domain/mentoring/reservation/service/ReservationService.java +++ b/back/src/main/java/com/back/domain/mentoring/reservation/service/ReservationService.java @@ -71,24 +71,28 @@ public ReservationResponse getReservation(Member member, Long reservationId) { @Transactional public ReservationResponse createReservation(Mentee mentee, ReservationRequest reqDto) { - Mentoring mentoring = mentoringStorage.findMentoring(reqDto.mentoringId()); - MentorSlot mentorSlot = mentoringStorage.findMentorSlot(reqDto.mentorSlotId()); + try { + Mentoring mentoring = mentoringStorage.findMentoring(reqDto.mentoringId()); + MentorSlot mentorSlot = mentoringStorage.findMentorSlot(reqDto.mentorSlotId()); - DateTimeValidator.validateStartTimeNotInPast(mentorSlot.getStartDateTime()); - validateMentorSlotStatus(mentorSlot, mentee); - validateOverlappingTimeForMentee(mentee, mentorSlot); + DateTimeValidator.validateStartTimeNotInPast(mentorSlot.getStartDateTime()); + validateMentorSlotStatus(mentorSlot, mentee); + validateOverlappingTimeForMentee(mentee, mentorSlot); - Reservation reservation = Reservation.builder() - .mentoring(mentoring) - .mentee(mentee) - .mentorSlot(mentorSlot) - .preQuestion(reqDto.preQuestion()) - .build(); - reservationRepository.save(reservation); + Reservation reservation = Reservation.builder() + .mentoring(mentoring) + .mentee(mentee) + .mentorSlot(mentorSlot) + .preQuestion(reqDto.preQuestion()) + .build(); + reservationRepository.save(reservation); - mentorSlot.updateStatus(MentorSlotStatus.PENDING); + mentorSlot.updateStatus(MentorSlotStatus.PENDING); - return ReservationResponse.from(reservation); + return ReservationResponse.from(reservation); + } catch (OptimisticLockException e) { + throw new ServiceException(ReservationErrorCode.CONCURRENT_RESERVATION_CONFLICT); + } } @Transactional