Skip to content

feat: 독서 기록 플로우 화면 - 입력 유효성 처리 및 세부 UX 개선#87

Merged
seoyoon513 merged 12 commits intodevelopfrom
BOOK-195-feature/#84
Jul 31, 2025
Merged

feat: 독서 기록 플로우 화면 - 입력 유효성 처리 및 세부 UX 개선#87
seoyoon513 merged 12 commits intodevelopfrom
BOOK-195-feature/#84

Conversation

@seoyoon513
Copy link
Copy Markdown
Contributor

@seoyoon513 seoyoon513 commented Jul 31, 2025

🔗 관련 이슈

📙 작업 설명

  • multipleEventsCutter로 터치 씹히던 부분 수정
  • 페이지 입력 TextField에서 유효한 번호만 입력되도록 구현
  • 최대 페이지 넘어갈 시 error message 뜨도록 구현 (현재 임시로 MAX_PAGE = 1000으로 설정)
  • 감상평 가이드 진입 시 기존 작성한 내용이 없으면 키보드 올라오도록 구현
  • 감상평 가이드 재선택 관련 정책 적용
  • 감상평 가이드 선택 시 커서가 문장 가장 앞에 오도록 구현
  • 은근 슬쩍 내서재 칩 영역 스크롤로 변경
  • 키보드 바깥 영역 터치 시 내려가게 수정
  • 키보드 올라왔을 때 문장 기록 TextField 가려지지 않게 수정

🧪 테스트 내역

  • 주요 기능 정상 동작 확인
  • 브라우저/기기에서 동작 확인
  • 엣지 케이스 테스트 완료
  • 기존 기능 영향 없음

📸 스크린샷 또는 시연 영상

✅ 페이지 입력 유효성 검사

Reed_._.mp4

✅ 감상평 가이드 진입 시 키보드

Reed_._.mp4

✅ TEST CASE 1: 선택한 가이드가 있는 상태에서 감상평 가이드 누른 경우

Reed_._TC1.mp4

✅ TEST CASE 2: 작성한 내용 있는 상태에서 감상평 가이드 누른 경우

Reed_._TC2.mp4

✅ TEST CASE 3: 기존에 있던 내용을 지우고 감상평 가이드 누른 경우

Reed_._TC3.mp4

💬 추가 설명 or 리뷰 포인트

  • 문장 수집 화면에서는 페이지 입력, 문장 입력, 문장 스캔 중 사용자의 입력 순서를 제한하지 않기 위해, 특정 TextField에 자동 포커스를 설정하지 않았습니다.
  • 감상평 가이드 정책사항 관련 PM 체크가 필요해서 먼저 pr올려요! 키보드 바깥 영역 터치 부분은 마저 작업해서 커밋하겠습니다~ -> 적용완
  • 키보드 바깥영역 터치 시 내려가게 하는 부분은 ReedFullScreen에 적용했습니다. (검색에서도 적용되게 전역적으로 처리)
  • 문장 기록쪽에서 '문장 스캔하기'버튼까지 키보드 바깥으로 밀렸으면 했지만, 지금까지 시도한게 다 안먹혀서 요건 나중에 각잡고 해보겠습니다

Summary by CodeRabbit

  • 신규 기능

    • 인상 가이드 선택 시 기존 입력 내용에 따라 안내 메시지, 버튼 스타일, 활성화 상태가 동적으로 변경됩니다.
    • 페이지 입력란에 숫자만 입력 가능하도록 변환 기능이 추가되고, 최대 페이지 수(1000)를 초과할 경우 오류 메시지가 표시됩니다.
    • 인상 입력란이 비어 있을 때 자동으로 포커스 및 키보드가 표시됩니다.
    • 입력란에 오류 상태 시 테두리 색상과 오류 메시지가 시각적으로 표시됩니다.
    • 버튼 색상 스타일에 PRIMARY_INVERSE_TEXT가 추가되었습니다.
    • 전체 화면 터치 시 입력 포커스가 해제되는 기능이 추가되었습니다.
    • 필터 칩 그룹이 스크롤 가능한 리스트로 변경되어 성능이 개선되었습니다.
  • UI/UX 개선

    • 인상 가이드 및 인상 선택 관련 안내 메시지, 버튼 텍스트, 경고 문구가 명확하게 변경되었습니다.
    • 인상 가이드, 문장 선택 등에서 클릭 시 리플 효과가 제거되어 더 깔끔한 인터랙션을 제공합니다.
    • 페이지 입력란 레이아웃이 개선되고, 버튼 위치 및 키보드 동작이 최적화되었습니다.
  • 버그 수정

    • 버튼 클릭 시 불필요한 중복 이벤트 발생을 방지합니다.
  • 문서화

    • 안내, 경고, 오류 관련 문자열 리소스가 추가 및 변경되었습니다.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 31, 2025

Walkthrough

페이지 입력 필드의 정수 입력 제한, 최대 페이지 초과 시 오류 처리, 감상평 가이드 UX 개선, 입력 변환 함수 추가, 버튼 색상 스타일 확장, 테마 색상 추가 등 다양한 입력 검증 및 UX 개선 작업이 반영되었습니다. 감상평 가이드 선택 정책 및 관련 UI/상태 관리 로직도 업데이트되었습니다.

Changes

Cohort / File(s) Change Summary
버튼 색상 스타일 확장
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/button/ButtonColorStyle.kt
ReedButtonColorStylePRIMARY_INVERSE_TEXT 추가, 관련 색상 반환 함수 확장, 비활성화 색상 로직 일부 변경
입력 변환 및 텍스트 필드 개선
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/textfield/InputTransformation.kt,
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/textfield/ReedRecordTextField.kt
정수만 입력받는 digitOnlyInputTransformation 추가, 텍스트필드에 입력 변환/에러 상태/에러 메시지 파라미터 추가 및 레이아웃 구조 개선
테마 색상 추가
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt
테마에 Blank 색상 상수 추가
감상평 가이드 UI 및 상호작용 개선
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/component/ImpressionGuideBottomSheet.kt,
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/component/ImpressionGuideBox.kt,
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/component/SentenceBox.kt
감상평 가이드 바텀시트에 상태 파라미터 추가, 버튼 스타일/상태/문구 동적 처리, 클릭 효과 ripple 제거, 테마 색상 적용 등 UX 개선
기록 등록 플로우 상태 및 이벤트 확장
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt,
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUiState.kt
페이지 최대값 초과 검증, 에러 상태/이전 가이드 선택값 관리, 감상평 가이드 선택 정책 반영, 관련 이벤트 및 상태값 추가/변경
기록 등록 UI 개선
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt,
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/ImpressionStep.kt,
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/QuoteStep.kt
"다음" 버튼 이벤트 컷터 옵션 추가, 감상평 입력 자동 포커스/키보드 표시, 페이지 입력 필드에 에러 처리/입력 변환 적용 등
문구 및 리소스 키 정비
feature/record/src/main/res/values/strings.xml
감상평 가이드, 입력 오류 등 관련 string 리소스 키/문구 정비 및 추가
전체 화면 터치 시 포커스 해제 추가
core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/component/ReedFullScreen.kt
전체 화면 클릭 시 포커스 해제 기능 추가
필터 칩 그룹 레이지 로우 변경
feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/component/FilterChipGroup.kt
Row에서 LazyRow로 변경하여 레이지 로딩 및 패딩 조정

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant QuoteStep
    participant ReedRecordTextField
    participant InputTransformation
    participant Presenter

    User->>QuoteStep: 페이지 입력
    QuoteStep->>ReedRecordTextField: 입력값 전달
    ReedRecordTextField->>InputTransformation: digitOnlyInputTransformation 적용
    InputTransformation-->>ReedRecordTextField: 정수만 필터링된 값 반환
    ReedRecordTextField-->>QuoteStep: 입력값/에러 상태 전달
    QuoteStep->>Presenter: 입력값 변경 이벤트
    Presenter-->>QuoteStep: isPageError 상태 업데이트
Loading
sequenceDiagram
    participant User
    participant ImpressionStep
    participant ImpressionGuideBottomSheet
    participant Presenter

    User->>ImpressionStep: 감상평 가이드 버튼 클릭
    ImpressionStep->>ImpressionGuideBottomSheet: 바텀시트 오픈, 이전 선택값 전달
    User->>ImpressionGuideBottomSheet: 가이드 선택/확정
    ImpressionGuideBottomSheet->>Presenter: OnImpressionGuideConfirmed 이벤트
    Presenter-->>ImpressionStep: 감상평 텍스트/선택값/커서 위치 상태 업데이트
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
페이지 입력 필드에 정수만 입력 가능하도록 제한 (#84)
최대 페이지 수 초과 시 오류 문구 표시 (#84)
감상평 가이드 문구 추가 시 커서 가장 앞에 위치시키기 (#84)
감상평 가이드 재선택 시 기존 작성하던 감상문 정책 적용 (#84)
키보드 열려있을 때 외부 터치 시 내려가도록 처리 (#84) 해당 기능(포커스 아웃 시 키보드 내리기) 구현 코드가 포함되어 있지 않음

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
전체 화면 클릭 시 포커스 해제 기능 추가 (core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/component/ReedFullScreen.kt) 이 기능은 키보드 내림과 관련되나, 명시된 이슈의 "키보드 열려있을 때 외부 터치 시 내려가도록 처리" 요구사항과 정확히 일치하지 않음. 포커스 해제는 구현되었으나 키보드 내림 동작 보장은 불분명함.
필터 칩 그룹을 LazyRow로 변경 (feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/component/FilterChipGroup.kt) 이 변경은 입력 유효성 처리 및 UX 개선 이슈와 직접적인 관련이 없어 보임. UI 최적화 차원의 변경으로 판단됨.

Possibly related PRs

  • YAPP-Github/Reed-Android#66: ButtonColorStyle.kt에서 enum 상수 및 색상 관련 함수 추가 등 버튼 색상 스타일 관련 변경이 겹칩니다.

Suggested reviewers

  • easyhooon

Poem

🐰
토끼가 깡총깡총 코드밭을 달려,
입력 검증 꼼꼼히, UX도 반짝!
버튼엔 새 옷, 필드는 정수만,
감상평 가이드엔 정책이 쏙쏙!
리뷰어님, 이 변화와 함께
더 나은 기록의 길로 hop hop!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1bbe5a and 706f22a.

📒 Files selected for processing (1)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/QuoteStep.kt (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/QuoteStep.kt
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BOOK-195-feature/#84

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3914a18 and 3cbf86a.

📒 Files selected for processing (13)
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/button/ButtonColorStyle.kt (3 hunks)
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/textfield/InputTransformation.kt (1 hunks)
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/textfield/ReedRecordTextField.kt (4 hunks)
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt (1 hunks)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/component/ImpressionGuideBottomSheet.kt (6 hunks)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/component/ImpressionGuideBox.kt (3 hunks)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/component/SentenceBox.kt (2 hunks)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt (6 hunks)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt (1 hunks)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUiState.kt (2 hunks)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/ImpressionStep.kt (5 hunks)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/QuoteStep.kt (2 hunks)
  • feature/record/src/main/res/values/strings.xml (1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#46
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/component/InfiniteLazyColumn.kt:83-95
Timestamp: 2025-07-14T00:46:03.843Z
Learning: seoyoon513과 팀은 한국어 주석을 선호하며, 한국어 주석을 영어로 번역하라는 제안을 하지 않아야 함
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#75
File: feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt:0-0
Timestamp: 2025-07-29T06:07:11.727Z
Learning: seoyoon513 팀에서는 UI 구현 단계에서 더미 데이터를 하드코딩하여 화면을 먼저 구현하고, 이후 서버 연동 시점에 실제 데이터로 교체하는 개발 방식을 사용합니다.
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt (1)

Learnt from: seoyoon513
PR: #32
File: core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/button/ButtonColorStyle.kt:10-16
Timestamp: 2025-07-08T12:33:01.863Z
Learning: Reed Android 프로젝트에서 KAKAO 버튼 스타일은 디자이너가 pressed 상태 색상을 별도로 정의하지 않았기 때문에 pressed 상태에서도 동일한 Kakao 색상을 사용한다.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/component/ImpressionGuideBox.kt (3)

Learnt from: seoyoon513
PR: #72
File: feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/component/CameraFrame.kt:21-82
Timestamp: 2025-07-28T18:08:47.298Z
Learning: Jetpack Compose에서 scale() 변환은 시각적 변환만 적용하며 레이아웃 좌표계는 변경하지 않는다. 따라서 scale(scaleX = -1f, scaleY = -1f)로 반전된 아이콘에서 padding()은 원래 레이아웃 기준으로 동작하므로, 시각적으로 올바른 위치를 위해서는 변환 전 좌표계 기준으로 padding을 설정해야 한다.

Learnt from: easyhooon
PR: #77
File: feature/onboarding/src/main/kotlin/com/ninecraft/booket/feature/onboarding/OnboardingUi.kt:73-73
Timestamp: 2025-07-29T07:02:18.885Z
Learning: Kotlin에서 같은 패키지 내의 파일들은 패키지 레벨에 정의된 const val 상수를 import 없이 직접 접근할 수 있습니다. OnboardingPresenter.kt와 OnboardingUi.kt 모두 com.ninecraft.booket.feature.onboarding 패키지에 속해 있어서 ONBOARDING_STEPS_COUNT 상수를 공유할 수 있습니다.

Learnt from: seoyoon513
PR: #45
File: core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/appbar/ReedTopAppBar.kt:65-65
Timestamp: 2025-07-12T01:33:57.101Z
Learning: Reed Android 프로젝트에서 타이포그래피 사용 규칙: 톱 앱바(Top App Bar)에서는 headline2SemiBold를 사용하고, 바텀시트(Bottom Sheet)에서는 heading2SemiBold를 사용한다. 이는 의도적인 디자인 시스템 차별화이다.

core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/button/ButtonColorStyle.kt (5)

Learnt from: seoyoon513
PR: #32
File: core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/button/ButtonColorStyle.kt:10-16
Timestamp: 2025-07-08T12:33:01.863Z
Learning: Reed Android 프로젝트에서 KAKAO 버튼 스타일은 디자이너가 pressed 상태 색상을 별도로 정의하지 않았기 때문에 pressed 상태에서도 동일한 Kakao 색상을 사용한다.

Learnt from: seoyoon513
PR: #45
File: core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/appbar/ReedTopAppBar.kt:65-65
Timestamp: 2025-07-12T01:33:57.101Z
Learning: Reed Android 프로젝트에서 타이포그래피 사용 규칙: 톱 앱바(Top App Bar)에서는 headline2SemiBold를 사용하고, 바텀시트(Bottom Sheet)에서는 heading2SemiBold를 사용한다. 이는 의도적인 디자인 시스템 차별화이다.

Learnt from: seoyoon513
PR: #32
File: core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Theme.kt:15-21
Timestamp: 2025-07-08T03:21:24.693Z
Learning: Reed Android 프로젝트에서는 다크모드/라이트모드 대응 계획이 없고 기본 테마 값만 사용할 예정이다.

Learnt from: easyhooon
PR: #55
File: core/designsystem/src/main/res/drawable/ic_x_circle.xml:6-11
Timestamp: 2025-07-17T23:41:33.929Z
Learning: Reed Android 프로젝트에서는 SVG에서 vector drawable로 변환시 색상 매핑 대신 모드별 별도 아이콘 파일을 사용하는 방식을 선호한다. Figma에서 SVG export한 형태를 그대로 사용하며, 다크모드 대응이 필요할 경우 drawable-night 폴더에 별도 아이콘을 배치하는 방식을 사용한다.

Learnt from: easyhooon
PR: #55
File: core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/ReedTextField.kt:0-0
Timestamp: 2025-07-17T23:43:38.687Z
Learning: Reed Android 프로젝트에서 아이콘의 tint 속성을 Color.Unspecified로 설정하는 이유는 theme 색상이 아이콘에 적용되어 아이콘 고유의 색상이 변경되는 것을 방지하기 위함이다. 이는 아이콘의 원본 색상을 보존하려는 디자인 시스템 정책과 일치한다.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/component/ImpressionGuideBottomSheet.kt (1)

Learnt from: seoyoon513
PR: #45
File: core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/appbar/ReedTopAppBar.kt:65-65
Timestamp: 2025-07-12T01:33:57.101Z
Learning: Reed Android 프로젝트에서 타이포그래피 사용 규칙: 톱 앱바(Top App Bar)에서는 headline2SemiBold를 사용하고, 바텀시트(Bottom Sheet)에서는 heading2SemiBold를 사용한다. 이는 의도적인 디자인 시스템 차별화이다.

🧬 Code Graph Analysis (1)
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/component/ImpressionGuideBottomSheet.kt (1)
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/button/ReedButton.kt (1)
  • ReedButton (33-107)
⏰ 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: ci-build
🔇 Additional comments (37)
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt (1)

67-67: 새로운 Blank 색상 추가가 적절합니다.

디자인 시스템에서 하드코딩된 색상값을 테마 상수로 분리한 것은 좋은 개선사항입니다. 플레이스홀더 텍스트용으로 사용되는 연한 회색(0xFFD6D6D6)을 테마 색상으로 정의하여 일관성을 높였습니다.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/component/SentenceBox.kt (1)

15-15: 클릭 상호작용 방식 통일화가 적절합니다.

clickableSingle에서 noRippleClickable로 변경하여 리플 효과를 제거했습니다. 이는 ImpressionGuideBox와 동일한 패턴으로 컴포넌트 간 일관된 사용자 경험을 제공합니다.

Also applies to: 43-45

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/component/ImpressionGuideBox.kt (2)

17-17: 리플 효과 제거로 상호작용 일관성을 개선했습니다.

clickableSingle에서 noRippleClickable로 변경하여 SentenceBox와 동일한 클릭 상호작용 패턴을 적용했습니다. 이는 전체 기록 플로우에서 일관된 사용자 경험을 제공합니다.

Also applies to: 47-49


19-19: 테마 색상 사용으로 디자인 시스템 일관성을 강화했습니다.

하드코딩된 Color(0xFFD6D6D6) 대신 새로 정의된 Blank 테마 색상을 사용하여 플레이스홀더 텍스트 색상을 관리합니다. 이는 디자인 시스템의 일관성을 높이고 향후 색상 변경 시 유지보수성을 개선합니다.

Also applies to: 58-58

core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/button/ButtonColorStyle.kt (3)

10-10: 새로운 버튼 스타일 추가 확인

PRIMARY_INVERSE_TEXT가 추가되었는데, PRIMARY와 거의 동일한 색상을 사용하는 것 같습니다. 두 스타일의 의도된 차이점은 disabled 상태에서의 콘텐츠 색상뿐인가요?


15-15: PRIMARY_INVERSE_TEXT 색상 처리 구현 완료

새로운 스타일에 대한 색상 처리가 올바르게 구현되었습니다.

Also applies to: 25-25


36-36: Disabled 상태 색상 처리 구현 완료

PRIMARY_INVERSE_TEXT의 핵심 차이점인 disabled 상태에서의 색상 처리가 올바르게 구현되었습니다.

core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/textfield/InputTransformation.kt (1)

8-19: 숫자 전용 입력 변환 로직 구현 완료

입력 변환 함수가 올바르게 구현되었습니다. 선행 0 제거 로직과 "0" 단독 허용 처리가 적절합니다.

다음과 같은 경우들이 올바르게 처리되는지 확인해보세요:

  • "01" → "1"
  • "00" → ""
  • "0" → "0"
  • "abc123" → "123"
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt (1)

84-84: IMPRESSION 단계 다중 이벤트 차단 처리 추가

감상평 단계에서만 다중 이벤트 차단을 활성화하여 사용자가 빠르게 여러 번 클릭하는 것을 방지하는 UX 개선이 적절하게 구현되었습니다.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/QuoteStep.kt (4)

28-28: 입력 변환 함수 import 추가

페이지 입력 필드의 숫자 전용 입력 처리를 위한 import가 올바르게 추가되었습니다.


62-62: 페이지 입력 필드에 숫자 전용 변환 적용

페이지 번호 입력에 digitOnlyInputTransformation이 적용되어 숫자만 입력 가능하고 선행 0이 제거되도록 구현되었습니다.


63-63: 키보드 타입 설정 간소화

페이지 입력 필드에서 숫자 키보드만 표시하도록 설정하고 불필요한 IME Action을 제거하여 입력 UX가 개선되었습니다.


65-66: 페이지 입력 에러 처리 구현

페이지 번호 초과 시 에러 상태 표시와 적절한 에러 메시지가 구현되어 사용자에게 명확한 피드백을 제공합니다.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUiState.kt (3)

16-16: 페이지 에러 상태 프로퍼티 추가

페이지 번호 입력 검증을 위한 isPageError 상태가 적절하게 추가되었습니다.


22-22: 감상평 가이드 이전 선택 상태 추가

감상평 가이드 재선택 정책 구현을 위한 beforeSelectedImpressionGuide 상태가 적절하게 추가되었습니다.


47-47: 이벤트 이름 명확화

OnSelectionConfirmed에서 OnImpressionGuideConfirmed로 변경되어 이벤트의 의미가 더 명확해졌습니다.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/ImpressionStep.kt (5)

14-22: 새로운 import 구문들이 적절히 추가되었습니다.

자동 포커스 및 키보드 제어 기능을 위한 필수 import들이 올바르게 추가되었습니다.


49-57: 자동 포커스 기능이 잘 구현되었습니다.

감상평 텍스트가 비어있을 때만 자동으로 포커스를 설정하고 키보드를 표시하는 로직이 적절합니다. LaunchedEffect의 Unit 키 사용으로 컴포저블 초기화 시에만 실행되도록 올바르게 설정되었습니다.


81-81: focusRequester 모디파이어가 올바르게 적용되었습니다.

텍스트 필드에 focusRequester가 연결되어 자동 포커스 기능이 정상 작동할 수 있도록 구성되었습니다.


108-110: 새로운 파라미터 전달이 적절합니다.

ImpressionGuideBottomSheet에 impressionState와 beforeSelectedImpressionGuide 파라미터가 추가되어 가이드 선택 정책을 구현할 수 있도록 되었습니다.


124-124: 이벤트 명칭 변경이 적절합니다.

OnImpressionGuideConfirmed로 변경된 이벤트명이 가이드 선택 확인 의도를 더 명확히 표현합니다.

feature/record/src/main/res/values/strings.xml (2)

32-35: 문자열 리소스 구조 개선 및 새로운 메시지들이 적절합니다.

  1. impression_guide_description: _bottomsheet 접미사 제거로 일반화된 명칭 사용
  2. impression_guide_warning: 기존 내용이 사라진다는 명확한 경고 메시지
  3. impression_guide_selection_done, impression_guide_change_done: 상황별 버튼 텍스트 구분

사용자 경험 향상을 위한 적절한 메시지들입니다.


41-41: 페이지 입력 오류 메시지가 명확합니다.

페이지 수 초과 시 표시되는 오류 메시지가 사용자가 이해하기 쉽게 작성되었습니다.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt (5)

68-73: 페이지 유효성 검사 로직이 올바르게 구현되었습니다.

derivedStateOf를 사용하여 상태 변경 시에만 재계산되도록 최적화되었고, toIntOrNull()을 사용해 안전한 형변환을 수행합니다.


78-78: 다음 버튼 활성화 조건에 페이지 오류 검사가 적절히 추가되었습니다.

페이지 입력이 비어있지 않고, 문장이 비어있지 않으며, 페이지 오류가 없을 때만 다음 버튼이 활성화되도록 논리적으로 구성되었습니다.


63-63: 이전 선택 상태 추적 변수가 적절히 추가되었습니다.

beforeSelectedImpressionGuide를 통해 가이드 변경 여부를 판단할 수 있도록 구성되었습니다.


169-173: 가이드 선택 시 상태 초기화 로직이 개선되었습니다.

감상평이 비어있을 때 선택된 가이드를 초기화하여 UI 상태를 일관성 있게 유지합니다.


184-191: 텍스트 교체 및 커서 위치 설정이 잘 구현되었습니다.

선택된 가이드로 텍스트를 교체하고 커서를 문장 맨 앞에 위치시키는 UX 개선사항이 정확히 구현되었습니다. TextRange(0)을 사용한 커서 위치 설정이 적절합니다.

core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/textfield/ReedRecordTextField.kt (5)

19-19: InputTransformation 지원이 적절히 추가되었습니다.

선택적 InputTransformation 파라미터를 통해 입력 변환 기능을 지원하도록 확장되었습니다. null 기본값으로 기존 동작을 유지하면서 새로운 기능을 제공합니다.

Also applies to: 46-46, 69-69


52-53: 오류 상태 파라미터가 적절히 추가되었습니다.

isError와 errorMessage 파라미터를 통해 오류 상태를 외부에서 제어할 수 있도록 설계되었습니다. 기본값 설정으로 기존 코드와의 호환성을 유지합니다.


65-126: 레이아웃 구조 변경이 적절합니다.

Column으로 감싸서 텍스트 필드 하단에 오류 메시지를 표시할 수 있도록 구조가 개선되었습니다. 조건부 오류 메시지 표시 로직도 올바르게 구현되었습니다.


62-62: 오류 상태 시각적 피드백이 잘 구현되었습니다.

오류 상태일 때 테두리 색상을 변경하여 사용자에게 명확한 시각적 피드백을 제공합니다. borderError 색상 사용이 적절합니다.

Also applies to: 85-85


117-124: 오류 메시지 표시 영역이 적절히 구현되었습니다.

오류 상태일 때만 메시지를 표시하고, 적절한 색상(contentError)과 타이포그래피(label2Regular)를 사용했습니다. 간격 설정도 적절합니다.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/component/ImpressionGuideBottomSheet.kt (4)

10-10: 새로운 파라미터가 적절히 추가되었습니다.

TextFieldState와 beforeSelectedImpressionGuide 파라미터를 통해 가이드 선택 정책을 구현할 수 있도록 확장되었습니다.

Also applies to: 40-42


48-51: 동적 UI 상태 로직이 잘 구현되었습니다.

감상평 텍스트가 비어있는지 여부에 따라 설명 텍스트와 색상을 동적으로 변경하는 로직이 적절합니다. 사용자에게 명확한 안내를 제공합니다.


110-132: 조건부 버튼 렌더링이 적절합니다.

감상평 텍스트 상태에 따라 다른 버튼을 표시하여 사용자 경험을 개선했습니다:

  • 빈 텍스트: PRIMARY 스타일, 가이드 선택 시 활성화
  • 기존 텍스트: PRIMARY_INVERSE_TEXT 스타일, 변경 시에만 활성화

버튼 활성화 조건도 적절하게 설정되었습니다.


162-164: 프리뷰 함수가 새로운 파라미터를 반영하여 업데이트되었습니다.

새로 추가된 파라미터들에 대한 기본값이 적절히 설정되어 프리뷰가 정상 작동합니다.

Comment on lines +261 to +263
companion object {
const val MAX_PAGE = 1000
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jul 31, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

MAX_PAGE 상수가 하드코딩되어 있습니다.

현재 MAX_PAGE가 1000으로 하드코딩되어 있는데, 실제 책의 페이지 수에 따라 동적으로 설정되어야 할 것 같습니다. PR 목표에서 언급한 "임시로 1000으로 설정"이라는 내용과 일치하지만, 향후 개선이 필요해 보입니다.

향후 실제 책 정보를 기반으로 최대 페이지 수를 설정할 수 있도록 개선을 고려해보세요:

-    companion object {
-        const val MAX_PAGE = 1000
-    }
+    companion object {
+        const val DEFAULT_MAX_PAGE = 1000
+    }

그리고 책 정보에서 실제 페이지 수를 가져와 사용하도록 변경하는 것을 권장합니다.

🤖 Prompt for AI Agents
In
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt
around lines 261 to 263, the MAX_PAGE constant is hardcoded to 1000. To fix
this, refactor the code to remove the hardcoded value and instead retrieve the
actual maximum page count dynamically from the book information. Implement a
mechanism to fetch and set MAX_PAGE based on the real book data rather than
using a fixed constant.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

서버 연동되면 적용할게~ 지금은 임시로 한거

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@seoyoon513 네, 이해했습니다! 임시 구현이라는 점 확인했고, 서버 연동 시점에 실제 책 정보를 기반으로 동적으로 설정하시면 되겠네요. 현재 단계에서는 충분히 적절한 접근 방식입니다. 👍


🧠 Learnings used
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#77
File: feature/onboarding/src/main/kotlin/com/ninecraft/booket/feature/onboarding/OnboardingUi.kt:73-73
Timestamp: 2025-07-29T07:02:18.885Z
Learning: Kotlin에서 같은 패키지 내의 파일들은 패키지 레벨에 정의된 const val 상수를 import 없이 직접 접근할 수 있습니다. OnboardingPresenter.kt와 OnboardingUi.kt 모두 com.ninecraft.booket.feature.onboarding 패키지에 속해 있어서 ONBOARDING_STEPS_COUNT 상수를 공유할 수 있습니다.

Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#46
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/SearchPresenter.kt:0-0
Timestamp: 2025-07-14T01:10:40.100Z
Learning: 알라딘 API의 searchBook에서 start 파라미터는 아이템 오프셋이 아닌 1-based 페이지 번호를 의미한다. 각 페이지는 maxResults 만큼의 아이템을 반환하므로, 다음 페이지를 요청할 때는 start + 1을 사용해야 한다.

Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#46
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/component/InfiniteLazyColumn.kt:83-95
Timestamp: 2025-07-14T00:46:03.843Z
Learning: seoyoon513과 팀은 한국어 주석을 선호하며, 한국어 주석을 영어로 번역하라는 제안을 하지 않아야 함

Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#75
File: feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt:0-0
Timestamp: 2025-07-29T06:07:11.727Z
Learning: seoyoon513 팀에서는 UI 구현 단계에서 더미 데이터를 하드코딩하여 화면을 먼저 구현하고, 이후 서버 연동 시점에 실제 데이터로 교체하는 개발 방식을 사용합니다.

* 숫자만 허용하고, 01, 00 같은 형식을 막는 InputTransformation
*/
val digitOnlyInputTransformation = {
text: TextFieldBuffer ->
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.

여기 뭔가 개행이 이상한데 이렇게{ text: TextFieldBuffer -> 바로 옆에 작성해주는게 람다 파라미터(매개변수) 작성 컨벤션에 부합할듯합니다

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

반영 완 b0b11dc

inputTransformation = digitOnlyInputTransformation,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
lineLimits = TextFieldLineLimits.SingleLine,
isError = state.isPageError,
Copy link
Copy Markdown
Contributor

@easyhooon easyhooon Jul 31, 2025

Choose a reason for hiding this comment

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

presenter 및 state를 아주 잘사용하는군 하산해도 좋다

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

image

impressionState.edit {
replace(0, length, "")
append(selectedImpressionGuide)
this.selection = TextRange(0) // 커서를 문장 맨 앞에 위치
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.

👍 👍 👍

Copy link
Copy Markdown
Contributor

@easyhooon easyhooon left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/component/ReedFullScreen.kt (1)

35-41: 포커스 해제 로직이 잘 구현되었습니다

전체 화면 터치 시 포커스를 해제하는 UX 개선이 적절하게 구현되었습니다. indication = nullremember { MutableInteractionSource() } 사용으로 성능과 시각적 피드백 측면에서도 올바른 접근입니다.

다만 하위 컴포넌트의 클릭 이벤트와의 상호작용을 고려해보세요. 현재 구현은 안전하지만, 향후 복잡한 상호작용이 필요한 경우 이벤트 전파 처리가 필요할 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3cbf86a and c1bbe5a.

📒 Files selected for processing (4)
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/textfield/InputTransformation.kt (1 hunks)
  • core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/component/ReedFullScreen.kt (2 hunks)
  • feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/component/FilterChipGroup.kt (2 hunks)
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/QuoteStep.kt (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/textfield/InputTransformation.kt
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step/QuoteStep.kt
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#46
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/component/InfiniteLazyColumn.kt:83-95
Timestamp: 2025-07-14T00:46:03.843Z
Learning: seoyoon513과 팀은 한국어 주석을 선호하며, 한국어 주석을 영어로 번역하라는 제안을 하지 않아야 함
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#75
File: feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt:0-0
Timestamp: 2025-07-29T06:07:11.727Z
Learning: seoyoon513 팀에서는 UI 구현 단계에서 더미 데이터를 하드코딩하여 화면을 먼저 구현하고, 이후 서버 연동 시점에 실제 데이터로 교체하는 개발 방식을 사용합니다.
📚 Learning: in the reed-android project's termsagreementscreen.kt, the ontermdetailclick event is intentionally ...
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#35
File: feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/TermsAgreementScreen.kt:127-127
Timestamp: 2025-07-09T01:14:29.836Z
Learning: In the Reed-Android project's TermsAgreementScreen.kt, the OnTermDetailClick event is intentionally passed an empty string for the URL parameter because the actual URLs for terms detail pages haven't been decided yet. This is a temporary implementation that will be updated once the URLs are finalized.

Applied to files:

  • core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/component/ReedFullScreen.kt
📚 Learning: feature/library의 librarypresenter에서 현재 filterchipstate의 카운트 값들은 ui 확인용 더미 데이터이며, api가 준비되면 실제 데이터로 교...
Learnt from: seoyoon513
PR: YAPP-Github/Reed-Android#63
File: feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt:28-53
Timestamp: 2025-07-22T05:19:10.071Z
Learning: feature/library의 LibraryPresenter에서 현재 FilterChipState의 카운트 값들은 UI 확인용 더미 데이터이며, API가 준비되면 실제 데이터로 교체될 예정입니다.

Applied to files:

  • feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/component/FilterChipGroup.kt
📚 Learning: reed-android 프로젝트에서는 `booket.android.feature` convention plugin을 사용하여 feature 모듈들의 공통 의존성을 관리한다. 이 p...
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#61
File: feature/webview/build.gradle.kts:17-21
Timestamp: 2025-07-20T12:34:23.786Z
Learning: Reed-Android 프로젝트에서는 `booket.android.feature` convention plugin을 사용하여 feature 모듈들의 공통 의존성을 관리한다. 이 plugin은 Circuit, Compose, 그리고 core 모듈들의 의존성을 자동으로 포함하므로, 각 feature 모듈의 build.gradle.kts에서는 특별한 의존성(예: libs.logger, libs.kakao.auth)만 별도로 선언하면 된다.

Applied to files:

  • feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/component/FilterChipGroup.kt
⏰ 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: ci-build
🔇 Additional comments (6)
core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/component/ReedFullScreen.kt (2)

4-5: 필요한 import 추가가 적절합니다

포커스 해제 기능 구현을 위한 필수 import들이 올바르게 추가되었습니다.

Also applies to: 11-11, 14-14


29-30: LocalFocusManager 사용이 적절합니다

포커스 관리를 위한 focusManager 인스턴스 생성이 올바르게 구현되었습니다.

feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/component/FilterChipGroup.kt (4)

4-4: 필요한 import 추가가 적절히 이루어졌습니다.

LazyRow 변환을 위한 필수 import들이 정확하게 추가되었습니다.

Also applies to: 7-8, 15-15


26-36: Row에서 LazyRow로의 전환이 적절하게 구현되었습니다.

필터 칩이 많을 경우 성능 향상을 위한 좋은 최적화입니다. 기존의 레이아웃 속성들이 모두 올바르게 유지되었습니다.


33-33: contentPadding 사용이 적절합니다.

LazyRow에서 아이템들의 패딩을 처리하는 올바른 방법이며, 디자인 시스템의 spacing 값을 사용하는 것도 좋습니다.


37-47: LazyRow의 items 함수 사용이 올바르게 구현되었습니다.

기존 forEach 로직이 LazyRow의 items 함수로 정확하게 변환되었으며, FilterChip의 모든 속성과 콜백이 그대로 유지되었습니다.

@seoyoon513 seoyoon513 merged commit 29c9b14 into develop Jul 31, 2025
3 checks passed
@seoyoon513 seoyoon513 deleted the BOOK-195-feature/#84 branch July 31, 2025 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOOK-195/feat] 독서 기록 플로우 화면 - 입력 유효성 처리 및 세부 UX 개선

2 participants