Skip to content

Conversation

@vvan2
Copy link
Member

@vvan2 vvan2 commented Nov 12, 2025

ISSUE

❗ WORK DESCRIPTION

  • module 수정(time out 나서 살짝 수정했어요)
  • domain/게시물 상세페이지 API
  • data/게시물 상세페이지 API
  • presentation/게시물 상세페이지 API

📸 SCREENSHOT

Android.Emulator.-.google_5554.2025-11-12.21-42-36.mp4

@vvan2 vvan2 requested a review from dmp100 November 12, 2025 12:43
@vvan2 vvan2 self-assigned this Nov 12, 2025
@vvan2 vvan2 added feature ✨ 기능 구현 주완🐹 주완 전용 라벨 labels Nov 12, 2025
@vvan2 vvan2 linked an issue Nov 12, 2025 that may be closed by this pull request
1 task
@coderabbitai
Copy link

coderabbitai bot commented Nov 12, 2025

Walkthrough

This PR implements community post detail retrieval functionality across the network, data, domain, and presentation layers. It introduces new DTOs for post details and nested comments, adds corresponding API endpoints and repository methods, refactors the post screen from static rendering to state-driven UI, and updates navigation to use Int instead of String for postId type.

Changes

Cohort / File(s) Change Summary
Network & Dependency Injection
app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt
Added TimeUnit import and configured OkHttpClient with explicit 30-second timeouts for connect, read, and write operations.
Data Transfer Objects
app/src/main/java/com/hsLink/hslink/data/dto/response/community/CommunityDetailResponseDto.kt
Introduced two new serializable data classes: CommunityDetailResponseDto (with id, author, studentId, authorStatus, title, body, mine, comments fields) and nested CommentDto (with id, commenter, commenterStatus, content, mine fields).
Service & Repository Layer
app/src/main/java/com/hsLink/hslink/data/service/commuunity/CommunityPostService.kt, app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CommunityRepositoryImpl.kt
Added GET posts/{postId} API endpoint and corresponding getCommunityDetail(postId: Int) repository method returning Result<CommunityDetailResponseDto>.
Domain Repository Interface
app/src/main/java/com/hsLink/hslink/domain/repository/community/CommunityRepository.kt
Added getCommunityDetail(postId: Int) suspend function and import for CommunityDetailResponseDto.
State Management
app/src/main/java/com/hsLink/hslink/presentation/community/state/CommunityContract.kt, app/src/main/java/com/hsLink/hslink/presentation/community/viewmodel/CommunityViewModel.kt
Introduced sealed interface CommunityDetailState with Loading, Success, and Error variants; added postDetailState: StateFlow<CommunityDetailState> and getPostDetail(postId: Int) function to ViewModel.
Navigation
app/src/main/java/com/hsLink/hslink/presentation/community/navigation/post/CommunityPostNavigation.kt, app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt, app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt
Changed postId parameter type from String to Int across navigation API (navigateToCommunityPost, CommunityPost data class) and updated call sites in MainNavHost and MainNavigator.
Presentation Screen
app/src/main/java/com/hsLink/hslink/presentation/community/screen/post/CommunityPostScreen.kt
Refactored CommunityPostScreen from static rendering to state-driven UI: added postId: Int and postDetailState: CommunityDetailState parameters, integrated LaunchedEffect-based data loading, added Loading and Error state rendering with CircularProgressIndicator and error messages, updated comment rendering to use dynamic data from state (commenter, commenterStatus) and changed selectedCommentId from String? to Int?.

Sequence Diagram

sequenceDiagram
    actor User
    participant Nav as Navigation
    participant Screen as CommunityPostScreen
    participant VM as CommunityViewModel
    participant Repo as CommunityRepository
    participant API as CommunityPostService
    
    User->>Nav: Navigate to post detail (postId)
    Nav->>Screen: CommunityPostRoute(postId)
    Screen->>Screen: LaunchedEffect triggered
    Screen->>VM: getPostDetail(postId)
    VM->>VM: Update postDetailState to Loading
    VM->>Repo: getCommunityDetail(postId)
    Repo->>API: GET /posts/{postId}
    API-->>Repo: Response<CommunityDetailResponseDto>
    alt Success
        Repo-->>VM: Result.Success
        VM->>VM: Update postDetailState to Success(post)
    else Error
        Repo-->>VM: Result.Failure
        VM->>VM: Update postDetailState to Error(message)
    end
    VM-->>Screen: postDetailState collected
    Screen->>Screen: Render based on state
    alt Loading State
        Screen->>Screen: Show CircularProgressIndicator
    else Error State
        Screen->>Screen: Show error message
    else Success State
        Screen->>Screen: Render post header, content & comments
    end
    Screen-->>User: Display post detail
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • CommunityPostScreen.kt requires careful attention: significant refactoring from static to state-driven rendering with multiple state branches (Loading, Error, Success), updated data model field names (commenter, commenterStatus), and modified deletion flow logic
  • Navigation type changes (String to Int) span multiple files (MainNavigator, MainNavHost, CommunityPostNavigation) but follow a consistent pattern—verify all call sites are updated
  • ViewModel and state management additions are straightforward but should be validated for proper scope and coroutine handling
  • New DTOs and API endpoint follow standard patterns but confirm JSON serialization mappings align with backend contract

