Skip to content

Comments

[WTH-149] account 도메인 코틀린 마이그레이션#12

Merged
soo0711 merged 26 commits intodevfrom
refactor/WTH-149-account-도메인-코틀린-마이그레이션
Feb 20, 2026

Hidden character warning

The head ref may contain hidden characters: "refactor/WTH-149-account-\ub3c4\uba54\uc778-\ucf54\ud2c0\ub9b0-\ub9c8\uc774\uadf8\ub808\uc774\uc158"
Merged

[WTH-149] account 도메인 코틀린 마이그레이션#12
soo0711 merged 26 commits intodevfrom
refactor/WTH-149-account-도메인-코틀린-마이그레이션

Conversation

@soo0711
Copy link
Collaborator

@soo0711 soo0711 commented Feb 19, 2026

📌 Summary

어떤 작업인지 한 줄 요약해 주세요.

Account 도메인 Java → Kotlin 마이그레이션 및 아키텍처 구조 변경

📝 Changes

변경사항을 what, why, how로 구분해 작성해 주세요.

What

  • Account 도메인 전체 Java → Kotlin 전환
  • UseCase command/query 분리
  • Money VO 추가
  • N+1 쿼리 개선

Why

  • 영수증 10개 기준 기존 12쿼리 → 3쿼리로 N + 1 개선

How

  • Entity 비즈니스 메서드에 require / check로 유효성 검사 내재화
  • ReceiptMapper.toResponses(receipts, filesByReceiptId) 배치 매핑으로 N+1 제거
  • UseCase는 Repository 직접 호출

📸 Screenshots / Logs

필요시 스크린샷 or 로그를 첨부해주세요.

💡 Reviewer 참고사항

리뷰에 참고할 내용을 작성해주세요.

  • N+1 개선은 GetAccountQueryServiceTest에서 fileReader.findAll 1회 호출 verify로 검증합니다.

✅ Checklist

  • PR 제목 설정 완료 (WTH-123 인증 필터 설정)
  • 테스트 구현 완료
  • 리뷰어 등록 완료
  • 자체 코드 리뷰 완료

Summary by CodeRabbit

  • New Features

    • 회비 조회 및 관리 API가 개편되어 계정 조회, 회비 생성(관리자), 영수증 등록/수정/삭제(관리자) 엔드포인트와 파일 첨부 처리 및 응답 포맷이 제공됩니다.
  • Refactor

    • 계정·영수증 관련 API 계약과 도메인 모델이 Kotlin으로 재구성되어 응답/요청 스키마, 검증 규칙과 오류 코드가 정비되었습니다.
  • Tests

    • 계정 및 영수증의 생성·조회·수정·삭제 흐름과 경계 조건을 검증하는 단위/통합 테스트가 대폭 추가되었습니다.

@soo0711 soo0711 requested a review from hyxklee February 19, 2026 09:40
@soo0711 soo0711 self-assigned this Feb 19, 2026
@soo0711 soo0711 added the 🔨 Refactor 코드 구조 개선 및 리팩토링 label Feb 19, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Account/Receipt 모듈을 Java에서 Kotlin으로 전면 마이그레이션하고 CQRS 기반으로 Use Case를 재구성했으며, 엔티티·DTO·매퍼·레포지토리·서비스·컨트롤러·테스트가 Java → Kotlin으로 교체되었습니다. (50단어 이내)

Changes

Cohort / File(s) Summary
Java API 제거
src/main/java/com/weeth/domain/account/application/dto/AccountDTO.java, src/main/java/com/weeth/domain/account/application/dto/ReceiptDTO.java, src/main/java/com/weeth/domain/account/application/exception/AccountExistsException.java, src/main/java/com/weeth/domain/account/application/exception/AccountNotFoundException.java, src/main/java/com/weeth/domain/account/application/exception/ReceiptNotFoundException.java, src/main/java/com/weeth/domain/account/application/mapper/AccountMapper.java, src/main/java/com/weeth/domain/account/application/mapper/ReceiptMapper.java, src/main/java/com/weeth/domain/account/application/usecase/..., src/main/java/com/weeth/domain/account/domain/entity/Account.java, src/main/java/com/weeth/domain/account/domain/entity/Receipt.java, src/main/java/com/weeth/domain/account/domain/repository/..., src/main/java/com/weeth/domain/account/domain/service/..., src/main/java/com/weeth/domain/account/presentation/...
기존 Java 기반 DTO, 예외, 매퍼, UseCase 인터페이스/구현, 엔티티, 레포지토리, 도메인 서비스, 컨트롤러, 응답 코드 enum 및 관련 파일들이 삭제됨.
Kotlin DTO/Response/Request 추가
src/main/kotlin/com/weeth/domain/account/application/dto/request/AccountSaveRequest.kt, src/main/kotlin/com/weeth/domain/account/application/dto/request/ReceiptSaveRequest.kt, src/main/kotlin/com/weeth/domain/account/application/dto/request/ReceiptUpdateRequest.kt, src/main/kotlin/com/weeth/domain/account/application/dto/response/AccountResponse.kt, src/main/kotlin/com/weeth/domain/account/application/dto/response/ReceiptResponse.kt
Kotlin 데이터 클래스 기반 요청/응답 DTO 추가. Swagger 스키마와 Jakarta Validation 어노테이션 포함.
Kotlin 예외·에러 코드 추가
src/main/kotlin/com/weeth/domain/account/application/exception/AccountErrorCode.kt, .../AccountExistsException.kt, .../AccountNotFoundException.kt, .../ReceiptNotFoundException.kt, .../ReceiptAccountMismatchException.kt
Kotlin enum/예외로 에러 코드와 세부 예외 타입 재도입(값 및 HttpStatus 포함).
Kotlin 매퍼 추가
src/main/kotlin/com/weeth/domain/account/application/mapper/AccountMapper.kt, src/main/kotlin/com/weeth/domain/account/application/mapper/ReceiptMapper.kt
MapStruct(Java) 제거 후 Kotlin @Component 매퍼로 대체, toResponse/toResponses 등 매핑 메서드 구현.
CQRS Use Case 재구성 (Kotlin)
src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCase.kt, .../command/ManageReceiptUseCase.kt, src/main/kotlin/com/weeth/domain/account/application/usecase/query/GetAccountQueryService.kt
명령(관리)과 조회(쿼리)로 분리된 Use Case 추가. 트랜잭션·비즈니스 로직(회비 생성, 영수증 저장/수정/삭제, 조회 흐름) 구현.
Kotlin 엔티티·VO 추가
src/main/kotlin/com/weeth/domain/account/domain/entity/Account.kt, src/main/kotlin/com/weeth/domain/account/domain/entity/Receipt.kt, src/main/kotlin/com/weeth/domain/account/domain/vo/Money.kt
Kotlin JPA 엔티티 재작성 및 Money 값 객체 도입(검증 및 연산).
Kotlin 레포지토리 추가
src/main/kotlin/com/weeth/domain/account/domain/repository/AccountRepository.kt, src/main/kotlin/com/weeth/domain/account/domain/repository/ReceiptRepository.kt
Spring Data JPA 레포지토리 Kotlin 인터페이스 추가(기존 쿼리 메서드 유지).
Kotlin 컨트롤러·응답 코드 추가
src/main/kotlin/com/weeth/domain/account/presentation/AccountAdminController.kt, .../AccountController.kt, .../ReceiptAdminController.kt, .../AccountResponseCode.kt
REST 엔드포인트 Kotlin 컨트롤러 재구성 및 응답 코드 enum 추가.
테스트 변경
src/test/kotlin/com/weeth/domain/account/application/usecase/ReceiptUseCaseImplTest.kt (삭제), src/test/kotlin/.../ManageAccountUseCaseTest.kt, .../ManageReceiptUseCaseTest.kt, .../GetAccountQueryServiceTest.kt, .../AccountTest.kt, .../ReceiptTest.kt, .../fixture/*
레거시 Java/Kotlin 테스트 일부 제거 및 Kotlin 기반 유닛/서비스/도메인 테스트와 테스트 픽스처 추가. 주요 흐름(저장/수정/삭제/조회) 검증 테스트 포함.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant AccountController
    participant GetAccountQueryService
    participant AccountRepository
    participant ReceiptRepository
    participant FileReader
    participant ReceiptMapper
    participant AccountMapper

    Client->>AccountController: GET /api/v1/account/{cardinal}
    AccountController->>GetAccountQueryService: findByCardinal(cardinal)
    GetAccountQueryService->>AccountRepository: findByCardinal(cardinal)
    AccountRepository-->>GetAccountQueryService: Account?
    GetAccountQueryService->>ReceiptRepository: findAllByAccountIdOrderByCreatedAtDesc(accountId)
    ReceiptRepository-->>GetAccountQueryService: List<Receipt>
    GetAccountQueryService->>FileReader: findAll(receiptIds)
    FileReader-->>GetAccountQueryService: Map<Long, List<FileResponse>>
    GetAccountQueryService->>ReceiptMapper: toResponses(receipts, filesByReceiptId)
    ReceiptMapper-->>GetAccountQueryService: List<ReceiptResponse>
    GetAccountQueryService->>AccountMapper: toResponse(account, receipts)
    AccountMapper-->>GetAccountQueryService: AccountResponse
    GetAccountQueryService-->>AccountController: AccountResponse
    AccountController-->>Client: CommonResponse<AccountResponse>
Loading
sequenceDiagram
    actor Admin
    participant ReceiptAdminController
    participant ManageReceiptUseCase
    participant CardinalGetService
    participant AccountRepository
    participant ReceiptRepository
    participant FileRepository
    participant FileMapper

    Admin->>ReceiptAdminController: POST /api/v1/admin/receipts
    ReceiptAdminController->>ManageReceiptUseCase: save(request)
    ManageReceiptUseCase->>CardinalGetService: findByAdminSide(cardinal)
    CardinalGetService-->>ManageReceiptUseCase: verified
    ManageReceiptUseCase->>AccountRepository: findByCardinal(cardinal)
    AccountRepository-->>ManageReceiptUseCase: Account
    ManageReceiptUseCase->>ReceiptRepository: save(Receipt)
    ReceiptRepository-->>ManageReceiptUseCase: saved Receipt
    ManageReceiptUseCase->>FileMapper: toEntities(files)
    FileMapper-->>ManageReceiptUseCase: List<File>
    ManageReceiptUseCase->>FileRepository: saveAll(files)
    FileRepository-->>ManageReceiptUseCase: saved files
    ManageReceiptUseCase-->>ReceiptAdminController: void
    ReceiptAdminController-->>Admin: CommonResponse<Void> (RECEIPT_SAVE_SUCCESS)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • hyxklee
  • JIN921
  • dalzzy

Poem

🐰 자바 굴을 빠져나와 코틀린 들판으로,
Money를 굴려 숫자 춤을 추네.
CQRS 깃발 아래 명령과 조회가 갈라지고,
영수증은 정렬되고 파일은 정제되네.
훌쩍뛰어온 토끼가 박수 보낸다, 바뀐 코드에 🥕✨

🚥 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
Title check ✅ Passed PR 제목은 Account 도메인의 Kotlin 마이그레이션이라는 주요 변경사항을 명확하게 설명하고 있으며, 변경집합의 핵심을 잘 반영합니다.
Description check ✅ Passed PR 설명은 제공된 템플릿의 모든 필수 섹션(Summary, Changes, Checklist)을 포함하고 있으며, 변경사항을 What/Why/How로 구조적으로 설명합니다.

✏️ 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 refactor/WTH-149-account-도메인-코틀린-마이그레이션

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

🧹 Nitpick comments (3)
src/main/kotlin/com/weeth/domain/account/domain/entity/Receipt.kt (1)

24-25: amount 필드가 var로 선언되어 유효성 검사를 우회할 수 있습니다.

create()update() 메서드에서 amount > 0 검증을 수행하지만, var로 선언된 필드는 직접 접근으로 유효성 검사를 우회할 수 있습니다 (예: receipt.amount = -5).

필드를 private set으로 변경하거나 custom setter에 검증 로직을 추가하는 것을 권장합니다.

♻️ private set 적용 제안
 `@Column`(nullable = false)
-var amount: Int,
+var amount: Int
+    private set,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/com/weeth/domain/account/domain/entity/Receipt.kt` around
lines 24 - 25, The amount field on Receipt is currently var and can be mutated
to invalid values; change its declaration to "var amount: Int private set" or
add a custom setter on amount that enforces amount > 0 and throws an
IllegalArgumentException on invalid values, and ensure the existing create() and
update() methods use the same validation (or rely on the new setter) so direct
property assignment (e.g., receipt.amount = -5) cannot bypass validation.
src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCaseTest.kt (1)

34-45: 정상 저장 테스트에서 cardinalGetService.findByAdminSide() 호출 검증 추가를 권장합니다.

현재 테스트는 accountRepository.save() 호출만 검증합니다. cardinalGetService.findByAdminSide()가 호출되었는지도 검증하면 UseCase의 전체 흐름을 더 완전하게 테스트할 수 있습니다.

♻️ 검증 추가 제안
                     useCase.save(dto)

                     verify(exactly = 1) { accountRepository.save(any()) }
+                    verify(exactly = 1) { cardinalGetService.findByAdminSide(40) }
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCaseTest.kt`
around lines 34 - 45, Add verification that
cardinalGetService.findByAdminSide(...) is invoked in the success save test:
after calling useCase.save(dto) add a verify call to assert
cardinalGetService.findByAdminSide(40) was called (e.g., exactly once). This
ensures the ManageAccountUseCaseTest checks both repository save and the
dependency call to cardinalGetService.findByAdminSide in the useCase.save path.
src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCase.kt (1)

16-21: cardinalGetService.findByAdminSide() 반환값이 사용되지 않습니다.

Line 19에서 findByAdminSide() 호출 결과가 무시됩니다. 이것이 유효하지 않은 cardinal에 대해 예외를 던지는 검증 목적이라면, 의도를 명확히 하기 위해 명시적으로 문서화하거나 also { } 블록 대신 검증 전용 메서드 사용을 권장합니다.

♻️ 의도 명확화 제안
     `@Transactional`
     fun save(request: AccountSaveRequest) {
         if (accountRepository.existsByCardinal(request.cardinal)) throw AccountExistsException()
-        cardinalGetService.findByAdminSide(request.cardinal)
+        cardinalGetService.findByAdminSide(request.cardinal) // 존재하지 않으면 예외 발생
         accountRepository.save(Account.create(request.description, request.totalAmount, request.cardinal))
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCase.kt`
around lines 16 - 21, ManageAccountUseCase.save에서
cardinalGetService.findByAdminSide(request.cardinal)의 반환값을 사용하지 않고 있어(현재 호출만 함)
검증 의도가 불명확합니다; findByAdminSide의 반환 엔티티를 실제로 사용하려면 저장 로직에서 활용하거나, 검증 전용 의도를 명확히
하기 위해 cardinalGetService에 findByAdminSide를 대체할 validate/ensure 메서드(예:
ensureExistsAdminSide 또는 validateAdminSideCardinal) 를 추가해
ManageAccountUseCase.save에서 해당 검증 메서드를 호출하도록 변경하세요; 또는 호출 결과를 val으로 받아 널 여부를
체크하여 예외를 던지도록(또는 주석/문서로 의도를 명시) 수정해 불필요한 무시를 제거하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageReceiptUseCase.kt`:
- Around line 46-49: Ensure the receipt belongs to the same account before
adjusting balances: after calling
cardinalGetService.findByAdminSide(request.cardinal) and fetching account via
accountRepository.findByCardinal(request.cardinal) and receipt via
receiptRepository.findByIdOrNull(receiptId), compare receipt.account.id with
account.id; if they differ, throw an appropriate exception (e.g.,
UnauthorizedOperationException or a domain-specific exception) instead of
calling account.adjustSpend(Money.of(receipt.amount), Money.of(request.amount));
keep existing AccountNotFoundException and ReceiptNotFoundException behavior but
add this ownership check to protect against adjusting the wrong account.
- Around line 50-53: The update currently treats null and empty request.files
the same, so an explicit empty list (user intent to delete all files) is
ignored; in ManageReceiptUseCase adjust the branch that reads request.files so
that if request.files is null you skip any file changes, but if request.files is
an empty list you perform deletion via
fileRepository.deleteAll(fileReader.findAll(FileOwnerType.RECEIPT, receiptId,
null)) and do not call saveAll, and if non-empty call
fileRepository.saveAll(fileMapper.toFileList(request.files,
FileOwnerType.RECEIPT, receiptId)); also review the asymmetric behavior between
update() and save() paths and add/adjust tests to cover the empty-list deletion
case.

In `@src/main/kotlin/com/weeth/domain/account/domain/entity/Account.kt`:
- Around line 38-44: adjustSpend currently calls cancelSpend(oldAmount) then
spend(newAmount) which can leave currentAmount in an inconsistent partial state
if spend throws; instead compute the net result first and perform validation
before mutating state so the update is atomic. In practice, calculate net =
currentAmount - oldAmount + newAmount (or a helper like computeAdjustedAmount),
validate that net is allowed, then assign currentAmount = net (or call a single
mutator method that applies the delta once); ensure any checks
(limits/constraints) run before changing currentAmount and remove the two-step
cancelSpend/spend sequence to avoid partial updates.

In `@src/main/kotlin/com/weeth/domain/account/presentation/AccountController.kt`:
- Around line 23-27: The find endpoint is missing the authenticated user
injection; update AccountController.find to accept a userId parameter annotated
with `@Parameter`(hidden = true) and `@CurrentUser` (e.g., fun
find(`@Parameter`(hidden = true) `@CurrentUser` userId: Long, `@PathVariable`
cardinal: Int): CommonResponse<AccountResponse>), then forward that userId to
getAccountQueryService (adjust getAccountQueryService.findByCardinal to accept
userId as first arg) so the service can perform ownership/authorization checks
before returning the AccountResponse; ensure method signatures for
AccountController.find and getAccountQueryService.findByCardinal are updated
consistently and any callers/tests adjusted accordingly.

In
`@src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageReceiptUseCaseTest.kt`:
- Around line 45-47: beforeTest의 clearMocks 호출에 fileRepository가 빠져 있어 mock 호출
누적으로 인해 verify(exactly = 1) 등이 불안정해질 수 있습니다; beforeTest 블록에서
clearMocks(receiptRepository, accountRepository, fileReader, cardinalGetService,
fileMapper) 호출에 fileRepository를 추가하여 모든 관련 mock을 초기화하도록 수정하세요 (참조: beforeTest,
clearMocks, receiptRepository, accountRepository, fileReader,
cardinalGetService, fileMapper, fileRepository).

---

Duplicate comments:
In
`@src/main/kotlin/com/weeth/domain/account/presentation/ReceiptAdminController.kt`:
- Around line 30-55: The controller methods save, delete, and update in
ReceiptAdminController are missing the `@CurrentUser` injection (same issue noted
in AccountController); add a parameter annotated with `@CurrentUser` (e.g.,
currentUser: <AuthUserType>) to each of the functions save, delete, and update,
and forward that currentUser to the corresponding manageReceiptUseCase methods
(manageReceiptUseCase.save, .delete, .update) or use it where
authorization/audit is required so the controller receives the authenticated
user context like AccountController does.

---

Nitpick comments:
In
`@src/main/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCase.kt`:
- Around line 16-21: ManageAccountUseCase.save에서
cardinalGetService.findByAdminSide(request.cardinal)의 반환값을 사용하지 않고 있어(현재 호출만 함)
검증 의도가 불명확합니다; findByAdminSide의 반환 엔티티를 실제로 사용하려면 저장 로직에서 활용하거나, 검증 전용 의도를 명확히
하기 위해 cardinalGetService에 findByAdminSide를 대체할 validate/ensure 메서드(예:
ensureExistsAdminSide 또는 validateAdminSideCardinal) 를 추가해
ManageAccountUseCase.save에서 해당 검증 메서드를 호출하도록 변경하세요; 또는 호출 결과를 val으로 받아 널 여부를
체크하여 예외를 던지도록(또는 주석/문서로 의도를 명시) 수정해 불필요한 무시를 제거하세요.

In `@src/main/kotlin/com/weeth/domain/account/domain/entity/Receipt.kt`:
- Around line 24-25: The amount field on Receipt is currently var and can be
mutated to invalid values; change its declaration to "var amount: Int private
set" or add a custom setter on amount that enforces amount > 0 and throws an
IllegalArgumentException on invalid values, and ensure the existing create() and
update() methods use the same validation (or rely on the new setter) so direct
property assignment (e.g., receipt.amount = -5) cannot bypass validation.

In
`@src/test/kotlin/com/weeth/domain/account/application/usecase/command/ManageAccountUseCaseTest.kt`:
- Around line 34-45: Add verification that
cardinalGetService.findByAdminSide(...) is invoked in the success save test:
after calling useCase.save(dto) add a verify call to assert
cardinalGetService.findByAdminSide(40) was called (e.g., exactly once). This
ensures the ManageAccountUseCaseTest checks both repository save and the
dependency call to cardinalGetService.findByAdminSide in the useCase.save path.

Comment on lines +38 to +44
fun adjustSpend(
oldAmount: Money,
newAmount: Money,
) {
cancelSpend(oldAmount)
spend(newAmount)
}
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

예외 발생 시 상태가 불일치할 수 있습니다.
adjustSpendcancelSpendspend를 호출하므로, spend에서 예외가 나면 currentAmount가 이미 증가한 상태로 남습니다(부분 변경). 변경 전 검증 후 한 번에 조정하도록 원자적으로 처리하는 편이 안전합니다.

🛠️ 안전한 원자적 조정 예시
     fun adjustSpend(
         oldAmount: Money,
         newAmount: Money,
     ) {
-        cancelSpend(oldAmount)
-        spend(newAmount)
+        require(oldAmount.value > 0) { "기존 금액은 0보다 커야 합니다: ${oldAmount.value}" }
+        require(newAmount.value > 0) { "변경 금액은 0보다 커야 합니다: ${newAmount.value}" }
+        val delta = newAmount.value - oldAmount.value
+        when {
+            delta > 0 -> {
+                check(currentAmount >= delta) { "잔액이 부족합니다. 현재: $currentAmount, 요청: $delta" }
+                currentAmount -= delta
+            }
+            delta < 0 -> {
+                check(currentAmount - delta <= totalAmount) { "총액을 초과할 수 없습니다. 총액: $totalAmount" }
+                currentAmount -= delta
+            }
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/com/weeth/domain/account/domain/entity/Account.kt` around
lines 38 - 44, adjustSpend currently calls cancelSpend(oldAmount) then
spend(newAmount) which can leave currentAmount in an inconsistent partial state
if spend throws; instead compute the net result first and perform validation
before mutating state so the update is atomic. In practice, calculate net =
currentAmount - oldAmount + newAmount (or a helper like computeAdjustedAmount),
validate that net is allowed, then assign currentAmount = net (or call a single
mutator method that applies the delta once); ensure any checks
(limits/constraints) run before changing currentAmount and remove the two-step
cancelSpend/spend sequence to avoid partial updates.

Comment on lines +23 to +27
@GetMapping("/{cardinal}")
@Operation(summary = "회비 내역 조회")
fun find(
@PathVariable cardinal: Int,
): CommonResponse<AccountResponse> = CommonResponse.success(ACCOUNT_FIND_SUCCESS, getAccountQueryService.findByCardinal(cardinal))
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# `@CurrentUser` 사용 패턴 및 AccountController 확인
echo "=== `@CurrentUser` 사용 현황 ==="
rg -n "@CurrentUser" src/main --type kotlin

echo -e "\n=== AccountController 전체 구조 ==="
rg -n "class AccountController" -A 50 src/main --type kotlin | head -80

echo -e "\n=== findByCardinal 메서드 정의 ==="
rg -n "fun findByCardinal" -B 2 -A 10 src/main --type kotlin

echo -e "\n=== 같은 패키지의 다른 controller 인증 패턴 ==="
fd "Controller.kt$" src/main/kotlin/com/weeth/domain --exec rg -l "@CurrentUser" {} \;

Repository: Team-Weeth/weeth-server

Length of output: 4825


@currentuser 주입 필수 — 회비 내역 조회 엔드포인트에 인증 사용자 주입 추가

컨트롤러 가이드라인 및 코드베이스 패턴(PostCommentController, NoticeCommentController 참고)에 따라 인증된 사용자 ID를 주입해야 합니다. 현재 구현은 @CurrentUser 매개변수가 누락되어 있으며, 서비스 레이어도 사용자 기반 접근 제어 검증을 수행하지 않습니다.

fun find(
    `@Parameter`(hidden = true) `@CurrentUser` userId: Long,
    `@PathVariable` cardinal: Int,
): CommonResponse<AccountResponse>

회비 내역은 특정 사용자의 개인 정보이므로 조회 권한을 검증하고, userId를 서비스에 전달하여 권한 체크(예: 해당 계정의 소유자 여부)를 수행하세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/com/weeth/domain/account/presentation/AccountController.kt`
around lines 23 - 27, The find endpoint is missing the authenticated user
injection; update AccountController.find to accept a userId parameter annotated
with `@Parameter`(hidden = true) and `@CurrentUser` (e.g., fun
find(`@Parameter`(hidden = true) `@CurrentUser` userId: Long, `@PathVariable`
cardinal: Int): CommonResponse<AccountResponse>), then forward that userId to
getAccountQueryService (adjust getAccountQueryService.findByCardinal to accept
userId as first arg) so the service can perform ownership/authorization checks
before returning the AccountResponse; ensure method signatures for
AccountController.find and getAccountQueryService.findByCardinal are updated
consistently and any callers/tests adjusted accordingly.

Copy link
Contributor

@hyxklee hyxklee left a comment

Choose a reason for hiding this comment

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

아 리뷰를 한 줄 았앗는데 pending으로 남아 있었네요..
고생하셨습니다! 스웨거 예시랑 파일 업데이트 관련해서만 한 번 확인 부탁드릴게요!

@field:NotNull
@field:Positive
val totalAmount: Int,
@field:Schema(description = "기수", example = "40")
Copy link
Contributor

Choose a reason for hiding this comment

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

40이 너무 크네오...ㅋㅋㅋㅋ

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

아앗 ㅋㅋ.. 수정하겟ㅅ습니닷

@field:Schema(description = "사용 날짜", example = "2024-09-01")
@field:NotNull
val date: LocalDate,
@field:Schema(description = "기수", example = "40")
Copy link
Contributor

Choose a reason for hiding this comment

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

요기도 40이네오..

@field:NotNull
val cardinal: Int,
@field:Valid
val files: List<@NotNull FileSaveRequest>?,
Copy link
Contributor

Choose a reason for hiding this comment

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

"첨부 파일 변경 규약: null=변경 안 함, []=전체 삭제, 배열 전달=해당 목록으로 교체"

이 내용도 같이 들어가면 좋을 것 같아요!

@field:Schema(description = "사용 날짜", example = "2024-09-01")
@field:NotNull
val date: LocalDate,
@field:Schema(description = "기수", example = "40")
Copy link
Contributor

Choose a reason for hiding this comment

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

요기도 40이네옹

@soo0711
Copy link
Collaborator Author

soo0711 commented Feb 20, 2026

다 수정했습니다!!

@soo0711 soo0711 merged commit 8c80d06 into dev Feb 20, 2026
2 checks passed
@soo0711 soo0711 deleted the refactor/WTH-149-account-도메인-코틀린-마이그레이션 branch February 20, 2026 08:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 Refactor 코드 구조 개선 및 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants