Skip to content

[feature] StudyRoom 기능 구현#77

Merged
Roysung0625 merged 82 commits intodevelopfrom
feature/studyroom
Aug 6, 2025
Merged

[feature] StudyRoom 기능 구현#77
Roysung0625 merged 82 commits intodevelopfrom
feature/studyroom

Conversation

@Roysung0625
Copy link
Contributor

@Roysung0625 Roysung0625 commented Aug 6, 2025

🚀 What's this PR about?

작업 내용 요약

  • 스터디룸 및 스터디홀 관리 시스템의 전면적인 구현으로, CRUD 기능, 예약 시스템, 지도 연동, 관리자 기능, 성능 최적화를 포함한 종합적인 스터디 공간 관리 플랫폼을 개발합니다.

<br>

🛠️ What's been done?

핵심 기능 구현

  • 스터디홀/스터디룸 CRUD 시스템: 완전한 생성, 조회, 수정, 삭제 기능 구현
  • 예약 시스템: 동시성 처리를 고려한 예약 생성/조회/삭제 기능 및 시간대별 예약 관리
  • 카카오 지도 연동: 위치 기반 스터디홀 검색 및 거리 계산 기능 구현
  • 관리자 기능: 스터디룸, 스터디홀, 예약 관리를 위한 전용 관리자 API 제공

성능 최적화 및 안정성 강화

  • Spring Cache 도입: 스터디룸/홀 조회 성능 대폭 개선
  • N+1 문제 해결: @BatchSize 적용으로 쿼리 최적화
  • 동시성 처리: Optimistic Lock을 통한 예약 충돌 방지
  • 논리적 삭제: DB 인덱스 추가로 삭제된 데이터 조회 성능 개선

시스템 아키텍처 개선

  • 예외 처리 체계 구축: Global Exception Handler 및 커스텀 예외 클래스 구현
  • API 응답 표준화: ApiResponse 구조로 일관된 응답 형식 제공
  • 인증/인가 강화: 패스워드 변경 기능 및 토큰 관리 개선

<br>

🧪 Testing Details

테스트 코드 및 결과

  • 7개의 테스트 파일 추가로 포괄적인 테스트 커버리지 확보
  • 동시성 테스트: StudyRoomReservationConcurrencyTest로 예약 시스템의 동시 접근 안정성 검증
  • 서비스 계층 테스트: 548개 이상의 테스트 케이스로 비즈니스 로직 검증
  • 통합 테스트: 관리자 기능 및 예약 시스템 전체 플로우 테스트

테스트 성과

  • 예약 동시성 처리 안정성 확인
  • 캐시 기능 정상 동작 검증
  • API 엔드포인트 전체 검증 완료

<br>

👀 Checkpoints for Reviewers

리뷰 시 확인할 사항

  • 동시성 처리 로직: Optimistic Lock 적용이 적절한지 확인
  • 캐시 무효화 전략: 데이터 수정 시 캐시 갱신 로직 검토
  • API 보안: 관리자 권한 검증 및 사용자 인증 로직 점검
  • 성능 개선 효과: N+1 문제 해결 및 인덱스 적용 결과 검증

논의하고 싶은 주제

  • 카카오 지도 API 요청 제한 및 에러 처리 방안
  • 예약 시스템의 확장성 (다중 예약, 대기열 시스템 등)
  • 캐시 TTL 설정 및 메모리 사용량 최적화 방안

<br>

📚 References & Resources

  • Spring Cache Documentation: 캐시 구현 및 최적화 참고
  • Kakao Map API: 지도 연동 및 위치 검색 기능 구현 참고
  • JPA Optimistic Locking: 동시성 제어 구현 참고
  • Global Exception Handler 패턴: 예외 처리 표준화 참고

<br>

🎯 Related Issues

Summary by CodeRabbit

  • 신규 기능

    • 스터디홀 및 스터디룸 관리, 위치 기반 검색, 예약 기능 등 스터디룸 도메인의 전체 관리자 및 사용자 API가 추가되었습니다.
    • 지도 기반 스터디홀 검색, 주소/이름 검색, 거리 계산, 예약 가능 시간대 조회 등 다양한 위치 및 예약 관련 기능이 제공됩니다.
    • 관리자용 예약 목록 조회 및 강제 취소, 스터디홀/룸 생성·수정·삭제, 상세 정보 조회 기능이 포함되었습니다.
    • Kakao Map API 연동을 통한 주소-좌표 변환 및 거리 계산 기능이 추가되었습니다.
  • 버그 수정

    • 예약 생성 시 Location 헤더가 응답에 포함되도록 개선되었습니다.
  • 문서화

    • Swagger/OpenAPI를 통한 API 문서화가 이루어졌으며, HTTP 테스트 스크립트가 추가되어 API 사용 예시를 제공합니다.
  • 리팩토링/스타일

    • 엔티티 구조가 위치 정보 통합, 소프트 삭제, 유효성 검증 등으로 개선되었습니다.
    • 서비스 레이어에 캐싱, 트랜잭션, 예외 처리, 외부 지도 API 연동이 적용되었습니다.
    • Redis 캐시 및 RestTemplate 설정이 보완되어 직렬화 및 HTTP 통신 안정성이 향상되었습니다.
  • 테스트

    • 서비스 및 관리자 기능에 대한 단위 테스트가 추가되어 주요 시나리오와 예외 케이스를 검증합니다.
  • 기타

    • Jackson, Spring Retry 등 의존성이 추가되었고, Redis 및 RestTemplate 설정이 보완되었습니다.
    • 더미 데이터 SQL 및 다양한 DTO/예외 클래스가 신규로 도입되었습니다.

mappedBy = "studyRoom" -> "studyHall"로 변경
- 테스트 시나리오:
  - 스터디 홀 생성 성공
  - 전체 스터디 홀 조회 성공
  - ID로 스터디 홀 조회 성공
  - ID로 스터디 홀 조회 실패 (존재하지 않는 ID)
- 스터디 홀 생성
- 전체 스터디 홀 조회
- 특정 스터디 홀 조조회
비즈니스 로직을 검증하는 단위 테스트를 추가
…e-read

[feat] 관리자 스터디 홀 생성/조회 기능 구현
…e-read

[feat] 관리자 스터디 룸 생성/조회 기능 구현
- StudyHall 엔티티에 수정(update) 및 논리적 삭제(delete) 메서드 추가
- StudyHallService에 관련 비즈니스 로직 추가
- 논리적 삭제를 반영하여 조회 로직 수정
- AdminStudyRoomController에 PUT, DELETE 엔드포인트 추가
- DTO 유효성 검사를 위해 @Valid 애너테이션 적용
- StudyHallService의 수정, 삭제 로직에 대한 단위 테스트 추가
- 논리적 삭제 로직을 반영하여 기존 조회 테스트 수정
- 통합 테스트(.http 파일)에 PUT, DELETE 시나리오 추가
- Mapper에 리스트 변환 메서드(toResponseDtos) 추가
- Controller의 stream().map() 로직을 Mapper 직접 호출로 변경하여 코드 간소화
- 엔티티 클래스에 있던 update 메서드를 StudyHallMapper로 이전하여 역할을 분리하고, null 값을 제외한 선택적 필드 업데이트 로직을 구현
jam9582 and others added 20 commits August 4, 2025 14:54
- boolean 타입인 isDeleted 필드가 JSON으로 변환될 때 발생하는 필드 이름 불일치 문제를 해결
…-improvements

[refactor] 스터디 룸 성능 개선
[feat] studyroom 지도 전체 crud 구현 완료
…improvement

[Feat] StudyRoom Reservation TestCode 작성 및 미흡부분 개선
Redis 캐싱 시 LocalDateTime 타입을 직렬화하지 못해 SerializationException이 발생하는 문제를 해결

- jackson-datatype-jsr310 의존성을 추가하여 Java 8 날짜 타입을 지원
- CacheManager Bean이 JavaTimeModule이 등록된 ObjectMapper를 사용하도록 RedisConfig를 수정하여, 캐시 데이터가 LocalDateTime을 올바르게 처리하도록 변경
…ation

[fix] Redis 캐싱 시 LocalDateTime 직렬화 오류 수정
@coderabbitai
Copy link

coderabbitai bot commented Aug 6, 2025

Walkthrough

스터디홀 및 스터디룸 도메인에 대한 전체 CRUD, 위치 기반 검색, 예약, 관리자 기능이 대규모로 추가 및 리팩터링되었습니다. 엔티티, DTO, 서비스, 컨트롤러, 예외, 리포지토리, 매퍼, 설정, 테스트 코드가 신규로 도입되거나 확장되었으며, 성능 개선(캐싱, 인덱싱, 배치 로딩), 외부 API 연동, 유효성 검증, 표준화된 응답 구조, 문서화 및 자동화 테스트가 포함됩니다.

Changes

Cohort / File(s) Change Summary
StudyHall & StudyRoom Domain: Entity, Repository, Service, DTO, Mapper, Exception, Projection
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyHall.java, .../entity/StudyRoom.java, .../entity/Location.java, .../repository/StudyHallRepository.java, .../repository/StudyRoomRepository.java, .../repository/StudyRoomReservationRepository.java, .../service/StudyHallService.java, .../service/StudyRoomService.java, .../service/StudyRoomReservationService.java, .../service/AdminReservationService.java, .../service/KakaoMapService.java, .../dto/request/StudyHallRequest.java, .../dto/request/StudyHallSearchRequest.java, .../dto/request/StudyRoomRequest.java, .../dto/request/ReservationCreateDto.java, .../dto/mapper/StudyHallMapper.java, .../dto/mapper/StudyRoomMapper.java, .../dto/response/StudyHallLocationResponse.java, .../dto/response/StudyHallResponse.java, .../dto/response/StudyRoomDetailResponse.java, .../dto/response/StudyRoomResponse.java, .../dto/response/TimeSlotResponse.java, .../dto/response/ReservationTimeResponse.java, .../dto/response/ReservationDetailDto.java, .../dto/response/ReservationListDto.java, .../exception/LocationServiceException.java, .../exception/StudyHallNotFoundException.java, .../exception/StudyRoomNotFoundException.java, .../projection/StudyHallWithDistanceProjection.java
엔티티 구조 개선(위치 필드 통합, 썸네일 추가, isDeleted 플래그, open/closeTime 타입 변경), CRUD 및 위치 기반 검색 서비스/리포지토리/프로젝션/예외/DTO/매퍼 도입, 예약 서비스/검증/예외/응답 DTO 확장, 관리자 예약 서비스 추가, 카카오맵 연동 및 거리 계산, 유효성 검증 강화, JPQL/Native 쿼리 추가
REST Controller & HTTP Test Script
.../controller/AdminStudyRoomController.java, .../controller/AdminReservationController.java, .../controller/StudyRoomMapController.java, .../controller/StudyRoomReservationController.java, .../controller/AdminStudyRoom.http, .../controller/studyRoomMap.http
관리자 및 사용자용 스터디홀/스터디룸/예약/지도 API 컨트롤러 신설, 표준화된 응답 구조, Swagger 문서화, HTTP 테스트 시나리오 추가
Configuration & Application
build.gradle, .../config/KakaoMapConfig.java, .../config/RedisConfig.java, .../config/RestTemplateConfig.java, .../HugmeExpApplication.java
의존성 추가(jackson-datatype-jsr310, spring-retry), 캐시/리트라이 활성화, 카카오맵/RestTemplate/Redis 설정, 직렬화 개선
SQL & Dummy Data
sql/study_hall_dummy_sql.sql
스터디홀/스터디룸 더미 데이터 및 쿼리 스크립트 추가
Test Code
.../service/AdminReservationServiceTest.java, .../service/StudyHallServiceTest.java, .../service/StudyRoomServiceTest.java, .../service/StudyRoomReservationServiceTest.java
서비스 계층 단위 테스트 신규 작성 및 시나리오별 검증(정상/예외/경계/성능)

Sequence Diagram(s)

sequenceDiagram
  participant Admin as 관리자
  participant AdminStudyRoomController
  participant StudyHallService
  participant StudyRoomService
  participant DB as DB

  Admin->>AdminStudyRoomController: 스터디홀 생성/조회/수정/삭제 요청
  AdminStudyRoomController->>StudyHallService: 비즈니스 로직 위임
  StudyHallService->>DB: StudyHall CRUD 쿼리
  StudyHallService-->>AdminStudyRoomController: 결과 반환
  AdminStudyRoomController-->>Admin: 응답

  Admin->>AdminStudyRoomController: 스터디룸 생성/수정/삭제 요청
  AdminStudyRoomController->>StudyRoomService: 비즈니스 로직 위임
  StudyRoomService->>DB: StudyRoom CRUD 쿼리
  StudyRoomService-->>AdminStudyRoomController: 결과 반환
  AdminStudyRoomController-->>Admin: 응답
Loading
sequenceDiagram
  participant User as 사용자
  participant StudyRoomMapController
  participant StudyHallService
  participant StudyRoomService
  participant KakaoMapService
  participant DB as DB

  User->>StudyRoomMapController: 근처 스터디홀 검색 요청(좌표, 반경)
  StudyRoomMapController->>StudyHallService: searchNearbyStudyHalls
  StudyHallService->>DB: Bounding Box + 거리 기반 쿼리
  StudyHallService-->>StudyRoomMapController: 위치/거리 포함 응답

  User->>StudyRoomMapController: 스터디룸 예약 가능 시간 조회
  StudyRoomMapController->>StudyRoomService: getAvailableTimeSlots
  StudyRoomService->>DB: 예약/룸/홀 정보 조회
  StudyRoomService-->>StudyRoomMapController: 시간대/가용여부 응답
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Assessment against linked issues

Objective Addressed Explanation
StudyRoomReservationService 단위 테스트 및 미흡 부분 개선 (#60)
스터디홀/스터디룸 지도 기반 CRUD 및 위치/예약/검색 기능 (#62)
스터디룸 성능 개선: N+1, 인덱싱, 캐싱 (#63)
스터디룸 썸네일 추가 (#49)
관리자 예약 관리(전체 조회/강제 취소) (#45)
관리자 스터디룸 수정/삭제 (#37)
스터디룸 예약 기능 및 예외 처리 (#35)

Assessment against linked issues: Out-of-scope changes

(해당 없음)

Possibly related issues

Possibly related PRs

Suggested labels

기능 개발, 리팩토링, 성능 개선

Suggested reviewers

  • seongjunin98
  • jam9582

Poem

🐇✨
새로 태어난 방과 홀,
지도 위에 꿈이 뛴다네.
예약도 척척, 썸네일 반짝—
캐시와 인덱스, 성능도 쑥쑥!
관리자의 손길 아래
토끼도 공부하고 싶어지는
신나는 코드의 숲!
🗺️📚🌟

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/studyroom

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link

github-actions bot commented Aug 6, 2025

Unit Test Results

359 tests   332 ✔️  13s ⏱️
  48 suites    27 💤
  48 files        0

Results for commit f52d035.

♻️ This comment has been updated with latest results.

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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd7647a and 4575c6b.

📒 Files selected for processing (45)
  • build.gradle (3 hunks)
  • sql/study_hall_dummy_sql.sql (1 hunks)
  • src/main/java/org/example/hugmeexp/HugmeExpApplication.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/controller/AdminReservationController.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/controller/AdminStudyRoom.http (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/controller/AdminStudyRoomController.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/controller/StudyRoomMapController.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/controller/StudyRoomReservationController.java (2 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/controller/studyRoomMap.http (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/mapper/StudyHallMapper.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/mapper/StudyRoomMapper.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/ReservationCreateDto.java (2 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyHallRequest.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyHallSearchRequest.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyRoomRequest.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationListDto.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationTimeResponse.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyHallLocationResponse.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyHallResponse.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyRoomDetailResponse.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyRoomResponse.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/TimeSlotResponse.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/entity/Location.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyHall.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyRoom.java (2 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/exception/LocationServiceException.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyHallNotFoundException.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyRoomNotFoundException.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/projection/StudyHallWithDistanceProjection.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/repository/StudyHallRepository.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/repository/StudyRoomRepository.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/repository/StudyRoomReservationRepository.java (2 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationService.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/service/KakaoMapService.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyHallService.java (1 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationService.java (5 hunks)
  • src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomService.java (1 hunks)
  • src/main/java/org/example/hugmeexp/global/common/config/KakaoMapConfig.java (1 hunks)
  • src/main/java/org/example/hugmeexp/global/common/config/RedisConfig.java (1 hunks)
  • src/main/java/org/example/hugmeexp/global/common/config/RestTemplateConfig.java (1 hunks)
  • src/test/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationServiceTest.java (1 hunks)
  • src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyHallServiceTest.java (1 hunks)
  • src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationServiceTest.java (1 hunks)
  • src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomServiceTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: reservationdetaildto와 같은 response dto에서는 @DaTa 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다....
Learnt from: Roysung0625
PR: Please-Hug/HugmeEXP_Backend#32
File: src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java:5-5
Timestamp: 2025-07-23T09:00:11.298Z
Learning: ReservationDetailDto와 같은 response DTO에서는 Data 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다.

Applied to files:

  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationListDto.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyHallResponse.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyRoomRequest.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/mapper/StudyRoomMapper.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/mapper/StudyHallMapper.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/TimeSlotResponse.java
  • src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationServiceTest.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationTimeResponse.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyRoomResponse.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/controller/StudyRoomReservationController.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/entity/Location.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyHallSearchRequest.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/ReservationCreateDto.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyHallRequest.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationService.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyRoomDetailResponse.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/controller/AdminReservationController.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyHallLocationResponse.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyRoom.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyHall.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/repository/StudyRoomReservationRepository.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationService.java
  • src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomService.java
🧬 Code Graph Analysis (14)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationListDto.java (1)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java (1)
  • Data (10-33)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java (1)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationListDto.java (1)
  • Data (10-27)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyRoomRequest.java (1)
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyRoom.java (1)
  • Getter (11-51)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyRoomResponse.java (2)
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyRoom.java (1)
  • Getter (11-51)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyRoomRequest.java (1)
  • Getter (15-30)
src/main/java/org/example/hugmeexp/domain/studyRoom/controller/AdminStudyRoomController.java (1)
src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyHallService.java (1)
  • Slf4j (24-228)
src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyHallServiceTest.java (1)
src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyHallNotFoundException.java (1)
  • StudyHallNotFoundException (9-13)
src/main/java/org/example/hugmeexp/global/common/config/KakaoMapConfig.java (3)
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyHall.java (1)
  • Getter (17-154)
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/Location.java (1)
  • Getter (12-118)
src/main/java/org/example/hugmeexp/global/common/config/RestTemplateConfig.java (1)
  • Configuration (9-23)
src/main/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationService.java (1)
src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyRoomReservationNotFoundException.java (1)
  • StudyRoomReservationNotFoundException (6-10)
src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomServiceTest.java (2)
src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyHallNotFoundException.java (1)
  • StudyHallNotFoundException (9-13)
src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyRoomNotFoundException.java (1)
  • StudyRoomNotFoundException (9-13)
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyRoom.java (2)
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyHall.java (1)
  • Getter (17-154)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyRoomRequest.java (1)
  • Getter (15-30)
src/test/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationServiceTest.java (1)
src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyRoomReservationNotFoundException.java (1)
  • StudyRoomReservationNotFoundException (6-10)
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyHall.java (4)
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyRoom.java (1)
  • Getter (11-51)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyHallLocationResponse.java (1)
  • Getter (11-62)
src/main/java/org/example/hugmeexp/domain/studyRoom/entity/Location.java (1)
  • Getter (12-118)
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyHallRequest.java (1)
  • Builder (14-47)
src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationService.java (1)
src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomService.java (1)
  • Service (25-202)
src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomService.java (2)
src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyHallNotFoundException.java (1)
  • StudyHallNotFoundException (9-13)
src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyRoomNotFoundException.java (1)
  • StudyRoomNotFoundException (9-13)
🪛 GitHub Actions: Back-End CI
src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationServiceTest.java

[error] 82-82: Compilation error: cannot find symbol method simpleAddress(String) in StudyHallBuilder.

src/test/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationServiceTest.java

[error] 41-41: Compilation error: cannot find symbol method simpleAddress(String) in StudyHallBuilder.

🔇 Additional comments (85)
src/main/java/org/example/hugmeexp/HugmeExpApplication.java (1)

5-5: LGTM! 캐싱 및 재시도 기능 활성화

@EnableCaching@EnableRetry 어노테이션이 올바르게 추가되어 성능 최적화와 낙관적 락 실패 시 재시도 로직을 지원합니다. 해당 import 구문도 적절히 포함되었습니다.

Also applies to: 7-7, 9-9, 11-11

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java (1)

10-10: 이전 선호사항과의 불일치 확인 필요

검색된 학습 내용에 따르면, PR #32에서 ReservationDetailDto와 같은 response DTO에서는 @Data 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다고 기록되어 있습니다.

현재 변경사항이 의도적인 정책 변경인지, 아니면 이전 선호사항을 고려해야 하는지 확인해 주세요.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationListDto.java (1)

10-10: ReservationDetailDto와 동일한 이전 선호사항 불일치

ReservationDetailDto와 마찬가지로, 이전 학습 내용에서 response DTO에 @Data 어노테이션 사용을 지양한다고 기록되어 있습니다.

두 DTO 모두 동일한 변경사항이 적용된 것으로 보아 의도적인 정책 변경일 가능성이 있지만, 확인이 필요합니다.

build.gradle (3)

33-34: LGTM! 포맷팅 개선

JWT 의존성에서 불필요한 공백이 제거되어 코드 가독성이 향상되었습니다.


50-50: LGTM! Java 8 날짜/시간 타입 지원 추가

jackson-datatype-jsr310 의존성이 추가되어 LocalDateTime 등 Java 8 날짜/시간 타입의 JSON 직렬화/역직렬화를 올바르게 처리할 수 있습니다. RestTemplateConfig의 ObjectMapper 설정과 일치합니다.


64-65: LGTM! Spring Retry 지원 추가

spring-retry 의존성이 추가되어 메인 애플리케이션의 @EnableRetry 어노테이션과 예약 서비스의 낙관적 락 재시도 로직을 지원합니다.

src/main/java/org/example/hugmeexp/global/common/config/RestTemplateConfig.java (2)

12-15: LGTM! RestTemplate 빈 설정

외부 API 호출을 위한 기본적인 RestTemplate 빈이 올바르게 구성되었습니다. KakaoMapService에서 활용될 것으로 보입니다.


17-22: LGTM! 카카오맵 전용 ObjectMapper 설정

JavaTimeModule이 등록된 전용 ObjectMapper가 잘 구성되었습니다. 빈 이름도 명확하고, 카카오맵 API 응답의 날짜/시간 필드를 올바르게 처리할 수 있습니다. build.gradle의 jackson-datatype-jsr310 의존성과도 일치합니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/controller/StudyRoomReservationController.java (1)

23-23: LGTM!

URI 임포트 추가가 적절합니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyRoomNotFoundException.java (2)

6-8: LGTM!

Javadoc 추가로 예외 클래스의 목적이 명확해졌습니다.


10-12: 우수한 예외 처리 개선!

ID 파라미터를 받아 구체적인 오류 메시지를 제공하는 것은 디버깅과 사용자 경험 측면에서 매우 유용합니다. HTTP 404 상태 코드도 적절합니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyHallNotFoundException.java (1)

1-13: LGTM!

StudyHallNotFoundException이 잘 구현되었습니다. StudyRoomNotFoundException과 동일한 패턴을 사용하여 일관성을 유지하고 있으며, Javadoc과 구체적인 오류 메시지가 포함되어 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/mapper/StudyRoomMapper.java (1)

1-18: LGTM!

StudyRoomMapper가 잘 구현되었습니다:

  • MapStruct와 Spring 통합이 적절함
  • 단일 엔티티와 리스트 변환 메서드 모두 제공
  • 명확한 Javadoc으로 문서화됨
  • 메서드 네이밍이 직관적임
src/main/java/org/example/hugmeexp/domain/studyRoom/dto/mapper/StudyHallMapper.java (1)

1-18: LGTM!

StudyHallMapper가 잘 구현되었습니다:

  • StudyRoomMapper와 동일한 패턴으로 일관성 유지
  • MapStruct와 Spring 통합이 적절함
  • 명확한 Javadoc 문서화
  • 필요한 변환 메서드들이 모두 포함됨
src/main/java/org/example/hugmeexp/global/common/config/RedisConfig.java (3)

57-64: Java 8 날짜/시간 타입을 위한 ObjectMapper 설정이 올바르게 구성되었습니다.

JavaTimeModule 등록과 WRITE_DATES_AS_TIMESTAMPS 비활성화로 LocalDateTime 등의 타입이 ISO 8601 문자열 형식으로 직렬화됩니다. 스터디룸 예약 시스템의 날짜/시간 데이터 캐싱에 필수적인 설정입니다.


66-68: 개선된 직렬화 규칙 생성이 적절합니다.

설정된 ObjectMapper를 사용해 GenericJackson2JsonRedisSerializer를 생성하여 일관된 직렬화 규칙을 보장합니다.


70-74: 키와 값 직렬화 규칙의 명시적 설정이 우수합니다.

키는 StringRedisSerializer, 값은 커스텀 GenericJackson2JsonRedisSerializer를 명시적으로 설정하여 Redis 캐시의 직렬화 동작을 명확하게 제어합니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/exception/LocationServiceException.java (1)

6-10: 위치 서비스 예외 클래스가 적절하게 구현되었습니다.

BaseCustomException을 상속하여 일관된 예외 처리 구조를 유지하며, 한국어 메시지 프리픽스와 적절한 HTTP 상태 코드(400)를 사용합니다. Kakao Map API 연동에서 발생할 수 있는 위치 관련 오류를 명확하게 구분할 수 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/ReservationCreateDto.java (2)

18-24: 예약 시간 필드의 검증이 적절하게 구현되었습니다.

@NotNull@Future 어노테이션을 사용하여 필수 입력과 미래 시간만 허용하도록 제한했습니다. 한국어 오류 메시지도 명확합니다. 예약 시스템의 비즈니스 규칙을 잘 반영한 검증입니다.


26-30: 필수 필드들의 검증이 올바르게 설정되었습니다.

partyNumstudyRoomId 필드에 @NotNull 검증을 추가하여 필수 데이터의 누락을 방지합니다. 오류 메시지가 한국어로 명확하게 제공됩니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/TimeSlotResponse.java (2)

11-22: TimeSlotResponse DTO 클래스가 올바르게 구현되었습니다.

Response DTO에 적합한 lombok 어노테이션들을 사용하고, DateTimeFormatter를 상수로 정의하여 성능과 가독성을 향상시켰습니다. 필드 구성도 시간대 정보 표현에 적절합니다.


24-33: Static factory method가 효율적으로 구현되었습니다.

of 메서드를 통해 시간 포맷팅과 객체 생성을 한 번에 처리하여 편의성을 제공합니다. displayTime 필드의 "HH:mm - HH:mm" 형식 생성이 적절합니다.

src/main/java/org/example/hugmeexp/global/common/config/KakaoMapConfig.java (1)

8-18: Kakao Map API 설정 클래스가 우수하게 구현되었습니다.

@ConfigurationProperties를 사용하여 외부 설정을 효과적으로 바인딩하고, 합리적인 기본값들을 제공합니다. apiKey는 기본값 없이 필수 설정으로 두어 보안상 적절합니다. URL 경로들의 기본값도 Kakao API 문서와 일치합니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyHallResponse.java (1)

1-27: 잘 구현된 불변 응답 DTO입니다.

불변성을 보장하는 final 필드와 protected 생성자를 사용하고, 이전 학습 내용에 따라 @DaTa 대신 적절한 Lombok 어노테이션을 사용했습니다. 필드 타입도 적절하게 선택되었습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/projection/StudyHallWithDistanceProjection.java (1)

5-17: 잘 설계된 projection interface입니다.

거리 계산이 포함된 스터디홀 데이터를 위한 projection으로 적절하게 설계되었습니다. 메서드 명명 규칙을 따르고, 적절한 타입을 사용했습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/controller/AdminStudyRoom.http (1)

1-134: 포괄적인 API 테스트 스크립트입니다.

전체적으로 관리자 API의 전체 생명주기를 잘 테스트하는 구조로 되어있습니다. 동적 변수 사용과 인증 처리가 적절합니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyRoomRequest.java (1)

15-30: 적절한 검증이 포함된 요청 DTO입니다.

필드별로 적절한 검증 어노테이션이 사용되었고, 한국어 검증 메시지도 일관성 있게 작성되었습니다. Lombok 어노테이션 사용도 적절합니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationTimeResponse.java (1)

12-37: LGTM! 응답 DTO 구조가 우수합니다.

코드가 이전 학습 내용에 따라 @Data 어노테이션 대신 개별 Lombok 어노테이션을 올바르게 사용하고 있습니다. 정적 팩토리 메서드 패턴과 시간 포맷팅도 적절하게 구현되어 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/repository/StudyRoomRepository.java (3)

17-18: LGTM! 메서드명과 구현이 적절합니다.

Spring Data JPA 네이밍 컨벤션을 잘 따르고 있으며, 논리적 삭제를 고려한 필터링이 올바르게 구현되어 있습니다.


24-25: LGTM! ID 기반 조회 메서드가 적절합니다.

관리자 기능에서 삭제된 룸도 포함하여 조회할 수 있도록 설계된 것이 합리적입니다.


27-28: LGTM! 단일 룸 조회 메서드가 잘 구현되었습니다.

Optional 반환 타입과 논리적 삭제 필터링이 올바르게 적용되어 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyRoom.java (4)

3-13: LGTM! 필수 임포트와 데이터베이스 인덱스가 적절히 추가되었습니다.

isDeleted 컬럼에 대한 인덱스 추가는 논리적 삭제 필터링 성능을 향상시킬 것입니다.


32-32: LGTM! 썸네일 필드 추가가 적절합니다.

필드 구현이 간단하고 명확하며, 유효성 검증은 DTO 레벨에서 처리하는 것이 좋은 설계입니다.


34-36: LGTM! 논리적 삭제 필드가 올바르게 구현되었습니다.

@JsonProperty 어노테이션을 통한 JSON 직렬화 처리와 기본값 설정이 적절합니다.


38-47: LGTM! 업데이트 및 삭제 메서드가 우수하게 구현되었습니다.

Optional.ofNullable을 사용한 null-safe 업데이트 패턴과 논리적 삭제 구현이 StudyHall 엔티티와 일관성 있게 작성되어 있습니다.

sql/study_hall_dummy_sql.sql (3)

1-33: LGTM! 스터디홀 더미 데이터 구조가 우수합니다.

현실적인 서울 지역 위치 데이터와 좌표가 정확하게 입력되어 있으며, 엔티티 구조와 일치하는 컬럼명을 사용하고 있습니다.


35-47: LGTM! ID 조회 및 변수 할당이 적절합니다.

자동 생성된 ID를 변수에 할당하여 외래 키 관계를 올바르게 유지하는 좋은 접근법입니다.


49-131: LGTM! 스터디룸 데이터와 검증 쿼리가 잘 구성되었습니다.

다양한 수용 인원과 의미 있는 룸 이름으로 현실적인 테스트 데이터가 생성되며, 최종 검증 쿼리로 데이터 삽입 결과를 확인할 수 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/controller/studyRoomMap.http (1)

1-115: LGTM! HTTP 테스트 스크립트가 잘 구성되어 있습니다.

인증 설정, 변수 사용, 그리고 포괄적인 API 엔드포인트 테스트가 체계적으로 구성되어 있습니다. 위치 기반 검색과 거리 계산 기능에 대한 다양한 테스트 케이스가 포함되어 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyHallSearchRequest.java (4)

1-40: 위치 기반 검색을 위한 잘 설계된 요청 DTO입니다.

지리적 좌표 검증 범위가 정확하고, 기본값 설정이 합리적입니다. Swagger 문서화와 유효성 검증이 적절히 적용되어 있으며, 검색된 학습 내용에 따라 @DaTa 어노테이션 대신 개별 Lombok 어노테이션을 사용한 것도 좋습니다.


13-16: Lombok 어노테이션 사용이 적절합니다.

이전 리뷰 학습 내용에 따라 @Data 어노테이션을 사용하지 않고 필요한 어노테이션만 선택적으로 사용한 것이 좋습니다.


20-27: 지리적 좌표 검증이 정확합니다.

위도와 경도의 유효 범위 검증이 올바르게 설정되어 있습니다 (위도: -9090, 경도: -180180).


30-33: 외부 API 최대 검색 반경 제약 조건 확인 필요

코드 전반에서 최대 반경을 50km로 일관되게 설정하고 있습니다. 실제 사용 시나리오와 외부 API 제약 조건을 다시 한번 검토해주세요.

• src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyHallSearchRequest.java

  • @DecimalMax(value = "50.0", message = "검색 반경은 50km 이하여야 합니다.")
    • src/main/java/org/example/hugmeexp/domain/studyRoom/repository/StudyHallRepository.java
  • HAVING distance <= :radiusInKm

구글 플레이스 Nearby Search(최대 50,000m)나 카카오 로컬 검색 API 등에서 지원하는 최대 반경과 비교하여, 50km 설정이 적절한지 확인 부탁드립니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/request/StudyHallRequest.java (3)

11-47: 스터디 홀 관리를 위한 잘 설계된 요청 DTO입니다.

필수 필드에 대한 유효성 검증이 적절히 적용되어 있고, 지리적 좌표 범위 검증도 정확합니다. 생성자의 접근 수준을 protected로 제한한 것과 검색된 학습 내용에 따라 적절한 Lombok 어노테이션을 사용한 것도 좋습니다.


14-18: 생성자 접근 제어가 적절합니다.

protected 접근 레벨로 생성자를 제한하여 빌더 패턴 사용을 장려하는 것이 좋은 설계입니다.


30-38: 지리적 좌표 검증의 일관성이 좋습니다.

StudyHallSearchRequest와 동일한 범위의 위도/경도 검증을 사용하여 일관성을 유지하고 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationService.java (3)

28-39: 예약 목록 조회 로직이 잘 구현되어 있습니다.

페이징 처리와 DTO 매핑이 적절하며, 필요한 관련 엔티티 정보를 포함하고 있습니다.


16-19: 트랜잭션 설정이 적절합니다.

읽기 전용 트랜잭션을 기본으로 설정하고 필요한 메서드에서만 쓰기 트랜잭션을 사용하는 것이 좋은 접근법입니다.


28-39: DTO 매핑 로직이 명확합니다.

빌더 패턴을 사용한 DTO 매핑이 가독성이 좋고 유지보수하기 쉽게 구현되어 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyRoomDetailResponse.java (1)

46-49: StudyHall 위치 접근 변경 불필요

StudyHall 엔티티는 @Embedded된 Location을 갖고 있으며, getLatitude(), getLongitude(), getAddress(), getSimpleAddress() 래퍼 메서드를 제공하고 있습니다.
따라서 아래 코드에서 기존 호출은 정상 작동하며, Location 객체 직접 호출로의 변경은 필요하지 않습니다:

.simpleAddress(studyRoom.getStudyHall().getSimpleAddress())
.address(studyRoom.getStudyHall().getAddress())
.latitude(studyRoom.getStudyHall().getLatitude())
.longitude(studyRoom.getStudyHall().getLongitude())

Likely an incorrect or invalid review comment.

src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/StudyHallLocationResponse.java (1)

37-40: StudyHall의 Location 임베딩 구조와 일관성 확인 필요

StudyHall 엔티티가 Location 객체를 임베딩하는 경우, 위치 관련 필드 접근 방식을 확인해야 합니다:

StudyHall 엔티티 구조에 따라 다음과 같은 수정이 필요할 수 있습니다:

.simpleAddress(studyHall.getLocation().getSimpleAddress())
.address(studyHall.getLocation().getAddress())
.latitude(studyHall.getLocation().getLatitude())
.longitude(studyHall.getLocation().getLongitude())
src/test/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationServiceTest.java (3)

36-58: 테스트 로직이 잘 구성되어 있습니다.

전체 예약 목록 조회 테스트가 적절하게 작성되었습니다. 페이징 처리, 모킹, 검증이 올바르게 구현되어 있습니다.


60-73: 예약 강제 취소 성공 케이스가 잘 테스트되었습니다.

레포지토리 메서드 호출 순서와 횟수가 올바르게 검증되고 있습니다.


75-89: 예외 상황 테스트가 철저하게 구현되었습니다.

존재하지 않는 예약에 대한 처리와 deleteById 메서드가 호출되지 않는 것을 적절히 검증하고 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/controller/AdminReservationController.java (2)

25-40: 관리자 예약 목록 조회 엔드포인트가 잘 구현되었습니다.

페이징 지원, 표준화된 응답 구조, 적절한 Swagger 문서화가 잘 적용되어 있습니다.


42-56: 예약 강제 취소 엔드포인트가 적절하게 구현되었습니다.

RESTful 패턴을 따르고 있으며, 서비스 레이어로의 적절한 위임과 표준 응답 구조를 사용하고 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/repository/StudyRoomReservationRepository.java (5)

43-48: 관리자용 페이징 쿼리가 효율적으로 구현되었습니다.

JOIN FETCH를 사용한 N+1 문제 방지와 별도의 count 쿼리를 통한 페이징 최적화가 잘 적용되어 있습니다.


50-58: 예약 충돌 검사 쿼리가 정확하게 구현되었습니다.

시간 겹침 로직(r.reservationStart < :endTime AND r.reservationEnd > :startTime)이 올바르게 적용되어 이중 예약을 방지할 수 있습니다.


60-67: 현재 진행 중인 예약 개수 조회 쿼리가 적절합니다.

시작은 했지만 종료되지 않은 예약을 정확히 식별하는 로직이 구현되어 있습니다.


69-80: 시간 범위별 예약 조회 쿼리가 잘 설계되었습니다.

시작 시간으로 정렬된 결과를 반환하여 시간대별 예약 현황을 효과적으로 조회할 수 있습니다.


82-91: MySQL에서 DATE() 함수 사용에 문제 없습니다

  • build.gradle에 com.mysql:mysql-connector-j가 Runtime 의존성으로 정의되어 있고, PostgreSQL 의존성은 없습니다.
  • src/main/resources 아래에 별도 datasource 설정이 없어도 기본 MySQL 설정을 사용 중인 것으로 보입니다.

따라서 현재 코드베이스에서는 DATE(r.reservationStart) 사용에 추가 검증이 필요하지 않습니다.

Likely an incorrect or invalid review comment.

src/main/java/org/example/hugmeexp/domain/studyRoom/repository/StudyHallRepository.java (5)

18-26: 소프트 삭제 필터링 메서드가 적절하게 구현되었습니다.

Spring Data JPA 네이밍 컨벤션을 따르고 있으며 페이징 지원도 포함되어 있습니다.


28-70: 지리적 검색 쿼리가 매우 잘 구현되었습니다.

Haversine 공식을 사용한 거리 계산과 바운딩 박스 최적화, GREATEST/LEAST를 통한 수치 안정성 확보가 훌륭합니다. 프로젝션을 사용한 결과 반환도 효율적입니다.


72-82: StudyRoom 조인 쿼리가 성능 최적화를 잘 고려했습니다.

LEFT JOIN FETCH를 사용하여 N+1 문제를 방지하고 소프트 삭제 필터링도 적절히 적용되어 있습니다.


84-87: 이름 검색 메서드가 적절하게 구현되었습니다.

대소문자 구분 없는 검색과 소프트 삭제 필터링이 올바르게 적용되어 있습니다.


89-93: 주소 검색 쿼리가 잘 구현되었습니다.

중첩된 프로퍼티 접근과 LIKE 연산자를 적절히 사용하여 주소 검색 기능을 제공하고 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationService.java (6)

39-78: 성능 테스트를 위한 비락 메서드가 잘 문서화되어 있습니다.

상세한 JavaDoc과 함께 락 없는 버전의 예약 생성 메서드가 적절히 구현되어 테스트 비교에 유용합니다.


126-158: 예약 상세 조회 메서드가 보안을 잘 고려했습니다.

사용자 권한 검증과 함께 상세한 문서화가 잘 되어 있습니다.


160-181: 예약 목록 조회 메서드가 깔끔하게 구현되었습니다.

페이징 처리와 DTO 변환이 적절하며 문서화도 잘 되어 있습니다.


183-210: 예약 삭제 메서드의 비즈니스 로직이 적절합니다.

권한 검증과 시작 시간 확인을 통한 삭제 가능 여부 판단이 올바르게 구현되어 있습니다.


217-240: 헬퍼 메서드들이 적절한 검증 로직을 구현하고 있습니다.

시간 유효성 검사와 충돌 검사가 올바른 비즈니스 규칙을 따르고 있습니다.


80-124: 락킹 전략이 일관적입니다.

StudyRoomRepository.findByIdWithLock에는

@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)

이 적용되어 있어 실제로 낙관적 락을 사용하고 있으며,
@Retryable에서 OptimisticLockingFailureException을 처리하는 방식은 적절합니다.
따라서 추가 수정은 필요하지 않습니다.

Likely an incorrect or invalid review comment.

src/main/java/org/example/hugmeexp/domain/studyRoom/controller/AdminStudyRoomController.java (1)

32-43: 컨트롤러 구조가 잘 설계되었습니다

관리자 전용 API 엔드포인트가 체계적으로 구성되어 있고, 적절한 서비스 레이어 분리와 DTO 매핑이 이루어져 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/entity/StudyHall.java (2)

77-79: Location 객체 생성 시 불필요한 인스턴스 생성

Line 78에서 새로운 Location 인스턴스를 생성한 후 Line 81에서 다시 덮어쓰고 있습니다. 이는 불필요한 객체 생성입니다.

-            if (this.location == null) {
-                this.location = new Location();
-            }
-
-            this.location = Location.of(
+            this.location = Location.of(
                     requestDto.getLatitude() != null ? requestDto.getLatitude() : this.location.getLatitude(),
                     requestDto.getLongitude() != null ? requestDto.getLongitude() : this.location.getLongitude(),
                     requestDto.getAddress() != null ? requestDto.getAddress() : this.location.getAddress(),
                     requestDto.getSimpleAddress() != null ? requestDto.getSimpleAddress() : this.location.getSimpleAddress()
             );
⛔ Skipped due to learnings
Learnt from: Roysung0625
PR: Please-Hug/HugmeEXP_Backend#32
File: src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java:5-5
Timestamp: 2025-07-23T09:00:11.298Z
Learning: ReservationDetailDto와 같은 response DTO에서는 Data 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다.

95-100: 거리 계산 메서드의 파라미터 순서 확인 필요

calculateDistanceFrom 메서드가 반대로 계산하고 있는 것으로 보입니다. 메서드 이름이 "from"이므로 파라미터 위치에서 현재 위치까지의 거리를 계산해야 하는데, 현재는 반대로 되어 있습니다.

 public Double calculateDistanceFrom(Location otherLocation) {
     if (this.location == null) {
         return null;
     }
-    return otherLocation.calculateDistanceTo(this.location);
+    return this.location.calculateDistanceTo(otherLocation);
 }

Likely an incorrect or invalid review comment.

src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyHallServiceTest.java (4)

27-34: 테스트 설정이 적절합니다.

MockitoExtension과 적절한 mock 설정으로 단위 테스트 환경이 잘 구성되어 있습니다.


36-58: 생성 테스트에서 ArgumentCaptor 사용이 효과적입니다.

저장된 엔티티의 속성을 검증하기 위해 ArgumentCaptor를 사용한 것이 좋은 접근법입니다.


102-115: 예외 테스트 시나리오가 완전합니다.

존재하지 않는 ID로 조회 시 StudyHallNotFoundException이 발생하는지 검증하는 테스트가 잘 구현되어 있습니다.


144-162: delete() 메서드 호출 검증은 상태 검증으로 충분합니다.

현재 테스트는 studyHall.delete() 실행 후 엔티티의 isDeleted() 상태 변경을 직접 검증하고 있으므로, 별도로 메서드 호출 자체를 Mockito로 검증할 필요가 없습니다. 이 상태 검증만으로도 논리적 삭제 로직이 정상 동작함을 확인할 수 있습니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/service/KakaoMapService.java (2)

23-26: 의존성 주입 설정이 적절합니다.

@Qualifier를 사용하여 특정 ObjectMapper를 주입받는 것이 좋은 설계입니다.


113-131: Haversine 공식 구현이 정확합니다.

두 지점 간 거리 계산 로직이 수학적으로 정확하며, null 체크와 반올림 처리가 적절합니다.

src/main/java/org/example/hugmeexp/domain/studyRoom/entity/Location.java (4)

12-17: Lombok 어노테이션과 JPA 어노테이션이 적절합니다.

@Embeddable 어노테이션으로 StudyHall 엔티티에 임베드될 수 있도록 설계되었고, Lombok 어노테이션들이 적절하게 사용되었습니다. @Builder(toBuilder = true)는 불변 객체 패턴에 유용합니다.


23-35: 위도/경도 validation이 정확합니다.

전 세계 좌표 범위에 대한 검증이 올바르게 구현되어 있고, 한국 위치에 대한 주석도 유용합니다. @NotNull 검증으로 필수 값임을 명시했습니다.


48-70: 정적 팩터리 메서드들이 잘 설계되었습니다.

다양한 매개변수 조합을 지원하는 정적 팩터리 메서드들이 제공되어 유연한 객체 생성이 가능합니다. 메서드 오버로딩을 통해 사용성을 높였습니다.


115-117: 위치 완전성 검사 로직이 정확합니다.

위도와 경도가 모두 존재하는지 확인하는 간단하고 명확한 로직입니다.

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

🔭 Outside diff range comments (2)
src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationConcurrencyTest.java (2)

185-192: Optimistic Lock 테스트 검증 개선 제안

현재 테스트는 Optimistic Lock 사용 시에도 완전한 동시성 제어가 되지 않을 수 있음을 인정하고 있습니다. 더 명확한 검증을 위해 다음을 고려해보세요:

  1. 예외 타입을 구분하여 OptimisticLockingFailureException 발생 횟수를 별도로 카운트
  2. 성공한 예약이 정확히 1개인지 검증하는 더 엄격한 assertion 추가
 AtomicInteger successCount = new AtomicInteger(0);
 AtomicInteger exceptionCount = new AtomicInteger(0);
+AtomicInteger optimisticLockExceptionCount = new AtomicInteger(0);

 // When
 for (int i = 0; i < threadCount; i++) {
     final int userIndex = i;
     executorService.submit(() -> {
         try {
             UserDetails userDetails = createUserDetails(testUsers.get(userIndex));
             studyRoomReservationService.createReservation(testReservationDto, userDetails);
             successCount.incrementAndGet();
+        } catch (OptimisticLockingFailureException e) {
+            optimisticLockExceptionCount.incrementAndGet();
+            System.out.println("OptimisticLockingFailureException in lock test: " + e.getMessage());
         } catch (Exception e) {
             exceptionCount.incrementAndGet();
             System.out.println("Exception in lock test: " + e.getMessage());
         } finally {
             latch.countDown();
         }
     });
 }

195-218: 비교 테스트 구현에 문제가 있습니다

두 가지 문제를 수정해야 합니다:

  1. setUp() 메서드를 직접 호출하는 것은 @BeforeEach 어노테이션과 충돌할 수 있습니다
  2. 동시성 제어 효과 판단 로직이 반대로 되어 있습니다
 @Test
 @DisplayName("두 방식 비교 테스트")
+@Transactional
 public void compareTwoApproaches() throws InterruptedException {
     System.out.println("\n=== 두 방식 비교 결과 ===");
     
     // Lock 미사용 테스트
     testConcurrentReservationWithoutLock();
     List<StudyRoomReservation> noLockResults = studyRoomReservationRepository.findAll();
     int noLockResultCount = noLockResults.size();
     
     // 데이터 초기화 후 Lock 사용 테스트
-    setUp();
+    // 데이터 초기화
+    studyRoomReservationRepository.deleteAll();
     testConcurrentReservationWithOptimisticLock();
     List<StudyRoomReservation> lockResults = studyRoomReservationRepository.findAll();
     int lockResultCount = lockResults.size();
     
     System.out.println("Lock 미사용 결과: " + noLockResultCount + "개 예약");
     System.out.println("Optimistic Lock 사용 결과: " + lockResultCount + "개 예약");
     
     // 동시성 제어의 효과를 확인
-    // (완벽하지 않더라도 Lock 사용이 더 나은 결과를 보여줄 수 있음)
-    System.out.println("동시성 제어 효과: " + 
-        (noLockResultCount >= lockResultCount ? "개선됨" : "변화 없음"));
+    // Lock 사용 시 중복 예약이 줄어들어야 함
+    System.out.println("동시성 제어 효과: " + 
+        (noLockResultCount > lockResultCount ? "개선됨" : "변화 없음"));
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4575c6b and f52d035.

📒 Files selected for processing (3)
  • src/test/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationServiceTest.java (1 hunks)
  • src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationConcurrencyTest.java (4 hunks)
  • src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationServiceTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: reservationdetaildto와 같은 response dto에서는 @DaTa 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다....
Learnt from: Roysung0625
PR: Please-Hug/HugmeEXP_Backend#32
File: src/main/java/org/example/hugmeexp/domain/studyRoom/dto/response/ReservationDetailDto.java:5-5
Timestamp: 2025-07-23T09:00:11.298Z
Learning: ReservationDetailDto와 같은 response DTO에서는 Data 어노테이션을 사용하지 않고, 사용하지 않는 lombok import는 제거하는 것을 선호한다.

Applied to files:

  • src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationServiceTest.java
🧬 Code Graph Analysis (1)
src/test/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationServiceTest.java (1)
src/main/java/org/example/hugmeexp/domain/studyRoom/exception/StudyRoomReservationNotFoundException.java (1)
  • StudyRoomReservationNotFoundException (6-10)
⏰ 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
🔇 Additional comments (9)
src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationConcurrencyTest.java (3)

1-35: 테스트 설정이 적절합니다

@ActiveProfiles("test") 어노테이션을 사용하여 테스트 프로파일을 명확하게 지정한 점이 좋습니다. 이는 테스트 환경의 격리를 보장하고 프로덕션 설정과의 충돌을 방지합니다.


56-100: 테스트 데이터 초기화가 체계적입니다

Location 값 객체를 올바르게 사용하여 StudyHall을 생성하고, 동시성 테스트를 위해 50명의 테스트 사용자를 생성하는 접근이 적절합니다.


102-144: Race Condition 테스트가 잘 구현되었습니다

Lock을 사용하지 않을 때 발생할 수 있는 동시성 문제를 명확하게 보여주는 테스트입니다. 결과를 콘솔에 출력하여 디버깅에 도움이 되도록 한 점이 좋습니다.

src/test/java/org/example/hugmeexp/domain/studyRoom/service/StudyRoomReservationServiceTest.java (4)

1-46: 테스트 클래스 설정이 적절합니다

Mockito 설정과 필요한 의존성들이 올바르게 구성되어 있습니다. @MockitoSettings(strictness = Strictness.LENIENT)를 사용하여 유연한 모킹을 허용한 점이 좋습니다.


72-111: 테스트 데이터 설정이 체계적입니다

StudyHall의 Location 객체를 올바르게 생성하고, 모든 필요한 테스트 데이터와 Mock 동작을 적절하게 설정했습니다.


323-397: 예약 상세 조회 테스트가 완벽합니다

모든 주요 시나리오(성공, 사용자 없음, 예약 없음, 권한 없음)를 포함하고 있으며, Location 객체의 필드 접근도 올바르게 처리되어 있습니다.


526-549: 이미 시작된 예약 삭제 방지 테스트가 우수합니다

비즈니스 규칙(이미 시작된 예약은 삭제 불가)을 명확하게 테스트하고 있습니다. 이는 실제 운영에서 중요한 제약사항입니다.

src/test/java/org/example/hugmeexp/domain/studyRoom/service/AdminReservationServiceTest.java (2)

1-36: 관리자 예약 서비스 테스트 구조가 적절합니다

MockitoExtension을 사용한 단위 테스트 설정이 올바르게 구성되어 있습니다.


37-59: 전체 예약 목록 조회 테스트가 잘 구현되었습니다

Location 객체를 올바르게 생성하여 StudyHall을 구성하고, 페이징 처리를 적절히 검증하고 있습니다.

@Roysung0625 Roysung0625 merged commit 60a965e into develop Aug 6, 2025
3 checks passed
@Roysung0625 Roysung0625 deleted the feature/studyroom branch August 6, 2025 06:55
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.

3 participants

Comments