diff --git a/config/guild-server-local.yml b/config/guild-server-local.yml index d1b3c1ac..7de62f28 100644 --- a/config/guild-server-local.yml +++ b/config/guild-server-local.yml @@ -18,9 +18,6 @@ spring: hikari: data-source-properties: serverTimezone: Asia/Seoul - jpa: - hibernate: - ddl-auto: none cloud: aws: diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/controller/DirectController.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/controller/DirectController.java index af21209c..fdaad2fd 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/controller/DirectController.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/controller/DirectController.java @@ -2,14 +2,12 @@ import com.asyncgate.guild_server.dto.request.DirectChannelCreateRequest; import com.asyncgate.guild_server.dto.response.DirectResponse; +import com.asyncgate.guild_server.dto.response.DirectResponses; import com.asyncgate.guild_server.service.DirectService; import com.asyncgate.guild_server.support.response.SuccessResponse; import lombok.RequiredArgsConstructor; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @@ -27,4 +25,14 @@ public SuccessResponse create( directService.create(currentUserId, request) ); } + + @GetMapping + public SuccessResponse get( + final @AuthenticationPrincipal String currentUserId + ) { + return SuccessResponse.ok( + directService.getDirectList(currentUserId) + ); + } + } diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/domain/Direct.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/domain/Direct.java index c95fef57..f43ba2e6 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/domain/Direct.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/domain/Direct.java @@ -3,6 +3,7 @@ import lombok.Builder; import lombok.Getter; +import java.time.LocalDateTime; import java.util.List; import java.util.UUID; @@ -10,10 +11,12 @@ public class Direct { private final String id; + private LocalDateTime createdDate; @Builder - private Direct(String id, String name) { + private Direct(String id, LocalDateTime createdDate) { this.id = id; + this.createdDate = createdDate; } public static Direct create() { diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/domain/DirectMember.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/domain/DirectMember.java index 4e16d5dc..21a50ee2 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/domain/DirectMember.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/domain/DirectMember.java @@ -3,6 +3,7 @@ import lombok.Builder; import lombok.Getter; +import java.time.LocalDateTime; import java.util.UUID; @Getter @@ -11,8 +12,17 @@ public class DirectMember { private final String directId; private final String memberId; private final String memberName; + private LocalDateTime createdDate; @Builder + private DirectMember(String id, String directId, String memberId, String memberName, LocalDateTime createdDate) { + this.id = id; + this.directId = directId; + this.memberId = memberId; + this.memberName = memberName; + this.createdDate = createdDate; + } + private DirectMember(String id, String directId, String memberId, String memberName) { this.id = id; this.directId = directId; diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/dto/response/DirectResponse.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/dto/response/DirectResponse.java index fbe10c42..a2d877b0 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/dto/response/DirectResponse.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/dto/response/DirectResponse.java @@ -2,7 +2,7 @@ import com.asyncgate.guild_server.client.UserClientInfoResponses; -public record DirectResponse(String directId,UserClientInfoResponses members) { +public record DirectResponse(String directId, UserClientInfoResponses members) { public static DirectResponse of(final String directId, final UserClientInfoResponses members) { return new DirectResponse(directId, members); } diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/dto/response/DirectResponses.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/dto/response/DirectResponses.java new file mode 100644 index 00000000..d36ff4b1 --- /dev/null +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/dto/response/DirectResponses.java @@ -0,0 +1,10 @@ +package com.asyncgate.guild_server.dto.response; + +import java.util.List; + +public record DirectResponses(List directResponses) { + + public static DirectResponses from(final List directResponses) { + return new DirectResponses(directResponses); + } +} diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/entity/DirectEntity.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/entity/DirectEntity.java index 6d79674e..b267bdb3 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/entity/DirectEntity.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/entity/DirectEntity.java @@ -12,7 +12,7 @@ @Entity @Table(name = "direct") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class DirectEntity { +public class DirectEntity extends BaseEntity { @Id private String id; diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/entity/DirectMemberEntity.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/entity/DirectMemberEntity.java index 44cfcc3b..c6b9601f 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/entity/DirectMemberEntity.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/entity/DirectMemberEntity.java @@ -12,16 +12,18 @@ @Entity @Table(name = "direct_member") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class DirectMemberEntity { +public class DirectMemberEntity extends BaseEntity { @Id private String id; + private String directId; private String memberId; private String memberName; @Builder - private DirectMemberEntity(String id, String memberId, String memberName) { + private DirectMemberEntity(String id, String directId, String memberId, String memberName) { this.id = id; + this.directId = directId; this.memberId = memberId; this.memberName = memberName; } diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberQueryDslRepository.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberQueryDslRepository.java new file mode 100644 index 00000000..ec64fac8 --- /dev/null +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberQueryDslRepository.java @@ -0,0 +1,29 @@ +package com.asyncgate.guild_server.repository; + +import com.asyncgate.guild_server.entity.DirectMemberEntity; +import com.asyncgate.guild_server.entity.QDirectMemberEntity; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class DirectMemberQueryDslRepository { + + private final JPAQueryFactory queryFactory; + private final QDirectMemberEntity directMemberEntity = QDirectMemberEntity.directMemberEntity; + + public List getDirectMessageList(final String currentUserId) { + return queryFactory + .select(directMemberEntity) + .from(directMemberEntity) + .where( + directMemberEntity.memberId.eq(currentUserId), + directMemberEntity.deleted.isFalse() + ) + .fetch(); + + } +} diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberRepository.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberRepository.java index d808003d..e7b09e4b 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberRepository.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberRepository.java @@ -6,4 +6,6 @@ public interface DirectMemberRepository { void saveAll(List directMembers); + + List getDirectMessageList(String currentUserId); } diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberRepositoryImpl.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberRepositoryImpl.java index 81e00ca1..620b4ad3 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberRepositoryImpl.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectMemberRepositoryImpl.java @@ -12,13 +12,23 @@ public class DirectMemberRepositoryImpl implements DirectMemberRepository { private final DirectMemberJpaRepository jpaRepository; + private final DirectMemberQueryDslRepository queryDslRepository; @Override - public void saveAll(List directMembers) { + public void saveAll(final List directMembers) { jpaRepository.saveAll( directMembers.stream() .map(DomainUtil.DirectMemberMapper::toEntity) .toList() ); } + + @Override + public List getDirectMessageList(final String currentUserId) { + return queryDslRepository.getDirectMessageList(currentUserId).stream() + .map( + DomainUtil.DirectMemberMapper::toDomain + ) + .toList(); + } } diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectRepositoryImpl.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectRepositoryImpl.java index b4d402c4..86c60a4a 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectRepositoryImpl.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/repository/DirectRepositoryImpl.java @@ -11,7 +11,7 @@ public class DirectRepositoryImpl implements DirectRepository { private final DirectJpaRepository jpaRepository; @Override - public void save(Direct direct) { + public void save(final Direct direct) { jpaRepository.save( DomainUtil.DirectMapper.toEntity(direct) ); diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/service/DirectService.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/service/DirectService.java index fdd951bb..9c9f0629 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/service/DirectService.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/service/DirectService.java @@ -2,7 +2,10 @@ import com.asyncgate.guild_server.dto.request.DirectChannelCreateRequest; import com.asyncgate.guild_server.dto.response.DirectResponse; +import com.asyncgate.guild_server.dto.response.DirectResponses; public interface DirectService { DirectResponse create(String currentUserId, DirectChannelCreateRequest request); + + DirectResponses getDirectList(String currentUserId); } diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/service/DirectServiceImpl.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/service/DirectServiceImpl.java index fe2ba512..6d20f9a0 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/service/DirectServiceImpl.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/service/DirectServiceImpl.java @@ -2,17 +2,21 @@ import com.asyncgate.guild_server.client.UserClient; import com.asyncgate.guild_server.client.UserClientInfoResponses; +import com.asyncgate.guild_server.client.UserClientInfoResponses.UserClientInfoResponse; import com.asyncgate.guild_server.domain.Direct; import com.asyncgate.guild_server.domain.DirectMember; import com.asyncgate.guild_server.dto.request.DirectChannelCreateRequest; import com.asyncgate.guild_server.dto.response.DirectResponse; +import com.asyncgate.guild_server.dto.response.DirectResponses; import com.asyncgate.guild_server.repository.DirectMemberRepository; import com.asyncgate.guild_server.repository.DirectRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -46,4 +50,50 @@ public DirectResponse create(final String currentUserId, final DirectChannelCrea return DirectResponse.of(direct.getId(), usersInfo); } + + @Override + public DirectResponses getDirectList(final String currentUserId) { + // 1. 현재 사용자의 다이렉트 메시지 목록 조회 + List directMembers = directMemberRepository.getDirectMessageList(currentUserId); + + // 2. 다이렉트 ID별로 DirectMember 그룹화 + Map> directGroups = directMembers.stream() + .collect(Collectors.groupingBy(DirectMember::getDirectId)); + + // 3. 모든 그룹에서 고유한 사용자 ID 수집 (한 번의 배치 API 호출을 위함) + Set uniqueMemberIds = directMembers.stream() + .map(DirectMember::getMemberId) + .collect(Collectors.toSet()); + + // 4. 배치 API 호출로 모든 사용자 프로필 정보 조회 + UserClientInfoResponses userInfos = userClient + .getUsersInfo(new ArrayList<>(uniqueMemberIds)) + .getResult(); + + // 5. 사용자 ID별 프로필 정보를 빠르게 조회할 수 있도록 Map으로 변환 + Map userInfoMap = userInfos.responses().stream() + .collect( + Collectors.toMap( + UserClientInfoResponse::userId, + Function.identity() + ) + ); + + // 6. 각 다이렉트 그룹별로 응답 객체를 생성 + List responses = directGroups.entrySet().stream() + .map(entry -> { + String directId = entry.getKey(); + List membersInGroup = entry.getValue(); + + // 해당 그룹에 속한 멤버들의 프로필 정보를 매핑 (null은 필터링) + List memberProfiles = membersInGroup.stream() + .map(directMember -> userInfoMap.get(directMember.getMemberId())) + .filter(Objects::nonNull) + .toList(); + + return DirectResponse.of(directId, new UserClientInfoResponses(memberProfiles)); + }).toList(); + return DirectResponses.from(responses); + } + } diff --git a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/support/utility/DomainUtil.java b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/support/utility/DomainUtil.java index 24c6e27a..a03ce15a 100644 --- a/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/support/utility/DomainUtil.java +++ b/src/backend/guild-server/src/main/java/com/asyncgate/guild_server/support/utility/DomainUtil.java @@ -101,6 +101,7 @@ public static DirectEntity toEntity(final Direct domain) { public static Direct toDomain(final DirectEntity entity) { return Direct.builder() .id(entity.getId()) + .createdDate(entity.getCreatedDate()) .build(); } } @@ -109,6 +110,7 @@ public static class DirectMemberMapper { public static DirectMemberEntity toEntity(final DirectMember directMember) { return DirectMemberEntity.builder() .id(directMember.getId()) + .directId(directMember.getDirectId()) .memberId(directMember.getMemberId()) .memberName(directMember.getMemberName()) .build(); @@ -119,6 +121,7 @@ public static DirectMember toDomain(final DirectMemberEntity entity) { .id(entity.getId()) .memberId(entity.getMemberId()) .memberName(entity.getMemberName()) + .createdDate(entity.getCreatedDate()) .build(); } }