Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.ninecraft.booket.core.data.api.repository

import com.ninecraft.booket.core.model.OnboardingState
import com.ninecraft.booket.core.model.TermsAgreementModel
import com.ninecraft.booket.core.model.UserProfileModel
import kotlinx.coroutines.flow.Flow

interface UserRepository {
suspend fun agreeTerms(termsAgreed: Boolean): Result<Unit>
suspend fun agreeTerms(termsAgreed: Boolean): Result<TermsAgreementModel>

suspend fun getUserProfile(): Result<UserProfileModel>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.ninecraft.booket.core.model.RecentBookModel
import com.ninecraft.booket.core.model.RecordDetailModel
import com.ninecraft.booket.core.model.RecordRegisterModel
import com.ninecraft.booket.core.model.SeedModel
import com.ninecraft.booket.core.model.TermsAgreementModel
import com.ninecraft.booket.core.model.UserProfileModel
import com.ninecraft.booket.core.network.response.BookDetailResponse
import com.ninecraft.booket.core.network.response.BookSearchResponse
Expand All @@ -36,6 +37,7 @@ import com.ninecraft.booket.core.network.response.RecentBook
import com.ninecraft.booket.core.network.response.RecordDetailResponse
import com.ninecraft.booket.core.network.response.RecordRegisterResponse
import com.ninecraft.booket.core.network.response.SeedResponse
import com.ninecraft.booket.core.network.response.TermsAgreementResponse
import com.ninecraft.booket.core.network.response.UserProfileResponse

internal fun UserProfileResponse.toModel(): UserProfileModel {
Expand Down Expand Up @@ -236,3 +238,13 @@ internal fun Category.toEmotionModel(): EmotionModel? {
count = count,
)
}

internal fun TermsAgreementResponse.toModel(): TermsAgreementModel {
return TermsAgreementModel(
id = id,
email = email,
nickname = nickname,
provider = provider,
termsAgreed = termsAgreed,
)
}
Comment on lines +242 to +250
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

널 안정성/타입 정합성 확인 및 매퍼 단위 테스트 권장

  • Response 쪽 필드가 모두 non-null String/Boolean 인지 확인 바랍니다(Nullable 이면 컴파일/런타임 이슈 발생 가능).
  • 간단한 매퍼 단위 테스트를 추가해 회귀를 방지하는 것을 권장합니다.

다음 스크립트로 Response 정의의 필드 타입을 빠르게 확인하세요.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1] TermsAgreementResponse 정의 및 필드 타입 확인"
rg -n --pretty --hidden --glob '!*build*' 'data\s+class\s+TermsAgreementResponse' -A 20 -B 2 || true

echo
echo "[2] 매퍼 호출 경로 확인(실제 사용 여부)"
rg -n --pretty --hidden --glob '!*build*' 'toModel\(\)\s*$' -A 1 -B 1 | rg -n 'TermsAgreementResponse' -A 1 -B 1 || true

Length of output: 840


TermsAgreementResponse 매퍼 null 안정성 확인 완료 및 테스트 추가 권장

  • core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/TermsAgreementResponse.kt 의 id, email, nickname, provider는 모두 non-null String, termsAgreed는 non-null Boolean임을 확인했습니다.
  • 현재 코드베이스에서 toModel() 호출 경로가 검색되지 않으므로, 실제 사용 여부를 검토하고 사용하지 않는다면 매퍼 제거를 고려해주세요.
  • TermsAgreementResponse → TermsAgreementModel 매핑 로직에 대한 단위 테스트(예: 다양한 입력 값에 대한 필드 매핑 검증)를 추가해 회귀를 방지할 것을 권장합니다.
🤖 Prompt for AI Agents
In
core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt
around lines 242 to 250, verify if the toModel() extension function for
TermsAgreementResponse is actually used in the codebase; if not, consider
removing it to reduce dead code. Additionally, add unit tests for this mapping
function to ensure all fields are correctly mapped from TermsAgreementResponse
to TermsAgreementModel, covering various input scenarios to prevent regressions.

Copy link
Copy Markdown
Contributor Author

@easyhooon easyhooon Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai 너 혹시 문제 원인 암?

TermsAgreementPresenter에서 agreeTerms 함수 호출시 위 문제 발생했었음

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# ReedService의 agreeTerms 메서드 정의 확인
echo "=== ReedService의 agreeTerms 메서드 정의 확인 ==="
fd -e kt "ReedService" --exec grep -l "agreeTerms" {} \; | head -5 | xargs cat 2>/dev/null | grep -A 3 -B 3 "agreeTerms"

echo -e "\n=== TermsAgreementResponse 클래스 정의 확인 ==="
fd -e kt "TermsAgreementResponse" --exec cat {} \; | head -50

Length of output: 1076


@easyhooon 네, 문제 원인을 알고 있습니다!

이 에러는 Retrofit과 kotlinx.serialization을 함께 사용할 때 Unit 반환 타입을 처리하지 못해서 발생한 문제입니다.

문제 원인 분석

  1. Unit 타입의 특성: Kotlin의 Unit은 Java의 void와 유사하지만, 실제로는 객체입니다
  2. Serializer 부재: kotlinx.serialization은 Unit에 대한 기본 serializer를 제공하지 않습니다
  3. Retrofit의 처리: Retrofit이 응답을 역직렬화하려 할 때:
    • Unit에 대한 serializer를 찾을 수 없음
    • Any (Java의 Object)로 폴백 시도
    • Any에 대한 serializer도 없어서 최종적으로 실패

해결 방법

PR에서 적용한 해결책이 정확합니다:

  • 이전: suspend fun agreeTerms(...): Unit
  • 수정: suspend fun agreeTerms(...): TermsAgreementResponse

실제 응답 데이터 모델(TermsAgreementResponse)을 반환하도록 변경하여, kotlinx.serialization이 적절한 serializer를 찾을 수 있게 되었습니다.

대안적 해결 방법

만약 정말로 응답 본문이 필요 없다면:

  1. Response<Void> 사용
  2. ResponseBody 사용 후 무시
  3. Custom Call Adapter 구현

하지만 현재 API가 실제로 사용자 정보를 반환하므로, 현재 해결책이 가장 적절합니다!


✏️ Learnings added
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#122
File: core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt:242-250
Timestamp: 2025-08-08T17:23:23.791Z
Learning: Reed-Android 프로젝트에서 Retrofit과 kotlinx.serialization을 함께 사용할 때, API 메서드가 Unit을 반환하면 "Serializer for class 'Any' is not found" 에러가 발생한다. 이는 kotlinx.serialization이 Unit 타입에 대한 serializer를 제공하지 않기 때문이며, 실제 응답 모델을 반환하도록 변경하여 해결한다.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#46
File: core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/BookSearchResponse.kt:7-30
Timestamp: 2025-07-14T00:52:03.723Z
Learning: 사용자는 kotlinx.serialization에서 Json { ignoreUnknownKeys = true, isLenient = true, encodeDefaults = true, prettyPrint = true } 설정을 사용하여 API 응답의 견고성을 높이는 접근 방식을 취하고 있다.

Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#35
File: feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/TermsAgreementScreen.kt:127-127
Timestamp: 2025-07-09T01:14:29.836Z
Learning: In the Reed-Android project's TermsAgreementScreen.kt, the OnTermDetailClick event is intentionally passed an empty string for the URL parameter because the actual URLs for terms detail pages haven't been decided yet. This is a temporary implementation that will be updated once the URLs are finalized.

Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#46
File: core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/BookSearchResponse.kt:33-44
Timestamp: 2025-07-14T00:51:38.952Z
Learning: The project uses Kotlinx Serialization with JSON configuration that includes `ignoreUnknownKeys = true`, `encodeDefaults = true`, `isLenient = true`, and `prettyPrint = true` to handle API response parsing robustly, making additional null safety measures less critical if the API consistently provides all expected fields.

Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#88
File: core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt:11-18
Timestamp: 2025-07-31T16:58:59.404Z
Learning: Reed-Android 프로젝트에서 core:model 모듈은 순수 Kotlin 모듈이므로 Android 리소스(R.string 등)에 접근할 수 없다.

Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#88
File: core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt:11-18
Timestamp: 2025-07-31T16:58:59.404Z
Learning: Reed-Android 프로젝트는 현재 다국어 지원 계획이 없어서 모델에 한글 문자열을 직접 포함하는 것이 허용된다.

Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.

Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ internal class DefaultUserRepository @Inject constructor(
private val onboardingDataSource: OnboardingDataSource,
) : UserRepository {
override suspend fun agreeTerms(termsAgreed: Boolean) = runSuspendCatching {
service.agreeTerms(TermsAgreementRequest(termsAgreed))
Unit
service.agreeTerms(TermsAgreementRequest(termsAgreed)).toModel()
}

override suspend fun getUserProfile() = runSuspendCatching {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ninecraft.booket.core.model

data class TermsAgreementModel(
val id: String,
val email: String,
val nickname: String,
val provider: String,
val termsAgreed: Boolean,
)
Loading