diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringDetailDto.java b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringDetailDto.java index ed36a8c5..21ebb055 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringDetailDto.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringDetailDto.java @@ -26,7 +26,7 @@ public static MentoringDetailDto from(Mentoring mentoring) { return new MentoringDetailDto( mentoring.getId(), mentoring.getTitle(), - mentoring.getTags(), + mentoring.getTagNames(), mentoring.getBio(), mentoring.getThumb(), mentoring.getCreateDate(), diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java index bdc44f91..1331f751 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java @@ -21,7 +21,7 @@ public static MentoringWithTagsDto from(Mentoring mentoring) { return new MentoringWithTagsDto( mentoring.getId(), mentoring.getTitle(), - mentoring.getTags(), + mentoring.getTagNames(), mentoring.getMentor().getId(), mentoring.getMentor().getMember().getNickname() ); 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 086a6d2e..f533fced 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 @@ -1,7 +1,6 @@ package com.back.domain.mentoring.mentoring.entity; import com.back.domain.member.mentor.entity.Mentor; -import com.back.global.converter.StringListConverter; import com.back.global.jpa.BaseEntity; import jakarta.persistence.*; import lombok.Builder; @@ -25,30 +24,45 @@ public class Mentoring extends BaseEntity { @Column(columnDefinition = "TEXT") private String bio; - @Convert(converter = StringListConverter.class) - @Column(columnDefinition = "JSON") - private List tags; + @OneToMany(mappedBy = "mentoring", cascade = CascadeType.ALL, orphanRemoval = true) + private List mentoringTags = new ArrayList<>(); @Column(length = 255) private String thumb; @Builder - public Mentoring(Mentor mentor, String title, String bio, List tags, String thumb) { + public Mentoring(Mentor mentor, String title, String bio, String thumb) { this.mentor = mentor; this.title = title; this.bio = bio; - this.tags = tags != null ? tags : new ArrayList<>(); this.thumb = thumb; } - public void update(String title, String bio, List tags, String thumb) { + public void update(String title, String bio, List tags, String thumb) { this.title = title; this.bio = bio; - this.tags = tags != null ? tags : new ArrayList<>(); this.thumb = thumb; + + updateTags(tags); + } + + public void updateTags(List tags) { + this.mentoringTags.clear(); + + if (tags != null) { + tags.forEach(tag -> + this.mentoringTags.add(new MentoringTag(this, tag)) + ); + } } public boolean isOwner(Mentor mentor) { return this.mentor.equals(mentor); } + + public List getTagNames() { + return mentoringTags.stream() + .map(mentoringTag -> mentoringTag.getTag().getName()) + .toList(); + } } diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/entity/MentoringTag.java b/back/src/main/java/com/back/domain/mentoring/mentoring/entity/MentoringTag.java new file mode 100644 index 00000000..97e5b73a --- /dev/null +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/entity/MentoringTag.java @@ -0,0 +1,27 @@ +package com.back.domain.mentoring.mentoring.entity; + +import com.back.global.jpa.BaseEntity; +import jakarta.persistence.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +@Table(name = "mentoring_tag") +public class MentoringTag extends BaseEntity { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "mentoring_id") + private Mentoring mentoring; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "tag_id") + private Tag tag; + + @Builder + public MentoringTag(Mentoring mentoring, Tag tag) { + this.mentoring = mentoring; + this.tag = tag; + } +} 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 new file mode 100644 index 00000000..6520868d --- /dev/null +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/entity/Tag.java @@ -0,0 +1,21 @@ +package com.back.domain.mentoring.mentoring.entity; + +import com.back.global.jpa.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class Tag extends BaseEntity { + @Column(length = 50, nullable = false, unique = true) + private String name; + + @Builder + public Tag(String name) { + this.name = name; + } +} diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/repository/MentoringRepositoryImpl.java b/back/src/main/java/com/back/domain/mentoring/mentoring/repository/MentoringRepositoryImpl.java index 04ac9a3f..77b59f93 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/repository/MentoringRepositoryImpl.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/repository/MentoringRepositoryImpl.java @@ -4,7 +4,11 @@ import com.back.domain.member.mentor.entity.QMentor; import com.back.domain.mentoring.mentoring.entity.Mentoring; import com.back.domain.mentoring.mentoring.entity.QMentoring; +import com.back.domain.mentoring.mentoring.entity.QMentoringTag; +import com.back.domain.mentoring.mentoring.entity.QTag; import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -23,40 +27,54 @@ public Page searchMentorings(String keyword, Pageable pageable) { QMentoring mentoring = QMentoring.mentoring; QMentor mentor = QMentor.mentor; QMember member = QMember.member; + QMentoringTag mentoringTag = QMentoringTag.mentoringTag; + QTag tag = QTag.tag; BooleanBuilder builder = new BooleanBuilder(); - // 제목, 멘토 닉네임 검색 조건 + // 제목, 멘토 닉네임, 태그 검색 조건 if (keyword != null && !keyword.isBlank()) { - builder.and( - mentoring.title.containsIgnoreCase(keyword) - .or(mentor.member.nickname.containsIgnoreCase(keyword)) - ); + // 제목, 멘토 닉네임 검색 조건 + BooleanExpression titleOrNickName = mentoring.title.containsIgnoreCase(keyword) + .or(mentor.member.nickname.containsIgnoreCase(keyword)); + + // 태그 검색 조건 (EXISTS 서브쿼리) + BooleanExpression tagSearch = JPAExpressions + .selectOne() + .from(mentoringTag) + .join(mentoringTag.tag, tag) + .where( + mentoringTag.mentoring.eq(mentoring) + .and(tag.name.containsIgnoreCase(keyword)) + ) + .exists(); + + builder.and(titleOrNickName).or(tagSearch); } - // 1. 조건에 맞는 모든 데이터 조회 (태그 제외) + // 조건에 맞는 모든 데이터 조회 List content = queryFactory .selectFrom(mentoring) .leftJoin(mentoring.mentor, mentor).fetchJoin() .leftJoin(mentor.member, member).fetchJoin() + .leftJoin(mentoring.mentoringTags, mentoringTag).fetchJoin() + .leftJoin(mentoringTag.tag, tag).fetchJoin() .where(builder) .orderBy(mentoring.id.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch(); - // 2. 태그 검색 - // TODO: 태그 테이블 분리 후 추가 예정 - - long total = getTotal(mentoring, builder); + long total = getTotal(mentoring, mentor, builder); return new PageImpl<>(content, pageable, total); } - private long getTotal(QMentoring mentoring, BooleanBuilder builder) { + private long getTotal(QMentoring mentoring, QMentor mentor, BooleanBuilder builder) { Long totalCount = queryFactory .select(mentoring.count()) .from(mentoring) + .leftJoin(mentoring.mentor, mentor) .where(builder) .fetchOne(); return totalCount != null ? totalCount : 0L; diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/repository/TagRepository.java b/back/src/main/java/com/back/domain/mentoring/mentoring/repository/TagRepository.java new file mode 100644 index 00000000..2c2648f5 --- /dev/null +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/repository/TagRepository.java @@ -0,0 +1,12 @@ +package com.back.domain.mentoring.mentoring.repository; + +import com.back.domain.mentoring.mentoring.entity.Tag; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; + +public interface TagRepository extends JpaRepository { + Optional findByName(String name); + List findByNameIn(List names); +} 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 119ac849..10769769 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 @@ -7,8 +7,10 @@ import com.back.domain.mentoring.mentoring.dto.request.MentoringRequest; import com.back.domain.mentoring.mentoring.dto.response.MentoringResponse; import com.back.domain.mentoring.mentoring.entity.Mentoring; +import com.back.domain.mentoring.mentoring.entity.Tag; import com.back.domain.mentoring.mentoring.error.MentoringErrorCode; import com.back.domain.mentoring.mentoring.repository.MentoringRepository; +import com.back.domain.mentoring.mentoring.repository.TagRepository; import com.back.global.exception.ServiceException; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -17,11 +19,17 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + @Service @RequiredArgsConstructor public class MentoringService { private final MentoringRepository mentoringRepository; private final MentoringStorage mentoringStorage; + private final TagRepository tagRepository; @Transactional(readOnly = true) public Page getMentorings(String keyword, int page, int size) { @@ -52,10 +60,12 @@ public MentoringResponse createMentoring(MentoringRequest reqDto, Mentor mentor) .mentor(mentor) .title(reqDto.title()) .bio(reqDto.bio()) - .tags(reqDto.tags()) .thumb(reqDto.thumb()) .build(); + List tags = getOrCreateTags(reqDto.tags()); + mentoring.updateTags(tags); + mentoringRepository.save(mentoring); return new MentoringResponse( @@ -70,7 +80,9 @@ public MentoringResponse updateMentoring(Long mentoringId, MentoringRequest reqD validateOwner(mentoring, mentor); - mentoring.update(reqDto.title(), reqDto.bio(), reqDto.tags(), reqDto.thumb()); + List tags = getOrCreateTags(reqDto.tags()); + + mentoring.update(reqDto.title(), reqDto.bio(), tags, reqDto.thumb()); return new MentoringResponse( MentoringDetailDto.from(mentoring), @@ -97,6 +109,43 @@ public void deleteMentoring(Long mentoringId, Mentor mentor) { } + // ==== Tag 관리 ===== + + private List getOrCreateTags(List tagNames) { + if (tagNames == null || tagNames.isEmpty()) { + return new ArrayList<>(); + } + + // 기존 태그 조회 + List existingTags = tagRepository.findByNameIn(tagNames); + + Set existingNames = existingTags.stream() + .map(Tag::getName) + .collect(Collectors.toSet()); + + // 신규 태그 생성 + List newTags = createNewTags(tagNames, existingNames); + + // 기존 태그 + 신규 태그 + List allTags = new ArrayList<>(existingTags); + allTags.addAll(newTags); + + return allTags; + } + + private List createNewTags(List tagNames, Set existingNames) { + List newTags = tagNames.stream() + .filter(name -> !existingNames.contains(name)) + .map(name -> Tag.builder().name(name).build()) + .toList(); + + if (!newTags.isEmpty()) { + tagRepository.saveAll(newTags); + } + return newTags; + } + + // ===== 유효성 검사 ===== private void validateOwner(Mentoring mentoring, Mentor mentor) { diff --git a/back/src/test/java/com/back/domain/mentoring/mentoring/controller/MentoringControllerTest.java b/back/src/test/java/com/back/domain/mentoring/mentoring/controller/MentoringControllerTest.java index c6837d92..73374ef3 100644 --- a/back/src/test/java/com/back/domain/mentoring/mentoring/controller/MentoringControllerTest.java +++ b/back/src/test/java/com/back/domain/mentoring/mentoring/controller/MentoringControllerTest.java @@ -192,9 +192,11 @@ void createMentoringSuccess() throws Exception { // Mentoring 정보 검증 .andExpect(jsonPath("$.data.mentoring.mentoringId").value(mentoring.getId())) .andExpect(jsonPath("$.data.mentoring.title").value(mentoring.getTitle())) - .andExpect(jsonPath("$.data.mentoring.tags").value(mentoring.getTags())) .andExpect(jsonPath("$.data.mentoring.bio").value(mentoring.getBio())) .andExpect(jsonPath("$.data.mentoring.thumb").value(mentoring.getThumb())) + .andExpect(jsonPath("$.data.mentoring.tags").isArray()) + .andExpect(jsonPath("$.data.mentoring.tags[0]").value("Spring")) + .andExpect(jsonPath("$.data.mentoring.tags[1]").value("Java")) // Mentor 정보 검증 .andExpect(jsonPath("$.data.mentor.mentorId").value(mentorOfMentoring.getId())) diff --git a/back/src/test/java/com/back/domain/mentoring/mentoring/service/MentoringServiceTest.java b/back/src/test/java/com/back/domain/mentoring/mentoring/service/MentoringServiceTest.java index 007bb28d..822f936f 100644 --- a/back/src/test/java/com/back/domain/mentoring/mentoring/service/MentoringServiceTest.java +++ b/back/src/test/java/com/back/domain/mentoring/mentoring/service/MentoringServiceTest.java @@ -6,11 +6,14 @@ import com.back.domain.mentoring.mentoring.dto.request.MentoringRequest; import com.back.domain.mentoring.mentoring.dto.response.MentoringResponse; import com.back.domain.mentoring.mentoring.entity.Mentoring; +import com.back.domain.mentoring.mentoring.entity.Tag; import com.back.domain.mentoring.mentoring.error.MentoringErrorCode; import com.back.domain.mentoring.mentoring.repository.MentoringRepository; +import com.back.domain.mentoring.mentoring.repository.TagRepository; import com.back.fixture.MemberFixture; import com.back.fixture.MentorFixture; import com.back.fixture.mentoring.MentoringFixture; +import com.back.fixture.mentoring.TagFixture; import com.back.global.exception.ServiceException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -41,6 +44,9 @@ class MentoringServiceTest { @Mock private MentoringRepository mentoringRepository; + @Mock + private TagRepository tagRepository; + @Mock private MentoringStorage mentoringStorage; @@ -166,6 +172,10 @@ class Describe_createMentoring { @Test @DisplayName("생성 성공") void createMentoring() { + List tags = TagFixture.createDefaultTags(); + + when(tagRepository.findByNameIn(request.tags())) + .thenReturn(tags); when(mentoringRepository.existsByMentorId(mentor1.getId())) .thenReturn(false); @@ -179,6 +189,7 @@ void createMentoring() { assertThat(result.mentoring().tags()).isEqualTo(request.tags()); assertThat(result.mentoring().thumb()).isEqualTo(request.thumb()); verify(mentoringRepository).existsByMentorId(mentor1.getId()); + verify(tagRepository).findByNameIn(request.tags()); verify(mentoringRepository).save(any(Mentoring.class)); } @@ -207,7 +218,10 @@ class Describe_updateMentoring { void updateMentoring() { // given Long mentoringId = 1L; + List tags = TagFixture.createDefaultTags(); + when(tagRepository.findByNameIn(request.tags())) + .thenReturn(tags); when(mentoringStorage.findMentoring(mentoringId)) .thenReturn(mentoring1); @@ -220,7 +234,9 @@ void updateMentoring() { assertThat(result.mentoring().bio()).isEqualTo(request.bio()); assertThat(result.mentoring().tags()).isEqualTo(request.tags()); assertThat(result.mentoring().thumb()).isEqualTo(request.thumb()); + verify(mentoringStorage).findMentoring(mentoringId); + verify(tagRepository).findByNameIn(request.tags()); } @Test diff --git a/back/src/test/java/com/back/fixture/mentoring/MentoringFixture.java b/back/src/test/java/com/back/fixture/mentoring/MentoringFixture.java index 05881bfb..c5ccb7dc 100644 --- a/back/src/test/java/com/back/fixture/mentoring/MentoringFixture.java +++ b/back/src/test/java/com/back/fixture/mentoring/MentoringFixture.java @@ -2,6 +2,7 @@ import com.back.domain.member.mentor.entity.Mentor; import com.back.domain.mentoring.mentoring.entity.Mentoring; +import com.back.domain.mentoring.mentoring.entity.Tag; import org.springframework.test.util.ReflectionTestUtils; import java.util.List; @@ -10,17 +11,18 @@ public class MentoringFixture { private static final String DEFAULT_TITLE = "테스트 멘토링"; private static final String DEFAULT_BIO = "테스트 설명"; - private static final List DEFAULT_TAGS = List.of("Spring", "Java"); + private static final List DEFAULT_TAGS = List.of(new Tag("Spring"), new Tag("Java")); private static final String DEFAULT_THUMB = "https://example.com/thumb.jpg"; public static Mentoring create(Mentor mentor) { - return Mentoring.builder() + Mentoring mentoring = Mentoring.builder() .mentor(mentor) .title(DEFAULT_TITLE) .bio(DEFAULT_BIO) - .tags(DEFAULT_TAGS) .thumb(DEFAULT_THUMB) .build(); + mentoring.updateTags(DEFAULT_TAGS); + return mentoring; } public static Mentoring create(Long id, Mentor mentor) { @@ -28,9 +30,9 @@ public static Mentoring create(Long id, Mentor mentor) { .mentor(mentor) .title(DEFAULT_TITLE) .bio(DEFAULT_BIO) - .tags(DEFAULT_TAGS) .thumb(DEFAULT_THUMB) .build(); + mentoring.updateTags(DEFAULT_TAGS); ReflectionTestUtils.setField(mentoring, "id", id); return mentoring; @@ -39,11 +41,11 @@ public static Mentoring create(Long id, Mentor mentor) { public static Mentoring create(Long id, Mentor mentor, String title, String bio, List tags) { Mentoring mentoring = Mentoring.builder() .mentor(mentor) - .title(title) - .bio(bio) - .tags(tags) + .title(DEFAULT_TITLE) + .bio(DEFAULT_BIO) .thumb(DEFAULT_THUMB) .build(); + mentoring.updateTags(DEFAULT_TAGS); if (id != null) { ReflectionTestUtils.setField(mentoring, "id", id); diff --git a/back/src/test/java/com/back/fixture/mentoring/MentoringTestFixture.java b/back/src/test/java/com/back/fixture/mentoring/MentoringTestFixture.java index ba4cbd05..fc6a6199 100644 --- a/back/src/test/java/com/back/fixture/mentoring/MentoringTestFixture.java +++ b/back/src/test/java/com/back/fixture/mentoring/MentoringTestFixture.java @@ -3,7 +3,9 @@ import com.back.domain.member.mentee.entity.Mentee; import com.back.domain.member.mentor.entity.Mentor; import com.back.domain.mentoring.mentoring.entity.Mentoring; +import com.back.domain.mentoring.mentoring.entity.Tag; import com.back.domain.mentoring.mentoring.repository.MentoringRepository; +import com.back.domain.mentoring.mentoring.repository.TagRepository; import com.back.domain.mentoring.reservation.entity.Reservation; import com.back.domain.mentoring.reservation.repository.ReservationRepository; import com.back.domain.mentoring.slot.entity.MentorSlot; @@ -15,6 +17,8 @@ import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.IntStream; @Component @@ -22,18 +26,22 @@ public class MentoringTestFixture { @Autowired private MentoringRepository mentoringRepository; @Autowired private MentorSlotRepository mentorSlotRepository; @Autowired private ReservationRepository reservationRepository; + @Autowired private TagRepository tagRepository; private int counter = 0; + List DEFAULT_TAG_NAMES = List.of("Spring", "Java"); // ===== Mentoring ===== - public Mentoring createMentoring(Mentor mentor, String title, String bio, List tags) { + public Mentoring createMentoring(Mentor mentor, String title, String bio, List tagNames) { + List tags = getOrCreateTags(tagNames); + Mentoring mentoring = Mentoring.builder() .mentor(mentor) .title(title) .bio(bio) - .tags(tags) .build(); + mentoring.updateTags(tags); return mentoringRepository.save(mentoring); } @@ -42,23 +50,55 @@ public Mentoring createMentoring(Mentor mentor) { mentor, "테스트 멘토링 " + (++counter), "테스트 설명", - List.of("Spring", "Java") + DEFAULT_TAG_NAMES ); } public List createMentorings(Mentor mentor, int count) { + List tags = getOrCreateTags(DEFAULT_TAG_NAMES); + List mentorings = IntStream.range(0, count) - .mapToObj(i -> Mentoring.builder() - .mentor(mentor) - .title("테스트 멘토링 " + (++counter)) - .bio("테스트 설명") - .tags(List.of("Spring", "Java")) - .build()) + .mapToObj(i -> { + Mentoring mentoring = Mentoring.builder() + .mentor(mentor) + .title("테스트 멘토링 " + (++counter)) + .bio("테스트 설명") + .build(); + mentoring.updateTags(tags); + return mentoring; + }) .toList(); return mentoringRepository.saveAll(mentorings); } + private List getOrCreateTags(List tagNames) { + if (tagNames == null || tagNames.isEmpty()) { + return new ArrayList<>(); + } + + // 기존 태그 조회 + List existingTags = tagRepository.findByNameIn(tagNames); + + Set existingTagNames = existingTags.stream() + .map(Tag::getName) + .collect(Collectors.toSet()); + + // 신규 태그만 생성 + List newTags = tagNames.stream() + .filter(name -> !existingTagNames.contains(name)) + .map(name -> Tag.builder().name(name).build()) + .collect(Collectors.toList()); + + if (!newTags.isEmpty()) { + tagRepository.saveAll(newTags); + } + + List allTags = new ArrayList<>(existingTags); + allTags.addAll(newTags); + return allTags; + } + // ===== MentorSlot ===== diff --git a/back/src/test/java/com/back/fixture/mentoring/TagFixture.java b/back/src/test/java/com/back/fixture/mentoring/TagFixture.java new file mode 100644 index 00000000..6588dd17 --- /dev/null +++ b/back/src/test/java/com/back/fixture/mentoring/TagFixture.java @@ -0,0 +1,29 @@ +package com.back.fixture.mentoring; + +import com.back.domain.mentoring.mentoring.entity.Tag; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +public class TagFixture { + + public static Tag create(String name) { + return Tag.builder() + .name(name) + .build(); + } + + public static Tag create(Long id, String name) { + Tag tag = Tag.builder() + .name(name) + .build(); + ReflectionTestUtils.setField(tag, "id", id); + return tag; + } + + public static List createDefaultTags() { + Tag spring = create(1L, "Spring"); + Tag java = create(2L, "Java"); + return List.of(spring, java); + } +}