Skip to content

Conversation

@chanho0908
Copy link
Member

@chanho0908 chanho0908 commented Feb 9, 2026

이슈 번호

#52

리뷰/머지 희망 기한 (선택)

작업내용

  • 인증샷 상세 화면 기본 UI 구현

결과물

image

리뷰어에게 추가로 요구하는 사항 (선택)

이번 PR은 기본 UI 만 구현했어 !
화면 이동과 인터렉션은 새로운 PR로 만드는게 나중에 참고하기 편할 것 같아서 이번 PR에선 API 연동과 기본 UI 구현쪽만 봐주면 될 것 같아

- 상세 보기 화면 진입 시 `goalTitle`을 직접 전달하는 방식에서, `goalId`를 통해 서버에서 `goalTitle`을 조회하는 방식으로 변경했습니다.
- 이에 따라 `TaskCertificationDetailRoute`의 탐색 인자에서 `goalTitle`이 제거되었습니다.
@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

📝 Walkthrough

Walkthrough

포토로그 조회 기능을 구현하기 위해 네트워크, 도메인, 데이터, UI 계층을 확장하는 변경사항입니다. 새로운 TaskCertificationDetail 화면을 추가하여 사진 인증의 상세 정보 조회와 반응(reaction) 선택 기능을 제공합니다. 6개의 감정 표현 이모티콘 drawable 리소스, 상대 시간 포맷팅 유틸리티, 그리고 관련 문자열 리소스를 추가하며, 네비게이션 구조를 goalId 기반 파라미터 라우팅으로 변경합니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes


상세 검토 의견

강점

  1. 계층화된 구조의 일관성
    네트워크(Response) → 도메인(Model) → 데이터(Repository) → UI(ViewModel, Screen)의 명확한 계층 구조가 유지되고 있습니다. 각 계층의 책임이 분리되어 있어 좋습니다.

  2. 매퍼를 통한 변환 로직
    PhotologMapper에서 response를 domain 모델로 변환하는 확장 함수(toDomain())를 활용한 것은 깔끔한 패턴입니다.

  3. UI 상태 관리의 명확성
    TaskCertificationDetailUiState에서 canModify, canReaction 같은 파생 상태를 계산 속성으로 제공하는 것은 UI 로직 단순화에 좋습니다.

  4. 프리뷰 데이터 제공
    TaskCertificationPreviewData, TaskCertificationDetailPreviewProvider를 통해 UI 컴포넌트 프리뷰를 위한 테스트 데이터를 체계적으로 제공하고 있습니다.


🤔 검토할 사항

  1. RelativeTimeFormatter 에러 처리

    val instant = Instant.parse(uploadedAt)

    현재 구현에서 파싱 실패 시 빈 문자열을 반환하는데, 이것이 의도된 동작인가요? 로그 기록이나 더 자세한 폴백 메시지가 필요하지 않을까요?

    제안: 파싱 실패 시 기본값이나 로깅을 추가하는 것이 디버깅에 도움될 것 같습니다.

  2. PhotoLogResponse 역직렬화 안정성
    Response 객체의 모든 필드가 null을 허용하지 않는데, API에서 null 값을 반환할 가능성이 있다면 @SerialName 또는 기본값 정의를 고려하시기 바랍니다.

  3. TaskCertificationDetailViewModel의 TODO

    Sting -> TODO()

    Sting 기능이 아직 미구현 상태입니다. 이것이 향후 작업인지, 아니면 현재 병합 전에 구현해야 하는지 명확히 해주시면 좋겠습니다.

  4. updatePartnerReaction 네이밍
    현재 파트너의 반응을 업데이트하는 구조인데, 이것이 실제로 로컬 UI 상태 업데이트인지, 아니면 서버에 반영되어야 하는지 불명확합니다.

    질문: 이 메서드는 순수하게 UI 상태만 업데이트하는 건가요? 아니면 API 호출이 필요한 건가요?

  5. CommentTextField 파라미터 순서 변경
    수정자(modifier)를 초반으로 옮기는 것은 Compose 관례를 따르는 것으로 좋으나, 기존 호출 사이트에서 호환성 이슈가 없는지 확인이 필요합니다.


💡 개선 제안

  1. PhotologDetailUiModel.isCertificated 복잡도
    현재 여러 null 체크가 함께 있는데, 가독성을 위해 다음과 같이 리팩토링할 수 있습니다:

    val isCertificated: Boolean
        get() = listOfNotNull(
            photologId, imageUrl, comment, 
            verificationDate, uploaderName
        ).size == 5 && goalId != -1L && uploadedAt.isNotEmpty()

    또는 별도의 함수로 추출하는 것도 고려해보세요.

  2. ReactionBar의 선택 상태 피드백
    현재 selectedReaction을 전달받지만, UI에서 선택된 항목의 시각적 구분이 명확한지 검토가 필요합니다.

  3. 네비게이션 기본값
    TaskCertificationDetailRouteARG_GOAL_ID 기본값이 -1L인데, 이것이 "로드 안 함" 상태를 의미한다면 보다 명확한 상수명(예: UNINITIALIZED_GOAL_ID)을 사용하는 것을 권장합니다.


📋 마이너 체크리스트

  • 모든 drawable 리소스 (ic_keepi_*)가 다양한 해상도에서 테스트되었는지 확인
  • TaskCertificationGraphstartDestination 변경이 다른 화면 진입 경로에 영향을 주지 않는지 검증
  • RelativeTimeFormatter에 대한 단위 테스트 존재 여부 확인
  • 한국어 문자열 리소스들이 모두 올바르게 인코딩되어 있는지 확인

전반적으로 잘 구조화된 변경사항입니다. 위의 검토 항목들을 함께 논의하면 더욱 견고한 코드가 될 것 같습니다.

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 pull request의 주요 변화를 명확히 반영하고 있습니다. '인증샷 보기 UI 구현'은 이 PR의 핵심인 certification-detail 화면의 기본 UI 구현을 간결하고 구체적으로 설명합니다.
Description check ✅ Passed PR 설명이 변경사항과 충분히 관련이 있습니다. 작업 내용에서 '인증샷 상세 화면 기본 UI 구현'을 명시하고 있으며, 스크린샷과 함께 API 연동과 UI 구현에 집중했음을 잘 설명하고 있습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#52-task-certification-detail

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

@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: 13

Caution

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

⚠️ Outside diff range comments (1)
core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt (1)

83-90: ⚠️ Potential issue | 🟡 Minor

enabled = false일 때 클릭 이벤트가 여전히 포커스를 요청합니다.

enabled 파라미터를 BasicTextField에 전달하여 입력을 막고 있지만, BoxnoRippleClickable(Line 88-89)과 CommentCirclenoRippleClickable(Line 143-145)에서는 enabled 상태와 무관하게 focusRequester.requestFocus()를 호출합니다.

비활성 상태에서 포커스가 요청되면 키보드가 불필요하게 표시되거나 예상치 못한 UI 동작이 발생할 수 있습니다.

🛠️ 개선 제안
             modifier =
                 modifier
                     .onGloballyPositioned { coordinates ->
                         onPositioned(coordinates.boundsInRoot())
                     }.noRippleClickable {
-                        focusRequester.requestFocus()
+                        if (enabled) focusRequester.requestFocus()
                     },

CommentCircle의 클릭 핸들러도 동일하게 enabled 체크를 추가해 주세요.

🤖 Fix all issues with AI agents
In `@core/design-system/src/main/res/drawable/ic_keepi_angry.xml`:
- Around line 1-5: 벡터 루트 태그 (<vector>)의 android:width 속성(132dp)과
android:viewportWidth 속성(133)이 불일치합니다; <vector>의 android:width 또는
android:viewportWidth 중 하나를 다른 값에 맞춰 동일한 숫자로 맞추어 (예: android:viewportWidth="132"
또는 android:width="133dp") 수정하여 수평 비율 일관성을 보장하세요. 참조 대상: <vector> 태그의
android:width 및 android:viewportWidth 속성.

