-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor: Like 도메인 리팩토링 #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Caution Review failedThe pull request is closed. Walkthroughlike 모듈의 모델 클래스를 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as 사용자
participant C as LikeController
participant S as LikeService
participant P as LikePolicyService
participant F as LikeTargetFetchService
participant R as LikeRepository
participant E as EventPublisher
U->>C: POST /likes {targetType, targetId}
C->>S: addLike(userId, targetType, targetId)
S->>P: validateTargetExists(targetType, targetId, userId)
P->>F: isTargetViewable(targetType, targetId, userId)
F-->>P: boolean
alt not viewable
P-->>S: throws LIKE_TARGET_NOT_FOUND
S-->>C: error response
else viewable
S->>R: save(new Like)
R-->>S: Like
S->>E: publish LikeEvent
S-->>C: success
end
sequenceDiagram
autonumber
actor U as 사용자
participant C as LikeController
participant S as LikeService
participant LR as LikeRepository
participant F as LikeTargetFetchService
U->>C: GET /likes?type=...&page=...
C->>S: getLikes(userId, targetType, searchReq)
S->>LR: findAllByUserAndType(paging)
LR-->>S: Page<Like>
S->>F: fetchByTypeAndIds(targetType, likeIdsSet, userId)
F-->>S: Map<id, LikeTargetResponse>
S-->>C: PagingResponse<LikeResponse<LikeTargetResponse>>
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java (1)
33-37: userId 미사용 — LikeCardSetFetcher.fetchByIds에 가시성 필터 적용 필요LikeTargetFetchService.fetchByTypeAndIds(..., userId)가 userId를 전달하므로 각 fetcher는 userId로 가시성 필터링해야 합니다. 현재 LikeCardSetFetcher.fetchByIds는 userId를 무시합니다.
public Map<Long, CardSetLikeResponse> fetchByIds(Set<Long> ids, Long userId) { - return cardSetService.getCardSetsByIds(ids).stream() - .map(CardSetLikeResponse::from) - .collect(Collectors.toMap(LikeTargetResponse::getId, Function.identity())); + return cardSetService.getCardSetsByIds(ids).stream() + .map(CardSetLikeResponse::from) + .filter(res -> isTargetViewable(res.getId(), userId)) + .collect(Collectors.toMap(LikeTargetResponse::getId, Function.identity())); }또는 CardSetService에 userId 기반 배치 조회 API를 만들어 권한 체크 횟수를 줄이세요. 필터링할 경우 LikeService.getLikes 쪽에서 targetMap.get(...)이 null을 반환할 수 있으니 누락된 대상 처리(혹은 건너뛰기)를 검토하세요.
src/main/java/project/flipnote/like/model/request/LikeTargetTypeRequest.java (1)
5-11: ENUM 네이밍: UPPER_SNAKE_CASE 권장 — PathVariable 매핑 보존 필요Java 관례상 enum 상수는 UPPER_SNAKE_CASE를 권장합니다. 그러나 이 enum은 컨트롤러의 @PathVariable로 바인딩되고 있어(src/main/java/project/flipnote/like/controller/LikeController.java), 단순히 CARD_SET으로 이름만 바꾸고 @JsonProperty("card_set")만 추가하면 PathVariable('card_set') → enum 매핑은 깨집니다(@JsonProperty는 Jackson 전용).
- 옵션 A (간단): 외부 API 계약을 유지하려면 현재처럼 card_set 이름 유지.
- 옵션 B (리팩터, 권장 컨벤션): 상수명 변경 → CARD_SET 및 JSON 바인딩 보장을 위해 @JsonProperty/@JsonCreator 추가하고, PathVariable 매핑 유지를 위해 아래 중 하나를 반드시 적용:
- Spring Converter<String, LikeTargetTypeRequest> 빈 등록(문자열 "card_set"을 CARD_SET으로 변환), 또는
- 컨트롤러에서 @PathVariable을 String으로 받고 내부에서 변환 처리.
- 참고: 저장소 검색 결과 상수명을 직접 참조한 곳은 enum 내부 switch 뿐이며(따라서 컴파일 에러는 발생하지 않음) 런타임의 PathVariable 매핑은 별도 처리 필요.
🧹 Nitpick comments (7)
src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java (1)
1-5: OpenAPI 다형성 스키마 노출 제안LikeControllerDocs에서 LikeResponse를 노출하므로, 베이스 타입에 oneOf로 서브타입을 명시하면 문서 가독성이 크게 개선됩니다.
아래와 같이 Swagger 어노테이션을 추가하는 것을 권장합니다(서브타입은 예시이며 실제 존재 타입으로 확장):
package project.flipnote.like.model.response; +import io.swagger.v3.oas.annotations.media.Schema; +import project.flipnote.like.model.response.CardSetLikeResponse; +@Schema(description = "좋아요 대상 응답의 베이스 타입", oneOf = {CardSetLikeResponse.class}) public abstract class LikeTargetResponse { public abstract Long getId(); }src/main/java/project/flipnote/like/controller/docs/LikeControllerDocs.java (1)
25-25: 제네릭 공변성 반영으로 문서/타입 안정성 개선서브타입을 반환할 수 있도록 와일드카드 사용을 권장합니다.
- ResponseEntity<PagingResponse<LikeResponse<LikeTargetResponse>>> getLikes( + ResponseEntity<PagingResponse<LikeResponse<? extends LikeTargetResponse>>> getLikes(src/main/java/project/flipnote/like/service/LikeReader.java (1)
18-21: 읽기 전용 트랜잭션 추가 권장조회 전용 메서드에 readOnly 트랜잭션을 부여하면 불필요한 flush 방지 및 성능/일관성에 도움이 됩니다.
package project.flipnote.like.service; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; ... public Like findByTargetAndUserId(LikeTargetType targetType, Long targetId, Long userId) { + @Transactional(readOnly = true) return likeRepository.findByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId) .orElseThrow(() -> new BizException(LikeErrorCode.LIKE_NOT_FOUND)); }src/main/java/project/flipnote/like/service/fetcher/LikeTargetFetcher.java (1)
12-15: 계약 명확화: 가시성/존재 판단과 일괄 조회의 책임 정의isTargetViewable와 fetchByIds의 동작 계약(존재/가시성 실패 시 반환/부재 처리)을 인터페이스 문서로 명시하면 구현/호출 일관성이 좋아집니다. 또한 fetchByIds가 “가시성 필터링된 결과만 반환하는지” 여부를 명확히 해 주세요.
public interface LikeTargetFetcher<T extends LikeTargetResponse> { LikeTargetType getTargetType(); - boolean isTargetViewable(Long targetId, Long userId); + /** + * 대상이 존재하고 사용자 컨텍스트에서 조회 가능하면 true. + * - 존재하지 않거나 비공개/권한 부족이면 false. + */ + boolean isTargetViewable(Long targetId, Long userId); - Map<Long, T> fetchByIds(Set<Long> targetIds, Long userId); + /** + * targetIds에 대해 사용자 컨텍스트를 고려한 조회 결과를 반환. + * - 반환 맵에는 조회 가능(존재 + 가시성 충족)한 대상만 포함하는 것을 권장. + * - 입력 ID 대비 누락된 키는 비존재/비가시성을 의미. + */ + Map<Long, T> fetchByIds(Set<Long> targetIds, Long userId); }src/main/java/project/flipnote/like/service/LikeTargetFetchService.java (2)
29-31: 중복 키 시 Collectors.toMap가 즉시 실패합니다 — 방어 코드 추가 권장.동일 LikeTargetType을 반환하는 페처가 2개 이상 등록되면 IllegalStateException이 발생합니다. 초기화 시점에서 명시적으로 실패시키는 편이 디버깅에 유리합니다.
아래처럼 병합 함수를 추가해 중복을 감지하며 명시적으로 예외를 던지세요.
- this.fetcherMap = this.fetchers.stream() - .collect(Collectors.toMap(LikeTargetFetcher::getTargetType, Function.identity())); + this.fetcherMap = this.fetchers.stream() + .collect(Collectors.toMap( + LikeTargetFetcher::getTargetType, + Function.identity(), + (a, b) -> { + throw new IllegalStateException( + "Duplicate LikeTargetFetcher for type: " + a.getTargetType() + ); + } + ));
49-56: 테스트 환경에서 @PostConstruct 미호출 대비.Spring 컨텍스트 없이 단위 테스트로 인스턴스화할 때 fetcherMap이 null이라 NPE가 납니다. 방어적으로 지연 초기화를 추가하는 것을 권장합니다.
다음 한 줄로 충분합니다.
private LikeTargetFetcher<?> getFetcher(LikeTargetType targetType) { + if (fetcherMap == null) init(); LikeTargetFetcher<?> fetcher = fetcherMap.get(targetType); if (fetcher == null) { throw new BizException(LikeErrorCode.INVALID_LIKE_TYPE); } return fetcher; }src/main/java/project/flipnote/like/service/LikeService.java (1)
104-107: 타겟 미존재/비가시성 케이스 처리 누락 가능성.fetchByTypeAndIds는 존재/가시성 기준으로 일부 ID를 제외해 반환할 수 있습니다. targetMap.get(...)이 null이면 LikeResponse 생성 시 NPE 또는 응답 스키마 누락이 발생할 수 있습니다. “누락 시 제외” 또는 “명시적 에러/플레이스홀더” 중 정책을 정해 처리하세요.
예시(정책에 따라 택일):
- 제외: Page를 리스트로 변환 후 null 필터링하여 PageImpl로 재조립(단, totalElements/페이지 크기 영향 고려).
- 실패: null 발견 시 BizException(LikeErrorCode.LIKE_TARGET_NOT_FOUND) 발생.
- 기록: 경고 로그 남기고 안전한 placeholder 응답 구성.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
src/main/java/project/flipnote/like/controller/LikeController.java(1 hunks)src/main/java/project/flipnote/like/controller/docs/LikeControllerDocs.java(1 hunks)src/main/java/project/flipnote/like/model/request/LikeSearchRequest.java(1 hunks)src/main/java/project/flipnote/like/model/request/LikeTargetTypeRequest.java(1 hunks)src/main/java/project/flipnote/like/model/response/CardSetLikeResponse.java(1 hunks)src/main/java/project/flipnote/like/model/response/LikeResponse.java(1 hunks)src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java(1 hunks)src/main/java/project/flipnote/like/service/LikePolicyService.java(1 hunks)src/main/java/project/flipnote/like/service/LikeReader.java(1 hunks)src/main/java/project/flipnote/like/service/LikeService.java(6 hunks)src/main/java/project/flipnote/like/service/LikeTargetFetchService.java(1 hunks)src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java(2 hunks)src/main/java/project/flipnote/like/service/fetcher/LikeTargetFetcher.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
src/main/java/project/flipnote/like/service/LikeReader.java (1)
src/main/java/project/flipnote/like/service/LikePolicyService.java (1)
RequiredArgsConstructor(13-31)
src/main/java/project/flipnote/like/controller/LikeController.java (1)
src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java (1)
LikeTargetResponse(3-5)
src/main/java/project/flipnote/like/controller/docs/LikeControllerDocs.java (1)
src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java (1)
LikeTargetResponse(3-5)
src/main/java/project/flipnote/like/service/LikeTargetFetchService.java (3)
src/main/java/project/flipnote/like/service/LikePolicyService.java (1)
RequiredArgsConstructor(13-31)src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java (1)
RequiredArgsConstructor(16-38)src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java (1)
LikeTargetResponse(3-5)
src/main/java/project/flipnote/like/service/LikePolicyService.java (3)
src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java (1)
LikeTargetResponse(3-5)src/main/java/project/flipnote/like/service/LikeTargetFetchService.java (1)
RequiredArgsConstructor(19-57)src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java (1)
RequiredArgsConstructor(16-38)
src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java (1)
src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java (1)
LikeTargetResponse(3-5)
src/main/java/project/flipnote/like/service/fetcher/LikeTargetFetcher.java (1)
src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java (1)
LikeTargetResponse(3-5)
src/main/java/project/flipnote/like/service/LikeService.java (1)
src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java (1)
LikeTargetResponse(3-5)
🔇 Additional comments (11)
src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java (2)
13-15: import 경로 변경 일관성 양호response 서브패키지로의 이동 반영이 올바릅니다.
28-30: 가시성 위임 메서드 추가 적절도메인 서비스로 위임하는
isTargetViewable추가 방향성 좋습니다. 서비스가 삭제/비공개/차단 등의 상태를 모두 포함해 false를 반환하는지 확인만 부탁드립니다.src/main/java/project/flipnote/like/model/response/LikeResponse.java (1)
1-1: 패키지 이동 LGTM — 구 FQN 참조만 재확인 필요
컨트롤러/문서의 제네릭 응답 타입과 일치함. 리포 검색 결과가 빈 출력이라 확증 불가하니 로컬에서 아래 명령을 실행해 구 경로 참조가 없는지 확인.
rg -nP -C2 'project.flipnote.like.model.LikeResponse'
rg -nP -C2 'project.flipnote.like.model.LikeTargetResponse'src/main/java/project/flipnote/like/model/request/LikeSearchRequest.java (1)
1-1: 패키지 분리 정돈 좋습니다 — 구 패키지 참조 없음 확인
project.flipnote.like.model.LikeSearchRequest 같은 이전 경로 참조는 발견되지 않았고, 현재는 project.flipnote.like.model.request 및 model.response 임포트만 사용되고 있습니다.src/main/java/project/flipnote/like/model/response/CardSetLikeResponse.java (1)
1-1: 패키지 이동 LGTM — 구 FQN 잔존 참조 없음project.flipnote.like.model.CardSetLikeResponse에 대한 정확한 FQN 검색 결과 없음; 선언과 사용은 모두 project.flipnote.like.model.response 패키지로 업데이트되어 있습니다.
src/main/java/project/flipnote/like/controller/LikeController.java (1)
18-21: import 경로 갱신 적절 — 구 패키지 미발견(자동 검색 결과)저장소 전체 검색(rg)에서 project.flipnote.like.model.* 경로가 발견되지 않았습니다. 문서(LikeControllerDocs)와 테스트가 동일 기준으로 갱신되었는지 수동으로 확인하고 CI/테스트를 실행하세요.
src/main/java/project/flipnote/like/model/response/LikeTargetResponse.java (1)
1-5: 패키지 이동 및 기본 형태 유지 LGTM공개 API의 패키지 재배치만 존재하며, 시그니처 변경 없음. 문제 없습니다.
src/main/java/project/flipnote/like/controller/docs/LikeControllerDocs.java (1)
10-13: import 경로 변경 적합request/response 서브패키지로 이동한 타입들에 맞춘 import 업데이트 적절합니다.
src/main/java/project/flipnote/like/service/LikeReader.java (1)
12-22: 크로스-파일: 잘못된 에러 코드 도메인 사용 가능성LikePolicyService.validateTargetExists에서 BookmarkErrorCode.BOOKMARK_TARGET_NOT_FOUND를 던지는 것으로 보입니다. Like 도메인에서 북마크 에러 코드를 사용하는 것은 응답 일관성과 추적성에 문제를 일으킬 수 있습니다. Like 전용 에러 코드 또는 공통 Not Found 코드 사용을 검토해 주세요.
문제 위치 확인 스크립트:
src/main/java/project/flipnote/like/service/LikeService.java (2)
50-50: 가시성 기반의 대상 검증 추가는 적절합니다.요청자 기준으로 보이는 대상만 좋아요 가능하도록 선검증하는 흐름이 명확합니다.
94-98: 반환 타입 변경(제네릭 제거)은 파급이 큽니다 — 상위 호출부/문서 확인 필요.PagingResponse<LikeResponse>로 고정되면서 기존 제네릭 기대치와 달라졌을 수 있습니다. 컨트롤러, 문서 스니펫, 프런트 소비 코드가 컴파일/스키마 레벨에서 깨지지 않는지 확인하세요.
| public void validateTargetExists(LikeTargetType targetType, Long targetId, Long userId) { | ||
| if (!likeTargetFetchService.isTargetViewable(targetType, targetId, userId)) { | ||
| throw new BizException(BookmarkErrorCode.BOOKMARK_TARGET_NOT_FOUND); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Like 도메인의 오류 코드를 사용하세요 (북마크 도메인으로 던짐).
가시성 검증 실패 시 BOOKMARK_TARGET_NOT_FOUND를 던지는 것은 도메인 불일치입니다. Like 흐름에서는 LikeErrorCode 쪽의 대상-부재 코드를 사용해야 API/문서 일관성이 유지됩니다. 또한 현재 메서드명은 “존재”를 암시하나, 실제로는 “가시성(뷰어블)”을 검증하므로 네이밍/주석 정비가 필요합니다.
다음과 같이 수정을 제안합니다(상수명은 기존 코드베이스에 맞춰 확인 필요합니다. 기존에 LIKE_TARGET_NOT_FOUND가 있었다면 그걸 사용하세요).
- if (!likeTargetFetchService.isTargetViewable(targetType, targetId, userId)) {
- throw new BizException(BookmarkErrorCode.BOOKMARK_TARGET_NOT_FOUND);
- }
+ if (!likeTargetFetchService.isTargetViewable(targetType, targetId, userId)) {
+ throw new BizException(LikeErrorCode.LIKE_TARGET_NOT_FOUND);
+ }추가로, 실질 검증이 “존재”가 아니라 “가시성”이므로 아래 중 하나를 권장합니다.
- 메서드명 변경: validateTargetViewable(...)
- 또는 Javadoc으로 “가시성(접근 가능성) 검증”임을 명시
🏁 Script executed:
#!/bin/bash
set -e
echo "Searching for BookmarkErrorCode occurrences..."
rg -n --hidden -S 'BookmarkErrorCode' || true
echo -e "\nSearching for LikeErrorCode occurrences..."
rg -n --hidden -S 'LikeErrorCode' || true
echo -e "\nSearching for LIKE_TARGET_NOT_FOUND or BOOKMARK_TARGET_NOT_FOUND..."
rg -n --hidden -S 'LIKE_TARGET_NOT_FOUND|BOOKMARK_TARGET_NOT_FOUND' || true
echo -e "\nSearching for validateTargetExists / validateTargetViewable..."
rg -n --hidden -S 'validateTargetExists|validateTargetViewable' || true
echo -e "\nLocating LikePolicyService.java..."
rg -n --hidden -S 'class\s+LikePolicyService' || true
if [ -f src/main/java/project/flipnote/like/service/LikePolicyService.java ]; then
echo -e "\n--- src/main/java/project/flipnote/like/service/LikePolicyService.java ---"
sed -n '1,240p' src/main/java/project/flipnote/like/service/LikePolicyService.java || true
fiLength of output: 5522
Like 도메인 오류 코드 사용 및 메서드명 정비
src/main/java/project/flipnote/like/service/LikePolicyService.java(라인 20-23): 가시성 검증 실패에 BookmarkErrorCode.BOOKMARK_TARGET_NOT_FOUND를 던지고 있음 — LikeErrorCode.LIKE_TARGET_NOT_FOUND로 교체하세요.
- if (!likeTargetFetchService.isTargetViewable(targetType, targetId, userId)) {
- throw new BizException(BookmarkErrorCode.BOOKMARK_TARGET_NOT_FOUND);
- }
+ if (!likeTargetFetchService.isTargetViewable(targetType, targetId, userId)) {
+ throw new BizException(LikeErrorCode.LIKE_TARGET_NOT_FOUND);
+ }메서드명은 '존재'가 아니라 '가시성(viewable)'을 검증하므로 validateTargetViewable(...)로 변경하거나 Javadoc으로 가시성 검증임을 명시하세요.
📝 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.
| public void validateTargetExists(LikeTargetType targetType, Long targetId, Long userId) { | |
| if (!likeTargetFetchService.isTargetViewable(targetType, targetId, userId)) { | |
| throw new BizException(BookmarkErrorCode.BOOKMARK_TARGET_NOT_FOUND); | |
| } | |
| public void validateTargetExists(LikeTargetType targetType, Long targetId, Long userId) { | |
| if (!likeTargetFetchService.isTargetViewable(targetType, targetId, userId)) { | |
| throw new BizException(LikeErrorCode.LIKE_TARGET_NOT_FOUND); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/project/flipnote/like/service/LikePolicyService.java around
lines 20-23, the method validateTargetExists throws
BookmarkErrorCode.BOOKMARK_TARGET_NOT_FOUND on visibility check failure; change
the thrown error to LikeErrorCode.LIKE_TARGET_NOT_FOUND and rename the method to
validateTargetViewable (or, if you prefer not to rename, add a Javadoc
clarifying it validates "viewable" not mere existence). Update the method
signature, its usages across the codebase (calls/tests), and any imports so they
compile, and run unit tests to ensure all references are adjusted.
| public class LikeTargetFetchService<T extends LikeTargetResponse> { | ||
|
|
||
| private final List<LikeTargetFetcher<T>> fetchers; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제네릭 불변성으로 인한 Spring 주입 실패 가능성 (치명적).
List<LikeTargetFetcher>는 제네릭 불변(invariant)이라 LikeTargetFetcher가 LikeTargetFetcher에 주입되지 않습니다. 현재 LikeService/LikePolicyService에서 LikeTargetFetchService를 주입하고 있어, 실제로는 컨텍스트 로딩 시점에 주입 실패가 발생할 확률이 높습니다.
아래처럼 서비스의 제네릭을 제거하고, 내부에서 안전한(경고 억제) 캐스팅을 사용하면 주입 문제가 해소됩니다.
-@RequiredArgsConstructor
-@Service
-public class LikeTargetFetchService<T extends LikeTargetResponse> {
+@RequiredArgsConstructor
+@Service
+public class LikeTargetFetchService {
- private final List<LikeTargetFetcher<T>> fetchers;
+ private final List<LikeTargetFetcher<?>> fetchers;
- private Map<LikeTargetType, LikeTargetFetcher<T>> fetcherMap;
+ private Map<LikeTargetType, LikeTargetFetcher<?>> fetcherMap;
@PostConstruct
public void init() {
- this.fetcherMap = this.fetchers.stream()
- .collect(Collectors.toMap(LikeTargetFetcher::getTargetType, Function.identity()));
+ this.fetcherMap = this.fetchers.stream()
+ .collect(Collectors.toMap(LikeTargetFetcher::getTargetType, Function.identity()));
}
public boolean isTargetViewable(LikeTargetType targetType, Long targetId, Long userId) {
- LikeTargetFetcher<T> targetFetcher = getFetcher(targetType);
+ LikeTargetFetcher<?> targetFetcher = getFetcher(targetType);
return targetFetcher.isTargetViewable(targetId, userId);
}
- public Map<Long, T> fetchByTypeAndIds(
+ public <T extends LikeTargetResponse> Map<Long, T> fetchByTypeAndIds(
LikeTargetType targetType,
Set<Long> targetIds,
Long userId
) {
- LikeTargetFetcher<T> targetFetcher = getFetcher(targetType);
- return targetFetcher.fetchByIds(targetIds, userId);
+ @SuppressWarnings("unchecked")
+ LikeTargetFetcher<T> targetFetcher = (LikeTargetFetcher<T>) getFetcher(targetType);
+ return targetFetcher.fetchByIds(targetIds, userId);
}
- private LikeTargetFetcher<T> getFetcher(LikeTargetType targetType) {
- LikeTargetFetcher<T> fetcher = fetcherMap.get(targetType);
+ private LikeTargetFetcher<?> getFetcher(LikeTargetType targetType) {
+ LikeTargetFetcher<?> fetcher = fetcherMap.get(targetType);
if (fetcher == null) {
throw new BizException(LikeErrorCode.INVALID_LIKE_TYPE);
}
return fetcher;
}
}이 변경에 맞춰 LikeService/LikePolicyService의 필드 타입도 LikeTargetFetchService (비제네릭)로 조정해야 합니다.
Also applies to: 39-47, 49-56
🤖 Prompt for AI Agents
In src/main/java/project/flipnote/like/service/LikeTargetFetchService.java
around lines 21-24 (and similarly at 39-47, 49-56), the class is declared with a
generic parameter T which makes the injected List<LikeTargetFetcher<T>>
invariant and prevents Spring from injecting implementations like
LikeTargetFetcher<CardSetLikeResponse>; remove the generic from the service
class declaration, change the internal fetchers field to use a wildcard or raw
type (e.g. List<LikeTargetFetcher<?>> or List) and perform any necessary
unchecked casts inside methods with @SuppressWarnings("unchecked") where you
return or operate on a specific LikeTargetResponse subtype, and then update
callers (LikeService and LikePolicyService) to inject LikeTargetFetchService
(non-generic) instead of LikeTargetFetchService<LikeTargetResponse>.
📝 변경 내용
✅ 체크리스트
💬 기타 참고 사항
Summary by CodeRabbit