Skip to content

Conversation

@HyunwooKiim
Copy link
Member

@HyunwooKiim HyunwooKiim commented Nov 13, 2025

aripick

Summary by CodeRabbit

  • New Features

    • Full proposal API: create, view, update status, delete
    • Like/unlike proposals with live like counts
    • List proposals with status filtering and pagination
    • Proposal statistics: total / approved / pending / rejected
    • Request validation on proposal creation
  • Bug Fixes / UX

    • Improved error responses for missing proposals and like-related actions
  • Documentation

    • Added REST API spec and architecture/coding guidelines
  • Chores

    • Ignore macOS .DS_Store files in repository

@HyunwooKiim HyunwooKiim linked an issue Nov 13, 2025 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Nov 13, 2025

Walkthrough

Adds a proposal feature: new JPA entities (Proposal, ProposalLike, VO, status enum), domain and persistence repositories (including QueryDSL-backed impl), services (command/query), REST controllers, DTOs, mappers, domain exceptions, API spec and architecture docs, and a .gitignore entry.

Changes

Cohort / File(s) Summary
Documentation & Config
\.gitignore, ari_pick_api_spec.md, prompt.md
Added .DS_Store ignore; new AriPick API specification; new architectural and coding guideline document.
Domain Entities & VO
src/main/kotlin/.../domain/proposal/Proposal.kt, .../ProposalLike.kt, .../type/ProposalStatus.kt, .../vo/ProposalInfo.kt
New JPA entities: Proposal (embedded VO, status, like count, soft-delete, domain mutators) and ProposalLike; ProposalStatus enum and embeddable ProposalInfo.
DTOs (requests/responses)
src/main/kotlin/.../application/dto/request/ProposalCreateRequest.kt, ProposalStatusUpdateRequest.kt
src/main/kotlin/.../application/dto/response/ProposalResponse.kt, ProposalListResponse.kt, ProposalLikeResponse.kt, ProposalStatsResponse.kt
Added request DTOs with validation and response DTOs for detail, list/pagination, likes, and statistics.
Exceptions
src/main/kotlin/.../application/exception/ProposalNotFoundException.kt, ProposalAlreadyLikedException.kt, ProposalLikeNotFoundException.kt
New domain exceptions extending BusinessBaseException, mapped to new error codes.
Mapper
src/main/kotlin/.../application/mapper/ProposalMapper.kt
New mapper converting between request DTOs, domain entities, and response DTOs.
Services
src/main/kotlin/.../application/service/ProposalCommandService.kt, ProposalQueryService.kt
New command service (create, update status, delete, like/unlike) with transactions and validations; new query service (detail, paginated list, stats).
Controllers
src/main/kotlin/.../presentation/ProposalCommandController.kt, ProposalQueryController.kt
New REST controllers exposing endpoints for proposal CRUD, likes, listing, and stats.
Domain Repositories (interfaces)
src/main/kotlin/.../repository/ProposalRepository.kt, ProposalLikeRepository.kt
New domain-level repository interfaces for proposals and proposal-likes.
Persistence Repositories & Implementations
src/main/kotlin/.../repository/ProposalPersistenceRepository.kt, ProposalLikePersistenceRepository.kt, ProposalRepositoryImpl.kt, ProposalLikeRepositoryImpl.kt
Added Spring Data JPA persistence interfaces and implementations; ProposalRepositoryImpl uses JPAQueryFactory/QueryDSL for filtered pagination and counts; like-repo impl delegates to persistence repo.
Global Errors
src/main/kotlin/.../global/error/ErrorMessage.kt
Added PROPOSAL_NOT_FOUND, PROPOSAL_ALREADY_LIKED, PROPOSAL_LIKE_NOT_FOUND error codes/messages.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant CmdCtrl as ProposalCommandController
    participant CmdSvc as ProposalCommandService
    participant Mapper as ProposalMapper
    participant Repo as ProposalRepository
    participant DB as Database

    Note right of CmdCtrl: Create proposal
    Client->>CmdCtrl: POST /api/proposals
    CmdCtrl->>CmdSvc: createProposal(request)
    CmdSvc->>Mapper: toEntity(request)
    Mapper-->>CmdSvc: Proposal
    CmdSvc->>Repo: save(proposal)
    Repo->>DB: INSERT proposal
    DB-->>Repo: persisted Proposal
    Repo-->>CmdSvc: Proposal
    CmdSvc->>Mapper: toProposalResponse(proposal)
    Mapper-->>CmdSvc: ProposalResponse
    CmdSvc-->>CmdCtrl: ProposalResponse
    CmdCtrl-->>Client: 201 Created + body
Loading
sequenceDiagram
    participant Client
    participant CmdCtrl as ProposalCommandController
    participant CmdSvc as ProposalCommandService
    participant LikeRepo as ProposalLikeRepository
    participant Repo as ProposalRepository
    participant DB as Database
    participant Err as Error

    Note right of CmdCtrl: Like flow
    Client->>CmdCtrl: POST /api/proposals/{id}/like?userId=X
    CmdCtrl->>CmdSvc: likeProposal(id, userId)
    CmdSvc->>Repo: findById(id)
    Repo->>DB: SELECT proposal
    DB-->>Repo: Proposal
    Repo-->>CmdSvc: Proposal
    CmdSvc->>LikeRepo: existsByProposalIdAndUserId(id,userId)
    alt already liked
        CmdSvc->>Err: throw ProposalAlreadyLikedException
        Err-->>Client: 409 Conflict
    else not liked
        CmdSvc->>LikeRepo: save(ProposalLike)
        LikeRepo->>DB: INSERT proposal_like
        DB-->>LikeRepo: persisted Like
        CmdSvc->>Repo: save(proposal.incrementLikeCount())
        Repo->>DB: UPDATE proposal.like_count
        DB-->>Repo: Proposal
        Repo-->>CmdSvc: Proposal
        CmdSvc-->>CmdCtrl: ProposalLikeResponse(liked=true, likeCount=N)
        CmdCtrl-->>Client: 200 OK + body
    end
Loading
sequenceDiagram
    participant Client
    participant QueryCtrl as ProposalQueryController
    participant QuerySvc as ProposalQueryService
    participant Repo as ProposalRepository
    participant Mapper as ProposalMapper
    participant DB as Database

    Note right of QueryCtrl: List / pagination
    Client->>QueryCtrl: GET /api/proposals?status=&page=&size=
    QueryCtrl->>QuerySvc: queryProposals(status,page,size)
    QuerySvc->>Repo: findAll(status, pageable)
    Repo->>DB: SELECT ... WHERE is_deleted=false [AND status=?] ORDER BY created_at DESC LIMIT/OFFSET
    DB-->>Repo: rows + total
    Repo-->>QuerySvc: Page<Proposal>
    loop map results
        QuerySvc->>Mapper: toProposalResponse(proposal)
        Mapper-->>QuerySvc: ProposalResponse
    end
    QuerySvc-->>QueryCtrl: ProposalListResponse
    QueryCtrl-->>Client: 200 OK + body
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review areas:
    • Proposal entity: optimistic locking, like count guards, soft-delete semantics.
    • ProposalRepositoryImpl: QueryDSL predicates, pagination and total-count correctness.
    • ProposalCommandService: transactional boundaries and potential race conditions in like/unlike flows (exists -> save/delete).
    • Controllers: parameter binding, validation, and response status mappings.
    • New ErrorMessage entries: ensure mapping aligns with BusinessBaseException handling.

Poem

🐇 I nibble code and hop with glee,
New proposals sprout for all to see,
Likes that bloom and statuses that change,
Queries fetch across the range,
Hooray — the garden grew today! 🌿

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
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.
Title check ❓ Inconclusive The title 'Ari pick' is vague and lacks specific information about the changeset. It does not clearly convey what the PR implements or changes. Use a descriptive title that summarizes the main change, such as 'Implement proposal domain feature with CQRS patterns' or 'Add AriPick proposal API endpoints and domain logic'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ 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 ari-pick

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

🧹 Nitpick comments (3)
ari_pick_api_spec.md (1)

13-19: Add language specifiers to all fenced code blocks.

All JSON code blocks throughout this specification should include the json language identifier for proper syntax highlighting and tooling support.

Apply this pattern to all code blocks:

-```
+```json
 {
   "title": "상품명",
   ...
 }

</blockquote></details>
<details>
<summary>prompt.md (1)</summary><blockquote>

`22-44`: **Consider adding language specifiers for clarity.**

The directory structure diagrams could specify `text` as the language identifier for consistency with documentation best practices.

</blockquote></details>
<details>
<summary>src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalLikeRepository.kt (1)</summary><blockquote>

`12-12`: **Consider returning a Boolean or count from the delete method.**

The `deleteByProposalIdAndUserId` method returns `Unit`, which means callers cannot distinguish between successful deletion and the case where the like didn't exist. This could complicate unlike flows where you need to verify the operation succeeded.



Consider one of these alternatives:

```diff
-    fun deleteByProposalIdAndUserId(proposalId: Long, userId: Long)
+    fun deleteByProposalIdAndUserId(proposalId: Long, userId: Long): Int  // returns count of deleted rows

Or check existence before deleting in the service layer.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb806f3 and dbcc400.

📒 Files selected for processing (28)
  • .gitignore (1 hunks)
  • ari_pick_api_spec.md (1 hunks)
  • prompt.md (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/request/ProposalCreateRequest.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/request/ProposalStatusUpdateRequest.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalLikeResponse.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalListResponse.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalResponse.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalStatsResponse.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/exception/ProposalAlreadyLikedException.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/exception/ProposalLikeNotFoundException.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/exception/ProposalNotFoundException.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/mapper/ProposalMapper.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/service/ProposalCommandService.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/service/ProposalQueryService.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/Proposal.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/ProposalLike.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/type/ProposalStatus.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/vo/ProposalInfo.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/presentation/ProposalCommandController.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/presentation/ProposalQueryController.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalLikePersistenceRepository.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalLikeRepository.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalLikeRepositoryImpl.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalPersistenceRepository.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalRepository.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalRepositoryImpl.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/global/error/ErrorMessage.kt (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
prompt.md

22-22: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


53-53: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (20)
src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/type/ProposalStatus.kt (1)

3-7: LGTM!

The enum correctly models the proposal lifecycle states and follows the architectural guidelines for domain types.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/request/ProposalStatusUpdateRequest.kt (1)

5-7: LGTM!

The request DTO is correctly structured with immutable property and follows the naming conventions outlined in the guidelines.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalStatsResponse.kt (1)

3-8: LGTM!

The response DTO correctly uses Long for count fields, preventing potential overflow issues with large datasets, and follows architectural conventions.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/exception/ProposalNotFoundException.kt (1)

6-6: LGTM!

The exception correctly extends BusinessBaseException and leverages the centralized error message system, following the architectural guidelines.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/exception/ProposalAlreadyLikedException.kt (1)

6-6: LGTM!

The exception correctly extends BusinessBaseException and maintains consistency with the error handling patterns established in the codebase.

src/main/kotlin/bssm/devcoop/occount/global/error/ErrorMessage.kt (1)

16-19: LGTM!

The new proposal-related error codes follow the existing pattern with appropriate HTTP status codes and clear Korean messages.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/vo/ProposalInfo.kt (1)

6-16: LGTM!

The embeddable value object is well-structured with appropriate JPA annotations. The reason field correctly uses columnDefinition = "TEXT" for potentially longer content.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/mapper/ProposalMapper.kt (1)

8-30: LGTM!

The mapper functions correctly transform between request/response DTOs and domain entities, properly using the ProposalInfo value object for embedded fields.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/exception/ProposalLikeNotFoundException.kt (1)

6-6: LGTM!

The exception class follows the existing pattern and properly integrates with the error handling framework.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalPersistenceRepository.kt (1)

7-8: LGTM!

Standard Spring Data JPA repository interface correctly configured for the Proposal entity.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalListResponse.kt (1)

3-8: LGTM!

The paginated response structure includes all necessary fields for proper pagination support.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalResponse.kt (1)

6-14: LGTM!

The DTO is well-structured with appropriate immutable fields for a response object.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/service/ProposalQueryService.kt (1)

40-52: LGTM!

The statistics aggregation is straightforward and correct.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalLikeRepositoryImpl.kt (1)

7-26: LGTM!

Clean delegation pattern that properly separates the domain repository interface from the persistence layer.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/presentation/ProposalQueryController.kt (1)

22-26: LGTM!

The endpoint implementations are clean and correctly delegate to the service layer.

Also applies to: 38-42

src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalRepositoryImpl.kt (2)

26-49: LGTM! QueryDSL pagination implemented correctly.

The findAll method properly:

  • Filters non-deleted proposals
  • Applies optional status filtering
  • Orders by createdAt descending
  • Paginates with offset/limit
  • Uses a separate count query with matching filters

55-72: LGTM!

Count methods correctly filter non-deleted proposals and provide safe defaults for null results.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalLikePersistenceRepository.kt (1)

8-14: LGTM!

Spring Data JPA repository correctly defines query methods following naming conventions. Methods will be auto-implemented by Spring Data.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/service/ProposalCommandService.kt (1)

24-28: Mapper-based create flow looks good.

Keeping the conversion logic in ProposalMapper keeps this service thin and focused on orchestration.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/presentation/ProposalCommandController.kt (1)

25-44: HTTP status mapping is on point.

The 201/200/204 responses match the semantics of each mutation, which will simplify client handling.

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

♻️ Duplicate comments (3)
src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/request/ProposalCreateRequest.kt (1)

6-14: Consider tightening string validation with @NotBlank.

You’ve added length and ID validation, which addresses the earlier concern. If you also want to reject whitespace‑only input (e.g. " "), consider:

import jakarta.validation.constraints.NotBlank

data class ProposalCreateRequest(
    @field:NotBlank(message = "제목은 비어 있을 수 없습니다")
    @field:Size(min = 1, max = 100, message = "제목은 1자 이상 100자 이하여야 합니다")
    val title: String,

    @field:NotBlank(message = "제안 이유는 비어 있을 수 없습니다")
    @field:Size(min = 1, max = 1000, message = "제안 이유는 1자 이상 1000자 이하여야 합니다")
    val reason: String,

    @field:Positive(message = "작성자 ID는 양수여야 합니다")
    val writerId: Long
)

If whitespace‑only values are acceptable in your domain, the current version is fine.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/presentation/ProposalQueryController.kt (1)

19-40: Pagination and validation setup look good.

Class‑level @Validated plus @Min/@Max on page and size address the earlier risk of invalid pagination hitting PageRequest.of. Status is nullable and optional as expected; controller cleanly delegates to the query service.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/Proposal.kt (1)

17-73: Entity design and optimistic locking for likes look solid.

  • likeCount: Long with incrementLikeCount / guarded decrementLikeCount is safe against underflow and consistent with response DTOs.
  • Adding @Version gives you optimistic locking, so concurrent like/unlike operations will no longer silently lose updates.

As a follow‑up, make sure service code that performs like/unlike either:

  • lets OptimisticLockException bubble to a well‑defined 4xx/5xx error, or
  • wraps these operations in a simple retry strategy,

so clients get a clear signal rather than an opaque failure if concurrent updates collide.

🧹 Nitpick comments (2)
src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalRepositoryImpl.kt (1)

26-68: QueryDSL pagination and counts are correct; consider minor DRY helper.

findAll correctly filters on isDeleted = false and optional status, orders by createdAt DESC, and uses matching predicates for the count query. countByStatus/countAll follow the same rules, so paging and totals should stay in sync.

If you want to reduce duplication, you could extract a small helper to build the base predicate (e.g. isDeleted = false + optional status) and reuse it across findAll, countByStatus, and countAll, but the current version is already clear.

src/main/kotlin/bssm/devcoop/occount/domain/proposal/presentation/ProposalCommandController.kt (1)

47-63: Validate userId and consider deriving it from authentication context.

Functionally the like/unlike endpoints are wired correctly, but two improvements are worth considering:

  1. Input validation for userId

To align with writerId validation on the request side and avoid negative/zero IDs:

import jakarta.validation.constraints.Positive

@PostMapping("/{proposalId}/like")
@ResponseStatus(HttpStatus.OK)
fun likeProposal(
    @PathVariable proposalId: Long,
    @RequestParam @Positive userId: Long,
): ProposalLikeResponse { ... }

@DeleteMapping("/{proposalId}/like")
@ResponseStatus(HttpStatus.OK)
fun unlikeProposal(
    @PathVariable proposalId: Long,
    @RequestParam @Positive userId: Long,
): ProposalLikeResponse { ... }
  1. Authorization / identity spoofing

If your system uses (or will use) authentication, consider resolving the acting user from the security context (e.g., @AuthenticationPrincipal) instead of accepting a userId query parameter. Allowing clients to supply arbitrary user IDs makes it easy to like/unlike on behalf of other users unless the service layer enforces additional checks.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78e191d and 631139f.

📒 Files selected for processing (10)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/request/ProposalCreateRequest.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/request/ProposalStatusUpdateRequest.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalLikeResponse.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalResponse.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/Proposal.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/ProposalLike.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/presentation/ProposalCommandController.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/presentation/ProposalQueryController.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalRepository.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalRepositoryImpl.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/request/ProposalStatusUpdateRequest.kt
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalResponse.kt
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/repository/ProposalRepository.kt
  • src/main/kotlin/bssm/devcoop/occount/domain/proposal/domain/ProposalLike.kt
🔇 Additional comments (1)
src/main/kotlin/bssm/devcoop/occount/domain/proposal/application/dto/response/ProposalLikeResponse.kt (1)

3-6: DTO looks consistent and future‑proof.

Using Long for likeCount aligns with stats DTOs and avoids overflow; immutable structure is appropriate for responses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

아리픽

2 participants