Skip to content

[FEAT]: 마이페이지에서의 내 가게 리스트 조회 API 추가#135

Merged
twodo0 merged 9 commits intodevelopfrom
feature/mypage-store
Feb 12, 2026
Merged

[FEAT]: 마이페이지에서의 내 가게 리스트 조회 API 추가#135
twodo0 merged 9 commits intodevelopfrom
feature/mypage-store

Conversation

@twodo0
Copy link
Contributor

@twodo0 twodo0 commented Feb 12, 2026

💡 작업 개요

  • 마이페이지에서의 내 가게 리스트 조회 API를 추가했습니다.

✅ 작업 내용

  • 기능 개발
  • 버그 수정
  • 리팩토링
  • 주석/포맷 정리
  • 기타 설정

🧪 테스트 내용

  • 로컬 정상 빌드 확인
  • Swagger에서 정상 작동 확인
image

📝 기타 참고 사항

Summary by CodeRabbit

  • 새로운 기능
    • 사용자가 소유한 매장 목록 조회 엔드포인트 추가 (GET /api/v1/stores/my)
    • 매장별 활성 예약 수 집계 제공으로 각 매장의 총 예약 수 확인 가능
    • 식당 카테고리에 이탈리안(Italian) 추가
  • 문서화
    • 소유 매장 응답 DTO 및 API 스키마 설명 추가
  • 응답 코드
    • 소유 매장 조회 성공용 성공 코드·메시지 추가

@twodo0 twodo0 self-assigned this Feb 12, 2026
@twodo0 twodo0 added the feature label Feb 12, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

Warning

Rate limit exceeded

@twodo0 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 13 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.

📝 Walkthrough

Walkthrough

사용자가 소유한 매장 목록을 반환하는 새 API가 추가되었습니다. 매장별 활성 예약 수를 일괄 집계하고, 개점 여부와 메인 이미지 URL을 판단해 새로운 DTO로 응답합니다. 관련 리포지토리 쿼리, 서비스 및 컨트롤러 엔드포인트, DTO/변환기와 상태 코드가 추가되었습니다.

Changes

Cohort / File(s) Summary
Repository Queries
src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java, src/main/java/com/eatsfine/eatsfine/domain/store/repository/StoreRepository.java
활성 예약 집계용 JPQL 메서드(countActiveBookings, countActiveBookingsByStores)와 소유자 기반 매장 조회(findAllByOwner) 추가.
Service Layer
src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryService.java, src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java
인터페이스에 getMyStores(String) 추가 및 구현: 사용자 조회, 소유 매장 조회, 일괄 예약 수 집계, 개점 여부 판단, S3 이미지 조회, DTO 조립 로직 추가. 필드로 bookingRepository, userRepository 주입.
Controller / API
src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java
GET /api/v1/stores/my 엔드포인트 추가(OWNER 권한 요구), 현재 사용자 기반 매장 리스트 반환.
DTOs & Converter
src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java, src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java
MyStoreDto, MyStoreListDto 레코드 추가 및 변환 유틸리티(toMyStoreDto, toMyStoreListDto) 추가. 각 필드에 @Schema 주석 포함.
Enums / Status
src/main/java/com/eatsfine/eatsfine/domain/store/enums/Category.java, src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java
카테고리에 ITALIAN 추가 및 성공 상태 코드 _MY_STORE_LIST_FOUND 추가.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Controller as "StoreController\n(rgba(52,152,219,0.5))"
    participant Service as "StoreQueryService\n(rgba(46,204,113,0.5))"
    participant UserRepo as "UserRepository\n(rgba(155,89,182,0.5))"
    participant StoreRepo as "StoreRepository\n(rgba(241,196,15,0.5))"
    participant BookingRepo as "BookingRepository\n(rgba(231,76,60,0.5))"
    participant S3 as "S3Service\n(rgba(52,73,94,0.5))"
    participant Converter as "StoreConverter\n(rgba(127,140,141,0.5))"

    User->>Controller: GET /api/v1/stores/my
    Controller->>Service: getMyStores(username)
    Service->>UserRepo: findByEmail(username)
    UserRepo-->>Service: User
    Service->>StoreRepo: findAllByOwner(user)
    StoreRepo-->>Service: List<Store>
    Service->>BookingRepo: countActiveBookingsByStores(stores)
    BookingRepo-->>Service: List(storeId, count)
    Service->>S3: getMainImageUrl(store) (loop per store)
    S3-->>Service: url
    Service->>Converter: toMyStoreDto(...) (loop per store)
    Converter-->>Service: MyStoreDto
    Service->>Converter: toMyStoreListDto(dtos)
    Converter-->>Service: MyStoreListDto
    Service-->>Controller: MyStoreListDto
    Controller-->>User: ApiResponse<MyStoreListDto>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • zerochani
  • SungMinju
  • sonjunkyu

