From aa2c09a3e1f28d4e5cf89f98b07ed88bc299c46a Mon Sep 17 00:00:00 2001 From: sso0om Date: Thu, 16 Oct 2025 07:59:49 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Refactor:=20=EC=98=88=EC=95=BD=20=EC=8B=A0?= =?UTF-8?q?=EC=B2=AD=20=EC=8B=9C=20=EB=82=99=EA=B4=80=EC=A0=81=EC=9D=B8=20?= =?UTF-8?q?=EB=9D=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ReservationService.java | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) 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 From 5e76ea0701d21e20dae55924dbcce7047a4153e3 Mon Sep 17 00:00:00 2001 From: sso0om Date: Thu, 16 Oct 2025 08:00:47 +0900 Subject: [PATCH 2/2] =?UTF-8?q?Fix:=20=ED=83=9C=EA=B7=B8=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=9C=A0=EB=8B=88=ED=81=AC=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=A0=9C=EC=99=B8,=20MySql=20Case-Insensitive=20=EA=B3=A0?= =?UTF-8?q?=EB=A0=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mentoring/mentoring/entity/Mentoring.java | 7 ++- .../mentoring/mentoring/entity/Tag.java | 2 +- .../mentoring/service/MentoringService.java | 60 ++++++++++++------- 3 files changed, 46 insertions(+), 23 deletions(-) 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()); }