Skip to content

Conversation

@m6z1
Copy link
Member

@m6z1 m6z1 commented Jan 10, 2026

📌𝘐𝘴𝘴𝘶𝘦𝘴

📎𝘞𝘰𝘳𝘬 𝘋𝘦𝘴𝘤𝘳𝘪𝘱𝘵𝘪𝘰𝘯

  • 피드 상세보기 뷰 ui 수정 및 api 수정했습니다.

📷𝘚𝘤𝘳𝘦𝘦𝘯𝘴𝘩𝘰𝘵

스크린샷 2026-01-10 오후 7 22 00 스크린샷 2026-01-10 오후 7 22 06 스크린샷 2026-01-10 오후 7 22 12 스크린샷 2026-01-10 오후 7 21 52

💬𝘛𝘰 𝘙𝘦𝘷𝘪𝘦𝘸𝘦𝘳𝘴

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 소설 장르별 아이콘 및 상세 정보 표시 기능 추가
    • 피드 상세 보기에 작가 평점 표시 기능 추가
  • 개선 사항

    • 이미지 로딩 시 둥근 모서리 효과 지원
    • 이미지 컨테이너 레이아웃 최적화 및 고정 크기 적용
    • 피드 상세 보기 UI 개선 및 재구성
  • 스타일

    • 8가지 소설 장르 아이콘 리소스 추가
    • 평점 표시 형식 개선

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions github-actions bot requested a review from s9hn January 10, 2026 10:22
@coderabbitai
Copy link

coderabbitai bot commented Jan 10, 2026

Walkthrough

피드 상세 뷰의 소설 정보 표시를 강화하기 위해 전용 FeedDetailEntity 및 FeedDetailModel을 도입하여 엔티티 구조를 재구성합니다. 데이터 계층의 매핑, 저장소, ViewModel, 그리고 UI 컴포넌트들을 업데이트하여 소설 메타데이터를 전파하고, 이미지 렌더링을 개선하며 장르별 드로어블 아이콘을 추가합니다.

Changes

코호트 / 파일(들) 변경 요약
핵심 모델 및 엔티티
app/src/main/java/com/into/websoso/data/model/FeedDetailEntity.kt, app/src/main/java/com/into/websoso/ui/feedDetail/model/FeedDetailModel.kt, app/src/main/java/com/into/websoso/ui/feedDetail/model/Genre.kt
새로운 FeedDetailEntity 추가 (중첩된 UserEntity, NovelEntity 포함), FeedDetailModel에 novel 필드 및 novelImage 계산 프로퍼티 추가, Genre enum 신규 생성으로 장르 매핑 지원
데이터 계층 변경
app/src/main/java/com/into/websoso/data/remote/response/FeedDetailResponseDto.kt, app/src/main/java/com/into/websoso/data/mapper/FeedMapper.kt, app/src/main/java/com/into/websoso/data/repository/FeedRepository.kt
FeedDetailResponseDto에 소설 관련 필드 5개 추가, FeedDetailEntity로 매핑하는 로직 변경, repository의 fetchFeed 반환 타입을 FeedDetailEntity로 업데이트
UI 레이어 ViewModel & Activity
app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailViewModel.kt, app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailActivity.kt
FeedEntity에서 FeedDetailEntity로 마이그레이션, UI 생성 로직에서 feedDetail.toUi()로 변경, 좋아요 처리 및 업데이트 로직 조정
어댑터 및 뷰홀더
app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailAdapter.kt, app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailType.kt, app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailContentViewHolder.kt
Header 데이터 클래스에서 feed를 feedDetail로 변경, 바인딩 로직 업데이트, 소설 이미지 표시 추가
UI 매퍼
app/src/main/java/com/into/websoso/ui/mapper/FeedMapper.kt
FeedDetailEntity.toUi(): FeedDetailModel 확장 함수 신규 추가
이미지 컨테이너 컴포넌트
app/src/main/java/com/into/websoso/ui/feedDetail/component/SingleImageContainer.kt, app/src/main/java/com/into/websoso/ui/feedDetail/component/DoubleImageContainer.kt, app/src/main/java/com/into/websoso/ui/feedDetail/component/TripleImageContainer.kt, app/src/main/java/com/into/websoso/ui/feedDetail/component/MultipleImageContainer.kt
Box 컨테이너 도입 및 고정 크기(100.dp) 적용, 동적 레이아웃 제약(aspect ratio, weight) 제거, 이미지 간 간격 8dp에서 6dp로 축소, forEachIndexed 사용으로 인덱스 처리 개선
바인딩 어댑터
app/src/main/java/com/into/websoso/core/common/util/BindingAdapter.kt
loadS3ImageUrl에 선택적 cornerRadius 파라미터 추가, requireAll = false로 설정하여 유연한 바인딩 지원
레이아웃 파일
app/src/main/res/layout/item_feed.xml, app/src/main/res/layout/item_feed_detail_header.xml
iv_feed_link를 iv_feed_novel_thumbnail로 이름 변경, tv_feed_score를 tv_feed_detail_all_score로 이름 변경, feedDetail 바인딩 변수 도입 및 모든 참조 업데이트, 소설 관련 바인딩 표현식 추가
드로어블 리소스 - 장르 아이콘
app/src/main/res/drawable/ic_romance.xml, app/src/main/res/drawable/ic_fantasy.xml, app/src/main/res/drawable/ic_mystery.xml, app/src/main/res/drawable/ic_wuxia.xml, app/src/main/res/drawable/ic_hf.xml, app/src/main/res/drawable/ic_ln.xml, app/src/main/res/drawable/ic_bl.xml, app/src/main/res/drawable/ic_drama.xml, app/src/main/res/drawable/ic_romance_fantasy.xml
소설 장르별 벡터 드로어블 9개 신규 추가(각각 70x70dp 크기)
드로어블 리소스 - 별 아이콘 및 기타
app/src/main/res/drawable/ic_feed_detail_novel_star.xml, app/src/main/res/drawable/ic_feed_detail_writer_star.xml, app/src/main/res/drawable/btn_feed_novel_info.xml
별 아이콘 2개 추가(12x12dp), 버튼 스타일 변경(solid fill 제거, stroke 추가)
문자열 리소스
core/resource/src/main/res/values/strings.xml
feed_score_count 형식 "%.1f (%d)"에서 "%.1f"로 변경(개수 표시 제거)

Sequence Diagram(s)

sequenceDiagram
    participant API as Remote API
    participant Repo as Repository
    participant Mapper as FeedMapper
    participant ViewModel as FeedDetailViewModel
    participant View as FeedDetailActivity

    API->>Repo: FeedDetailResponseDto (novel metadata)
    Repo->>Mapper: FeedDetailResponseDto
    Mapper->>Mapper: toData() 매핑
    Mapper->>ViewModel: FeedDetailEntity (novel 포함)
    ViewModel->>ViewModel: toUi() 변환
    ViewModel->>ViewModel: novelImage 계산<br/>(Genre.from())
    ViewModel->>View: FeedDetailModel 발행
    View->>View: binding.feedDetail ← FeedDetailModel
    View->>View: 소설 장르 아이콘 표시
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested labels

🍯 [FEAT], 🏹 궁사 명지

Suggested reviewers

  • s9hn
  • yeonjeen
  • junseo511

Poem

🐰 소설의 장르를 담은 별들이,
피드 상세 뷰에 반짝이며,
고정된 이미지로 정렬되고,
FeedDetailEntity가 데이터를 품으니,
더욱 풍성한 피드의 모습이로다! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 피드 상세보기 뷰 수정의 주요 변경사항을 명확하게 요약하고 있으며, 제공된 모든 코드 변경사항과 일치합니다.
Description check ✅ Passed PR 설명에는 필수 섹션인 Issues, Work Description, Screenshots이 포함되어 있으나, Reviewers를 위한 추가 정보가 제공되지 않았습니다.
Linked Issues check ✅ Passed PR은 #781 이슈의 피드 상세보기 뷰 UI 수정 요구사항을 충족합니다. 새로운 FeedDetailEntity 모델, UI 구성요소 업데이트, 레이아웃 조정, 장르 아이콘 추가 등의 변경사항이 포함되어 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 피드 상세보기 뷰 UI 수정과 관련 API 개선에 집중되어 있으며, #781 이슈 범위 내에 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Member

@s9hn s9hn left a comment

Choose a reason for hiding this comment

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

어프했습니다

@m6z1 m6z1 merged commit 99a26e1 into develop Jan 10, 2026
1 of 3 checks passed
@m6z1 m6z1 deleted the feat/781 branch January 10, 2026 10:26
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
app/src/main/java/com/into/websoso/ui/feedDetail/component/TripleImageContainer.kt (1)

22-34: 고정 100dp 3개 배치는 화면 폭이 작은 환경에서 잘림 위험

padding(20dp) + 100dp * 3 + spacedBy(6dp * 2)면 최소 약 352dp가 필요해서, 작은 기기/분할화면/접근성 설정에서 Row가 넘칠 수 있습니다. 디자인이 고정 사이즈를 요구하지 않는다면 weight(1f) + aspectRatio(1f) 같은 반응형으로 유지하거나, BoxWithConstraints로 가용 폭 기준으로 size를 계산하는 방식이 더 안전합니다.

Also applies to: 26-33

app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailActivity.kt (1)

372-379: ActivityResult의 result code별로 올바른 ResultFrom을 넘겨야 함

NovelDetailBack, OtherUserProfileBack에서 받은 결과도 모두 CreateFeed로 처리되므로, 에러 발생 시 previousStack.from 기반 분기(496-497줄)가 왜곡됩니다. CreateFeed와 FeedDetailRefreshed 경로로 모두 빠져 RemovedFeedDialogFragment를 표시하게 되지만, 다른 소스에서 온 경우 다른 동작(FeedDetailRemoved 결과 반환)이 필요할 수 있습니다.

result code별로 맞는 ResultFrom을 전달하도록 수정 필요:

제안된 수정
 when (result.resultCode) {
     NovelDetailBack.RESULT_OK,
     CreateFeed.RESULT_OK,
     OtherUserProfileBack.RESULT_OK,
     -> {
-        feedDetailViewModel.updateFeedDetail(feedId, CreateFeed)
+        val from = when (result.resultCode) {
+            NovelDetailBack.RESULT_OK -> NovelDetailBack
+            CreateFeed.RESULT_OK -> CreateFeed
+            OtherUserProfileBack.RESULT_OK -> OtherUserProfileBack
+            else -> Feed
+        }
+        feedDetailViewModel.updateFeedDetail(feedId, from)
     }
app/src/main/java/com/into/websoso/core/common/util/BindingAdapter.kt (1)

30-54: Coil transformations(...) 여러 번 호출 시 마지막 변환만 적용됨

Coil의 API에서 transformations()는 누적되지 않으며, 각 호출이 이전의 변환 목록을 교체합니다. 현재 구현에서 cornerRadius, blur, circular 변환이 동시에 활성화되면, 마지막으로 실행된 변환만 남고 이전 변환들은 모두 무시됩니다. 변환들을 리스트에 수집한 후 한 번에 설정해야 합니다.

🛠️ 수정 제안
 view.load(imageUrl) {
     crossfade(true)
     if (isVectorImage == true) decoderFactory(SvgDecoder.Factory())
-    if (cornerRadius != null) transformations(RoundedCornersTransformation(cornerRadius.toFloatPxFromDp()))
-    if (blurRadius != null) transformations(BlurTransformation(view.context, blurRadius))
-    if (isCircularImage == true) transformations(CircleCropTransformation())
+    val ts = mutableListOf<coil.transform.Transformation>()
+    if (cornerRadius != null) ts += RoundedCornersTransformation(cornerRadius.toFloatPxFromDp())
+    if (blurRadius != null) ts += BlurTransformation(view.context, blurRadius)
+    if (isCircularImage == true) ts += CircleCropTransformation()
+    if (ts.isNotEmpty()) transformations(*ts.toTypedArray())
     error(img_loading_thumbnail)
 }
app/src/main/res/layout/item_feed.xml (2)

172-183: iv_feed_novel_thumbnail가 실제 “썸네일”이면 이미지 바인딩이 빠진 상태입니다.

Line 173~182에서 현재 android:src="@drawable/ic_link"로 고정되어 있어, ID/네이밍(썸네일)과 실제 동작(아이콘)이 어긋날 수 있어요. 썸네일을 보여주는 의도라면 loadImageUrl(또는 프로젝트 표준 어댑터)로 feed.novel.thumbnail 등을 바인딩하는 쪽이 안전합니다. 반대로 아이콘이 의도라면 ID를 아이콘 의미로 유지하는 게 혼동이 덜합니다.

✅ (썸네일 의도일 때) 예시 수정안
             <ImageView
                 android:id="@+id/iv_feed_novel_thumbnail"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_width="86dp"
+                android:layout_height="124dp"
                 android:layout_marginStart="16dp"
                 android:layout_marginTop="16dp"
                 android:layout_marginBottom="16dp"
-                android:src="@drawable/ic_link"
+                android:scaleType="centerCrop"
+                app:loadImageUrl="@{feed.novel.thumbnail}"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toTopOf="parent" />

Also applies to: 184-200


202-235: @string/feed_score_count(...) 포맷 인자 수를 레이아웃들 간에 통일해야 합니다(런타임 크래시 위험).

여기서는 Line 217에서 feed_score_count(rating, ratingCount)처럼 2개 인자를 넘기고 있는데, 다른 레이아웃에서는 같은 리소스를 1개 인자로 호출하고 있습니다(아래 파일 참고). 문자열 리소스 placeholder 개수와 호출 인자 개수가 불일치하면 MissingFormatArgumentException 등으로 터질 수 있어, 리소스를 분리(예: 점수만/점수+카운트)하거나 호출을 통일해 주세요.

🤖 Fix all issues with AI agents
In @app/src/main/java/com/into/websoso/core/common/util/BindingAdapter.kt:
- Around line 15-27: The BindingAdapter loadS3ImageUrl is declared with a
non-null s3ImageKey but requireAll=false can pass null and cause an NPE; change
the parameter s3ImageKey to nullable (String?) and guard at the start of
loadS3ImageUrl (e.g., if s3ImageKey.isNullOrEmpty() return or clear the
ImageView/set placeholder) before calling view.getS3ImageUrl and view.load;
mirror the safe pattern used in loadImageWithOptions to avoid calling extension
methods with a null key.

In
@app/src/main/java/com/into/websoso/data/remote/response/FeedDetailResponseDto.kt:
- Around line 46-55: The new nullable properties in FeedDetailResponseDto
(novelThumbnailImage, novelGenre, novelAuthor, feedWriterNovelRating,
novelDescription) lack default values and will cause kotlinx.serialization
MissingFieldException when the server omits them; update each property
declaration in the FeedDetailResponseDto primary constructor to provide a
default of = null so they become truly optional during decoding.

In
@app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailContentViewHolder.kt:
- Around line 25-29: The code calls binding.cvFeedImage.setContent with
AdaptationFeedImageContainer using feedDetail.feed?.imageUrls for early-return,
but the lambda passed to AdaptationFeedImageContainer references
feedDetail.feed.imageUrls without null-safety which can NPE; fix by capturing
the non-null image list into a local val (e.g., val images =
feedDetail.feed?.imageUrls ?: return@setContent) before calling
AdaptationFeedImageContainer and then use that local images inside the
onFeedImageClick lambda when invoking
feedDetailClickListener.onFeedImageClick(index, images).

In
@app/src/main/java/com/into/websoso/ui/feedDetail/component/SingleImageContainer.kt:
- Around line 3-5: The build can fail because Modifier.matchParentSize() used in
SingleImageContainer.kt is missing its import; add the import
androidx.compose.foundation.layout.matchParentSize to the file so the unresolved
reference for Modifier.matchParentSize (used in the SingleImageContainer
composable) is resolved.

In @app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailActivity.kt:
- Around line 340-355: The code currently throws plain
IllegalArgumentException() when menuType is null in the
DialogRemovePopupMenuBinding and DialogReportPopupMenuBinding branches; update
the throws (or use requireNotNull) to include a descriptive message such as
"menuType is null when creating FeedRemoveDialogFragment" and "menuType is null
when creating FeedReportDialogFragment" (or similar) so the exception identifies
which fragment and parameter failed; adjust the two calls inside
FeedDetailActivity where FeedRemoveDialogFragment.newInstance and
FeedReportDialogFragment.newInstance are invoked.

In @app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailViewModel.kt:
- Around line 65-81: feedDetail.toUi().feed을 uiFeed에 할당한 후 분기에서 null 처리가 일관되지 않아
NPE 가능성이 있으니, uiFeed가 nullable인지 확정하고 일관된 처리를 적용하세요: 만약 uiFeed가 null일 수 있다면 기존의
uiFeed?.copy(...) 패턴처럼 모든 분기에서 안전 호출을 사용하거나 early return/대체값(예: null 또는 기본 Feed
객체)으로 처리하고, uiFeed가 절대 null이 될 수 없다면 val uiFeed = feedDetail.toUi().feed!!로 명시적
non-null 보장을 하여 updatedFeed 분기(현재의 첫 분기에서 직접 uiFeed 반환, 다른 분기에서는 uiFeed?.copy
사용)들을 일관되게 정리하세요.

In @app/src/main/java/com/into/websoso/ui/feedDetail/model/Genre.kt:
- Around line 6-26: The Genre.from(tag) lookup currently does a case-sensitive
exact match and can fail for inputs like a lowercase "bl"; update the lookup in
the Genre companion object (function from(tag)) to normalize the input and
comparison by trimming and comparing case-insensitively (e.g., normalize both
the incoming tag and Genre.tag via trim() and toLowerCase() or use
equalsIgnoreCase), so entries.find matches regardless of casing/whitespace and
fallback to ROMANCE remains unchanged.

In @app/src/main/res/layout/item_feed_detail_header.xml:
- Around line 12-15: The string resource feed_score_count is being called with
different argument counts across layouts (e.g., item_feed.xml uses two args
while item_feed_detail_header.xml uses one), which can crash at runtime; fix by
splitting the resource into two clearly named entries (e.g., feed_score_only and
feed_score_with_count) and update the data-binding expressions in both layouts
(replace usages of feed_score_count in item_feed.xml and
item_feed_detail_header.xml) to call the appropriate resource with the matching
number of arguments, or alternatively make all calls supply the same argument
list/signature if you prefer a single resource.

In @core/resource/src/main/res/values/strings.xml:
- Line 98: The string resource feed_score_count now expects a single float
argument ("%.1f"), but one binding still passes two args; update the
data-binding expression in the item_feed layout that currently calls
@string/feed_score_count(feed.novel.rating, feed.novel.ratingCount) to pass only
the rating (e.g., @string/feed_score_count(feed.novel.rating)) so it matches the
feed_score_count format and avoids runtime FormatException; verify other usages
already passing one arg remain unchanged.
🧹 Nitpick comments (13)
app/src/main/res/drawable/ic_ln.xml (1)

12-18: 아이콘 경로가 올바르게 정의되었습니다.

채팅 버블 모양의 아이콘이 적절한 스트로크 속성으로 구현되어 있습니다. 라운드 처리된 선 연결과 캡 스타일도 잘 적용되었습니다.

선택적으로, strokeWidth 값(1.95653)을 더 간단한 값(예: 1.96 또는 2.0)으로 반올림하여 가독성을 높일 수 있습니다. 다만 디자인 도구에서 내보낸 정확한 값일 수 있으므로 필수 변경은 아닙니다.

♻️ (선택사항) strokeWidth 값 단순화
-        android:strokeWidth="1.95653"
+        android:strokeWidth="1.96"

또는

-        android:strokeWidth="1.95653"
+        android:strokeWidth="2.0"
app/src/main/res/drawable/ic_mystery.xml (2)

2-3: 리소스 추출을 통한 유지보수성 향상을 권장합니다.

하드코딩된 색상(#9560BF, #1A103F)과 크기(70dp)를 리소스 파일로 추출하면 다음과 같은 이점이 있습니다:

  • 모든 장르 아이콘에서 일관된 크기와 색상 체계 유지
  • 디자인 변경 시 한 곳에서만 수정 가능
  • 테마 지원 시 색상 변경 용이
♻️ 리소스 추출 제안

res/values/colors.xml에 색상 추가:

<color name="genre_icon_primary">#9560BF</color>
<color name="genre_icon_stroke">#1A103F</color>

res/values/dimens.xml에 크기 추가:

<dimen name="genre_icon_size">70dp</dimen>

그런 다음 이 파일에 적용:

 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="70dp"
-    android:height="70dp"
+    android:width="@dimen/genre_icon_size"
+    android:height="@dimen/genre_icon_size"
     android:viewportWidth="70"
     android:viewportHeight="70">
   <group>
     <clip-path
         android:pathData="M0,0h70v70h-70z"/>
     <path
         android:pathData="M69.998,70.001V0.001L-0.002,70.001H69.998Z"
-        android:fillColor="#9560BF"/>
+        android:fillColor="@color/genre_icon_primary"/>
     <path
         android:pathData="M50.301,50.314H50.312M54.007,54.02C49.095,58.932 43.454,61.255 41.408,59.208C39.361,57.161 41.684,51.521 46.596,46.609C51.508,41.697 57.149,39.374 59.195,41.421C61.242,43.467 58.919,49.108 54.007,54.02ZM54.007,46.608C58.919,51.52 61.242,57.161 59.195,59.208C57.149,61.255 51.508,58.932 46.596,54.02C41.684,49.108 39.361,43.467 41.408,41.42C43.454,39.374 49.095,41.697 54.007,46.608ZM50.826,50.314C50.826,50.604 50.591,50.838 50.301,50.838C50.012,50.838 49.777,50.604 49.777,50.314C49.777,50.025 50.012,49.79 50.301,49.79C50.591,49.79 50.826,50.025 50.826,50.314Z"
         android:strokeLineJoin="round"
         android:strokeWidth="1.91217"
         android:fillColor="#00000000"
-        android:strokeColor="#1A103F"
+        android:strokeColor="@color/genre_icon_stroke"
         android:strokeLineCap="round"/>
   </group>
 </vector>

Also applies to: 10-11, 17-17


15-15: strokeWidth 값 단순화 고려 (선택사항)

strokeWidth="1.91217"의 정밀도가 과도해 보입니다. 1.9 또는 2.0으로 단순화해도 시각적 차이가 거의 없으며 가독성이 향상됩니다.

app/src/main/res/drawable/ic_romance_fantasy.xml (1)

1-16: 벡터 리소스 추가 자체는 문제 없어 보입니다.
viewport/dp 크기 일치하고, clip-path도 현재 형태에선 안전합니다. 다만 clip-path가 뷰포트 전체를 그대로 자르는 용도라면(항상 동일한 사각형) 리소스 단순화를 위해 제거 가능 여부만 한 번 검토해도 좋겠습니다.

app/src/main/res/drawable/ic_feed_detail_writer_star.xml (1)

1-9: 피드 상세 UI용 아이콘으로 깔끔하게 추가되었습니다.
색상 #FF675D가 디자인 시스템/테마 색상과 연동돼야 하는 요구가 있다면, 추후 color 리소스로 분리하는 정도만 고려하면 좋겠습니다.

app/src/main/java/com/into/websoso/data/repository/FeedRepository.kt (1)

129-129: 함수명 명확화를 권고합니다.

fetchFeed()의 반환 타입이 FeedDetailEntity로 변경되었으므로, 함수명을 fetchFeedDetail()로 변경하는 것을 권고합니다. 현재 호출부들(FeedDetailViewModel, CreateFeedViewModel)은 FeedDetailEntityimages 속성에 접근하므로 타입 호환성 측면에서는 문제가 없으나, 함수명이 의도를 명확히 나타내면 향후 유지보수에 도움이 됩니다.

app/src/main/res/drawable/ic_feed_detail_novel_star.xml (1)

1-9: 하드코딩된 색상(#949399)은 리소스 색상으로 치환 고려

현재 android:fillColor가 헥스 하드코딩이라 테마/다크모드/디자인 시스템 변경 시 일괄 대응이 어렵습니다. 기존 아이콘들과 동일한 방식이라면 유지해도 되지만, 가능하면 @color/...로 통일을 권장합니다.

app/src/main/java/com/into/websoso/data/mapper/FeedMapper.kt (1)

78-111: novel 필드 orEmpty() 처리로 “값 없음” 의미가 소실될 수 있음

title/thumbnail/genre/author/descriptionorEmpty()로 강제하면, API에서 값이 없는 경우와 “빈 문자열”이 의미 있는 경우를 구분할 수 없습니다. UI에서 “없음” 처리가 필요하면 String?을 유지하거나, 최소한 null/blank 정책을 한 곳(예: UI 매퍼)에서만 결정하는 편이 안전합니다.

app/src/main/java/com/into/websoso/data/model/FeedDetailEntity.kt (2)

26-39: 플래그 네이밍(isRatingNoting) 오타/의미 불명확 가능성

isRatingNoting, isWriterRatingNoting은 의도가 “없음(nothing)”인지 “기록 중(noting)”인지 모호합니다. 읽는 쪽 혼동을 줄이도록 isRatingMissing/hasRating 같은 명확한 네이밍으로 변경을 권장합니다.


3-19: imageCount는 파생값이면 중복 저장 대신 계산 프로퍼티 고려

현재는 생성 시 주입받는 구조라 images와 불일치 가능성이 생깁니다. 파생값이면 val imageCount get() = images.size 형태도 고려해볼 만합니다.

app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailAdapter.kt (1)

61-68: 코드 스타일 개선이 적용되었습니다.

명시적인 블록 브레이스 추가로 가독성이 향상되었습니다. 비교 로직은 동일하게 유지됩니다.

app/src/main/java/com/into/websoso/ui/feedDetail/model/FeedDetailModel.kt (1)

11-17: UI 모델이 Data 레이어 타입(FeedDetailEntity.NovelEntity)에 직접 의존
장기적으로 모듈/레이어 분리(테스트, 재사용) 관점에서 결합도가 커질 수 있어 NovelModel(또는 genreTag: String?만)로 끊는 방향을 추천합니다. 또한 novel == nullROMANCE로 폴백하면 의미가 섞일 수 있어 placeholder drawable(예: UNKNOWN)을 고려해도 좋겠습니다.

app/src/main/res/layout/item_feed_detail_header.xml (1)

106-113: (권장) ComposeView 폭/제약과 “전체” 하드코딩 문자열을 정리하면 UI 안정성이 좋아집니다.

  • Line 108~113: ComposeViewwrap_content + start만 걸려 있어, 콘텐츠에 따라 폭/정렬이 흔들릴 수 있습니다(가로 꽉이 목표면 0dp + start/end 제약 고려).
  • Line 231: "전체"는 문자열 리소스로 빼는 게 안전합니다(i18n).

Also applies to: 227-236

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06c5412 and 0e29d8c.

📒 Files selected for processing (32)
  • app/src/main/java/com/into/websoso/core/common/util/BindingAdapter.kt
  • app/src/main/java/com/into/websoso/data/mapper/FeedMapper.kt
  • app/src/main/java/com/into/websoso/data/model/FeedDetailEntity.kt
  • app/src/main/java/com/into/websoso/data/remote/response/FeedDetailResponseDto.kt
  • app/src/main/java/com/into/websoso/data/repository/FeedRepository.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailActivity.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailViewModel.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailAdapter.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailContentViewHolder.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailType.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/component/DoubleImageContainer.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/component/MultipleImageContainer.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/component/SingleImageContainer.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/component/TripleImageContainer.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/model/FeedDetailModel.kt
  • app/src/main/java/com/into/websoso/ui/feedDetail/model/Genre.kt
  • app/src/main/java/com/into/websoso/ui/mapper/FeedMapper.kt
  • app/src/main/res/drawable/btn_feed_novel_info.xml
  • app/src/main/res/drawable/ic_bl.xml
  • app/src/main/res/drawable/ic_drama.xml
  • app/src/main/res/drawable/ic_fantasy.xml
  • app/src/main/res/drawable/ic_feed_detail_novel_star.xml
  • app/src/main/res/drawable/ic_feed_detail_writer_star.xml
  • app/src/main/res/drawable/ic_hf.xml
  • app/src/main/res/drawable/ic_ln.xml
  • app/src/main/res/drawable/ic_mystery.xml
  • app/src/main/res/drawable/ic_romance.xml
  • app/src/main/res/drawable/ic_romance_fantasy.xml
  • app/src/main/res/drawable/ic_wuxia.xml
  • app/src/main/res/layout/item_feed.xml
  • app/src/main/res/layout/item_feed_detail_header.xml
  • core/resource/src/main/res/values/strings.xml
💤 Files with no reviewable changes (1)
  • app/src/main/java/com/into/websoso/ui/feedDetail/component/MultipleImageContainer.kt
🧰 Additional context used
🧬 Code graph analysis (3)
app/src/main/java/com/into/websoso/ui/feedDetail/component/SingleImageContainer.kt (1)
app/src/main/java/com/into/websoso/core/common/ui/component/AdaptationImage.kt (1)
  • AdaptationImage (11-32)
app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailContentViewHolder.kt (1)
app/src/main/java/com/into/websoso/ui/feedDetail/component/AdaptationFeedImageContainer.kt (1)
  • AdaptationFeedImageContainer (11-23)
app/src/main/java/com/into/websoso/ui/feedDetail/component/DoubleImageContainer.kt (1)
app/src/main/java/com/into/websoso/core/common/ui/component/AdaptationImage.kt (1)
  • AdaptationImage (11-32)
🪛 detekt (1.23.8)
app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailActivity.kt

[warning] 343-343: A call to the default constructor of an exception was detected. Instead one of the constructor overloads should be called. This allows to provide more meaningful exceptions.

(detekt.exceptions.ThrowingExceptionsWithoutMessageOrCause)


[warning] 351-351: A call to the default constructor of an exception was detected. Instead one of the constructor overloads should be called. This allows to provide more meaningful exceptions.

(detekt.exceptions.ThrowingExceptionsWithoutMessageOrCause)

⏰ 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 (23)
app/src/main/res/drawable/ic_ln.xml (2)

1-5: 벡터 드로어블 선언이 올바르게 구성되었습니다.

크기(70dp)와 뷰포트(70) 값이 일치하여 좋습니다. 표준 Android 벡터 드로어블 구조를 따르고 있습니다.


6-11: 배경 도형과 클리핑 경로가 적절합니다.

대각선 삼각형 배경과 클립 경로가 70x70 캔버스에 맞게 정의되어 있습니다. 장르별 아이콘의 시각적 차별화를 위한 배경 처리가 잘 구현되었습니다.

app/src/main/java/com/into/websoso/data/repository/FeedRepository.kt (1)

9-10: FeedDetailEntity import 추가: OK
리포지토리에서 상세 엔티티를 반환하도록 바뀐 흐름에 맞는 변경으로 보입니다.

app/src/main/res/drawable/ic_wuxia.xml (1)

1-16: 새 장르 아이콘 리소스 추가: OK
70dp/70dp 고정 크기라서 실제 사용처 ImageViewlayout_width/heightscaleType에 따라 의도치 않은 스케일/크롭이 생길 수 있으니, 적용된 화면에서만 한 번 최종 확인하면 충분해 보입니다.

app/src/main/res/drawable/ic_fantasy.xml (1)

1-16: 새 장르 아이콘 리소스 추가: OK
다른 장르 아이콘들과 동일한 패턴(clip-path + 2 path)이라 일관성도 좋아 보입니다. 위와 동일하게 실제 렌더링(스케일/크롭)만 화면에서 확인되면 될 것 같습니다.

app/src/main/res/drawable/ic_hf.xml (1)

1-16: 벡터 드로어블 리소스가 올바르게 정의되었습니다.

장르 아이콘으로 사용될 새로운 벡터 드로어블 리소스가 적절하게 추가되었습니다. XML 구조와 경로 데이터가 Android 표준을 따르고 있습니다.

app/src/main/res/drawable/ic_romance.xml (1)

1-23: 벡터 드로어블 리소스가 올바르게 정의되었습니다.

로맨스 장르 아이콘이 적절하게 추가되었습니다. 중첩된 그룹과 복잡한 경로 데이터가 올바르게 구성되어 있습니다.

app/src/main/res/drawable/ic_drama.xml (1)

1-20: 벡터 드로어블 리소스가 올바르게 정의되었습니다.

드라마 장르 아이콘이 적절하게 추가되었습니다. fill과 stroke를 함께 사용한 디자인이 올바르게 구현되어 있습니다.

app/src/main/res/drawable/ic_bl.xml (1)

1-23: 벡터 드로어블 리소스가 올바르게 정의되었습니다.

BL 장르 아이콘이 적절하게 추가되었습니다. 중첩된 그룹 구조와 경로 데이터가 올바르게 구성되어 있습니다.

app/src/main/java/com/into/websoso/data/mapper/FeedMapper.kt (1)

5-7: 임포트/타입 분리 방향은 적절합니다

Feed detail 전용 엔티티를 분리해 매핑 경로를 명확히 만든 점은 좋습니다.

app/src/main/res/drawable/btn_feed_novel_info.xml (1)

5-7: 외곽선 스타일 변경은 의도에 맞게 잘 반영됨

단색 채움 제거 + stroke 적용으로 더 가벼운 톤이 되었고, 테두리 색상도 리소스로 관리되어 좋습니다. 다만 눌림/비활성 상태가 필요하면 selector로 별도 정의되어 있는지 확인만 해주세요.

app/src/main/java/com/into/websoso/ui/feedDetail/component/TripleImageContainer.kt (1)

28-38: 클릭 인덱스 처리 개선은 좋음(중복 URL/성능 이슈 방지)

forEachIndexed로 index를 전달하는 변경은 안전하고 효율적입니다.

app/src/main/java/com/into/websoso/ui/mapper/FeedMapper.kt (1)

86-119: FeedDetailModel의 소설 정보 경로 분산 - 리팩토링 개선 권장

FeedDetailModel.feed.novel(NovelModel: 기본 정보)과 FeedDetailModel.novel(NovelEntity: 장르 포함)이 두 경로로 나뉘어 있습니다. 현재는 매퍼에서 동일 소스로 동시에 설정되어 실제 불일치는 없으나, 향후 유지보수 시 한쪽만 수정될 위험이 있습니다.

예를 들어 FeedDetailActivityfeed.novel.id/title을 사용하고, novelImage 계산 속성은 novel?.genre에 의존합니다. 이 구조를 정리하면 좋습니다:

  • 옵션 A: FeedModel.novel을 장르 정보까지 포함하도록 확장
  • 옵션 B: FeedDetailModel.novel 하나의 경로만 사용하고 FeedModel.novel 제거

[optional_refactor]

app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailType.kt (1)

4-9: LGTM! 타입 마이그레이션이 올바르게 적용되었습니다.

FeedModel에서 FeedDetailModel로의 전환이 깔끔하게 처리되었습니다. 프로퍼티명도 feedDetail로 변경되어 의미가 더 명확해졌습니다.

app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailAdapter.kt (1)

50-50: LGTM! 바인딩 로직이 새로운 모델 구조에 맞게 업데이트되었습니다.

Header.feedDetail을 사용하도록 변경되어 FeedDetailType의 프로퍼티 변경사항과 일관성이 유지됩니다.

app/src/main/java/com/into/websoso/ui/feedDetail/component/DoubleImageContainer.kt (2)

26-26: 간격 조정이 적용되었습니다.

이미지 간 수평 간격이 8dp에서 6dp로 줄어들어 더 밀집된 레이아웃을 제공합니다.


28-39: 이미지 렌더링 및 클릭 처리가 개선되었습니다.

여러 개선사항이 적용되었습니다:

  • take(2)로 안전하게 최대 2개 이미지만 처리
  • forEachIndexed로 인덱스를 직접 전달하여 indexOf 호출 제거 (성능 향상)
  • size(100.dp)로 고정 크기 적용하여 일관된 UI 제공

다양한 화면 밀도에서 100dp 크기가 적절한지 확인해주세요. 특히 작은 화면에서 레이아웃이 잘리지 않는지 테스트가 필요합니다.

app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailViewModel.kt (2)

9-9: 데이터 모델 마이그레이션이 적용되었습니다.

FeedEntity에서 FeedDetailEntity로 전환하여 피드 상세 정보를 더 풍부하게 제공할 수 있게 되었습니다.


92-92: 소설 정보가 모델에 추가되었습니다.

feedDetail.novelFeedDetailModel에 전달하여 소설 메타데이터를 UI에 표시할 수 있게 되었습니다.

app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailContentViewHolder.kt (2)

20-24: 모델 바인딩이 안전하게 업데이트되었습니다.

FeedDetailModel을 사용하도록 변경되었고, null-safe 접근이 적절히 적용되었습니다:

  • 사용자 아바타 URL 변환 시 feedDetail.user? 사용
  • 좋아요 상태 확인 시 feedDetail.feed?.isLiked == true로 null-safe 처리

30-30: 소설 장르 이미지 표시 기능이 추가되었습니다.

feedDetail.novelImage를 사용하여 소설 장르별 아이콘을 표시하는 기능이 추가되었습니다.

app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailActivity.kt (1)

489-492: UI 상태 참조가 feedDetail 기준으로 정리된 점은 좋습니다
헤더 생성이 feedDetailUiState.feedDetail로 일관되게 맞춰져 NPE/옵셔널 분기 복잡도가 줄어 보입니다.

Also applies to: 497-504, 564-566

app/src/main/res/layout/item_feed_detail_header.xml (1)

140-147: iv_feed_detail_novel_genre은 어댑터 코드에서 이미 설정되고 있습니다.

FeedDetailContentViewHolder.bind() 메서드에서 binding.ivFeedDetailNovelGenre.setImageResource(feedDetail.novelImage)로 명시적으로 이미지가 설정되고 있습니다. FeedDetailModelnovelImage 속성은 @DrawableRes Int를 반환하며, 장르 정보가 없을 경우 Genre.ROMANCE.drawableRes로 기본값을 제공하므로 런타임에 빈 상태가 될 가능성이 없습니다.

Comment on lines +15 to 27
@BindingAdapter(value = ["loads3ImageUrl", "cornerRadius"], requireAll = false)
fun loadS3ImageUrl(
view: ImageView,
s3ImageKey: String,
cornerRadius: Float?,
) {
val fullUrl: String = view.getS3ImageUrl(s3ImageKey)

view.load(fullUrl) {
error(img_loading_thumbnail)
if (cornerRadius != null) transformations(RoundedCornersTransformation(cornerRadius.toFloatPxFromDp()))
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n app/src/main/java/com/into/websoso/core/common/util/BindingAdapter.kt | head -40

Repository: Team-WSS/WSS-Android

Length of output: 1638


🌐 Web query:

DataBinding requireAll false non-nullable parameter null behavior Android

💡 Result:

Short answer: If you use requireAll = false, Data Binding will pass null for omitted reference-type attributes (and JVM default values for primitives). If your @BindingAdapter or setter in Kotlin declares non-nullable parameters, that null will trigger a runtime NPE (Kotlin inserts null-checks for non-null parameters). Make binding-adapter parameters nullable (e.g., Int?, CharSequence?) and handle null to avoid crashes (or box primitives to get null instead of 0). (medium.com)

Recommended patterns:

  • Declare reference params as nullable and guard: fun View.bindFoo(value: CharSequence?) { val v = value ?: default; ... }. (medium.com)
  • For primitives you want “no value” behavior, use boxed nullable types (Int?) so Data Binding will pass null instead of 0. (medium.com)

If you want, tell me a specific BindingAdapter signature or XML case and I’ll show the exact Kotlin fix.

Citations:


🏁 Script executed:

find . -type f -name "*.xml" | xargs grep -l "loads3ImageUrl" 2>/dev/null | head -5

Repository: Team-WSS/WSS-Android

Length of output: 168


🌐 Web query:

Coil image library transformations() multiple calls overwrite or accumulate

💡 Result:

Short answer: successive calls to transformations(...) on an ImageRequest.Builder replace the previous transformations (they do not automatically accumulate). Use one call with multiple Transformation objects (varargs or a list) or build from an existing request (newBuilder()) and set the combined list yourself. [1][2]

Sources:

  • Coil ImageView.load / ImageRequest builder docs (shows transformations as a request property). [1]
  • ImageRequest docs / builder immutability note — use newBuilder() to start from an existing request if you want to preserve/extend settings. [2]

🏁 Script executed:

grep -A 5 -B 5 "loads3ImageUrl\|cornerRadius" ./app/src/main/res/layout/dialog_avatar_change.xml

Repository: Team-WSS/WSS-Android

Length of output: 625


🏁 Script executed:

grep -A 5 -B 5 "loads3ImageUrl\|cornerRadius" ./app/src/main/res/layout/item_feed_detail_header.xml

Repository: Team-WSS/WSS-Android

Length of output: 674


🏁 Script executed:

# Check all layout files for complete usage patterns
find . -type f -name "*.xml" -exec grep -l "cornerRadius\|loads3ImageUrl" {} \; 2>/dev/null | head -10

Repository: Team-WSS/WSS-Android

Length of output: 593


🏁 Script executed:

# Check if any layout uses cornerRadius without loads3ImageUrl
for file in ./app/src/main/res/layout/dialog_avatar_change.xml ./app/src/main/res/layout/dialog_novel_detail_cover.xml ./app/src/main/res/layout/item_blocked_user.xml ./app/src/main/res/layout/item_common_keyword.xml ./app/src/main/res/layout/item_detail_explore_result.xml ./app/src/main/res/layout/item_feed_detail_comment.xml ./app/src/main/res/layout/item_my_activity.xml ./app/src/main/res/layout/item_normal_explore.xml ./app/src/main/res/layout/item_popular_novel.xml ./app/src/main/res/layout/item_recommended_novel_by_user_taste.xml; do
  echo "=== $file ==="
  grep -A 10 -B 2 "cornerRadius\|loads3ImageUrl" "$file" 2>/dev/null | head -20
done

Repository: Team-WSS/WSS-Android

Length of output: 6904


🏁 Script executed:

# Check the complete loadImageWithOptions function to understand if there's a pattern for handling multiple transformations
cat -n app/src/main/java/com/into/websoso/core/common/util/BindingAdapter.kt | sed -n '29,60p'

Repository: Team-WSS/WSS-Android

Length of output: 1456


requireAll=false + s3ImageKey: String 조합은 DataBinding NPE 크래시 위험

cornerRadius만 설정된 레이아웃(예: dialog_avatar_change.xml)에서 loads3ImageUrl이 null로 전달되는데, 현재 시그니처가 non-nullable로 선언되어 있어 런타임 NPE가 발생합니다. loadImageWithOptions처럼 s3ImageKey: String?로 변경하고 guard 처리하여 안전하게 처리해야 합니다.

🛠️ 제안 수정 (nullable + guard)
 @BindingAdapter(value = ["loads3ImageUrl", "cornerRadius"], requireAll = false)
 fun loadS3ImageUrl(
     view: ImageView,
-    s3ImageKey: String,
+    s3ImageKey: String?,
     cornerRadius: Float?,
 ) {
+    if (s3ImageKey.isNullOrBlank()) {
+        view.setImageResource(img_loading_thumbnail)
+        return
+    }
     val fullUrl: String = view.getS3ImageUrl(s3ImageKey)

     view.load(fullUrl) {
         error(img_loading_thumbnail)
         if (cornerRadius != null) transformations(RoundedCornersTransformation(cornerRadius.toFloatPxFromDp()))
     }
 }
📝 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.

Suggested change
@BindingAdapter(value = ["loads3ImageUrl", "cornerRadius"], requireAll = false)
fun loadS3ImageUrl(
view: ImageView,
s3ImageKey: String,
cornerRadius: Float?,
) {
val fullUrl: String = view.getS3ImageUrl(s3ImageKey)
view.load(fullUrl) {
error(img_loading_thumbnail)
if (cornerRadius != null) transformations(RoundedCornersTransformation(cornerRadius.toFloatPxFromDp()))
}
}
@BindingAdapter(value = ["loads3ImageUrl", "cornerRadius"], requireAll = false)
fun loadS3ImageUrl(
view: ImageView,
s3ImageKey: String?,
cornerRadius: Float?,
) {
if (s3ImageKey.isNullOrBlank()) {
view.setImageResource(img_loading_thumbnail)
return
}
val fullUrl: String = view.getS3ImageUrl(s3ImageKey)
view.load(fullUrl) {
error(img_loading_thumbnail)
if (cornerRadius != null) transformations(RoundedCornersTransformation(cornerRadius.toFloatPxFromDp()))
}
}
🤖 Prompt for AI Agents
In @app/src/main/java/com/into/websoso/core/common/util/BindingAdapter.kt around
lines 15 - 27, The BindingAdapter loadS3ImageUrl is declared with a non-null
s3ImageKey but requireAll=false can pass null and cause an NPE; change the
parameter s3ImageKey to nullable (String?) and guard at the start of
loadS3ImageUrl (e.g., if s3ImageKey.isNullOrEmpty() return or clear the
ImageView/set placeholder) before calling view.getS3ImageUrl and view.load;
mirror the safe pattern used in loadImageWithOptions to avoid calling extension
methods with a null key.

Comment on lines +46 to +55
@SerialName("novelThumbnailImage")
val novelThumbnailImage: String?,
@SerialName("novelGenre")
val novelGenre: String?,
@SerialName("novelAuthor")
val novelAuthor: String?,
@SerialName("feedWriterNovelRating")
val feedWriterNovelRating: Float?,
@SerialName("novelDescription")
val novelDescription: String?,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "FeedDetailResponseDto.kt" -type f

Repository: Team-WSS/WSS-Android

Length of output: 261


🏁 Script executed:

git ls-files "*.kt" | grep -i "feeddetail\|FeedDetail" | head -20

Repository: Team-WSS/WSS-Android

Length of output: 1634


🌐 Web query:

kotlinx.serialization nullable field without default value missing from JSON MissingFieldException

💡 Result:

Why it happens (short)

  • By default kotlinx.serialization treats a property as "required" unless it has a default value. A nullable property without a default is considered non-optional, so if the JSON omits it the generated serializer throws MissingFieldException. [1][2]

How to fix (pick one)

  • Give the property a default (e.g. val description: String? = null). That makes the element optional in the descriptor and prevents the exception. [2]
  • Or configure Json to treat missing nullable fields as nulls: Json { explicitNulls = false } — with this setting absence of a nullable field is interpreted as null during decoding. (Be careful: this changes encoding/decoding symmetry for nullable-with-non-null-default cases.) [1]
  • Alternatively, use coerceInputValues for some invalid inputs to be treated as missing (not a general fix for omitted fields). [3]

References

  • explicitNulls behavior and example. [1]
  • MissingFieldException / optional element rules (default values → optional). [2]
  • coerceInputValues docs. [3]

🏁 Script executed:

cat -n app/src/main/java/com/into/websoso/data/remote/response/FeedDetailResponseDto.kt

Repository: Team-WSS/WSS-Android

Length of output: 2068


🏁 Script executed:

rg "Json\s*\{" -A 10 --type kt

Repository: Team-WSS/WSS-Android

Length of output: 90


🏁 Script executed:

rg "Json\s*\{" -A 10 --type-list | head -5

Repository: Team-WSS/WSS-Android

Length of output: 453


🏁 Script executed:

find . -name "*.kt" -type f -exec grep -l "Json\s*{" {} \; | head -10

Repository: Team-WSS/WSS-Android

Length of output: 142


🏁 Script executed:

rg "explicitNulls|ignoreUnknownKeys" --type kt

Repository: Team-WSS/WSS-Android

Length of output: 90


🏁 Script executed:

cat core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt

Repository: Team-WSS/WSS-Android

Length of output: 2562


🏁 Script executed:

rg "explicitNulls|ignoreUnknownKeys|Json"

Repository: Team-WSS/WSS-Android

Length of output: 50379


kotlinx.serialization 역호환성: 새 필드에 = null 기본값 필요

현재 새 필드들(novelThumbnailImage, novelGenre, novelAuthor, feedWriterNovelRating, novelDescription)이 String?/Float? 타입이지만 기본값이 없어, 서버가 해당 키를 내려주지 않는 경우(구버전 응답 등) 디코딩 시 MissingFieldException이 발생합니다.

ignoreUnknownKeys = true는 JSON의 여분 키만 무시하고, 누락된 필수 필드는 여전히 예외를 발생시킵니다. 역호환성을 위해 각 필드에 = null 기본값을 추가하세요.

제안 diff
 @SerialName("novelThumbnailImage")
-    val novelThumbnailImage: String?,
+    val novelThumbnailImage: String? = null,
 @SerialName("novelGenre")
-    val novelGenre: String?,
+    val novelGenre: String? = null,
 @SerialName("novelAuthor")
-    val novelAuthor: String?,
+    val novelAuthor: String? = null,
 @SerialName("feedWriterNovelRating")
-    val feedWriterNovelRating: Float?,
+    val feedWriterNovelRating: Float? = null,
 @SerialName("novelDescription")
-    val novelDescription: String?,
+    val novelDescription: String? = null,
📝 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.

Suggested change
@SerialName("novelThumbnailImage")
val novelThumbnailImage: String?,
@SerialName("novelGenre")
val novelGenre: String?,
@SerialName("novelAuthor")
val novelAuthor: String?,
@SerialName("feedWriterNovelRating")
val feedWriterNovelRating: Float?,
@SerialName("novelDescription")
val novelDescription: String?,
@SerialName("novelThumbnailImage")
val novelThumbnailImage: String? = null,
@SerialName("novelGenre")
val novelGenre: String? = null,
@SerialName("novelAuthor")
val novelAuthor: String? = null,
@SerialName("feedWriterNovelRating")
val feedWriterNovelRating: Float? = null,
@SerialName("novelDescription")
val novelDescription: String? = null,
🤖 Prompt for AI Agents
In
@app/src/main/java/com/into/websoso/data/remote/response/FeedDetailResponseDto.kt
around lines 46 - 55, The new nullable properties in FeedDetailResponseDto
(novelThumbnailImage, novelGenre, novelAuthor, feedWriterNovelRating,
novelDescription) lack default values and will cause kotlinx.serialization
MissingFieldException when the server omits them; update each property
declaration in the FeedDetailResponseDto primary constructor to provide a
default of = null so they become truly optional during decoding.

Comment on lines 25 to 29
binding.cvFeedImage.setContent {
AdaptationFeedImageContainer(feed.imageUrls) { index ->
feedDetailClickListener.onFeedImageClick(index, feed.imageUrls)
AdaptationFeedImageContainer(feedDetail.feed?.imageUrls ?: return@setContent) { index ->
feedDetailClickListener.onFeedImageClick(index, feedDetail.feed.imageUrls)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

잠재적인 NPE 위험이 있습니다.

Line 26에서 feedDetail.feed?.imageUrls가 null이면 early return하지만, Line 27의 onFeedImageClick 람다 내부에서 feedDetail.feed.imageUrls를 null-safe 연산자 없이 접근합니다. feedDetail.feed가 null인 경우 NPE가 발생할 수 있습니다.

🐛 NPE 방지를 위한 수정 제안
         binding.cvFeedImage.setContent {
-            AdaptationFeedImageContainer(feedDetail.feed?.imageUrls ?: return@setContent) { index ->
-                feedDetailClickListener.onFeedImageClick(index, feedDetail.feed.imageUrls)
+            val imageUrls = feedDetail.feed?.imageUrls ?: return@setContent
+            AdaptationFeedImageContainer(imageUrls) { index ->
+                feedDetailClickListener.onFeedImageClick(index, imageUrls)
             }
         }
📝 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.

Suggested change
binding.cvFeedImage.setContent {
AdaptationFeedImageContainer(feed.imageUrls) { index ->
feedDetailClickListener.onFeedImageClick(index, feed.imageUrls)
AdaptationFeedImageContainer(feedDetail.feed?.imageUrls ?: return@setContent) { index ->
feedDetailClickListener.onFeedImageClick(index, feedDetail.feed.imageUrls)
}
}
binding.cvFeedImage.setContent {
val imageUrls = feedDetail.feed?.imageUrls ?: return@setContent
AdaptationFeedImageContainer(imageUrls) { index ->
feedDetailClickListener.onFeedImageClick(index, imageUrls)
}
}
🤖 Prompt for AI Agents
In
@app/src/main/java/com/into/websoso/ui/feedDetail/adapter/FeedDetailContentViewHolder.kt
around lines 25 - 29, The code calls binding.cvFeedImage.setContent with
AdaptationFeedImageContainer using feedDetail.feed?.imageUrls for early-return,
but the lambda passed to AdaptationFeedImageContainer references
feedDetail.feed.imageUrls without null-safety which can NPE; fix by capturing
the non-null image list into a local val (e.g., val images =
feedDetail.feed?.imageUrls ?: return@setContent) before calling
AdaptationFeedImageContainer and then use that local images inside the
onFeedImageClick lambda when invoking
feedDetailClickListener.onFeedImageClick(index, images).

Comment on lines +3 to +5
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

matchParentSize() import 누락으로 컴파일 실패 가능
Line 29의 Modifier.matchParentSize()import androidx.compose.foundation.layout.matchParentSize가 없으면 unresolved reference가 납니다.

🛠️ Proposed fix
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.matchParentSize

Also applies to: 20-35

🤖 Prompt for AI Agents
In
@app/src/main/java/com/into/websoso/ui/feedDetail/component/SingleImageContainer.kt
around lines 3 - 5, The build can fail because Modifier.matchParentSize() used
in SingleImageContainer.kt is missing its import; add the import
androidx.compose.foundation.layout.matchParentSize to the file so the unresolved
reference for Modifier.matchParentSize (used in the SingleImageContainer
composable) is resolved.

Comment on lines +340 to 355
DialogRemovePopupMenuBinding::class -> {
FeedRemoveDialogFragment
.newInstance(
menuType = menuType ?: throw IllegalArgumentException(),
event = { event() },
).show(supportFragmentManager, FeedRemoveDialogFragment.TAG)
}

DialogReportPopupMenuBinding::class ->
DialogReportPopupMenuBinding::class -> {
FeedReportDialogFragment
.newInstance(
menuType = menuType ?: throw IllegalArgumentException(),
event = { event() },
).show(supportFragmentManager, FeedReportDialogFragment.TAG)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

IllegalArgumentException()에 메시지/원인 없이 throw (detekt 경고)
디버깅 가능성을 위해 어떤 값이 왜 필요한지 메시지를 넣는 게 좋습니다.

🛠️ Proposed fix
 menuType = menuType ?: throw IllegalArgumentException(),
+// ->
+menuType = menuType ?: throw IllegalArgumentException("menuType is required for ${Dialog::class.simpleName}"),
🧰 Tools
🪛 detekt (1.23.8)

[warning] 343-343: A call to the default constructor of an exception was detected. Instead one of the constructor overloads should be called. This allows to provide more meaningful exceptions.

(detekt.exceptions.ThrowingExceptionsWithoutMessageOrCause)


[warning] 351-351: A call to the default constructor of an exception was detected. Instead one of the constructor overloads should be called. This allows to provide more meaningful exceptions.

(detekt.exceptions.ThrowingExceptionsWithoutMessageOrCause)

🤖 Prompt for AI Agents
In @app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailActivity.kt
around lines 340 - 355, The code currently throws plain
IllegalArgumentException() when menuType is null in the
DialogRemovePopupMenuBinding and DialogReportPopupMenuBinding branches; update
the throws (or use requireNotNull) to include a descriptive message such as
"menuType is null when creating FeedRemoveDialogFragment" and "menuType is null
when creating FeedReportDialogFragment" (or similar) so the exception identifies
which fragment and parameter failed; adjust the two calls inside
FeedDetailActivity where FeedRemoveDialogFragment.newInstance and
FeedReportDialogFragment.newInstance are invoked.

Comment on lines +65 to 81
val feedDetail = (result[1] as FeedDetailEntity)
val comments = result[2] as CommentsEntity

val uiFeed = feed.toUi()
val updatedFeed = if (feed.isLiked == isLiked) {
val uiFeed = feedDetail.toUi().feed
val updatedFeed = if (feedDetail.isLiked == isLiked) {
uiFeed
} else if (!isLiked && feed.isLiked) {
uiFeed.copy(
} else if (!isLiked && feedDetail.isLiked) {
uiFeed?.copy(
isLiked = false,
likeCount = feed.likeCount - 1,
likeCount = feedDetail.likeCount - 1,
)
} else {
uiFeed.copy(
uiFeed?.copy(
isLiked = true,
likeCount = feed.likeCount + 1,
likeCount = feedDetail.likeCount + 1,
)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Null 안전성 처리에 일관성이 필요합니다.

Line 70에서 uiFeed를 직접 반환하지만, Line 72와 77에서는 uiFeed?.copy로 null-safe 호출을 사용합니다. Line 68에서 feedDetail.toUi().feed가 nullable을 반환하는 경우, Line 70의 분기에서도 null 처리가 필요합니다.

🛡️ Null 안전성 개선 제안
-                        val updatedFeed = if (feedDetail.isLiked == isLiked) {
-                            uiFeed
-                        } else if (!isLiked && feedDetail.isLiked) {
+                        val updatedFeed = if (feedDetail.isLiked == isLiked) {
+                            uiFeed
+                        } else if (!isLiked && feedDetail.isLiked) {
                             uiFeed?.copy(
                                 isLiked = false,
                                 likeCount = feedDetail.likeCount - 1,
                             )
                         } else {
                             uiFeed?.copy(
                                 isLiked = true,
                                 likeCount = feedDetail.likeCount + 1,
                             )
                         }

또는 uiFeed가 null이 될 수 없다면, Line 68 이후에 null 체크를 추가하는 것을 고려해주세요.

🤖 Prompt for AI Agents
In @app/src/main/java/com/into/websoso/ui/feedDetail/FeedDetailViewModel.kt
around lines 65 - 81, feedDetail.toUi().feed을 uiFeed에 할당한 후 분기에서 null 처리가 일관되지
않아 NPE 가능성이 있으니, uiFeed가 nullable인지 확정하고 일관된 처리를 적용하세요: 만약 uiFeed가 null일 수 있다면
기존의 uiFeed?.copy(...) 패턴처럼 모든 분기에서 안전 호출을 사용하거나 early return/대체값(예: null 또는 기본
Feed 객체)으로 처리하고, uiFeed가 절대 null이 될 수 없다면 val uiFeed = feedDetail.toUi().feed!!로
명시적 non-null 보장을 하여 updatedFeed 분기(현재의 첫 분기에서 직접 uiFeed 반환, 다른 분기에서는
uiFeed?.copy 사용)들을 일관되게 정리하세요.

Comment on lines +6 to +26
enum class Genre(
val tag: String,
@DrawableRes val drawableRes: Int,
) {
ROMANCE("romance", R.drawable.ic_romance),
ROMANCE_FANTASY("romanceFantasy", R.drawable.ic_romance_fantasy),
BL("BL", R.drawable.ic_bl),
FANTASY("fantasy", R.drawable.ic_fantasy),
MODERN_FANTASY(tag = "modernFantasy", drawableRes = R.drawable.ic_hf),
WUXIA("wuxia", R.drawable.ic_wuxia),
LIGHT_NOVEL(
"lightNovel",
R.drawable.ic_ln,
),
DRAMA("drama", R.drawable.ic_drama),
MYSTERY("mystery", R.drawable.ic_mystery),
;

companion object {
fun from(tag: String): Genre = entries.find { it.tag == tag } ?: ROMANCE
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# 프로젝트 Kotlin 버전 확인
find . -name "build.gradle" -o -name "build.gradle.kts" | head -5

Repository: Team-WSS/WSS-Android

Length of output: 200


🏁 Script executed:

# build.gradle 또는 build.gradle.kts 파일 검색 및 Kotlin 버전 확인
fd -e "build.gradle*" . | head -10 | xargs -I {} sh -c 'echo "=== {} ===" && grep -i "kotlin" "{}" | head -10'

Repository: Team-WSS/WSS-Android

Length of output: 46


🏁 Script executed:

# gradle.properties 확인
fd -name "gradle.properties" . | xargs -I {} sh -c 'echo "=== {} ===" && cat {}'

Repository: Team-WSS/WSS-Android

Length of output: 294


🏁 Script executed:

# Genre.from() 호출 위치 확인
rg "Genre\.from\(" --type kotlin -B 2 -A 2

Repository: Team-WSS/WSS-Android

Length of output: 580


🏁 Script executed:

# API 통신 관련 코드 검색 - genre 매핑 패턴 확인
rg "genre|Genre" --type kotlin -B 2 -A 2 | grep -A 5 -B 5 "from\|server\|api\|response" | head -50

Repository: Team-WSS/WSS-Android

Length of output: 5350


🏁 Script executed:

# app/build.gradle.kts에서 Kotlin 버전 확인
cat app/build.gradle.kts | grep -i "kotlin" | head -20

Repository: Team-WSS/WSS-Android

Length of output: 164


🏁 Script executed:

# 최상위 build.gradle.kts 확인
cat build.gradle.kts | grep -i "kotlin" | head -20

Repository: Team-WSS/WSS-Android

Length of output: 209


🏁 Script executed:

# 검토 대상 Genre enum 파일 전체 확인
cat app/src/main/java/com/into/websoso/ui/feedDetail/model/Genre.kt

Repository: Team-WSS/WSS-Android

Length of output: 848


🏁 Script executed:

# 서버 API 응답 관련 데이터 클래스 확인 (novel/genre 관련)
rg "class.*Novel|data class.*Novel" --type kotlin -A 10 | head -50

Repository: Team-WSS/WSS-Android

Length of output: 4930


🏁 Script executed:

# API 응답에서 genre 필드가 어떻게 정의되어 있는지 확인
rg "genre.*:" --type kotlin -B 2 -A 2 | grep -A 5 -B 5 "String\|val genre" | head -40

Repository: Team-WSS/WSS-Android

Length of output: 3429


🏁 Script executed:

# FeedDetailModel.kt 파일 확인 - novel 필드 정의
cat app/src/main/java/com/into/websoso/ui/feedDetail/model/FeedDetailModel.kt

Repository: Team-WSS/WSS-Android

Length of output: 692


🏁 Script executed:

# libs.versions.toml 또는 gradle.properties에서 Kotlin 버전 확인
find . -name "libs.versions.toml" -o -name "gradle.properties" | xargs cat 2>/dev/null | grep -i "kotlin"

Repository: Team-WSS/WSS-Android

Length of output: 1601


🏁 Script executed:

# API 응답 관련 데이터 모델 - genre 필드 타입 확인
rg "val genre|genre:" --type kotlin | grep -v "Genre\." | head -20

Repository: Team-WSS/WSS-Android

Length of output: 2428


대소문자 일치 규칙 확인 후 필요시 정규화 추가

from(tag)가 완전일치만 보므로, "BL"처럼 케이스가 섞인 enum 값과 서버에서 소문자로 내려오는 응답이 일치하지 않을 수 있습니다. API 스펙에서 genre 태그 케이싱 규칙을 확인하고, 필요하면 ignoreCase = true 또는 .trim()을 추가하여 안정성을 높이세요.

🛠️ 권장 변경사항
 companion object {
-    fun from(tag: String): Genre = entries.find { it.tag == tag } ?: ROMANCE
+    fun from(tag: String): Genre =
+        entries.firstOrNull { it.tag.equals(tag.trim(), ignoreCase = true) } ?: ROMANCE
 }
🤖 Prompt for AI Agents
In @app/src/main/java/com/into/websoso/ui/feedDetail/model/Genre.kt around lines
6 - 26, The Genre.from(tag) lookup currently does a case-sensitive exact match
and can fail for inputs like a lowercase "bl"; update the lookup in the Genre
companion object (function from(tag)) to normalize the input and comparison by
trimming and comparing case-insensitively (e.g., normalize both the incoming tag
and Genre.tag via trim() and toLowerCase() or use equalsIgnoreCase), so
entries.find matches regardless of casing/whitespace and fallback to ROMANCE
remains unchanged.

Comment on lines 12 to 15
<variable
name="feed"
type="com.into.websoso.ui.main.feed.model.FeedModel" />
name="feedDetail"
type="com.into.websoso.ui.feedDetail.model.FeedDetailModel" />
</data>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

@string/feed_score_count(...) 호출 인자 수 불일치(크래시 가능) — 리소스 분리/호출 통일 필요.

Line 212, Line 253에서 feed_score_count를 1개 인자로 호출하고 있고, item_feed.xml에서는 같은 리소스를 2개 인자로 호출합니다. 문자열 리소스 포맷이 어느 쪽이든 한쪽은 런타임에서 깨질 수 있어요. “점수만”/“점수+카운트”를 분리하거나 모든 호출을 동일 시그니처로 맞춰주세요.

Also applies to: 179-260

🤖 Prompt for AI Agents
In @app/src/main/res/layout/item_feed_detail_header.xml around lines 12 - 15,
The string resource feed_score_count is being called with different argument
counts across layouts (e.g., item_feed.xml uses two args while
item_feed_detail_header.xml uses one), which can crash at runtime; fix by
splitting the resource into two clearly named entries (e.g., feed_score_only and
feed_score_with_count) and update the data-binding expressions in both layouts
(replace usages of feed_score_count in item_feed.xml and
item_feed_detail_header.xml) to call the appropriate resource with the matching
number of arguments, or alternatively make all calls supply the same argument
list/signature if you prefer a single resource.


<!-- 피드 뷰 -->
<string name="feed_score_count">%.1f (%d)</string>
<string name="feed_score_count">%.1f</string>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# feed_score_count 문자열 리소스의 모든 사용처를 찾아 포맷 인자 개수를 확인합니다.

echo "=== feed_score_count 사용처 검색 ==="
rg -n 'feed_score_count' --type=kotlin --type=java

echo -e "\n=== 상세 컨텍스트 (앞뒤 5줄) ==="
rg -n -B 5 -A 5 'feed_score_count' --type=kotlin --type=java

Repository: Team-WSS/WSS-Android

Length of output: 120


🏁 Script executed:

#!/bin/bash
# 모든 파일 타입에서 feed_score_count를 검색합니다.

echo "=== 전체 파일에서 feed_score_count 검색 ==="
rg -n 'feed_score_count' .

echo -e "\n=== R.string.feed_score_count 검색 ==="
rg -n 'R\.string\.feed_score_count' .

echo -e "\n=== 레이아웃 파일에서 검색 ==="
fd -e xml -path '*/layout*' -o -path '*/res*' | xargs rg -l 'feed_score_count' 2>/dev/null || echo "No matches in layout files"

echo -e "\n=== strings.xml 파일 내용 확인 ==="
cat core/resource/src/main/res/values/strings.xml | grep -n 'feed_score_count' -A 2 -B 2

Repository: Team-WSS/WSS-Android

Length of output: 1334


데이터 바인딩 표현식에서 포맷 인자 개수 불일치로 인한 런타임 예외 위험

문자열 포맷이 "%.1f"로 변경되어 1개의 인자만 받도록 수정되었으나, 일부 사용처에서 여전히 이전 포맷에 맞춰 2개의 인자를 전달하고 있습니다.

문제가 있는 호출:

  • app/src/main/res/layout/item_feed.xml:217 - @string/feed_score_count(feed.novel.rating, feed.novel.ratingCount) ❌ 2개 인자 전달 (1개만 필요)

올바른 호출:

  • app/src/main/res/layout/item_feed_detail_header.xml:212 - 1개 인자 ✓
  • app/src/main/res/layout/item_feed_detail_header.xml:253 - 1개 인자 ✓

item_feed.xml의 호출을 새로운 포맷에 맞게 수정해야 하며, 그렇지 않으면 런타임 예외가 발생합니다.

🤖 Prompt for AI Agents
In @core/resource/src/main/res/values/strings.xml at line 98, The string
resource feed_score_count now expects a single float argument ("%.1f"), but one
binding still passes two args; update the data-binding expression in the
item_feed layout that currently calls
@string/feed_score_count(feed.novel.rating, feed.novel.ratingCount) to pass only
the rating (e.g., @string/feed_score_count(feed.novel.rating)) so it matches the
feed_score_count format and avoids runtime FormatException; verify other usages
already passing one arg remain unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 피드 상세보기 뷰 수정

3 participants