Refactor: Like 도메인 리팩토링#50
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.
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.
🧩 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.
제네릭 불변성으로 인한 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