Skip to content

D3vJ30n/zb-payment-study

Repository files navigation

# πŸͺ 맀μž₯ μ˜ˆμ•½ 및 리뷰 관리 μ‹œμŠ€ν…œ

## πŸ“ ν”„λ‘œμ νŠΈ μ†Œκ°œ

이 ν”„λ‘œμ νŠΈλŠ” 맀μž₯ μ˜ˆμ•½ 및 리뷰 관리λ₯Ό μœ„ν•œ REST API μ„œλΉ„μŠ€μž…λ‹ˆλ‹€.

### 핡심 κΈ°λŠ₯

- νŒŒνŠΈλ„ˆ(점μž₯): 맀μž₯ 등둝/관리 및 μ˜ˆμ•½ 승인/거절
- 일반 μ‚¬μš©μž: 맀μž₯ 검색, μ˜ˆμ•½, 체크인, 리뷰 μž‘μ„±

---

## βš™οΈ 개발 ν™˜κ²½

- μ–Έμ–΄: Java 17
- ν”„λ ˆμž„μ›Œν¬: Spring Boot 3.3.7
- λ°μ΄ν„°λ² μ΄μŠ€: MySQL 8.0
- λΉŒλ“œ 도ꡬ: Gradle
- ν…ŒμŠ€νŠΈ: JUnit5, Mockito
- 기타 라이브러리
    - Spring Data JPA
    - QueryDSL (동적 쿼리 처리)
    - JWT (인증/인가)
    - Lombok (μ½”λ“œ κ°„μ†Œν™”)

---

## πŸ” μ£Όμš” κΈ°λŠ₯

### 1️⃣ νšŒμ›κ°€μž… 및 맀μž₯ 등둝

- νšŒμ› κΆŒν•œ 관리
    - USER: 일반 μ‚¬μš©μž
    - PARTNER: 맀μž₯ κ΄€λ¦¬μž(점μž₯)
- JWT 기반 인증
    - Access Token λ°œκΈ‰/검증
    - κΆŒν•œλ³„ API μ ‘κ·Ό μ œμ–΄
- 맀μž₯ 관리 (PARTNER μ „μš©)
    - 맀μž₯ 등둝/μˆ˜μ •/μ‚­μ œ
    - 맀μž₯ 정보: 이름, μœ„μΉ˜, μ„€λͺ…, μš΄μ˜μ‹œκ°„ λ“±

### 2️⃣ 맀μž₯ 검색 및 μ˜ˆμ•½

- 맀μž₯ 검색 κΈ°λŠ₯
    - ν‚€μ›Œλ“œ 검색 (맀μž₯λͺ…, μœ„μΉ˜)
    - μ •λ ¬ μ˜΅μ…˜
        - κ°€λ‚˜λ‹€μˆœ βœ…
        - λ³„μ μˆœ βœ… (평균 별점 κΈ°μ€€)
        - 거리순 βœ… (ν˜„μž¬ μœ„μΉ˜ κΈ°μ€€)
    - 동적 검색 쑰건 (QueryDSL)
    - νŽ˜μ΄μ§• 처리
- μ˜ˆμ•½ μ‹œμŠ€ν…œ
    - μ˜ˆμ•½ 생성 β†’ 점μž₯ 승인 ν•„μš”
    - μ˜ˆμ•½ μƒνƒœ 관리
        - PENDING: 승인 λŒ€κΈ°
        - APPROVED: 승인됨
        - REJECTED: 거절됨
        - CHECKED_IN: 체크인 μ™„λ£Œ
        - COMPLETED: 이용 μ™„λ£Œ

### 맀μž₯ 검색 API μ˜ˆμ‹œ

- μ •λ ¬ μ˜΅μ…˜
    - `sort=name,asc`: 맀μž₯λͺ… μ˜€λ¦„μ°¨μˆœ
    - `sort=rating,desc`: 별점 λ†’μ€μˆœ
    - `sort=distance,asc`: κ°€κΉŒμš΄ 순 (μœ„μΉ˜ 정보 ν•„μš”)
        - `latitude`: ν˜„μž¬ μœ„λ„
        - `longitude`: ν˜„μž¬ 경도

### 3️⃣ 체크인 및 리뷰

- 체크인 μ‹œμŠ€ν…œ
    - μ˜ˆμ•½ μ‹œκ°„ 10λΆ„ μ „λΆ€ν„° 체크인 κ°€λŠ₯
    - ν‚€μ˜€μŠ€ν¬ 인증 μ½”λ“œ 검증
- 리뷰 μ‹œμŠ€ν…œ
    - 이용 μ™„λ£Œ ν›„ 리뷰 μž‘μ„± κ°€λŠ₯
    - 별점 및 ν…μŠ€νŠΈ 리뷰
    - κΆŒν•œ 관리
        - μˆ˜μ •: μž‘μ„±μžλ§Œ κ°€λŠ₯
        - μ‚­μ œ: μž‘μ„±μž λ˜λŠ” 맀μž₯ κ΄€λ¦¬μž

---

## πŸ“‹ API λͺ…μ„Έ

### νšŒμ› API

- νšŒμ›κ°€μž…: `POST /api/members/signup`
    - Request: 이메일, λΉ„λ°€λ²ˆν˜Έ, 이름, μ—­ν• (USER/PARTNER)
- 둜그인: `POST /api/members/login`
    - Response: JWT 토큰

### 맀μž₯ API

- 맀μž₯ 등둝: `POST /api/stores`
- 맀μž₯ λͺ©λ‘ 쑰회: `GET /api/stores`
    - Query Parameters:
        - keyword: 검색어
        - sort: μ •λ ¬ κΈ°μ€€
        - page: νŽ˜μ΄μ§€ 번호
        - size: νŽ˜μ΄μ§€ 크기
- 맀μž₯ 상세 쑰회: `GET /api/stores/{id}`

### μ˜ˆμ•½ API

- μ˜ˆμ•½ 생성: `POST /api/reservations`
- μ˜ˆμ•½ 승인/거절: `PATCH /api/reservations/{id}`
- 체크인: `POST /api/reservations/check-in`

### 리뷰 API

- 리뷰 μž‘μ„±: `POST /api/reviews`
- 리뷰 μˆ˜μ •: `PATCH /api/reviews/{id}`
- 리뷰 μ‚­μ œ: `DELETE /api/reviews/{id}`
- 맀μž₯별 리뷰 쑰회: `GET /api/reviews/stores/{id}`

---

## πŸ—οΈ ν”„λ‘œμ νŠΈ ꡬ쑰

```plaintext
src
β”œβ”€β”€ main
β”‚   β”œβ”€β”€ java
β”‚   β”‚   └── com.zerobase.zbpaymentstudy
β”‚   β”‚       β”œβ”€β”€ common      // 곡톡 κΈ°λŠ₯ (예: 응닡 ν˜•μ‹, μ˜ˆμ™Έ 처리)
β”‚   β”‚       β”œβ”€β”€ config      // μ„€μ • 파일 (예: JWT, Security)
β”‚   β”‚       β”œβ”€β”€ domain      // 도메인별 κΈ°λŠ₯ (νšŒμ›, 맀μž₯, μ˜ˆμ•½, 리뷰)
β”‚   β”‚       β”‚   β”œβ”€β”€ member       // νšŒμ› 관리
β”‚   β”‚       β”‚   β”œβ”€β”€ reservation  // μ˜ˆμ•½ 관리
β”‚   β”‚       β”‚   β”œβ”€β”€ review       // 리뷰 관리
β”‚   β”‚       β”‚   └── store        // 맀μž₯ 관리
β”‚   β”‚       └── exception   // μ»€μŠ€ν…€ μ˜ˆμ™Έ 처리
β”‚   └── resources               // μ„€μ • 파일 및 λ¦¬μ†ŒμŠ€
└── test                        // ν…ŒμŠ€νŠΈ μ½”λ“œ

πŸ“Š μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜

계측 ꡬ쑰

Architecture Diagram

μ‹œμŠ€ν…œμ€ λ‹€μŒκ³Ό 같은 계측 ꡬ쑰둜 μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

  • Presentation Layer: REST API μ—”λ“œν¬μΈνŠΈ 제곡 및 μš”μ²­/응닡 처리
  • Business Layer: 핡심 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 및 νŠΈλžœμž­μ…˜ 관리
  • Persistence Layer: λ°μ΄ν„°λ² μ΄μŠ€ μ—°μ‚° 및 데이터 μ ‘κ·Ό
  • Domain Layer: λΉ„μ¦ˆλ‹ˆμŠ€ μ—”ν‹°ν‹° 및 κ·œμΉ™ μ •μ˜

μ˜ˆμ•½ ν”„λ‘œμ„ΈμŠ€ 흐름

Sequence Diagram

μ˜ˆμ•½λΆ€ν„° λ¦¬λ·°κΉŒμ§€μ˜ 전체 ν”„λ‘œμ„ΈμŠ€λ₯Ό μ‹œκ°ν™”ν•˜μ—¬ ν‘œν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.


πŸ’Ύ ERD 섀계

ERD Diagram

μ£Όμš” μ—”ν‹°ν‹°

  • Member: μ‚¬μš©μž 정보 관리 (일반 μ‚¬μš©μž/νŒŒνŠΈλ„ˆ)
  • Store: 맀μž₯ 정보 관리
  • Reservation: μ˜ˆμ•½ 정보 및 μƒνƒœ 관리
  • Review: 리뷰 정보 관리

핡심 관계

  • Member(1) - Store(N): νŒŒνŠΈλ„ˆλŠ” μ—¬λŸ¬ 맀μž₯을 μ†Œμœ ν•  수 있음
  • Store(1) - Reservation(N): 맀μž₯은 μ—¬λŸ¬ μ˜ˆμ•½μ„ κ°€μ§ˆ 수 있음
  • Reservation(1) - Review(1): ν•˜λ‚˜μ˜ μ˜ˆμ•½λ‹Ή ν•˜λ‚˜μ˜ 리뷰 μž‘μ„± κ°€λŠ₯

πŸš€ μ‹œμž‘ν•˜κΈ°

μš”κ΅¬μ‚¬ν•­

  • Java 17
  • MySQL 8.0

μ‹€ν–‰ 방법

  1. μ €μž₯μ†Œ 클둠

    git clone https://github.com/your-username/store-reservation.git
  2. λ°μ΄ν„°λ² μ΄μŠ€ μ„€μ •

    • application.yml νŒŒμΌμ— λ°μ΄ν„°λ² μ΄μŠ€ 정보λ₯Ό μž…λ ₯ν•©λ‹ˆλ‹€.
    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/your_database
        username: your_username
        password: your_password
  3. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰

    ./gradlew bootRun

πŸ§ͺ ν…ŒμŠ€νŠΈ

ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•˜λ €λ©΄ λ‹€μŒ λͺ…λ Ήμ–΄λ₯Ό μ‚¬μš©ν•˜μ„Έμš”.

./gradlew test

πŸ“ λΌμ΄μ„ μŠ€

이 ν”„λ‘œμ νŠΈλŠ” MIT λΌμ΄μ„ μŠ€ ν•˜μ— λ°°ν¬λ©λ‹ˆλ‹€.
μžμ„Έν•œ λ‚΄μš©μ€ LICENSE νŒŒμΌμ„ μ°Έκ³ ν•˜μ„Έμš”.


μ£Όμš” μ½”λ“œ μ˜ˆμ‹œ

μ˜ˆμ•½ μ„œλΉ„μŠ€ κ΅¬ν˜„

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class ReservationServiceImpl implements ReservationService {
    private final ReservationRepository reservationRepository;
    private final MemberRepository memberRepository;
    private final StoreRepository storeRepository;

    @Override
    public ApiResponse<ReservationDto> createReservation(String memberEmail, ReservationCreateDto dto) {
        try {
            Member member = memberRepository.findByEmail(memberEmail)
                .orElseThrow(() -> new BusinessException(ErrorCode.MEMBER_NOT_FOUND));

            Store store = storeRepository.findById(dto.storeId())
                .orElseThrow(() -> new BusinessException(ErrorCode.STORE_NOT_FOUND));

            validateReservationTime(dto.reservationTime());

            Reservation reservation = Reservation.builder()
                .store(store)
                .member(member)
                .reservationTime(dto.reservationTime())
                .status(ReservationStatus.PENDING)
                .createdAt(LocalDateTime.now())
                .updatedAt(LocalDateTime.now())
                .build();

            Reservation savedReservation = reservationRepository.save(reservation);
            log.info("μ˜ˆμ•½ 생성 μ™„λ£Œ - memberEmail: {}, storeId: {}", memberEmail, dto.storeId());

            return new ApiResponse<>("SUCCESS", "μ˜ˆμ•½μ΄ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", ReservationDto.from(savedReservation));
        } catch (BusinessException e) {
            log.warn("μ˜ˆμ•½ 생성 μ‹€νŒ¨ - {}", e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("μ˜ˆμ•½ 생성 쀑 였λ₯˜ λ°œμƒ", e);
            throw new BusinessException(ErrorCode.INTERNAL_SERVER_ERROR);
        }
    }
}

리뷰 μ‹œμŠ€ν…œ κ΅¬ν˜„

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class ReviewServiceImpl implements ReviewService {
    private final ReviewRepository reviewRepository;
    private final ReservationRepository reservationRepository;

    @Override
    public ApiResponse<ReviewDto> createReview(String memberEmail, ReviewCreateDto dto) {
        try {
            Reservation reservation = reservationRepository.findById(dto.reservationId())
                .orElseThrow(() -> new BusinessException(ErrorCode.RESERVATION_NOT_FOUND));

            validateReviewCreation(reservation, memberEmail);

            Review review = Review.builder()
                .reservation(reservation)
                .rating(dto.rating())
                .content(dto.content())
                .createdAt(LocalDateTime.now())
                .build();

            Review savedReview = reviewRepository.save(review);
            return new ApiResponse<>("SUCCESS", "리뷰가 μž‘μ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", ReviewDto.from(savedReview));
        } catch (BusinessException e) {
            throw e;
        }
    }
}

About

For Study

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages