[Fix] 1차 QA 수정사항 반영 (로그인/회원가입 · 홈 · 설정) - #218
Hidden character warning
Conversation
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>
📝 WalkthroughWalkthroughOTT 응답 기본값과 저장 콘텐츠의 OTT 목록 표시 흐름을 변경했습니다. 사용자 프로필 이메일을 설정 화면에 연결했습니다. 온보딩과 프로필 수정 화면에서 숫자를 포함한 닉네임을 허용하고 관련 테스트를 추가했습니다. ChangesOTT 목록 처리
프로필 이메일 연동
닉네임 검증
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 목록 표시 이벤트 전달
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (13)
app/src/main/java/com/flint/data/dto/ott/response/OttListResponseDto.ktapp/src/main/java/com/flint/data/dto/user/response/UserProfileResponseDto.ktapp/src/main/java/com/flint/domain/mapper/user/ProfileMapper.ktapp/src/main/java/com/flint/domain/model/user/UserProfileResponseModel.ktapp/src/main/java/com/flint/presentation/home/HomeScreen.ktapp/src/main/java/com/flint/presentation/home/HomeViewModel.ktapp/src/main/java/com/flint/presentation/onboarding/OnboardingUiState.ktapp/src/main/java/com/flint/presentation/setting/SettingScreen.ktapp/src/main/java/com/flint/presentation/setting/SettingUiState.ktapp/src/main/java/com/flint/presentation/setting/SettingViewModel.ktapp/src/main/java/com/flint/presentation/setting/editprofile/EditProfileUiState.ktapp/src/test/java/com/flint/data/dto/ott/OttListResponseDtoTest.ktapp/src/test/java/com/flint/presentation/onboarding/NicknameValidationTest.kt
| contents = bookmarkedContents.contents | ||
| .take(MAX_SAVED_CONTENT_COUNT) | ||
| .toImmutableList(), |
There was a problem hiding this comment.
🎯 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/javaRepository: 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.ktRepository: 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.ktRepository: 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.ktRepository: 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}")
PYRepository: 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.
| 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), | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 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'
fiRepository: 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.ktRepository: 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.")
PYRepository: 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의 제한된 폭 안에서 말줄임되도록 처리해 주세요.
email의 Modifier.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.
| 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의 기존 말줄임 설정은 유지하세요.
| fun `한글 영문 숫자를 모두 섞은 닉네임을 허용한다`() { | ||
| assertTrue(bothAccept("플린트flint7")) | ||
| } |
There was a problem hiding this comment.
🎯 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.
| 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
left a comment
There was a problem hiding this comment.
굿굿 pr이랑 이미 리뷰가 달려서 달게 없네요. 리뷰 확인만 부탁드려요!
리뷰어 섹션에 남겨주신 이메일/OTT 서버 불일치 2건은 이슈로 분리해두시면 좋을 것 같습니다!
📮 관련 이슈
📌 작업 내용
1차 QA 시트의 로그인/회원가입 · 홈 · 설정 탭 Android fail 항목 대응입니다.
1. 닉네임 유효성 검사에 숫자 허용 (TC 1-34)
NICKNAME_REGEX에0-9가 빠져 있어 숫자가 포함된 닉네임이 거부됐습니다. 서버는 원래 숫자를 허용하고 있었고 클라이언트만 막고 있었습니다.온보딩(
OnboardingUiState)과 프로필 수정(EditProfileUiState)에 동일한 정규식이 복사돼 있어 양쪽 모두 수정했습니다. 한쪽만 고치면 "가입은flint2로 되는데 설정에서 수정은 거부"되는 불일치가 생깁니다.2. 홈 최근 저장한 콘텐츠 10개 제한 (TC 3-23, 3-24)
저장 콘텐츠가 10개를 넘어도 전부 노출되고 있었습니다.
getUserBookmarkedContents()는 프로필·저장한 콘텐츠 화면에서도 쓰이고SavedContentsSection도ProfileScreen과 공유하는 컴포넌트라, 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)
스웨거
MyProfileRes에email이 문서화돼 있으나UserProfileResponseDto에 필드가 없어 값이 와도 받을 수 없는 상태였습니다. DTO → 모델 → 매퍼 → UiState → UI까지 연결했습니다.email을 내려주지 않습니다. 아래 "To. 리뷰어" 참고.5. OTT 응답 DTO 방어 + 유닛 테스트
OttItemResponseDto.contentUrl이 기본값 없는 필수 필드인데 서버 스키마GetOttResponse에는 존재하지 않습니다. 지금은 서버가 빈 배열을 주고 있어 드러나지 않지만, 실제 OTT 데이터가 채워지는 순간MissingFieldException으로 깨집니다. 기본값을 지정해 막았습니다.함께 유닛 테스트 16개를 추가했습니다.
OttListResponseDtoTest—contentUrl유무 양쪽 역직렬화 (4개)NicknameValidationTest— 닉네임 숫자 허용/거부 규칙, 온보딩·프로필 수정 판정 일치 검증 (12개)📸 스크린샷
✅ 검증
Pixel_9a 에뮬레이터에서 신규 가입 후 실제 서버로 확인했습니다.
{"available":true}/contents/ott/호출 0건정렬 방향 확인: 북마크를 순서대로 추가해 API가 최신순으로 반환하는 것을 확인했습니다.
take(10)이 맞고takeLast가 아닙니다.유닛 테스트 16개 전부 통과 (
./gradlew testDebugUnitTest).😅 미구현
email을 내려주지 않아 클라이언트만으로 닫을 수 없음🫛 To. 리뷰어
백엔드 확인이 필요한 사항 2건입니다.
1.
GET /api/v1/users/me가email을 반환하지 않습니다스웨거
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는 비어 있습니다.
이번 PR은 북마크 응답 쪽 데이터를 쓰도록 우회했지만 서버 데이터 불일치는 그대로 남아 있습니다. 덧붙여
logoUrl값이 전부"adsf","asgfd","adfds"같은 더미값입니다.참고로 봐주실 부분
HomeViewModel에서ContentRepository의존성을 제거했습니다 (OTT API를 더 이상 호출하지 않음)ProfileViewModel.getOttListPerContent(142행)는 호출처가 없는 데드코드입니다. 이번 PR 범위에서 벗어나 두었는데 정리하는 게 좋을 것 같습니다SettingMenuItem의trailingContent를RowScope로 바꿨습니다. 라벨이weight(1f)라 trailing이 먼저 측정되는데, 긴 이메일이 "계정" 라벨을 밀어내는 걸 막기 위함입니다🤖 Generated with Claude Code
Summary by CodeRabbit
새로운 기능
개선
버그 수정