Skip to content

[Feature] 콜밴 글쓰기 상태 및 뷰모델 추가#1328

Open
JaeYoung290 wants to merge 4 commits intofeature/#1283-callvan-create-screenfrom
feature/#1283-callvan-create-viewmodel
Open

[Feature] 콜밴 글쓰기 상태 및 뷰모델 추가#1328
JaeYoung290 wants to merge 4 commits intofeature/#1283-callvan-create-screenfrom
feature/#1283-callvan-create-viewmodel

Conversation

@JaeYoung290
Copy link
Contributor

@JaeYoung290 JaeYoung290 commented Mar 7, 2026

PR 개요

이슈 번호: #1283

PR 체크리스트

  • Code convention을 잘 지켰나요?
  • Lint check를 수행하였나요?
  • Assignees를 추가했나요?

작업사항

  • 버그 수정
  • 신규 기능
  • 코드 스타일 수정 (포맷팅 등)
  • 리팩토링 (기능 수정 X, API 수정 X)
  • 기타

작업사항의 상세한 설명

콜밴 글쓰기 화면의 viewModel을 추가합니다.

  • CallvanCreateState: 위치·날짜·시간·인원·피커 가시성 등 UI 상태 정의
  • CallvanCreateViewModel

논의 사항

스크린샷

추가내용

  • develop, sprint 브랜치를 향하고 있습니다
  • production 브랜치를 향하고 있습니다

Summary by CodeRabbit

새로운 기능

  • 콜밴 생성 흐름에 출발지, 도착지 선택 기능 추가 (커스텀 위치 입력 지원)
  • 날짜 및 시간 선택 기능 추가
  • 탑승 인원 증감 기능 추가
  • 폼 유효성 검사 및 제출 기능 구현

@JaeYoung290 JaeYoung290 requested a review from a team as a code owner March 7, 2026 12:29
@coderabbitai
Copy link

coderabbitai bot commented Mar 7, 2026

개요

새로운 CallVan 생성 기능의 UI 상태 관리를 위해 상태 데이터 클래스와 ViewModel을 추가합니다. 출발지/도착지 선택, 날짜/시간 설정, 참여자 수 관리, 폼 제출 등의 인터랙션을 Orbit MVI 패턴으로 처리합니다.

변경사항

Cohort / File(s) 요약
CallVan 생성 UI 상태 관리
feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateState.kt, feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateViewModel.kt
새로운 데이터 클래스 CallvanCreateState는 출발지/도착지 위치, 선택된 날짜/시간 컴포넌트(연/월/일/시/분), 피커 표시 여부 플래그를 포함합니다. 계산 속성으로 폼 완성도 검증, 포맷된 날짜/시간, API용 날짜/시간 문자열을 제공합니다. 새로운 CallvanCreateViewModel은 위치/날짜/시간 피커 관리, 참여자 수 조정, 폼 제출 처리 등 19개의 공개 메서드를 통해 상태 전이를 관리하며, CreateCallvanPostUseCase와 연동하여 제출 흐름을 처리합니다.

예상 코드 리뷰 시간

🎯 2 (Simple) | ⏱️ ~12분

권장 리뷰어

  • TTRR1007
🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 새로운 CallvanCreateState와 CallvanCreateViewModel 추가라는 주요 변경사항을 명확하게 요약하고 있습니다.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#1283-callvan-create-viewmodel
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@github-actions github-actions bot requested review from KYM-P and TTRR1007 March 7, 2026 12:30
@JaeYoung290 JaeYoung290 force-pushed the feature/#1283-callvan-create-screen branch from 0446ee2 to 2f28c8d Compare March 7, 2026 12:50
@JaeYoung290 JaeYoung290 force-pushed the feature/#1283-callvan-create-viewmodel branch from 4abbee0 to 5a34625 Compare March 7, 2026 12:51
Copy link
Member

@kongwoojin kongwoojin left a comment

Choose a reason for hiding this comment

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

고생하셨습니다.
코멘트 확인해주세요.

Comment on lines +39 to +46
val amPmText: String
get() = if (isAm) "오전" else "오후"

val participantsText: String
get() = "$maxParticipants 명"

val apiDepartureDate: String
get() = "%04d-%02d-%02d".format(selectedYear, selectedMonth, selectedDay)
Copy link
Member

Choose a reason for hiding this comment

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

이런건 UI에서 처리하는게 낫지 않나요

Copy link
Member

Choose a reason for hiding this comment

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

@KYM-P @skdud0629 @TTRR1007 어떻게 생각하시나요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

포맷팅 로직이라 여기 넣었는데 ui단이 나을까요? 재사용성이나 유닛 테스트 시에 더 좋지 않을까 생각되는데 어떤가요

}

fun submit() = intent {
val currentState = state
Copy link
Member

Choose a reason for hiding this comment

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

currentState를 만든 이유가 있나요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

intent의 경우는 비동기이기 때문에 로직이 실행중에도 다른 입력으로 변경될 수 있고 이에 따라 값이 변할 수 있는데 이때 빠르게 조작하면서 생기는 오류를 해결하고자 submit()이 실행될 때의 상태를 저장하도록 했습니다!

예를 들어 사용자가 submit을 누른 후 조작 실수로 다른 장소를 클릭했을 때 저 변수가 없다면 그 장소가 그대로 저장되는 문제가 있습니다

@JaeYoung290 JaeYoung290 self-assigned this Mar 9, 2026
@github-actions
Copy link

✅ CI 완료. CodeRabbit 리뷰를 요청합니다.
@coderabbitai review

@JaeYoung290
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 3

🧹 Nitpick comments (2)
feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateState.kt (1)

11-13: Calendar.getInstance()가 3번 호출되어 비효율적입니다.

기본값 초기화 시 Calendar 인스턴스를 매번 새로 생성합니다. 날짜가 바뀌는 정확한 시점에 호출되면 year/month/day가 서로 다른 날짜를 참조할 수 있는 엣지 케이스도 존재합니다.

♻️ 수정 제안: companion object에서 기본값 계산
 data class CallvanCreateState(
     val departureLocation: CallvanLocationOption? = null,
     val arrivalLocation: CallvanLocationOption? = null,
     val departureCustomText: String? = null,
     val arrivalCustomText: String? = null,
-    val selectedYear: Int = Calendar.getInstance().get(Calendar.YEAR),
-    val selectedMonth: Int = Calendar.getInstance().get(Calendar.MONTH) + 1,
-    val selectedDay: Int = Calendar.getInstance().get(Calendar.DAY_OF_MONTH),
+    val selectedYear: Int = DEFAULT_YEAR,
+    val selectedMonth: Int = DEFAULT_MONTH,
+    val selectedDay: Int = DEFAULT_DAY,
     val selectedHour: Int = 12,
     ...
-) {
+) {
+    companion object {
+        private val calendar = Calendar.getInstance()
+        private val DEFAULT_YEAR = calendar.get(Calendar.YEAR)
+        private val DEFAULT_MONTH = calendar.get(Calendar.MONTH) + 1
+        private val DEFAULT_DAY = calendar.get(Calendar.DAY_OF_MONTH)
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateState.kt`
around lines 11 - 13, The three default properties selectedYear, selectedMonth,
and selectedDay call Calendar.getInstance() separately which is inefficient and
can produce inconsistent date parts; compute a single Calendar instance once
(e.g., in a companion object or a private local val like now) and derive
selectedYear, selectedMonth (+1), and selectedDay from that single instance
(update references to selectedYear, selectedMonth, selectedDay in
CallvanCreateState to use that single-calendar-derived values).
feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateViewModel.kt (1)

148-166: 매직 넘버를 상수로 추출하는 것을 고려해주세요.

참가자 수 범위 18을 companion object 상수로 추출하면 의미가 명확해지고, 향후 변경 시 유지보수가 용이합니다.

♻️ 상수 추출 제안

companion object 추가:

companion object {
    private const val MIN_PARTICIPANTS = 1
    private const val MAX_PARTICIPANTS = 8
}

메서드에 적용:

 fun decrementParticipants() = blockingIntent {
     reduce {
-        if (state.maxParticipants > 1) {
+        if (state.maxParticipants > MIN_PARTICIPANTS) {
             state.copy(maxParticipants = state.maxParticipants - 1)
         } else {
             state
         }
     }
 }

 fun incrementParticipants() = blockingIntent {
     reduce {
-        if (state.maxParticipants < 8) {
+        if (state.maxParticipants < MAX_PARTICIPANTS) {
             state.copy(maxParticipants = state.maxParticipants + 1)
         } else {
             state
         }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateViewModel.kt`
around lines 148 - 166, 현재 decrementParticipants와 incrementParticipants에서 사용된 매직
넘버 1과 8을 companion object의 상수로 추출하고 사용하세요: CallvanCreateViewModel에 companion
object를 추가하여 private const val MIN_PARTICIPANTS = 1 및 private const val
MAX_PARTICIPANTS = 8을 정의하고, decrementParticipants의 조건(state.maxParticipants >
MIN_PARTICIPANTS)과 incrementParticipants의 조건(state.maxParticipants <
MAX_PARTICIPANTS) 및 상태 복사 시 해당 상수를 사용하도록 수정하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateState.kt`:
- Around line 14-16: 기본 시간이 현재 자정으로 설정되어 있으므로 CallvanCreateState의 기본값을 정오로 바꾸세요:
selectedHour를 12로 유지하고 isAm를 true에서 false로 변경(또는 AM/PM 플래그를 반전)하여 12:00 PM(정오)을
기본값으로 만들고 selectedMinute는 0으로 유지하도록 CallvanCreateState의 필드(selectedHour,
selectedMinute, isAm)를 업데이트하세요.
- Line 6: The CallvanCreateState data class is missing the `@Immutable` annotation
required by the feature module coding guideline; add the
androidx.compose.runtime.Immutable annotation above the CallvanCreateState
declaration and ensure any list properties inside CallvanCreateState use an
ImmutableList (or other immutable collection) instead of mutable lists so
Compose can optimize correctly.

In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateViewModel.kt`:
- Around line 172-186: The call to CreateCallvanPostUseCase is mapping custom
names into departureType/arrivalType; instead pass the actual enum/type into
departureType/arrivalType and supply the custom text to
departureCustomName/arrivalCustomName when the location is OTHER. Update the
call site in CallvanCreateViewModel (the createCallvanPostUseCase invocation)
so: set departureType = currentState.departureLocation!!.type (or
CallvanLocationOption.OTHER.type) and departureCustomName =
currentState.departureCustomText ?: "" when departureLocation == OTHER
(otherwise empty), and likewise set arrivalType =
currentState.arrivalLocation!!.type and arrivalCustomName =
currentState.arrivalCustomText ?: "" when arrivalLocation == OTHER (otherwise
empty), keeping the other params (departureDate, departureTime, maxParticipants)
unchanged.

---

Nitpick comments:
In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateState.kt`:
- Around line 11-13: The three default properties selectedYear, selectedMonth,
and selectedDay call Calendar.getInstance() separately which is inefficient and
can produce inconsistent date parts; compute a single Calendar instance once
(e.g., in a companion object or a private local val like now) and derive
selectedYear, selectedMonth (+1), and selectedDay from that single instance
(update references to selectedYear, selectedMonth, selectedDay in
CallvanCreateState to use that single-calendar-derived values).

In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateViewModel.kt`:
- Around line 148-166: 현재 decrementParticipants와 incrementParticipants에서 사용된 매직
넘버 1과 8을 companion object의 상수로 추출하고 사용하세요: CallvanCreateViewModel에 companion
object를 추가하여 private const val MIN_PARTICIPANTS = 1 및 private const val
MAX_PARTICIPANTS = 8을 정의하고, decrementParticipants의 조건(state.maxParticipants >
MIN_PARTICIPANTS)과 incrementParticipants의 조건(state.maxParticipants <
MAX_PARTICIPANTS) 및 상태 복사 시 해당 상수를 사용하도록 수정하세요.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 86a117c4-d9ff-4d43-be86-a8c39e1a9384

📥 Commits

Reviewing files that changed from the base of the PR and between 5520eae and ba69007.

📒 Files selected for processing (2)
  • feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateState.kt
  • feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateViewModel.kt

import `in`.koreatech.koin.feature.callvan.model.CallvanLocationOption
import java.util.Calendar

data class CallvanCreateState(
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

State 클래스에 @Immutable 애노테이션이 누락되었습니다.

코딩 가이드라인에 따르면 feature 모듈의 State 클래스는 Compose 최적화를 위해 @Immutable 애노테이션 사용이 필수입니다.

🛠️ 수정 제안
 package `in`.koreatech.koin.feature.callvan.ui.create
 
 import `in`.koreatech.koin.feature.callvan.model.CallvanLocationOption
+import androidx.compose.runtime.Immutable
 import java.util.Calendar
 
+@Immutable
 data class CallvanCreateState(

As per coding guidelines: "feature/**" - "Compose 최적화를 위해 State 클래스에 @Immutable 및 ImmutableList 사용 필수"

📝 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
data class CallvanCreateState(
package `in`.koreatech.koin.feature.callvan.ui.create
import `in`.koreatech.koin.feature.callvan.model.CallvanLocationOption
import androidx.compose.runtime.Immutable
import java.util.Calendar
`@Immutable`
data class CallvanCreateState(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateState.kt`
at line 6, The CallvanCreateState data class is missing the `@Immutable`
annotation required by the feature module coding guideline; add the
androidx.compose.runtime.Immutable annotation above the CallvanCreateState
declaration and ensure any list properties inside CallvanCreateState use an
ImmutableList (or other immutable collection) instead of mutable lists so
Compose can optimize correctly.

Comment on lines +14 to +16
val selectedHour: Int = 12,
val selectedMinute: Int = 0,
val isAm: Boolean = true,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

기본 시간이 자정(12:00 AM)으로 설정되어 있습니다.

selectedHour = 12isAm = true 조합은 자정(00:00)을 의미합니다. 콜밴 예약의 기본값으로는 정오(12:00 PM)가 더 적합할 수 있습니다.

💡 정오를 기본값으로 하려면
     val selectedHour: Int = 12,
     val selectedMinute: Int = 0,
-    val isAm: Boolean = true,
+    val isAm: Boolean = false,
📝 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
val selectedHour: Int = 12,
val selectedMinute: Int = 0,
val isAm: Boolean = true,
val selectedHour: Int = 12,
val selectedMinute: Int = 0,
val isAm: Boolean = false,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateState.kt`
around lines 14 - 16, 기본 시간이 현재 자정으로 설정되어 있으므로 CallvanCreateState의 기본값을 정오로
바꾸세요: selectedHour를 12로 유지하고 isAm를 true에서 false로 변경(또는 AM/PM 플래그를 반전)하여 12:00
PM(정오)을 기본값으로 만들고 selectedMinute는 0으로 유지하도록 CallvanCreateState의 필드(selectedHour,
selectedMinute, isAm)를 업데이트하세요.

Comment on lines +172 to +186
createCallvanPostUseCase(
departureType = if (currentState.departureLocation == CallvanLocationOption.OTHER) {
currentState.departureCustomText ?: ""
} else {
currentState.departureLocation!!.type
},
arrivalType = if (currentState.arrivalLocation == CallvanLocationOption.OTHER) {
currentState.arrivalCustomText ?: ""
} else {
currentState.arrivalLocation!!.type
},
departureDate = currentState.apiDepartureDate,
departureTime = currentState.apiDepartureTime,
maxParticipants = currentState.maxParticipants
).onSuccess { post ->
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

UseCase 호출 시 파라미터 매핑이 잘못되었습니다.

CreateCallvanPostUseCase의 시그니처를 확인하면 departureCustomNamearrivalCustomName이 별도의 파라미터로 존재합니다. 현재 코드는 OTHER 위치 타입일 때 customTextdepartureType에 직접 전달하고 있어 API 호출이 올바르지 않습니다.

domain/src/main/java/in/koreatech/koin/domain/usecase/callvan/CreateCallvanPostUseCase.kt의 시그니처:

  • departureType: 위치 타입 (예: "KOREATECH", "OTHER" 등)
  • departureCustomName: OTHER일 때의 커스텀 이름
🐛 수정 제안
         createCallvanPostUseCase(
-            departureType = if (currentState.departureLocation == CallvanLocationOption.OTHER) {
-                currentState.departureCustomText ?: ""
-            } else {
-                currentState.departureLocation!!.type
-            },
-            arrivalType = if (currentState.arrivalLocation == CallvanLocationOption.OTHER) {
-                currentState.arrivalCustomText ?: ""
-            } else {
-                currentState.arrivalLocation!!.type
-            },
+            departureType = currentState.departureLocation!!.type,
+            departureCustomName = if (currentState.departureLocation == CallvanLocationOption.OTHER) {
+                currentState.departureCustomText
+            } else {
+                null
+            },
+            arrivalType = currentState.arrivalLocation!!.type,
+            arrivalCustomName = if (currentState.arrivalLocation == CallvanLocationOption.OTHER) {
+                currentState.arrivalCustomText
+            } else {
+                null
+            },
             departureDate = currentState.apiDepartureDate,
             departureTime = currentState.apiDepartureTime,
             maxParticipants = currentState.maxParticipants
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/CallvanCreateViewModel.kt`
around lines 172 - 186, The call to CreateCallvanPostUseCase is mapping custom
names into departureType/arrivalType; instead pass the actual enum/type into
departureType/arrivalType and supply the custom text to
departureCustomName/arrivalCustomName when the location is OTHER. Update the
call site in CallvanCreateViewModel (the createCallvanPostUseCase invocation)
so: set departureType = currentState.departureLocation!!.type (or
CallvanLocationOption.OTHER.type) and departureCustomName =
currentState.departureCustomText ?: "" when departureLocation == OTHER
(otherwise empty), and likewise set arrivalType =
currentState.arrivalLocation!!.type and arrivalCustomName =
currentState.arrivalCustomText ?: "" when arrivalLocation == OTHER (otherwise
empty), keeping the other params (departureDate, departureTime, maxParticipants)
unchanged.

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.

2 participants