-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/#61 시간대별 랜덤 장소 추천 기능(홈화면) 구현 #62
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
Walkthrough시간대별 장소 추천 기능을 추가했습니다. Redis 캐시 서비스, 장소/파트너십 DTO 및 매퍼, PlaceService의 추천 흐름과 컨트롤러 엔드포인트, StudentCouncilPostRepository의 파트너십 조회 쿼리가 도입되었으며 일부 메서드 시그니처·포맷팅이 변경되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant PlaceController
participant PlaceService
participant RedisPlaceCacheService
participant GooglePlaceClient
participant StudentCouncilPostRepository
participant PlaceMapper
User->>PlaceController: GET /places/random (userId, lat, lng)
PlaceController->>PlaceService: findRecommendations(userId, lat, lng)
PlaceService->>RedisPlaceCacheService: getCachedPlaces(lat, lng, keyword)
alt Cache Hit
RedisPlaceCacheService-->>PlaceService: List<SavedPlaceInfo>
else Cache Miss
PlaceService->>GooglePlaceClient: searchByLocationAndKeyword(lat, lng, keyword, imageLimit=3)
GooglePlaceClient-->>PlaceService: List<SavedPlaceInfo>
PlaceService->>RedisPlaceCacheService: cachePlaces(keyword, lat, lng, places)
end
PlaceService->>StudentCouncilPostRepository: findRandomPartnershipPlace(schoolId,collegeId,majorId,icon,now,pageable)
StudentCouncilPostRepository-->>PlaceService: List<StudentCouncilPost>
PlaceService->>PlaceMapper: toRecommendPartnershipPlaceResponse/post, toRecommendNearByPlaceResponse/savedPlace
PlaceMapper-->>PlaceService: RecommendPartnershipPlaceResponse / RecommendNearByPlaceResponse
PlaceService->>PlaceMapper: toRecommendPlaceByTimeResponse(type, partnerships, nearbyPlaces)
PlaceMapper-->>PlaceService: RecommendPlaceByTimeResponse
PlaceService-->>PlaceController: RecommendPlaceByTimeResponse
PlaceController-->>User: CommonResponse<RecommendPlaceByTimeResponse>
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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
🤖 Fix all issues with AI agents
In
@src/main/java/com/campus/campus/domain/place/application/dto/response/RecommendPlaceByTimeResponse.java:
- Around line 7-16: The @Schema on the RecommendPlaceByTimeResponse record's
type field is inaccurate; update its description (and example if present) to
match the actual values emitted by PlaceService (e.g., "LUNCH", "CAFE",
"DINNER", "BAR", and "잠잘시간입니다.") or alternatively change PlaceService to emit
only the documented values—ensure the documented enum/list in
RecommendPlaceByTimeResponse.type matches PlaceService behavior so API docs and
runtime are consistent.
In
@src/main/java/com/campus/campus/domain/place/application/service/PlaceService.java:
- Around line 324-338: Boundary checks for meal/time windows are inconsistent
causing overlaps (e.g., 17:00 true for both isCafeTime and isDinnerTime); change
all end-bound checks to use half-open interval semantics [start, end) by
replacing "!now.isAfter(…)" with "now.isBefore(…)" in isCafeTime, isDinnerTime,
and isBarTime so they match isLunchTime’s logic (which already uses isBefore for
LUNCH_END) and avoid inclusive end overlaps.
In
@src/main/java/com/campus/campus/domain/place/application/service/RedisPlaceCacheService.java:
- Around line 24-25: GRID_PRECISION is set to 1000.0 which rounds coordinates to
3 decimal places but the PR requires rounding to 4 decimal places; update the
constant GRID_PRECISION in RedisPlaceCacheService to 10000.0 so methods that use
GRID_PRECISION (e.g., any coordinate rounding logic in RedisPlaceCacheService)
will round to four decimal places as documented.
🧹 Nitpick comments (3)
src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java (1)
286-304:@EntityGraph와JOIN FETCH중복 사용 및 하드코딩된 문자열 리터럴 검토 필요
중복 페칭:
@EntityGraph(attributePaths = {"writer", ..., "place"})와JOIN FETCH p.writer w,JOIN FETCH p.place pl이 동시에 사용되고 있습니다.@EntityGraph만으로 충분하므로JOIN FETCH를 일반JOIN으로 변경하는 것을 권장합니다.하드코딩된 리터럴:
'PARTNERSHIP','SCHOOL_COUNCIL'등이 문자열로 하드코딩되어 있습니다. 이 파일의 다른 메서드들(findRandomActivePartnerships등)처럼 enum 파라미터를 사용하는 것이 유지보수성과 일관성 면에서 더 좋습니다.♻️ 개선 제안
@EntityGraph(attributePaths = {"writer", "writer.school", "writer.college", "writer.major", "place"}) @Query(""" SELECT p FROM StudentCouncilPost p - JOIN FETCH p.writer w - JOIN FETCH p.place pl + JOIN p.writer w + JOIN p.place pl LEFT JOIN w.school s LEFT JOIN w.college c LEFT JOIN w.major m - WHERE p.category = 'PARTNERSHIP' + WHERE p.category = :category AND p.thumbnailIcon = :icon AND :now BETWEEN p.startDateTime AND p.endDateTime AND w.deletedAt IS NULL AND ( - (w.councilType = 'SCHOOL_COUNCIL' AND s.schoolId = :schoolId) - OR (w.councilType = 'COLLEGE_COUNCIL' AND c.collegeId = :collegeId) - OR (w.councilType = 'MAJOR_COUNCIL' AND m.majorId = :majorId) + (w.councilType = :schoolType AND s.schoolId = :schoolId) + OR (w.councilType = :collegeType AND c.collegeId = :collegeId) + OR (w.councilType = :majorType AND m.majorId = :majorId) ) ORDER BY function('RAND') """) List<StudentCouncilPost> findRandomPartnershipPlace( @Param("schoolId") Long schoolId, @Param("collegeId") Long collegeId, @Param("majorId") Long majorId, @Param("icon") ThumbnailIcon icon, + @Param("category") PostCategory category, + @Param("schoolType") CouncilType schoolType, + @Param("collegeType") CouncilType collegeType, + @Param("majorType") CouncilType majorType, @Param("now") LocalDateTime now, Pageable pageable );src/main/java/com/campus/campus/domain/place/presentation/PlaceController.java (1)
196-206: @parameter 어노테이션 누락으로 API 문서 불완전다른 엔드포인트들(
/search,/partnership등)과 달리lat,lng파라미터에@Parameter어노테이션이 없어 Swagger 문서의 일관성이 떨어집니다.📝 제안 수정안
@GetMapping("/random") @Operation(summary = "시간대별 랜덤 장소 추천 (제휴 장소 2, 랜덤 장소 2) (홈화면)") public CommonResponse<RecommendPlaceByTimeResponse> getRandomPlaceByTime( @CurrentUserId Long userId, + @Parameter(description = "현재 위치의 위도", example = "37.50415") @RequestParam double lat, + @Parameter(description = "현재 위치의 경도", example = "126.9570") @RequestParam double lng ) {src/main/java/com/campus/campus/domain/place/application/service/PlaceService.java (1)
196-198: API 응답 type 값의 일관성 문제추천 시간대가 아닐 때 "잠잘시간입니다."라는 한글 문장을
type필드에 반환하고 있습니다. 다른 값들(LUNCH, CAFE, DINNER, BAR)이 enum 스타일의 영문 상수인 것과 비교하면 일관성이 없어 API 소비자가 파싱하기 어렵습니다.📝 제안 수정안
} else { - return placeMapper.toRecommendPlaceByTimeResponse("잠잘시간입니다.", List.of(), List.of()); + return placeMapper.toRecommendPlaceByTimeResponse("NONE", List.of(), List.of()); }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.javasrc/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.javasrc/main/java/com/campus/campus/domain/place/application/dto/response/RecommendNearByPlaceResponse.javasrc/main/java/com/campus/campus/domain/place/application/dto/response/RecommendPartnershipPlaceResponse.javasrc/main/java/com/campus/campus/domain/place/application/dto/response/RecommendPlaceByTimeResponse.javasrc/main/java/com/campus/campus/domain/place/application/mapper/PlaceMapper.javasrc/main/java/com/campus/campus/domain/place/application/service/PlaceService.javasrc/main/java/com/campus/campus/domain/place/application/service/RedisPlaceCacheService.javasrc/main/java/com/campus/campus/domain/place/infrastructure/google/GooglePlaceClient.javasrc/main/java/com/campus/campus/domain/place/presentation/PlaceController.javasrc/main/java/com/campus/campus/domain/place/presentation/PlaceResponseCode.java
🧰 Additional context used
🧬 Code graph analysis (5)
src/main/java/com/campus/campus/domain/place/application/dto/response/RecommendPartnershipPlaceResponse.java (5)
src/main/java/com/campus/campus/domain/place/application/dto/response/partnership/PartnershipResponse.java (1)
PartnershipResponse(6-24)src/main/java/com/campus/campus/domain/place/application/dto/response/PartnershipPinResponse.java (1)
PartnershipPinResponse(5-22)src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetActivePartnershipListForUserResponse.java (1)
GetActivePartnershipListForUserResponse(5-18)src/main/java/com/campus/campus/domain/place/application/dto/response/SavedPlaceInfo.java (1)
SavedPlaceInfo(10-41)src/main/java/com/campus/campus/domain/place/application/dto/response/google/GooglePlaceDetailResponse.java (1)
GooglePlaceDetailResponse(5-12)
src/main/java/com/campus/campus/domain/place/presentation/PlaceResponseCode.java (6)
src/main/java/com/campus/campus/domain/councilnotice/presentation/NoticeResponseCode.java (1)
Getter(10-23)src/main/java/com/campus/campus/domain/place/application/exception/ErrorCode.java (1)
Getter(10-24)src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostResponseCode.java (1)
Getter(10-27)src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java (1)
Getter(10-21)src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilResponseCode.java (1)
Getter(10-26)src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java (1)
Getter(10-22)
src/main/java/com/campus/campus/domain/place/application/mapper/PlaceMapper.java (2)
src/main/java/com/campus/campus/domain/place/application/service/PartnershipPlaceService.java (1)
entry(112-128)src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (2)
toGetActivePartnershipListForUserResponse(72-79)toGetLikedPostResponse(169-178)
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java (3)
src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
UserNotFoundException(5-9)src/main/java/com/campus/campus/domain/councilpost/domain/entity/PostCategory.java (2)
Override(14-23)Override(27-36)src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java (1)
GetMapping(89-118)
src/main/java/com/campus/campus/domain/place/application/service/PlaceService.java (3)
src/main/java/com/campus/campus/domain/place/application/service/RedisPlaceCacheService.java (1)
Service(17-68)src/main/java/com/campus/campus/domain/councilpost/application/exception/AcademicInfoNotSetException.java (1)
AcademicInfoNotSetException(5-9)src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
UserNotFoundException(5-9)
⏰ 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
🔇 Additional comments (10)
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java (1)
110-141: LGTM!메서드 시그니처와 호출부의 줄바꿈 포맷팅만 변경되었으며, 기능적 변경은 없습니다.
src/main/java/com/campus/campus/domain/place/presentation/PlaceResponseCode.java (1)
17-18: LGTM!새로운
GET_RANDOM_PLACE_SUCCESS응답 코드가 기존 패턴에 맞게 추가되었습니다. 시간대별 랜덤 장소 추천 기능의 성공 응답에 적합합니다.src/main/java/com/campus/campus/domain/place/application/dto/response/RecommendPartnershipPlaceResponse.java (1)
5-23: LGTM!제휴 장소 추천 응답을 위한 DTO가 적절하게 정의되었습니다. Swagger 문서화를 위한
@Schema어노테이션도 잘 적용되어 있습니다.src/main/java/com/campus/campus/domain/place/infrastructure/google/GooglePlaceClient.java (1)
54-76: LGTM!
imageLimit파라미터 도입으로 이미지 개수 제한을 호출자가 제어할 수 있게 되었습니다. 기존 하드코딩된 값에서 유연한 구조로 개선되었습니다.src/main/java/com/campus/campus/domain/place/application/service/RedisPlaceCacheService.java (1)
27-67: 캐시 서비스 구현 적절Redis 캐시 로직이 잘 구현되어 있습니다:
null/빈 리스트 가드 처리- JSON 직렬화/역직렬화 예외 처리
- 적절한 로깅 레벨 사용 (성공 시 info, 실패 시 warn)
src/main/java/com/campus/campus/domain/place/application/dto/response/RecommendNearByPlaceResponse.java (1)
10-38: LGTM!주변 장소 추천 응답 DTO가 적절히 정의되었습니다.
SavedPlaceInfo와 필드 구조가 유사한데, 응답 DTO를 별도로 분리한 것은 API 계층 분리 관점에서 좋은 접근입니다.src/main/java/com/campus/campus/domain/place/application/mapper/PlaceMapper.java (2)
92-104: LGTM!
SavedPlaceInfo를RecommendNearByPlaceResponse로 매핑하는 로직이 명확하고, 기존 매핑 패턴과 일관성이 있습니다.
81-90: The code is safe from NullPointerException. ThefindRandomPartnershipPlacequery explicitly usesJOIN FETCH p.writerandJOIN FETCH p.place, which are INNER JOINs that guarantee both the writer and place are present. FETCH ensures eager loading, preventing any initialization issues. No defensive null checks are needed.Likely an incorrect or invalid review comment.
src/main/java/com/campus/campus/domain/place/application/service/PlaceService.java (2)
363-394: LGTM! Redis 캐싱과 랜덤 선택 로직Redis 캐시를 활용한 외부 API 호출 최소화 전략이 잘 구현되어 있습니다.
ArrayList로 복사 후shuffle하여 불변 리스트 문제도 방지했습니다.
349-361: Partnership 쿼리에서 랜덤 정렬 확인 완료쿼리가
ORDER BY function('RAND')를 통해 실제로 랜덤 정렬을 수행하고 있습니다.PageRequest.of(0, 2)는 랜덤 정렬 후 결과를 2개로 제한하는 것이므로 올바르게 구현되어 있습니다.
| public record RecommendPlaceByTimeResponse( | ||
| @Schema(description = "추천 타입 (LUNCH: 점심, CAFE: 카페, NONE: 해당 시간 아님)", example = "LUNCH") | ||
| String type, | ||
|
|
||
| @Schema(description = "추천 제휴 게시글 (최대 2개)") | ||
| List<RecommendPartnershipPlaceResponse> partnershipPosts, | ||
|
|
||
| @Schema(description = "추천 주변 장소 (최대 2개)") | ||
| List<RecommendNearByPlaceResponse> nearbyPlaces | ||
| ) { |
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.
@Schema 설명과 실제 사용 값 불일치
@Schema description에 "LUNCH: 점심, CAFE: 카페, NONE: 해당 시간 아님"으로 명시되어 있으나, PlaceService에서 실제로 사용하는 값은 "LUNCH", "CAFE", "DINNER", "BAR", "잠잘시간입니다."입니다. API 문서와 실제 동작이 일치하도록 수정이 필요합니다.
📝 제안 수정안
public record RecommendPlaceByTimeResponse(
- @Schema(description = "추천 타입 (LUNCH: 점심, CAFE: 카페, NONE: 해당 시간 아님)", example = "LUNCH")
+ @Schema(description = "추천 타입 (LUNCH: 점심, CAFE: 카페, DINNER: 저녁, BAR: 술집, 또는 추천 시간대 아님)", example = "LUNCH")
String type,📝 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 record RecommendPlaceByTimeResponse( | |
| @Schema(description = "추천 타입 (LUNCH: 점심, CAFE: 카페, NONE: 해당 시간 아님)", example = "LUNCH") | |
| String type, | |
| @Schema(description = "추천 제휴 게시글 (최대 2개)") | |
| List<RecommendPartnershipPlaceResponse> partnershipPosts, | |
| @Schema(description = "추천 주변 장소 (최대 2개)") | |
| List<RecommendNearByPlaceResponse> nearbyPlaces | |
| ) { | |
| public record RecommendPlaceByTimeResponse( | |
| @Schema(description = "추천 타입 (LUNCH: 점심, CAFE: 카페, DINNER: 저녁, BAR: 술집, 또는 추천 시간대 아님)", example = "LUNCH") | |
| String type, | |
| @Schema(description = "추천 제휴 게시글 (최대 2개)") | |
| List<RecommendPartnershipPlaceResponse> partnershipPosts, | |
| @Schema(description = "추천 주변 장소 (최대 2개)") | |
| List<RecommendNearByPlaceResponse> nearbyPlaces | |
| ) { |
🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/place/application/dto/response/RecommendPlaceByTimeResponse.java
around lines 7 - 16, The @Schema on the RecommendPlaceByTimeResponse record's
type field is inaccurate; update its description (and example if present) to
match the actual values emitted by PlaceService (e.g., "LUNCH", "CAFE",
"DINNER", "BAR", and "잠잘시간입니다.") or alternatively change PlaceService to emit
only the documented values—ensure the documented enum/list in
RecommendPlaceByTimeResponse.type matches PlaceService behavior so API docs and
runtime are consistent.
src/main/java/com/campus/campus/domain/place/application/service/PlaceService.java
Show resolved
Hide resolved
jjaeroong
left a 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.
고생하셨습니다!
...ava/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java
Show resolved
Hide resolved
...ava/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java
Show resolved
Hide resolved
…서 최신 15개만 가져와 service 계층에서 랜덤 처리 방식으로 수정
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: 1
🤖 Fix all issues with AI agents
In
@src/main/java/com/campus/campus/domain/place/application/service/PlaceService.java:
- Around line 64-72: The BAR_END constant in PlaceService is set to 23:30 but
should be 23:00 per PR requirements; update the BAR_END declaration (static
final ZoneId KST remains) to LocalTime.of(23, 0) so the bar recommendation
window becomes 20:00–23:00, and run related tests or usages that rely on
BAR_START/BAR_END to verify behavior.
🧹 Nitpick comments (3)
src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java (2)
305-312: 메서드 이름이 실제 동작과 일치하지 않습니다.
findRandomPartnershipPlace라는 이름이지만 쿼리에서는ORDER BY p.id DESC로 정렬만 수행하고 랜덤 선택은 하지 않습니다. PR 설명에 따르면 랜덤 선택이 서비스 레이어로 이동된 것으로 보이므로, 메서드 이름을findPartnershipPlacesByIcon또는findLatestPartnershipPlaces와 같이 실제 동작을 반영하도록 변경하는 것이 좋습니다.
294-302: Enum 값 처리 방식이 기존 메서드와 일관성이 없습니다.이 파일의 다른 메서드들은 enum 비교 시 파라미터를 사용합니다:
findRandomActivePartnerships::category,:councilType파라미터 사용findByUserScopeWithCursor::majorType,:collegeType,:schoolType파라미터 사용하지만
findRandomPartnershipPlace메서드는'PARTNERSHIP','SCHOOL_COUNCIL'등의 문자열 리터럴을 직접 사용합니다. 비록@Enumerated(EnumType.STRING)설정으로 인해 기술적으로는 동작하지만, 이 방식은 enum 값 이름이 변경되면 컴파일 타임에 오류를 감지할 수 없으며 코드베이스 전체의 패턴과도 맞지 않습니다.파라미터 기반 접근 방식으로 통일하여 유지보수성과 타입 안정성을 높여 주세요.
♻️ 파라미터 기반 접근 방식 제안
@EntityGraph(attributePaths = {"writer", "writer.school", "writer.college", "writer.major", "place"}) @Query(""" SELECT p FROM StudentCouncilPost p JOIN p.writer w JOIN p.place pl LEFT JOIN w.school s LEFT JOIN w.college c LEFT JOIN w.major m - WHERE p.category = 'PARTNERSHIP' + WHERE p.category = :category AND p.thumbnailIcon = :icon AND :now BETWEEN p.startDateTime AND p.endDateTime AND w.deletedAt IS NULL AND ( - (w.councilType = 'SCHOOL_COUNCIL' AND s.schoolId = :schoolId) - OR (w.councilType = 'COLLEGE_COUNCIL' AND c.collegeId = :collegeId) - OR (w.councilType = 'MAJOR_COUNCIL' AND m.majorId = :majorId) + (w.councilType = :schoolType AND s.schoolId = :schoolId) + OR (w.councilType = :collegeType AND c.collegeId = :collegeId) + OR (w.councilType = :majorType AND m.majorId = :majorId) ) ORDER BY p.id DESC """) List<StudentCouncilPost> findRandomPartnershipPlace( @Param("schoolId") Long schoolId, @Param("collegeId") Long collegeId, @Param("majorId") Long majorId, + @Param("category") PostCategory category, + @Param("schoolType") CouncilType schoolType, + @Param("collegeType") CouncilType collegeType, + @Param("majorType") CouncilType majorType, @Param("icon") ThumbnailIcon icon, @Param("now") LocalDateTime now, Pageable pageable );src/main/java/com/campus/campus/domain/place/application/service/PlaceService.java (1)
372-384: Redis 캐시 메서드의 파라미터 순서가 일관성이 없습니다.
getCachedPlaces(lat, lng, keyword)와cachePlaces(keyword, lat, lng, places)의 파라미터 순서가 다릅니다. 내부적으로generateKey가 동일한 순서를 사용하므로 동작에는 문제가 없지만, API 일관성을 위해RedisPlaceCacheService의 메서드 시그니처를 통일하는 것이 좋습니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.javasrc/main/java/com/campus/campus/domain/place/application/service/PlaceService.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/campus/campus/domain/place/application/service/PlaceService.java (3)
src/main/java/com/campus/campus/domain/place/application/service/RedisPlaceCacheService.java (1)
Service(17-68)src/main/java/com/campus/campus/domain/councilpost/application/exception/AcademicInfoNotSetException.java (1)
AcademicInfoNotSetException(5-9)src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
UserNotFoundException(5-9)
🔇 Additional comments (7)
src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java (1)
17-17: LGTM!새로운 메서드 파라미터로 사용되는
ThumbnailIconimport가 올바르게 추가되었습니다.src/main/java/com/campus/campus/domain/place/application/service/PlaceService.java (6)
178-199: 시간대별 추천 로직이 잘 구현되어 있습니다.타임존 처리와 사용자 검증 로직이 적절합니다. 다만, 23:00(또는 23:30)
11:30 사이의 기본 메시지 "잠잘시간입니다"가 이른 아침 시간(예: 611시)에 사용자에게 어색하게 느껴질 수 있습니다. 추후 UX 개선 시 고려해 보세요.
324-338: 시간대 헬퍼 메서드가 올바르게 구현되었습니다.경계값 처리 (
START <= now < END)가 일관되게 적용되어 있어 시간대 전환 시점에서의 동작이 명확합니다.
340-347: LGTM!협력업체 장소와 주변 장소를 조합하여 응답을 생성하는 로직이 깔끔합니다.
201-243: 이미지 제한 파라미터 전파와 비동기 처리가 적절합니다.
imageLimit파라미터가 메서드 체인을 통해 올바르게 전달되고 있으며,CompletableFuture의 타임아웃 및 폴백 처리가 잘 구현되어 있습니다.
390-402: LGTM!셔플을 위한 mutable 리스트 복사와 이미지 URL null 처리가 안전하게 구현되어 있습니다.
349-361: 학교 정보 null 체크는 불필요합니다.
findRecommendations()메서드의 184-186줄에서user.isProfileNotCompleted()검증이 수행됩니다.isProfileNotCompleted()는school == null || major == null을 반환하므로, 이 검증을 통과한 후generateResponse()→getRandomPartnerships()가 호출될 때 school은 반드시 null이 아닙니다. 따라서 NPE 위험은 없으며, 현재 코드는 안전합니다.Likely an incorrect or invalid review 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: 0
🧹 Nitpick comments (2)
src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java (2)
317-344: 메서드 이름이 실제 동작과 불일치합니다.
findRandomPartnershipPlace라는 이름은 랜덤 결과를 반환할 것처럼 보이지만, 실제 쿼리는ORDER BY p.id DESC로 최신순 정렬만 수행합니다. PR 설명에 따르면 랜덤 선택은 서비스 레이어에서 처리되므로, 메서드 이름을 실제 동작에 맞게 수정하는 것이 좋습니다.♻️ 메서드 이름 변경 제안
- List<StudentCouncilPost> findRandomPartnershipPlace( + List<StudentCouncilPost> findLatestPartnershipPlacesByIcon( @Param("schoolId") Long schoolId, @Param("collegeId") Long collegeId, @Param("majorId") Long majorId, @Param("icon") ThumbnailIcon icon, @Param("now") LocalDateTime now, Pageable pageable );
326-334: JPQL에서 문자열 리터럴 대신 enum 참조 또는 파라미터 사용을 권장합니다.동일 파일 내 다른 메서드들(lines 57-60, 297-302)은 fully qualified enum 참조(
com.campus.campus.domain.councilpost.domain.entity.PostCategory.PARTNERSHIP등)를 사용하거나 파라미터(:category)를 사용합니다. 문자열 리터럴은 오타 발생 시 컴파일 타임에 감지되지 않고 런타임 오류로 이어질 수 있습니다.♻️ enum 참조 또는 파라미터 사용 제안
WHERE p.category = 'PARTNERSHIP' AND p.thumbnailIcon = :icon AND :now BETWEEN p.startDateTime AND p.endDateTime AND w.deletedAt IS NULL AND ( - (w.councilType = 'SCHOOL_COUNCIL' AND s.schoolId = :schoolId) - OR (w.councilType = 'COLLEGE_COUNCIL' AND c.collegeId = :collegeId) - OR (w.councilType = 'MAJOR_COUNCIL' AND m.majorId = :majorId) + (w.councilType = com.campus.campus.domain.council.domain.entity.CouncilType.SCHOOL_COUNCIL AND s.schoolId = :schoolId) + OR (w.councilType = com.campus.campus.domain.council.domain.entity.CouncilType.COLLEGE_COUNCIL AND c.collegeId = :collegeId) + OR (w.councilType = com.campus.campus.domain.council.domain.entity.CouncilType.MAJOR_COUNCIL AND m.majorId = :majorId) )또는 기존
findByUserScopeWithCursor메서드(lines 237-248)처럼 파라미터로 전달:List<StudentCouncilPost> findLatestPartnershipPlacesByIcon( @Param("schoolId") Long schoolId, @Param("collegeId") Long collegeId, @Param("majorId") Long majorId, @Param("icon") ThumbnailIcon icon, @Param("now") LocalDateTime now, @Param("schoolType") CouncilType schoolType, @Param("collegeType") CouncilType collegeType, @Param("majorType") CouncilType majorType, Pageable pageable );
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.javasrc/main/java/com/campus/campus/domain/user/application/service/KakaoOauthService.java
✅ Files skipped from review due to trivial changes (1)
- src/main/java/com/campus/campus/domain/user/application/service/KakaoOauthService.java
⏰ 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
🔀 변경 내용
✅ 작업 항목
오전 11시 30분 - 오후 2시: 밥집 추천
오후 2시 - 오후 5시: 카페 추천
오후 5시 - 오후 8시: 밥집 추천
오후 8시 - 오후 11시: 술집 추천
추가적으로 레디스 캐시를 적용해 랜덤 장소 검색 시 Naver/Google API 호출 수를 줄이고자 했습니다.
좌표의 경우 소수점 3자리에 반올림을 진행하며 redis에 후보군 리스트를 고정합니다. 그 후 1시간 동안은 사용자가 새로고침할 때마다 고정된 해당 리스트 안에서 랜덤으로 2개를 뽑아서 보여주게 됩니다.
이렇게 할 경우 가까운 위치에 있는 유저들이 해당 기능을 사용하게 되면 불필요한 외부 api의 호출을 줄일 수 있게 됩니다.
📸 스크린샷 (선택)
시간대별 랜덤 장소 추천 기능 호출 시 다음과 같이 제휴가 등록된 장소 2개와 현재 위치를 기반으로 한 장소 2개가 랜덤으로 검색되어 한번에 조회됩니다.
📎 참고 이슈
관련 이슈 번호 #61
Summary by CodeRabbit
새로운 기능
성능 개선
✏️ Tip: You can customize this high-level summary in your review settings.