diff --git a/docs/20260328_콘텐츠조회파라미터제거및비로그인기본값고정.md b/docs/20260328_콘텐츠조회파라미터제거및비로그인기본값고정.md new file mode 100644 index 00000000..7caad73a --- /dev/null +++ b/docs/20260328_콘텐츠조회파라미터제거및비로그인기본값고정.md @@ -0,0 +1,44 @@ +# 20260328 콘텐츠 조회 파라미터 제거 및 비로그인 기본값 고정 + +## 목적 +- 모든 API에서 `isAdultContentVisible`, `contentType` 요청 파라미터를 제거한다. +- 비로그인 사용자는 항상 `isAdultContentVisible = false`, `contentType = ContentType.ALL`로 처리한다. +- 로그인 사용자는 기존과 동일하게 `MemberContentPreference` 기반 로직을 유지한다. + +## 구현 체크리스트 +- [x] `isAdultContentVisible`, `contentType`를 받는 잔여 API 시그니처를 모두 제거한다. + - QA: `grep("@RequestParam(\"isAdultContentVisible\"|@RequestParam(\"contentType\")")` 결과가 0인지 확인 +- [x] 연관 서비스 메서드 시그니처/호출부를 정리한다. + - QA: `compileKotlin` 성공으로 시그니처 불일치가 없는지 확인 +- [x] 비로그인 기본값을 `false/ALL`로 고정한다. + - QA: 익명 분기 `ViewerContentPreference(false/ALL)` 코드 확인 + 관련 테스트 통과 +- [x] 로그인 분기는 기존 `memberContentPreferenceService.resolveForQuery(member = member)` 흐름을 유지한다. + - QA: 관련 컨트롤러/서비스에서 로그인 분기 호출 유지 확인 +- [x] 회귀 검증을 수행한다. + - QA: `./gradlew test`, `./gradlew build` 성공 + +## 구현 완료 후 기록 +### 1차 구현 +- 무엇을: + - 잔여 API 파라미터를 전부 제거했다. + - `HomeController`, `LiveApiController`, `LiveRoomController`, `AudioContentController`, `AudioContentMainTabHomeController` + - 연관 서비스 시그니처와 호출부를 정리했다. + - `HomeService`, `LiveApiService`, `LiveRoomService` + - 비로그인 분기 기본값을 `ViewerContentPreference(isAdultContentVisible = false, contentType = ContentType.ALL, isAdult = false)`로 고정했다. +- 왜: + - 요청사항이 “모든 API에서 해당 파라미터 제거 + 비로그인 기본값 고정 + 로그인 기존 동작 유지”로 명확했기 때문이다. +- 어떻게: + - 명령: + - `./gradlew compileKotlin compileTestKotlin` + - `grep("@RequestParam(\"isAdultContentVisible\"|@RequestParam(\"contentType\")", include="*Controller.kt")` + - `ast-grep: ViewerContentPreference(isAdultContentVisible = false, contentType = ContentType.ALL)` + - `./gradlew test` + - `./gradlew build` + - `lsp_diagnostics`(수정된 `.kt` 파일 대상) + - 결과: + - 컴파일 성공(`compileKotlin`, `compileTestKotlin`). + - 컨트롤러의 `@RequestParam("isAdultContentVisible")`, `@RequestParam("contentType")` 검색 결과 0건. + - 비로그인 기본값 고정 분기 5개 위치 확인(`HomeService`, `LiveApiService`, `LiveRoomService`, `AudioContentController`, `AudioContentMainTabHomeController`). + - `./gradlew test` 성공. + - `./gradlew build` 성공. + - 현재 환경은 Kotlin LSP 서버 미구성으로 `lsp_diagnostics(.kt)` 실행 불가였고, Gradle 컴파일/테스트/빌드로 정합성 검증 완료. diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt index 662b6ffd..315be764 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt @@ -1,7 +1,6 @@ package kr.co.vividnext.sodalive.api.home import kr.co.vividnext.sodalive.common.ApiResponse -import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.rank.ContentRankingSortType @@ -17,15 +16,11 @@ class HomeController(private val service: HomeService) { @GetMapping fun fetchData( @RequestParam timezone: String, - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { ApiResponse.ok( service.fetchData( timezone = timezone, - isAdultContentVisible = isAdultContentVisible, - contentType = contentType, member ) ) @@ -34,15 +29,11 @@ class HomeController(private val service: HomeService) { @GetMapping("/latest-content") fun getLatestContentByTheme( @RequestParam("theme") theme: String, - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { ApiResponse.ok( service.getLatestContentByTheme( theme = theme, - isAdultContentVisible = isAdultContentVisible, - contentType = contentType, member ) ) @@ -51,15 +42,11 @@ class HomeController(private val service: HomeService) { @GetMapping("/day-of-week-series") fun getDayOfWeekSeriesList( @RequestParam("dayOfWeek") dayOfWeek: SeriesPublishedDaysOfWeek, - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { ApiResponse.ok( service.getDayOfWeekSeriesList( dayOfWeek = dayOfWeek, - isAdultContentVisible = isAdultContentVisible, - contentType = contentType, member ) ) @@ -68,14 +55,10 @@ class HomeController(private val service: HomeService) { // 추천 콘텐츠만 새로고침하기 위한 엔드포인트 @GetMapping("/recommend-contents") fun getRecommendContents( - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { ApiResponse.ok( service.getRecommendContentList( - isAdultContentVisible = isAdultContentVisible, - contentType = contentType, member = member ) ) @@ -85,8 +68,6 @@ class HomeController(private val service: HomeService) { @GetMapping("/content-ranking") fun getContentRanking( @RequestParam("sort", required = false) sort: ContentRankingSortType? = null, - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @RequestParam("offset", required = false) offset: Long? = null, @RequestParam("limit", required = false) limit: Long? = null, @RequestParam("theme", required = false) theme: String? = null, @@ -95,8 +76,6 @@ class HomeController(private val service: HomeService) { ApiResponse.ok( service.getContentRankingBySort( sort = sort ?: ContentRankingSortType.REVENUE, - isAdultContentVisible = isAdultContentVisible, - contentType = contentType, offset = offset, limit = limit, theme = theme, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt index 3548adbe..cf7b925f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt @@ -72,11 +72,9 @@ class HomeService( fun fetchData( timezone: String, - isAdultContentVisible: Boolean?, - contentType: ContentType?, member: Member? ): GetHomeResponse { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) val memberId = member?.id val isAdult = preference.isAdult val resolvedContentType = preference.contentType @@ -84,7 +82,6 @@ class HomeService( val liveList = liveRoomService.getRoomList( dateString = null, status = LiveRoomStatus.NOW, - isAdultContentVisible = isAdult, pageable = Pageable.ofSize(10), member = member, timezone = timezone @@ -227,11 +224,9 @@ class HomeService( fun getLatestContentByTheme( theme: String, - isAdultContentVisible: Boolean?, - contentType: ContentType?, member: Member? ): List { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) val memberId = member?.id val isAdult = preference.isAdult val resolvedContentType = preference.contentType @@ -258,11 +253,9 @@ class HomeService( fun getDayOfWeekSeriesList( dayOfWeek: SeriesPublishedDaysOfWeek, - isAdultContentVisible: Boolean?, - contentType: ContentType?, member: Member? ): List { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) val memberId = member?.id val isAdult = preference.isAdult @@ -276,14 +269,12 @@ class HomeService( fun getContentRankingBySort( sort: ContentRankingSortType, - isAdultContentVisible: Boolean?, - contentType: ContentType?, offset: Long?, limit: Long?, theme: String?, member: Member? ): List { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) val memberId = member?.id val isAdult = preference.isAdult @@ -328,12 +319,10 @@ class HomeService( } fun getRecommendContentList( - isAdultContentVisible: Boolean?, - contentType: ContentType?, member: Member?, excludeContentIds: List = emptyList() ): List { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) return getRecommendContentListByPreference(preference, member, excludeContentIds) } @@ -391,16 +380,12 @@ class HomeService( return result.take(RECOMMEND_TARGET_SIZE).shuffled() } - private fun resolvePreference( - member: Member?, - isAdultContentVisible: Boolean?, - contentType: ContentType? - ): ViewerContentPreference { + private fun resolvePreference(member: Member?): ViewerContentPreference { if (member == null) { return ViewerContentPreference( countryCode = "KR", - isAdultContentVisible = isAdultContentVisible ?: false, - contentType = contentType ?: ContentType.ALL, + isAdultContentVisible = false, + contentType = ContentType.ALL, isAdult = false ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/live/LiveApiController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/live/LiveApiController.kt index 37fd63bc..8541fb81 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/api/live/LiveApiController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/live/LiveApiController.kt @@ -1,7 +1,6 @@ package kr.co.vividnext.sodalive.api.live import kr.co.vividnext.sodalive.common.ApiResponse -import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.member.Member import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping @@ -17,14 +16,10 @@ class LiveApiController( @GetMapping fun fetchData( @RequestParam timezone: String, - @RequestParam("contentType", required = false) contentType: ContentType? = null, - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { ApiResponse.ok( service.fetchData( - isAdultContentVisible = isAdultContentVisible, - contentType = contentType, timezone = timezone, member = member ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/live/LiveApiService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/live/LiveApiService.kt index 94fe228f..a2dcca68 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/api/live/LiveApiService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/live/LiveApiService.kt @@ -24,19 +24,16 @@ class LiveApiService( private val blockMemberRepository: BlockMemberRepository ) { fun fetchData( - isAdultContentVisible: Boolean?, - contentType: ContentType?, timezone: String, member: Member? ): LiveMainResponse { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) val memberId = member?.id val isAdult = preference.isAdult val liveOnAirRoomList = liveService.getRoomList( dateString = null, status = LiveRoomStatus.NOW, - isAdultContentVisible = isAdult, pageable = Pageable.ofSize(20), member = member, timezone = timezone @@ -81,7 +78,6 @@ class LiveApiService( val liveReservationRoomList = liveService.getRoomList( dateString = null, status = LiveRoomStatus.RESERVATION, - isAdultContentVisible = isAdult, pageable = Pageable.ofSize(10), member = member, timezone = timezone @@ -98,16 +94,12 @@ class LiveApiService( ) } - private fun resolvePreference( - member: Member?, - isAdultContentVisible: Boolean?, - contentType: ContentType? - ): ViewerContentPreference { + private fun resolvePreference(member: Member?): ViewerContentPreference { if (member == null) { return ViewerContentPreference( countryCode = "KR", - isAdultContentVisible = isAdultContentVisible ?: false, - contentType = contentType ?: ContentType.ALL, + isAdultContentVisible = false, + contentType = ContentType.ALL, isAdult = false ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt index 66258861..fd52954e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt @@ -111,13 +111,11 @@ class AudioContentController( @RequestParam("creator-id") creatorId: Long, @RequestParam("sort-type", required = false) sortType: SortType? = SortType.NEWEST, @RequestParam("category-id", required = false) categoryId: Long? = 0, - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, pageable: Pageable ) = run { if (member == null) throw SodaException(messageKey = "common.error.bad_credentials") - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) ApiResponse.ok( service.getAudioContentList( @@ -137,11 +135,10 @@ class AudioContentController( fun getDetail( @PathVariable id: Long, @RequestParam timezone: String, - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) throw SodaException(messageKey = "common.error.bad_credentials") - val preference = resolvePreference(member, isAdultContentVisible, null) + val preference = resolvePreference(member) ApiResponse.ok( service.getDetail( @@ -194,12 +191,10 @@ class AudioContentController( @GetMapping("/ranking") fun getAudioContentRanking( @RequestParam("sort-type", required = false) sortType: String? = "매출", - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, pageable: Pageable ) = run { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) val currentDateTime = LocalDateTime.now() val startDate = currentDateTime .withHour(15) @@ -247,8 +242,6 @@ class AudioContentController( @GetMapping("/all") fun getAllContents( - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @RequestParam("isFree", required = false) isFree: Boolean? = null, @RequestParam("isPointAvailableOnly", required = false) isPointAvailableOnly: Boolean? = null, @RequestParam("sort-type", required = false) sortType: SortType? = SortType.NEWEST, @@ -257,7 +250,7 @@ class AudioContentController( pageable: Pageable ) = run { if (member == null) throw SodaException(messageKey = "common.error.bad_credentials") - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) ApiResponse.ok( service.getLatestContentByTheme( @@ -276,11 +269,9 @@ class AudioContentController( @GetMapping("/replay-live") fun replayLive( - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) ApiResponse.ok( service.getLatestContentByTheme( memberId = member?.id, @@ -292,16 +283,12 @@ class AudioContentController( ) } - private fun resolvePreference( - member: Member?, - isAdultContentVisible: Boolean?, - contentType: ContentType? - ): ViewerContentPreference { + private fun resolvePreference(member: Member?): ViewerContentPreference { if (member == null) { return ViewerContentPreference( countryCode = "KR", - isAdultContentVisible = isAdultContentVisible ?: false, - contentType = contentType ?: ContentType.ALL, + isAdultContentVisible = false, + contentType = ContentType.ALL, isAdult = false ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeController.kt index 0e0ea1f2..1a068c0b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeController.kt @@ -19,11 +19,9 @@ class AudioContentMainTabHomeController( ) { @GetMapping fun fetchContentMainHome( - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) ApiResponse.ok( service.fetchData( isAdultContentVisible = preference.isAdultContentVisible, @@ -36,11 +34,9 @@ class AudioContentMainTabHomeController( @GetMapping("/popular-content-by-creator") fun getPopularContentByCreator( @RequestParam creatorId: Long, - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) ApiResponse.ok( service.getPopularContentByCreator( creatorId = creatorId, @@ -53,11 +49,9 @@ class AudioContentMainTabHomeController( @GetMapping("/content/ranking") fun getContentRanking( @RequestParam("sort-type", required = false) sortType: String? = "매출", - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, - @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { - val preference = resolvePreference(member, isAdultContentVisible, contentType) + val preference = resolvePreference(member) ApiResponse.ok( service.getContentRanking( sortType = sortType ?: "매출", @@ -68,16 +62,12 @@ class AudioContentMainTabHomeController( ) } - private fun resolvePreference( - member: Member?, - isAdultContentVisible: Boolean?, - contentType: ContentType? - ): ViewerContentPreference { + private fun resolvePreference(member: Member?): ViewerContentPreference { if (member == null) { return ViewerContentPreference( countryCode = "KR", - isAdultContentVisible = isAdultContentVisible ?: false, - contentType = contentType ?: ContentType.ALL, + isAdultContentVisible = false, + contentType = ContentType.ALL, isAdult = false ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt index d1deba6e..0c7bb0b2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt @@ -35,7 +35,6 @@ class LiveRoomController( @RequestParam timezone: String, @RequestParam dateString: String? = null, @RequestParam status: LiveRoomStatus, - @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, pageable: Pageable ) = run { @@ -43,7 +42,6 @@ class LiveRoomController( service.getRoomList( dateString, status, - isAdultContentVisible, pageable, member, timezone diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt index 04a7e5db..6628337e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt @@ -205,12 +205,11 @@ class LiveRoomService( fun getRoomList( dateString: String?, status: LiveRoomStatus, - isAdultContentVisible: Boolean?, pageable: Pageable, member: Member?, timezone: String ): List { - val preference = resolvePreference(member, isAdultContentVisible) + val preference = resolvePreference(member) val isAdult = preference.isAdult val effectiveGender = member?.let { if (it.auth != null) { @@ -1470,11 +1469,11 @@ class LiveRoomService( return tokenLocks.computeIfAbsent(memberId) { ReentrantReadWriteLock() } } - private fun resolvePreference(member: Member?, isAdultContentVisible: Boolean?): ViewerContentPreference { + private fun resolvePreference(member: Member?): ViewerContentPreference { if (member == null) { return ViewerContentPreference( countryCode = "KR", - isAdultContentVisible = isAdultContentVisible ?: false, + isAdultContentVisible = false, contentType = ContentType.ALL, isAdult = false )