Skip to content

[Fix] 1차 QA 수정사항 반영 (로그인/회원가입 · 홈 · 설정) - #218

Open
kimjw2003 wants to merge 5 commits into
developfrom
FLT-26-1차-qa-수정

Hidden character warning

The head ref may contain hidden characters: "FLT-26-1\ucc28-qa-\uc218\uc815"
Open

[Fix] 1차 QA 수정사항 반영 (로그인/회원가입 · 홈 · 설정)#218
kimjw2003 wants to merge 5 commits into
developfrom
FLT-26-1차-qa-수정

Conversation

@kimjw2003

@kimjw2003 kimjw2003 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📮 관련 이슈

  • closed #이슈번호

📌 작업 내용

1차 QA 시트의 로그인/회원가입 · 홈 · 설정 탭 Android fail 항목 대응입니다.

1. 닉네임 유효성 검사에 숫자 허용 (TC 1-34)

NICKNAME_REGEX0-9가 빠져 있어 숫자가 포함된 닉네임이 거부됐습니다. 서버는 원래 숫자를 허용하고 있었고 클라이언트만 막고 있었습니다.

온보딩(OnboardingUiState)과 프로필 수정(EditProfileUiState)에 동일한 정규식이 복사돼 있어 양쪽 모두 수정했습니다. 한쪽만 고치면 "가입은 flint2로 되는데 설정에서 수정은 거부"되는 불일치가 생깁니다.

2. 홈 최근 저장한 콘텐츠 10개 제한 (TC 3-23, 3-24)

저장 콘텐츠가 10개를 넘어도 전부 노출되고 있었습니다.

getUserBookmarkedContents()는 프로필·저장한 콘텐츠 화면에서도 쓰이고 SavedContentsSectionProfileScreen과 공유하는 컴포넌트라, Repository나 컴포넌트가 아닌 HomeViewModel에서 잘랐습니다. totalCount는 프로필 쪽에서 사용하므로 전체 개수를 유지합니다.

3. 홈 OTT 바텀시트 노출 (TC 3-33, 3-34)

홈에서 저장 콘텐츠를 눌러도 바텀시트가 뜨지 않았습니다.

원인은 서버가 GET /api/v1/contents/ott/{contentId}에 대해 {"otts":[]} 빈 배열만 반환하는 것이었습니다. 빈 목록이면 isNotEmpty() 가드에 걸려 무반응이 됩니다.

반면 북마크 목록 응답은 같은 콘텐츠에 getOttSimpleList를 정상적으로 내려주고 있고, 프로필·저장한 콘텐츠 화면은 이미 이 값을 사용합니다. 홈만 별도 API를 호출해서 홈에서만 실패한 구조였습니다. 홈도 동일한 방식으로 맞췄습니다.

바텀시트는 OttType의 로컬 iconRes/ottName으로 렌더링하므로 서버의 logoUrl·contentUrl은 애초에 필요하지 않습니다.

4. 설정 계정 이메일 노출 (TC 12-6, 12-7)

스웨거 MyProfileResemail이 문서화돼 있으나 UserProfileResponseDto에 필드가 없어 값이 와도 받을 수 없는 상태였습니다. DTO → 모델 → 매퍼 → UiState → UI까지 연결했습니다.

⚠️ 다만 실기기 확인 결과 서버가 실제로는 email을 내려주지 않습니다. 아래 "To. 리뷰어" 참고.

5. OTT 응답 DTO 방어 + 유닛 테스트

OttItemResponseDto.contentUrl이 기본값 없는 필수 필드인데 서버 스키마 GetOttResponse에는 존재하지 않습니다. 지금은 서버가 빈 배열을 주고 있어 드러나지 않지만, 실제 OTT 데이터가 채워지는 순간 MissingFieldException으로 깨집니다. 기본값을 지정해 막았습니다.

함께 유닛 테스트 16개를 추가했습니다.

  • OttListResponseDtoTestcontentUrl 유무 양쪽 역직렬화 (4개)
  • NicknameValidationTest — 닉네임 숫자 허용/거부 규칙, 온보딩·프로필 수정 판정 일치 검증 (12개)

📸 스크린샷

OTT 바텀시트 (수정 후) 설정 계정 행
저장 콘텐츠 탭 시 왓챠피디아/넷플릭스 정상 노출 이메일 있으면 노출, 없으면 로고만

✅ 검증

Pixel_9a 에뮬레이터에서 신규 가입 후 실제 서버로 확인했습니다.

TC 결과
1-34 닉네임 숫자 ✅ 클라 통과 + 서버 {"available":true}
3-23 / 3-24 홈 10개 제한 ✅ 북마크 12개 → 홈 10개 노출, 가장 오래된 2개 제거 확인
3-33 / 3-34 OTT 바텀시트 ✅ 정상 노출, /contents/ott/ 호출 0건
12-6 / 12-7 계정 이메일 🔶 클라 완료 · 서버 미제공

정렬 방향 확인: 북마크를 순서대로 추가해 API가 최신순으로 반환하는 것을 확인했습니다. take(10)이 맞고 takeLast가 아닙니다.

유닛 테스트 16개 전부 통과 (./gradlew testDebugUnitTest).

😅 미구현

  • TC 12-6, 12-7 — 서버가 email을 내려주지 않아 클라이언트만으로 닫을 수 없음
  • OTT 데이터가 없는 콘텐츠 탭 시 무반응 (403·네트워크 실패도 동일). 전체오류 모달(TC 1-45, 3-51, 12-68)과 묶어서 별도 처리 필요

🫛 To. 리뷰어

백엔드 확인이 필요한 사항 2건입니다.

1. GET /api/v1/users/meemail을 반환하지 않습니다

스웨거 MyProfileRes에는 "email": "이메일 (미보유 시 null)"로 문서화돼 있으나 실제 응답에는 키 자체가 없습니다.

{"status":200,"data":{"id":"873202440779292987","nickname":"김종우",
 "isFliner":false,"keywordRecalculatable":false,"termsAgreementStatus":{...}}}

카카오 로그인 시 이메일 동의항목을 수집하는지 확인이 필요합니다. 클라이언트는 값이 오면 노출, 없으면 로고만 표시하는 구조로 잡아뒀습니다.

2. GET /api/v1/contents/ott/{contentId}가 빈 배열을 반환합니다

같은 콘텐츠에 대해 북마크 API는 OTT를 정상적으로 내려주는데, 전용 OTT API는 비어 있습니다.

GET /api/v1/contents/ott/4  →  {"otts":[]}
북마크 응답의 id=4          →  getOttSimpleList:[{"ottName":"Netflix","logoUrl":"adsf"}]

이번 PR은 북마크 응답 쪽 데이터를 쓰도록 우회했지만 서버 데이터 불일치는 그대로 남아 있습니다. 덧붙여 logoUrl 값이 전부 "adsf", "asgfd", "adfds" 같은 더미값입니다.


참고로 봐주실 부분

  • HomeViewModel에서 ContentRepository 의존성을 제거했습니다 (OTT API를 더 이상 호출하지 않음)
  • ProfileViewModel.getOttListPerContent(142행)는 호출처가 없는 데드코드입니다. 이번 PR 범위에서 벗어나 두었는데 정리하는 게 좋을 것 같습니다
  • SettingMenuItemtrailingContentRowScope로 바꿨습니다. 라벨이 weight(1f)라 trailing이 먼저 측정되는데, 긴 이메일이 "계정" 라벨을 밀어내는 걸 막기 위함입니다

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 새로운 기능

    • 설정 화면의 계정 메뉴에서 이메일 주소를 확인할 수 있습니다.
    • 저장된 콘텐츠의 OTT 목록을 더 빠르게 확인할 수 있습니다.
  • 개선

    • 닉네임에 숫자를 포함할 수 있도록 허용 범위를 확장했습니다.
    • OTT 정보가 일부 누락된 응답도 안정적으로 처리합니다.
  • 버그 수정

    • 저장된 콘텐츠에서 OTT 목록을 불러오는 동작을 개선했습니다.

kimjw2003 and others added 5 commits August 6, 2026 22:34
QA TC 1-34 - 한글/영문/숫자 혼합 입력 시 에러가 발생하던 문제 수정