Possibly related PRs

Suggested reviewers

  • dmp100

🐰 A post detail springs to life so bright,
With state-driven screens and Int types tight,
Comments nested, clean and neat,
Navigation refactored—what a sweet feat!

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title '[feat/#30] 게시물 상세페이지 API' clearly describes the main change: implementing the post detail page API feature, directly matching the PR's primary objective.
Description check ✅ Passed The PR description covers the required template sections: ISSUE (#30), WORK DESCRIPTION (module fixes, domain/data/presentation layer APIs), SCREENSHOT (video included), and TO REVIEWERS note. All major sections are present with appropriate content.
Linked Issues check ✅ Passed The PR successfully implements the 게시글 상세조회 API integration from issue #30 across all three architectural layers (domain, data, presentation), with necessary supporting changes to network configuration and navigation.
Out of Scope Changes check ✅ Passed All changes are within scope: API integration across domain/data/presentation layers, navigation parameter type updates for consistency, and OkHttpClient timeout configuration are all related to implementing the post detail API feature.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#30-post-detail

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.

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

♻️ Duplicate comments (1)
app/src/main/java/com/hsLink/hslink/domain/repository/community/CommunityRepository.kt (1)

15-15: Domain layer exposes data layer DTO.

This interface method returns CommunityDetailResponseDto, a data-layer type. As noted in the implementation review, this violates clean architecture by creating a dependency from domain to data layer.

🧹 Nitpick comments (4)
app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt (1)

29-31: Good addition to address timeout issues.

The timeout configuration correctly addresses the issue mentioned in the PR description. Setting all three timeouts to 30 seconds is reasonable for mobile applications with varying network conditions.

However, consider whether all API operations require the same timeout. For example:

  • Quick read operations (like fetching post details) might work well with shorter timeouts
  • File upload/download operations might need longer timeouts in the future

For now, this uniform configuration is acceptable, but you may want to implement per-endpoint timeouts if you encounter specific performance requirements later.

app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CommunityRepositoryImpl.kt (1)

36-44: Consider introducing a domain model instead of exposing data layer DTOs.

The repository implementation directly returns CommunityDetailResponseDto (a data-layer DTO) to the domain layer. This creates a dependency from the domain layer on the data layer, which violates clean architecture principles. Consider introducing a domain model (e.g., CommunityPostDetail) and mapping the DTO to this model within the repository implementation.

Note: The existing createCommunityPost method follows the same pattern, so this may be a broader architectural decision for the codebase.

app/src/main/java/com/hsLink/hslink/presentation/community/viewmodel/CommunityViewModel.kt (1)

58-69: Consider externalizing the error message.

The error message "게시물 상세 조회 실패" is hardcoded in Korean. For better maintainability and localization support, consider externalizing this string to a resource file.

app/src/main/java/com/hsLink/hslink/presentation/community/screen/post/CommunityPostScreen.kt (1)

17-18: Prefer lifecycle-aware state collection.

collectAsState() keeps collecting whenever the composable stays in the composition, even if the user navigates away but the destination is still on the back stack. Switching to collectAsStateWithLifecycle() lets the flow respect the Lifecycle state and stops extra work when the screen is not resumed.

Apply this diff:

-import androidx.compose.runtime.collectAsState
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 ...
-    val postDetailState by viewModel.postDetailState.collectAsState()
+    val postDetailState by viewModel.postDetailState.collectAsStateWithLifecycle()

Also applies to: 66-67

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99fd358 and cec9b3f.

📒 Files selected for processing (11)
  • app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/data/dto/response/community/CommunityDetailResponseDto.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CommunityRepositoryImpl.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/data/service/commuunity/CommunityPostService.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/domain/repository/community/CommunityRepository.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/community/navigation/post/CommunityPostNavigation.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/community/screen/post/CommunityPostScreen.kt (5 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/community/state/CommunityContract.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/community/viewmodel/CommunityViewModel.kt (3 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
app/src/main/java/com/hsLink/hslink/presentation/community/navigation/post/CommunityPostNavigation.kt (1)
app/src/main/java/com/hsLink/hslink/presentation/community/screen/post/CommunityPostScreen.kt (1)
  • CommunityPostRoute (54-74)
app/src/main/java/com/hsLink/hslink/presentation/community/screen/post/CommunityPostScreen.kt (7)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/presentation/community/component/post/PostHeader.kt (1)
  • PostHeader (15-52)
app/src/main/java/com/hsLink/hslink/presentation/community/component/post/PostContent.kt (1)
  • PostContent (13-37)
app/src/main/java/com/hsLink/hslink/presentation/community/component/post/EmptyComment.kt (1)
  • EmptyComment (15-38)
app/src/main/java/com/hsLink/hslink/presentation/community/component/main/CommentItem.kt (1)
  • CommentItem (24-79)
app/src/main/java/com/hsLink/hslink/presentation/community/component/post/CommentInput.kt (1)
  • CommentInput (23-79)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkDialog.kt (1)
  • HsLinkDialog (22-95)
🔇 Additional comments (5)
app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt (1)

18-18: LGTM!

The TimeUnit import is necessary for the timeout configurations below.

app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt (1)

68-70: LGTM! Type migration from String to Int is consistent.

The parameter type change from String to Int for postId aligns with the API layer changes and improves type safety.

app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt (1)

54-54: LGTM! Navigation wiring updated correctly.

The navigation callback now correctly passes postId as an Int, consistent with the updated navigateToCommunityPost signature.

app/src/main/java/com/hsLink/hslink/data/service/commuunity/CommunityPostService.kt (1)

26-29: LGTM! API endpoint properly defined.

The new endpoint for fetching post details is correctly configured with @Path parameter binding and appropriate return type.

app/src/main/java/com/hsLink/hslink/presentation/community/state/CommunityContract.kt (1)

15-19: LGTM! Well-structured state definition.

The CommunityDetailState sealed interface properly models the three states of post detail loading with appropriate data for each case.

Comment on lines +23 to 31
composable<CommunityPost> { backStackEntry ->

val postId = backStackEntry.arguments?.getInt("postId") ?: -1

CommunityPostRoute(
postId = postId,
paddingValues = padding,
navigateUp = navigateUp
)
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

🧩 Analysis chain

Verify error handling for invalid postId fallback.

The code extracts postId from backStackEntry.arguments with a fallback value of -1 (Line 25). If the navigation arguments are missing or malformed, -1 will be passed to CommunityPostRoute. Ensure that downstream code (particularly the API call and UI) properly handles this invalid ID case, or consider throwing an error/navigating back if the postId is missing.

Run the following script to check how -1 (or invalid postId) is handled in the detail flow:


🏁 Script executed:

#!/bin/bash
# Description: Check error handling for invalid postId values in the detail flow

# Search for postId validation or error handling in CommunityPostRoute and related components
ast-grep --pattern $'fun getPostDetail(postId: Int) {
  $$$
}'

# Search for API error handling when postId is invalid
rg -n -A 5 -B 2 "getCommunityDetail|getPostDetail" --type kotlin

Length of output: 6512


Let me verify how the invalid postId is handled in the UI and whether there are guards in place:

Now let me check the CommunityPostScreen to see if there's any UI handling for invalid postId:

Let me search for the actual CommunityPostScreen implementation to see the full picture:

Based on the script output and web research, I can now make a definitive assessment. The review comment raises a valid concern about error handling for invalid postId values. Let me verify the complete error handling flow by examining how the error state is handled in the screen:

Analysis Summary:

From the script execution results, I found:

  1. Navigation layer (CommunityPostNavigation.kt:25): Extracts postId with fallback value -1
  2. ViewModel (CommunityViewModel.kt:58-69): Calls repository.getCommunityDetail(postId) with .onFailure error handler
  3. Repository (CommunityRepositoryImpl.kt:36-43): Wraps API call in runCatching for exception handling
  4. API Service (CommunityPostService.kt:27): Makes HTTP GET request with postId as path parameter
  5. Screen (CommunityPostScreen.kt:63): Calls viewModel.getPostDetail(postId) in LaunchedEffect(postId)

Current State: Error handling exists downstream (ViewModel's .onFailure handler), but there is no early validation for the invalid -1 postId before making the API call. Kotlin best practices recommend throwing exceptions for logic errors with conditions like invalid IDs, and Jetpack Compose documentation notes that providing default values and covering edge cases makes code safer.

Issues identified:

  • API call is made with -1 (wasteful network request for known invalid ID)
  • No explicit validation guard in CommunityPostScreen using LaunchedEffect condition
  • Missing early error check before ViewModel invocation

Recommendation: Add validation for postId != -1 before calling viewModel.getPostDetail() to prevent unnecessary API calls and provide faster user feedback.


Add postId validation in CommunityPostScreen before API call.

Include a guard condition in LaunchedEffect to check if postId is valid (-1 indicates missing argument), and navigate back or show error immediately without making an unnecessary API request:

LaunchedEffect(postId) {
    if (postId == -1) {
        navigateUp()  // or show error UI
        return@LaunchedEffect
    }
    viewModel.getPostDetail(postId)
}

This prevents wasteful API calls and provides faster error feedback when navigation arguments are missing.

🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/community/navigation/post/CommunityPostNavigation.kt
lines 23-31 the navigation extracts postId with a fallback of -1 but downstream
screens immediately call the ViewModel which triggers unnecessary API calls for
an invalid ID; update the CommunityPostScreen to validate postId before invoking
viewModel.getPostDetail by adding a guard in the LaunchedEffect (if postId == -1
then call navigateUp() or set an error state and return) so you avoid wasteful
network requests and provide immediate user feedback.

@vvan2 vvan2 merged commit cf1faba into develop Nov 12, 2025
1 check passed
@vvan2 vvan2 deleted the feat/#30-post-detail branch November 12, 2025 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature ✨ 기능 구현 주완🐹 주완 전용 라벨

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 게시글 상세조회 API

2 participants