[RELEASE] v0.2.1#108
Conversation
…-및-전역-soft-delete-적용 [REFACTOR] DB Flush 호출 최소화 및 전역 Soft Delete 적용
[REFACTOR] 서버 리소스 스펙 최적화
…위한-환경-설정 [FEAT] GC 로그 및 JVM 프로파일링 환경 설정
Walkthrough이 PR은 채팅·방 도메인의 삭제를 소프트 삭제로 마이그레이션하고, Java Flight Recording 모니터링과 PostgreSQL 지원을 추가하며, k6 로드 테스트 인프라를 구성하는 대규모 변경입니다. 소프트 삭제 패턴 구현부터 인프라 업그레이드까지 여러 독립적인 기능 영역을 포함합니다. Changes소프트 삭제 패턴 구현
인프라 및 배포 업그레이드
🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/main/java/com/project/dorumdorum/domain/chat/domain/repository/ChatMessageRepository.java (1)
13-21:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
decreaseUnreadCount에 소프트 삭제 필터가 빠져 있어요.이번 변경으로
ChatMessage에 소프트 삭제가 도입되었지만, 바로 위의decreaseUnreadCountUPDATE 쿼리는m.deletedAt IS NULL조건이 없어서 이미 소프트 삭제된 메시지의unreadCount까지 함께 감소시킬 수 있어요. 동일 채팅방에 과거 삭제된 이력이 남아 있는 경우 불필요한 쓰기가 발생하고, 만약 향후 복원 정책이 생긴다면 카운터가 음수로 떨어진 채 노출될 위험도 있습니다.
AND m.deletedAt IS NULL한 줄을 추가해 두면 의미상 일관되고 안전합니다. (참고:@SQLRestriction은 Hibernate가 생성하는 SELECT에만 적용되고 JPQLUPDATE에는 자동 적용되지 않아, 명시적 조건이 필요합니다.)♻️ 제안 수정
`@Modifying`(clearAutomatically = true, flushAutomatically = true) `@Query`("UPDATE ChatMessage m SET m.unreadCount = m.unreadCount - 1 " + "WHERE m.chatRoom.chatRoomNo = :chatRoomNo " + "AND m.createdAt > :fromTime " + "AND m.senderNo != :userNo " + - "AND m.unreadCount > 0") + "AND m.unreadCount > 0 " + + "AND m.deletedAt IS NULL") int decreaseUnreadCount(`@Param`("chatRoomNo") String chatRoomNo, `@Param`("fromTime") LocalDateTime fromTime, `@Param`("userNo") String userNo);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/project/dorumdorum/domain/chat/domain/repository/ChatMessageRepository.java` around lines 13 - 21, The JPQL UPDATE in decreaseUnreadCount (in ChatMessageRepository) is missing the soft-delete filter and can modify unreadCount for soft-deleted ChatMessage rows; update the query in the decreaseUnreadCount method (the `@Query` on ChatMessage) to include "AND m.deletedAt IS NULL" so the UPDATE only affects non-deleted messages..github/workflows/cicd.yml (2)
100-107:⚠️ Potential issue | 🟠 Major | ⚡ Quick win배포 대상을 mutable tag/branch head가 아니라 커밋 SHA로 고정해 주세요.
지금은 이미지를
:pinpoint로만 push/pull하고, 배포 시점에는production브랜치 HEAD를 다시 clone 하고 있습니다. 이 조합이면 production에 연속 push가 들어오거나 workflow를 재실행할 때, 이번 run이 빌드한 결과물이 아니라 더 최신/이전 커밋의 이미지·compose를 섞어서 배포할 수 있습니다.pinpoint-${{ github.sha }}같은 불변 태그와${{ github.sha }}checkout으로 같은 스냅샷을 배포하도록 맞추는 편이 안전합니다.예시
- name: Build and push pinpoint image uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile.pinpoint push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/dorumdorum-be:pinpoint + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/dorumdorum-be:pinpoint + ${{ secrets.DOCKERHUB_USERNAME }}/dorumdorum-be:pinpoint-${{ github.sha }} - name: Deploy over SSH uses: appleboy/ssh-action@v1.2.0 env: - IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/dorumdorum-be:pinpoint + IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/dorumdorum-be:pinpoint-${{ github.sha }} + GITHUB_SHA: ${{ github.sha }} with: - envs: IMAGE,DOCKERHUB_USERNAME,DOCKERHUB_TOKEN,CONTAINER_NAME,GITHUB_TOKEN,REPO_FULL_NAME + envs: IMAGE,DOCKERHUB_USERNAME,DOCKERHUB_TOKEN,CONTAINER_NAME,GITHUB_TOKEN,REPO_FULL_NAME,GITHUB_SHA script: | - git clone --depth 1 -b production "https://x-access-token:${GITHUB_TOKEN}`@github.com/`${REPO_FULL_NAME}.git" temp_repo + git clone --depth 1 "https://x-access-token:${GITHUB_TOKEN}`@github.com/`${REPO_FULL_NAME}.git" temp_repo ( cd temp_repo + git fetch --depth 1 origin "${GITHUB_SHA}" + git checkout --detach "${GITHUB_SHA}" git -c url."https://x-access-token:${GITHUB_TOKEN}`@github.com/`".insteadOf="https://github.com/" \ submodule update --init --recursive --depth 1 )Also applies to: 121-132, 165-170
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/cicd.yml around lines 100 - 107, The workflow pushes images using docker/build-push-action@v6 with a mutable tag (tags: ${{ secrets.DOCKERHUB_USERNAME }}/dorumdorum-be:pinpoint) which can cause mismatched deploys; change the image tag to an immutable one using the commit SHA (e.g., pinpoint-${{ github.sha }}) when building/pushing, update any other docker/build-push-action steps likewise, and ensure subsequent deployment steps that checkout code or reference the image use actions/checkout with ref: ${{ github.sha }} (or otherwise reference the same ${{ github.sha }} tag) so the built image and the checked-out compose/manifests match the exact same commit.
125-125:⚠️ Potential issue | 🟠 Major | ⚡ Quick win별도 private submodule이면
GITHUB_TOKEN인증 실패 가능성이 큽니다현재 워크플로우가
GITHUB_TOKEN만으로git submodule update --init --recursive까지 수행하는데, submodule이 워크플로우가 실행되는 저장소와 다른 private 저장소라면 기본GITHUB_TOKEN권한 범위 때문에 클론/가져오기가 실패할 수 있습니다(보통 “접근 권한 없음/Repository not found”). 따라서 submodule 저장소까지 읽을 수 있는 PAT 또는 GitHub App 토큰을 별도 secret으로 두고 submodule fetch 단계에 사용하도록 구성해 주세요. (동일 이슈는 165-170 라인에도 적용됩니다.)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/cicd.yml at line 125, The workflow currently uses GITHUB_TOKEN for submodule operations which can fail for private submodules; create a new secret (e.g., SUBMODULE_PAT or SUBMODULE_ACCESS_TOKEN) and update the steps that run git submodule update --init --recursive and any actions/checkout steps to pass that secret as the token input instead of GITHUB_TOKEN (or call git with an authenticated URL using the PAT); specifically replace uses of GITHUB_TOKEN around the git submodule update --init --recursive step and the other occurrence noted (lines ~165-170) so the submodule fetch uses secrets.SUBMODULE_PAT or an equivalent GitHub App token. Ensure the new secret is documented and referenced in the job/env where token is needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/cicd.yml:
- Around line 44-47: The CI is always skipping integration tests by passing the
flag "-PskipIntegrationTests" in the "Run tests" step (./gradlew test
-PskipIntegrationTests --no-daemon); remove that unconditional skip and instead
either (a) make the flag conditional based on the pipeline context (e.g., only
add -PskipIntegrationTests for non-production branches/PR types using a GitHub
Actions if/conditional or an environment variable) or (b) create a separate
required job (e.g., "integration-tests") that runs ./gradlew test without
-PskipIntegrationTests for production-targeted pushes/PRs; update the "Run
tests" step and/or add the new job so integration tests run for
production-relevant runs while still allowing faster checks on other branches.
In @.gitmodules:
- Line 3: Update the submodule URL in .gitmodules from the HTTPS entry (url =
https://github.com/DorumDorum/secrets) to the SSH form (e.g., url =
git@github.com:DorumDorum/secrets.git), then update the repo to use the new URL
by running git submodule sync && git submodule update --init --recursive; also
ensure your CI is configured with the corresponding GitHub deploy key or SSH key
with proper access and that any CI job clones submodules using SSH (or add the
key to the runner) so authentication works in pipeline runs.
In `@docker-compose.yml`:
- Around line 162-163: Compose 파일의 k6 서비스가 이미지 태그 grafana/k6:latest 를 사용해 릴리스
재현성을 깨뜨리고 있으니 k6 서비스의 image 설정(서비스 이름 "k6"과 image 항목)을 의도한 안정적인 버전으로 고정하세요; 예를
들어 image 값을 최신 대신 버전 고정 문자열으로 바꾸거나 환경변수 K6_IMAGE 기본값을
grafana/k6:<desired-version>처럼 명시적으로 설정해 재현 가능한 빌드를 보장하도록 수정하세요.
In `@Dockerfile`:
- Line 22: The ENTRYPOINT line sets JFR_SETTINGS default to "profile" which
incurs runtime overhead; update the DEFAULT_JAVA_OPTS substitution to use
settings=${JFR_SETTINGS:-default} instead of profile so production containers
use the lightweight JFR "default" by default and tests can still enable
profiling via JFR_SETTINGS=profile; modify the ENTRYPOINT string where
DEFAULT_JAVA_OPTS and JFR_SETTINGS are referenced (the DEFAULT_JAVA_OPTS
variable construction in the ENTRYPOINT) to make this change.
- Line 22: The ENTRYPOINT's DEFAULT_JAVA_OPTS uses a static default for
JFR_RUN_ID which causes JFR output filename
(filename=/app/logs/jfr/dorumdorum-${JFR_RUN_ID:-cloud-load-test}.jfr) to be
overwritten on restarts; change the default expansion for JFR_RUN_ID in the
ENTRYPOINT (and related DEFAULT_JAVA_OPTS/EFFECTIVE_JAVA_OPTS) to include a
unique suffix such as a timestamp or container identifier (e.g. date
+%Y%m%d-%H%M%S or hostname/CONTAINER_ID) so the generated filename is unique per
run and preserves previous recordings.
In `@Dockerfile.pinpoint`:
- Around line 43-51: Extract the long DEFAULT_JAVA_OPTS/EFFECTIVE_JAVA_OPTS
construction and java launch logic out of the Dockerfile ENTRYPOINT into a
shared entrypoint script (entrypoint.sh) and update this Dockerfile to COPY that
script and use ENTRYPOINT ["sh","-c","/app/entrypoint.sh"] so both images share
one JFR/JAVA_OPTS source; move the -javaagent and -D... flags into that script
as written (refer to DEFAULT_JAVA_OPTS, EFFECTIVE_JAVA_OPTS, ENTRYPOINT and the
java launch command) and ensure the JFR filename pattern (currently
dorumdorum-${JFR_RUN_ID:-cloud-load-test}.jfr) generates a true unique filename
(e.g., prefer JFR_RUN_ID with a timestamp fallback) to avoid overwriting JFR
files when refactoring.
In `@monitoring/grafana/provisioning/dashboards/json/k6-jvm-gc-load-test.json`:
- Around line 175-180: The PromQL in this dashboard uses k6 metric names that
may include incorrect `_seconds_` infixes; update any queries referencing
metrics like k6_http_req_duration_seconds_p95 or
k6_http_req_duration_seconds_p99 to use k6_http_req_duration_p95 and
k6_http_req_duration_p99 (and similarly for other duration quantiles) while
keeping k6_http_req_failed_rate as-is; also verify the
K6_PROMETHEUS_RW_TREND_STATS export settings so the p95/p99 quantiles are
actually exported and adjust the panel queries to match the exported metric
names (e.g., replace *_duration_seconds_* → *_duration_* and ensure testid label
selectors remain unchanged).
- Line 240: 현재 대시보드의 PromQL 식
(sum(jvm_memory_used_bytes{application="dorumdorum",area="heap"}) /
sum(jvm_memory_max_bytes{application="dorumdorum",area="heap"}))는
application="dorumdorum" 라벨에 하드코딩되어 있는데, 프로메테우스의
static_configs.labels.application에 의해 이미 붙는지(또는 다른 환경에서 라벨 키가 다른지)를 확인하고,
Grafana가 바라보는 Prometheus 인스턴스가 어떤 라벨을 넣는지 확인한 뒤 식을 다음 중 하나로 수정하세요: 1) 애플리케이션을
대시보드 변수(예: $application)로 치환해 여러 환경에 대응하거나, 2) application 라벨을 제거해 라벨 없는 집계로
만들거나, 3) 필요하면 실제 라벨 키(예: job 또는 다른 키)로 교체하여 jvm_memory_used_bytes 및
jvm_memory_max_bytes 선택자가 올바른 라벨과 일치하게 하세요; 관련 식 문자열과
메트릭명(jvm_memory_used_bytes, jvm_memory_max_bytes, area="heap")을 찾아 변경하면 됩니다.
In
`@src/main/java/com/project/dorumdorum/domain/checklist/domain/entity/ChecklistBase.java`:
- Around line 133-135: delete() currently overwrites deletedAt every call losing
the original deletion timestamp; modify ChecklistBase.delete() to be idempotent
by setting deletedAt only if it is null (i.e., guard with a check like “if
(deletedAt == null) { deletedAt = LocalDateTime.now(); }”) so repeated calls
preserve the first deletion time while keeping the same method and field names
(ChecklistBase.delete, deletedAt).
In
`@src/test/java/com/project/dorumdorum/domain/chat/integration/ChatTransactionAtomicityIntegrationTest.java`:
- Around line 349-362: The duplicated truncateTables() helper (containing the
multi-table TRUNCATE ... RESTART IDENTITY CASCADE SQL) should be extracted to a
single reusable test utility to avoid sync errors; create a shared component
(e.g., testsupport.DatabaseCleaner with a TRUNCATE_SQL constant and a
truncateAll() method) or an abstract base test class (e.g.,
AbstractPersistenceCleanupTest) that has JdbcTemplate injected and exposes
truncateTables()/truncateAll(), then replace the local truncateTables()
implementations in ChatTransactionAtomicityIntegrationTest,
DeleteRoomPersistenceIntegrationTest, and KickRoommatePersistenceIntegrationTest
with calls to the shared DatabaseCleaner.truncateAll() or the base class method.
In
`@src/test/java/com/project/dorumdorum/domain/room/integration/FlushRemovalIntegrationTest.java`:
- Line 82: The test class FlushRemovalIntegrationTest currently declares
chatRoomMemberService with `@MockitoSpyBean` but never stubs or verifies it;
remove the unused partial spy to avoid breaking ApplicationContext caching by
either (A) change the annotation to `@Autowired` on the chatRoomMemberService
field if you intend to use the real bean, or (B) if you intended to control
behavior, keep `@MockitoSpyBean` and add explicit stubbing
(doReturn(...).when(chatRoomMemberService)...) or verify(...) calls in the test;
pick one of these and update the chatRoomMemberService declaration and tests
accordingly.
- Around line 186-194: 테스트의 현재 assertion은 roommateRepository.findByUserNo가 결과를
비우는지를 확인해 소프트 삭제 필터링 여부만 검증하므로, 검증 의도를 명확히 하세요: либо DisplayName과 assertion
메시지에서 "제거된다"를 "조회되지 않는다"로 바꾸어 findByUserNo의 동작을 명세하고 그대로 유지하거나,
deleteRoomUseCase.execute 호출 후 JdbcTemplate를 사용해 Roommate 테이블의 해당 row를 직접 조회(예:
SELECT deleted_at FROM roommate WHERE user_no = ?)하여 deleted_at이 NOT NULL인지 확인해
실질적 소프트 삭제가 이루어졌음을 검증하세요; 관련 식별자는
delete_RoommateCleanup_PersistedWithoutExplicitFlush 테스트 메소드,
roommateRepository.findByUserNo 호출, 그리고 deleteRoomUseCase.execute입니다.
- Around line 214-227: The truncateTables method in FlushRemovalIntegrationTest
currently omits the users (user) table which can leave residual User rows
affecting assertions like findByUserNo; update truncateTables (the
jdbcTemplate.execute TRUNCATE block) to include the users (or user) table name
in the list and then extract this method into a shared testsupport utility
(e.g., a common truncateTables helper) so other tests can reuse it and avoid
per-test maintenance whenever entities with `@SQLRestriction` are added.
- Around line 110-124: The fixture sets kickRoom.plusCurrentMate() only once but
then persists two Roommate entries (HOST and MEMBER), causing currentMateCount
to be 1 while two roommates exist; update the setup so kickRoom.currentMateCount
matches the two saved roommates—e.g., call kickRoom.plusCurrentMate() twice (or
otherwise set/increment currentMateCount to 2) before saving with
roomRepository.saveAndFlush(kickRoom) so the test
kick_CurrentMateCountDecrement_PersistedWithoutExplicitFlush accurately reflects
the HOST+MEMBER state.
---
Outside diff comments:
In @.github/workflows/cicd.yml:
- Around line 100-107: The workflow pushes images using
docker/build-push-action@v6 with a mutable tag (tags: ${{
secrets.DOCKERHUB_USERNAME }}/dorumdorum-be:pinpoint) which can cause mismatched
deploys; change the image tag to an immutable one using the commit SHA (e.g.,
pinpoint-${{ github.sha }}) when building/pushing, update any other
docker/build-push-action steps likewise, and ensure subsequent deployment steps
that checkout code or reference the image use actions/checkout with ref: ${{
github.sha }} (or otherwise reference the same ${{ github.sha }} tag) so the
built image and the checked-out compose/manifests match the exact same commit.
- Line 125: The workflow currently uses GITHUB_TOKEN for submodule operations
which can fail for private submodules; create a new secret (e.g., SUBMODULE_PAT
or SUBMODULE_ACCESS_TOKEN) and update the steps that run git submodule update
--init --recursive and any actions/checkout steps to pass that secret as the
token input instead of GITHUB_TOKEN (or call git with an authenticated URL using
the PAT); specifically replace uses of GITHUB_TOKEN around the git submodule
update --init --recursive step and the other occurrence noted (lines ~165-170)
so the submodule fetch uses secrets.SUBMODULE_PAT or an equivalent GitHub App
token. Ensure the new secret is documented and referenced in the job/env where
token is needed.
In
`@src/main/java/com/project/dorumdorum/domain/chat/domain/repository/ChatMessageRepository.java`:
- Around line 13-21: The JPQL UPDATE in decreaseUnreadCount (in
ChatMessageRepository) is missing the soft-delete filter and can modify
unreadCount for soft-deleted ChatMessage rows; update the query in the
decreaseUnreadCount method (the `@Query` on ChatMessage) to include "AND
m.deletedAt IS NULL" so the UPDATE only affects non-deleted messages.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8ed2d60d-b10f-41e6-a4fd-1d13c4437a0a
⛔ Files ignored due to path filters (1)
load-testing/README.mdis excluded by!**/*.md
📒 Files selected for processing (44)
.dockerignore.github/workflows/cicd.yml.gitignore.gitmodulesDockerfileDockerfile.pinpointbuild.gradledocker-compose.ymlload-testing/k6/.gitkeepmonitoring/grafana/provisioning/dashboards/json/k6-jvm-gc-load-test.jsonsecretssrc/main/java/com/project/dorumdorum/domain/chat/domain/entity/ChatMessage.javasrc/main/java/com/project/dorumdorum/domain/chat/domain/entity/ChatRoom.javasrc/main/java/com/project/dorumdorum/domain/chat/domain/entity/ChatRoomMember.javasrc/main/java/com/project/dorumdorum/domain/chat/domain/repository/ChatMessageRepository.javasrc/main/java/com/project/dorumdorum/domain/chat/domain/repository/ChatRoomMemberRepository.javasrc/main/java/com/project/dorumdorum/domain/chat/domain/repository/ChatRoomRepository.javasrc/main/java/com/project/dorumdorum/domain/chat/domain/service/ChatRoomMemberService.javasrc/main/java/com/project/dorumdorum/domain/chat/domain/service/ChatRoomService.javasrc/main/java/com/project/dorumdorum/domain/checklist/domain/entity/ChecklistBase.javasrc/main/java/com/project/dorumdorum/domain/checklist/domain/entity/RoomRule.javasrc/main/java/com/project/dorumdorum/domain/checklist/domain/repository/RoomRuleRepository.javasrc/main/java/com/project/dorumdorum/domain/room/application/usecase/DeleteRoomUseCase.javasrc/main/java/com/project/dorumdorum/domain/room/application/usecase/KickRoommateUseCase.javasrc/main/java/com/project/dorumdorum/domain/room/domain/entity/RoomLike.javasrc/main/java/com/project/dorumdorum/domain/room/domain/entity/RoomRequest.javasrc/main/java/com/project/dorumdorum/domain/room/domain/repository/RoomLikeRepository.javasrc/main/java/com/project/dorumdorum/domain/room/domain/repository/RoomRequestRepository.javasrc/main/java/com/project/dorumdorum/domain/room/domain/service/RoomRequestService.javasrc/main/java/com/project/dorumdorum/domain/room/domain/service/RoomService.javasrc/main/java/com/project/dorumdorum/domain/roommate/domain/entity/Roommate.javasrc/main/java/com/project/dorumdorum/domain/roommate/domain/service/RoommateService.javasrc/main/resources/application-prod.ymlsrc/main/resources/schema.sqlsrc/test/java/com/project/dorumdorum/domain/chat/integration/ChatTransactionAtomicityIntegrationTest.javasrc/test/java/com/project/dorumdorum/domain/chat/unit/service/ChatRoomMemberServiceTest.javasrc/test/java/com/project/dorumdorum/domain/chat/unit/service/ChatRoomServiceTest.javasrc/test/java/com/project/dorumdorum/domain/room/integration/DeleteRoomPersistenceIntegrationTest.javasrc/test/java/com/project/dorumdorum/domain/room/integration/FlushRemovalIntegrationTest.javasrc/test/java/com/project/dorumdorum/domain/room/integration/KickRoommatePersistenceIntegrationTest.javasrc/test/java/com/project/dorumdorum/domain/room/unit/service/RoomRequestServiceTest.javasrc/test/java/com/project/dorumdorum/domain/room/unit/usecase/DeleteRoomUseCaseTest.javasrc/test/java/com/project/dorumdorum/domain/room/unit/usecase/KickRoommateUseCaseTest.javasrc/test/java/com/project/dorumdorum/domain/roommate/unit/service/RoommateServiceTest.java
💤 Files with no reviewable changes (3)
- src/main/java/com/project/dorumdorum/domain/room/domain/service/RoomService.java
- src/main/java/com/project/dorumdorum/domain/room/application/usecase/KickRoommateUseCase.java
- src/test/java/com/project/dorumdorum/domain/room/unit/usecase/KickRoommateUseCaseTest.java
📝 Pull Request Template
📌 제목
✅ PR 체크리스트
📜 기타
Summary by CodeRabbit
릴리스 노트
New Features
Bug Fixes
Chores