Skip to content

Feature/#134 일정 API 연결 및 기능 연동#135

Merged
DongChyeon merged 12 commits intorelease/v1.1.0from
feature/#134-connect-schedule-api
Apr 27, 2025
Merged

Feature/#134 일정 API 연결 및 기능 연동#135
DongChyeon merged 12 commits intorelease/v1.1.0from
feature/#134-connect-schedule-api

Conversation

@DongChyeon
Copy link
Member

@DongChyeon DongChyeon commented Apr 23, 2025

💡 Issue

🌱 Key changes

  • 전체 일정 조회 API 호출 로직 구현
  • ScheduleResponse 및 관련 DTO 구현과 도메인 모델 매핑
  • 시간, 날짜 포맷팅 유틸 함수를 ui:util 모델의 DateTime 파일로 분리

✅ To Reviewers

시간, 날짜 포맷팅 유틸 함수 어디가 넣을지 고민하다가 stringResource를 사용하기도 하고
보여주는 레이어에서만 사용한다고 생각해서 ui:util 모듈에 넣었습니다. 혹시 문제가 되면 말씀해주세요.

image 추가로 저도 똑같은 API 를 사용해야하기 때문에 태성님이 하신 커밋 몇개를 cherry-pick 했습니다. 이 4개 커밋은 무시하고 리뷰해주시면 됩니다.

📸 스크린샷

2025-04-25.3.14.44.mov

Summary by CodeRabbit

  • 신규 기능

    • 출석 내역(Attendance History) 기능이 추가되어 출석 현황, 세션 진행률, 출석/지각/결석/지각 면제권 등 다양한 출석 통계와 세션 정보를 한눈에 확인할 수 있습니다.
    • 스케줄(일정) 화면이 동적으로 개선되어 월별 일정, 세션, 과제 등 다양한 항목을 탭으로 구분하여 조회할 수 있습니다.
    • 출석 및 스케줄 관련 데이터 모델과 API 연동이 새롭게 적용되었습니다.
    • 날짜 및 시간 포맷이 한국어로 보기 쉽게 표시됩니다.
  • UI/UX 개선

    • 출석 상태, 세션 현황 등 주요 정보를 카드 및 리스트 형태로 직관적으로 제공합니다.
    • 탭, 리스트, 상태 칩 등 다양한 컴포넌트가 추가되어 사용성이 향상되었습니다.
  • 문서 및 리소스

    • 출석 및 세션 관련 한글 문자열 리소스가 추가되었습니다.
    • 시간 및 날짜 포맷을 위한 리소스가 보강되었습니다.
  • 버그 수정 및 리팩터

    • 기존 UI 컴포넌트의 enum 참조 방식이 통일되어 유지보수가 쉬워졌습니다.
    • 스케줄 관련 컴포넌트 파라미터 및 구조가 간결하게 리팩터링되었습니다.

@DongChyeon DongChyeon added the Feature 기능 추가, 개발 label Apr 23, 2025
@DongChyeon DongChyeon self-assigned this Apr 23, 2025
@coderabbitai
Copy link

coderabbitai bot commented Apr 23, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

이번 변경 사항은 출석 및 일정 관리 기능의 데이터 계층과 UI 계층을 대대적으로 확장하는 내용입니다. 새로운 데이터 모델(AttendStatistics, ScheduleInfo, Sessions)과 API 인터페이스, 레포지토리 구현체, DI 모듈이 추가되었으며, 일정 및 출석 관련 화면(히스토리, 스케줄)이 Compose 기반으로 신설되었습니다. 일정 조회, 출석 통계, 세션 목록 등은 코루틴 Flow로 비동기 처리됩니다. 또한, 날짜 및 시간 포맷 유틸, 문자열 리소스, 네비게이션 그래프 등도 추가되어 전체적인 일정/출석 기능의 토대를 마련하였습니다.

Changes

파일/경로 그룹 변경 요약
core/data-api/.../AttendanceRepository.kt,
core/data-api/.../ScheduleRepository.kt
출석/일정 레포지토리 인터페이스 신설, Flow 기반 메서드 정의
core/data/.../di/RepositoryModule.kt,
core/data/.../repository/AttendanceRepositoryImpl.kt,
core/data/.../repository/ScheduleRepositoryImpl.kt
레포지토리 DI 바인딩 추가 및 레포지토리 구현체 추가, API 연동 구현
core/data/.../api/AttendanceApi.kt,
core/data/.../api/ScheduleApi.kt,
core/data/.../di/ApiModule.kt
출석/일정 API 인터페이스 및 DI 모듈에 API 프로바이더 추가
core/data/.../model/response/AttendStatisticsResponse.kt,
core/data/.../model/response/DateGroupedScheduleResponse.kt,
core/data/.../model/response/SessionResponse.kt
출석/일정/세션 응답 모델 및 도메인 모델 변환 함수 추가
core/model/.../AttendStatistics.kt,
core/model/.../ScheduleInfo.kt,
core/model/.../Sessions.kt
출석 통계, 일정, 세션 도메인 모델 및 Enum 정의
core/ui/.../component/AttendanceStatusChip.kt AttendanceStatus Enum 외부 모델로 대체
core/ui/.../util/DateTime.kt,
core/ui/.../res/values/strings.xml
날짜/시간 포맷 유틸 함수 및 한글 포맷 문자열 리소스 추가
feature/history/... 출석 히스토리 화면, 상태/인텐트/SideEffect, UI 컴포넌트, 네비게이션, 리소스 등 신규 추가
feature/schedule/... 일정 화면, 상태/인텐트/SideEffect, 뷰모델, UI 컴포넌트, Enum, 동적 데이터 연동 등 대규모 추가 및 리팩토링
settings.gradle.kts (오타 포함) ":featrue:history" 모듈 추가

Sequence Diagram(s)

sequenceDiagram
    participant UI as ScheduleScreen
    participant VM as ScheduleViewModel
    participant Repo as ScheduleRepository
    participant API as ScheduleApi

    UI->>VM: ScheduleIntent(EnterScheduleScreen)
    VM->>Repo: getSchedules(year, month)
    Repo->>API: getSchedules(year, month)
    API-->>Repo: DateGroupedScheduleResponse
    Repo-->>VM: Flow<ScheduleList>
    VM-->>UI: Update ScheduleState with schedules
Loading
sequenceDiagram
    participant UI as AttendHistoryScreen
    participant VM as AttendHistoryViewModel
    participant UseCase as AttendHistoryUseCase
    participant AttendRepo as AttendanceRepository
    participant SessionRepo as SessionRepository
    participant AttendAPI as AttendanceApi
    participant SessionAPI as ScheduleApi

    UI->>VM: AttendHistoryIntent.OnEntryScreen
    VM->>UseCase: invoke()
    UseCase->>AttendRepo: getAttendanceStatistics()
    UseCase->>SessionRepo: getSessions()
    AttendRepo->>AttendAPI: getAttendanceStatistics()
    AttendAPI-->>AttendRepo: AttendStatisticsResponse
    SessionRepo->>SessionAPI: getSessions()
    SessionAPI-->>SessionRepo: List<SessionResponse>
    AttendRepo-->>UseCase: Flow<AttendStatistics>
    SessionRepo-->>UseCase: Flow<List<Sessions>>
    UseCase-->>VM: Flow<Pair<AttendStatistics, List<Sessions>>>
    VM-->>UI: Update AttendHistoryState
Loading

Assessment against linked issues

Objective (이슈 번호) Addressed Explanation
기간별 일정 조회 API 연결 (#134)
일정 목록 무한 스크롤(페이지네이션) 구현 (#134) 페이지네이션(무한 스크롤) 관련 구현은 포함되어 있지 않음.

Possibly related PRs

Suggested reviewers

  • TaeseongYun

Poem

🐰

새싹처럼 돋아난 출석과 일정의 길
데이터 흐름 따라 코루틴이 빙글빙글
오늘도 토끼는 깡총깡총,
히스토리와 스케줄을 한 번에 품었네!
Enum과 Flow가 춤을 추고,
UI엔 봄바람이 솔솔~
코드밭에 꽃이 피었어요! 🌸


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:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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
Member

@jinukeu jinukeu left a comment

Choose a reason for hiding this comment

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

Draft 상태가 아니어서 일단 리뷰 드립니다 ... !
(회사에서 작성해서 코멘트를 신경써서 작성하지 못했어요. 이해 안가는 부분이 있다면 말씀해주세요!)

Comment on lines +55 to +63
LaunchedEffect(viewModel.store.sideEffects) {
viewModel.store.sideEffects.onEach { effect ->
when(effect) {
SideEffect.NavigateToBack -> {
navigateToBack()
}
}
}.launchIn(scope)
}
Copy link
Member

Choose a reason for hiding this comment

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

C: collectWihtLifeCycle 확장함수 만들어놔서 이거 써주시면 좋을거같아요

Comment on lines +27 to +28
data object OnEntryScreen : AttendHistoryIntent
data object OnClickBackButton : AttendHistoryIntent
Copy link
Member

Choose a reason for hiding this comment

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

C: Intent는 'On' 없이 그냥 EntryScreen으로 작성하고 있어서 컨벤션 맞춰주시면 감사하겠습니다.

Comment on lines +16 to +23
val alertMessage: String
get() {
return if (absenceCount >= 10) {
"결석이 10번 이상이 되면 출결에 문제가 생겨 재적처리가 될 수 있어요. 출석 관리에 조금 더 신경 써 주세요!"
} else {
""
}
}
Copy link
Member

Choose a reason for hiding this comment

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

A: get을 쓰지 않더라도 매번 AttendHistoryState는 copy로 새로 생성되기 때문에 get이 없어도 될거에요

Comment on lines +30 to +32
fun fromApiValue(apiValue: String): ScheduleType {
return (entries.find { it.name == apiValue }) ?: ETC
}
Copy link
Member

Choose a reason for hiding this comment

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

C: 도메인 레이어에서 Api라는 Data 레이어를 위한 로직 처리가 있는 것 보다는 fromApiValue 로직 자체를 Data 레이어에서 처리하는건 어떠신가요?

Copy link
Member Author

Choose a reason for hiding this comment

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

package com.yapp.model


data class NoticeList(
    val notices: List<NoticeInfo>,
    val lastNoticeId: String,
    val hasNext: Boolean,
)

data class NoticeInfo(
    val id: String,
    val writerName: String,
    val writerId: String,
    val writerPosition: String,
    val writerGeneration: Int,
    val createdAt: String,
    val title: String,
    val content: String,
    val noticeType: NoticeType
) {
    val writerLabel = "${writerGeneration}$writerName"
}

enum class NoticeType(val apiValue: String, val displayValue: String) {
    ALL("ALL", "전체"),
    OPERATION("OPERATION", "운영"),
    SESSION("SESSION", "세션");

    companion object {
        fun fromApiValue(apiValue: String): NoticeType {
            return (entries.find { it.apiValue == apiValue }) ?: ALL
        }
    }
}

기존에 작업되어있던 코드를 봤을 때 fromApiValue 로직이 domain layer에 있어서 우선 그렇게 했습니다
넵 근데 여러 곳에서 쓰일 수 있는 도메인 레이어 특성상 API에 의존적인 로직이 있는게 안 어울리긴 하네요....

Copy link
Member Author

Choose a reason for hiding this comment

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

Data Layer쪽에 둔다면 단순히 dto 클래스안에 메소드를 만들지... 아니면 따로 Mapper 클래스를 만들지 고민이네요...

Copy link
Member

Choose a reason for hiding this comment

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

아 저것도 옮기긴 해야할 것 같습니다 ㅜ

저는 Mapper 클래스는 뭔가 자바 느낌이라, dto 클래스 안에 메서드를 만들 것 같아요 (또는 확장함수로 처리)

Copy link
Member Author

Choose a reason for hiding this comment

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

넵 반영했습니다

Comment on lines +8 to +18
internal class AttendHistoryUseCase @Inject constructor(
private val attendanceRepository: AttendanceRepository,
private val sessionRepository: SessionRepository
){
operator fun invoke() = combine(
attendanceRepository.getAttendanceStatistics(),
sessionRepository.getSessions()
) { attendStatistics, sessions ->
attendStatistics to sessions
}
}
Copy link
Member

Choose a reason for hiding this comment

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

A:
combine 좋네요.
getAttendanceStatistics(), getSessions()가 모두 flow로 되어있던데 일반 suspend 함수로 바꾼 후 UseCase에서 flowOf로 만들어서 처리하는건 어떠세요?

Repository 단에서는 최대한 기본 suspend 함수로 처리하면 좋을 것 같아요.
만약 getAttendanceStatistics()만 별도로 사용하게 되는 경우, flow는 오버헤드가 있을 것 같습니다.


val uiState by viewModel.store.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
viewModel.store.sideEffects.collectWithLifecycle { effect ->
Copy link
Member

Choose a reason for hiding this comment

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

여기는 collectWithLifecycle가 잘 사용되고 있군요!

Comment on lines +85 to +81
if (it == scheduleState.selectedTabIndex) return@ScheduleTabRow
onIntent(ScheduleIntent.SelectTab(it))
Copy link
Member

Choose a reason for hiding this comment

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

C: it == scheduleState.selectedTabIndex인 경우 아무 동작을 실행하지 않는 것 같은데요. 이 로직은 ViewModel로 위임하는게 더 좋을 것 같아요. UI로직은 아닌 것 같아서요!

Comment on lines 91 to 97
0 -> ScheduleAllScreen(
selectedYear = scheduleState.selectedYear,
selectedMonth = scheduleState.selectedMonth,
schedules = scheduleState.schedules,
onIntent = onIntent
)
1 -> ScheduleSessionScreen()
Copy link
Member

Choose a reason for hiding this comment

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

A: 0, 1은 enum으로 처리하면 더 좋을 것 같습니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

넵 수정했습니다
ScheduleTab을 enum 으로 관리하니 관련 컴포넌트까지 다 변경하게 되네요

@DongChyeon DongChyeon marked this pull request as draft April 23, 2025 08:21
@DongChyeon
Copy link
Member Author

Draft 상태가 아니어서 일단 리뷰 드립니다 ... ! (회사에서 작성해서 코멘트를 신경써서 작성하지 못했어요. 이해 안가는 부분이 있다면 말씀해주세요!)

헉 Draft로 표시한다는 걸 깜빡했네요... 리뷰 감사합니다!
태성님이 작업하신 프로필 조회 쪽의 세션 조회 API 설정 부분을 일정 탭 구현할 때도 사용해야 해서 필요한 부분을 cherry-pick 해서 사용했는데
그래서 태성님 커밋에 대한 리뷰도 많네요... 해당 내용은 시간날 때 옮겨둘게요

@DongChyeon DongChyeon force-pushed the feature/#125-schedule-ui branch from 40ad273 to 44de13e Compare April 23, 2025 15:12
@DongChyeon DongChyeon changed the base branch from feature/#125-schedule-ui to develop April 25, 2025 06:16
@DongChyeon DongChyeon force-pushed the feature/#134-connect-schedule-api branch from edf5b5d to 5aa54c0 Compare April 25, 2025 06:32
@DongChyeon DongChyeon requested a review from TaeseongYun April 25, 2025 06:37
@DongChyeon DongChyeon marked this pull request as ready for review April 25, 2025 06:37
@DongChyeon DongChyeon added the CodeRabbit CodeRabbit 코드 리뷰 요청 label Apr 25, 2025
@DongChyeon
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Apr 25, 2025

✅ 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: 46

🔭 Outside diff range comments (2)
feature/history/src/main/java/com/yapp/feature/history/navigation/HistoryNavigatoion.kt (1)

1-23: ⚠️ Potential issue

파일명에 오타가 있습니다.

파일 이름이 "HistoryNavigatoion.kt"로 되어 있는데, 올바른 철자는 "HistoryNavigation.kt"입니다. 코드 일관성 및 가독성을 위해 파일명을 수정하는 것이 좋겠습니다.

feature/schedule/src/main/java/com/yapp/feature/schedule/ScheduleScreen.kt (1)

233-237: 🧹 Nitpick (assertive)

날짜 문자열 포매팅 시 두 자리 월 표시를 고려해 보세요

"${year}년 ${month}월" 표현은 1월일 때 2024년 1월 로 출력됩니다.
디자인 요구 사항에 따라 01월 처럼 두 자리로 표시해야 할 수도 있으니 "%02d".format(month) 사용 여부를 한번 확인해 주세요.

♻️ Duplicate comments (4)
feature/history/src/main/java/com/yapp/feature/history/AttendHistoryUseCase.kt (1)

8-18: 리포지토리 API 설계에 대한 고려 사항

이전 리뷰에서 제안된 것처럼, Repository에서 Flow 대신 suspend 함수를 사용하고 UseCase에서 필요에 따라 Flow로 변환하는 방식을 고려해보세요. 이 접근 방식은:

  1. Repository 계층을 더 단순하게 유지하고
  2. 데이터를 필요에 따라 한 번만 가져오거나 여러 번 가져올 수 있는 유연성을 제공하며
  3. Flow가 항상 필요하지 않은 경우 오버헤드를 줄일 수 있습니다

현재 구현에서 Flow를 사용하는 것이 실시간 데이터 업데이트가 필요한 경우에는 적절할 수 있지만, 단순히 데이터를 한 번 가져오는 경우라면 불필요한 복잡성을 추가할 수 있습니다.

feature/history/src/main/java/com/yapp/feature/history/AttendHistoryContract.kt (2)

16-23: get 프로퍼티를 사용하지 않아도 될 것 같습니다.

이전 리뷰 코멘트에 언급된 것처럼, AttendHistoryState는 매번 새로 생성되므로 get 프로퍼티를 사용하지 않아도 됩니다.

- val alertMessage: String
-     get() {
-         return if (absenceCount >= 10) {
-             "결석이 10번 이상이 되면 출결에 문제가 생겨 재적처리가 될 수 있어요. 출석 관리에 조금 더 신경 써 주세요!"
-         } else {
-             ""
-         }
-     }
+ val alertMessage: String = if (absenceCount >= 10) {
+     "결석이 10번 이상이 되면 출결에 문제가 생겨 재적처리가 될 수 있어요. 출석 관리에 조금 더 신경 써 주세요!"
+ } else {
+     ""
+ }

27-28: Intent 네이밍 컨벤션을 맞춰주세요.

이전 리뷰 코멘트에서 언급된 것처럼, Intent 네이밍에는 'On' 접두사를 사용하지 않는 것이 컨벤션입니다.

sealed interface AttendHistoryIntent {
-   data object OnEntryScreen : AttendHistoryIntent
-   data object OnClickBackButton : AttendHistoryIntent
+   data object EntryScreen : AttendHistoryIntent
+   data object ClickBackButton : AttendHistoryIntent
}
feature/history/src/main/java/com/yapp/feature/history/AttendHistoryScreen.kt (1)

55-63: 🧹 Nitpick (assertive)

사용자 정의 확장 함수 사용을 고려하세요.

LaunchedEffectonEach를 사용한 사이드 이펙트 수집 코드를 간결하게 만들 수 있습니다.

개발팀에서 이미 만들어 둔 collectWihtLifeCycle 확장함수를 사용하는 것이 코드를 더 간결하게 만들 수 있습니다.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e8319a8 and 5aa54c0.

📒 Files selected for processing (38)
  • core/data-api/src/main/java/com/yapp/dataapi/AttendanceRepository.kt (1 hunks)
  • core/data-api/src/main/java/com/yapp/dataapi/ScheduleRepository.kt (1 hunks)
  • core/data/src/main/java/com/yapp/core/data/data/di/RepositoryModule.kt (2 hunks)
  • core/data/src/main/java/com/yapp/core/data/data/repository/AttendanceRepositoryImpl.kt (1 hunks)
  • core/data/src/main/java/com/yapp/core/data/data/repository/ScheduleRepositoryImpl.kt (1 hunks)
  • core/data/src/main/java/com/yapp/core/data/remote/api/AttendanceApi.kt (1 hunks)
  • core/data/src/main/java/com/yapp/core/data/remote/api/ScheduleApi.kt (1 hunks)
  • core/data/src/main/java/com/yapp/core/data/remote/di/ApiModule.kt (2 hunks)
  • core/data/src/main/java/com/yapp/core/data/remote/model/response/AttendStatisticsResponse.kt (1 hunks)
  • core/data/src/main/java/com/yapp/core/data/remote/model/response/DateGroupedScheduleResponse.kt (1 hunks)
  • core/data/src/main/java/com/yapp/core/data/remote/model/response/SessionResponse.kt (1 hunks)
  • core/model/src/main/java/com/yapp/model/AttendStatistics.kt (1 hunks)
  • core/model/src/main/java/com/yapp/model/ScheduleInfo.kt (1 hunks)
  • core/model/src/main/java/com/yapp/model/Sessions.kt (1 hunks)
  • core/ui/src/main/java/com/yapp/core/ui/component/AttendanceStatusChip.kt (1 hunks)
  • core/ui/src/main/java/com/yapp/core/ui/util/DateTime.kt (1 hunks)
  • core/ui/src/main/res/values/strings.xml (1 hunks)
  • feature/history/.gitignore (1 hunks)
  • feature/history/build.gradle.kts (1 hunks)
  • feature/history/proguard-rules.pro (1 hunks)
  • feature/history/src/main/AndroidManifest.xml (1 hunks)
  • feature/history/src/main/java/com/yapp/feature/history/AttendHistoryContract.kt (1 hunks)
  • feature/history/src/main/java/com/yapp/feature/history/AttendHistoryScreen.kt (1 hunks)
  • feature/history/src/main/java/com/yapp/feature/history/AttendHistoryUseCase.kt (1 hunks)
  • feature/history/src/main/java/com/yapp/feature/history/AttendHistoryViewModel.kt (1 hunks)
  • feature/history/src/main/java/com/yapp/feature/history/components/AttendanceStatusSession.kt (1 hunks)
  • feature/history/src/main/java/com/yapp/feature/history/components/SessionAttendanceItem.kt (1 hunks)
  • feature/history/src/main/java/com/yapp/feature/history/components/StatusItem.kt (1 hunks)
  • feature/history/src/main/java/com/yapp/feature/history/navigation/HistoryNavigatoion.kt (1 hunks)
  • feature/history/src/main/res/values/strings.xml (1 hunks)
  • feature/schedule/src/main/java/com/yapp/feature/schedule/ScheduleContract.kt (1 hunks)
  • feature/schedule/src/main/java/com/yapp/feature/schedule/ScheduleScreen.kt (3 hunks)
  • feature/schedule/src/main/java/com/yapp/feature/schedule/ScheduleViewModel.kt (1 hunks)
  • feature/schedule/src/main/java/com/yapp/feature/schedule/component/AssignmentItem.kt (2 hunks)
  • feature/schedule/src/main/java/com/yapp/feature/schedule/component/DateGroupedScheduleItem.kt (1 hunks)
  • feature/schedule/src/main/java/com/yapp/feature/schedule/component/ScheduleTabRow.kt (5 hunks)
  • feature/schedule/src/main/java/com/yapp/feature/schedule/component/SessionItem.kt (3 hunks)
  • settings.gradle.kts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (10)
feature/history/build.gradle.kts (1)
build-logic/src/main/java/com/yapp/AppNameExtension.kt (1)
  • setNamespace (6-10)
feature/history/src/main/java/com/yapp/feature/history/components/StatusItem.kt (1)
core/designsystem/src/main/java/com/yapp/core/designsystem/theme/Theme.kt (1)
  • YappTheme (9-24)
feature/history/src/main/java/com/yapp/feature/history/components/AttendanceStatusSession.kt (2)
core/designsystem/src/main/java/com/yapp/core/designsystem/theme/Theme.kt (1)
  • YappTheme (9-24)
feature/history/src/main/java/com/yapp/feature/history/components/StatusItem.kt (1)
  • StatusItem (15-29)
feature/schedule/src/main/java/com/yapp/feature/schedule/component/DateGroupedScheduleItem.kt (4)
core/ui/src/main/java/com/yapp/core/ui/util/DateTime.kt (3)
  • isPastDate (52-60)
  • formatToDay (9-16)
  • formatTimeRange (18-22)
feature/schedule/src/main/java/com/yapp/feature/schedule/component/SessionItem.kt (1)
  • SessionItem (20-66)
feature/schedule/src/main/java/com/yapp/feature/schedule/component/AssignmentItem.kt (1)
  • AssignmentItem (15-43)
core/designsystem/src/main/java/com/yapp/core/designsystem/theme/Theme.kt (1)
  • YappTheme (9-24)
feature/history/src/main/java/com/yapp/feature/history/AttendHistoryViewModel.kt (1)
core/ui/src/main/java/com/yapp/core/ui/mvi/MviIntentStore.kt (1)
  • mviIntentStore (53-60)
feature/history/src/main/java/com/yapp/feature/history/AttendHistoryScreen.kt (3)
core/designsystem/src/main/java/com/yapp/core/designsystem/theme/Theme.kt (1)
  • YappTheme (9-24)
feature/history/src/main/java/com/yapp/feature/history/components/AttendanceStatusSession.kt (1)
  • AttendanceStatusSection (24-67)
feature/history/src/main/java/com/yapp/feature/history/components/StatusItem.kt (1)
  • StatusItem (15-29)
feature/schedule/src/main/java/com/yapp/feature/schedule/ScheduleViewModel.kt (1)
core/ui/src/main/java/com/yapp/core/ui/mvi/MviIntentStore.kt (1)
  • mviIntentStore (53-60)
feature/history/src/main/java/com/yapp/feature/history/navigation/HistoryNavigatoion.kt (1)
feature/history/src/main/java/com/yapp/feature/history/AttendHistoryScreen.kt (1)
  • AttendHistoryRoute (30-64)
feature/schedule/src/main/java/com/yapp/feature/schedule/component/ScheduleTabRow.kt (1)
core/designsystem/src/main/java/com/yapp/core/designsystem/theme/Theme.kt (1)
  • YappTheme (9-24)
feature/schedule/src/main/java/com/yapp/feature/schedule/component/SessionItem.kt (1)
feature/schedule/src/main/java/com/yapp/feature/schedule/component/IconWithText.kt (1)
  • IconWithText (14-36)
🪛 detekt (1.23.7)
feature/history/src/main/java/com/yapp/feature/history/components/SessionAttendanceItem.kt

[warning] 9-11: This empty block of code can be removed.

(detekt.empty-blocks.EmptyFunctionBlock)

core/model/src/main/java/com/yapp/model/Sessions.kt

[warning] 13-15: The class or object AttendType is empty.

(detekt.empty-blocks.EmptyClassBlock)

core/ui/src/main/java/com/yapp/core/ui/util/DateTime.kt

[warning] 13-13: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)


[warning] 47-47: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)


[warning] 57-57: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)


[warning] 13-13: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 47-47: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 57-57: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🔇 Additional comments (27)
core/ui/src/main/res/values/strings.xml (1)

10-16: 새로운 시간 형식 문자열 리소스 추가
시간/분 포맷팅을 위한 문자열 리소스를 깔끔하게 잘 정의하셨습니다. 로컬라이제이션도 적절히 수행되었네요.

feature/history/.gitignore (1)

1-1: 빌드 아티팩트 무시 설정
/build 디렉토리를 무시하도록 한 것은 적절합니다.

feature/history/src/main/AndroidManifest.xml (1)

1-4: 라이브러리 모듈 namespace 설정 확인 요청
AndroidManifest에 package 속성이 없는 상태이므로, AGP 7.0+ 환경에서 build.gradle.ktsnamespace 설정을 통해 정상 병합되는지 검증이 필요합니다.

feature/history/build.gradle.ktsnamespace = "com.yapp.feature.history"와 같은 설정이 존재하는지 확인해주세요.

feature/history/proguard-rules.pro (1)

1-21: 스킵: 프로가드 설정 파일에 활성화된 룰이 없습니다.
현재 모든 내용이 주석 처리된 템플릿으로, 향후 필요 시 프로가드 룰을 추가할 예정이라면 문제 없습니다.

feature/history/build.gradle.kts (1)

1-17: 모듈 설정 및 네임스페이스 확인
setNamespace("feature.history")com.yapp.feature.history로 올바르게 설정되었고, 필요한 Compose 및 Material 의존성을 잘 추가했습니다.

core/ui/src/main/java/com/yapp/core/ui/component/AttendanceStatusChip.kt (1)

17-30: 공유 모델 enum 사용으로 일관성 확보
로컬 정의한 AttendanceStatus를 제거하고 com.yapp.model.AttendanceStatus를 사용하여 도메인 모델과 UI 사이의 중복을 해소한 점이 적절합니다. 문자열 리소스 매핑도 문제 없어 보입니다.

core/model/src/main/java/com/yapp/model/AttendStatistics.kt (1)

3-12: 잘 구현된 데이터 모델입니다!

출석 통계를 위한 도메인 모델이 명확하게 설계되어 있습니다. 필드명이 직관적이고 모든 속성이 적절한 타입으로 정의되어 있습니다.

core/data/src/main/java/com/yapp/core/data/remote/api/AttendanceApi.kt (1)

11-12: 잘 구현된 API 메서드입니다

통계 데이터를 가져오는 GET 요청이 적절하게 정의되어 있습니다.

core/data-api/src/main/java/com/yapp/dataapi/ScheduleRepository.kt (1)

1-11: 인터페이스가 잘 정의되었습니다.

ScheduleRepository 인터페이스는 스케줄 관련 데이터를 비동기적으로 가져오는 두 가지 메서드를 명확하게 정의하고 있습니다. Kotlin Flow를 사용하여 반응형 프로그래밍 방식을 적절히 적용했습니다.

core/data/src/main/java/com/yapp/core/data/remote/model/response/AttendStatisticsResponse.kt (1)

7-28: 응답 모델과 도메인 변환 로직이 잘 구현되었습니다.

AttendStatisticsResponse 클래스와 toModel() 메서드를 통해 API 응답을 도메인 모델로 변환하는 로직이 깔끔하게 구현되었습니다. @Serializable 어노테이션을 사용하여 JSON 직렬화/역직렬화를 적절히 처리하고 있습니다.

feature/schedule/src/main/java/com/yapp/feature/schedule/component/AssignmentItem.kt (2)

17-20: ID 타입의 변경이 잘 적용되었습니다.

기존 Long 타입에서 String 타입으로 ID 매개변수와 onClick 핸들러를 변경한 것은 백엔드 시스템과의 호환성을 높이는 좋은 접근입니다. UUID나 다른 문자열 기반 식별자를 사용하는 API와 더 잘 맞습니다.


22-41: UI 구성 요소가 적절히 단순화되었습니다.

날짜 관련 정보와 과거/미래 상태에 따른 조건부 스타일링을 제거하고 제목과 내용만 표시하도록 변경되었습니다. 이는 관심사 분리에 좋은 접근이며, 날짜 관련 스타일링은 상위 컴포넌트(DateGroupedScheduleItem)에서 처리하는 방식이 코드 유지보수성을 높입니다.

feature/schedule/src/main/java/com/yapp/feature/schedule/component/DateGroupedScheduleItem.kt (2)

46-50: 오늘 날짜의 시각적 구분이 잘 구현되었습니다.

오늘 날짜를 isToday 플래그를 통해 기본 색상으로 강조하는 방식은 사용자 경험 측면에서 좋습니다. 현재 날짜를 시각적으로 구분해 사용자가 일정을 더 쉽게 파악할 수 있게 합니다.


40-40: 접근성 고려사항

과거 날짜에 alpha 값 0.5f를 적용하는 것은 시각적 계층 구조를 만드는 데 효과적이지만, 낮은 대비로 인해 시각적 장애가 있는 사용자에게 접근성 문제를 일으킬 수 있습니다. 최소 대비 비율을 유지하면서 과거 항목을 구분할 수 있는 다른 방법(색상 변경, 레이블 추가 등)도 고려해보세요.

feature/history/src/main/java/com/yapp/feature/history/navigation/HistoryNavigatoion.kt (2)

9-14: 타입 안전 네비게이션 구현이 잘 되었습니다.

Serializable data object 대상과 NavController 확장 함수를 사용한 네비게이션 구현은 타입 안전성을 보장하고 네비게이션 로직을 깔끔하게 캡슐화합니다. 모듈식 아키텍처에 잘 맞는 접근 방식입니다.


16-22: 백 네비게이션 처리가 잘 구현되었습니다.

NavGraphBuilder 확장 함수는 백 네비게이션을 위한 콜백을 적절히 전달하여 사용자 경험을 잘 고려한 구현입니다. 각 화면의 책임을 명확히 분리하고 있습니다.

feature/history/src/main/java/com/yapp/feature/history/components/AttendanceStatusSession.kt (1)

69-99: 다양한 사용 사례를 보여주는 프리뷰가 잘 구현되었습니다.

두 가지 다른 사용 사례(개인 출석 및 세션 상태)를 보여주는 프리뷰 구현이 좋습니다. 개발 및 디자인 검토 과정에서 컴포넌트가 다양한 상황에서 어떻게 보이는지 빠르게 확인할 수 있게 해줍니다.

core/ui/src/main/java/com/yapp/core/ui/util/DateTime.kt (1)

18-22: formatTimeRange 함수의 null 처리가 잘되어 있습니다.

null이나 빈 문자열에 대한 검사를 통해 안전하게 함수를 구현했습니다.

feature/schedule/src/main/java/com/yapp/feature/schedule/component/ScheduleTabRow.kt (5)

31-37: Enum 타입 사용이 좋습니다.

String 리스트 대신 ScheduleTab enum을 사용하여 타입 안전성을 높였습니다. 이는 오타나 잘못된 값을 방지하는 데 도움이 됩니다.


57-61: 코드가 더 명확하고 유지보수하기 쉬워졌습니다.

인덱스 비교 대신 enum의 index 속성을 비교하도록 변경한 것이 좋습니다. 이렇게 하면 코드의 의도가 더 명확해집니다.


68-73: 문자열 리소스 ID 사용은 좋은 방식입니다.

하드코딩된 문자열 대신 리소스 ID를 사용하는 것은 다국어 지원과 유지보수성을 향상시킵니다.


84-86: stringResource 함수 사용이 잘 되었습니다.

문자열을 하드코딩하는 대신 stringResource를 사용하여 리소스에서 문자열을 가져오는 것이 좋습니다.


107-115: enum 사용과 상태 관리 개선이 잘 되었습니다.

ViewModel에서 상태로 관리되는 selectedTab을 사용하고, 전체 enum 항목을 tabList로 사용하는 것이 간결하고 유지보수하기 좋습니다.

feature/history/src/main/java/com/yapp/feature/history/AttendHistoryScreen.kt (3)

36-38: onIntent를 LaunchedEffect 내부에서 호출하는 것이 좋습니다.

화면 진입 시 Intent를 발행하는 패턴이 잘 적용되었습니다.


86-102: UI 구조가 잘 구성되어 있습니다.

상태 섹션이 명확하게 나누어져 있고, 각 섹션에 적절한 여백을 사용하여 가독성이 좋습니다.


103-120: 코드 재사용이 잘 되어 있습니다.

AttendanceStatusSectionStatusItem 컴포넌트를 재사용하여 코드 중복을 피하고 일관된 UI를 유지하는 좋은 접근 방식입니다.

core/data/src/main/java/com/yapp/core/data/remote/model/response/DateGroupedScheduleResponse.kt (1)

120-128: API 신규 상태가 들어오면 ‘SCHEDULED’로 묻혀 버립니다

else -> AttendanceStatus.SCHEDULED 로 fallback 하는 것은 안전장치지만,
미지원 값이 들어왔을 때 로그를 남기거나 UNKNOWN 값을 두어 문제를 조기에 인지하는 편이 좋습니다.
무심코 잘못된 통계를 보게 될 여지가 있습니다.

":feature:login",
":feature:schedule",
":feature:profile",
":featrue:history"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

모듈 경로 오타 수정 필요
include에 등록된 모듈명이 "feature"가 아닌 "featrue"로 오타가 있어 프로젝트 동기화 오류가 발생합니다.

아래와 같이 수정해주세요:

-    ":featrue:history"
+    ":feature:history"
📝 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
":featrue:history"
":feature:history"

Comment on lines +1 to +14
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="attendance_title">출석내역</string>
<string name="attendance_my_status">내 출석 현황</string>
<string name="attendance_total_score">총 점수</string>
<string name="attendance_item">출석</string>
<string name="late_item">지각</string>
<string name="absence_item">결석</string>
<string name="latePass_item">지각 면제권</string>
<string name="session_title">세션 현황</string>
<string name="total_session_count_title">전체 세션</string>
<string name="total_remain_session_count_title">남은 세션</string>
<string name="total_remain_session_progress_rate_title">진행률</string>
</resources>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

리소스 키 네이밍 일관성 검토
대부분 snake_case를 사용하시는 중에 "latePass_item"만 camelCase로 혼용되고 있습니다. 일관성을 위해 "late_pass_item"으로 변경을 권장드립니다.

-    <string name="latePass_item">지각 면제권</string>
+    <string name="late_pass_item">지각 면제권</string>

코드에서 해당 리소스를 참조하는 부분도 함께 업데이트해 주세요.

📝 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
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="attendance_title">출석내역</string>
<string name="attendance_my_status">내 출석 현황</string>
<string name="attendance_total_score">총 점수</string>
<string name="attendance_item">출석</string>
<string name="late_item">지각</string>
<string name="absence_item">결석</string>
<string name="latePass_item">지각 면제권</string>
<string name="session_title">세션 현황</string>
<string name="total_session_count_title">전체 세션</string>
<string name="total_remain_session_count_title">남은 세션</string>
<string name="total_remain_session_progress_rate_title">진행률</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="attendance_title">출석내역</string>
<string name="attendance_my_status">내 출석 현황</string>
<string name="attendance_total_score">총 점수</string>
<string name="attendance_item">출석</string>
<string name="late_item">지각</string>
<string name="absence_item">결석</string>
<string name="late_pass_item">지각 면제권</string>
<string name="session_title">세션 현황</string>
<string name="total_session_count_title">전체 세션</string>
<string name="total_remain_session_count_title">남은 세션</string>
<string name="total_remain_session_progress_rate_title">진행률</string>
</resources>

Comment on lines +5 to +11
@Composable
internal fun SessionAttendanceItem(
date: String,
time: String? = null,
) {

}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

빈 Composable 함수 구현 필요
현재 SessionAttendanceItem이 본문 없이 빈 함수로 정의되어 있습니다. UI 요소가 없으면 화면에 아무것도 그려지지 않으니, 실제 디자인에 맞춘 레이아웃 또는 최소한 // TODO: 구현 주석을 추가하여 향후 작업 계획을 명확히 남겨주세요.

🧰 Tools
🪛 detekt (1.23.7)

[warning] 9-11: This empty block of code can be removed.

(detekt.empty-blocks.EmptyFunctionBlock)

Comment on lines +6 to +8
interface AttendanceRepository {
fun getAttendanceStatistics(): Flow<AttendStatistics>
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

인터페이스에 KDoc 추가 고려
AttendanceRepositorygetAttendanceStatistics 메서드에 설명을 위한 KDoc을 추가하면, 나중에 IDE에서 문서화를 확인하거나 API 사용 시 이해도를 높일 수 있습니다.

Comment on lines +59 to +61
fun providesSessionsApi(@AuthRetrofit retrofit: Retrofit): ScheduleApi {
return retrofit.create(ScheduleApi::class.java)
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

메서드 이름의 일관성 개선 필요

메서드 이름이 현재 providesSessionsApi로 되어 있는데 다른 모든 메서드들은 provide로 시작합니다. 코드 일관성을 위해 provideSessionsApi 또는 provideScheduleApi로 변경하는 것이 좋겠습니다.

-    fun providesSessionsApi(@AuthRetrofit retrofit: Retrofit): ScheduleApi {
+    fun provideScheduleApi(@AuthRetrofit retrofit: Retrofit): ScheduleApi {
        return retrofit.create(ScheduleApi::class.java)
    }
📝 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 providesSessionsApi(@AuthRetrofit retrofit: Retrofit): ScheduleApi {
return retrofit.create(ScheduleApi::class.java)
}
fun provideScheduleApi(@AuthRetrofit retrofit: Retrofit): ScheduleApi {
return retrofit.create(ScheduleApi::class.java)
}

Comment on lines +56 to 60
IconWithText(
iconResId = R.drawable.icon_location,
text = location,
contentDescription = null,
)
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

접근성 향상을 위해 contentDescription을 채워 주세요

아이콘의 contentDescriptionnull로 전달되어 스크린리더가 해당 정보를 건너뜁니다.
장애인 접근성을 고려해 "시간 정보", "위치 정보" 등 의미 있는 문자열을 지정해 주시면 좋겠습니다.

Comment on lines +45 to 52
duration?.let {
IconWithText(
iconResId = R.drawable.icon_time,
text = duration,
contentDescription = null,
)
}

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

duration 가 없을 때 불필요한 여백이 생깁니다

Spacer(4.dp)duration 블록 바깥에 있어, durationlocation 모두 null일 때도 빈 공간이 렌더링됩니다.
조건부 UI 안쪽으로 옮기면 예상치 못한 여백을 방지할 수 있습니다.

-            duration?.let {
-                IconWithText(
-                    iconResId = R.drawable.icon_time,
-                    text = duration,
-                    contentDescription = null,
-                )
-            }
-
-            Spacer(modifier = Modifier.height(4.dp))
+            duration?.let {
+                IconWithText(
+                    iconResId = R.drawable.icon_time,
+                    text = it,
+                    contentDescription = null,
+                )
+                Spacer(modifier = Modifier.height(4.dp))
+            }
📝 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
duration?.let {
IconWithText(
iconResId = R.drawable.icon_time,
text = duration,
contentDescription = null,
)
}
duration?.let {
IconWithText(
iconResId = R.drawable.icon_time,
text = it,
contentDescription = null,
)
Spacer(modifier = Modifier.height(4.dp))
}

Comment on lines +96 to +104
companion object {
fun toScheduleType(value: String): ScheduleType =
when (value) {
"SESSION" -> ScheduleType.SESSION
"TASK" -> ScheduleType.TASK
else -> ScheduleType.ETC
}

fun toSessionType(value: String?): SessionType? =
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

문자열 → enum 매핑 로직이 중복되고 확장에 약합니다

when 블록을 enum 개수만큼 반복하면 새 값 추가 시 세 곳을 모두 수정해야 합니다.
enum.entries.firstOrNull { it.name == value } ?: DEFAULT 패턴으로 일반화하거나, 각 enum 클래스 안에 fromApiValue 를 확장 함수로 두면 중복을 없앨 수 있습니다.

fun String.toScheduleType() =
    ScheduleType.entries.firstOrNull { it.name == this } ?: ScheduleType.ETC

이렇게 하면 가독성과 유지보수성이 향상됩니다.

Copy link
Member

Choose a reason for hiding this comment

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

A: 코드 래빗 리뷰 반영하시는거 어떠세요?

Comment on lines 117 to 127
items(items = schedules.dates) {
DateGroupedScheduleItem(
date = it.date,
dayOfWeek = it.dayOfTheWeek,
isToday = it.isToday,
schedules = it.schedules,
) { }
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

LazyColumn items에 key를 지정해 리스트 성능을 높여 주세요

schedules.dates 의 각 원소는 날짜 기준으로 고유하므로 key = { it.date } 정도를 주면
아이템 재구성 시 불필요한 recomposition 및 스크롤 위치 잃어버림을 방지할 수 있습니다.

items(
    items = schedules.dates,
    key = { it.date }
) { dateGroup -> /* ... */ }

Comment on lines +48 to +56
val uiState by viewModel.store.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
viewModel.store.sideEffects.collectWithLifecycle { effect ->
when (effect) {
is ScheduleSideEffect.ShowToast -> {
Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show()
}
}
}
Copy link

@coderabbitai coderabbitai bot Apr 25, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

collectWithLifecycle가 재컴포지션마다 중복 구독될 가능성이 있습니다

viewModel.store.sideEffects.collectWithLifecycle { ... } 호출이 ScheduleRoute 컴포저블 본문에 직접 위치해 있습니다.
재컴포지션이 잦은 화면이라면 Flow 구독이 여러 번 생성되어 Toast 가 중복 표시되거나 불필요한 리소스가 낭비될 수 있습니다.

LaunchedEffect(Unit) 블록 안으로 옮겨 한 번만 collect 되도록 하는 편이 안전합니다.

-    viewModel.store.sideEffects.collectWithLifecycle { effect ->
+    LaunchedEffect(Unit) {
+        viewModel.store.sideEffects.collectWithLifecycle { effect ->
             /* ... */
-    }
+        }
+    }
📝 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 uiState by viewModel.store.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
viewModel.store.sideEffects.collectWithLifecycle { effect ->
when (effect) {
is ScheduleSideEffect.ShowToast -> {
Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show()
}
}
}
val uiState by viewModel.store.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
LaunchedEffect(Unit) {
viewModel.store.sideEffects.collectWithLifecycle { effect ->
when (effect) {
is ScheduleSideEffect.ShowToast -> {
Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show()
}
}
}
}

Copy link
Member

Choose a reason for hiding this comment

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

@coderabbitai

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.flow.Flow

@Composable
inline fun <reified T> Flow<T>.collectWithLifecycle(
    minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
    noinline action: suspend (T) -> Unit,
) {
    val lifecycleOwner = LocalLifecycleOwner.current
    LaunchedEffect(this, lifecycleOwner) {
        lifecycleOwner.lifecycle.repeatOnLifecycle(minActiveState) {
            this@collectWithLifecycle.collect { action(it) }
        }
    }
}

collectWithLifecycle의 내부 구현을 살펴보면 이미 LaunchedEffect로 관리되고 있어. 다시 한번 검토해줘.

Copy link

Choose a reason for hiding this comment

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

@jinukeu 네, 맞습니다! 제가 코드를 다시 검토해보니 collectWithLifecycle 확장 함수가 이미 내부적으로 LaunchedEffect를 사용하고 있군요. 따라서 이를 다시 LaunchedEffect로 감싸는 것은 불필요합니다.

@Composable
inline fun <reified T> Flow<T>.collectWithLifecycle(
    minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
    noinline action: suspend (T) -> Unit,
) {
    val lifecycleOwner = LocalLifecycleOwner.current
    LaunchedEffect(this, lifecycleOwner) { // 이미 LaunchedEffect 포함
        lifecycleOwner.lifecycle.repeatOnLifecycle(minActiveState) {
            this@collectWithLifecycle.collect { action(it) }
        }
    }
}

Flow와 lifecycleOwner를 키로 사용하여 재구성 시에도 한 번만 실행되도록 이미 설계되어 있습니다. 제 제안은 중첩된 LaunchedEffect를 만들어 불필요한 복잡성을 추가할 뿐이었네요. 지적해 주셔서 감사합니다!


✏️ Learnings added
Learnt from: jinukeu
PR: YAPP-admin/yappu-world-android#135
File: feature/schedule/src/main/java/com/yapp/feature/schedule/ScheduleScreen.kt:48-56
Timestamp: 2025-04-26T17:07:21.372Z
Learning: `collectWithLifecycle` 확장 함수는 이미 내부적으로 LaunchedEffect를 사용하여 재컴포지션 시에도 Flow가 한 번만 구독되도록 설계되어 있으므로, 이를 추가적인 LaunchedEffect로 감싸는 것은 불필요하다.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

오옹 안에 있는 코드까지 제대로 안봤었네요... 확인해주셔서 감사합니다

@DongChyeon
Copy link
Member Author

@jinukeu @TaeseongYun 리뷰 가능하신가요??

@jinukeu
Copy link
Member

jinukeu commented Apr 26, 2025

@jinukeu @TaeseongYun 리뷰 가능하신가요??

아 review re request가 안되어서 아직 작업 중이신 줄 알았어요 지금 하겠습니당

@jinukeu jinukeu self-requested a review April 26, 2025 14:26
Comment on lines +96 to +104
companion object {
fun toScheduleType(value: String): ScheduleType =
when (value) {
"SESSION" -> ScheduleType.SESSION
"TASK" -> ScheduleType.TASK
else -> ScheduleType.ETC
}

fun toSessionType(value: String?): SessionType? =
Copy link
Member

Choose a reason for hiding this comment

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

A: 코드 래빗 리뷰 반영하시는거 어떠세요?

Comment on lines +26 to +33
ALL(0, R.string.schedule_tab_all),
SESSION(1, R.string.schedule_tab_session);

companion object {
fun fromIndex(index: Int): ScheduleTab {
return entries.find { it.index == index } ?: ALL
}
}
Copy link
Member

Choose a reason for hiding this comment

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

A:
index를 없애고 ordinal를 사용해도 좋을 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

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

넵 코드 래빗 리뷰 반영했고... fromIndex 메소드는 현재 사용하지 않아서 제거했습니다.

Copy link
Member

Choose a reason for hiding this comment

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

감사합니다. 어프로브는 이미 해놔서 머지하셔도 됩니다요!

@DongChyeon
Copy link
Member Author

@TaeseongYun
현재 제 PR 에서 태성님 커밋을 사용하기도 하고
SessionRepository, SessionApi의 경우 크게 보면 Schedule이라는 도메인이라고 생각해서 이름을
ScheduleRepository, ScheduleApi로 수정한 상태입니다!

그래서 태성님 PR을 먼저 병합하고 나서 해당 PR은 충돌을 해결해서 병합하는 게 좋다고 생각하는데
마이페이지 관련 PR을 release/1.1.0에 병합하셔서 사용할 브랜치 전략이랑 병합 순서에 대해 생각해두신 게 있다면 알려주시면 감사하겠습니다!

@DongChyeon DongChyeon changed the base branch from develop to release/v1.1.0 April 26, 2025 15:45
@TaeseongYun
Copy link
Contributor

@TaeseongYun 현재 제 PR 에서 태성님 커밋을 사용하기도 하고 SessionRepository, SessionApi의 경우 크게 보면 Schedule이라는 도메인이라고 생각해서 이름을 ScheduleRepository, ScheduleApi로 수정한 상태입니다!

그래서 태성님 PR을 먼저 병합하고 나서 해당 PR은 충돌을 해결해서 병합하는 게 좋다고 생각하는데 마이페이지 관련 PR을 release/1.1.0에 병합하셔서 사용할 브랜치 전략이랑 병합 순서에 대해 생각해두신 게 있다면 알려주시면 감사하겠습니다!

크게 생각해둔 브랜치 전력이 없긴했는데 제 피알에 코멘트가 달려 수정하는데 좀 걸릴거 같아 해당 피알을 먼저 선 반영해도 될듯합니다.

Copy link
Contributor

@TaeseongYun TaeseongYun 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 +52 to +51
fun isPastDate(dateString: String, pattern: String = "yyyy-MM-dd"): Boolean {
return try {
val formatter = DateTimeFormatter.ofPattern(pattern)
val inputDate = LocalDate.parse(dateString, formatter)
inputDate.isBefore(LocalDate.now())
} catch (e: Exception) {
false
}
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

C : 오전 오후를 표시하고 싶으면 a 패턴을 사용하면 간편하게 사용할 수 있습니다 ~

스크린샷 2025-04-27 오전 9 48 06

https://developer.android.com/reference/java/time/format/DateTimeFormatter

Copy link
Member Author

Choose a reason for hiding this comment

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

반영했습니다!

@DongChyeon DongChyeon force-pushed the feature/#134-connect-schedule-api branch from 4c70229 to e3e5a80 Compare April 27, 2025 03:47
@DongChyeon DongChyeon merged commit 4c47f5b into release/v1.1.0 Apr 27, 2025
1 check passed
@DongChyeon DongChyeon deleted the feature/#134-connect-schedule-api branch May 14, 2025 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CodeRabbit CodeRabbit 코드 리뷰 요청 Feature 기능 추가, 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 일정 API 연결 및 기능 연동

3 participants