NICKNAME_REGEX에 0-9가 빠져 있어 숫자가 포함된 닉네임이 거부됐습니다.
온보딩과 프로필 수정 화면에 동일한 정규식이 복사돼 있어 함께 수정했습니다.
(온보딩만 고치면 가입은 되는데 수정은 거부되는 불일치가 생깁니다)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
QA TC 3-23, 3-24 - 저장한 콘텐츠가 10개를 넘어도 계속 노출되던 문제 수정

getUserBookmarkedContents()는 프로필/저장한 콘텐츠 화면에서도 사용하므로
Repository가 아닌 HomeViewModel에서 잘랐습니다.
totalCount는 프로필 쪽에서 쓰이므로 전체 개수를 유지합니다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
QA TC 12-6, 12-7 - 계정 정보 영역에 이메일이 표시되지 않던 문제 대응

스웨거 MyProfileRes(GET /api/v1/users/me)에 email이 문서화돼 있으나
UserProfileResponseDto에 필드가 없어 값이 와도 받을 수 없는 상태였습니다.
DTO/모델/매퍼/UiState까지 email을 연결하고 UI에 노출합니다.

단, 실기기 확인 결과 서버가 실제로는 email을 내려주지 않습니다
(null인 필드를 응답에서 생략하는 구조라 키 자체가 없음).
따라서 이 커밋만으로 TC 12-6/12-7이 닫히지는 않으며,
카카오 이메일 동의항목 수집 여부에 대한 백엔드 확인이 필요합니다.

이메일이 있으면 노출하고 없으면 로고만 표시하는 구조로 정리했습니다.

타 유저 응답(UserProfileRes)에는 email이 없고 DTO를 공유하므로
keywordRecalculatable과 동일하게 nullable + 기본값으로 처리했습니다.

긴 이메일이 "계정" 라벨을 밀어내지 않도록 trailingContent를
RowScope로 변경하고 weight(1f, fill = false)를 적용했습니다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
서버 스키마 GetOttResponse는 { ottId, name, logoUrl } 세 필드뿐이라
기본값 없는 contentUrl이 있으면 MissingFieldException으로 역직렬화가 실패합니다.
현재는 서버가 빈 배열을 주고 있어 드러나지 않지만,
실제 OTT 데이터가 채워지는 순간 홈 바텀시트가 깨집니다.

ignoreUnknownKeys/coerceInputValues로는 막을 수 없어(누락 필드는 대상 아님)
기본값을 지정했습니다.

함께 추가한 유닛 테스트:
- OttListResponseDtoTest: contentUrl 유무 양쪽 역직렬화 (4개)
- NicknameValidationTest: 닉네임 숫자 허용 및 거부 규칙 (12개)
  온보딩과 프로필 수정의 판정이 일치하는지도 함께 검증

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
QA TC 3-33, 3-34 - 홈에서 저장 콘텐츠를 눌러도 바텀시트가 뜨지 않던 문제

원인은 서버가 GET /api/v1/contents/ott/{contentId} 에 대해
{"otts":[]} 빈 배열만 반환하는 것이었습니다.
빈 목록이면 HomeScreen 의 isNotEmpty() 가드에 걸려 아무 반응이 없었습니다.

반면 북마크 목록 응답은 같은 콘텐츠에 대해 getOttSimpleList 를 정상적으로
내려주고 있고, 프로필(ProfileScreen)과 저장한 콘텐츠(SavedContentScreen)는
이미 이 값을 사용합니다. 홈만 별도 API 를 호출하고 있어 홈에서만 실패했습니다.

홈도 동일하게 이미 로드된 북마크 목록의 getOttSimpleList 를 사용하도록 변경했습니다.
바텀시트는 OttType 의 로컬 iconRes/ottName 으로 렌더링하므로
서버의 logoUrl(현재 "adsf" 등 더미값)과 contentUrl 은 필요하지 않습니다.

부수 효과:
- 콘텐츠 탭마다 발생하던 네트워크 호출이 사라짐
- OttShortCutListItem 의 OttType.valueOf() 는 runCatching 없이 호출되는데,
  로컬 값은 이미 OttType 으로 검증된 값이라 IllegalArgumentException 위험이 없어짐
- 미사용이 된 ContentRepository 의존성 제거

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

OTT 응답 기본값과 저장 콘텐츠의 OTT 목록 표시 흐름을 변경했습니다. 사용자 프로필 이메일을 설정 화면에 연결했습니다. 온보딩과 프로필 수정 화면에서 숫자를 포함한 닉네임을 허용하고 관련 테스트를 추가했습니다.

Changes

OTT 목록 처리

Layer / File(s) Summary
OTT 응답 기본값 처리
app/src/main/java/com/flint/data/dto/ott/response/OttListResponseDto.kt, app/src/test/java/com/flint/data/dto/ott/OttListResponseDtoTest.kt
contentUrl이 없는 응답에 빈 문자열을 적용합니다. 제공된 값과 빈 목록의 역직렬화를 테스트합니다.
저장 콘텐츠 OTT 목록 표시
app/src/main/java/com/flint/presentation/home/HomeViewModel.kt, app/src/main/java/com/flint/presentation/home/HomeScreen.kt
홈 화면이 저장된 콘텐츠의 ottSimpleList를 사용해 ShowOttListBottomSheet side effect를 발생시킵니다. 북마크 상태는 최대 10개로 제한합니다.

프로필 이메일 연동

Layer / File(s) Summary
프로필 이메일 데이터 계약
app/src/main/java/com/flint/data/dto/user/response/UserProfileResponseDto.kt, app/src/main/java/com/flint/domain/model/user/UserProfileResponseModel.kt, app/src/main/java/com/flint/domain/mapper/user/ProfileMapper.kt
프로필 DTO와 모델에 nullable email을 추가하고 매퍼에서 전달합니다.
설정 화면 이메일 표시
app/src/main/java/com/flint/presentation/setting/SettingUiState.kt, app/src/main/java/com/flint/presentation/setting/SettingViewModel.kt, app/src/main/java/com/flint/presentation/setting/SettingScreen.kt
프로필 조회 결과의 이메일을 설정 상태에 저장합니다. 계정 메뉴에 한 줄 말줄임으로 표시합니다.

닉네임 검증

Layer / File(s) Summary
숫자 포함 닉네임 검증
app/src/main/java/com/flint/presentation/onboarding/OnboardingUiState.kt, app/src/main/java/com/flint/presentation/setting/editprofile/EditProfileUiState.kt, app/src/test/java/com/flint/presentation/onboarding/NicknameValidationTest.kt
한글·영문·숫자 닉네임을 허용하도록 정규식을 변경합니다. 허용·거부 입력과 자음·모음 검출 규칙을 검증합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HomeScreen
  participant HomeViewModel
  participant ContentModel
  participant ShowOttListBottomSheet
  HomeScreen->>HomeViewModel: showOttList(contentId)
  HomeViewModel->>ContentModel: 저장된 콘텐츠에서 contentId 검색
  ContentModel-->>HomeViewModel: ottSimpleList 반환
  HomeViewModel->>ShowOttListBottomSheet: OTT 목록 표시 이벤트 전달
Loading

Possibly related PRs

Suggested reviewers: chanmi1125, ckals413

Poem

