[hotfix] 사용자 팔로워 조회시 나인지 여부 반환 플래그 반환#269
Conversation
Walkthrough사용자 팔로워 응답 DTO에 isMyself 필드가 추가되었고, 이를 매핑하기 위해 FollowQueryMapper가 @context 파라미터(loginUserId)를 받도록 변경되었으며, 서비스에서 해당 파라미터를 전달하도록 수정되었다. 피드 관련 테스트에서 자기 자신 피드 제외 기대값이 변경되었다. Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant S as UserGetFollowService
participant M as FollowQueryMapper
participant R as UserFollowersResponse
C->>S: getUserFollowers(targetUserId, loginUserId, page)
S->>S: 조회/페이징 데이터 준비
loop for each follower UserQueryDto
S->>M: toFollowerDto(dto, loginUserId)
M->>M: isMyself(dto.userId, loginUserId)
M-->>S: FollowerDto(userId, ..., isMyself)
end
S-->>C: UserFollowersResponse(list<FollowerDto>, total, pageInfo)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
Test Results416 tests 416 ✅ 31s ⏱️ Results for commit b9792f6. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/test/java/konkuk/thip/feed/adapter/in/web/FeedRelatedWithBookApiTest.java (1)
265-269: 표현 변경은 적절합니다만, 메서드명과 불일치합니다DisplayName은 "비공개 피드 제외 검증"으로 바뀌었는데, 메서드명은 여전히
getFeedsByBook_visibility_and_self_filter로 자기 자신 제외를 암시합니다. 혼동을 줄이기 위해 메서드명을 DisplayName과 일치하도록 정리하는 것을 권장합니다.src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowersResponse.java (1)
21-23: API 스키마 확장: isMyself 추가는 요구사항과 부합합니다클라이언트가 추가 필드에 관대하다면 하위 호환에 큰 문제는 없습니다. 다만 다음을 고려해 주세요:
- OpenAPI/Swagger 문서에 isMyself 추가 반영 필요.
- Jackson 환경에서 레코드 컴포넌트
isMyself는 기본적으로"isMyself"로 직렬화됩니다. 사내 Naming 전략에 특이규칙이 있다면 한 번만 확인해 주세요.선택사항: 직렬화 명시성을 높이고자 한다면 다음과 같이 @JsonProperty를 추가할 수 있습니다.
+import com.fasterxml.jackson.annotation.JsonProperty; ... public record FollowerDto( Long userId, String nickname, String profileImageUrl, String aliasName, String aliasColor, Integer followerCount, - boolean isMyself + @JsonProperty("isMyself") boolean isMyself ){isMyself 추가가 공개 API에 반영되었는지(문서/Mock/스냅샷 테스트 등) 한 번만 확인 부탁드립니다.
src/main/java/konkuk/thip/user/application/mapper/FollowQueryMapper.java (1)
14-21: MapStruct 컨텍스트 매핑 적절합니다
@Context와@Named를 사용한isMyself주입은 의도에 부합합니다.- null 방어 로직도 포함되어 있어 안전합니다.
사소한 개선사항: 가독성을 위해
Objects.equals사용을 고려해 보세요.다음처럼 단순화 가능합니다:
+import java.util.Objects; ... @Named("isMyself") default boolean isMyself(Long userId, @Context Long loginUserId) { - return userId != null && userId.equals(loginUserId); + return Objects.equals(userId, loginUserId); }추가로, 서비스에서 반드시 "로그인 사용자 ID"를 Context로 전달해야 의미가 있으므로, 해당 부분이 올바르게 연결되었는지 확인 바랍니다(현재는 대상 사용자 ID가 넘어가고 있어 오동작 가능성이 있습니다 — 서비스 파일 코멘트 참고).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowersResponse.java(1 hunks)src/main/java/konkuk/thip/user/application/mapper/FollowQueryMapper.java(1 hunks)src/main/java/konkuk/thip/user/application/service/following/UserGetFollowService.java(1 hunks)src/test/java/konkuk/thip/feed/adapter/in/web/FeedRelatedWithBookApiTest.java(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
| var followers = result.contents().stream() | ||
| .map(followQueryMapper::toFollowerDto) | ||
| .map(dto -> followQueryMapper.toFollowerDto(dto, userId)) | ||
| .toList(); |
There was a problem hiding this comment.
💡 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.
| 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.
| // 자기 자신 글 제외 비공개 제외로 인해 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(); | ||
| } |
There was a problem hiding this comment.
🛠️ 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.
| // 자기 자신 글 제외 비공개 제외로 인해 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).
#️⃣ 연관된 이슈
📝 작업 내용
@heeeeyong 님의 요청에 따라 사용자 팔로워 조회시에 로그인한 사용자인지 아닌지를 구분하는 isMyself 플래그를 추가합니다.
추가적으로 앞에서 수정했던 특정 책으로 작성된 피드 조회에서 내 피드도 조회되도록 수정하였는데 이에 따른 통합 테스트도 수정하였습니다.
📸 스크린샷
💬 리뷰 요구사항
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit