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
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public record FollowerDto(
String profileImageUrl,
String aliasName,
String aliasColor,
Integer followerCount
Integer followerCount,
boolean isMyself
){

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@
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.UserQueryDto;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;

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

UserFollowersResponse.FollowerDto toFollowerDto(UserQueryDto dto);
// UserQueryDto의 userId와 Context로 넘어온 userId를 비교해서 true,false를 isMyself 필드에 주입
@Mapping(target = "isMyself", source = "dto.userId", qualifiedByName = "isMyself")
UserFollowersResponse.FollowerDto toFollowerDto(UserQueryDto dto, @Context Long loginUserId);

@Named("isMyself")
default boolean isMyself(Long userId, @Context Long loginUserId) {
return userId != null && userId.equals(loginUserId);
}

@Mapping(target = "isFollowing", constant = "true")
UserFollowingResponse.FollowingDto toFollowingDto(UserQueryDto dto);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public UserFollowersResponse getUserFollowers(Long userId, String cursor, int si
);

var followers = result.contents().stream()
.map(followQueryMapper::toFollowerDto)
.map(dto -> followQueryMapper.toFollowerDto(dto, userId))
.toList();
Comment on lines 38 to 40

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

isMyself 컨텍스트에 잘못된 userId가 전달되어 항상 false가 됩니다

현재 .map(dto -> followQueryMapper.toFollowerDto(dto, userId))에서 전달되는 userId는 "조회 대상 사용자"의 ID입니다. 그러나 요구사항의 isMyself는 "로그인 사용자" 기준이어야 합니다. 대상 사용자의 팔로워 목록에는 대상 사용자 본인이 포함될 수 없기 때문에, 현 구현은 isMyself가 사실상 항상 false가 되는 버그를 유발합니다.

즉, Mapper에는 로그인 사용자 ID를 전달해야 합니다.

권장 수정 방향:

  • 서비스/유스케이스 시그니처를 확장하여 targetUserId와 loginUserId를 구분 전달합니다.
  • 컨트롤러에서 인증 컨텍스트의 로그인 사용자 ID를 받아 서비스로 전달합니다.

해당 라인에 대한 변경 예시는 다음과 같습니다(참고: 아래 코드는 새로운 매개변수 도입을 전제로 합니다).

-        var followers = result.contents().stream()
-                .map(dto -> followQueryMapper.toFollowerDto(dto, userId))
+        var followers = result.contents().stream()
+                .map(dto -> followQueryMapper.toFollowerDto(dto, loginUserId))
                 .toList();

시그니처 변경(파일 외부 변경)은 아래처럼 반영되어야 합니다:

// UserGetFollowUsecase
UserFollowersResponse getUserFollowers(Long targetUserId, Long loginUserId, String cursor, int size);

// UserGetFollowService
public UserFollowersResponse getUserFollowers(Long targetUserId, Long loginUserId, String cursor, int size) {
    User user = userCommandPort.findById(targetUserId);
    ...
    var followers = result.contents().stream()
            .map(dto -> followQueryMapper.toFollowerDto(dto, loginUserId))
            .toList();
    ...
}

다음 스크립트로 호출부와 시그니처를 점검해 주세요. 기대: 컨트롤러(혹은 호출자)가 로그인 사용자 ID를 별도 인자로 전달하지 않고 있을 가능성이 높습니다.

원하시면 컨트롤러/유스케이스/테스트까지 포함한 전체 변경 패치를 준비하겠습니다.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Interface & Service signatures =="
rg -n -C3 --type=java '\binterface\s+UserGetFollowUsecase\b|class\s+UserGetFollowService\b'
rg -n -C3 --type=java '\bgetUserFollowers\s*\('

echo
echo "== Call sites of getUserFollowers =="
rg -n -C3 --type=java '\bgetUserFollowers\s*\('

echo
echo "== Controllers/resources mentioning followers endpoints =="
rg -n -C3 --type=java 'followers|getUserFollowers|/followers'

Length of output: 16191


로그인 사용자 ID를 컨트롤러→서비스→매퍼로 전달해 isMyself를 올바르게 계산하세요

현재 getUserFollowers(Long userId, String cursor, int size) 메서드에 대상 사용자 ID만 전달되고 있어,
followQueryMapper.toFollowerDto(dto, userId) 호출 시 isMyself가 항상 false로 처리됩니다.
isMyself는 “로그인한 사용자” 기준이어야 합니다.

수정이 필요한 위치:

  • UserGetFollowUsecase#getUserFollowers 시그니처 (src/main/java/.../port/in/UserGetFollowUsecase.java)
  • UserGetFollowService#getUserFollowers 시그니처 및 mapper 호출부
    (src/main/java/.../service/following/UserGetFollowService.java: 라인 29–40)
  • UserQueryController#showFollowers
    (src/main/java/.../adapter/in/web/UserQueryController.java: 라인 81)
  • Mapper 호출부:
    followQueryMapper.toFollowerDto(dto, userId)followQueryMapper.toFollowerDto(dto, loginUserId)

예시 diff:

// src/main/java/konkuk/thip/user/application/port/in/UserGetFollowUsecase.java
-public interface UserGetFollowUsecase {
-    UserFollowersResponse getUserFollowers(Long userId, String cursor, int size);
+public interface UserGetFollowUsecase {
+    UserFollowersResponse getUserFollowers(Long targetUserId, Long loginUserId, String cursor, int size);

// src/main/java/konkuk/thip/user/application/service/following/UserGetFollowService.java
-    public UserFollowersResponse getUserFollowers(Long userId, String cursor, int size) {
+    public UserFollowersResponse getUserFollowers(Long targetUserId, Long loginUserId, String cursor, int size) {
         User user = userCommandPort.findById(targetUserId);
         ...
-        var followers = result.contents().stream()
-            .map(dto -> followQueryMapper.toFollowerDto(dto, userId))
+        var followers = result.contents().stream()
+            .map(dto -> followQueryMapper.toFollowerDto(dto, loginUserId))
             .toList();

// src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java
-    return BaseResponse.ok(userGetFollowUsecase.getUserFollowers(userId, cursor, size));
+    Long loginUserId = authContext.getLoginUserId();
+    return BaseResponse.ok(
+        userGetFollowUsecase.getUserFollowers(userId, loginUserId, cursor, size)
+    );

테스트 및 매퍼 구현도 함께 업데이트해야 합니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var followers = result.contents().stream()
.map(followQueryMapper::toFollowerDto)
.map(dto -> followQueryMapper.toFollowerDto(dto, userId))
.toList();
var followers = result.contents().stream()
.map(dto -> followQueryMapper.toFollowerDto(dto, loginUserId))
.toList();
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/user/application/service/following/UserGetFollowService.java
around lines 38–40, the service passes the target userId into
followQueryMapper.toFollowerDto(dto, userId) causing isMyself to always be
false; change the service and related usecase/controller signatures to accept
and forward the currently logged-in user's ID (e.g., loginUserId) from
UserQueryController#showFollowers → UserGetFollowUsecase#getUserFollowers →
UserGetFollowService#getUserFollowers, and update the mapper call to
followQueryMapper.toFollowerDto(dto, loginUserId); also update corresponding
interface signatures, controller invocation, and tests/mapper implementations to
use loginUserId throughout.


return UserFollowersResponse.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ void getFeedsByBook_cursor_paging_success() throws Exception {
}

@Test
@DisplayName("비공개 피드 제외 및 자기 자신 피드 제외 검증")
@DisplayName("비공개 피드 제외 검증")
void getFeedsByBook_visibility_and_self_filter() throws Exception {
// given
AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias());
Expand Down Expand Up @@ -304,7 +304,7 @@ void getFeedsByBook_visibility_and_self_filter() throws Exception {
JsonNode feeds = root.path("data").path("feeds");

// 자기 자신 글 제외 비공개 제외로 인해 only othersPublic 만 남아야 함
assertThat(feeds.size()).isEqualTo(1);
assertThat(feeds.size()).isEqualTo(2);
assertThat(feeds.get(0).path("creatorId").asLong()).isEqualTo(other.getUserId());
assertThat(feeds.get(0).path("isWriter").asBoolean()).isFalse();
}
Comment on lines 306 to 310

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

주석/검증 불일치 및 순서 의존으로 인한 테스트 취약성

  • 주석은 "자기 자신 글 제외"라고 되어 있으나, 현재 요구사항은 "자기 자신 글 포함"입니다.
  • 첫 번째 요소가 다른 사용자의 피드라고 가정하는 검증은 정렬/동률 상황에서 쉽게 깨지는 플래키 테스트를 야기합니다.

아래처럼 순서 비의존적이고 새로운 요구사항에 맞는 검증으로 교체해 주세요.

다음 diff를 적용하세요:

-        // 자기 자신 글 제외 비공개 제외로 인해 only othersPublic 만 남아야 함
-        assertThat(feeds.size()).isEqualTo(2);
-        assertThat(feeds.get(0).path("creatorId").asLong()).isEqualTo(other.getUserId());
-        assertThat(feeds.get(0).path("isWriter").asBoolean()).isFalse();
+        // 비공개 제외 + 자기 자신 글 포함 검증: 공개 글 2개만 반환되어야 함
+        assertThat(feeds.size()).isEqualTo(2);
+
+        boolean hasMyPublic = false;
+        boolean hasOthersPublic = false;
+        for (JsonNode f : feeds) {
+            long creatorId = f.path("creatorId").asLong();
+            boolean isWriter = f.path("isWriter").asBoolean();
+            if (creatorId == requester.getUserId()) {
+                assertThat(isWriter).isTrue();
+                hasMyPublic = true;
+            }
+            if (creatorId == other.getUserId()) {
+                assertThat(isWriter).isFalse();
+                hasOthersPublic = true;
+            }
+        }
+        assertThat(hasMyPublic).isTrue();
+        assertThat(hasOthersPublic).isTrue();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 자기 자신 글 제외 비공개 제외로 인해 only othersPublic 만 남아야 함
assertThat(feeds.size()).isEqualTo(1);
assertThat(feeds.size()).isEqualTo(2);
assertThat(feeds.get(0).path("creatorId").asLong()).isEqualTo(other.getUserId());
assertThat(feeds.get(0).path("isWriter").asBoolean()).isFalse();
}
// 비공개 제외 + 자기 자신 글 포함 검증: 공개 글 2개만 반환되어야 함
assertThat(feeds.size()).isEqualTo(2);
boolean hasMyPublic = false;
boolean hasOthersPublic = false;
for (JsonNode f : feeds) {
long creatorId = f.path("creatorId").asLong();
boolean isWriter = f.path("isWriter").asBoolean();
if (creatorId == requester.getUserId()) {
assertThat(isWriter).isTrue();
hasMyPublic = true;
}
if (creatorId == other.getUserId()) {
assertThat(isWriter).isFalse();
hasOthersPublic = true;
}
}
assertThat(hasMyPublic).isTrue();
assertThat(hasOthersPublic).isTrue();
}
🤖 Prompt for AI Agents
In src/test/java/konkuk/thip/feed/adapter/in/web/FeedRelatedWithBookApiTest.java
around lines 306 to 310, the test comment and assertions assume "self posts
excluded" and rely on feeds.get(0) ordering, but requirement now includes self
posts and the index-based check is brittle; replace with order-independent
assertions: assert the feeds size is 2, collect creatorId values into a set and
assert it equals the set of the current user's ID and the other user's ID, and
for each feed assert isWriter is true when creatorId equals the current user's
ID and false otherwise; also update the comment to reflect "자기 자신 글 포함" (self
posts included).

Expand Down