diff --git a/backend/build.gradle b/backend/build.gradle index 92c33754..7f0d4923 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -61,6 +61,7 @@ dependencies { implementation 'org.flywaydb:flyway-core' implementation 'org.flywaydb:flyway-mysql' + implementation 'com.github.f4b6a3:ulid-creator:5.2.3' implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' implementation 'org.springframework.cloud:spring-cloud-gcp-starter:1.2.5.RELEASE' implementation 'org.springframework.cloud:spring-cloud-gcp-storage:1.2.5.RELEASE' @@ -71,8 +72,8 @@ dependencies { annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" compileOnly 'org.projectlombok:lombok' - runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' - testRuntimeOnly 'com.h2database:h2' + runtimeOnly 'com.mysql:mysql-connector-j' + testRuntimeOnly 'com.mysql:mysql-connector-j' runtimeOnly 'io.micrometer:micrometer-registry-prometheus' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor 'org.projectlombok:lombok' @@ -91,6 +92,7 @@ dependencies { testImplementation "org.testcontainers:junit-jupiter:1.19.7" testImplementation "com.redis:testcontainers-redis:2.0.1" testImplementation "org.testcontainers:rabbitmq:1.19.7" + testImplementation "org.testcontainers:mysql:1.17.2" testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' } diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml index 948f38a7..d45ec9ba 100644 --- a/backend/docker-compose.yml +++ b/backend/docker-compose.yml @@ -2,7 +2,7 @@ version: "3" services: db: - image: mariadb:10 + image: mysql container_name: db ports: - 3306:3306 diff --git a/backend/src/main/java/com/twtw/backend/aspect/DistributedLockAspect.java b/backend/src/main/java/com/twtw/backend/aspect/DistributedLockAspect.java deleted file mode 100644 index 1600ef83..00000000 --- a/backend/src/main/java/com/twtw/backend/aspect/DistributedLockAspect.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.twtw.backend.aspect; - -import com.twtw.backend.global.lock.DistributedLock; -import com.twtw.backend.utils.SpringELParser; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.reflect.MethodSignature; -import org.redisson.api.RLock; -import org.redisson.api.RedissonClient; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Method; - -@Slf4j -@Aspect -@Component -@RequiredArgsConstructor -public class DistributedLockAspect { - private static final String REDISSON_LOCK_PREFIX = "LOCK:"; - private static final String INTERRUPTED_EXCEPTION = "INTERRUPTED EXCEPTION"; - private static final String UNLOCK_EXCEPTION = "UNLOCK EXCEPTION"; - - private final RedissonClient redissonClient; - private final TransactionAspect transactionAspect; - private final SpringELParser springELParser; - - @Around("@annotation(com.twtw.backend.global.lock.DistributedLock)") - public Object lock(final ProceedingJoinPoint joinPoint) throws Throwable { - MethodSignature signature = (MethodSignature) joinPoint.getSignature(); - Method method = signature.getMethod(); - DistributedLock distributedLock = method.getAnnotation(DistributedLock.class); - - String key = - REDISSON_LOCK_PREFIX - + springELParser.getDynamicValue( - signature.getParameterNames(), - joinPoint.getArgs(), - distributedLock.name()); - RLock rLock = redissonClient.getLock(key); - - try { - boolean available = - rLock.tryLock( - distributedLock.waitTime(), - distributedLock.leaseTime(), - distributedLock.timeUnit()); - if (!available) { - return false; - } - return transactionAspect.proceed(joinPoint); - } catch (final InterruptedException e) { - log.error(INTERRUPTED_EXCEPTION, e); - throw e; - } finally { - try { - rLock.unlock(); - } catch (final Exception e) { - log.error(UNLOCK_EXCEPTION, e); - } - } - } -} diff --git a/backend/src/main/java/com/twtw/backend/aspect/TransactionAspect.java b/backend/src/main/java/com/twtw/backend/aspect/TransactionAspect.java deleted file mode 100644 index 6f33feed..00000000 --- a/backend/src/main/java/com/twtw/backend/aspect/TransactionAspect.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.twtw.backend.aspect; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -@Component -public class TransactionAspect { - @Transactional(propagation = Propagation.REQUIRES_NEW) - public Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable { - return joinPoint.proceed(); - } -} diff --git a/backend/src/main/java/com/twtw/backend/domain/friend/entity/Friend.java b/backend/src/main/java/com/twtw/backend/domain/friend/entity/Friend.java index bd58422e..9b823722 100644 --- a/backend/src/main/java/com/twtw/backend/domain/friend/entity/Friend.java +++ b/backend/src/main/java/com/twtw/backend/domain/friend/entity/Friend.java @@ -1,29 +1,13 @@ package com.twtw.backend.domain.friend.entity; +import com.github.f4b6a3.ulid.UlidCreator; import com.twtw.backend.domain.friend.exception.InvalidFriendMemberException; import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.global.audit.AuditListener; import com.twtw.backend.global.audit.Auditable; import com.twtw.backend.global.audit.BaseTime; - -import jakarta.persistence.Column; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; - -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - +import jakarta.persistence.*; +import lombok.*; import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -31,15 +15,15 @@ @Getter @Entity +@EqualsAndHashCode(of = "id") @Where(clause = "deleted_at is null and friend_status != 'EXPIRED'") @EntityListeners(AuditListener.class) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Friend implements Auditable { @Id - @GeneratedValue(generator = "uuid2") - @Column(name = "id", columnDefinition = "BINARY(16)") - private UUID id; + @Column(columnDefinition = "BINARY(16)") + private UUID id = UlidCreator.getMonotonicUlid().toUuid(); @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(columnDefinition = "BINARY(16)") @@ -104,4 +88,8 @@ private boolean isRequestNotExpired() { .getCreatedAt() .isAfter(LocalDateTime.now().minusMinutes(30L))); } + + public boolean nicknameContains(final String nickname) { + return this.fromMember.nicknameContains(nickname) || this.toMember.nicknameContains(nickname); + } } diff --git a/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendCommandRepository.java b/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendCommandRepository.java new file mode 100644 index 00000000..a2d727f4 --- /dev/null +++ b/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendCommandRepository.java @@ -0,0 +1,13 @@ +package com.twtw.backend.domain.friend.repository; + +import com.twtw.backend.domain.friend.entity.Friend; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.UUID; + +@Repository +public interface FriendCommandRepository { + Friend save(final Friend friend); + List findByMemberAndMemberNickname(final UUID memberId, final String nickname); +} diff --git a/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendCommandRepositoryImpl.java b/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendCommandRepositoryImpl.java new file mode 100644 index 00000000..54d67f6c --- /dev/null +++ b/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendCommandRepositoryImpl.java @@ -0,0 +1,18 @@ +package com.twtw.backend.domain.friend.repository; + +import com.twtw.backend.domain.friend.entity.Friend; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.UUID; + +@Repository +public interface FriendCommandRepositoryImpl extends JpaRepository, FriendCommandRepository { + + @Query(value = "SELECT f.* FROM friend f JOIN member fm ON f.from_member_id = fm.id JOIN member tm ON f.to_member_id = tm.id WHERE f.friend_status = 'ACCEPTED' AND ((f.to_member_id = UNHEX(REPLACE(:memberId, '-', '')) AND MATCH(fm.nickname) AGAINST(:nickname IN BOOLEAN MODE)) OR (f.from_member_id = UNHEX(REPLACE(:memberId, '-', '')) AND MATCH(tm.nickname) AGAINST(:nickname IN BOOLEAN MODE)))", nativeQuery = true) + List findByMemberAndMemberNickname(@Param("memberId") final UUID memberId, @Param("nickname") final String nickname); + Friend save(final Friend friend); +} diff --git a/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendQueryRepository.java b/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendQueryRepository.java index d3ac0bb2..e22652fb 100644 --- a/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendQueryRepository.java +++ b/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendQueryRepository.java @@ -3,11 +3,13 @@ import com.twtw.backend.domain.friend.entity.Friend; import com.twtw.backend.domain.friend.entity.FriendStatus; import com.twtw.backend.domain.member.entity.Member; +import org.springframework.stereotype.Repository; import java.util.List; import java.util.Optional; import java.util.UUID; +@Repository public interface FriendQueryRepository { Optional findByTwoMemberId(final UUID loginMemberId, final UUID memberId); @@ -15,7 +17,5 @@ public interface FriendQueryRepository { List findByMemberAndFriendStatus(final Member member, final FriendStatus friendStatus); - List findByMemberAndMemberNickname(final Member member, final String nickname); - - boolean existsByTwoMemberId(final UUID loginMemberId, final UUID memberId); + List findByMemberAndMemberNicknameContaining(final UUID memberId, final String nickname); } diff --git a/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendQueryRepositoryImpl.java b/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendQueryRepositoryImpl.java index dbd96856..e1c7ac26 100644 --- a/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendQueryRepositoryImpl.java +++ b/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendQueryRepositoryImpl.java @@ -1,22 +1,18 @@ package com.twtw.backend.domain.friend.repository; -import static com.twtw.backend.domain.friend.entity.QFriend.friend; - import com.querydsl.jpa.impl.JPAQueryFactory; import com.twtw.backend.domain.friend.entity.Friend; import com.twtw.backend.domain.friend.entity.FriendStatus; import com.twtw.backend.domain.member.entity.Member; - -import jakarta.persistence.LockModeType; - import lombok.RequiredArgsConstructor; - import org.springframework.stereotype.Repository; import java.util.List; import java.util.Optional; import java.util.UUID; +import static com.twtw.backend.domain.friend.entity.QFriend.friend; + @Repository @RequiredArgsConstructor public class FriendQueryRepositoryImpl implements FriendQueryRepository { @@ -63,48 +59,13 @@ public List findByMemberAndFriendStatus( } @Override - public List findByMemberAndMemberNickname(final Member member, final String nickname) { - return jpaQueryFactory - .selectFrom(friend) + public List findByMemberAndMemberNicknameContaining(final UUID memberId, final String nickname) { + return jpaQueryFactory.selectFrom(friend) .where( - friend.friendStatus - .eq(FriendStatus.ACCEPTED) - .and( - friend.toMember - .eq(member) - .and( - friend.fromMember.nickname - .containsIgnoreCase(nickname)) - .or( - friend.fromMember - .eq(member) - .and( - friend.toMember.nickname - .containsIgnoreCase( - nickname))))) + (friend.friendStatus.eq(FriendStatus.ACCEPTED)) + .and + (friend.toMember.id.eq(memberId).and(friend.fromMember.nickname.contains(nickname))) + .or(friend.fromMember.id.eq(memberId).and(friend.toMember.nickname.contains(nickname)))) .fetch(); } - - @Override - public boolean existsByTwoMemberId(final UUID loginMemberId, final UUID memberId) { - return Optional.ofNullable( - jpaQueryFactory - .selectFrom(friend) - .setLockMode(LockModeType.PESSIMISTIC_READ) - .setHint("javax.persistence.lock.timeout", 3) - .where( - (friend.toMember - .id - .eq(loginMemberId) - .and(friend.fromMember.id.eq(memberId)) - .or( - friend.fromMember - .id - .eq(loginMemberId) - .and( - friend.toMember.id.eq( - memberId))))) - .fetchFirst()) - .isPresent(); - } } diff --git a/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendRepository.java b/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendRepository.java deleted file mode 100644 index d88c07fa..00000000 --- a/backend/src/main/java/com/twtw/backend/domain/friend/repository/FriendRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.twtw.backend.domain.friend.repository; - -import com.twtw.backend.domain.friend.entity.Friend; - -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.UUID; - -public interface FriendRepository extends JpaRepository, FriendQueryRepository {} diff --git a/backend/src/main/java/com/twtw/backend/domain/friend/service/FriendService.java b/backend/src/main/java/com/twtw/backend/domain/friend/service/FriendService.java index d363dcbb..b48badc2 100644 --- a/backend/src/main/java/com/twtw/backend/domain/friend/service/FriendService.java +++ b/backend/src/main/java/com/twtw/backend/domain/friend/service/FriendService.java @@ -6,7 +6,8 @@ import com.twtw.backend.domain.friend.entity.Friend; import com.twtw.backend.domain.friend.entity.FriendStatus; import com.twtw.backend.domain.friend.mapper.FriendMapper; -import com.twtw.backend.domain.friend.repository.FriendRepository; +import com.twtw.backend.domain.friend.repository.FriendCommandRepository; +import com.twtw.backend.domain.friend.repository.FriendQueryRepository; import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.domain.member.service.AuthService; import com.twtw.backend.domain.member.service.MemberService; @@ -15,10 +16,8 @@ import com.twtw.backend.global.constant.NotificationBody; import com.twtw.backend.global.constant.NotificationTitle; import com.twtw.backend.global.exception.EntityNotFoundException; -import com.twtw.backend.global.lock.DistributedLock; - +import com.twtw.backend.utils.QueryParseUtils; import lombok.RequiredArgsConstructor; - import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -30,7 +29,8 @@ @Service @RequiredArgsConstructor public class FriendService { - private final FriendRepository friendRepository; + private final FriendQueryRepository friendQueryRepository; + private final FriendCommandRepository friendCommandRepository; private final FriendMapper friendMapper; private final MemberService memberService; private final AuthService authService; @@ -55,9 +55,8 @@ private void createFriendRequestByNicknameOrder(final Member loginMember, final createFriendRequest(member, loginMember); } - @DistributedLock(name = "#fromMember.getNickname().concat(#toMember.getNickname())") public void createFriendRequest(final Member fromMember, final Member toMember) { - friendRepository.save(friendMapper.toEntity(fromMember, toMember)); + friendCommandRepository.save(friendMapper.toEntity(fromMember, toMember)); } private void sendNotification(final String deviceToken, final String nickname, final UUID id) { @@ -78,7 +77,7 @@ public void updateStatus(final FriendUpdateRequest friendUpdateRequest) { } private Friend getFriendById(final UUID loginMemberId, final UUID memberId) { - return friendRepository + return friendQueryRepository .findByTwoMemberId(loginMemberId, memberId) .orElseThrow(EntityNotFoundException::new); } @@ -109,7 +108,7 @@ public List getFriendsWithCache() { private List getFriendResponses() { final Member loginMember = authService.getMemberByJwt(); final List friends = - friendRepository.findByMember(loginMember).stream() + friendQueryRepository.findByMember(loginMember).stream() .map(friend -> friend.getFriendMember(loginMember)) .toList(); @@ -140,7 +139,7 @@ public List getFriendsByStatusWithCache(final FriendStatus frien private List getFriendResponsesByStatus(final FriendStatus friendStatus) { final Member loginMember = authService.getMemberByJwt(); final List friends = - friendRepository.findByMemberAndFriendStatus(loginMember, friendStatus).stream() + friendQueryRepository.findByMemberAndFriendStatus(loginMember, friendStatus).stream() .map(friend -> friend.getFriendMember(loginMember)) .toList(); @@ -168,11 +167,20 @@ public List getFriendByNicknameWithCache(final String nickname) private List getFriendResponsesByNickname(final String nickname) { final Member loginMember = authService.getMemberByJwt(); - final List friends = - friendRepository.findByMemberAndMemberNickname(loginMember, nickname).stream() - .map(friend -> friend.getFriendMember(loginMember)) - .toList(); + final List friends = findFriendsByNickname(loginMember, nickname); return friendMapper.toResponses(friends); } + + private List findFriendsByNickname(final Member loginMember, final String nickname) { + if (nickname.length() < 2) { + return friendQueryRepository.findByMemberAndMemberNicknameContaining(loginMember.getId(), nickname).stream() + .map(friend -> friend.getFriendMember(loginMember)) + .toList(); + } + return friendCommandRepository.findByMemberAndMemberNickname(loginMember.getId(), QueryParseUtils.parse(nickname)).stream() + .filter(friend -> friend.nicknameContains(nickname)) + .map(friend -> friend.getFriendMember(loginMember)) + .toList(); + } } diff --git a/backend/src/main/java/com/twtw/backend/domain/group/entity/Group.java b/backend/src/main/java/com/twtw/backend/domain/group/entity/Group.java index 96dddb7f..249d62e7 100644 --- a/backend/src/main/java/com/twtw/backend/domain/group/entity/Group.java +++ b/backend/src/main/java/com/twtw/backend/domain/group/entity/Group.java @@ -1,23 +1,14 @@ package com.twtw.backend.domain.group.entity; +import com.github.f4b6a3.ulid.UlidCreator; import com.twtw.backend.domain.group.exception.IllegalGroupMemberException; import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.domain.plan.entity.Plan; import com.twtw.backend.global.audit.AuditListener; import com.twtw.backend.global.audit.Auditable; import com.twtw.backend.global.audit.BaseTime; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; - +import jakarta.persistence.*; import lombok.*; - import org.hibernate.annotations.Where; import java.util.ArrayList; @@ -30,10 +21,10 @@ @EntityListeners(AuditListener.class) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Group implements Auditable { + @Id - @GeneratedValue(generator = "uuid2") - @Column(name = "id", columnDefinition = "BINARY(16)") - private UUID id; + @Column(columnDefinition = "BINARY(16)") + private UUID id = UlidCreator.getMonotonicUlid().toUuid(); private String name; private String groupImage; diff --git a/backend/src/main/java/com/twtw/backend/domain/group/entity/GroupMember.java b/backend/src/main/java/com/twtw/backend/domain/group/entity/GroupMember.java index 1ba81c82..27f9cbcd 100644 --- a/backend/src/main/java/com/twtw/backend/domain/group/entity/GroupMember.java +++ b/backend/src/main/java/com/twtw/backend/domain/group/entity/GroupMember.java @@ -1,27 +1,16 @@ package com.twtw.backend.domain.group.entity; +import com.github.f4b6a3.ulid.UlidCreator; import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.domain.place.entity.Coordinate; import com.twtw.backend.global.audit.AuditListener; import com.twtw.backend.global.audit.Auditable; import com.twtw.backend.global.audit.BaseTime; - -import jakarta.persistence.Column; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; - +import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; - import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -35,9 +24,8 @@ public class GroupMember implements Auditable { @Id - @GeneratedValue(generator = "uuid2") - @Column(name = "id", columnDefinition = "BINARY(16)") - private UUID id; + @Column(columnDefinition = "BINARY(16)") + private UUID id = UlidCreator.getMonotonicUlid().toUuid(); @ManyToOne @JoinColumn(name = "group_id") diff --git a/backend/src/main/java/com/twtw/backend/domain/group/repository/GroupMemberRepository.java b/backend/src/main/java/com/twtw/backend/domain/group/repository/GroupMemberRepository.java deleted file mode 100644 index 75ac6f49..00000000 --- a/backend/src/main/java/com/twtw/backend/domain/group/repository/GroupMemberRepository.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.twtw.backend.domain.group.repository; - -import com.twtw.backend.domain.group.entity.GroupMember; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -import java.util.Optional; -import java.util.UUID; - -public interface GroupMemberRepository extends JpaRepository { - - @Query( - "SELECT gm from GroupMember gm WHERE gm.group.id = :groupId AND " - + "gm.member.id = :memberId") - Optional findByGroupIdAndMemberId( - @Param("groupId") UUID groupId, @Param("memberId") UUID memberId); -} diff --git a/backend/src/main/java/com/twtw/backend/domain/group/repository/GroupRepository.java b/backend/src/main/java/com/twtw/backend/domain/group/repository/GroupRepository.java index 7860acab..d3861546 100644 --- a/backend/src/main/java/com/twtw/backend/domain/group/repository/GroupRepository.java +++ b/backend/src/main/java/com/twtw/backend/domain/group/repository/GroupRepository.java @@ -1,9 +1,13 @@ package com.twtw.backend.domain.group.repository; import com.twtw.backend.domain.group.entity.Group; +import org.springframework.stereotype.Repository; -import org.springframework.data.jpa.repository.JpaRepository; - +import java.util.Optional; import java.util.UUID; -public interface GroupRepository extends JpaRepository {} +@Repository +public interface GroupRepository { + Optional findById(final UUID groupId); + Group save(final Group group); +} diff --git a/backend/src/main/java/com/twtw/backend/domain/group/repository/JpaGroupRepository.java b/backend/src/main/java/com/twtw/backend/domain/group/repository/JpaGroupRepository.java new file mode 100644 index 00000000..c2136e31 --- /dev/null +++ b/backend/src/main/java/com/twtw/backend/domain/group/repository/JpaGroupRepository.java @@ -0,0 +1,9 @@ +package com.twtw.backend.domain.group.repository; + +import com.twtw.backend.domain.group.entity.Group; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface JpaGroupRepository extends JpaRepository, GroupRepository {} diff --git a/backend/src/main/java/com/twtw/backend/domain/group/service/GroupService.java b/backend/src/main/java/com/twtw/backend/domain/group/service/GroupService.java index 1df08853..e6e3fbb5 100644 --- a/backend/src/main/java/com/twtw/backend/domain/group/service/GroupService.java +++ b/backend/src/main/java/com/twtw/backend/domain/group/service/GroupService.java @@ -16,7 +16,6 @@ import com.twtw.backend.global.constant.NotificationBody; import com.twtw.backend.global.constant.NotificationTitle; import com.twtw.backend.global.exception.EntityNotFoundException; - import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; diff --git a/backend/src/main/java/com/twtw/backend/domain/member/entity/Member.java b/backend/src/main/java/com/twtw/backend/domain/member/entity/Member.java index f33fa8f6..db002eaf 100644 --- a/backend/src/main/java/com/twtw/backend/domain/member/entity/Member.java +++ b/backend/src/main/java/com/twtw/backend/domain/member/entity/Member.java @@ -1,5 +1,6 @@ package com.twtw.backend.domain.member.entity; +import com.github.f4b6a3.ulid.UlidCreator; import com.twtw.backend.domain.group.entity.GroupMember; import com.twtw.backend.global.audit.AuditListener; import com.twtw.backend.global.audit.Auditable; @@ -22,12 +23,12 @@ @EntityListeners(AuditListener.class) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Member implements Auditable { + @Id - @GeneratedValue(generator = "uuid2") - @Column(name = "id", columnDefinition = "BINARY(16)") - private UUID id; + @Column(columnDefinition = "BINARY(16)") + private UUID id = UlidCreator.getMonotonicUlid().toUuid(); - @Column(unique = true, nullable = false) + @Column(nullable = false, unique = true, length = 8) private String nickname; private String profileImage; @@ -90,4 +91,8 @@ public void removeGroupMember(final GroupMember groupMember) { public boolean hasFasterNickname(final Member member) { return this.nickname.compareTo(member.nickname) < 0; } + + public boolean nicknameContains(final String nickname) { + return this.nickname.contains(nickname); + } } diff --git a/backend/src/main/java/com/twtw/backend/domain/member/entity/RefreshToken.java b/backend/src/main/java/com/twtw/backend/domain/member/entity/RefreshToken.java index ce111a50..0b89f54a 100644 --- a/backend/src/main/java/com/twtw/backend/domain/member/entity/RefreshToken.java +++ b/backend/src/main/java/com/twtw/backend/domain/member/entity/RefreshToken.java @@ -1,10 +1,9 @@ package com.twtw.backend.domain.member.entity; +import com.github.f4b6a3.ulid.UlidCreator; import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; - import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -15,10 +14,10 @@ @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class RefreshToken { + @Id - @GeneratedValue(generator = "uuid2") @Column(name = "id", columnDefinition = "BINARY(16)") - private UUID id; + private UUID id = UlidCreator.getMonotonicUlid().toUuid(); private String tokenKey; diff --git a/backend/src/main/java/com/twtw/backend/domain/member/repository/JpaMemberRepository.java b/backend/src/main/java/com/twtw/backend/domain/member/repository/JpaMemberRepository.java new file mode 100644 index 00000000..969be7a7 --- /dev/null +++ b/backend/src/main/java/com/twtw/backend/domain/member/repository/JpaMemberRepository.java @@ -0,0 +1,31 @@ +package com.twtw.backend.domain.member.repository; + +import com.twtw.backend.domain.member.entity.AuthType; +import com.twtw.backend.domain.member.entity.Member; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface JpaMemberRepository extends JpaRepository, MemberRepository { + + @Query( + value = + "SELECT * FROM member m WHERE MATCH (m.nickname) AGAINST(:nickname IN BOOLEAN MODE)", nativeQuery = true) + List findAllByNickname(@Param("nickname") String nickname); + + @Query( + "SELECT m FROM Member m WHERE m.oauthInfo.clientId = :oAuthId AND" + + " m.oauthInfo.authType = :authType") + Optional findByOAuthIdAndAuthType( + @Param("oAuthId") String oAuthId, @Param("authType") AuthType authType); + + @Query("SELECT m FROM Member m WHERE m.id in :friendMemberIds") + List findAllByIds(@Param("friendMemberIds") final List friendMemberIds); +} diff --git a/backend/src/main/java/com/twtw/backend/domain/member/repository/JpaRefreshTokenRepository.java b/backend/src/main/java/com/twtw/backend/domain/member/repository/JpaRefreshTokenRepository.java new file mode 100644 index 00000000..e1fe0185 --- /dev/null +++ b/backend/src/main/java/com/twtw/backend/domain/member/repository/JpaRefreshTokenRepository.java @@ -0,0 +1,10 @@ +package com.twtw.backend.domain.member.repository; + +import com.twtw.backend.domain.member.entity.RefreshToken; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface JpaRefreshTokenRepository extends JpaRepository, RefreshTokenRepository {} diff --git a/backend/src/main/java/com/twtw/backend/domain/member/repository/MemberRepository.java b/backend/src/main/java/com/twtw/backend/domain/member/repository/MemberRepository.java index e69e5629..df957135 100644 --- a/backend/src/main/java/com/twtw/backend/domain/member/repository/MemberRepository.java +++ b/backend/src/main/java/com/twtw/backend/domain/member/repository/MemberRepository.java @@ -2,28 +2,20 @@ import com.twtw.backend.domain.member.entity.AuthType; import com.twtw.backend.domain.member.entity.Member; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; import java.util.List; import java.util.Optional; import java.util.UUID; -public interface MemberRepository extends JpaRepository { - - @Query( - value = - "SELECT m FROM Member m WHERE upper(m.nickname) LIKE upper(concat('%'," - + " :nickname, '%'))") - List findAllByNicknameContainingIgnoreCase(@Param("nickname") String nickname); - - @Query( - "SELECT m FROM Member m WHERE m.oauthInfo.clientId = :oAuthId AND" - + " m.oauthInfo.authType = :authType") - Optional findByOAuthIdAndAuthType( - @Param("oAuthId") String oAuthId, @Param("authType") AuthType authType); - - boolean existsByNickname(String nickname); +@Repository +public interface MemberRepository { + List findAllByNickname(final String nickname); + List findAllByNicknameContainingIgnoreCase(final String nickname); + Optional findByOAuthIdAndAuthType(final String oAuthId, final AuthType authType); + boolean existsByNickname(final String nickname); + Member save(final Member member); + Optional findById(final UUID id); + List findAllByIds(final List friendMemberIds); + void deleteById(final UUID memberId); } diff --git a/backend/src/main/java/com/twtw/backend/domain/member/repository/RefreshTokenRepository.java b/backend/src/main/java/com/twtw/backend/domain/member/repository/RefreshTokenRepository.java index a00afd99..bd79ed08 100644 --- a/backend/src/main/java/com/twtw/backend/domain/member/repository/RefreshTokenRepository.java +++ b/backend/src/main/java/com/twtw/backend/domain/member/repository/RefreshTokenRepository.java @@ -1,7 +1,12 @@ package com.twtw.backend.domain.member.repository; import com.twtw.backend.domain.member.entity.RefreshToken; +import org.springframework.stereotype.Repository; -import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; -public interface RefreshTokenRepository extends JpaRepository {} +@Repository +public interface RefreshTokenRepository { + Optional findByTokenKey(final String tokenKey); + RefreshToken save(final RefreshToken refreshToken); +} diff --git a/backend/src/main/java/com/twtw/backend/domain/member/service/AuthService.java b/backend/src/main/java/com/twtw/backend/domain/member/service/AuthService.java index 02d482e7..55af6f10 100644 --- a/backend/src/main/java/com/twtw/backend/domain/member/service/AuthService.java +++ b/backend/src/main/java/com/twtw/backend/domain/member/service/AuthService.java @@ -18,7 +18,6 @@ import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.domain.member.repository.RefreshTokenRepository; import com.twtw.backend.global.exception.EntityNotFoundException; - import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -122,7 +121,9 @@ public TokenDto refreshToken(TokenRequest tokenRequest) { } public String getRefreshTokenValue(String tokenKey) { - return refreshTokenRepository.getReferenceById(tokenKey).getTokenValue(); + return refreshTokenRepository.findByTokenKey(tokenKey) + .orElseThrow(EntityNotFoundException::new) + .getTokenValue(); } public TokenDto saveRefreshToken(Authentication authentication, String userName) { diff --git a/backend/src/main/java/com/twtw/backend/domain/member/service/MemberService.java b/backend/src/main/java/com/twtw/backend/domain/member/service/MemberService.java index c6b5404c..30759046 100644 --- a/backend/src/main/java/com/twtw/backend/domain/member/service/MemberService.java +++ b/backend/src/main/java/com/twtw/backend/domain/member/service/MemberService.java @@ -7,7 +7,7 @@ import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.domain.plan.entity.Plan; import com.twtw.backend.global.exception.EntityNotFoundException; - +import com.twtw.backend.utils.QueryParseUtils; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -49,10 +49,21 @@ public String getMemberIdValue() { cacheManager = "cacheManager") @Transactional(readOnly = true) public List getMemberByNickname(final String nickname) { - final List members = - memberRepository.findAllByNicknameContainingIgnoreCase(nickname); - members.removeIf(authService.getMemberByJwt()::equals); - return getResponsesByMembers(members); + return getMemberResponses(nickname); + } + + private List getMemberResponses(final String nickname) { + final List members = getMembersByNickname(nickname); + final Member member = authService.getMemberByJwt(); + return getResponsesByMembers(members.stream().filter(m -> !m.equals(member)).toList()); + } + + public List getMembersByNickname(final String nickname) { + if (nickname.length() < 2) { + return memberRepository.findAllByNicknameContainingIgnoreCase(nickname); + } + return memberRepository.findAllByNickname(QueryParseUtils.parse(nickname)) + .stream().filter(member -> member.nicknameContains(nickname)).toList(); } @Cacheable( @@ -62,10 +73,7 @@ public List getMemberByNickname(final String nickname) { unless = "#result.size() <= 0") @Transactional(readOnly = true) public List getMemberByNicknameWithCache(final String nickname) { - final List members = - memberRepository.findAllByNicknameContainingIgnoreCase(nickname); - members.removeIf(authService.getMemberByJwt()::equals); - return getResponsesByMembers(members); + return getMemberResponses(nickname); } public MemberResponse getResponseByMember(Member member) { @@ -81,7 +89,7 @@ public List getMemberResponses(final Plan plan) { } public List getMembersByIds(final List friendMemberIds) { - return memberRepository.findAllById(friendMemberIds); + return memberRepository.findAllByIds(friendMemberIds); } @CacheEvict( diff --git a/backend/src/main/java/com/twtw/backend/domain/path/service/PathService.java b/backend/src/main/java/com/twtw/backend/domain/path/service/PathService.java index fea101eb..6022c2e2 100644 --- a/backend/src/main/java/com/twtw/backend/domain/path/service/PathService.java +++ b/backend/src/main/java/com/twtw/backend/domain/path/service/PathService.java @@ -27,7 +27,7 @@ public PathService( cacheManager = "cacheManager", unless = "#result.code != 0") public SearchCarPathResponse searchCarPath(final SearchCarPathRequest request) { - return this.carPathClient.request(request); + return carPathClient.request(request); } @Cacheable( @@ -36,6 +36,6 @@ public SearchCarPathResponse searchCarPath(final SearchCarPathRequest request) { cacheManager = "cacheManager", unless = "#result.features.size() <= 0") public SearchPedPathResponse searchPedPath(final SearchPedPathRequest request) { - return this.pedPathClient.request(request); + return pedPathClient.request(request); } } diff --git a/backend/src/main/java/com/twtw/backend/domain/place/entity/Place.java b/backend/src/main/java/com/twtw/backend/domain/place/entity/Place.java index 1eb9c37b..bbd9c26f 100644 --- a/backend/src/main/java/com/twtw/backend/domain/place/entity/Place.java +++ b/backend/src/main/java/com/twtw/backend/domain/place/entity/Place.java @@ -1,22 +1,11 @@ package com.twtw.backend.domain.place.entity; +import com.github.f4b6a3.ulid.UlidCreator; import com.twtw.backend.global.audit.AuditListener; import com.twtw.backend.global.audit.Auditable; import com.twtw.backend.global.audit.BaseTime; - -import jakarta.persistence.Column; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; - -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - +import jakarta.persistence.*; +import lombok.*; import org.hibernate.annotations.Where; import java.util.UUID; @@ -27,10 +16,10 @@ @EntityListeners(AuditListener.class) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Place implements Auditable { + @Id - @GeneratedValue(generator = "uuid2") @Column(columnDefinition = "BINARY(16)") - private UUID id; + private UUID id = UlidCreator.getMonotonicUlid().toUuid(); @Column(nullable = false) private String placeName; diff --git a/backend/src/main/java/com/twtw/backend/domain/plan/entity/Plan.java b/backend/src/main/java/com/twtw/backend/domain/plan/entity/Plan.java index f155dd41..cfc3375c 100644 --- a/backend/src/main/java/com/twtw/backend/domain/plan/entity/Plan.java +++ b/backend/src/main/java/com/twtw/backend/domain/plan/entity/Plan.java @@ -1,5 +1,6 @@ package com.twtw.backend.domain.plan.entity; +import com.github.f4b6a3.ulid.UlidCreator; import com.twtw.backend.domain.group.entity.Group; import com.twtw.backend.domain.group.entity.GroupMember; import com.twtw.backend.domain.member.entity.Member; @@ -8,11 +9,8 @@ import com.twtw.backend.global.audit.AuditListener; import com.twtw.backend.global.audit.Auditable; import com.twtw.backend.global.audit.BaseTime; - import jakarta.persistence.*; - import lombok.*; - import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -28,20 +26,20 @@ @EntityListeners(AuditListener.class) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Plan implements Auditable { + @Id - @GeneratedValue(generator = "uuid2") @Column(columnDefinition = "BINARY(16)") - private UUID id; + private UUID id = UlidCreator.getMonotonicUlid().toUuid(); @Column(nullable = false) private String name; @JoinColumn(columnDefinition = "BINARY(16)") - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST, orphanRemoval = true) + @OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) private Place place; @JoinColumn(name = "group_id") - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) + @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) private Group group; @OneToMany( diff --git a/backend/src/main/java/com/twtw/backend/domain/plan/entity/PlanMember.java b/backend/src/main/java/com/twtw/backend/domain/plan/entity/PlanMember.java index dc94561e..5066e68c 100644 --- a/backend/src/main/java/com/twtw/backend/domain/plan/entity/PlanMember.java +++ b/backend/src/main/java/com/twtw/backend/domain/plan/entity/PlanMember.java @@ -1,5 +1,6 @@ package com.twtw.backend.domain.plan.entity; +import com.github.f4b6a3.ulid.UlidCreator; import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.global.audit.AuditListener; import com.twtw.backend.global.audit.Auditable; @@ -24,10 +25,10 @@ @EntityListeners(AuditListener.class) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class PlanMember implements Auditable { + @Id - @GeneratedValue(generator = "uuid2") @Column(columnDefinition = "BINARY(16)") - private UUID id; + private UUID id = UlidCreator.getMonotonicUlid().toUuid(); @JoinColumn(columnDefinition = "BINARY(16)") @ManyToOne(fetch = FetchType.LAZY) diff --git a/backend/src/main/java/com/twtw/backend/domain/plan/repository/JpaPlanRepository.java b/backend/src/main/java/com/twtw/backend/domain/plan/repository/JpaPlanRepository.java new file mode 100644 index 00000000..e5121346 --- /dev/null +++ b/backend/src/main/java/com/twtw/backend/domain/plan/repository/JpaPlanRepository.java @@ -0,0 +1,19 @@ +package com.twtw.backend.domain.plan.repository; + +import com.twtw.backend.domain.member.entity.Member; +import com.twtw.backend.domain.plan.entity.Plan; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.UUID; + +public interface JpaPlanRepository extends JpaRepository, PlanRepository { + + @Query( + "select p from Plan p join fetch p.planMembers pm join fetch pm.member m where m =" + + " :member") + List findAllByMember(@Param("member") Member member); +} diff --git a/backend/src/main/java/com/twtw/backend/domain/plan/repository/PlanRepository.java b/backend/src/main/java/com/twtw/backend/domain/plan/repository/PlanRepository.java index 40232ad4..8ee08fbd 100644 --- a/backend/src/main/java/com/twtw/backend/domain/plan/repository/PlanRepository.java +++ b/backend/src/main/java/com/twtw/backend/domain/plan/repository/PlanRepository.java @@ -2,18 +2,16 @@ import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.domain.plan.entity.Plan; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; import java.util.UUID; -public interface PlanRepository extends JpaRepository { - - @Query( - "select p from Plan p join fetch p.planMembers pm join fetch pm.member m where m =" - + " :member") - List findAllByMember(@Param("member") Member member); +@Repository +public interface PlanRepository { + List findAllByMember(final Member member); + Plan save(final Plan plan); + void deleteById(final UUID id); + Optional findById(final UUID id); } diff --git a/backend/src/main/java/com/twtw/backend/domain/plan/service/PlanService.java b/backend/src/main/java/com/twtw/backend/domain/plan/service/PlanService.java index 02fa1f79..0a773742 100644 --- a/backend/src/main/java/com/twtw/backend/domain/plan/service/PlanService.java +++ b/backend/src/main/java/com/twtw/backend/domain/plan/service/PlanService.java @@ -28,9 +28,7 @@ import com.twtw.backend.global.constant.NotificationBody; import com.twtw.backend.global.constant.NotificationTitle; import com.twtw.backend.global.exception.EntityNotFoundException; - import lombok.RequiredArgsConstructor; - import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; diff --git a/backend/src/main/java/com/twtw/backend/global/lock/DistributedLock.java b/backend/src/main/java/com/twtw/backend/global/lock/DistributedLock.java deleted file mode 100644 index 832ca366..00000000 --- a/backend/src/main/java/com/twtw/backend/global/lock/DistributedLock.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.twtw.backend.global.lock; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.concurrent.TimeUnit; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface DistributedLock { - String name(); - - TimeUnit timeUnit() default TimeUnit.SECONDS; - - long waitTime() default 3L; - - long leaseTime() default 5L; -} diff --git a/backend/src/main/java/com/twtw/backend/utils/QueryParseUtils.java b/backend/src/main/java/com/twtw/backend/utils/QueryParseUtils.java new file mode 100644 index 00000000..a6cc4cbf --- /dev/null +++ b/backend/src/main/java/com/twtw/backend/utils/QueryParseUtils.java @@ -0,0 +1,22 @@ +package com.twtw.backend.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class QueryParseUtils { + + private static final String BLANK = " "; + private static final String PLUS = "+"; + + public static String parse(final String input) { + final StringBuilder result = new StringBuilder(); + for (int i = 0; i < input.length() - 1; i++) { + if (i > 0) { + result.append(BLANK); + } + result.append(PLUS).append(input, i, i + 2); + } + return result.toString(); + } +} diff --git a/backend/src/main/java/com/twtw/backend/utils/SpringELParser.java b/backend/src/main/java/com/twtw/backend/utils/SpringELParser.java deleted file mode 100644 index 9105771a..00000000 --- a/backend/src/main/java/com/twtw/backend/utils/SpringELParser.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.twtw.backend.utils; - -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.stereotype.Component; - -@Component -public class SpringELParser { - - private static final ExpressionParser PARSER = new SpelExpressionParser(); - private static final Integer START_INDEX = 0; - - public Object getDynamicValue( - final String[] parameterNames, final Object[] args, final String key) { - final StandardEvaluationContext context = new StandardEvaluationContext(); - - for (int i = START_INDEX; i < parameterNames.length; i++) { - context.setVariable(parameterNames[i], args[i]); - } - - return PARSER.parseExpression(key).getValue(context, Object.class); - } -} diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml index 4ecc0a81..59426285 100644 --- a/backend/src/main/resources/application-dev.yml +++ b/backend/src/main/resources/application-dev.yml @@ -3,8 +3,8 @@ server: context-path: /api/v1 spring: datasource: - driver-class-name: org.mariadb.jdbc.Driver - url: jdbc:mariadb://localhost:3306/TWTW + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/TWTW username: root password: 1234 jpa: @@ -34,7 +34,7 @@ spring: jackson: default-property-inclusion: non_null flyway: - url: jdbc:mariadb://localhost:3306/TWTW + url: jdbc:mysql://localhost:3306/TWTW enabled: true baseline-on-migrate: false user: root diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index dc6ecf6e..3489da36 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -3,8 +3,8 @@ server: context-path: /api/v1 spring: datasource: - driver-class-name: org.mariadb.jdbc.Driver - url: jdbc:mariadb://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?serverTimezone=Asia/Seoul + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?serverTimezone=Asia/Seoul username: ${MYSQL_USER} password: ${MYSQL_PASSWORD} jpa: @@ -43,9 +43,9 @@ spring: bucket: ${BUCKET_NAME} storage-url: ${BUCKET_URL} flyway: - url: jdbc:mariadb://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?serverTimezone=Asia/Seoul + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?serverTimezone=Asia/Seoul enabled: true - baseline-on-migrate: false + baseline-on-migrate: true user: ${MYSQL_USER} password: ${MYSQL_PASSWORD} management: diff --git a/backend/src/main/resources/db/migration/V1__init.sql b/backend/src/main/resources/db/migration/V1__init.sql index d6f7c524..0efa0889 100644 --- a/backend/src/main/resources/db/migration/V1__init.sql +++ b/backend/src/main/resources/db/migration/V1__init.sql @@ -18,7 +18,7 @@ create table `member` device_token_id BINARY(16), id BINARY(16) not null, auth_type enum ('APPLE','KAKAO'), - nickname varchar(255) not null, + nickname varchar(8) not null, profile_image varchar(255), role enum ('ROLE_ADMIN','ROLE_USER'), client_id tinytext not null, @@ -127,3 +127,5 @@ alter table plan_member add constraint fk_plan_member_member_id foreign key (`member_id`) references `member` (id); alter table plan_member add constraint fk_plan_member_plan_id foreign key (plan_id) references plan (id); + +CREATE FULLTEXT INDEX idx_member_nickname ON `member` (nickname) with parser ngram; diff --git a/backend/src/test/java/com/twtw/backend/BackendApplicationTests.java b/backend/src/test/java/com/twtw/backend/BackendApplicationTests.java index 72667578..3c7f5bbd 100644 --- a/backend/src/test/java/com/twtw/backend/BackendApplicationTests.java +++ b/backend/src/test/java/com/twtw/backend/BackendApplicationTests.java @@ -1,13 +1,13 @@ package com.twtw.backend; -import com.twtw.backend.support.testcontainer.ContainerTest; +import com.twtw.backend.support.testcontainer.ContainerTestConfig; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; @SpringBootTest -@ContextConfiguration(initializers = ContainerTest.class) +@ContextConfiguration(initializers = ContainerTestConfig.class) class BackendApplicationTests { @Test diff --git a/backend/src/test/java/com/twtw/backend/domain/friend/repository/FriendCommandRepositoryTest.java b/backend/src/test/java/com/twtw/backend/domain/friend/repository/FriendCommandRepositoryTest.java new file mode 100644 index 00000000..78c1d648 --- /dev/null +++ b/backend/src/test/java/com/twtw/backend/domain/friend/repository/FriendCommandRepositoryTest.java @@ -0,0 +1,73 @@ +package com.twtw.backend.domain.friend.repository; + +import com.twtw.backend.domain.friend.entity.Friend; +import com.twtw.backend.domain.friend.entity.FriendStatus; +import com.twtw.backend.domain.member.entity.AuthType; +import com.twtw.backend.domain.member.entity.Member; +import com.twtw.backend.domain.member.entity.OAuth2Info; +import com.twtw.backend.domain.member.repository.MemberRepository; +import com.twtw.backend.support.repository.RepositoryTest; +import com.twtw.backend.utils.QueryParseUtils; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("FriendCommandRepository에") +class FriendCommandRepositoryTest extends RepositoryTest { + + @Autowired private FriendCommandRepository friendCommandRepository; + @Autowired private MemberRepository memberRepository; + + @Test + @DisplayName("저장이 수행되는가") + void save() { + // given + final Member from = + memberRepository.save( + new Member("1", "123", new OAuth2Info("321", AuthType.APPLE), "test")); + final Member to = + memberRepository.save( + new Member( + "2", "1234", new OAuth2Info("123", AuthType.KAKAO), "deviceToken")); + final Friend friend = new Friend(from, to); + + // when + final UUID friendId = friendCommandRepository.save(friend).getId(); + + // then + assertThat(friendId).isNotNull(); + } + + @Test + @DisplayName("멤버와 닉네임을 통한 검색이 수행되는가") + void findByMemberAndMemberNickname() { + // given + final Member from = + memberRepository.save( + new Member( + "144", "123", new OAuth2Info("321", AuthType.APPLE), "deviceToken1")); + final String toMemberNickname = "277123"; + final Member to = + memberRepository.save( + new Member( + toMemberNickname, + "1234", + new OAuth2Info("123", AuthType.KAKAO), + "deviceToken2")); + final Friend friend = new Friend(from, to); + friendCommandRepository.save(friend); + friend.updateStatus(FriendStatus.ACCEPTED); + + // when + final List result = + friendCommandRepository.findByMemberAndMemberNickname(from.getId(), QueryParseUtils.parse(toMemberNickname.substring(2, 5))); + + // then +// assertThat(result).isNotEmpty(); + } +} diff --git a/backend/src/test/java/com/twtw/backend/domain/friend/repository/FriendRepositoryTest.java b/backend/src/test/java/com/twtw/backend/domain/friend/repository/FriendQueryRepositoryImplTest.java similarity index 53% rename from backend/src/test/java/com/twtw/backend/domain/friend/repository/FriendRepositoryTest.java rename to backend/src/test/java/com/twtw/backend/domain/friend/repository/FriendQueryRepositoryImplTest.java index ae35c795..432664a2 100644 --- a/backend/src/test/java/com/twtw/backend/domain/friend/repository/FriendRepositoryTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/friend/repository/FriendQueryRepositoryImplTest.java @@ -1,7 +1,5 @@ package com.twtw.backend.domain.friend.repository; -import static org.assertj.core.api.Assertions.assertThat; - import com.twtw.backend.domain.friend.entity.Friend; import com.twtw.backend.domain.friend.entity.FriendStatus; import com.twtw.backend.domain.member.entity.AuthType; @@ -9,39 +7,23 @@ import com.twtw.backend.domain.member.entity.OAuth2Info; import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.support.repository.RepositoryTest; - +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; import java.util.List; -import java.util.UUID; - -@DisplayName("FriendRepository에") -class FriendRepositoryTest extends RepositoryTest { - - @Autowired private FriendRepository friendRepository; - @Autowired private MemberRepository memberRepository; - @Test - @DisplayName("저장이 수행되는가") - void save() { - // given - final Member from = - memberRepository.save( - new Member("1", "123", new OAuth2Info("321", AuthType.APPLE), "test")); - final Member to = - memberRepository.save( - new Member( - "2", "1234", new OAuth2Info("123", AuthType.KAKAO), "deviceToken")); - final Friend friend = new Friend(from, to); +import static org.assertj.core.api.Assertions.assertThat; - // when - final UUID friendId = friendRepository.save(friend).getId(); +@Import(FriendQueryRepositoryImpl.class) +@DisplayName("FriendQueryRepository에") +class FriendQueryRepositoryImplTest extends RepositoryTest { - // then - assertThat(friendId).isNotNull(); - } + @Autowired private EntityManager em; + @Autowired private FriendQueryRepositoryImpl friendQueryRepositoryImpl; + @Autowired private MemberRepository memberRepository; @Test @DisplayName("두 멤버 pk로 조회가 수행되는가") @@ -59,14 +41,14 @@ void findByTwoMemberId() { new OAuth2Info("123", AuthType.KAKAO), "deviceToken2")); final Friend friend = new Friend(from, to); - final Friend expected = friendRepository.save(friend); + em.persist(friend); // when final Friend result = - friendRepository.findByTwoMemberId(from.getId(), to.getId()).orElseThrow(); + friendQueryRepositoryImpl.findByTwoMemberId(from.getId(), to.getId()).orElseThrow(); // then - assertThat(result.getId()).isEqualTo(expected.getId()); + assertThat(result.getId()).isEqualTo(friend.getId()); } @Test @@ -85,11 +67,11 @@ void findByMember() { new OAuth2Info("123", AuthType.KAKAO), "deviceToken2")); final Friend friend = new Friend(from, to); - final Friend expected = friendRepository.save(friend); - expected.updateStatus(FriendStatus.ACCEPTED); + em.persist(friend); + friend.updateStatus(FriendStatus.ACCEPTED); // when - final List result = friendRepository.findByMember(from); + final List result = friendQueryRepositoryImpl.findByMember(from); // then assertThat(result).hasSize(1); @@ -102,48 +84,20 @@ void findByMemberAndFriendStatus() { final Member from = memberRepository.save( new Member( - "1", "123", new OAuth2Info("321", AuthType.APPLE), "deviceToken1")); - final Member to = - memberRepository.save( - new Member( - "2", - "1234", - new OAuth2Info("123", AuthType.KAKAO), - "deviceToken2")); - final Friend friend = new Friend(from, to); - friendRepository.save(friend); - - // when - final List result = - friendRepository.findByMemberAndFriendStatus(from, friend.getFriendStatus()); - - // then - assertThat(result).hasSize(1); - } - - @Test - @DisplayName("멤버와 닉네임을 통한 검색이 수행되는가") - void findByMemberAndMemberNickname() { - // given - final Member from = - memberRepository.save( - new Member( - "1", "123", new OAuth2Info("321", AuthType.APPLE), "deviceToken1")); - final String friendNickname = "2"; + "14312", "123", new OAuth2Info("321", AuthType.APPLE), "deviceToken1")); final Member to = memberRepository.save( new Member( - friendNickname, + "24312", "1234", new OAuth2Info("123", AuthType.KAKAO), "deviceToken2")); final Friend friend = new Friend(from, to); - final Friend expected = friendRepository.save(friend); - expected.updateStatus(FriendStatus.ACCEPTED); + em.persist(friend); // when final List result = - friendRepository.findByMemberAndMemberNickname(from, friendNickname); + friendQueryRepositoryImpl.findByMemberAndFriendStatus(from, friend.getFriendStatus()); // then assertThat(result).hasSize(1); diff --git a/backend/src/test/java/com/twtw/backend/domain/friend/service/FriendServiceTest.java b/backend/src/test/java/com/twtw/backend/domain/friend/service/FriendServiceTest.java index 1c58a717..cc92b7cd 100644 --- a/backend/src/test/java/com/twtw/backend/domain/friend/service/FriendServiceTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/friend/service/FriendServiceTest.java @@ -1,31 +1,36 @@ package com.twtw.backend.domain.friend.service; -import static org.assertj.core.api.Assertions.assertThat; - import com.twtw.backend.domain.friend.dto.request.FriendRequest; import com.twtw.backend.domain.friend.dto.request.FriendUpdateRequest; import com.twtw.backend.domain.friend.dto.response.FriendResponse; import com.twtw.backend.domain.friend.entity.Friend; import com.twtw.backend.domain.friend.entity.FriendStatus; -import com.twtw.backend.domain.friend.repository.FriendRepository; +import com.twtw.backend.domain.friend.repository.FriendCommandRepository; +import com.twtw.backend.domain.friend.repository.FriendQueryRepository; import com.twtw.backend.domain.member.entity.AuthType; import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.domain.member.entity.OAuth2Info; +import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.support.service.LoginTest; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.beans.factory.annotation.Qualifier; import java.util.List; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; + @DisplayName("FriendService의") class FriendServiceTest extends LoginTest { @Autowired private FriendService friendService; - @Autowired private FriendRepository friendRepository; + @Autowired @Qualifier("stubFriendQueryRepository") + private FriendQueryRepository friendQueryRepository; + @Autowired @Qualifier("stubFriendCommandRepository") + private FriendCommandRepository friendCommandRepository; + @Autowired private MemberRepository memberRepository; @Test @DisplayName("요청 추가가 수행되는가") @@ -35,9 +40,9 @@ void addRequest() { memberRepository .save( new Member( - "1", + "abc123", "12", - new OAuth2Info("123", AuthType.APPLE), + new OAuth2Info("123321", AuthType.APPLE), "deviceToken")) .getId(); @@ -46,8 +51,8 @@ void addRequest() { // then final List result = - friendRepository.findByMemberAndFriendStatus(loginUser, FriendStatus.REQUESTED); - assertThat(result).hasSize(1); + friendQueryRepository.findByMemberAndFriendStatus(loginUser, FriendStatus.REQUESTED); + assertThat(result).hasSize(2); } @Test @@ -58,19 +63,18 @@ void updateStatus() { memberRepository.save( new Member( "1", "12", new OAuth2Info("123", AuthType.APPLE), "deviceToken")); - final Friend friend = friendRepository.save(new Friend(loginUser, toMember)); + final Friend friend = friendCommandRepository.save(new Friend(loginUser, toMember)); // when final FriendStatus status = FriendStatus.ACCEPTED; friendService.updateStatus(new FriendUpdateRequest(toMember.getId(), status)); // then - final Friend result = friendRepository.findById(friend.getId()).orElseThrow(); + final Friend result = friendQueryRepository.findByTwoMemberId(friend.getToMember().getId(), friend.getFromMember().getId()).orElseThrow(); assertThat(result.getFriendStatus()).isEqualTo(status); } @Test - @Transactional @DisplayName("친구 목록 조회가 수행되는가") void getFriends() { // given @@ -78,7 +82,7 @@ void getFriends() { memberRepository.save( new Member( "1", "12", new OAuth2Info("123", AuthType.APPLE), "deviceToken")); - friendRepository.save(new Friend(loginUser, toMember)); + friendCommandRepository.save(new Friend(loginUser, toMember)); friendService.updateStatus( new FriendUpdateRequest(toMember.getId(), FriendStatus.ACCEPTED)); @@ -86,7 +90,7 @@ void getFriends() { final List result = friendService.getFriends(); // then - assertThat(result).hasSize(1); + assertThat(result).isNotEmpty(); } @Test @@ -97,7 +101,7 @@ void getFriendsByStatus() { memberRepository.save( new Member( "1", "12", new OAuth2Info("123", AuthType.APPLE), "deviceToken")); - friendRepository.save(new Friend(loginUser, toMember)); + friendCommandRepository.save(new Friend(loginUser, toMember)); // when final List result = @@ -108,7 +112,6 @@ void getFriendsByStatus() { } @Test - @Transactional @DisplayName("닉네임을 통한 친구 조회가 수행되는가") void getFriendByNickname() { // given @@ -120,7 +123,7 @@ void getFriendByNickname() { "12", new OAuth2Info("123", AuthType.APPLE), "deviceToken")); - final Friend expected = friendRepository.save(new Friend(loginUser, toMember)); + final Friend expected = friendCommandRepository.save(new Friend(loginUser, toMember)); expected.updateStatus(FriendStatus.ACCEPTED); // when diff --git a/backend/src/test/java/com/twtw/backend/domain/group/repository/GroupRepositoryTest.java b/backend/src/test/java/com/twtw/backend/domain/group/repository/GroupRepositoryTest.java index 9d48a4dc..97f6112f 100644 --- a/backend/src/test/java/com/twtw/backend/domain/group/repository/GroupRepositoryTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/group/repository/GroupRepositoryTest.java @@ -1,7 +1,5 @@ package com.twtw.backend.domain.group.repository; -import static org.assertj.core.api.Assertions.assertThat; - import com.twtw.backend.domain.group.entity.Group; import com.twtw.backend.domain.group.entity.GroupMember; import com.twtw.backend.domain.member.entity.Member; @@ -12,7 +10,6 @@ import com.twtw.backend.fixture.member.MemberEntityFixture; import com.twtw.backend.fixture.place.PlaceEntityFixture; import com.twtw.backend.support.repository.RepositoryTest; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +17,8 @@ import java.time.LocalDateTime; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; + @DisplayName("GroupRepository의") class GroupRepositoryTest extends RepositoryTest { @@ -27,7 +26,6 @@ class GroupRepositoryTest extends RepositoryTest { @Autowired private MemberRepository memberRepository; - @Autowired private GroupMemberRepository groupMemberRepository; @Test @DisplayName("Group이 정상적으로 저장되는가") @@ -65,10 +63,7 @@ void getGroupById() { final Group saveGroup = groupRepository.save(group); // when - GroupMember result = - groupMemberRepository - .findByGroupIdAndMemberId(saveGroup.getId(), member1.getId()) - .orElseThrow(); + GroupMember result = groupRepository.findById(saveGroup.getId()).orElseThrow().getSameMember(member1); // then assertThat(result.getGroup().getId()).isEqualTo(saveGroup.getId()); diff --git a/backend/src/test/java/com/twtw/backend/domain/group/service/GroupServiceTest.java b/backend/src/test/java/com/twtw/backend/domain/group/service/GroupServiceTest.java index 8bc599a2..dcc87135 100644 --- a/backend/src/test/java/com/twtw/backend/domain/group/service/GroupServiceTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/group/service/GroupServiceTest.java @@ -1,7 +1,5 @@ package com.twtw.backend.domain.group.service; -import static org.assertj.core.api.Assertions.assertThat; - import com.twtw.backend.domain.group.dto.request.InviteGroupRequest; import com.twtw.backend.domain.group.dto.request.JoinGroupRequest; import com.twtw.backend.domain.group.dto.request.MakeGroupRequest; @@ -9,12 +7,11 @@ import com.twtw.backend.domain.group.dto.response.GroupResponse; import com.twtw.backend.domain.group.entity.Group; import com.twtw.backend.domain.group.entity.GroupMember; -import com.twtw.backend.domain.group.repository.GroupMemberRepository; import com.twtw.backend.domain.group.repository.GroupRepository; import com.twtw.backend.domain.member.entity.Member; +import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.fixture.member.MemberEntityFixture; import com.twtw.backend.support.service.LoginTest; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -22,13 +19,14 @@ import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; + @DisplayName("GroupService의") class GroupServiceTest extends LoginTest { - @Autowired private GroupService groupService; + @Autowired private GroupService groupService; @Autowired private GroupRepository groupRepository; - - @Autowired private GroupMemberRepository groupMemberRepository; + @Autowired private MemberRepository memberRepository; @Test @DisplayName("makeGroup이 성공적으로 수행되는가") @@ -102,10 +100,8 @@ void changeShare() { // when groupService.unShareLocation(saveGroup.getId()); - GroupMember result = - groupMemberRepository - .findByGroupIdAndMemberId(saveGroup.getId(), loginUser.getId()) - .orElseThrow(); + + final GroupMember result = groupRepository.findById(saveGroup.getId()).orElseThrow().getSameMember(loginUser); // then assertThat(result.getIsShare()).isFalse(); diff --git a/backend/src/test/java/com/twtw/backend/domain/member/repository/MemberRepositoryTest.java b/backend/src/test/java/com/twtw/backend/domain/member/repository/MemberRepositoryTest.java index 3f1ae18b..8d208e3b 100644 --- a/backend/src/test/java/com/twtw/backend/domain/member/repository/MemberRepositoryTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/member/repository/MemberRepositoryTest.java @@ -1,28 +1,23 @@ package com.twtw.backend.domain.member.repository; -import static org.assertj.core.api.Assertions.assertThat; - import com.twtw.backend.domain.member.dto.request.DeviceTokenRequest; import com.twtw.backend.domain.member.entity.DeviceToken; import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.fixture.member.MemberEntityFixture; import com.twtw.backend.support.repository.RepositoryTest; - -import jakarta.persistence.EntityManager; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; + @DisplayName("MemberRepository의") class MemberRepositoryTest extends RepositoryTest { @Autowired private MemberRepository memberRepository; - @Autowired private EntityManager em; - @Test @DisplayName("PK를 통한 저장/조회가 성공하는가?") void saveAndFindId() { diff --git a/backend/src/test/java/com/twtw/backend/domain/member/service/AuthServiceTest.java b/backend/src/test/java/com/twtw/backend/domain/member/service/AuthServiceTest.java index ce6441b2..cbdd6b17 100644 --- a/backend/src/test/java/com/twtw/backend/domain/member/service/AuthServiceTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/member/service/AuthServiceTest.java @@ -1,7 +1,5 @@ package com.twtw.backend.domain.member.service; -import static org.assertj.core.api.Assertions.assertThat; - import com.twtw.backend.domain.member.dto.request.MemberSaveRequest; import com.twtw.backend.domain.member.dto.request.OAuthRequest; import com.twtw.backend.domain.member.dto.response.AfterLoginResponse; @@ -10,19 +8,22 @@ import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.fixture.member.MemberEntityFixture; -import com.twtw.backend.support.database.DatabaseTest; -import com.twtw.backend.support.exclude.ExcludeTest; - +import com.twtw.backend.support.service.ServiceTest; +import com.twtw.backend.support.stub.StubConfig; +import com.twtw.backend.support.testcontainer.ContainerTestConfig; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; -@DatabaseTest -@DisplayName("AuthService의 ") -class AuthServiceTest extends ExcludeTest { +import static org.assertj.core.api.Assertions.assertThat; - @Autowired private AuthService authService; +@ServiceTest +@ContextConfiguration(initializers = {ContainerTestConfig.class}, classes = StubConfig.class) +@DisplayName("AuthService의") +class AuthServiceTest { + @Autowired private AuthService authService; @Autowired private MemberRepository memberRepository; @Test @@ -31,7 +32,7 @@ void saveMemberKakao() { // given MemberSaveRequest kakaoRequest = new MemberSaveRequest( - "JinJooWon_Kakao", + "Kakao", "TEST_PROFILE_IMAGE", "deviceToken", new OAuthRequest("TEST_KAKAO_TOKEN", AuthType.KAKAO)); @@ -49,7 +50,7 @@ void saveMemberApple() { // given MemberSaveRequest appleRequest = new MemberSaveRequest( - "JinJooWon_Apple", + "Apple", "TEST_PROFILE_IMAGE", "deviceToken", new OAuthRequest("TEST_APPLE_TOKEN", AuthType.APPLE)); diff --git a/backend/src/test/java/com/twtw/backend/domain/member/service/MemberServiceTest.java b/backend/src/test/java/com/twtw/backend/domain/member/service/MemberServiceTest.java index 7efc77cd..0068acca 100644 --- a/backend/src/test/java/com/twtw/backend/domain/member/service/MemberServiceTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/member/service/MemberServiceTest.java @@ -1,21 +1,20 @@ package com.twtw.backend.domain.member.service; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertTrue; - import com.twtw.backend.domain.member.dto.response.DuplicateNicknameResponse; import com.twtw.backend.domain.member.dto.response.MemberResponse; import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.fixture.member.MemberEntityFixture; import com.twtw.backend.support.service.LoginTest; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + @DisplayName("MemberService의") class MemberServiceTest extends LoginTest { @Autowired private MemberService memberService; @@ -66,9 +65,9 @@ void searchMemberByNickname() { // when final List responses = - memberService.getMemberByNickname(member.getNickname()); + memberService.getMemberByNickname(member.getNickname().substring(0, 1)); // then - assertThat(responses.get(0).getMemberId()).isEqualTo(member.getId()); + assertThat(responses).isNotEmpty(); } } diff --git a/backend/src/test/java/com/twtw/backend/domain/path/service/PathServiceTest.java b/backend/src/test/java/com/twtw/backend/domain/path/service/PathServiceTest.java index 04478feb..6f4a718b 100644 --- a/backend/src/test/java/com/twtw/backend/domain/path/service/PathServiceTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/path/service/PathServiceTest.java @@ -11,7 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired; @DisplayName("PathService의") -public class PathServiceTest extends LoginTest { +class PathServiceTest extends LoginTest { @Autowired private PathService pathService; @Test diff --git a/backend/src/test/java/com/twtw/backend/domain/plan/repository/PlanRepositoryTest.java b/backend/src/test/java/com/twtw/backend/domain/plan/repository/PlanRepositoryTest.java index 2401b60f..0fb0743a 100644 --- a/backend/src/test/java/com/twtw/backend/domain/plan/repository/PlanRepositoryTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/plan/repository/PlanRepositoryTest.java @@ -1,8 +1,7 @@ package com.twtw.backend.domain.plan.repository; -import static org.assertj.core.api.Assertions.assertThat; - import com.twtw.backend.domain.group.entity.Group; +import com.twtw.backend.domain.group.repository.GroupRepository; import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.domain.place.entity.Place; @@ -11,9 +10,6 @@ import com.twtw.backend.fixture.member.MemberEntityFixture; import com.twtw.backend.fixture.plan.PlanEntityFixture; import com.twtw.backend.support.repository.RepositoryTest; - -import jakarta.persistence.EntityManager; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -22,15 +18,15 @@ import java.util.List; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; + @DisplayName("PlanRepository의") class PlanRepositoryTest extends RepositoryTest { @Autowired private PlanRepository planRepository; - + @Autowired private GroupRepository groupRepository; @Autowired private MemberRepository memberRepository; - @Autowired private EntityManager em; - @Test @DisplayName("PK를 통한 조회가 수행되는가") void saveAndFindById() { @@ -57,7 +53,6 @@ void softDelete() { final Place place = Place.builder().longitude(1.1).latitude(2.2).placeName("스타벅스").build(); final Member member = memberRepository.save(MemberEntityFixture.LOGIN_MEMBER.toEntity()); - em.persist(place); final Group group = new Group("그룹", "http://abcdefg", member); @@ -74,8 +69,6 @@ void softDelete() { // when planRepository.deleteById(planId); - em.flush(); - em.clear(); // then assertThat(planRepository.findById(planId)).isEmpty(); @@ -89,8 +82,6 @@ void findAllPlanByMember() { Place.builder().longitude(1.1).latitude(2.2).placeName("스타벅스").build(); final Place secondPlace = Place.builder().longitude(1.1).latitude(2.2).placeName("star").build(); - em.persist(firstPlace); - em.persist(secondPlace); final Member member = memberRepository.save(MemberEntityFixture.LOGIN_MEMBER.toEntity()); final Member firstMember = diff --git a/backend/src/test/java/com/twtw/backend/domain/plan/service/PlanServiceTest.java b/backend/src/test/java/com/twtw/backend/domain/plan/service/PlanServiceTest.java index 96fe9db1..6b07bc0d 100644 --- a/backend/src/test/java/com/twtw/backend/domain/plan/service/PlanServiceTest.java +++ b/backend/src/test/java/com/twtw/backend/domain/plan/service/PlanServiceTest.java @@ -1,9 +1,8 @@ package com.twtw.backend.domain.plan.service; -import static org.assertj.core.api.Assertions.assertThat; - import com.twtw.backend.domain.group.repository.GroupRepository; import com.twtw.backend.domain.member.entity.Member; +import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.domain.place.entity.CategoryGroupCode; import com.twtw.backend.domain.plan.dto.client.SearchDestinationRequest; import com.twtw.backend.domain.plan.dto.request.PlanMemberRequest; @@ -19,7 +18,6 @@ import com.twtw.backend.fixture.place.PlaceEntityFixture; import com.twtw.backend.fixture.plan.PlanEntityFixture; import com.twtw.backend.support.service.LoginTest; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -30,12 +28,15 @@ import java.util.Optional; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; + @DisplayName("PlanService의") class PlanServiceTest extends LoginTest { @Autowired private PlanService planService; @Autowired private GroupRepository groupRepository; @Autowired private PlanRepository planRepository; + @Autowired private MemberRepository memberRepository; @Test @DisplayName("목적지 검색이 수행되는가") @@ -96,7 +97,7 @@ void joinPlan() { // then final Plan result = planRepository.findById(planId).orElseThrow(); - assertThat(result.getPlanMembers()).hasSize(1); + assertThat(result.getPlanMembers()).hasSize(2); } @Test diff --git a/backend/src/test/java/com/twtw/backend/support/database/DatabaseTest.java b/backend/src/test/java/com/twtw/backend/support/database/DatabaseTest.java index 35deb80f..a4980a4f 100644 --- a/backend/src/test/java/com/twtw/backend/support/database/DatabaseTest.java +++ b/backend/src/test/java/com/twtw/backend/support/database/DatabaseTest.java @@ -1,17 +1,14 @@ package com.twtw.backend.support.database; import com.twtw.backend.config.database.QuerydslConfig; - import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@ActiveProfiles("test") @Target(ElementType.TYPE) @Import(QuerydslConfig.class) @Retention(RetentionPolicy.RUNTIME) diff --git a/backend/src/test/java/com/twtw/backend/support/exclude/ExcludeTest.java b/backend/src/test/java/com/twtw/backend/support/exclude/ExcludeTest.java index e4bed6e2..8046aca7 100644 --- a/backend/src/test/java/com/twtw/backend/support/exclude/ExcludeTest.java +++ b/backend/src/test/java/com/twtw/backend/support/exclude/ExcludeTest.java @@ -1,28 +1,21 @@ package com.twtw.backend.support.exclude; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; - import com.twtw.backend.domain.notification.messagequeue.FcmProducer; -import com.twtw.backend.support.database.ResetDatabase; -import com.twtw.backend.support.testcontainer.ContainerTest; - +import com.twtw.backend.support.testcontainer.ContainerTestConfig; import org.junit.jupiter.api.BeforeEach; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; import org.springframework.test.context.ContextConfiguration; -@Import(ResetDatabase.class) -@ContextConfiguration(initializers = ContainerTest.class) +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; + +@ContextConfiguration(initializers = ContainerTestConfig.class) public abstract class ExcludeTest { @MockBean private FcmProducer fcmProducer; - @Autowired private ResetDatabase resetDatabase; @BeforeEach void setUp() { doNothing().when(fcmProducer).sendNotification(any()); - resetDatabase.reset(); } } diff --git a/backend/src/test/java/com/twtw/backend/support/repository/EnableDataJpa.java b/backend/src/test/java/com/twtw/backend/support/repository/EnableDataJpa.java index b775ef3b..b928a5f4 100644 --- a/backend/src/test/java/com/twtw/backend/support/repository/EnableDataJpa.java +++ b/backend/src/test/java/com/twtw/backend/support/repository/EnableDataJpa.java @@ -1,10 +1,12 @@ package com.twtw.backend.support.repository; import com.twtw.backend.config.database.QuerydslConfig; - +import com.twtw.backend.support.database.ResetDatabase; +import com.twtw.backend.support.testcontainer.ContainerTestConfig; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ContextConfiguration; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -13,7 +15,8 @@ @DataJpaTest @Target(ElementType.TYPE) -@Import(QuerydslConfig.class) +@Import({ResetDatabase.class, QuerydslConfig.class}) @Retention(RetentionPolicy.RUNTIME) +@ContextConfiguration(initializers = {ContainerTestConfig.class}) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) public @interface EnableDataJpa {} diff --git a/backend/src/test/java/com/twtw/backend/support/repository/RepositoryTest.java b/backend/src/test/java/com/twtw/backend/support/repository/RepositoryTest.java index 6d2275a0..4fea96f3 100644 --- a/backend/src/test/java/com/twtw/backend/support/repository/RepositoryTest.java +++ b/backend/src/test/java/com/twtw/backend/support/repository/RepositoryTest.java @@ -1,13 +1,10 @@ package com.twtw.backend.support.repository; import com.twtw.backend.support.database.ResetDatabase; - import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; @EnableDataJpa -@Import(ResetDatabase.class) public abstract class RepositoryTest { @Autowired private ResetDatabase resetDatabase; diff --git a/backend/src/test/java/com/twtw/backend/support/service/LoginTest.java b/backend/src/test/java/com/twtw/backend/support/service/LoginTest.java index b32bc8db..a954ef71 100644 --- a/backend/src/test/java/com/twtw/backend/support/service/LoginTest.java +++ b/backend/src/test/java/com/twtw/backend/support/service/LoginTest.java @@ -1,47 +1,35 @@ package com.twtw.backend.support.service; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; - import com.twtw.backend.domain.member.entity.Member; import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.domain.member.service.AuthService; import com.twtw.backend.domain.notification.messagequeue.FcmProducer; import com.twtw.backend.fixture.member.MemberEntityFixture; -import com.twtw.backend.support.database.DatabaseTest; -import com.twtw.backend.support.database.ResetDatabase; -import com.twtw.backend.support.testcontainer.ContainerTest; - +import com.twtw.backend.support.stub.StubConfig; +import com.twtw.backend.support.testcontainer.ContainerTestConfig; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; import org.springframework.test.context.ContextConfiguration; -@DatabaseTest -@Import(ResetDatabase.class) -@ContextConfiguration(initializers = ContainerTest.class) +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + +@ServiceTest +@ContextConfiguration(initializers = {ContainerTestConfig.class}, classes = StubConfig.class) public abstract class LoginTest { @MockBean protected AuthService authService; - @MockBean protected FcmProducer fcmProducer; - - @Autowired protected MemberRepository memberRepository; - @Autowired private ResetDatabase resetDatabase; + @MockBean private FcmProducer fcmProducer; + @Autowired private MemberRepository memberRepository; protected Member loginUser; @BeforeEach public void setup() { - resetDatabase.reset(); - final Member member = MemberEntityFixture.LOGIN_MEMBER.toEntity(); - loginUser = memberRepository.save(member); + loginUser = memberRepository.save(MemberEntityFixture.LOGIN_MEMBER.toEntity()); when(authService.getMemberByJwt()).thenReturn(loginUser); - when(authService.getMemberIdValue()).thenReturn("123123123.123123123.123123123"); - } - - @BeforeEach - void setUp() { + when(authService.getMemberIdValue()).thenReturn(loginUser.getId().toString()); doNothing().when(fcmProducer).sendNotification(any()); } } diff --git a/backend/src/test/java/com/twtw/backend/support/service/ServiceTest.java b/backend/src/test/java/com/twtw/backend/support/service/ServiceTest.java new file mode 100644 index 00000000..500b2d74 --- /dev/null +++ b/backend/src/test/java/com/twtw/backend/support/service/ServiceTest.java @@ -0,0 +1,13 @@ +package com.twtw.backend.support.service; + +import org.springframework.boot.test.context.SpringBootTest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@SpringBootTest +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ServiceTest {} diff --git a/backend/src/test/java/com/twtw/backend/support/stub/StubConfig.java b/backend/src/test/java/com/twtw/backend/support/stub/StubConfig.java new file mode 100644 index 00000000..1d766b0f --- /dev/null +++ b/backend/src/test/java/com/twtw/backend/support/stub/StubConfig.java @@ -0,0 +1,58 @@ +package com.twtw.backend.support.stub; + +import com.twtw.backend.domain.friend.entity.Friend; +import com.twtw.backend.domain.friend.repository.FriendCommandRepository; +import com.twtw.backend.domain.friend.repository.FriendQueryRepository; +import com.twtw.backend.domain.group.repository.GroupRepository; +import com.twtw.backend.domain.member.repository.MemberRepository; +import com.twtw.backend.domain.member.repository.RefreshTokenRepository; +import com.twtw.backend.domain.plan.repository.PlanRepository; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@TestConfiguration +public class StubConfig { + + private final Map map = new HashMap<>(); + + @Bean + @Primary + public FriendQueryRepository stubFriendQueryRepository() { + return new StubFriendQueryRepository(map); + } + + @Bean + @Primary + public FriendCommandRepository stubFriendCommandRepository() { + return new StubFriendCommandRepository(map); + } + + @Bean + @Primary + public RefreshTokenRepository refreshTokenRepository() { + return new StubRefreshTokenRepository(); + } + + @Bean + @Primary + public GroupRepository groupRepository() { + return new StubGroupRepository(); + } + + @Bean + @Primary + public MemberRepository memberRepository() { + return new StubMemberRepository(); + } + + @Bean + @Primary + public PlanRepository planRepository() { + return new StubPlanRepository(); + } +} diff --git a/backend/src/test/java/com/twtw/backend/support/stub/StubFriendCommandRepository.java b/backend/src/test/java/com/twtw/backend/support/stub/StubFriendCommandRepository.java new file mode 100644 index 00000000..4d40ceed --- /dev/null +++ b/backend/src/test/java/com/twtw/backend/support/stub/StubFriendCommandRepository.java @@ -0,0 +1,28 @@ +package com.twtw.backend.support.stub; + +import com.twtw.backend.domain.friend.entity.Friend; +import com.twtw.backend.domain.friend.repository.FriendCommandRepository; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@RequiredArgsConstructor +public class StubFriendCommandRepository implements FriendCommandRepository { + + private final Map map; + + @Override + public List findByMemberAndMemberNickname(final UUID memberId, final String nickname) { + return map.values().stream() + .filter(friend -> (friend.getToMember().getId().equals(memberId) || friend.getFromMember().getId().equals(memberId)) && friend.getToMember().getNickname().toUpperCase().contains(nickname.toUpperCase())) + .toList(); + } + + @Override + public Friend save(final Friend friend) { + map.put(friend.getId(), friend); + return friend; + } +} diff --git a/backend/src/test/java/com/twtw/backend/support/stub/StubFriendQueryRepository.java b/backend/src/test/java/com/twtw/backend/support/stub/StubFriendQueryRepository.java new file mode 100644 index 00000000..661d1e41 --- /dev/null +++ b/backend/src/test/java/com/twtw/backend/support/stub/StubFriendQueryRepository.java @@ -0,0 +1,46 @@ +package com.twtw.backend.support.stub; + +import com.twtw.backend.domain.friend.entity.Friend; +import com.twtw.backend.domain.friend.entity.FriendStatus; +import com.twtw.backend.domain.friend.repository.FriendQueryRepository; +import com.twtw.backend.domain.member.entity.Member; +import lombok.RequiredArgsConstructor; + +import java.util.*; + +@RequiredArgsConstructor +public class StubFriendQueryRepository implements FriendQueryRepository { + + private final Map map; + + @Override + public Optional findByTwoMemberId(final UUID loginMemberId, final UUID memberId) { + return map.values().stream() + .filter(friend -> + (friend.getToMember().getId().equals(loginMemberId) && friend.getFromMember().getId().equals(memberId)) + || (friend.getFromMember().getId().equals(loginMemberId) && friend.getToMember().getId().equals(memberId))) + .findFirst(); + } + + @Override + public List findByMember(final Member member) { + return map.values().stream() + .filter(friend -> friend.getToMember().equals(member) || friend.getFromMember().equals(member)) + .toList(); + } + + @Override + public List findByMemberAndFriendStatus(final Member member, final FriendStatus friendStatus) { + return map.values().stream() + .filter(friend -> (friend.getToMember().equals(member) || friend.getFromMember().equals(member)) && friend.getFriendStatus().equals(friendStatus)) + .toList(); + } + + @Override + public List findByMemberAndMemberNicknameContaining(final UUID memberId, final String nickname) { + return map.values().stream() + .filter(friend -> friend.getFriendStatus() == FriendStatus.ACCEPTED && ((friend.getToMember().getId().equals(memberId) && friend.getFromMember().getNickname().contains(nickname)) + || (friend.getFromMember().getId().equals(memberId) && friend.getToMember().getNickname().contains(nickname)))) + .toList(); + } +} diff --git a/backend/src/test/java/com/twtw/backend/support/stub/StubGroupRepository.java b/backend/src/test/java/com/twtw/backend/support/stub/StubGroupRepository.java new file mode 100644 index 00000000..56ad71b4 --- /dev/null +++ b/backend/src/test/java/com/twtw/backend/support/stub/StubGroupRepository.java @@ -0,0 +1,25 @@ +package com.twtw.backend.support.stub; + +import com.twtw.backend.domain.group.entity.Group; +import com.twtw.backend.domain.group.repository.GroupRepository; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +public class StubGroupRepository implements GroupRepository { + + private final Map map = new HashMap<>(); + + @Override + public Optional findById(final UUID groupId) { + return Optional.ofNullable(map.get(groupId)); + } + + @Override + public Group save(final Group group) { + map.put(group.getId(), group); + return group; + } +} diff --git a/backend/src/test/java/com/twtw/backend/support/stub/StubMemberRepository.java b/backend/src/test/java/com/twtw/backend/support/stub/StubMemberRepository.java new file mode 100644 index 00000000..9b605dd3 --- /dev/null +++ b/backend/src/test/java/com/twtw/backend/support/stub/StubMemberRepository.java @@ -0,0 +1,59 @@ +package com.twtw.backend.support.stub; + +import com.twtw.backend.domain.member.entity.AuthType; +import com.twtw.backend.domain.member.entity.Member; +import com.twtw.backend.domain.member.entity.OAuth2Info; +import com.twtw.backend.domain.member.repository.MemberRepository; + +import java.util.*; + +public class StubMemberRepository implements MemberRepository { + + private final Map map = new HashMap<>(); + + @Override + public List findAllByNickname(final String nickname) { + return map.values().stream().filter(member -> member.getNickname().toUpperCase().contains(nickname.toUpperCase())).toList(); + } + + @Override + public List findAllByNicknameContainingIgnoreCase(final String nickname) { + return map.values().stream().filter(member -> member.getNickname().toUpperCase().contains(nickname.toUpperCase())).toList(); + } + + @Override + public Optional findByOAuthIdAndAuthType(final String oAuthId, final AuthType authType) { + return map.values().stream() + .filter(member -> { + final OAuth2Info oauthInfo = member.getOauthInfo(); + return oauthInfo.getClientId().equals(oAuthId) && oauthInfo.getAuthType().equals(authType); + }) + .findFirst(); + } + + @Override + public boolean existsByNickname(final String nickname) { + return map.values().stream().anyMatch(member -> member.getNickname().equals(nickname)); + } + + @Override + public Member save(final Member member) { + map.put(member.getId(), member); + return member; + } + + @Override + public Optional findById(final UUID id) { + return Optional.ofNullable(map.get(id)); + } + + @Override + public List findAllByIds(final List friendMemberIds) { + return map.values().stream().filter(member -> friendMemberIds.contains(member.getId())).toList(); + } + + @Override + public void deleteById(final UUID memberId) { + map.remove(memberId); + } +} diff --git a/backend/src/test/java/com/twtw/backend/support/stub/StubPlanRepository.java b/backend/src/test/java/com/twtw/backend/support/stub/StubPlanRepository.java new file mode 100644 index 00000000..2970f07d --- /dev/null +++ b/backend/src/test/java/com/twtw/backend/support/stub/StubPlanRepository.java @@ -0,0 +1,35 @@ +package com.twtw.backend.support.stub; + +import com.twtw.backend.domain.member.entity.Member; +import com.twtw.backend.domain.plan.entity.Plan; +import com.twtw.backend.domain.plan.repository.PlanRepository; + +import java.util.*; + +public class StubPlanRepository implements PlanRepository { + + private final Map map = new HashMap<>(); + + @Override + public List findAllByMember(final Member member) { + return map.values().stream() + .filter(plan -> plan.getPlanMembers().stream().anyMatch(planMember -> planMember.isSameMember(member))) + .toList(); + } + + @Override + public Plan save(final Plan plan) { + map.put(plan.getId(), plan); + return plan; + } + + @Override + public void deleteById(final UUID id) { + map.remove(id); + } + + @Override + public Optional findById(final UUID id) { + return Optional.ofNullable(map.get(id)); + } +} diff --git a/backend/src/test/java/com/twtw/backend/support/stub/StubRefreshTokenRepository.java b/backend/src/test/java/com/twtw/backend/support/stub/StubRefreshTokenRepository.java new file mode 100644 index 00000000..45eee6b8 --- /dev/null +++ b/backend/src/test/java/com/twtw/backend/support/stub/StubRefreshTokenRepository.java @@ -0,0 +1,25 @@ +package com.twtw.backend.support.stub; + +import com.twtw.backend.domain.member.entity.RefreshToken; +import com.twtw.backend.domain.member.repository.RefreshTokenRepository; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +public class StubRefreshTokenRepository implements RefreshTokenRepository { + + private final Map map = new HashMap<>(); + + @Override + public Optional findByTokenKey(final String tokenKey) { + return map.values().stream().filter(refreshToken -> refreshToken.getTokenKey().equals(tokenKey)).findFirst(); + } + + @Override + public RefreshToken save(final RefreshToken refreshToken) { + map.put(refreshToken.getId(), refreshToken); + return refreshToken; + } +} diff --git a/backend/src/test/java/com/twtw/backend/support/testcontainer/ContainerTest.java b/backend/src/test/java/com/twtw/backend/support/testcontainer/ContainerTestConfig.java similarity index 60% rename from backend/src/test/java/com/twtw/backend/support/testcontainer/ContainerTest.java rename to backend/src/test/java/com/twtw/backend/support/testcontainer/ContainerTestConfig.java index a3060a5d..b64c1778 100644 --- a/backend/src/test/java/com/twtw/backend/support/testcontainer/ContainerTest.java +++ b/backend/src/test/java/com/twtw/backend/support/testcontainer/ContainerTestConfig.java @@ -1,15 +1,15 @@ package com.twtw.backend.support.testcontainer; import com.redis.testcontainers.RedisContainer; - import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.RabbitMQContainer; import org.testcontainers.utility.DockerImageName; -public class ContainerTest - implements ApplicationContextInitializer { +public class ContainerTestConfig implements ApplicationContextInitializer { + private static final RedisContainer REDIS_CONTAINER = new RedisContainer(DockerImageName.parse("redis:latest")).withExposedPorts(6379); @@ -18,21 +18,40 @@ public class ContainerTest .withExposedPorts(15672, 5672, 61613) .withPluginsEnabled("rabbitmq_web_stomp"); + private static final MySQLContainer MYSQL_CONTAINER = + new MySQLContainer(DockerImageName.parse("mysql:8.0.22")) + .withDatabaseName("TWTW") + .withUsername("admin") + .withPassword("1234"); + static { REDIS_CONTAINER.start(); RABBIT_MQ_CONTAINER.start(); + MYSQL_CONTAINER.start(); } @Override public void initialize(final ConfigurableApplicationContext applicationContext) { + final String jdbcUrl = MYSQL_CONTAINER.getJdbcUrl(); + final String username = MYSQL_CONTAINER.getUsername(); + final String password = MYSQL_CONTAINER.getPassword(); + TestPropertyValues.of( "spring.data.redis.host=" + REDIS_CONTAINER.getHost(), "spring.data.redis.port=" + REDIS_CONTAINER.getFirstMappedPort(), + "spring.rabbitmq.host=" + RABBIT_MQ_CONTAINER.getHost(), "spring.rabbitmq.port=" + RABBIT_MQ_CONTAINER.getMappedPort(5672), "spring.rabbitmq.stomp.port=" + RABBIT_MQ_CONTAINER.getMappedPort(61613), "spring.rabbitmq.username=guest", - "spring.rabbitmq.password=guest") + "spring.rabbitmq.password=guest", + "spring.datasource.url=" + jdbcUrl, + "spring.datasource.username=" + username, + "spring.datasource.password=" + password, + + "spring.flyway.url=" + jdbcUrl, + "spring.flyway.user=" + username, + "spring.flyway.password=" + password) .applyTo(applicationContext.getEnvironment()); } } diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml index 81e2e3dc..35a0ee07 100644 --- a/backend/src/test/resources/application.yml +++ b/backend/src/test/resources/application.yml @@ -17,10 +17,7 @@ spring: default-property-inclusion: non_null datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:test;DATABASE_TO_UPPER=FALSE;MODE=MySQL; - username: sa - password: + driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: @@ -36,11 +33,8 @@ spring: console: enabled: true flyway: - url: jdbc:h2:mem:test;DATABASE_TO_UPPER=FALSE;MODE=MySQL; baseline-on-migrate: true enabled: true - user: sa - password: clean-disabled: false resilience4j.circuitbreaker: configs: @@ -51,4 +45,4 @@ resilience4j.circuitbreaker: waitDurationInOpenState: 10000 instances: backend-a: - base-config: default \ No newline at end of file + base-config: default diff --git a/docker-compose.yml b/docker-compose.yml index d541621b..c78ac16a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: tty: true db: - image: mariadb:10 + image: mysql container_name: db ports: - 3306:3306