당근밭 토끼가 이메일을 심고
OTT 바텀시트를 살짝 열어요.
숫자 닉네임도 반짝 허용,
빠진 URL엔 빈 문자열 한 줌.
테스트가 깡총, 변경을 지켜요.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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 제목은 1차 QA 수정 범위인 로그인·회원가입, 홈, 설정 변경을 명확하게 요약합니다.
Description check ✅ Passed 작업 내용, 검증 결과, 미구현 항목, 리뷰어 참고 사항을 포함해 템플릿 요구사항을 대부분 충족합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch FLT-26-1차-qa-수정

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/flint/presentation/home/HomeViewModel.kt`:
- Around line 80-82: Update getUserBookmarkedContents() and the
bookmarked-content flow so contents are ordered by save date descending before
take(MAX_SAVED_CONTENT_COUNT), or add the save-date field to the response DTO to
enable that ordering. Ensure the resulting home list always contains the latest
saved 10 items, and keep the immutable-list conversion after limiting.

In `@app/src/main/java/com/flint/presentation/setting/SettingScreen.kt`:
- Around line 117-130: 중첩된 Row가 SettingMenuItem의 가용 폭을 사용하도록 수정하고, 그 안의 email
Text가 남은 공간만 차지하게 하여 TextOverflow.Ellipsis가 동작하도록 하세요. SettingMenuItem의 제한된 폭과
아이콘 영역을 침범하지 않도록 Row의 weight 또는 동등한 폭 제약을 적용하고, Text의 기존 말줄임 설정은 유지하세요.

In
`@app/src/test/java/com/flint/presentation/onboarding/NicknameValidationTest.kt`:
- Around line 49-51: Update the test input in `한글 영문 숫자를 모두 섞은 닉네임을 허용한다` to a
2–8-character nickname that mixes Korean, English, and digits, so it satisfies
both UI states’ `MAX_LENGTH` constraint while preserving the intended validation
coverage.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6d2a7556-7db1-4401-afa4-33e160bb4db8

📥 Commits

Reviewing files that changed from the base of the PR and between 11c1e1a and 71c29be.

📒 Files selected for processing (13)
  • app/src/main/java/com/flint/data/dto/ott/response/OttListResponseDto.kt
  • app/src/main/java/com/flint/data/dto/user/response/UserProfileResponseDto.kt
  • app/src/main/java/com/flint/domain/mapper/user/ProfileMapper.kt
  • app/src/main/java/com/flint/domain/model/user/UserProfileResponseModel.kt
  • app/src/main/java/com/flint/presentation/home/HomeScreen.kt
  • app/src/main/java/com/flint/presentation/home/HomeViewModel.kt
  • app/src/main/java/com/flint/presentation/onboarding/OnboardingUiState.kt
  • app/src/main/java/com/flint/presentation/setting/SettingScreen.kt
  • app/src/main/java/com/flint/presentation/setting/SettingUiState.kt
  • app/src/main/java/com/flint/presentation/setting/SettingViewModel.kt
  • app/src/main/java/com/flint/presentation/setting/editprofile/EditProfileUiState.kt
  • app/src/test/java/com/flint/data/dto/ott/OttListResponseDtoTest.kt
  • app/src/test/java/com/flint/presentation/onboarding/NicknameValidationTest.kt

Comment on lines +80 to +82
contents = bookmarkedContents.contents
.take(MAX_SAVED_CONTENT_COUNT)
.toImmutableList(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# UserRepository 선언과 구현을 확인합니다.
fd -t f 'UserRepository.kt' app/src/main/java -x ast-grep outline {} --items all

# 호출부와 구현부의 정렬·페이지네이션 계약을 확인합니다.
rg -n -C 8 --glob '*.kt' '\bgetUserBookmarkedContents\s*\(' app/src/main/java
rg -n -C 8 --glob '*.kt' 'bookmarks|savedAt|createdAt|sort|cursor' app/src/main/java

Repository: imflint/Flint-Android

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== UserRepository relevant implementation =="
sed -n '68,120p' app/src/main/java/com/flint/domain/repository/UserRepository.kt

echo
echo "== ContentApi bookmark DTOs =="
fd -t f 'MyBookmarkedContentListResponseDto.kt|BookmarkedContentItemDto.kt|BookmarkedContentItemModel.kt|ContentModelNew.kt' app/src/main/java -x sh -c 'echo "--- $1"; ast-grep outline "$1" --items all; sed -n "1,140p" "$1"' sh {}

echo
echo "== bookmark date fields and sort mentions in relevant API/dto/domain =="
rg -n -C 3 --glob '*.kt' 'bookmarked|savedAt|savedAtMs|createdAt|createdAtMs|sort|order|size|MyBookmarkedContent|BookmarkedContentItem|contentId|id' app/src/main/java/com/flint/data/dto/app/src/main/java/com/flint/domain/model

echo
echo "== HomeViewModel relevant state/comparator derivation =="
sed -n '1,130p' app/src/main/java/com/flint/presentation/home/HomeViewModel.kt

Repository: imflint/Flint-Android

Length of output: 2585


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== focused repository implementation slices =="
sed -n '68,120p' app/src/main/java/com/flint/domain/repository/UserRepository.kt

echo
echo "== bookmark-related DTO/model files =="
fd -i '.*bookmark*.kt$|.*savedContent*.kt$|.*content.*dto.*\.kt$|.*content.*model.*\.kt$' app/src/main/java -x sh -c '
  path="$1"
  name=$(basename "$path")
  printf "### %s\n" "$path"
  wc -l "$path"
  sed -n "1,180p" "$path"
' sh {}

echo
echo "== sorted bookmark API parameters and saved timestamps =="
rg -n -C 3 --glob '*.kt' '(/contents/bookmarks|bookmarked-contents|bookmarkedContents|savedAt|savedAtMs|createdAt|createdAtMs|sort|order|size|cursor|MyBookmarkedContentListResponseDto|MyBookmarkedContentListDto|BookmarkedContentItemDto|BookmarkedContentItemModel)' app/src/main/java

echo
echo "== HomeViewModel relevant state/derivation =="
sed -n '1,120p' app/src/main/java/com/flint/presentation/home/HomeViewModel.kt

Repository: imflint/Flint-Android

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== UserApi bookmark endpoint declaration =="
fd -t f 'UserApi.kt' app/src/main/java -x sh -c 'echo "--- $1"; sed -n "1,120p" "$1"' sh {}

echo
echo "== Bookmark DTO/model files =="
fd -t f 'BookmarkedContent.*Dto.kt|BookmarkedContent.*Model.kt|MyBookmarkedContent.*Dto.kt|MyBookmarkedContent.*Model.kt|Bookmark.*Dto.kt|Bookmark.*Model.kt' app/src/main/java \
  | sed -n '1,80p' \
  | xargs -r sh -c 'echo "--- $1"; wc -l "$1"; sed -n "1,200p" "$1"' sh {}

echo
echo "== Exact bookmark timestamp/sort/sortBy fields in relevant DTOs/models (case-insensitive) =="
rg -n --iglob '*.kt' '(\bbookmarked|SavedAt|savedAt|createdAt|created_at|sort|Sort|sortBy|SortBy|cursor|size|BookmarkedContent|MyBookmarkedContent|bookmarked-contents|contents/bookmarks)' app/src/main/java/com/flint/data/app/src/main/java/com/flint/domain \
  | rg -n '(Dto|Model|`@GET`|`@Query`|bookmarked|SavedAt|savedAt|createdAt|created_at|sort|Sort|sortBy|SortBy|contents/bookmarks|bookmarked-contents)' \
  | sed -n '1,220p'

Repository: imflint/Flint-Android

Length of output: 3394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== bookmark DTO/model files =="
fd -t f '.*Bookmark.*\.kt$' app/src/main/java/com/flint/data/dto app/src/main/java/com/flint/domain/model/content | sort

echo
echo "== bookmark timestamps, sorts, and params in bookmark DTOs/models =="
python3 - <<'PY'
import subprocess, pathlib, re
base = pathlib.Path("app/src/main/java")
files = [
    p for f in subprocess.check_output(["fd", "-t", "f", r'.*Bookmark.*\.kt$', "app/src/main/java"], text=True).splitlines()
    for p in [base/f] if p.is_file()
]
terms = ["BookmarkedContent", "MyBookmarkedContent", "BookmarkedContentListResponseDto", "BookmarkedContentItemDto", "BookmarkedContentItemModel", "savedAt", "createdAt", "sort", "sortBy", "cursor", "size", "contents/bookmarks", "bookmarked-contents"]
for f in files:
    text = f.read_text(errors="replace")
    matches = [i+1 for i, line in enumerate(text.splitlines()) if any(t.lower() in line.lower() for t in terms)]
    if matches:
        print(f"--- {f}")
        for line in matches:
            print(f"{line}: {text.splitlines()[line-1]}")
PY

echo
echo "== HomeViewModel relevant lines =="
sed -n '73,83p' app/src/main/java/com/flint/presentation/home/HomeViewModel.kt

Repository: imflint/Flint-Android

Length of output: 1308


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== BookmarkedContentListResponseDto and mapper =="
for f in \
  app/src/main/java/com/flint/data/dto/content/response/BookmarkedContentListResponseDto.kt \
  app/src/main/java/com/flint/domain/mapper/content/BookmarkedContentMapper.kt
do
  echo "--- $f"
  wc -l "$f"
  sed -n '1,220p' "$f"
done

echo
echo "== ContentApi bookmark API declaration =="
sed -n '1,25p' app/src/main/java/com/flint/data/api/ContentApi.kt

echo
echo "== Any API params/fields mentioning saved time or sort for bookmarks (focused) =="
python3 - <<'PY'
from pathlib import Path
terms = ["contents/bookmarks", "bookmarked-contents", "BookmarkedContentListResponseDto", "savedAt", "createdAt", "cursor", "size", "sort", "sortBy"]
for p in Path("app/src/main/java").rglob("*.kt"):
    if "test" in p.parts:
        continue
    lines = p.read_text(errors="replace").splitlines()
    hits = [
        (i+1, line.strip())
        for i, line in enumerate(lines)
        if any(t in line for t in terms)
    ]
    if not hits:
        continue
    print(f"--- {p}")
    for n, line in hits:
        print(f"{n}: {line}")
PY

Repository: imflint/Flint-Android

Length of output: 2242


홈 저장 콘텐츠 목록 정렬 계약을 명시하거나 take() 앞에서 정렬하세요.

getContentBookmarkedContents(userId == null)/api/v1/contents/bookmarks를 cursor로 페이지네이션하고 taken 순서로 contents에 누적합니다. 저장 시간 필드가 응답 DTO에 없으므로 take(MAX_SAVED_CONTENT_COUNT) 순서가 최신 10건이 되지 않을 수 있습니다. 저장 시간 내림차순을 보장해주지 않으면 getUserBookmarkedContents()에서 정렬이나 save-date 필드를 추가하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/flint/presentation/home/HomeViewModel.kt` around lines
80 - 82, Update getUserBookmarkedContents() and the bookmarked-content flow so
contents are ordered by save date descending before
take(MAX_SAVED_CONTENT_COUNT), or add the save-date field to the response DTO to
enable that ordering. Ensure the resulting home list always contains the latest
saved 10 items, and keep the immutable-list conversion after limiting.

Comment on lines +117 to +130
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
uiState.email?.let { email ->
Text(
text = email,
style = FlintTheme.typography.body2R14,
color = FlintTheme.colors.gray100,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f, fill = false),
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file around the reported lines and nearby layout code.
if [ -f "app/src/main/java/com/flint/presentation/setting/SettingScreen.kt" ]; then
  echo "== file exists =="
  wc -l app/src/main/java/com/flint/presentation/setting/SettingScreen.kt
  echo "== relevant lines 100-140 =="
  sed -n '100,140p' app/src/main/java/com/flint/presentation/setting/SettingScreen.kt | nl -ba -v100
  echo "== relevant lines 220-285 =="
  sed -n '220,285p' app/src/main/java/com/flint/presentation/setting/SettingScreen.kt | nl -ba -v220
else
  echo "file not found"
  fd -i 'SettingScreen.kt'
fi

Repository: imflint/Flint-Android

Length of output: 316


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== SettingScreen.kt file =="
wc -l app/src/main/java/com/flint/presentation/setting/SettingScreen.kt

echo "== lines 100-140 =="
sed -n '100,140p' app/src/main/java/com/flint/presentation/setting/SettingScreen.kt

echo "== lines 220-285 =="
sed -n '220,285p' app/src/main/java/com/flint/presentation/setting/SettingScreen.kt

echo "== occurrences of SettingMenuItem / main row usage =="
rg -n "SettingMenuItem|SettingScreenUiState|email|Composable|`@Preview`" app/src/main/java/com/flint/presentation/setting/SettingScreen.kt

Repository: imflint/Flint-Android

Length of output: 4275


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== compose dependencies referencing layout code =="
if [ -f "app/build.gradle.kts" ]; then
  cat app/build.gradle.kts
elif [ -f "app/build.gradle" ]; then
  cat app/build.gradle
fi

echo "== find settings-related test files =="
fd -i 'Setting.*Test.*|.*Setting.*Test.*' . || true
fd -i 'SettingScreen.*Test.*|.*SettingScreen.*Test.*' . || true

echo "== search for long/truncation/compose UI tests =="
rg -n "composeTestRule|createComposeRule|Long|long|overflow|EllipseEllipsis|TextOverflow.Ellipsis|setting|SettingScreen" app/src/test app/src/androidTest app/src/main/java/com/flint/presentation/setting || true

echo "== Compose weight semantics probe (source only if Gradle dependency unavailable) =="
python3 - <<'PY'
print("The code shows outer Row has label Modifier.weight(1f).")
print("The trailing Row has no modifier for its own width.")
print("The email Text has modifier Modifier.weight(1f, fill = false).")
print("In Compose, an unbounded weight in a nested Row with no width constraint cannot determine its measured width.")
print("This matches a potential truncation problem for long email values under fixed-width parent Row.")
PY

Repository: imflint/Flint-Android

Length of output: 6760


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== SettingUiState =="
cat app/src/main/java/com/flint/presentation/setting/SettingUiState.kt

echo "== root version catalog references =="
if [ -f "gradle/libs.versions.toml" ]; then
  sed -n '1,180p' gradle/libs.versions.toml
fi

echo "== all SettingScreen references =="
rg -n "SettingScreen|SettingUiState\\(" .

Repository: imflint/Flint-Android

Length of output: 6549


긴 이메일도 SettingMenuItem의 제한된 폭 안에서 말줄임되도록 처리해 주세요.

emailModifier.weight(1f, fill = false)는 중첩 Row 안에서만 적용됩니다. 이 중첩 Row는 현재 폭 제약을 제공하지 않아 긴 이메일이 말줄임 없이 가로로 확장될 수 있습니다.

중첩 Row에 상위 Row의 가용 폭을 전달하거나, 이메일과 아이콘을 SettingMenuItem의 직접 자식으로 배치해 폭 제한을 두어 주세요.

예시 수정
                     Row(
+                        modifier = Modifier.weight(1f, fill = false),
                         horizontalArrangement = Arrangement.spacedBy(8.dp),
                         verticalAlignment = Alignment.CenterVertically,
                     ) {
📝 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
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
uiState.email?.let { email ->
Text(
text = email,
style = FlintTheme.typography.body2R14,
color = FlintTheme.colors.gray100,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f, fill = false),
)
}
Row(
modifier = Modifier.weight(1f, fill = false),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
uiState.email?.let { email ->
Text(
text = email,
style = FlintTheme.typography.body2R14,
color = FlintTheme.colors.gray100,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f, fill = false),
)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/flint/presentation/setting/SettingScreen.kt` around
lines 117 - 130, 중첩된 Row가 SettingMenuItem의 가용 폭을 사용하도록 수정하고, 그 안의 email Text가 남은
공간만 차지하게 하여 TextOverflow.Ellipsis가 동작하도록 하세요. SettingMenuItem의 제한된 폭과 아이콘 영역을
침범하지 않도록 Row의 weight 또는 동등한 폭 제약을 적용하고, Text의 기존 말줄임 설정은 유지하세요.

Comment on lines +49 to +51
fun `한글 영문 숫자를 모두 섞은 닉네임을 허용한다`() {
assertTrue(bothAccept("플린트flint7"))
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

QA 길이 조건에 맞는 테스트 입력값을 사용하세요.

"플린트flint7"은 9자입니다. 두 UI 상태의 MAX_LENGTH는 8입니다. 이 값은 실제 입력 흐름에서 허용되지 않습니다.

2~8자 한글·영문·숫자 혼합 값으로 바꾸세요.

수정 예시
-        assertTrue(bothAccept("플린트flint7"))
+        assertTrue(bothAccept("플린트f2"))
📝 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
fun `한글 영문 숫자를 모두 섞은 닉네임을 허용한다`() {
assertTrue(bothAccept("플린트flint7"))
}
fun `한글 영문 숫자를 모두 섞은 닉네임을 허용한다`() {
assertTrue(bothAccept("플린트f2"))
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/test/java/com/flint/presentation/onboarding/NicknameValidationTest.kt`
around lines 49 - 51, Update the test input in `한글 영문 숫자를 모두 섞은 닉네임을 허용한다` to a
2–8-character nickname that mixes Korean, English, and digits, so it satisfies
both UI states’ `MAX_LENGTH` constraint while preserving the intended validation
coverage.

@ckals413 ckals413 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

굿굿 pr이랑 이미 리뷰가 달려서 달게 없네요. 리뷰 확인만 부탁드려요!
리뷰어 섹션에 남겨주신 이메일/OTT 서버 불일치 2건은 이슈로 분리해두시면 좋을 것 같습니다!

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.

2 participants