Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ dependencies {

//s3 버킷
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

// MapStruct
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
}

def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/konkuk/thip/common/util/CursorBasedList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package konkuk.thip.common.util;

import java.util.List;

public record CursorBasedList<T>(
List<T> contents,
String nextCursor,
boolean hasNext
) {
Comment on lines +5 to +9
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클라이언트와 주고받는 cursor 값을 String 타입으로 명시하고, 영속성 adapter 내부에서 클라이언트에게 전달받은 커서의 타입을 LocalDateTime 으로 수정하셨군요!!

전 "내 모임방 목록 조회 api" 에서

  1. controller 의 cursor request parameter 의 타입을 LocalDate, Long 으로 설정
  2. 따라서 스프링부트가 자동으로 String -> LocalDate, String -> Long 으로 변환해줌
  3. LocalDate, Long 타입의 커서를 사용하여 페이징 처리
  4. 다시 클라이언트에게 nextCursor 값을 전달하기 위해 response에 LocalDate, Long 타입의 nextCursor 를 정의
  5. 클라이언트는 JSON 형식의 response를 받는데, 내부 nextCursor의 값은 LocalDate의 String 형식(ex "2020-00-00") 과 JS의 Number 타입 으로 변환
  6. 따라서 nextCursor 의 값을 이용해 다시 요청을 보낼 경우, response 로 응답받은 커서값을 형변환없이 그대로 사용 가능

위와 같은 플로우를 생각하여 controller 에서 받는 cursor request parameter, response 로 전달하는 nextCursor 의 타입을 모두 저희가 페이징 처리시 사용하는 타입 그대로 정의하였습니다.

@buzz0331 @hd0rable 혹시 이 부분에 대해서 어떻게 생각하시나요??

위 플로우처럼 스프링부트가 제공해주는 Jackson 라이브러리를 활용하면 클라이언트와 주고받는 커서의 타입을 신경쓰지 않아도 되고, 명시한 커서 타입이 아니라 다른 타입이 요청 파라미터로 들어올 경우 스프링이 내부적으로 "파라미터 바인딩 실패 -> 400 error throw" 해주므로 저희가 추가로 예외처리를 하지 않아도 되는 장점이 있을 것 같습니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 개인적으로 커서를 하나의 문자열로 받아 처리하는 방식이 응답 구조의 일관성이나 확장성 측면에서 더 적합하다고 생각하고 있습니다. 앞으로 저희가 커서 페이징에서 공통적으로 사용할 CursorBasedList 구조에서도 nextCursor는 단일 String 타입으로 정의되어 있기 때문에, 커서 역시 하나의 문자열로 통합하여 사용하는 것이 다양한 조회 API에 일관되게 적용할 수 있고, 응답 구조 또한 통일된 형태로 유지할 수 있다는 점에서 장점이 있다고 생각합니다.

반대로, 커서를 여러 파라미터로 나누게 되면 각 API마다 커서 필드를 개별적으로 정의해야 하며, 공통 응답 객체를 사용하는 데에도 제한이 생기게 됩니다. 또한 이 경우 응답 DTO뿐만 아니라 요청 파라미터도 각각의 이름을 따로 지정해야 하기 때문에, 예를 들어 cursorDate, cursorRoomId처럼 네이밍이 API마다 달라질 가능성이 높고, 이는 클라이언트 측에서도 사용 시 혼란을 줄 수 있다고 생각합니다.

반면 커서를 cursor={String} 형태로 일관되게 주고받게 되면, 클라이언트는 커서 값을 단순히 저장하고 그대로 다음 요청에 넘겨주는 방식으로 처리할 수 있기 때문에, 구현 부담도 줄고 인터페이스도 단순해진다는 장점이 있습니다.

무엇보다 커서 구조를 외부에 드러내지 않고, 서버 내부 로직에 따라 파싱하여 처리하는 방식은 캡슐화 측면에서도 바람직하며, 추후 커서 구조가 변경되거나 인코딩/보안 처리 등을 추가해야 할 때도 서버 단에서만 수정하면 되므로 유지보수에도 유리하다고 생각합니다.

이런 이유로 성준님 PR에서 사용하신 LocalDate와 roomId 두 개의 커서 값을 "2024-07-22|102"와 같은 형식으로 하나의 문자열 커서로 묶어서 전달하고, 이를 서버 내부에서 분리·파싱하여 사용하는 방식도 한번 고려해보면 좋을 것 같습니다!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아아 CursorBasedList에 정의해두신 String nextCursor 가 커서 하나의 String 형변환값이 아니라, 그냥 저희가 페이징처리시에 사용할 커서 자체를 의미하신 거군요!

코멘트로 남겨주신 내용처럼 복합 커서가 필요할 경우, 저희가 내부적으로 복합 커서를 String 으로 변환하고, 받은 cursor 값을 다시 복합 커서로 파싱하는 방식 좋은 것 같습니다!!

현준님 pr 머지되면 복합 커서로 작성된 코드 수정해보겠습니다!!

public static <T> CursorBasedList<T> of(List<T> queryList, int size, CursorExtractor<T> extractor) {
boolean hasNext = queryList.size() > size;
List<T> contents = hasNext ? queryList.subList(0, size) : queryList;
String nextCursor = hasNext ? extractor.extractCursor(contents.get(size - 1)) : null;
return new CursorBasedList<>(contents, nextCursor, hasNext);
}
}
6 changes: 6 additions & 0 deletions src/main/java/konkuk/thip/common/util/CursorExtractor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package konkuk.thip.common.util;

@FunctionalInterface
public interface CursorExtractor<T> {
String extractCursor(T lastElement);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public class UserCommandController {
private final UserFollowUsecase userFollowUsecase;
private final JwtUtil jwtUtil;

/**
* 사용자 회원가입
*/
@PostMapping("/users/signup")
public BaseResponse<UserSignupResponse> signup(@Valid @RequestBody final UserSignupRequest request,
@Oauth2Id final String oauth2Id,
Expand All @@ -43,14 +46,19 @@ public BaseResponse<UserSignupResponse> signup(@Valid @RequestBody final UserSig
return BaseResponse.ok(UserSignupResponse.of(userId));
}

/**
* 닉네임 중복 확인
*/
@PostMapping("/users/nickname")
public BaseResponse<UserVerifyNicknameResponse> verifyNickname(@Valid @RequestBody final UserVerifyNicknameRequest request) {
return BaseResponse.ok(UserVerifyNicknameResponse.of(
userVerifyNicknameUseCase.isNicknameUnique(request.nickname()))
);
}

// 팔루우 상태 변경 : true -> 팔로우, false -> 언팔로우
/**
* 사용자 팔로우 상태 변경 : true -> 팔로우, false -> 언팔로우
*/
@PostMapping("/users/following/{followingUserId}")
public BaseResponse<UserFollowResponse> followUser(@UserId final Long userId,
@PathVariable final Long followingUserId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package konkuk.thip.user.adapter.in.web;

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import konkuk.thip.common.dto.BaseResponse;
import konkuk.thip.common.security.annotation.UserId;
import konkuk.thip.user.adapter.in.web.response.UserFollowersResponse;
import konkuk.thip.user.adapter.in.web.response.UserFollowingResponse;
import konkuk.thip.user.adapter.in.web.response.UserViewAliasChoiceResponse;
import konkuk.thip.user.application.port.in.UserGetFollowersUsecase;
import konkuk.thip.user.application.port.in.UserGetFollowUsecase;
import konkuk.thip.user.application.port.in.UserViewAliasChoiceUseCase;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -16,18 +20,35 @@
public class UserQueryController {

private final UserViewAliasChoiceUseCase userViewAliasChoiceUseCase;
private final UserGetFollowersUsecase userGetFollowersUsecase;
private final UserGetFollowUsecase userGetFollowUsecase;

/**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

* 사용자 별칭 선택 화면 조회
*/
@GetMapping("/users/alias")
public BaseResponse<UserViewAliasChoiceResponse> showAliasChoiceView() {
return BaseResponse.ok(UserViewAliasChoiceResponse.of(
userViewAliasChoiceUseCase.getAllAliasesAndCategories()
));
}

/**
* 사용자 팔로워 조회
*/
@GetMapping("/users/{userId}/followers")
public BaseResponse<UserFollowersResponse> showFollowers(@PathVariable final Long userId,
@RequestParam(required = false) final String cursor) {
return BaseResponse.ok(userGetFollowersUsecase.getUserFollowers(userId, cursor));
@RequestParam(required = false) final String cursor,
@RequestParam(defaultValue = "10") @Max(value = 10) @Min(value = 1) final int size) {
return BaseResponse.ok(userGetFollowUsecase.getUserFollowers(userId, cursor, size));
}

/**
* 내 팔로잉 리스트 조회
*/
@GetMapping("/users/my/following")
public BaseResponse<UserFollowingResponse> showMyFollowing(@UserId final Long userId,
@RequestParam(required = false) final String cursor,
@RequestParam(defaultValue = "10") @Max(value = 10) @Min(value = 1) final int size) {
Comment on lines +40 to +51
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3 : 저희 페이지 사이즈 BE 내부에서 정의하는게 아니라 요청 파라미터로 받는 건가요??

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵, 프론트 쪽에서 단일 페이지 크기를 요청 파라미터로 전달할 수도 있고, 전달하지 않는 경우에는 최대 페이지 수 10을 기본값(default) 으로 설정해두었습니다
이렇게 하면 API의 유연성이 좀 더 높아지지 않을까 생각했습니다!

return BaseResponse.ok(userGetFollowUsecase.getMyFollowing(userId, cursor, size));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@

import lombok.Builder;

import java.time.LocalDateTime;
import java.util.List;

@Builder
public record UserFollowersResponse(
List<Follower> followerList,
int size,
LocalDateTime nextCursor,
boolean isFirst,
List<Follower> followers,
String nextCursor,
boolean isLast
) {
@Builder
Expand All @@ -21,6 +18,7 @@ public record Follower(
String aliasName,
Integer followerCount
){

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package konkuk.thip.user.adapter.in.web.response;

import lombok.Builder;

import java.util.List;

@Builder
public record UserFollowingResponse(
List<Following> followings,
String nextCursor,
boolean isLast
) {
@Builder
public record Following(
Long userId,
String nickname,
String profileImageUrl,
String aliasName
){
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ public class FollowingJpaEntity extends BaseJpaEntity {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private UserJpaEntity userJpaEntity;
private UserJpaEntity userJpaEntity; // 팔로잉 하는 유저

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "following_user_id")
private UserJpaEntity followingUserJpaEntity;
private UserJpaEntity followingUserJpaEntity; // 팔로우 당하는 유저
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋ 팔로우당하는유저(비웃은거아님웃겨서웃은거임)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋㅋ 팔로잉 팔로워 헷갈리긴 하네여

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필드 이름이 너무 애매해서 그냥 주석 남겼습니다 ㅎ

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package konkuk.thip.user.adapter.out.persistence;

import konkuk.thip.common.util.CursorBasedList;
import konkuk.thip.common.util.DateUtil;
import konkuk.thip.user.adapter.in.web.response.UserFollowersResponse;
import konkuk.thip.user.adapter.out.jpa.FollowingJpaEntity;
import konkuk.thip.user.adapter.out.jpa.UserJpaEntity;
import konkuk.thip.user.application.port.out.dto.FollowQueryDto;
import konkuk.thip.user.adapter.out.persistence.repository.following.FollowingJpaRepository;
import konkuk.thip.user.application.port.out.FollowingQueryPort;
import lombok.RequiredArgsConstructor;
Expand All @@ -19,39 +18,26 @@ public class FollowingQueryPersistenceAdapter implements FollowingQueryPort {
private final FollowingJpaRepository followingJpaRepository;

@Override
public UserFollowersResponse getFollowersByUserId(Long userId, String cursor, int size) {
LocalDateTime nextCursor = null;
if (cursor != null && !cursor.isBlank()) {
nextCursor = DateUtil.parseDateTime(cursor);
}

List<FollowingJpaEntity> followerEntities =
followingJpaRepository.findFollowersByUserIdBeforeCreatedAt(userId, nextCursor, size);

List<UserJpaEntity> followers = followerEntities.stream()
.map(FollowingJpaEntity::getUserJpaEntity) // 팔로워 사용자
.toList();

List<UserFollowersResponse.Follower> followerList = followers.stream()
.map(follower -> UserFollowersResponse.Follower.builder()
.userId(follower.getUserId())
.nickname(follower.getNickname())
.profileImageUrl(follower.getAliasForUserJpaEntity().getImageUrl())
.aliasName(follower.getAliasForUserJpaEntity().getValue())
.followerCount(follower.getFollowerCount())
.build())
.toList();

boolean isLast = followerEntities.size() < size;
nextCursor = isLast ? null :
followerEntities.get(followerEntities.size() - 1).getCreatedAt();

return UserFollowersResponse.builder()
.followerList(followerList)
.size(followerList.size())
.nextCursor(nextCursor)
.isFirst(cursor == null) // cursor가 null이면 첫 페이지
.isLast(isLast)
.build();
public CursorBasedList<FollowQueryDto> getFollowersByUserId(Long userId, String cursor, int size) {
LocalDateTime cursorVal = cursor != null && !cursor.isBlank() ? DateUtil.parseDateTime(cursor) : null;
List<FollowQueryDto> followerDtos = followingJpaRepository.findFollowerDtosByUserIdBeforeCreatedAt(
userId,
cursorVal,
size
);
Comment on lines +23 to +27
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

커서 기반 페이지네이션 구현 확인

메서드명 findFollowerDtosByUserIdBeforeCreatedAt는 커서 이전(exclusive) 데이터를 조회하는 것을 암시하지만, 학습된 내용에 따르면 THIP 프로젝트는 커서 이상(inclusive)의 데이터를 조회해야 합니다.


🏁 Script executed:

#!/bin/bash
# Repository 구현에서 실제 쿼리 조건 확인
rg -B 5 -A 10 "findFollowerDtosByUserIdBeforeCreatedAt.*cursor.*size" src/main/java/konkuk/thip/user/adapter/out/persistence/repository/

Length of output: 3627


🏁 Script executed:

#!/bin/bash
# find the implementation of findFollowDtos to check cursor comparison operator
rg -n "private List<FollowQueryDto> findFollowDtos" -A 20 src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java

Length of output: 1269


커서 이상(inclusive) 조회로 조건 수정 필요

THIP 프로젝트의 커서 페이지네이션 컨벤션에 따라, 전달받은 커서 이상의 데이터를 조회해야 합니다. 현재 findFollowDtos 구현에서 following.createdAt.lt(cursor)로 strict하게(exclusive) 필터링하고 있으므로, loe(cursor)로 수정해주세요.

  • 대상 파일:
    src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java
  • 대상 라인:
    // 변경 전 (exclusive)
    if (cursor != null) {
        condition.and(following.createdAt.lt(cursor));
    }
  • 변경 제안:
    if (cursor != null) {
    -    condition.and(following.createdAt.lt(cursor));
    +    condition.and(following.createdAt.loe(cursor));
    }
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java
around the lines where the cursor condition is applied, change the filtering
condition from exclusive (following.createdAt.lt(cursor)) to inclusive
(following.createdAt.loe(cursor)) to align with the THIP project's cursor
pagination convention that requires fetching data with createdAt values greater
than or equal to the cursor.


return CursorBasedList.of(followerDtos, size, followerDto -> followerDto.createdAt().toString());
}

@Override
public CursorBasedList<FollowQueryDto> getFollowingByUserId(Long userId, String cursor, int size) {
LocalDateTime cursorVal = cursor != null && !cursor.isBlank() ? DateUtil.parseDateTime(cursor) : null;
List<FollowQueryDto> followingDtos = followingJpaRepository.findFollowingDtosByUserIdBeforeCreatedAt(
userId,
cursorVal,
size
);

return CursorBasedList.of(followingDtos, size, followingDto -> followingDto.createdAt().toString());
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 함수형 인터페이스 좋네요

}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package konkuk.thip.user.adapter.out.persistence.repository.following;

import konkuk.thip.user.adapter.out.jpa.FollowingJpaEntity;
import konkuk.thip.user.application.port.out.dto.FollowQueryDto;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -9,5 +10,6 @@
public interface FollowingQueryRepository {
Optional<FollowingJpaEntity> findByUserAndTargetUser(Long userId, Long targetUserId);

List<FollowingJpaEntity> findFollowersByUserIdBeforeCreatedAt(Long userId, LocalDateTime cursor, int size);
List<FollowQueryDto> findFollowerDtosByUserIdBeforeCreatedAt(Long userId, LocalDateTime cursor, int size);
List<FollowQueryDto> findFollowingDtosByUserIdBeforeCreatedAt(Long userId, LocalDateTime cursor, int size);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import konkuk.thip.user.adapter.out.jpa.QAliasJpaEntity;
import konkuk.thip.user.adapter.out.jpa.QFollowingJpaEntity;
import konkuk.thip.user.adapter.out.jpa.QUserJpaEntity;
import konkuk.thip.user.application.port.out.dto.FollowQueryDto;
import konkuk.thip.user.application.port.out.dto.QFollowQueryDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

Expand Down Expand Up @@ -34,26 +36,55 @@ public Optional<FollowingJpaEntity> findByUserAndTargetUser(Long userId, Long ta
}

@Override
public List<FollowingJpaEntity> findFollowersByUserIdBeforeCreatedAt(Long userId, LocalDateTime cursor, int size) {
public List<FollowQueryDto> findFollowerDtosByUserIdBeforeCreatedAt(Long userId, LocalDateTime cursor, int size) {
return findFollowDtos(
userId,
cursor,
size,
true // isFollowerQuery
);
}

@Override
public List<FollowQueryDto> findFollowingDtosByUserIdBeforeCreatedAt(Long userId, LocalDateTime cursor, int size) {
return findFollowDtos(
userId,
cursor,
size,
false // isFollowingQuery
);
}

private List<FollowQueryDto> findFollowDtos(Long userId, LocalDateTime cursor, int size, boolean isFollowerQuery) {
QFollowingJpaEntity following = QFollowingJpaEntity.followingJpaEntity;
QUserJpaEntity user = QUserJpaEntity.userJpaEntity;
QAliasJpaEntity alias = QAliasJpaEntity.aliasJpaEntity;

BooleanBuilder condition = new BooleanBuilder()
.and(following.followingUserJpaEntity.userId.eq(userId))
.and((isFollowerQuery ? following.followingUserJpaEntity.userId.eq(userId) : following.userJpaEntity.userId.eq(userId)))
.and(following.status.eq(StatusType.ACTIVE));

if (cursor != null) {
condition.and(following.createdAt.lt(cursor));
}
Comment on lines 67 to 69
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

커서 조건이 프로젝트 컨벤션과 불일치

학습된 내용에 따르면 THIP 프로젝트의 커서 페이지네이션은 "커서 이상(inclusive)"의 데이터를 조회해야 하므로 goe/loe를 사용해야 합니다. 하지만 현재 구현은 lt()를 사용하여 exclusive 조건으로 되어 있습니다.

         if (cursor != null) {
-            condition.and(following.createdAt.lt(cursor));
+            condition.and(following.createdAt.loe(cursor));
         }
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java
at lines 67 to 69, the cursor pagination condition uses an exclusive less-than
(lt) comparison, which conflicts with the THIP project convention requiring
inclusive cursor pagination. Change the condition to use a greater-than-or-equal
(goe) or less-than-or-equal (loe) comparison as appropriate to include the
cursor value in the query results.


QUserJpaEntity targetUser = isFollowerQuery ? following.userJpaEntity : following.followingUserJpaEntity;

return jpaQueryFactory
.selectFrom(following)
.leftJoin(following.userJpaEntity, user).fetchJoin() // N+1 문제 방지를 위해 fetchJoin
.leftJoin(user.aliasForUserJpaEntity, alias).fetchJoin()
.select(new QFollowQueryDto(
targetUser.userId,
targetUser.nickname,
alias.imageUrl,
alias.value,
targetUser.followerCount,
following.createdAt
))
.from(following)
.leftJoin(targetUser, user)
.leftJoin(user.aliasForUserJpaEntity, alias)
.where(condition)
.orderBy(following.createdAt.desc())
.limit(size)
.limit(size + 1)
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package konkuk.thip.user.application.mapper;

import konkuk.thip.user.adapter.in.web.response.UserFollowersResponse;
import konkuk.thip.user.adapter.in.web.response.UserFollowingResponse;
import konkuk.thip.user.application.port.out.dto.FollowQueryDto;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
public interface FollowDtoMapper {

UserFollowersResponse.Follower toFollowerList(FollowQueryDto dto);

UserFollowingResponse.Following toFollowingList(FollowQueryDto dto);
}
Comment on lines +8 to +14
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 새로운 기술 도입 좋습니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package konkuk.thip.user.application.port.in;

import konkuk.thip.user.adapter.in.web.response.UserFollowersResponse;
import konkuk.thip.user.adapter.in.web.response.UserFollowingResponse;

public interface UserGetFollowUsecase {
UserFollowersResponse getUserFollowers(Long userId, String cursor, int size);

UserFollowingResponse getMyFollowing(Long userId, String cursor, int size);
Comment on lines +7 to +9
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
메서드 네이밍이 직관적이어서 보기 편하네요!!

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package konkuk.thip.user.application.port.out;

import konkuk.thip.user.adapter.in.web.response.UserFollowersResponse;
import konkuk.thip.common.util.CursorBasedList;
import konkuk.thip.user.application.port.out.dto.FollowQueryDto;

public interface FollowingQueryPort {
UserFollowersResponse getFollowersByUserId(Long userId, String cursor, int size);
CursorBasedList<FollowQueryDto> getFollowersByUserId(Long userId, String cursor, int size);
CursorBasedList<FollowQueryDto> getFollowingByUserId(Long userId, String cursor, int size);
}

Loading