Poem

🐰 주인님 가게 불러왔어요, 당근 소리 쿵쿵
예약 수 세서 품에 넣고 문 연 시간도 척척
사진 한 장 퐁당 담아 DTO로 쏙
라빗이 기뻐 뛰어요, 주인님 만세 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용을 정확하게 나타내고 있습니다. 마이페이지에서의 내 가게 리스트 조회 API 추가라는 핵심 기능을 명확하게 설명하고 있습니다.
Description check ✅ Passed PR 설명이 저장소의 템플릿 구조를 따르고 있으며, 작업 개요, 작업 내용 체크리스트, 테스트 내용(로컬 빌드 및 Swagger 확인)을 포함하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/mypage-store

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java`:
- Around line 83-88: Rename the method countActiveBookings in BookingRepository
to a name matching intent (e.g., countValidBookings or countTotalBookings) and
update any call sites; adjust the JPQL to reflect the intended semantics—either
remove COMPLETED from the IN list if completed bookings should not be counted,
or switch to a NOT IN pattern excluding only CANCELLED (and any other
non-counted statuses) using BookingStatus enum constants—to avoid future enum
additions breaking this logic; ensure the repository method signature and any
usages (countActiveBookings -> new name) are updated accordingly.

In
`@src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java`:
- Around line 111-121: The response status used in getMyStores currently
references StoreSuccessStatus._STORE_FOUND which is semantically for single-item
fetches; add a new enum constant (e.g., _MY_STORE_LIST_FOUND or
_STORE_LIST_FOUND) to StoreSuccessStatus and update StoreController.getMyStores
to return ApiResponse.of(StoreSuccessStatus._MY_STORE_LIST_FOUND,
storeQueryService.getMyStores(user.getUsername())); also update any related
documentation/messages for the new enum constant so the list-response message is
clear.

In
`@src/main/java/com/eatsfine/eatsfine/domain/store/repository/StoreRepository.java`:
- Around line 24-25: The findAllByOwner method in StoreRepository can cause an
N+1 when StoreQueryServiceImpl.getMyStores calls Store.isOpenNow() which
accesses store.findBusinessHoursByDay() (the businessHours collection); update
StoreRepository.findAllByOwner to eagerly load businessHours (either add an
`@EntityGraph`(attributePaths = "businessHours") on findAllByOwner or replace it
with a custom JPQL/Spring Data query that uses a fetch join for businessHours)
so that businessHours are fetched in the same query and avoids per-Store
additional selects.

In
`@src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java`:
- Around line 120-122: The parameter name username in
StoreQueryService.getMyStores and its implementation
StoreQueryServiceImpl.getMyStores is misleading because the code calls
userRepository.findByEmail(username); rename the parameter to email in both the
interface and implementation (and update all callers) so the signature reads
getMyStores(String email), or if renaming is risky add a clarifying comment
above StoreQueryServiceImpl.getMyStores stating that the passed-in username is
an email used with userRepository.findByEmail; make sure to update any
references to the method and keep userRepository.findByEmail(email) and the
existing exception handling unchanged.
- Around line 119-137: getMyStores performs N+1 DB calls because it calls
bookingRepository.countActiveBookings(store) inside the stream for each Store
returned by storeRepository.findAllByOwner; replace per-store counting with a
single bulk query method (e.g., add
BookingRepository.countActiveBookingsByStores(List<Store> stores) that returns
storeId→count) and in getMyStores call that once, convert the result to a
Map<Long,Long>, then use that map when building each StoreConverter.toMyStoreDto
(retain s3Service.toUrl and isOpenNow usage) so booking counts are fetched in
one query instead of N calls.

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

🤖 Fix all issues with AI agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java`:
- Around line 83-96: Both methods countActiveBookings and
countActiveBookingsByStores duplicate the same status filter and use a different
enum reference style than other queries; change both queries to accept a single
parameter (e.g., :statuses) of type
List<com.eatsfine.eatsfine.domain.booking.enums.BookingStatus> and use "b.status
in :statuses" to centralize the filter, and update other queries in the file to
use the fully-qualified BookingStatus enum
(com.eatsfine.eatsfine.domain.booking.enums.BookingStatus) for consistency so
adding a new status only requires updating the call site that builds the
statuses list rather than modifying multiple repository queries.

In
`@src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java`:
- Around line 129-134: Replace the raw List<Object[]> projection from
bookingRepository.countActiveBookingsByStores with a type-safe DTO projection
(e.g., a record or interface like StoreBookingCount) returned by the repository
method, update the repository method signature (countActiveBookingsByStores) to
return List<StoreBookingCount> and construct the projection in the JPQL/Query,
then update the service mapping that builds bookingCountMap to collect from
StoreBookingCount using its accessors (StoreBookingCount::storeId,
StoreBookingCount::count) instead of casting Object[] elements.
- Around line 126-134: When myStores (from storeRepository.findAllByOwner(user))
is empty, avoid calling bookingRepository.countActiveBookingsByStores(myStores);
add a guard: if myStores.isEmpty(), set bookingCounts to an empty list (or skip
the DB call) and initialize bookingCountMap as an empty map, otherwise call
countActiveBookingsByStores(myStores) and build the map as existing code does;
this change should be applied around the myStores/bookingCounts logic in
StoreQueryServiceImpl (referencing myStores,
bookingRepository.countActiveBookingsByStores, and bookingCountMap).

In
`@src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java`:
- Around line 23-25: The two enum constants _STORE_MAIN_IMAGE_UPLOAD_SUCCESS and
_STORE_MAIN_IMAGE_GET_SUCCESS in StoreSuccessStatus both use the duplicate code
"STORE2005"; update one to a distinct code (e.g., change
_STORE_MAIN_IMAGE_GET_SUCCESS to "STORE2007" or another unused code) so each
response is unique, ensuring you modify the code string passed to the enum
constructor for _STORE_MAIN_IMAGE_GET_SUCCESS and run any tests or usages that
reference that code to verify no other collisions occur.

Comment on lines +83 to +96
@Query("select count(b) from Booking b " +
"where b.store = :store " +
"and b.status in (com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.CONFIRMED, " +
"com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.PENDING, " +
"com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.COMPLETED)")
Long countActiveBookings(@Param("store") Store store);

@Query("select b.store.id, count(b) from Booking b " +
"where b.store in :stores " +
"and b.status in (com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.CONFIRMED, " +
"com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.PENDING, " +
"com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.COMPLETED) " +
"group by b.store.id")
List<Object[]> countActiveBookingsByStores(@Param("stores") List<Store> stores);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

상태 필터 조건의 중복 및 쿼리 스타일 불일치

두 가지 개선 포인트가 있습니다:

  1. 상태 필터 중복: CONFIRMED, PENDING, COMPLETED 조건이 두 메서드에서 동일하게 반복됩니다. 새 상태 추가 시 양쪽 모두 수정해야 하므로 유지보수 위험이 있습니다.

  2. 쿼리 스타일 불일치: 이 파일 내 기존 쿼리(Lines 29, 36, 44 등)는 문자열 리터럴('CONFIRMED')을, 새 쿼리는 FQN enum 참조를 사용합니다. FQN 방식이 더 타입 안전하지만, 파일 내 일관성을 위해 통일하는 것이 좋습니다. 가능하다면 기존 쿼리도 FQN 방식으로 통일하는 것을 권장합니다.

🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java`
around lines 83 - 96, Both methods countActiveBookings and
countActiveBookingsByStores duplicate the same status filter and use a different
enum reference style than other queries; change both queries to accept a single
parameter (e.g., :statuses) of type
List<com.eatsfine.eatsfine.domain.booking.enums.BookingStatus> and use "b.status
in :statuses" to centralize the filter, and update other queries in the file to
use the fully-qualified BookingStatus enum
(com.eatsfine.eatsfine.domain.booking.enums.BookingStatus) for consistency so
adding a new status only requires updating the call site that builds the
statuses list rather than modifying multiple repository queries.

Comment on lines +129 to +134
List<Object[]> bookingCounts = bookingRepository.countActiveBookingsByStores(myStores);
Map<Long, Long> bookingCountMap = bookingCounts.stream()
.collect(Collectors.toMap(
row -> (Long) row[0],
row -> (Long) row[1]
));
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

List<Object[]> 반환 타입의 타입 안전성 부족

bookingCounts의 각 원소를 (Long)으로 캐스팅하고 있는데, 이는 런타임에서만 검증됩니다. JPQL count() 결과가 Long을 보장하지만, 쿼리 변경 시 ClassCastException 위험이 있습니다. 타입 안전한 DTO projection(record 또는 인터페이스) 사용을 권장합니다.

♻️ 타입 안전한 projection 예시

BookingRepository에서:

public record StoreBookingCount(Long storeId, Long count) {}

`@Query`("select new com.eatsfine.eatsfine.domain.booking.repository.BookingRepository.StoreBookingCount(" +
       "b.store.id, count(b)) from Booking b " +
       "where b.store in :stores " +
       "and b.status in (...) " +
       "group by b.store.id")
List<StoreBookingCount> countActiveBookingsByStores(`@Param`("stores") List<Store> stores);

서비스에서:

Map<Long, Long> bookingCountMap = bookingCounts.stream()
        .collect(Collectors.toMap(StoreBookingCount::storeId, StoreBookingCount::count));
🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java`
around lines 129 - 134, Replace the raw List<Object[]> projection from
bookingRepository.countActiveBookingsByStores with a type-safe DTO projection
(e.g., a record or interface like StoreBookingCount) returned by the repository
method, update the repository method signature (countActiveBookingsByStores) to
return List<StoreBookingCount> and construct the projection in the JPQL/Query,
then update the service mapping that builds bookingCountMap to collect from
StoreBookingCount using its accessors (StoreBookingCount::storeId,
StoreBookingCount::count) instead of casting Object[] elements.

@twodo0 twodo0 force-pushed the feature/mypage-store branch from b1f3e35 to 1b383ce Compare February 12, 2026 05:31
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

🤖 Fix all issues with AI agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java`:
- Around line 113-119: The repository method countActiveBookingsByStores
currently returns List<Object[]> which is error-prone; replace it with a
type-safe projection (either an interface or a record) such as StoreBookingCount
(with methods/getters getStoreId() and getBookingCount()) and change the
BookingRepository method signature to return List<StoreBookingCount>; update the
`@Query` to select the projected fields (store id and count) matching the
projection and keep the same parameter `@Param`("stores") List<Store> stores so
callers receive a typed result instead of Object[].
- Around line 106-111: Remove the unused single-entity repository method
countActiveBookings from BookingRepository: delete the entire method declaration
(the `@Query` and Long countActiveBookings(`@Param`("store") Store store);) since
StoreQueryServiceImpl only uses the bulk method countActiveBookingsByStores;
ensure no other callers reference countActiveBookings and run tests/compile to
verify removal.

Comment on lines +106 to +111
@Query("select count(b) from Booking b " +
"where b.store = :store " +
"and b.status in (com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.CONFIRMED, " +
"com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.PENDING, " +
"com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.COMPLETED)")
Long countActiveBookings(@Param("store") Store store);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

rg -nP '\bcountActiveBookings\b' --type=java -C2

Repository: Eatsfine/BE

Length of output: 784


🏁 Script executed:

rg -nP '\bcountActiveBookingsByStores\b' --type=java -C2

Repository: Eatsfine/BE

Length of output: 1312


countActiveBookings 메서드는 사용되지 않으므로 제거해주세요.

코드베이스에서 이 메서드가 호출되는 곳이 없으며, StoreQueryServiceImpl에서는 countActiveBookingsByStores bulk 쿼리만 사용 중입니다. N+1 문제를 해결하기 위한 bulk 쿼리만 필요하므로 단건 메서드는 삭제하는 것이 좋습니다.

🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java`
around lines 106 - 111, Remove the unused single-entity repository method
countActiveBookings from BookingRepository: delete the entire method declaration
(the `@Query` and Long countActiveBookings(`@Param`("store") Store store);) since
StoreQueryServiceImpl only uses the bulk method countActiveBookingsByStores;
ensure no other callers reference countActiveBookings and run tests/compile to
verify removal.

Comment on lines +113 to +119
@Query("select b.store.id, count(b) from Booking b " +
"where b.store in :stores " +
"and b.status in (com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.CONFIRMED, " +
"com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.PENDING, " +
"com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.COMPLETED) " +
"group by b.store.id")
List<Object[]> countActiveBookingsByStores(@Param("stores") List<Store> stores);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

List<Object[]> 대신 타입 안전한 반환 타입 사용을 권장합니다.

List<Object[]>는 인덱스 기반 접근이 필요하여 호출부에서 캐스팅 실수가 발생하기 쉽습니다. DTO 프로젝션이나 record를 사용하면 타입 안전성과 가독성이 향상됩니다.

♻️ 인터페이스 프로젝션 예시
public interface StoreBookingCount {
    Long getStoreId();
    Long getBookingCount();
}
-    `@Query`("select b.store.id, count(b) from Booking b " +
+    `@Query`("select b.store.id as storeId, count(b) as bookingCount from Booking b " +
             "where b.store in :stores " +
             "and b.status in (com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.CONFIRMED, " +
             "com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.PENDING, " +
             "com.eatsfine.eatsfine.domain.booking.enums.BookingStatus.COMPLETED) " +
             "group by b.store.id")
-    List<Object[]> countActiveBookingsByStores(`@Param`("stores") List<Store> stores);
+    List<StoreBookingCount> countActiveBookingsByStores(`@Param`("stores") List<Store> stores);
🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java`
around lines 113 - 119, The repository method countActiveBookingsByStores
currently returns List<Object[]> which is error-prone; replace it with a
type-safe projection (either an interface or a record) such as StoreBookingCount
(with methods/getters getStoreId() and getBookingCount()) and change the
BookingRepository method signature to return List<StoreBookingCount>; update the
`@Query` to select the projected fields (store id and count) matching the
projection and keep the same parameter `@Param`("stores") List<Store> stores so
callers receive a typed result instead of Object[].

@twodo0 twodo0 force-pushed the feature/mypage-store branch from 0fac292 to 1451dda Compare February 12, 2026 05:40
@twodo0 twodo0 merged commit 3ebd37a into develop Feb 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants