Skip to content

Conversation

@dolong2
Copy link
Owner

@dolong2 dolong2 commented Sep 14, 2025

๊ฐœ์š”

  • ๋งˆ์šดํŠธ ํ•ด์ œ API๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

์ž‘์—…๋‚ด์šฉ

  • ๋ณผ๋ฅจ ๋งˆ์šดํŠธ์‹œ ์ด๋ฏธ ๋งˆ์šดํŠธ๋์„๋•Œ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋กœ์ง ์ถ”๊ฐ€
  • ๋งˆ์šดํŠธ ์‚ญ์ œ, ๋งˆ์šดํŠธ ์กฐํšŒ ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€
  • VolumeMountNotFoundException ์ถ”๊ฐ€
  • ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ํ•ด์ œ ์œ ์Šค์ผ€์ด์Šค ์ถ”๊ฐ€
  • ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ํ•ด์ œ ์—”๋“œํฌ์ธํŠธ ์ถ”๊ฐ€

์ฒดํฌ๋ฆฌ์ŠคํŠธ

ํƒฌํ”Œ๋ฆฟ์™ธ์— ํ•„์š”ํ•œ ํ•ญ๋ชฉ์ด ์žˆ์œผ๋ฉด ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.

  • ๋กœ์ปฌ์—์„œ ๋นŒ๋“œ๊ฐ€ ์„ฑ๊ณตํ•˜๋‚˜์š”?
  • ์ถ”๊ฐ€(์ˆ˜์ •)ํ•œ ์ฝ”๋“œ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋‚˜์š”?
  • pr ํƒ€์ผ“ ๋ธŒ๋žœ์น˜๊ฐ€ ๋งž๊ฒŒ ์„ค์ •๋˜์–ด ์žˆ๋‚˜์š”?
  • pr์—์„œ ์ž‘์—…ํ•  ๋‚ด์šฉ๋งŒ ์ž‘์—…๋๋‚˜์š”?
  • ๊ธฐ์กด API์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” ์‚ฌํ•ญ์ด ์žˆ๋‚˜์š”?

Summary by CodeRabbit

  • New Features

    • ๋ณผ๋ฅจ ์–ธ๋งˆ์šดํŠธ ๊ธฐ๋Šฅ ์ถ”๊ฐ€: DELETE /{workspaceId}/volume/{volumeId}/mount (applicationId ํ•„์š”), ์„ฑ๊ณต ์‹œ 200 ๋ฐ˜ํ™˜.
    • ์ค‘๋ณต ๋งˆ์šดํŠธ ๋ฐฉ์ง€ ๋กœ์ง ์ถ”๊ฐ€(์ด๋ฏธ ๋งˆ์šดํŠธ๋œ ๊ฒฝ์šฐ ์˜ค๋ฅ˜ ๋ฐœ์ƒ).
    • ์–ธ๋งˆ์šดํŠธ ์—”๋“œํฌ์ธํŠธ์— ์ธ์ฆ ์š”๊ตฌ ์ถ”๊ฐ€.
    • ์‹ ๊ทœ ์˜ค๋ฅ˜ ์ฝ”๋“œ: ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ๋ฏธ์กด์žฌ(404) ๋ฐ ๊ด€๋ จ ์˜ˆ์™ธ ์ถ”๊ฐ€.
  • Tests

    • ๋งˆ์šดํŠธ/์–ธ๋งˆ์šดํŠธ ์œ ์Šค์ผ€์ด์Šค ๋ฐ ์›น ์–ด๋Œ‘ํ„ฐ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€, ์ •์ƒยท์˜ค๋ฅ˜ยท๊ฒฝ๊ณ„ ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์ฆ.

@dolong2 dolong2 self-assigned this Sep 14, 2025
@dolong2 dolong2 added 1๏ธโƒฃ Priority: ์ƒ ์šฐ์„ ์ˆœ์œ„ ์ƒ โœจ Feature ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ labels Sep 14, 2025
@dolong2 dolong2 linked an issue Sep 14, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 14, 2025

Warning

Rate limit exceeded

@dolong2 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 6 seconds before requesting another review.

โŒ› How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

๐Ÿšฆ How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between c75eeb6 and 45b06e6.

๐Ÿ“’ Files selected for processing (2)
  • src/main/kotlin/com/dcd/server/persistence/volume/entity/VolumeMountJpaEntity.kt (1 hunks)
  • src/test/resources/data.sql (1 hunks)

Walkthrough

๋ณผ๋ฅจ ๋งˆ์šดํŠธ ๊ด€๋ จ: ๋‹จ๊ฑด ๋งˆ์šดํŠธ ์กฐํšŒ/์‚ญ์ œ ํฌํŠธยทํผ์‹œ์Šคํ„ด์Šคยท๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ฐ ์›น ์—”๋“œํฌ์ธํŠธ(DELETE) ์ถ”๊ฐ€, ์ค‘๋ณต ๋งˆ์šดํŠธ ๊ฒ€์‚ฌยท์˜ˆ์™ธ ์ถ”๊ฐ€, VolumeMount ์‹๋ณ„์ž ๋ชจ๋ธ์„ UUID ๋‹จ์ผํ‚ค์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜+๋ณผ๋ฅจ ๋ณตํ•ฉํ‚ค๋กœ ๋ณ€๊ฒฝ, ๊ด€๋ จ ์—๋Ÿฌ ์ฝ”๋“œยท์˜ˆ์™ธยทํ…Œ์ŠคํŠธ ์ถ”๊ฐ€.

Changes

Cohort / File(s) Summary
Errors & Exceptions
src/main/kotlin/.../common/error/ErrorCode.kt, src/main/kotlin/.../domain/volume/exception/VolumeMountNotFoundException.kt
VOLUME_MOUNT_NOT_FOUND("ํ•ด๋‹น ๋ณผ๋ฅจ ๋งˆ์šดํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ", 404) ์—๋Ÿฌ ์ฝ”๋“œ ์ถ”๊ฐ€ ๋ฐ VolumeMountNotFoundException ์‹ ์„ค.
Ports (Volume)
src/main/kotlin/.../domain/volume/spi/CommandVolumePort.kt, src/main/kotlin/.../domain/volume/spi/QueryVolumePort.kt
deleteMount(volumeMount: VolumeMount) ๋ฐ findMountByApplicationAndVolume(application: Application, volume: Volume): VolumeMount? ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€.
Use cases (Volume)
src/main/kotlin/.../domain/volume/usecase/MountVolumeUseCase.kt, src/main/kotlin/.../domain/volume/usecase/UnMountVolumeUseCase.kt
๋งˆ์šดํŠธ ์‹œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜-๋ณผ๋ฅจ ์ค‘๋ณต ๊ฒ€์‚ฌ(AlreadyExistsVolumeMountException) ์ถ”๊ฐ€. ์‹ ๊ทœ UnMountVolumeUseCase.execute(volumeId, applicationId) ์ถ”๊ฐ€(์›Œํฌ์ŠคํŽ˜์ด์Šค/์กด์žฌ ๊ฒ€์ฆ โ†’ ๋‹จ๊ฑด ์กฐํšŒ โ†’ ์‚ญ์ œ โ†’ Deploy ์ด๋ฒคํŠธ ๋ฐœํ–‰).
Web & Security
src/main/kotlin/.../presentation/domain/volume/VolumeWebAdapter.kt, src/main/kotlin/.../infrastructure/global/config/SecurityConfig.kt
DELETE /{workspaceId}/volume/{volumeId}/mount?applicationId=... ์—”๋“œํฌ์ธํŠธ ๋ฐ ์ธ์ฆ ๊ทœ์น™ ์ถ”๊ฐ€; VolumeWebAdapter์— UnMountVolumeUseCase ์ฃผ์ž….
Persistence & DB model
src/main/kotlin/.../persistence/volume/VolumePersistenceAdapter.kt, src/main/kotlin/.../persistence/volume/repository/VolumeMountRepository.kt, src/main/kotlin/.../persistence/volume/entity/VolumeMountJpaEntity.kt, src/main/kotlin/.../persistence/volume/adapter/VolumeAdapter.kt, src/test/resources/data.sql
VolumeMount JPA ์—”ํ‹ฐํ‹ฐ๋ฅผ UUID ๋‹จ์ผ PK โ†’ (application_id, volume_id) ๋ณตํ•ฉํ‚ค(EmbeddedId)๋กœ ๋ณ€๊ฒฝ. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ œ๋„ค๋ฆญ ID ํƒ€์ž… ๋ณ€๊ฒฝ ๋ฐ findByVolumeAndApplication(...) ์ถ”๊ฐ€. ์–ด๋Œ‘ํ„ฐ์—์„œ id ๋งคํ•‘ ์ œ๊ฑฐ, ์–ด๋Œ‘ํ„ฐ์— findMountByApplicationAndVolumeยทdeleteMount ๊ตฌํ˜„ ์ถ”๊ฐ€. ํ…Œ์ŠคํŠธ DDL์„ ๋ณตํ•ฉํ‚ค๋กœ ๋ณ€๊ฒฝ ๋ฐ FK ์ œ์•ฝ ์ถ”๊ฐ€.
Domain model
src/main/kotlin/.../core/domain/volume/model/VolumeMount.kt
VolumeMount ์ƒ์„ฑ์ž์—์„œ id: UUID ์ œ๊ฑฐ(๋„๋ฉ”์ธ API ๋ณ€๊ฒฝ).
Tests
src/test/kotlin/.../core/domain/volume/usecase/MountVolumeUseCaseTest.kt, src/test/kotlin/.../core/domain/volume/usecase/UnMountVolumeUseCaseTest.kt, src/test/kotlin/.../presentation/domain/volume/VolumeWebAdapterTest.kt, src/test/resources/data.sql
์ค‘๋ณต ๋งˆ์šดํŠธ ์˜ˆ์™ธ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€, UnMountVolumeUseCase ํ…Œ์ŠคํŠธ(์„ฑ๊ณตยท์—ฌ๋Ÿฌ ์˜ค๋ฅ˜ ๊ฒฝ๋กœ) ์ถ”๊ฐ€, ์›น ์–ด๋Œ‘ํ„ฐ DELETE ์—”๋“œํฌ์ธํŠธ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ ๋ฐ ํ…Œ์ŠคํŠธ ์‹œ๊ทธ๋‹ˆ์ฒ˜/DIยท๋ฐ์ดํ„ฐDDL ์—…๋ฐ์ดํŠธ.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Web as VolumeWebAdapter
  participant MUC as MountVolumeUseCase
  participant QV as QueryVolumePort
  participant QA as QueryApplicationPort
  participant EV as ApplicationEventPublisher

  Client->>Web: POST /{ws}/volume/{vid}/mount?applicationId=...
  Web->>MUC: execute(volumeId, applicationId)
  MUC->>QV: findById(volumeId)
  MUC->>QA: findById(applicationId)
  MUC->>QV: findMountByApplicationAndVolume(app, vol)
  alt Mount exists
    MUC-->>Web: throw AlreadyExistsVolumeMountException
    Web-->>Client: 4xx
  else Not exists
    MUC->>QV: save mount
    MUC->>EV: publish DeployApplicationEvent
    Web-->>Client: 200 OK
  end
Loading
sequenceDiagram
  autonumber
  actor Client
  participant Web as VolumeWebAdapter
  participant UUC as UnMountVolumeUseCase
  participant QV as QueryVolumePort
  participant QA as QueryApplicationPort
  participant CV as CommandVolumePort
  participant EV as ApplicationEventPublisher

  Client->>Web: DELETE /{ws}/volume/{vid}/mount?applicationId=...
  Web->>UUC: execute(volumeId, applicationId)
  UUC->>QV: findById(volumeId)
  UUC->>QA: findById(applicationId)
  UUC->>QV: findMountByApplicationAndVolume(app, vol)
  alt Mount not found
    UUC-->>Web: throw VolumeMountNotFoundException
    Web-->>Client: 404
  else Found
    UUC->>CV: deleteMount(volumeMount)
    UUC->>EV: publish DeployApplicationEvent
    Web-->>Client: 200 OK
  end
Loading

Estimated code review effort

๐ŸŽฏ 4 (Complex) | โฑ๏ธ ~45 minutes

Possibly related PRs

  • ๐Ÿ”€ :: [#717] - ๋ณผ๋ฅจ ๋งˆ์šดํŠธ API ์ถ”๊ฐ€ย #718: ๋™์ผ ์„œ๋ธŒ์‹œ์Šคํ…œ(๋ณผ๋ฅจ-๋งˆ์šดํŠธ)์—์„œ ํฌํŠธยทํผ์‹œ์Šคํ„ด์Šคยท์›น ์–ด๋Œ‘ํ„ฐ ํ™•์žฅ์„ ๋‹ค๋ฃธ โ€” ๋งˆ์šดํŠธ/์–ธ๋งˆ์šดํŠธ ๋ณ€๊ฒฝ๊ณผ ์ฝ”๋“œ ๋ ˆ๋ฒจ ์—ฐ๊ด€.
  • ๐Ÿ”€ :: [#702] - ๋ณผ๋ฅจ ์—”ํ‹ฐํ‹ฐ ์ถ”๊ฐ€ย #703: VolumeMount ๋„๋ฉ”์ธยทํผ์‹œ์Šคํ„ด์Šค ๊ตฌ์กฐ ๋ณ€๊ฒฝ(์‹๋ณ„์ž/๋งคํ•‘) ๊ด€๋ จ PR โ€” ๋ณตํ•ฉํ‚ค ์ „ํ™˜ ๋ฐ ์–ด๋Œ‘ํ„ฐ/๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ณ€๊ฒฝ๊ณผ ์ง์ ‘ ์—ฐ๊ฒฐ.
  • ๐Ÿ”€ :: [#709] - ๋ณผ๋ฅจ ์‚ญ์ œ API ์ถ”๊ฐ€ย #710: ์ค‘๋ณต ๋งˆ์šดํŠธ ๊ฒ€์‚ฌยท์˜ˆ์™ธ ๋ฐ ๊ด€๋ จ ํ๋ฆ„ ๋ณ€๊ฒฝ๊ณผ ์—ฐ๊ด€๋œ PR โ€” ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋งˆ์šดํŠธ ์ฒ˜๋ฆฌ์™€ ์ง์ ‘ ์—ฐ๊ฒฐ.

Poem

ํ† ๋ผ๊ฐ€ ๋งํ•˜๋„ค, "๋งˆ์šดํŠธ ๋‚ ๊ฐœ ํŽด!" ๐Ÿฅ•
์ค‘๋ณต์ด๋ฉด ๋ฉˆ์ถ”๊ณ , ์—†์œผ๋ฉด ์กฐ์šฉํžˆ ์ง€์›Œ์š”.
๋‘ ํ‚ค๊ฐ€ ์†์žก์•„ ์•„์ด๋ดํ‹ฐํ‹ฐ๋ฅผ ์ง€ํ‚ค๊ณ ,
์ด๋ฒคํŠธ ํ•œ ์คŒ ๋ฟŒ๋ ค ๋ฐฐํฌ ๊ธธ์„ ์—ฌ๋‹ˆ,
๋‹น๊ทผ ํ•œ ์กฐ๊ฐ์— ๊ธฐ์œ ๊นก์ถฉ ๊นก์ถฉ.

Pre-merge checks and finishing touches

โœ… Passed checks (3 passed)
Check name Status Explanation
Description Check โœ… Passed Check skipped - CodeRabbitโ€™s high-level summary is enabled.
Title Check โœ… Passed ์ œ๋ชฉ "๐Ÿ”€ :: [#719] - ๋งˆ์šดํŠธ ํ•ด์ œ API ์ถ”๊ฐ€"๋Š” PR์˜ ํ•ต์‹ฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ธ '๋งˆ์šดํŠธ ํ•ด์ œ API ์ถ”๊ฐ€'๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์š”์•ฝํ•˜๊ณ  ์žˆ์–ด ๋ณ€๊ฒฝ๋‚ด์šฉ๊ณผ ๋ถ€ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ด๋ชจ์ง€, ์ด์Šˆ ๋ฒˆํ˜ธ ๋ฐ '::' ๊ฐ™์€ ๋ฉ”ํƒ€๋ฌธ์ž๋Š” ํžˆ์Šคํ† ๋ฆฌ ํƒ์ƒ‰ ์‹œ ๋ถˆํ•„์š”ํ•œ ๋…ธ์ด์ฆˆ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ „๋ฐ˜์ ์œผ๋กœ ๋™๋ฃŒ๊ฐ€ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ํ›‘์„ ๋•Œ ์ฃผ์š” ์˜๋„๋ฅผ ์ดํ•ดํ•˜๊ธฐ์— ์ถฉ๋ถ„ํ•˜๋ฏ€๋กœ ๋ณธ ์ฒดํฌ๋Š” ํ†ต๊ณผ๋กœ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.
Docstring Coverage โœ… Passed No functions found in the changes. Docstring coverage check skipped.

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
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

๐Ÿงน Nitpick comments (10)
src/main/kotlin/com/dcd/server/core/domain/volume/spi/CommandVolumePort.kt (1)

13-13: ์‚ญ์ œ ์—ฐ์‚ฐ ๊ณ„์•ฝ ๋ช…ํ™•ํ™” ์ œ์•ˆ

deleteMount์˜ ๊ฒฐ๊ณผ ๊ณ„์•ฝ(์กด์žฌํ•˜์ง€ ์•Š์•„๋„ ์„ฑ๊ณต์œผ๋กœ ๋ณผ์ง€, ์˜ˆ์™ธ๋ฅผ ๋˜์งˆ์ง€)์„ KDoc์— ๋ช…์‹œํ•˜๋ฉด ํ˜ธ์ถœ๋ถ€ ์ฒ˜๋ฆฌ ์ผ๊ด€์„ฑ์ด ์ข‹์•„์ง‘๋‹ˆ๋‹ค.

src/main/kotlin/com/dcd/server/core/domain/volume/spi/QueryVolumePort.kt (1)

20-20: ๋‹จ๊ฑด ์กฐํšŒ ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€ ์ ์ ˆ โ€” ๋„ค์ด๋ฐ/๊ณ„์•ฝ ๋ช…์‹œ ์ œ์•ˆ

๋‹จ๊ฑด ๋ฐ˜ํ™˜์€ โ€œ์œ ์ผ์„ฑ ๋ณด์žฅโ€ ์ „์ œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ KDoc์— ์œ ์ผ์„ฑ ์ „์ œ์™€ NotFound/์ค‘๋ณต ์‹œ ๋™์ž‘์„ ๋ช…์‹œํ•˜๊ฑฐ๋‚˜, ์ด๋ฆ„์„ findMountByVolumeAndApplication๋กœ ๋งž์ถ”๋ฉด ์ €์žฅ์†Œ ๋ฉ”์„œ๋“œ์™€ ์ผ๊ด€์„ฑ์ด ์ข‹์•„์ง‘๋‹ˆ๋‹ค.

src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt (1)

61-63: ์กฐ์ธ ํ‚ค ๊ธฐ๋ฐ˜ ์กฐํšŒ ๊ตฌํ˜„ OK โ€” ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜ ๊ถŒ์žฅ

์ฝ๊ธฐ ๋ฉ”์„œ๋“œ๋Š” @Transactional(readOnly = true) ์ ์šฉ ์‹œ ์„ฑ๋Šฅ ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค(2์ฐจ ์บ์‹œ/ํ”Œ๋Ÿฌ์‹œ ๋ฐฉ์ง€).

src/test/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapterTest.kt (1)

155-170: ์–ธ๋งˆ์šดํŠธ API ํ˜ธ์ถœ ๊ฒ€์ฆ ๊ฐ•ํ™” ์ œ์•ˆ

์ •ํ™•ํ•œ ์ƒํ˜ธ์ž‘์šฉ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ํ˜ธ์ถœ ํšŸ์ˆ˜ ๋ฐ ๋ถˆํ•„์š” ์ƒํ˜ธ์ž‘์šฉ ๋ถ€์žฌ๋ฅผ ๊ฒ€์ฆํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

-            then("๋ณผ๋ฅจ ๋งˆ์šดํŠธ ํ•ด์ œ ์œ ์Šค์ผ€์ด์Šค๊ฐ€ ์‹คํ–‰๋˜์–ด์•ผํ•จ") {
-                verify { unMountVolumeUseCase.execute(testVolumeId, testApplicationId) }
-            }
+            then("๋ณผ๋ฅจ ๋งˆ์šดํŠธ ํ•ด์ œ ์œ ์Šค์ผ€์ด์Šค๊ฐ€ 1ํšŒ ์‹คํ–‰๋˜์–ด์•ผํ•จ") {
+                verify(exactly = 1) { unMountVolumeUseCase.execute(testVolumeId, testApplicationId) }
+                confirmVerified(unMountVolumeUseCase)
+            }
src/test/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCaseTest.kt (1)

117-136: ์ค‘๋ณต ๋งˆ์šดํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค: ํ…Œ์ŠคํŠธ ๊ฐ„ ์ƒํƒœ ๊ฒฉ๋ฆฌ ๋ณด๊ฐ•

๊ฐ™์€ ์ปจํ…Œ์ด๋„ˆ ๋‚ด ์„ ํ–‰ ํ…Œ์ŠคํŠธ์—์„œ ์ด๋ฏธ ๋งˆ์šดํŠธ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์„ ์ˆ˜ ์žˆ์–ด, ์ด ๋ธ”๋ก์˜ beforeContainer์—์„œ ๋˜ ์ €์žฅํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์ˆœ์„œ์— ๋”ฐ๋ผ ์ค‘๋ณต ๋ฐ์ดํ„ฐ๋กœ ์ธํ•œ ๋ถˆ์•ˆ์ •์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ธ”๋ก ์ง„์ž… ์ „์— ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ •๋ฆฌํ•˜๊ฑฐ๋‚˜ beforeTest๋กœ ์ „ํ™˜ํ•ด ์ผ€์ด์Šค ๊ฐ„ ์ƒํƒœ๋ฅผ ๊ฒฉ๋ฆฌํ•ด ์ฃผ์„ธ์š”.

-        `when`("์ด๋ฏธ ๋ณผ๋ฅจ์— ๋งˆ์šดํŠธ ๋์„๋•Œ") {
-            beforeContainer {
+        `when`("์ด๋ฏธ ๋ณผ๋ฅจ์— ๋งˆ์šดํŠธ ๋์„๋•Œ") {
+            beforeTest {
+                volumeMountRepository.deleteAll()
                 val application = queryApplicationPort.findById(targetApplicationId)!!
                 val volume = volumeRepository.findByIdOrNull(targetVolumeId)!!.toDomain()
                 val volumeMount = VolumeMount(
                     id = UUID.randomUUID(),
                     application = application,
                     volume = volume,
                     mountPath = "/test/volume",
                     readOnly = false
                 )
                 volumeMountRepository.save(volumeMount.toEntity())
             }
src/main/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapter.kt (2)

37-39: ์ƒ์„ฑ์ž ์˜์กด์„ฑ ํ™•์žฅ OK โ€” REST ์‹œ๋งจํ‹ฑ์Šค์™€ ์‘๋‹ต ์ฝ”๋“œ ์žฌ๊ฒ€ํ†  ๊ถŒ์žฅ

์–ธ๋งˆ์šดํŠธ๋Š” ๋ฆฌ์†Œ์Šค ์ œ๊ฑฐ ์„ฑ๊ฒฉ์ด๋ฏ€๋กœ 200๋ณด๋‹ค 204(No Content)๊ฐ€ ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์—”๋“œํฌ์ธํŠธ์—์„œ ๋ฐ˜์˜ ์ œ์•ˆ๋“œ๋ฆฝ๋‹ˆ๋‹ค.


94-103: DELETE ์‘๋‹ต ์ฝ”๋“œ๋ฅผ 204(No Content)๋กœ ๋ณ€๊ฒฝ ์ œ์•ˆ

REST ๊ด€๋ก€์ƒ ์‚ญ์ œ ์„ฑ๊ณต ์‹œ 204๊ฐ€ ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋„ ํ•จ๊ป˜ ์—…๋ฐ์ดํŠธํ•ด ์ฃผ์„ธ์š”.

-    @DeleteMapping("/{volumeId}/mount")
+    @DeleteMapping("/{volumeId}/mount")
     @WorkspaceOwnerVerification("#workspaceId")
     fun unMountVolume(
         @PathVariable workspaceId: String,
         @PathVariable volumeId: UUID,
         @RequestParam applicationId: String,
     ): ResponseEntity<Void> =
-        unMountVolumeUseCase.execute(volumeId, applicationId)
-            .run { ResponseEntity.ok().build() }
+        unMountVolumeUseCase.execute(volumeId, applicationId)
+            .run { ResponseEntity.noContent().build() }
src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCaseTest.kt (1)

45-64: ์‹œ๋“œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ์œ„์น˜ ์ ๊ฒ€

beforeSpec์—์„œ ์ƒ์„ฑ๋œ ์—”ํ‹ฐํ‹ฐ๋Š” ์ผ€์ด์Šค ๊ฐ„ ๊ณต์œ ๋ฉ๋‹ˆ๋‹ค. ์ดํ›„ ์ผ€์ด์Šค์—์„œ ์‚ญ์ œ/์ˆ˜์ • ์‹œ ๋‹ค๋ฅธ ์ผ€์ด์Šค์— ์˜ํ–ฅ ์ค„ ์ˆ˜ ์žˆ์œผ๋‹ˆ, ๊ฐ€๋Šฅํ•˜๋ฉด ์ผ€์ด์Šค๋ณ„ beforeTest๋กœ ๊ฒฉ๋ฆฌํ•˜๊ฑฐ๋‚˜ ๊ฐ ์ผ€์ด์Šค ์‹œ์ž‘ ์‹œ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ •๋ฆฌ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

src/main/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCase.kt (2)

24-43: ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์™€ ์ด๋ฒคํŠธ ๋ฐœํ–‰ ์ˆœ์„œ ์žฌ๊ฒ€ํ† 

์‚ญ์ œ ํ›„ ์ฆ‰์‹œ ApplicationEventPublisher๋กœ ๋™๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๋ฉด ๋กค๋ฐฑ ์‹œ ์ •ํ•ฉ์„ฑ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„ ๋‚ด์—์„œ ์‚ญ์ œ๋ฅผ ๋ณด์žฅํ•˜๊ณ , ์ปค๋ฐ‹ ์ดํ›„์— ์ด๋ฒคํŠธ๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋„๋ก ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๊ถŒ์žฅ์‚ฌํ•ญ:

  • @Transactional(์„œ๋น„์Šค ๊ณ„์ธต) ์ถ”๊ฐ€๋กœ ์‚ญ์ œ ์›์ž์„ฑ ๋ณด์žฅ.
  • TransactionalEventPublisher ๋˜๋Š” @TransactionalEventListener(phase = AFTER_COMMIT)/์•„์›ƒ๋ฐ•์Šค ํŒจํ„ด ๋„์ž….

์˜ˆ์‹œ:

+import org.springframework.transaction.annotation.Transactional
@@
-@UseCase
-class UnMountVolumeUseCase(
+@UseCase
+@Transactional
+class UnMountVolumeUseCase(

38-41: ์–ธ๋งˆ์šดํŠธ์˜ ๋ฉฑ๋“ฑ์„ฑ(idempotency) ๊ณ ๋ ค(์„ ํƒ)

์‹ค๋ฌด ์šด์˜์—์„œ๋Š” ์ด๋ฏธ ํ•ด์ œ๋œ ์ƒํƒœ์— ๋Œ€ํ•œ ๋ฐ˜๋ณต ์š”์ฒญ์„ ์„ฑ๊ณต(204) ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ๋„ UX/์šด์˜์ƒ ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” VolumeMountNotFoundException์„ ๋˜์ง€๋ฏ€๋กœ, API ๊ณ„์•ฝ์— ๋”ฐ๋ผ ์œ ์ง€ํ•˜๋˜ ํ•„์š” ์‹œ ์˜ต์…˜ํ™”(์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ๋˜๋Š” ๋ณ„๋„ ์—”๋“œํฌ์ธํŠธ ์ •์ฑ…) ๊ฒ€ํ† ๋ฅผ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์กด API ๊ฐ€์ด๋“œ์— DELETE ๋ฉฑ๋“ฑ์„ฑ ์š”๊ตฌ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between aa35205 and 1af84eb.

๐Ÿ“’ Files selected for processing (13)
  • src/main/kotlin/com/dcd/server/core/common/error/ErrorCode.kt (1 hunks)
  • src/main/kotlin/com/dcd/server/core/domain/volume/exception/VolumeMountNotFoundException.kt (1 hunks)
  • src/main/kotlin/com/dcd/server/core/domain/volume/spi/CommandVolumePort.kt (1 hunks)
  • src/main/kotlin/com/dcd/server/core/domain/volume/spi/QueryVolumePort.kt (1 hunks)
  • src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt (2 hunks)
  • src/main/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCase.kt (1 hunks)
  • src/main/kotlin/com/dcd/server/infrastructure/global/config/SecurityConfig.kt (1 hunks)
  • src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt (2 hunks)
  • src/main/kotlin/com/dcd/server/persistence/volume/repository/VolumeMountRepository.kt (1 hunks)
  • src/main/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapter.kt (3 hunks)
  • src/test/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCaseTest.kt (4 hunks)
  • src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCaseTest.kt (1 hunks)
  • src/test/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapterTest.kt (3 hunks)
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (3)
๐Ÿ““ Common learnings
Learnt from: dolong2
PR: dolong2/dcd#703
File: src/main/kotlin/com/dcd/server/core/domain/volume/model/Volume.kt:1-12
Timestamp: 2025-09-01T14:52:42.625Z
Learning: dolong2 prefers to implement validation logic (such as path validation for Volume.physicalPath) in the business logic layer rather than in domain model init blocks, keeping domain models as simple data containers.
๐Ÿ“š Learning: 2025-09-04T14:59:15.319Z
Learnt from: dolong2
PR: dolong2/dcd#710
File: src/main/kotlin/com/dcd/server/core/domain/volume/service/impl/DeleteDockerVolumeServiceImpl.kt:13-18
Timestamp: 2025-09-04T14:59:15.319Z
Learning: dolong2 addressed command injection vulnerability in volume deletion by adding validation in the volume creation service (CreateDockerVolumeServiceImpl) using NAME_SAFE_REGEX pattern, preferring to validate at the source rather than in the deletion service for better security and fail-fast behavior.

Applied to files:

  • src/main/kotlin/com/dcd/server/infrastructure/global/config/SecurityConfig.kt
๐Ÿ“š Learning: 2024-12-17T14:26:50.307Z
Learnt from: dolong2
PR: dolong2/dcd#411
File: src/test/kotlin/com/dcd/server/core/domain/auth/usecase/SignOutUseCaseTest.kt:32-40
Timestamp: 2024-12-17T14:26:50.307Z
Learning: `SignOutUseCaseTest.kt`์˜ `afterContainer`์—์„œ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ •๋ฆฌ๋ฅผ ํ•  ๋•Œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์•„๋„ ๊ดœ์ฐฎ๋‹ค.

Applied to files:

  • src/test/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCaseTest.kt
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build and Test
๐Ÿ”‡ Additional comments (13)
src/main/kotlin/com/dcd/server/core/common/error/ErrorCode.kt (1)

50-50: ์ƒˆ ์—๋Ÿฌ ์ฝ”๋“œ ์ถ”๊ฐ€ ์ ์ ˆ โ€” Web/์˜ˆ์™ธ ๋งคํ•‘ ๋™๊ธฐํ™”๋งŒ ํ™•์ธํ•ด์ฃผ์„ธ์š”

๋„๋ฉ”์ธยท์›น ๋ ˆ์ด์–ด์—์„œ VOLUME_MOUNT_NOT_FOUND๋กœ ์ง๋ ฌํ™”/๋งคํ•‘๋˜๋Š”์ง€, ์—๋Ÿฌ ์ŠคํŽ™ ๋ฌธ์„œ๋„ ํ•จ๊ป˜ ๊ฐฑ์‹ ๋๋Š”์ง€ ํ™•์ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt (1)

9-9: ์˜ˆ์™ธ import ์ถ”๊ฐ€ ์ ์ ˆ

์ค‘๋ณต ๋งˆ์šดํŠธ ๋ฐฉ์ง€ ํ”Œ๋กœ์šฐ์— ํ•„์š”ํ•œ ์˜์กด์„ฑ ์ •ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

src/main/kotlin/com/dcd/server/core/domain/volume/exception/VolumeMountNotFoundException.kt (1)

1-6: ๋„๋ฉ”์ธ ์˜ˆ์™ธ ์ •์˜ ์ ์ ˆ

404 ์ฝ”๋“œ์™€ ์˜๋ฏธ๊ฐ€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€/i18n ๋ฆฌ์†Œ์Šค ๋™๊ธฐํ™”๋งŒ ํ™•์ธํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt (1)

35-37: ์‚ญ์ œ ์œ„์ž„ ๊ตฌํ˜„ OK

์ƒ์œ„ ๋ ˆ์ด์–ด์—์„œ ์กด์žฌ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ deleteById๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์—์„œ ๋™์ž‘ํ•˜๋Š”์ง€๋งŒ ํ™•์ธํ•ด์ฃผ์„ธ์š”.

src/main/kotlin/com/dcd/server/infrastructure/global/config/SecurityConfig.kt (1)

108-108: ๊ฒ€์ฆ ์™„๋ฃŒ โ€” DELETE ๋งค์ฒ˜ ๋ณ€๊ฒฝ ๋ถˆํ•„์š”

VolumeWebAdapter๋Š” @WebAdapter("/{workspaceId}/volume") + @DeleteMapping("/{volumeId}/mount")์ด๊ณ  applicationId๋Š” @RequestParam์œผ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ SecurityConfig์˜ it.requestMatchers(HttpMethod.DELETE, "/{workspaceId}/volume/{volumeId}/mount").authenticated() ๋งค์ฒ˜๋Š” ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค.

Likely an incorrect or invalid review comment.

src/test/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapterTest.kt (3)

14-14: ์‹ ๊ทœ ์œ ์Šค์ผ€์ด์Šค ์˜์กด์„ฑ ์ž„ํฌํŠธ ์ถ”๊ฐ€ OK

์›น ์–ด๋Œ‘ํ„ฐ/ํ…Œ์ŠคํŠธ ๊ฐ„ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์ •ํ•ฉ์„ฑ์— ๋งž๊ฒŒ UnMountVolumeUseCase ์ž„ํฌํŠธ ์ถ”๊ฐ€๋œ ์  ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.


35-35: ์–ธ๋งˆ์šดํŠธ ์œ ์Šค์ผ€์ด์Šค ๋ชฉ ์ถ”๊ฐ€ OK

relaxUnitFun = true ์„ค์ •์œผ๋กœ ๋ถˆํ•„์š”ํ•œ ์Šคํ… ์—†์ด ํ˜ธ์ถœ ๊ฒ€์ฆ์ด ๊ฐ€๋Šฅํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.


43-45: ํ™•์ธ ์™„๋ฃŒ โ€” ์ƒ์„ฑ์ž ์ธ์ž ์ˆœ์„œ/์ฃผ์ž… ์ผ์น˜
src/main/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapter.kt(31โ€“38)์˜ ์ƒ์„ฑ์ž์™€ src/test/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapterTest.kt(37โ€“45)์˜ ์ธ์Šคํ„ด์Šคํ™” ์ธ์ž ์ˆœ์„œ๊ฐ€ ์ผ์น˜ํ•˜๋ฉฐ, ์ €์žฅ์†Œ ๋‚ด ๋‹ค๋ฅธ ์ƒ์„ฑ์ž ํ˜ธ์ถœ/Bean ํŒฉํ† ๋ฆฌ ์ฐธ์กฐ๋Š” ๊ฒ€์ƒ‰๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

src/main/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapter.kt (2)

9-9: ์–ธ๋งˆ์šดํŠธ ์œ ์Šค์ผ€์ด์Šค ์˜์กด์„ฑ ์ถ”๊ฐ€ OK

์ปจํŠธ๋กค๋Ÿฌ ์ฑ…์ž„ ๋ถ„๋ฆฌ๊ฐ€ ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค.


94-103: ํ™•์ธ: DELETE /{workspaceId}/volume/{volumeId}/mount ๋ณด์•ˆ ๊ทœ์น™์ด ๋“ฑ๋ก๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค

src/main/kotlin/com/dcd/server/infrastructure/global/config/SecurityConfig.kt:108 โ€” it.requestMatchers(HttpMethod.DELETE, "/{workspaceId}/volume/{volumeId}/mount").authenticated() ํ™•์ธ.

src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCaseTest.kt (2)

72-78: ํ•ต์‹ฌ ์„ฑ๊ณต ๊ฒฝ๋กœ ๊ฒ€์ฆ LGTM

์–ธ๋งˆ์šดํŠธ ํ›„ ํผ์‹œ์Šคํ„ด์Šค๊ฐ€ ๋น„์–ด์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋‹จ์ •์ด ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค.


100-110: ์–ธ๋งˆ์šดํŠธ ๋น„์กด์žฌ ์ผ€์ด์Šค ํ…Œ์ŠคํŠธ LGTM

์ƒํƒœ ์ •๋ฆฌ ํ›„ VolumeMountNotFoundException ๊ฒ€์ฆ์ด ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

src/main/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCase.kt (1)

30-37: ํ™•์ธ: Workspace.equals()๊ฐ€ id ๊ธฐ๋ฐ˜์œผ๋กœ ์˜ค๋ฒ„๋ผ์ด๋“œ๋˜์–ด ๊ฐ์ฒด ๋™๋“ฑ์„ฑ ๋น„๊ต๋Š” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.
ํ™•์ธ ์œ„์น˜: src/main/kotlin/com/dcd/server/core/domain/workspace/model/Workspace.kt (equals โ†’ this.id == other.id)

Copy link
Contributor

@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: 6

โ™ป๏ธ Duplicate comments (2)
src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt (1)

41-43: ์ค‘๋ณต ๋งˆ์šดํŠธ ๊ฒฝ์Ÿ ์กฐ๊ฑด: DB ์˜ˆ์™ธ ๋งคํ•‘๊นŒ์ง€ ๋ณด์™„ ํ•„์š”

๋ณตํ•ฉ PK๋กœ ์œ ๋‹ˆํฌ๋Š” ๋ณด์žฅ๋˜์ง€๋งŒ, ๋™์‹œ ์š”์ฒญ ์‹œ ์ €์žฅ ๋‹จ๊ณ„์—์„œ DB ์˜ˆ์™ธ๊ฐ€ ๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. saveMount ๊ฒฝ๋กœ์—์„œ DataIntegrityViolationException์„ AlreadyExistsVolumeMountException์œผ๋กœ ๋งคํ•‘ํ•ด ์ผ๊ด€ ์‘๋‹ต์„ ๋ณด์žฅํ•˜์„ธ์š”.

์˜ˆ์‹œ(ํผ์‹œ์Šคํ„ด์Šค ์–ด๋Œ‘ํ„ฐ ์ชฝ ๊ถŒ์žฅ):

 try {
     commandVolumePort.saveMount(volumeMount)
-} 
+} catch (e: DataIntegrityViolationException) {
+    throw AlreadyExistsVolumeMountException()
+}
src/main/kotlin/com/dcd/server/persistence/volume/repository/VolumeMountRepository.kt (1)

9-13: ์กด์žฌ ์—ฌ๋ถ€ ์ „์šฉ ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€๋กœ ๋ถˆํ•„์š”ํ•œ ๋กœ๋”ฉ ๋ฐฉ์ง€

์ค‘๋ณต ์ฒดํฌ ์šฉ๋„์—๋Š” existsByVolumeAndApplication(...)๊ฐ€ ๋” ๊ฒฝ๋Ÿ‰์ž…๋‹ˆ๋‹ค. ๋„์ž… ํ›„ UseCase์—์„œ ํ•ด๋‹น ๊ฒฝ๋กœ ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

 interface VolumeMountRepository : JpaRepository<VolumeMountJpaEntity, VolumeMountJpaEntity.VolumeMountId> {
     fun findAllByVolume(volume: VolumeJpaEntity): List<VolumeMountJpaEntity>
     fun findAllByApplication(application: ApplicationJpaEntity): List<VolumeMountJpaEntity>
     fun findByVolumeAndApplication(volume: VolumeJpaEntity, application: ApplicationJpaEntity): VolumeMountJpaEntity?
+    fun existsByVolumeAndApplication(volume: VolumeJpaEntity, application: ApplicationJpaEntity): Boolean
 }
๐Ÿงน Nitpick comments (2)
src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt (1)

50-54: ์ด๋ฒคํŠธ ๋ฐœํ–‰์˜ ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„ ํ™•์ธ

์ €์žฅ ์‹คํŒจ/๋กค๋ฐฑ ์‹œ ์žฌ๋ฐฐํฌ ์ด๋ฒคํŠธ๊ฐ€ ๋‚˜๊ฐ€์ง€ ์•Š๋„๋ก afterโ€‘commit ๊ธฐ๋ฐ˜(TransactionSynchronizationManager/@TransactionalEventListener)์ธ์ง€ ํ™•์ธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

src/test/resources/data.sql (1)

29-30: ์‚ญ์ œ ์ •์ฑ… ์žฌํ™•์ธ: CASCADE๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๊ฒ€์ฆ์„ ์šฐํšŒํ•  ์ˆ˜ ์žˆ์Œ

๋Ÿฌ๋‹์ƒ(๊ฒ€์ฆ์€ ๋น„์ฆˆ๋‹ˆ์Šค ๋ ˆ์ด์–ด) CASCADE๋Š” ์˜๋„์™€ ์ƒ์ถฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ตœ์†Œํ•œ volume_id ์ชฝ CASCADE ์ œ๊ฑฐ๋ฅผ ๊ณ ๋ คํ•˜๊ฑฐ๋‚˜, ๊ด€๋ จ ํ…Œ์ŠคํŠธ๋กœ ์•ฑ ๋ ˆ์ด์–ด ๊ฒ€์ฆ์ด ๋ฐ˜๋“œ์‹œ ์‹คํ–‰๋จ์„ ๋ณด์žฅํ•˜์„ธ์š”.

-alter table if exists volume_mount_entity add constraint FKr5bb2ev813ioxtylyobgdphel foreign key (application_id) references application_entity (id) on delete cascade;
-alter table if exists volume_mount_entity add constraint FKq6dppr5p20mvgjditmpypicey foreign key (volume_id) references volume_entity (id) on delete cascade;
+alter table if exists volume_mount_entity add constraint FKr5bb2ev813ioxtylyobgdphel foreign key (application_id) references application_entity (id);
+alter table if exists volume_mount_entity add constraint FKq6dppr5p20mvgjditmpypicey foreign key (volume_id) references volume_entity (id);
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between cbb8c6b and bb36242.

๐Ÿ“’ Files selected for processing (7)
  • src/main/kotlin/com/dcd/server/core/domain/volume/model/VolumeMount.kt (0 hunks)
  • src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt (2 hunks)
  • src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt (2 hunks)
  • src/main/kotlin/com/dcd/server/persistence/volume/adapter/VolumeAdapter.kt (0 hunks)
  • src/main/kotlin/com/dcd/server/persistence/volume/entity/VolumeMountJpaEntity.kt (1 hunks)
  • src/main/kotlin/com/dcd/server/persistence/volume/repository/VolumeMountRepository.kt (1 hunks)
  • src/test/resources/data.sql (1 hunks)
๐Ÿ’ค Files with no reviewable changes (2)
  • src/main/kotlin/com/dcd/server/core/domain/volume/model/VolumeMount.kt
  • src/main/kotlin/com/dcd/server/persistence/volume/adapter/VolumeAdapter.kt
๐Ÿšง Files skipped from review as they are similar to previous changes (1)
  • src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (3)
๐Ÿ““ Common learnings
Learnt from: dolong2
PR: dolong2/dcd#703
File: src/main/kotlin/com/dcd/server/core/domain/volume/model/Volume.kt:1-12
Timestamp: 2025-09-01T14:52:42.625Z
Learning: dolong2 prefers to implement validation logic (such as path validation for Volume.physicalPath) in the business logic layer rather than in domain model init blocks, keeping domain models as simple data containers.
๐Ÿ“š Learning: 2025-09-01T14:52:42.625Z
Learnt from: dolong2
PR: dolong2/dcd#703
File: src/main/kotlin/com/dcd/server/core/domain/volume/model/Volume.kt:1-12
Timestamp: 2025-09-01T14:52:42.625Z
Learning: dolong2 prefers to implement validation logic (such as path validation for Volume.physicalPath) in the business logic layer rather than in domain model init blocks, keeping domain models as simple data containers.

Applied to files:

  • src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt
๐Ÿ“š Learning: 2025-09-01T14:57:07.656Z
Learnt from: dolong2
PR: dolong2/dcd#703
File: src/test/resources/data.sql:28-30
Timestamp: 2025-09-01T14:57:07.656Z
Learning: dolong2 prefers to implement deletion validation in business logic rather than using database CASCADE constraints. For volume deletion, they want to validate if any mounts exist before allowing deletion, preventing accidental data loss while maintaining explicit control over the deletion process.

Applied to files:

  • src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt
๐Ÿ”‡ Additional comments (2)
src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt (1)

44-49: mountPath ์ž…๋ ฅ ๊ฒ€์ฆ ์ถ”๊ฐ€ ๊ถŒ์žฅ

๋น„์ฆˆ๋‹ˆ์Šค ๊ณ„์ธต์—์„œ mountPath์— ๋Œ€ํ•ด ๋นˆ๊ฐ’/๊ณต๋ฐฑ, ์ ˆ๋Œ€๊ฒฝ๋กœ(/๋กœ ์‹œ์ž‘), ์ƒ์œ„ ๋””๋ ‰ํ† ๋ฆฌ ์ฐธ์กฐ("..") ๋“ฑ ๊ฒ€์ฆ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.
ํŒŒ์ผ: src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt (๋ผ์ธ 44-49)

     val volumeMount = VolumeMount(
         application = application,
         volume = volume,
-        mountPath = mountVolumeReqDto.mountPath,
+        mountPath = mountVolumeReqDto.mountPath.trim().also {
+            require(it.isNotEmpty()) { "mountPath must not be blank" }
+            require(it.startsWith("/")) { "mountPath must be absolute" }
+            require(!it.contains("..")) { "mountPath must not contain '..'" }
+        },
         readOnly = mountVolumeReqDto.readOnly
     )
src/main/kotlin/com/dcd/server/persistence/volume/entity/VolumeMountJpaEntity.kt (1)

1-37: kotlin-jpa ํ”Œ๋Ÿฌ๊ทธ์ธยทJakarta ์–ด๋…ธํ…Œ์ด์…˜ ์ ์šฉ ํ™•์ธ (build.gradle.kts)

  • build.gradle.kts์— kotlin("plugin.jpa") version "1.8.22" ์ ์šฉ๋˜์–ด ์žˆ์Œ.
  • ์—”ํ‹ฐํ‹ฐ๊ฐ€ jakarta.persistence๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ kotlin-jpa๊ฐ€ jakarta ์–ด๋…ธํ…Œ์ด์…˜์„ ๋Œ€์ƒ์œผ๋กœ no-arg/all-open์„ ์ ์šฉํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ฑฐ๋‚˜, ๋ช…์‹œ์ ์œผ๋กœ noArg/allOpen์— jakarta.persistence ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ Hibernate bytecode enhancer๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•จ. (kotlinlang.org)
  • ์ €์žฅ์†Œ ๊ฒ€์ƒ‰์—์„œ Hibernate bytecode enhancer ์„ค์ •์€ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์Œ โ€” ๋นŒ๋“œ/ํ…Œ์ŠคํŠธ๋กœ no-arg ์ƒ์„ฑ(๊ธฐ๋ณธ ์ƒ์„ฑ์ž)๊ณผ ํ”„๋ก์‹œ ๋™์ž‘์„ ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•  ๊ฒƒ.

@dolong2 dolong2 merged commit aa06f2a into develop Sep 14, 2025
2 checks passed
@dolong2 dolong2 deleted the feature/un-mount-volume-api branch September 14, 2025 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1๏ธโƒฃ Priority: ์ƒ ์šฐ์„ ์ˆœ์œ„ ์ƒ โœจ Feature ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ

Projects

None yet

Development

Successfully merging this pull request may close these issues.

๋งˆ์šดํŠธ ํ•ด์ œ API ์ถ”๊ฐ€

2 participants