In `@core/design-system/src/main/res/drawable/ic_keepi_sting.xml`:
- Around line 1-5: The review notes that ic_keepi_sting is intentionally larger
because BackgroundCard uses it as a decorative background, but ic_keepi_angry
(132x113dp) is unused; either remove ic_keepi_angry or normalize it to the
52x52dp reaction size and wire it into ReactionUiModel if you intend to use it.
Locate the drawable resource ic_keepi_angry.xml, confirm there are no
references, then either delete the file or update its
android:width/android:height and viewport attributes to match the 52x52dp
convention and add the appropriate entry in ReactionUiModel (and any mapping
used by Reaction UI) so sizes are consistent with other reaction icons; if
keeping ic_keepi_sting as a background, leave it unchanged and add a clarifying
comment in BackgroundCard.

In
`@core/network/src/main/java/com/twix/network/model/response/photolog/mapper/PhotologMapper.kt`:
- Line 17: The mapper currently uses uploadedAt.toString() which turns a null
uploadedAt into the literal string "null"; in PhotologMapper (the mapping that
builds PhotologDetailResponse.uploadedAt) change the assignment to preserve
nulls (e.g., use uploadedAt?.toString() or uploadedAt?.let { /* desired
formatting */ } ) so PhotologDetailResponse.uploadedAt remains null when the
source is null and does not propagate the string "null".

In `@core/network/src/main/java/com/twix/network/service/PhotoLogService.kt`:
- Around line 10-18: The `@GET` annotation in PhotoLogService.kt is inconsistent:
change the fetchPhotoLogs endpoint from `@GET`("/api/v1/photologs/goals/{goalId}")
to match the project convention without a leading slash (i.e.,
"api/v1/photologs/goals/{goalId}"), so getUploadUrl and fetchPhotoLogs resolve
relative to the base URL the same way; update only the annotation on
fetchPhotoLogs (function fetchPhotoLogs) to remove the leading '/' and ensure no
other endpoints in this file use a leading slash.

In
`@feature/task-certification/src/main/java/com/twix/task_certification/certification/component/ReactionBox.kt`:
- Around line 28-91: ReactionBox duplicates ReactionBar UI logic; extract the
shared UI into a single reusable composable (e.g., ReactionSelector) that takes
parameters for onSelectReaction: (GoalReactionType)->Unit, selectedReaction:
GoalReactionType?, modifier: Modifier, and any visual tweaks (shape, height,
shadow offsets, divider color) so both ReactionBox and ReactionBar simply call
ReactionSelector. Move the loop over ReactionUiModel.entries, the Row layout,
per-item Box/Image rendering and the VerticalDivider logic into
ReactionSelector, and update ReactionBox and ReactionBar to delegate to it while
passing their specific parameters (if any).
- Around line 76-79: The Image in ReactionBox currently sets contentDescription
= null; update accessibility by adding a `@StringRes` property (e.g.,
contentDescriptionRes: Int) to ReactionUiModel and use
stringResource(reaction.contentDescriptionRes) for the Image's
contentDescription in ReactionBox (and any other place constructing the Image),
ensuring all ReactionUiModel instances are provided appropriate string resource
IDs like "happy", "love", etc.; also update tests/usages where ReactionUiModel
is constructed to include the new contentDescriptionRes.

In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/component/PhotologCard.kt`:
- Around line 29-44: In PhotologCard's Box modifier chain the
.clip(RoundedCornerShape(12.dp)) is applied before .border(...), causing the
border to be clipped; move .border(...) earlier than .clip(...) (i.e., apply
.border(...) then .clip(...)) so the full 1.6.dp border is visible, or
alternatively remove .clip(...) and supply the shape to background (use
background(shape = RoundedCornerShape(12.dp))) to preserve the rounded
background and border correctly.

In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/model/PhotoLogsUiModel.kt`:
- Around line 19-27: The current PhotoLogs.toUiModel() uses
photologDetails.first { it.isMine } and first { !it.isMine } which will throw if
no element matches; change to use firstOrNull and provide safe fallbacks: obtain
val my = photologDetails.firstOrNull { it.isMine } and val partner =
photologDetails.firstOrNull { !it.isMine }, then call my?.toUiModel(myNickname)
?: emptyList() (or an appropriate empty/default photolog list) and
partner?.toUiModel(partnerNickname) ?: emptyList() when creating the
PhotoLogsUiModel so myPhotologs/partnerPhotologs never cause a crash.

In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt`:
- Around line 93-107: The passed-in modifier is being applied twice (to the
outer Column and again to the inner Box), which can cause external
paddings/sizes to be duplicated; update the Box usage inside
TaskCertificationDetail to stop reusing the parameterized modifier and instead
use a fresh Modifier (e.g., Modifier.fillMaxWidth()) so only the Column receives
the external modifier and the Box uses a local Modifier to control width.
- Around line 107-144: BackgroundCard and ForegroundCard are being passed the
same PhotologDetailUiModel based on uiState.currentShow; change BackgroundCard
to receive the opposite side's data so the two stacked cards show different
people. Specifically, in the BackgroundCard call swap the conditional mapping so
that when uiState.currentShow == BetweenUs.ME you pass
uiState.photoLogs.partnerPhotologs, and when currentShow == BetweenUs.PARTNER
you pass uiState.photoLogs.myPhotologs; leave ForegroundCard mapping (and
rotation/currentShow props) unchanged.

In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt`:
- Line 32: In TaskCertificationDetailViewModel update the reduce call inside the
photo logs success handler so it uses the API response's goalTitle rather than
the current UI state's value; replace the current use of goalTitle in reduce {
copy(photoLogs = it.toUiModel(), goalTitle = goalTitle) } with the response
field (use it.goalTitle) so the copied TaskCertificationDetailUiState reflects
the API-provided goalTitle.
- Line 24: The current handler for TaskCertificationDetailIntent.Sting uses
TODO("찌르기 API 연동") which throws NotImplementedError at runtime when the button
dispatches that intent; replace the TODO with a safe no-op or emit a UI effect
(e.g., a "준비 중" toast/event) so the app doesn't crash—update the
TaskCertificationDetailViewModel's handling of
TaskCertificationDetailIntent.Sting to either do nothing or send a user-facing
"coming soon" UI event instead of calling TODO().

In `@feature/task-certification/src/main/res/values/strings.xml`:
- Line 6: task_certification_image_upload currently has the same text as
task_certification_title; decide whether they should be distinct or
consolidated: if they are intended to be the same, replace the duplicate string
usage with a single resource reference (use task_certification_title from
callers or vice versa) or add an XML comment next to
task_certification_image_upload to document the intentional duplication; if they
should differ, update the value of task_certification_image_upload to the
correct, distinct text and ensure callers use the appropriate resource name
(task_certification_image_upload vs task_certification_title).
🧹 Nitpick comments (16)
feature/task-certification/src/main/res/values/strings.xml (1)

2-2: XML 주석 닫기 태그가 비표준입니다.

<!-- 인증샷 보기 촬영 화면 !--><!-- 인증샷 보기 화면 !--> 에서 닫기 태그가 !-->로 되어 있습니다. 표준 XML 주석 닫기 태그는 -->입니다. Android에서 동작에는 문제가 없지만, 표준을 따르는 것이 좋습니다.

✏️ 수정 제안
-    <!-- 인증샷 보기 촬영 화면 !-->
+    <!-- 인증샷 보기 촬영 화면 -->
     ...
-    <!-- 인증샷 보기 화면 !-->
+    <!-- 인증샷 보기 화면 -->

Also applies to: 9-9

feature/task-certification/src/main/java/com/twix/task_certification/detail/component/PhotologCard.kt (1)

47-57: Preview 함수의 가시성이 컴포넌트와 불일치합니다.

PhotologCardinternal로 선언되어 있지만, PhotologCardPreviewpublic입니다. Preview 함수는 외부에 노출될 필요가 없으므로 private으로 변경하는 것이 좋습니다.

♻️ 수정 제안
 `@Preview`
 `@Composable`
-fun PhotologCardPreview() {
+private fun PhotologCardPreview() {
core/util/src/main/java/com/twix/util/RelativeTimeFormatter.kt (1)

8-30: Instant.now() 직접 호출로 인해 단위 테스트가 어렵습니다.

현재 Instant.now()를 직접 호출하고 있어 시간 기반 로직을 테스트하기 어렵습니다. Clock을 주입받도록 하면 테스트에서 시간을 제어할 수 있습니다.

또한 uploadedAt이 미래 시각인 경우, duration이 음수가 되어 minutes < 1이 참이 되므로 "방금 전"으로 표시됩니다. 의도된 동작인지 확인이 필요합니다.

♻️ Clock 주입 제안
+import java.time.Clock
+
 object RelativeTimeFormatter {
-    fun format(uploadedAt: String): String =
+    fun format(uploadedAt: String, clock: Clock = Clock.systemUTC()): String =
         runCatching {
             val uploaded = Instant.parse(uploadedAt)
-            val now = Instant.now()
+            val now = clock.instant()
 
             val duration = Duration.between(uploaded, now)
feature/task-certification/src/main/java/com/twix/task_certification/detail/model/ReactionUiModel.kt (1)

6-15: 프로퍼티 이름 imageResourcesimageResource로 변경 권장

단일 리소스 ID를 담고 있으므로 복수형 imageResources보다 단수형 imageResource가 의미를 더 정확히 전달합니다. 복수형을 사용하면 리스트나 배열을 기대하게 되어 혼동을 줄 수 있습니다.

♻️ 이름 변경 제안
 enum class ReactionUiModel(
     val type: GoalReactionType,
-    val imageResources: Int,
+    val imageResource: Int,
 ) {
feature/task-certification/src/main/java/com/twix/task_certification/certification/component/ReactionBox.kt (1)

82-82: 매직 넘버 4 대신 ReactionUiModel.entries.size - 1 사용 권장

리액션 종류가 추가되거나 제거될 때 이 숫자를 함께 수정해야 한다는 점을 놓치기 쉽습니다. ReactionUiModel.entries.size - 1로 대체하면 enum 변경에 자동으로 대응됩니다.

♻️ 수정 제안
-                if (index < 4) {
+                if (index < ReactionUiModel.entries.size - 1) {
feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt (2)

82-99: showModifyfalse일 때 불필요한 클릭 영역이 남아있습니다.

현재 noRippleClickableshowModify 값과 무관하게 항상 적용되고 있어, 수정 버튼이 보이지 않을 때도 해당 영역이 클릭 가능합니다. 실질적으로 onClickModifynull이면 아무 동작도 하지 않지만, 접근성(TalkBack 등) 측면에서 빈 클릭 영역은 혼란을 줄 수 있습니다. showModifytrue일 때만 clickable을 적용하는 것이 더 적절합니다.

♻️ 개선 제안
             Box(
                 modifier =
                     Modifier
                         .width(60.dp)
                         .fillMaxHeight()
                         .then(
                             if (showModify) Modifier.background(GrayColor.C100) else Modifier,
-                        ).noRippleClickable { onClickModify?.invoke() },
+                        ).then(
+                            if (showModify) Modifier.noRippleClickable { onClickModify?.invoke() } else Modifier,
+                        ),
                 contentAlignment = Alignment.Center,
             ) {

63-67: contentDescription을 문자열 리소스로 분리하는 것을 권장합니다.

"back"이 하드코딩된 영문 문자열로 되어 있어, 다국어 지원 및 접근성 측면에서 stringResource를 사용하는 것이 바람직합니다.

feature/task-certification/src/main/java/com/twix/task_certification/detail/component/ForegroundCard.kt (1)

34-36: stringResource의 포맷 인자를 직접 전달하는 방식이 더 관용적입니다.

현재 stringResource(...).format(...) 방식도 동작하지만, Compose에서는 stringResource(resId, formatArgs) 오버로드를 사용하는 것이 더 관용적이고 안전합니다. .format()은 Locale 설정이 반영되지 않을 수 있기 때문입니다.

♻️ 개선 제안
                     BetweenUs.PARTNER ->
-                            stringResource(R.string.task_certification_detail_partner_not_task_certification)
-                                .format(uiModel.nickName)
+                            stringResource(
+                                R.string.task_certification_detail_partner_not_task_certification,
+                                uiModel.nickName,
+                            )
domain/src/main/java/com/twix/domain/model/photolog/PhotologDetail.kt (1)

3-12: 도메인 모델에서 verificationDateString? 타입인 점은 토론해볼 만합니다.

현재 대부분의 nullable 필드는 인증 전 상태를 표현하기 위한 것으로 이해됩니다. 다만 verificationDateString?으로 두면, 도메인 레이어에서 날짜에 대한 타입 안전성이 없어지고, 포맷 의존성이 생깁니다. LocalDateInstant 같은 타입을 사용하고 포맷팅은 UI/Presentation 레이어에서 처리하는 방식은 어떨까요?

물론 현재 단계에서는 API 응답을 그대로 전달하는 것이 간결할 수 있으므로, 향후 리팩토링 시 고려해주시면 좋겠습니다.

feature/task-certification/src/main/java/com/twix/task_certification/detail/component/CertificatedCard.kt (3)

50-50: Preview 함수의 가시성이 private이 아닙니다.

같은 PR의 ForegroundCardPreviewprivate으로 선언되어 있는데, 이 Preview는 public입니다. Preview 함수는 외부에서 호출될 필요가 없으므로 private으로 통일하는 것이 좋습니다.

♻️ 개선 제안
-fun CertificatedCardPreview() {
+private fun CertificatedCardPreview() {

37-38: CommentUiModel이 매 recomposition마다 새로 생성됩니다.

enabled = false인 읽기 전용 텍스트 필드이므로 기능상 문제는 없지만, uiModel.comment가 변경되지 않는 한 매번 새 TextFieldValueCommentUiModel 객체를 생성하는 것은 불필요한 할당입니다. remember로 감싸면 불필요한 객체 생성을 줄일 수 있습니다.


25-35: AsyncImage에 placeholder/error 이미지가 없습니다.

네트워크 지연이나 로드 실패 시 빈 화면이 표시됩니다. 사용자 경험을 위해 placeholdererror 파라미터 추가를 고려해주세요. 향후 PR에서 처리할 계획이라면 무시하셔도 됩니다.

feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt (1)

33-35: fallback 값의 타입이 일관되지 않습니다.

Line 29에서 defaultValue = -1L (Long)을 사용하고 있지만, Line 35의 fallback은 ?: -1 (Int)입니다. Kotlin에서 자동 확장되므로 버그는 아니지만, 타입 일관성을 위해 -1L로 통일하는 것이 좋습니다.

♻️ 개선 제안
                val id =
                    backStackEntry.arguments?.getLong(NavRoutes.TaskCertificationDetailRoute.ARG_GOAL_ID)
-                        ?: -1
+                        ?: -1L
core/network/src/main/java/com/twix/network/model/response/photolog/mapper/PhotologMapper.kt (1)

7-9: 불필요한 @OptIn(ExperimentalTime::class) 어노테이션

이 매퍼 함수에서 kotlin.time 관련 API를 사용하지 않으므로, @OptIn(ExperimentalTime::class)와 해당 import는 불필요합니다. 사용하지 않는 opt-in은 코드 독자에게 혼란을 줄 수 있으므로 제거하는 것이 좋습니다.

🧹 제거 제안
-import kotlin.time.ExperimentalTime
-
-@OptIn(ExperimentalTime::class)
 fun PhotologDetailResponse.toDomain(): PhotologDetail =
feature/task-certification/src/main/java/com/twix/task_certification/detail/component/BackgroundCard.kt (2)

32-37: currentShow 파라미터가 사용되지 않습니다

BackgroundCard에 전달되는 currentShow: BetweenUs 파라미터가 함수 본문에서 전혀 참조되지 않고 있습니다. 사용하지 않는 파라미터는 API 표면을 불필요하게 넓히고, 호출부에서 값을 전달해야 하는 부담을 줍니다. 현재 사용 계획이 없다면 제거하고, 향후 필요할 때 추가하는 것이 YAGNI 원칙에 부합합니다.

♻️ 제거 제안
 `@Composable`
 fun BackgroundCard(
     uiModel: PhotologDetailUiModel,
-    currentShow: BetweenUs = BetweenUs.PARTNER,
     buttonTitle: String,
     onClick: () -> Unit,
     rotation: Float,
 ) {

86-101: Preview 함수의 가시성과 네이밍

Preview 함수가 public으로 노출되어 있습니다. 내부 컴포넌트의 Preview이므로 private으로 제한하면 모듈 외부 노출을 방지할 수 있습니다. 사소한 부분이지만 일관성을 위해 참고해 주세요.

@YAPP-Github YAPP-Github deleted a comment from coderabbitai bot Feb 9, 2026
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: 1

🤖 Fix all issues with AI agents
In `@feature/task-certification/src/main/res/values/strings.xml`:
- Line 2: Replace the nonstandard XML comment closing sequences in the
strings.xml comments (e.g., change "<!-- 인증샷 보기 촬영 화면 !-->" and the similar
comment at line 8) to use the standard "-->" closing token; locate the comment
strings in the file (search for "인증샷 보기 촬영 화면" and the other flagged comment)
and update "!-->" to "-->" so the comments conform to XML syntax.
🧹 Nitpick comments (3)
feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt (1)

267-317: DimmedScreen의 가시성(visibility) 검토를 제안드립니다.

DimmedScreenpublic으로 선언되어 있지만, 이 파일 내에서만 사용되고 있습니다. 모듈 외부에서 재사용할 계획이 아니라면 internal 또는 private으로 제한하면 API 표면을 줄이고 의도를 명확히 할 수 있습니다.

♻️ 제안
 `@Composable`
-fun DimmedScreen(
+private fun DimmedScreen(
     isFocused: Boolean,
     textFieldRect: Rect,
     guideTextRect: Rect,
     focusManager: FocusManager,
 ) {
feature/task-certification/src/main/res/values/strings.xml (1)

9-9: 다국어 지원을 위해 %s 대신 %1$s 사용을 권장합니다.

%s님은\n아직…에서 포맷 인자가 하나뿐이지만, Android 다국어 지원 시 언어별로 인자 순서가 달라질 수 있으므로 위치 지정 인자(%1$s)를 사용하는 것이 안전합니다. 이는 Android Lint에서도 권장하는 패턴입니다.

♻️ 수정 제안
-    <string name="task_certification_detail_partner_not_task_certification">%s님은\n아직…</string>
+    <string name="task_certification_detail_partner_not_task_certification">%1$s님은\n아직…</string>

As per coding guidelines, "다국어 지원을 고려했는가?"

feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt (1)

154-169: ReactionSection 잘 분리되어 있습니다 👍

표시 여부(visible)에 따른 조건부 렌더링이 깔끔하고, private 접근 제한자로 캡슐화도 적절합니다.

작은 참고: Line 157의 reaction 파라미터에 기본값 null이 있는데, 현재 호출부(Line 148)에서 항상 값을 전달하고 있으므로 기본값이 불필요합니다. 필수 파라미터로 변경하면 호출부에서의 누락을 컴파일 타임에 잡을 수 있습니다.

@chanho0908 chanho0908 self-assigned this Feb 9, 2026
@chanho0908 chanho0908 added the Feature Extra attention is needed label Feb 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature Extra attention is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant