From 099f069985953c1fd5eb11bad717a5d881f3c640 Mon Sep 17 00:00:00 2001 From: yeon-22k Date: Sat, 18 Oct 2025 02:02:49 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat(readme):=20api-doc=20=EC=9E=91?= =?UTF-8?q?=EC=97=85,=20=EA=B0=9C=EB=B0=9C=ED=99=98=EA=B2=BD=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- api-docs.md | 962 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 924 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 2fef77b..a6876e7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ TDD(Today Delicious Delivery)는 이름 그대로 '오늘의 맛있는 배달' | 분류 | 상세 | |---------------------|----------------------------------------------------------------------------| | **Back-End** | Java 21, Spring Boot 3.5.6, Spring Data JPA, Querydsl 7.0, Spring Security | -| **Database** | PostgreSQL 18.0 | + **Database** | PostgreSQL 17.6 | | **Build Tool** | Gradle | | **Infra** | Docker compose, Github Actions(CI) | | **open API** | Google GenAI API, Naver Map API | diff --git a/api-docs.md b/api-docs.md index 070bb23..8904bf2 100644 --- a/api-docs.md +++ b/api-docs.md @@ -2,53 +2,937 @@ ## 도메인 개요 -- **Address**: 가게와 사용자 주소를 등록 및 관리합니다. -- **AI**: Google GenAI 를 활용한 글 생성을 제공합니다. -- **Auth**:사용자의 회원가입, 로그인, 토큰 발급 및 검증을 포함한 인증과 인가 로직을 담당합니다. -- **Cart**:장바구니는 주문 전에 사용자가 선택한 메뉴 아이템을 임시로 저장하고 관리합니다. -- **Coupon**: MASTER 및 STORE 쿠폰 발행, 수정, 삭제, 조회와 사용자의 쿠폰 발급 및 상태를 관리합니다. -- **Menu**: 가게별 메뉴의 등록, 수정, 삭제, 조회를 관리하며 메뉴의 노출상태와 가격 등의 정보를 제공합니다. -- **Order**: 사용자의 주문 요청부터 상태 변경까지의 주문 처리 전 과정을 제어합니다. -- **OrderMenu**: 개별 주문에 포함된 메뉴 항목을 관리하며, 주문-메뉴 간 다대다 관계를 매핑하는 역할을 수행합니다. -- **Payment**: 주문에 대한 결제 정보를 관리하며, 결제 요청/승인/취소 등의 프로세스를 처리합니다. -- **Review**: 고객의 리뷰 작성, 수정, 삭제와 점주의 답글 기능을 지원하여 양방향 피드백 시스템을 제공합니다. -- **Store**: 가게의 등록, 수정, 검색, 상세조회 기능을 담당하며 점주와 고객 간의 매장 정보 접근 제어를 수행합니다. -- **User**: 사용자의 권한(Role) 및 프로필 정보를 관리하며 이용자 유형에 따른 접근 가능 리소스를 제한합니다. +| 도메인 | 설명 | +|---------------|-------------------------------------------------------------| +| **Address** | 가게와 사용자 주소를 등록 및 관리합니다. | +| **AI** | Google GenAI 를 활용한 글 생성을 제공합니다. | +| **Auth** | 사용자의 회원가입, 로그인, 토큰 발급 및 검증을 포함한 인증과 인가 로직을 담당합니다. | +| **Cart** | 장바구니는 주문 전에 사용자가 선택한 메뉴 아이템을 임시로 저장하고 관리합니다. | +| **Coupon** | MASTER 및 STORE 쿠폰 발행, 수정, 삭제, 조회와 사용자의 쿠폰 발급 및 상태를 관리합니다. | +| **Menu** | 가게별 메뉴의 등록, 수정, 삭제, 조회를 관리하며 메뉴의 노출상태와 가격 등의 정보를 제공합니다. | +| **Order** | 사용자의 주문 요청부터 상태 변경까지의 주문 처리 전 과정을 제어합니다. | +| **OrderMenu** | 개별 주문에 포함된 메뉴 항목을 관리하며, 주문-메뉴 간 다대다 관계를 매핑하는 역할을 수행합니다. | +| **Payment** | 주문에 대한 결제 정보를 관리하며, 결제 요청/승인/취소 등의 프로세스를 처리합니다. | +| **Review** | 고객의 리뷰 작성, 수정, 삭제와 점주의 답글 기능을 지원하여 양방향 피드백 시스템을 제공합니다. | +| **Store** | 가게의 등록, 수정, 검색, 상세조회 기능을 담당하며 점주와 고객 간의 매장 정보 접근 제어를 수행합니다. | +| **User** | 사용자의 권한(Role) 및 프로필 정보를 관리하며 이용자 유형에 따른 접근 가능 리소스를 제한합니다. | + +## API 명세서 ![GET](https://img.shields.io/badge/GET-2196F3?style=flat) ![POST](https://img.shields.io/badge/POST-4CAF50?style=flat) ![PATCH](https://img.shields.io/badge/PATCH-FFC107?style=flat) ![DELETE](https://img.shields.io/badge/DELETE-F44336?style=flat) -## API 명세서 - - - - - - - - + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
도메인기능메서드URIRequestResponse
Address가게 주소 등록메서드/v1/address/store
{  
-"roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
-"jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
-“detailAddress”: “101동”,
-"latitude": "127.1052160",
-"longitude": "37.3595033"
-}
-
도메인기능메서드URIRequestResponse
Address가게 주소 등록POST/v1/address/store
{
+  "roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
+  "jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
+  "detailAddress": "101동",
+  "latitude": "127.1052160",
+  "longitude": "37.3595033"
+}
Address가게 주소 수정메서드/v1/address/store/{storeId}
{  
-"roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
-"jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
-“detailAddress”: “101동”,
-"latitude": "127.1052160",
-"longitude": "37.3595033"
-}
-
Address가게 주소 수정PATCH/v1/address/store/{storeId}
Address가게 주소 삭제DELETE/v1/address/store/{storeId}
Address회원 주소 등록POST/v1/address/user
{
+  "roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
+  "jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
+  "detailAddress": "101동",
+  "alias": "집",
+  "latitude": "127.1052160",
+  "longitude": "37.3595033"
+}
Address회원 주소 조회GET/v1/address/user
{}
여기 넣으세요
Address회원 주소 수정PATCH/v1/address/user/{addressId}
{
+  "roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
+  "jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
+  "detailAddress": "102동",
+  "alias": "회사",
+  "latitude": "127.1052160",
+  "longitude": "37.3595033"
+}
여기 넣으세요
Address회원 주소 삭제DELETE/v1/address/user/{addressId}
{}
여기 넣으세요
AI음식 소개 작성POST/v1/ai/req
{
+  "content": "요청내용"
+}
{
+  "id": "id",
+  "inputText": "만두",
+  "outputText": "오감만족, 인생만두!",
+  "createdAt": "2025-10-02T12:57:43.983978"
+}
Auth회원가입POST/v1/auth/signup
{
+  "username": "username",
+  "password": "password"
+}
{
+  "userId": 1
+}
Auth유저네임 중복확인GET/v1/auth/exists?username={username}
{}
{
+  "exists": true
+}
Auth로그인POST/v1/auth/login
{
+  "username": "username",
+  "password": "password"
+}
{
+  "userId": 1
+}
Auth로그아웃POST/v1/auth/logout
{}
{}
Auth회원 탈퇴DELETE/v1/auth/withdrawal
{}
{}
Auth토큰 재발급POST/v1/auth/token/reissue
{}
AT: 헤더, RT: 쿠키
Cart장바구니 조회GET/v1/cart
{}
{
+  "cartId": "UUID",
+  "userId": 1,
+  "items": [
+    {
+      "cartItemId": "UUID",
+      "menuId": "UUID",
+      "menuName": "김치찌개",
+      "price": 8000,
+      "quantity": 2,
+      "totalPrice": 16000,
+      "storeId": "UUID",
+      "storeName": "맛있는식당"
+    }
+  ],
+  "totalPrice": 16000,
+  "storeId": "UUID"
+}
Cart장바구니에 아이템 추가POST/v1/cart/items
{
+  "menuId": "UUID",
+  "quantity": 2
+}
{
+  "cartId": "UUID",
+  "userId": 1,
+  "items": [
+    {
+      "cartItemId": "UUID",
+      "menuId": "UUID",
+      "menuName": "김치찌개",
+      "price": 8000,
+      "quantity": 2,
+      "totalPrice": 16000,
+      "storeId": "UUID",
+      "storeName": "맛있는식당"
+    }
+  ],
+  "totalPrice": 16000,
+  "storeId": "UUID"
+}
Cart장바구니 아이템 수량 수정PATCH/v1/cart/items/{cartItemId}?quantity={quantity}
{}
{
+  "cartId": "UUID",
+  "userId": 1,
+  "items": [],
+  "totalPrice": 0,
+  "storeId": null
+}
Cart장바구니 아이템 삭제DELETE/v1/cart/items/{cartItemId}
{}
{
+  "cartId": "UUID",
+  "userId": 1,
+  "items": [],
+  "totalPrice": 0,
+  "storeId": null
+}
Cart장바구니 비우기DELETE/v1/cart
{}
{
+  "cartId": "UUID",
+  "userId": 1,
+  "items": [],
+  "totalPrice": 0,
+  "storeId": null
+}
Coupon매장 쿠폰 목록 조회GET/v1/coupon/list/{storeId}
{}
{}
Coupon내 쿠폰 목록 조회GET/v1/coupon/my/list
{}
{}
CouponStore 쿠폰 등록POST/v1/coupon/{storeId}
{}
{}
CouponMaster 쿠폰 등록POST/v1/coupon/{storeId}
{}
{}
Coupon쿠폰 발급POST/v1/coupon/{couponId}
{}
{}
Coupon쿠폰 수정PATCH/v1/coupon/{couponId}
{}
{}
Coupon쿠폰 사용POST/v1/coupon/{couponId}
{}
{}
Coupon쿠폰 삭제DELETE/v1/coupon/{couponId}
{}
{}
Coupon쿠폰 만료DELETE/v1/coupon/expire/{couponId}
{}
{}
Menu메뉴 조회(목록)GET/v1/store/{storeId}/menu
{}
[
+  {
+    "menuId": "550e8400-e29b-41d4-a716-446655440000",
+    "name": "치킨버거",
+    "description": "바삭한 치킨과 신선한 야채가 들어간 버거",
+    "price": 8900,
+    "imageUrl": "https://example.com/images/chicken-burger.jpg",
+    "isHidden": false
+  },
+  {
+    "menuId": "551e8400-e29b-41d4-a716-446655440001",
+    "name": "새우버거",
+    "description": "통통한 새우와 타르타르소스의 조화",
+    "price": 9500,
+    "imageUrl": "https://example.com/images/shrimp-burger.jpg",
+    "isHidden": false
+  }
+]
Menu메뉴 조회(개별)GET/v1/store/{storeId}/menu/{menuId}
{}
{
+  "menuId": "550e8400-e29b-41d4-a716-446655440000",
+  "storeId": "660e8400-e29b-41d4-a716-446655440000",
+  "name": "치킨버거",
+  "description": "바삭한 치킨과 신선한 야채가 들어간 버거",
+  "price": 8900,
+  "imageUrl": "https://example.com/images/chicken-burger.jpg",
+  "isHidden": false
+}
Menu메뉴 등록POST/v1/store/{storeId}/menu
{
+  "name": "치킨버거",
+  "description": "바삭한 치킨과 신선한 야채가 들어간 버거",
+  "price": 8900,
+  "imageUrl": "https://example.com/images/chicken-burger.jpg",
+  "isHidden": false
+}
{
+  "menuId": "550e8400-e29b-41d4-a716-446655440000",
+  "restaurantId": "660e8400-e29b-41d4-a716-446655440000",
+  "name": "치킨버거",
+  "description": "바삭한 치킨과 신선한 야채가 들어간 버거",
+  "price": 8900,
+  "imageUrl": "https://example.com/images/chicken-burger.jpg",
+  "isHidden": false,
+  "createdAt": "2025-09-29T12:00:00.000000000"
+}
Menu메뉴 수정PATCH/v1/store/{storeId}/menu/{menuId}
{ "name": "수정버거" }
{}
Menu메뉴 상태 변경PATCH/v1/store/{storeId}/menu/{menuId}/status
{ "isHidden": true }
{}
Menu메뉴 삭제DELETE/v1/store/{storeId}/menu/{menuId}
{}
{}
Order주문 목록 조회GET/v1/orders?from={startDate}&to={endDate}&status={status}&userId={userId}&storeId={storeId}&sort={sort}&page={page}&size={size}
{}
{ 
+  "totalElements": 0,
+  "totalPages": 0,
+  "size": 0,
+  "content": [
+    {
+      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+      "customerName": "string",
+      "storeName": "string",
+      "price": 0,
+      "address": "string",
+      "orderMenuList": [
+        {
+          "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+          "name": "string",
+          "price": 0,
+          "quantity": 0
+        }
+      ],
+      "createdAt": "2025-10-17T08:14:04.380Z",
+      "orderStatus": "CANCELLED"
+    }
+  ],
+  "number": 0,
+  "sort": {
+    "empty": true,
+    "sorted": true,
+    "unsorted": true
+  },
+  "first": true,
+  "last": true,
+  "numberOfElements": 0,
+  "pageable": {
+    "offset": 0,
+    "sort": {
+      "empty": true,
+      "sorted": true,
+      "unsorted": true
+    }
+  }
+}
Order주문 조회GET/v1/orders/{orderId}
{}
{ 
+  "id": 1002,
+  "customerName": "c",
+  "storeName": "d",
+  "price": 45000,
+  "address": "부산시 어떤가 456",
+  "orderMenuList": [
+    { 
+      "id": "UUID", 
+      "name": "탕수육", 
+      "price": 20000, 
+      "quantity": 1 
+    },
+    { 
+      "id": "UUID", 
+      "name": "깐풍기", 
+      "price": 25000, 
+      "quantity": 2 
+    }
+  ],
+  "createdAt": "2025-09-29T12:34:56",
+  "orderStatus": "DELIVERED"
+}
Order주문 등록POST/v1/orders
{
+  "address": "부산시 어떤가 456",
+  "customerName": "c",
+  "storeId": "UUID",
+  "storeName": "d",
+  "price": 45000,
+  "menu": [
+    { 
+      "id": "UUID", 
+      "name": "탕수육", 
+      "price": 20000, 
+      "quantity": 1 
+    },
+    { 
+      "id": "UUID", 
+      "name": "깐풍기", 
+      "price": 25000, 
+      "quantity": 2 
+    }
+  ]
+}
{
+  "id": 1002,
+  "customerName": "c",
+  "storeName": "d",
+  "price": 45000,
+  "address": "부산시 어떤가 456",
+  "orderMenuList": [
+    { 
+      "id": "UUID", 
+      "name": "탕수육", 
+      "price": 20000, 
+      "quantity": 1 
+    },
+    { 
+      "id": "UUID", 
+      "name": "깐풍기", 
+      "price": 25000, 
+      "quantity": 2 
+    }
+  ],
+  "createdAt": "2025-09-29T12:34:56",
+  "orderStatus": "DELIVERED"
+}
Order주문 상태 변경(관리자)PATCH/v1/orders/{orderId}
{ "status": "DELIVERED" }
{}
Order주문 상태 변경(가게주인, 관리자)PATCH/orderId/next-status
{}
{}
Order주문 취소PATCH/cancel/{orderId}
{}
{ 
+  "id": 1002,
+  "customerName": "c",
+  "storeName": "d",
+  "price": 45000,
+  "address": "부산시 어떤가 456",
+  "orderMenuList": [
+    { 
+      "id": "UUID", 
+      "name": "탕수육", 
+      "price": 20000, 
+      "quantity": 1 
+    },
+    { 
+      "id": "UUID", 
+      "name": "깐풍기", 
+      "price": 25000, 
+      "quantity": 2 
+    }
+  ],
+  "createdAt": "2025-09-29T12:34:56",
+  "orderStatus": "CANCELLED"
+}
Payment결제내역 조회GET/v1/payments?page=1&size=10&orderBy={정렬조건}&keyword={검색조건}
{}
{
+  "payments": [
+    {
+      "id": 1,
+      "paymentNumber": "12312312",
+      "status": "결제 성공",
+      "storeName": "음식점1",
+      "price": 10000
+    }
+  ]
+}
Payment결제내역 상세 조회GET/v1/payments/{paymentId}
{}
{
+  "paymentNumber": "1q2w3e4r!",
+  "price": 10000,
+  "cardCompany": "XXX카드",
+  "cardNumber": "1111 2222 3333 4444",
+  "approvedAt": "2025-09-29T10:15:00",
+  "restaurant": {
+    "id": 1,
+    "storeName": "음식점",
+    "phone": "02-1234-1234",
+    "address": "서울 어떤가"
+  },
+  "orderItem": [
+    {
+      "id": 5,
+      "storeName": "음식1",
+      "quantity": 1,
+      "price": 5000,
+      "totalPrice": 5000
+    }
+  ]
+}
Payment결제 상태 변경PATCH/v1/payments/status/{paymentId}
{ "status": "결제 완료" }
{}
Payment결제 요청POST/v1/payments
{
+  "cardCompany": "삼성",
+  "cardNumber": "123123"
+}
{ "status": "approve" }
Review리뷰 등록POST/v1/reviews/order/{orderId}
{
+  "content": "리뷰 내용",
+  "storeId": 1,
+  "rating": 3,
+  "photos": "abc.png"
+}
{
+  "reviewId": 123,
+  "storeId": 1,
+  "content": "너무 맛있어요!",
+  "rating": 3,
+  "userId": "user01",
+  "photos": "abc.png",
+  "createdAt": "2025-09-29T10:15:00",
+  "modifiedAt": "2025-09-29T10:20:00"
+}
Review리뷰 수정PATCH/v1/reviews/{reviewId}
{
+  "content": "리뷰 내용",
+  "rating": 3,
+  "photos": "abc.png"
+}
{
+  "reviewId": 123,
+  "storeId": 1,
+  "content": "너무 맛있어요!",
+  "rating": 3,
+  "userId": "user01",
+  "photos": "abc.png",
+  "createdAt": "2025-09-29T10:15:00",
+  "modifiedAt": "2025-09-29T10:20:00"
+}
Review리뷰 개별 조회GET/v1/reviews/{reviewId}
{}
{
+  "reviewId": 123,
+  "storeId": 1,
+  "content": "너무 맛있어요!",
+  "rating": 3,
+  "userId": "user01",
+  "photos": "abc.png",
+  "createdAt": "2025-09-29T10:15:00",
+  "modifiedAt": "2025-09-29T10:20:00"
+}
Review리뷰 목록 조회GET/v1/reviews/{storeId}?page=1&size=10
{}
{
+  "reviews": [
+    {
+      "reviewId": 101,
+      "storeId": 1,
+      "content": "음식이 맛있어요!",
+      "rating": 3,
+      "userId": "user01",
+      "photos": "abc.png",
+      "createdAt": "2025-09-25T14:30:00",
+      "modifiedAt": "2025-09-29T10:20:00",
+      "reply": {
+        "content": "이용해주셔서 감사합니다"
+      }
+    },
+    {
+      "reviewId": 102,
+      "storeId": 1,
+      "content": "서비스가 친절했어요.",
+      "rating": 3,
+      "userId": "user02",
+      "photos": "abc.png",
+      "createdAt": "2025-09-26T16:45:00",
+      "modifiedAt": "2025-09-29T10:20:00",
+      "reply": {
+        "content": "다음에도 오세요"
+      }
+    }
+  ],
+  "pageInfo": {
+    "page": 1,
+    "size": 10,
+    "totalElements": 52,
+    "totalPages": 6,
+    "hasNext": true
+  }
+}
Review리뷰 삭제DELETE/v1/reviews/{reviewId}
{}
{}
Review리뷰 답글 등록POST/v1/reviews/{reviewId}/reply
{ "content": "사장님 답글 내용" }
{
+  "replyId": 88,
+  "reviewId": 123,
+  "storeId": 1,
+  "ownerId": 1,
+  "content": "리뷰 감사합니다! 또 이용해주세요",
+  "createdAt": "2025-09-29T10:15:00",
+  "modifiedAt": "2025-09-29T10:20:00"
+}
Review리뷰 답글 수정PATCH/v1/reviews/{reviewId}/reply
{ "content": "사장님 수정 답글 내용" }
{
+  "replyId": 88,
+  "reviewId": 123,
+  "storeId": 1,
+  "ownerId": 1,
+  "content": "리뷰 감사합니다! 또 이용해주세요",
+  "createdAt": "2025-09-29T10:15:00",
+  "modifiedAt": "2025-09-29T10:20:00"
+}
Review리뷰 답글 삭제DELETE/v1/reviews/{reviewId}/reply
{}
{}
Store음식점 검색GET/v1/search?name={keyword}&category={keyword}&description={keyword}&sort=createdAt,desc&page=2&size=30
{ "String": "keyword" }
{
+  "content": [
+    {
+      "id": "d9756dfd-99e9-480d-a7f7-4e38b00d5163",
+      "name": "맛스터치 강남점",
+      "ownerName": "jae",
+      "category": "CHICKEN",
+      "description": "강남 최고 치킨버거",
+      "imageUrl": "https://example.com/images/chicken-burger.jpg",
+      "avgRating": 0,
+      "reviewCount": 0,
+      "orderCount": null,
+      "menus": [
+        {
+          "menuId": "bf03b171-1ccb-4173-80b6-e890a176f69b2",
+          "name": "싸이버거",
+          "description": "맘스터치 대표 메뉴",
+          "price": 5500,
+          "imageUrl": null,
+          "isHidden": null
+        },
+        {
+          "menuId": "704c5850-d379-454d-8f66-6fdba7e5e22",
+          "name": "감자튀김",
+          "description": "바삭한 감자튀김",
+          "price": 2500,
+          "imageUrl": null,
+          "isHidden": null
+        }
+      ]
+    }
+  ]
+}
Store음식점 상세 조회GET/v1/store/{storeId}
{ "storeId": "storeId" }
{
+  "status": 200,
+  "message": "음식점 상세 조회에 성공했습니다.",
+  "data": {
+    "name": "name",
+    "category": "category",
+    "description": "description",
+    "address": "address",
+    "imageUrl": "imageUrl",
+    "avgRating": 4.5,
+    "reviewCount": 500
+  }
+}
Store음식점 등록POST/v1/store
{
+  "name": "name",
+  "category": "category",
+  "description": "description",
+  "address": "address",
+  "imageUrl": "imageUrl"
+}
{
+  "status": 201,
+  "message": "음식점 등록에 성공했습니다.",
+  "data": {
+    "storeId": "storeId",
+    "name": "name",
+    "category": "category",
+    "description": "description",
+    "address": "address",
+    "imageUrl": "imageUrl",
+    "avgRating": 0,
+    "reviewCount": 0,
+    "createdAt": "",
+    "createdBy": ""
+  }
+}
Store음식점 수정PATCH/v1/store/{storeId}
{
+  "storeId": "storeId",
+  "name": "newName",
+  "category": "newCategory",
+  "description": "newDescription",
+  "address": "newAddress",
+  "imageUrl": "newImageUrl"
+}
{
+  "status": 200,
+  "message": "음식점 수정에 성공했습니다.",
+  "data": {
+    "storeId": "storeId",
+    "name": "newName",
+    "category": "newCategory",
+    "description": "newDescription",
+    "address": "newAddress",
+    "imageUrl": "newImageUrl",
+    "avgRating": 4.5,
+    "reviewCount": 500,
+    "updatedAt": "",
+    "updatedBy": ""
+  }
+}
Store음식점 삭제DELETE/v1/store/{storeId}
{}
{
+  "status": 200,
+  "message": "음식점 삭제에 성공했습니다.",
+  "data": {
+    "storeId": "storeId",
+    "deletedAt": "",
+    "deletedBy": ""
+  }
+}
User회원 정보 조회GET/v1/users/{userId}
{}
{
+  "userId": "userId",
+  "username": "username"
+}
User회원 닉네임 수정PATCH/v1/users/{userId}/nickname
{
+  "nickname": "nickname"
+}
{}
User회원 비밀번호 수정PATCH/v1/users/{userId}/password
{
+  "password": "password"
+}
{}
User회원 리뷰 목록 조회GET/v1/users/{userId}/reviews
정렬조건
{
+  "data": [
+    {
+      "page": 1,
+      "size": 1,
+      "reviews": {
+        "reviewId": "reviewId",
+        "content": "content",
+        "createdAt": "2025-09-29T10:15:00",
+        "storeName": "storeName"
+      }
+    }
+  ]
+}
User회원 목록 조회GET/v1/users
{ page=1&size=10&sort=createdAt,desc }
{
+  "data": [
+    {
+      "user1": {
+        "userId": "userId",
+        "username": "username"
+      }
+    }
+  ]
+}
User회원 주문 목록 조회GET/v1/users/{userId}/orders
{ page=1&size=10&sort=createdAt,desc }
{
+  "data": [
+    {
+      "orderId": "orderId",
+      "storeName": "storeName",
+      "price": "price",
+      "address": "address",
+      "createdAt": "2025-09-29T12:34:56",
+      "menu": [
+        { "name": "탕수육", "price": 20000, "quantity": 1 },
+        { "name": "깐풍기", "price": 25000, "quantity": 2 }
+      ]
+    }
+  ]
+}
User매니저 권한 부여PATCH/v1/users/{userId}/authority
{}
{}
From f82735b0cc41aa1767c366f3eeab6f1a5b33db4a Mon Sep 17 00:00:00 2001 From: yeon-22k Date: Sat, 18 Oct 2025 02:24:48 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat(readme):=20api-doc=20>=20review=20api?= =?UTF-8?q?=201=EA=B0=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api-docs.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/api-docs.md b/api-docs.md index 8904bf2..5ced295 100644 --- a/api-docs.md +++ b/api-docs.md @@ -728,6 +728,49 @@
{}
{}
+ + Review리뷰 목록 조회(사장님 답글 추가 시)GET/v1/reviews/{storeId}?page=1&size=10 +
{}
+
{
+  "reviews": [
+  {
+    "reviewId": 101,
+    “storeId”: 1,
+    "content": "음식이 맛있어요!",
+    “rating”: 3,
+    "userId": "user01",
+    “photos”: “abc.png”,
+    "createdAt": "2025-09-25T14:30:00",
+    "modifiedAt": "2025-09-29T10:20:00”,
+    “reply”: { 
+      “conetent”: 이용해주셔서 감사합니다
+    }
+
+},
+{
+"reviewId": 102,
+“storeId”: 1,
+"content": "서비스가 친절했어요.",
+“rating”: 3,
+"userId": "user02",
+“photos”: “abc.png”,
+"createdAt": "2025-09-26T16:45:00",
+"modifiedAt": "2025-09-29T10:20:00”
+“reply”: {
+“conetent”: 다음에 또 오세요
+}
+
+}
+],
+"pageInfo": {
+"page": 1,
+"size": 10,
+"totalElements": 52,
+"totalPages": 6,
+"hasNext": true
+}
+}
+ From 57299250bf2da09c04e8b3bc4ae3c7d3451273dc Mon Sep 17 00:00:00 2001 From: yeon-22k Date: Sat, 18 Oct 2025 02:41:33 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat(readme):=20=EC=97=AD=ED=95=A0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=ED=95=B5=EC=8B=AC=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=82=B4=EC=9A=A9=20=EC=B6=94=EA=B0=80,?= =?UTF-8?q?=20erd=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a6876e7..6c5a3c2 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ docker exec -it tdd-db psql -U test -d tdd-db ## 1. ERD -![erd](https://github.com/user-attachments/assets/96efb039-16e5-4f0d-95e6-b0b7e0a40cbb) +![erd](https://github.com/user-attachments/assets/5a08601b-8d4d-4993-a7f2-833bdfa86d16) ## 2. 도메인 다이어그램 @@ -98,6 +98,26 @@ docker exec -it tdd-db psql -U test -d tdd-db ## 1. 도메인별 핵심 기능 상세 구현 +### (1) Order + +![Order diagram](https://github.com/user-attachments/assets/6caa1364-0a1b-4e17-8706-25f320bb0004) + +- Order 도메인 + - DDD 원칙에 따라 주문의 상태 전이(`nextStatus`, `changeOrderStatus`) 로직을 엔티티 내부에 위치 + - Order 와 Menu 의 다대다 관계를 OrderMenu 로 풀어내 도메인 응집도 강화 + - Mapper 를 통해 Service 는 트랜잭션 중심의 조합 역할만 수행 +- getOrders(주문 목록 조회) + - QueryDSL을 이용한 동적 검색 (조건 null 시 필터 미적용) + - MANAGER, MASTER 권한 외 사용자는 본인 주문만 조회 (권한 검증) + - Page 처리로 ID 목록 우선 조회 후 fetch join으로 세부 데이터 일괄 로딩 (N+1 방지) +- nextOrderStatus(주문 단계 변경) + - OWNER 가 가능한 주문 단계변경 메서드로 시스템의 규칙에 따라서만 변경이 가능하도록 nextStatus 로 동작 + - 관리자 권한의 사용자들은 changeOrderStatus 를 통하여 임의 변경가능하도록 별도 API 구성 + +- PageableHandlerMethodArgumentResolver + - 공통 페이징 정책을 일괄 적용하기 위해 WebMvcConfig 에 정책 등록 + - 기본 정렬 기준(`createdAt DESC`)을 지정하여 일관된 정렬 정책 유지 + ## 2. 트러블슈팅 ### (1) CustomPageableResolver Bean 등록 미적용 문제 @@ -354,11 +374,11 @@ class StoreRepositoryTest extends RepositoryTest { # 팀원 소개 -| 팀원 | 깃허브 | 역할 | -|-----|--------------------------------------------|-----------------------------------------------| -| 박성민 | [@dnjsals45](https://github.com/dnjsals45) | Auth, Payment 도메인 개발 및 테스트코드 작성 | -| 김민수 | [@Doritosch](https://github.com/Doritosch) | User, AI, Address 도메인 개발 및 테스코드 작성 | -| 김채연 | [@yeon-22k](https://github.com/yeon-22k) | Menu, Coupon 도메인 개발 및 테스트코드 작성 | -| 박주찬 | [@p990805](https://github.com/p990805) | Review, ReviewReply, Cart 도메인 개발 및 테스트코드 작성 | -| 변영재 | [@bbangjae](https://github.com/bbangjae) | Store, Point 도메인 개발 및 테스트코드 작성 | -| 송의현 | [@yawning5](https://github.com/yawning5) | Order, OrderMenu 도메인 개발 및 테스트코드 작성, 페이징 정책 적용 | \ No newline at end of file +| 팀원 | 깃허브 | 역할 | +|-----|--------------------------------------------|----------------------------------------------------------------------------------| +| 박성민 | [@dnjsals45](https://github.com/dnjsals45) | Auth, Payment 도메인 개발 및 테스트코드 작성 | +| 김민수 | [@Doritosch](https://github.com/Doritosch) | User, AI, Address 도메인 개발 및 테스코드 작성 | +| 김채연 | [@yeon-22k](https://github.com/yeon-22k) | Menu, Coupon 도메인 개발 및 테스트코드 작성 | +| 박주찬 | [@p990805](https://github.com/p990805) | Review, ReviewReply, Cart 도메인 개발 및 테스트코드 작성 | +| 변영재 | [@bbangjae](https://github.com/bbangjae) | Store 도메인 개발 및 테스트 코드 작성, Point 로직(AOP 기반), AuditorAware를 통한 생성자/수정자 자동 관리 기능 구현 | +| 송의현 | [@yawning5](https://github.com/yawning5) | Order, OrderMenu 도메인 개발 및 테스트코드 작성, 페이징 정책 적용 | \ No newline at end of file From 36b36dc0a929c26d2ba59660b043e725483ef287 Mon Sep 17 00:00:00 2001 From: yeon-22k Date: Sat, 18 Oct 2025 14:57:10 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat(readme):=20=ED=8C=80=EC=9B=90=20?= =?UTF-8?q?=EC=97=AD=ED=95=A0=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6c5a3c2..466aad6 100644 --- a/README.md +++ b/README.md @@ -377,8 +377,8 @@ class StoreRepositoryTest extends RepositoryTest { | 팀원 | 깃허브 | 역할 | |-----|--------------------------------------------|----------------------------------------------------------------------------------| | 박성민 | [@dnjsals45](https://github.com/dnjsals45) | Auth, Payment 도메인 개발 및 테스트코드 작성 | -| 김민수 | [@Doritosch](https://github.com/Doritosch) | User, AI, Address 도메인 개발 및 테스코드 작성 | -| 김채연 | [@yeon-22k](https://github.com/yeon-22k) | Menu, Coupon 도메인 개발 및 테스트코드 작성 | +| 김민수 | [@Doritosch](https://github.com/Doritosch) | AI 메뉴 소개 생성, 주소 및 유저 도메인 개발 및 테스트코드 작성 | +| 김채연 | [@yeon-22k](https://github.com/yeon-22k) | Menu, Coupon 도메인 개발 및 테스트코드 작성, Coupon 스케쥴링 만료/삭제 구현, ReadMe 통합 작업 | | 박주찬 | [@p990805](https://github.com/p990805) | Review, ReviewReply, Cart 도메인 개발 및 테스트코드 작성 | | 변영재 | [@bbangjae](https://github.com/bbangjae) | Store 도메인 개발 및 테스트 코드 작성, Point 로직(AOP 기반), AuditorAware를 통한 생성자/수정자 자동 관리 기능 구현 | | 송의현 | [@yawning5](https://github.com/yawning5) | Order, OrderMenu 도메인 개발 및 테스트코드 작성, 페이징 정책 적용 | \ No newline at end of file From a02194562a75afba9d7e807c774c4846b259113c Mon Sep 17 00:00:00 2001 From: yeon-22k Date: Mon, 20 Oct 2025 05:55:24 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat(readme):=20API=20=EB=AA=85=EC=84=B8=20?= =?UTF-8?q?=EC=B5=9C=EC=A2=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api-docs.md | 758 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 546 insertions(+), 212 deletions(-) diff --git a/api-docs.md b/api-docs.md index 5ced295..8b2bd97 100644 --- a/api-docs.md +++ b/api-docs.md @@ -31,19 +31,44 @@ Address가게 주소 등록POST/v1/address/store
{
-  "roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
-  "jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
-  "detailAddress": "101동",
-  "latitude": "127.1052160",
-  "longitude": "37.3595033"
+"roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
+"jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
+"detailAddress": "101동",
+"latitude": "127.105216",
+"longitude": "37.3595033"
+}
+
{
+"uuid": "90553608-0f86-441f-811f-8dc7abf70e9b",
+"jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
+"roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
+"detailAddress": "101동",
+"latitude": "127.105216",
+"longitude": "37.3595033"
 }
- Address가게 주소 수정PATCH/v1/address/store/{storeId} - - +

+{
+"uuid": "90553608-0f86-441f-811f-8dc7abf70e9b",
+"jibunAddress": "서울특별시 구로구 구로동 5-1",
+"roadAddress": "서울특별시 구로구 새말로 89",
+"detailAddress": "2층",
+"latitude": "126.8896815",
+"longitude": "37.505573"
+}
+
+

+{
+"uuid": "90553608-0f86-441f-811f-8dc7abf70e9b",
+"jibunAddress": "서울특별시 구로구 구로동 5-1",
+"roadAddress": "서울특별시 구로구 새말로 89",
+"detailAddress": "2층",
+"latitude": "126.8896815",
+"longitude": "37.505573"
+}
+
@@ -62,13 +87,88 @@ "latitude": "127.1052160", "longitude": "37.3595033" } - +

+{
+"uuid": "2c92e6c7-6906-4f06-b34e-124b51210520",
+"jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
+"roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
+"detailAddress": "101동",
+"alias": "우리집",
+"latitude": "127.105216",
+"longitude": "37.3595033"
+}
+
+ Address전체 회원 주소 목록 조회GET/v1/address/user/all?page=0&size=10 + +

+{
+"content": [
+{
+"uuid": "90553608-0f86-441f-811f-8dc7abf70e9b",
+"jibunAddress": "서울특별시 구로구 구로동 5-1",
+"roadAddress": "서울특별시 구로구 새말로 89",
+"detailAddress": "2층",
+"latitude": "126.8896815",
+"longitude": "37.505573"
+}
+],
+"pageable": {
+"pageNumber": 0,
+"pageSize": 10,
+"sort": {
+"empty": false,
+"unsorted": false,
+"sorted": true
+},
+"offset": 0,
+"unpaged": false,
+"paged": true
+},
+"last": true,
+"totalPages": 1,
+"totalElements": 1,
+"first": true,
+"size": 10,
+"number": 0,
+"sort": {
+"empty": false,
+"unsorted": false,
+"sorted": true
+},
+"numberOfElements": 1,
+"empty": false
+}
+
+ + + Address회원 주소 조회GET/v1/address/user -
{}
- 여기 넣으세요 + +

+[
+{
+"uuid": "d06857dd-0090-4821-a1a3-ffbb662b40c6",
+"jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
+"roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
+"detailAddress": "101동",
+"alias": "우리집",
+"latitude": "127.105216",
+"longitude": "37.3595033"
+},
+{
+"uuid": "6174753c-6b24-43fe-b526-754825ff1b1f",
+"jibunAddress": "서울특별시 구로구 구로동 5-1",
+"roadAddress": "서울특별시 구로구 새말로 89",
+"detailAddress": "2층",
+"alias": "회사",
+"latitude": "126.8896815",
+"longitude": "37.505573"
+}
+]
+
@@ -80,29 +180,66 @@ "alias": "회사", "latitude": "127.1052160", "longitude": "37.3595033" -} - 여기 넣으세요 +} +{ +"uuid": "d06857dd-0090-4821-a1a3-ffbb662b40c6", +"jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리", +"roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리", +"detailAddress": "101동", +"alias": "회사2", +"latitude": "127.105216", +"longitude": "37.3595033" +} + Address회원 주소 삭제DELETE/v1/address/user/{addressId} -
{}
- 여기 넣으세요 + + - - AI음식 소개 작성POST/v1/ai/req -
{
-  "content": "요청내용"
-}
-
{
-  "id": "id",
-  "inputText": "만두",
-  "outputText": "오감만족, 인생만두!",
-  "createdAt": "2025-10-02T12:57:43.983978"
+    AddressAPI 주소 조회GET/v1/address?query={address}
+    
+    

+{
+"content": [
+{
+"jibunAddress": "경기도 성남시 분당구 정자동 178-1 NAVER그린팩토리",
+"roadAddress": "경기도 성남시 분당구 불정로 6 NAVER그린팩토리",
+"latitude": "127.1052160",
+"longitude": "37.3595033"
+}
+],
+"pageable": {
+"pageNumber": 0,
+"pageSize": 10,
+"sort": {
+"empty": false,
+"unsorted": false,
+"sorted": true
+},
+"offset": 0,
+"unpaged": false,
+"paged": true
+},
+"last": true,
+"totalPages": 1,
+"totalElements": 1,
+"first": true,
+"size": 10,
+"number": 0,
+"sort": {
+"empty": false,
+"unsorted": false,
+"sorted": true
+},
+"numberOfElements": 1,
+"empty": false
 }
+ @@ -118,7 +255,7 @@ Auth유저네임 중복확인GET/v1/auth/exists?username={username} -
{}
+
{
   "exists": true
 }
@@ -137,25 +274,26 @@ Auth로그아웃POST/v1/auth/logout -
{}
-
{}
+ + Auth회원 탈퇴DELETE/v1/auth/withdrawal -
{}
-
{}
+ + Auth토큰 재발급POST/v1/auth/token/reissue -
{}
-
AT: 헤더, RT: 쿠키
+ + - + + Cart장바구니 조회GET/v1/cart -
{}
+
{
   "cartId": "UUID",
   "userId": 1,
@@ -204,7 +342,7 @@
 
   
     Cart장바구니 아이템 수량 수정PATCH/v1/cart/items/{cartItemId}?quantity={quantity}
-    
{}
+
/v1/cart/items/{cartItemId}?quantity=5
{
   "cartId": "UUID",
   "userId": 1,
@@ -216,87 +354,157 @@
 
   
     Cart장바구니 아이템 삭제DELETE/v1/cart/items/{cartItemId}
-    
{}
-
{
-  "cartId": "UUID",
-  "userId": 1,
-  "items": [],
-  "totalPrice": 0,
-  "storeId": null
-}
+
/v1/cart/items/{cartItemId}
+

+{
+"cartId": “UUID",
+"userId": 1,
+"items": [],
+"totalPrice": 0,
+"storeId": null
+}
+
Cart장바구니 비우기DELETE/v1/cart -
{}
-
{
-  "cartId": "UUID",
-  "userId": 1,
-  "items": [],
-  "totalPrice": 0,
-  "storeId": null
-}
+ + - + Coupon매장 쿠폰 목록 조회GET/v1/coupon/list/{storeId} -
{}
-
{}
+ +
[
+  {
+    "couponId": "9f2d9a2e-1b4a-45a3-9b7f-2f9e6b2d2f00",
+    "name": "주말 10% 할인",
+    "type": "PERCENT",
+    "scope": "STORE",
+    "discountValue": 10,
+    "minOrderPrice": 15000,
+    "quantity": 500,
+    "issuedCount": 132,
+    "expiredAt": "2025-12-31T23:59:59"
+  },
+  {
+    "couponId": "74a7c6c5-8a18-4e1e-9a3b-0c2b4f8f9c11",
+    "name": "3천원 즉시할인",
+    "type": "FIXED",
+    "scope": "STORE",
+    "discountValue": 3000,
+    "minOrderPrice": 12000,
+    "quantity": 300,
+    "issuedCount": 98,
+    "expiredAt": "2025-11-30T23:59:59"
+  }
+]
Coupon내 쿠폰 목록 조회GET/v1/coupon/my/list -
{}
-
{}
+ +
[
+  {
+    "userCouponId": "a2b0a5ef-2d9c-4c6d-9b0e-1a2b3c4d5e6f",
+    "userId": 101,
+    "couponId": "9f2d9a2e-1b4a-45a3-9b7f-2f9e6b2d2f00",
+    "couponStatus": "ACTIVE"
+  },
+  {
+    "userCouponId": "e3c1d2f4-5678-49ab-9cde-0123456789ab",
+    "userId": 101,
+    "couponId": "74a7c6c5-8a18-4e1e-9a3b-0c2b4f8f9c11",
+    "couponStatus": "USED"
+  }
+]
CouponStore 쿠폰 등록POST/v1/coupon/{storeId} -
{}
-
{}
+
{
+  "name": "2000원 할인",
+  "type": "FIXED",
+  "scope": "STORE",
+  "discountValue": 2000,
+  "minOrderPrice": 10000,
+  "quantity": 1000,
+  "expiredAt": "2025-12-31T23:59:59"
+}
+
{
+  "couponId": "2c3b4a5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
+  "name": "2000원 할인",
+  "type": "FIXED",
+  "scope": "STORE",
+  "discountValue": 2000,
+  "minOrderPrice": 10000,
+  "quantity": 1000,
+  "issuedCount": 0,
+  "expiredAt": "2025-12-31T23:59:59"
+}
- CouponMaster 쿠폰 등록POST/v1/coupon/{storeId} -
{}
-
{}
+ CouponMASTER 쿠폰 등록POST/v1/coupon/{couponId} +
{
+  "name": "전매장 5천원 할인",
+  "type": "FIXED",
+  "scope": "MASTER",
+  "discountValue": 5000,
+  "minOrderPrice": 20000,
+  "quantity": 10000,
+  "expiredAt": "2026-01-31T23:59:59"
+}
+
{
+  "couponId": "d4f1a2b3-c4d5-4e6f-8a90-b1c2d3e4f5a6",
+  "name": "전매장 5천원 할인",
+  "type": "FIXED",
+  "scope": "MASTER",
+  "discountValue": 5000,
+  "minOrderPrice": 20000,
+  "quantity": 10000,
+  "issuedCount": 0,
+  "expiredAt": "2026-01-31T23:59:59"
+}
- Coupon쿠폰 발급POST/v1/coupon/{couponId} -
{}
-
{}
+ CouponMaster 유저 쿠폰 등록POST/v1/coupon/{storeId} + +
{
+  "userCouponId": "ab12cd34-ef56-7890-ab12-cd34ef567890",
+  "userId": 101,
+  "couponId": "d4f1a2b3-c4d5-4e6f-8a90-b1c2d3e4f5a6",
+  "couponStatus": "ACTIVE"
+}
Coupon쿠폰 수정PATCH/v1/coupon/{couponId} -
{}
-
{}
- - - - Coupon쿠폰 사용POST/v1/coupon/{couponId} -
{}
-
{}
+
{
+  "name": "2000원 할인",
+  "type": "FIXED",
+  "scope": "STORE",
+  "discountValue": 2000,
+  "minOrderPrice": 10000,
+  "quantity": 1000,
+  "expiredAt": "2025-12-31T23:59:59"
+}
+ Coupon쿠폰 삭제DELETE/v1/coupon/{couponId} -
{}
-
{}
+ + - - Coupon쿠폰 만료DELETE/v1/coupon/expire/{couponId} -
{}
-
{}
- - + Menu메뉴 조회(목록)GET/v1/store/{storeId}/menu -
{}
+
[
   {
     "menuId": "550e8400-e29b-41d4-a716-446655440000",
@@ -319,7 +527,7 @@
 
   
     Menu메뉴 조회(개별)GET/v1/store/{storeId}/menu/{menuId}
-    
{}
+
{
   "menuId": "550e8400-e29b-41d4-a716-446655440000",
   "storeId": "660e8400-e29b-41d4-a716-446655440000",
@@ -355,25 +563,25 @@
   
     Menu메뉴 수정PATCH/v1/store/{storeId}/menu/{menuId}
     
{ "name": "수정버거" }
-
{}
+ Menu메뉴 상태 변경PATCH/v1/store/{storeId}/menu/{menuId}/status
{ "isHidden": true }
-
{}
+ Menu메뉴 삭제DELETE/v1/store/{storeId}/menu/{menuId} -
{}
-
{}
+ + - + Order주문 목록 조회GET/v1/orders?from={startDate}&to={endDate}&status={status}&userId={userId}&storeId={storeId}&sort={sort}&page={page}&size={size} -
{}
+
{ 
   "totalElements": 0,
   "totalPages": 0,
@@ -419,7 +627,7 @@
 
   
     Order주문 조회GET/v1/orders/{orderId}
-    
{}
+
{ 
   "id": 1002,
   "customerName": "c",
@@ -496,18 +704,43 @@
   
     Order주문 상태 변경(관리자)PATCH/v1/orders/{orderId}
     
{ "status": "DELIVERED" }
-
{}
+
{
+"id": 1002,
+"customerName": "c",
+"storeName": "d",
+"price": 45000,
+"address": "부산시 어딘가 456",
+"orderMenuList": [
+{ “id”: UUID, "name": "탕수육", "price": 20000, "quantity": 1 },
+{”id”: UUID,  "name": "깐풍기", "price": 25000, "quantity": 2 }
+],
+"createdAt": "2025-09-29T12:34:56",
+”oderStatus”: “DELIVERED”
+}
Order주문 상태 변경(가게주인, 관리자)PATCH/orderId/next-status -
{}
-
{}
+ +
{
+"id": 1002,
+"customerName": "c",
+"storeName": "d",
+"price": 45000,
+"address": "부산시 어딘가 456",
+"orderMenuList": [
+{ “id”: UUID, "name": "탕수육", "price": 20000, "quantity": 1 },
+{”id”: UUID,  "name": "깐풍기", "price": 25000, "quantity": 2 }
+],
+"createdAt": "2025-09-29T12:34:56",
+”oderStatus”: “DELIVERED”
+}
+
Order주문 취소PATCH/cancel/{orderId} -
{}
+
{ 
   "id": 1002,
   "customerName": "c",
@@ -532,67 +765,95 @@
   "orderStatus": "CANCELLED"
 }
- + Payment결제내역 조회GET/v1/payments?page=1&size=10&orderBy={정렬조건}&keyword={검색조건} -
{}
+
{
-  "payments": [
-    {
-      "id": 1,
-      "paymentNumber": "12312312",
-      "status": "결제 성공",
-      "storeName": "음식점1",
-      "price": 10000
-    }
-  ]
+    “content”: [
+      {
+           “id”: UUID
+        “paymentNumber”: “12312312”,
+        “status”: “결제 성공”,
+         “storeName”: “음식점1”,
+         “price”: 10000
+      },
+      …
+    ],
+    "pageable": {
+         "pageNumber": 0,
+         "pageSize": 10,
+         "sort": {
+              "empty": false,
+              "sorted": true,
+              "unsorted": false
+      },
+      "offset": 0,
+      "paged": true,
+      "unpaged": false
+},
+    "totalPages": 1,
+    "last": true,
+    "totalElements": 8,
+    "size": 10,
+    "number": 0,
+    "sort": {
+        "empty": false,
+        "sorted": true,
+        "unsorted": false
+    },
+    "first": true,
+    "numberOfElements": 8,
+    "empty": false
 }
Payment결제내역 상세 조회GET/v1/payments/{paymentId} -
{}
-
{
-  "paymentNumber": "1q2w3e4r!",
-  "price": 10000,
-  "cardCompany": "XXX카드",
-  "cardNumber": "1111 2222 3333 4444",
-  "approvedAt": "2025-09-29T10:15:00",
-  "restaurant": {
-    "id": 1,
-    "storeName": "음식점",
-    "phone": "02-1234-1234",
-    "address": "서울 어떤가"
-  },
-  "orderItem": [
-    {
-      "id": 5,
-      "storeName": "음식1",
-      "quantity": 1,
-      "price": 5000,
-      "totalPrice": 5000
-    }
-  ]
+    
+    
{
+        “paymentNumber”: “1q2w3e4r!”,
+        “price”: 10000,
+        “cardCompany”: “XX카드”,
+        “cardNumber”: “1111 **** **** 4444”,
+        “processedAt”: "2025-09-29T10:15:00"
+        “restaurant”: {
+            “id”: 1,
+            “storeName”: “음식점”,
+        },
+        “orderItem” [
+            {
+                “id”: 5,
+                “menuName”: “음식1”,
+                “quantity”: 1
+                “price”: 5000,
+                “totalPrice”: 5000
+            },
+            …
+        ]
 }
Payment결제 상태 변경PATCH/v1/payments/status/{paymentId} -
{ "status": "결제 완료" }
-
{}
+
{
+    “status”: “COMPLETED”
+}
+ Payment결제 요청POST/v1/payments
{
-  "cardCompany": "삼성",
-  "cardNumber": "123123"
+    “orderId”: UUID,
+    “cardCompany”: “SAMSUNG”,
+    “cardNumber”: “1111 **** **** 4444”
 }
-
{ "status": "approve" }
+ - + Review리뷰 등록POST/v1/reviews/order/{orderId}
{
@@ -634,7 +895,7 @@
 
   
     Review리뷰 개별 조회GET/v1/reviews/{reviewId}
-    
{}
+
{
   "reviewId": 123,
   "storeId": 1,
@@ -649,7 +910,7 @@
 
   
     Review리뷰 목록 조회GET/v1/reviews/{storeId}?page=1&size=10
-    
{}
+
{
   "reviews": [
     {
@@ -691,8 +952,11 @@
 
   
     Review리뷰 삭제DELETE/v1/reviews/{reviewId}
-    
{}
-
{}
+ +
{
+  "status": 200,
+  "message": "리뷰가 성공적으로 삭제되었습니다."
+}
@@ -725,12 +989,12 @@ Review리뷰 답글 삭제DELETE/v1/reviews/{reviewId}/reply -
{}
-
{}
+ + Review리뷰 목록 조회(사장님 답글 추가 시)GET/v1/reviews/{storeId}?page=1&size=10 -
{}
+
{
   "reviews": [
   {
@@ -745,7 +1009,6 @@
     “reply”: { 
       “conetent”: 이용해주셔서 감사합니다
     }
-
 },
 {
 "reviewId": 102,
@@ -759,7 +1022,6 @@
 “reply”: {
 “conetent”: 다음에 또 오세요
 }
-
 }
 ],
 "pageInfo": {
@@ -838,22 +1100,7 @@
   "address": "address",
   "imageUrl": "imageUrl"
 }
-
{
-  "status": 201,
-  "message": "음식점 등록에 성공했습니다.",
-  "data": {
-    "storeId": "storeId",
-    "name": "name",
-    "category": "category",
-    "description": "description",
-    "address": "address",
-    "imageUrl": "imageUrl",
-    "avgRating": 0,
-    "reviewCount": 0,
-    "createdAt": "",
-    "createdBy": ""
-  }
-}
+ @@ -866,43 +1113,22 @@ "address": "newAddress", "imageUrl": "newImageUrl" }
-
{
-  "status": 200,
-  "message": "음식점 수정에 성공했습니다.",
-  "data": {
-    "storeId": "storeId",
-    "name": "newName",
-    "category": "newCategory",
-    "description": "newDescription",
-    "address": "newAddress",
-    "imageUrl": "newImageUrl",
-    "avgRating": 4.5,
-    "reviewCount": 500,
-    "updatedAt": "",
-    "updatedBy": ""
-  }
-}
+ Store음식점 삭제DELETE/v1/store/{storeId} -
{}
-
{
-  "status": 200,
-  "message": "음식점 삭제에 성공했습니다.",
-  "data": {
-    "storeId": "storeId",
-    "deletedAt": "",
-    "deletedBy": ""
-  }
-}
+ { +”storeId”:”storeId” +} + User회원 정보 조회GET/v1/users/{userId} -
{}
+
{
   "userId": "userId",
   "username": "username"
@@ -913,18 +1139,30 @@
     
{
   "nickname": "nickname"
 }
-
{}
+ { +"id": 1, +"username": "test01", +"password": "$2a$10$FYt/selrPyrJDEBzyjIyAu3z21285Kzjxh10GuLO9NYQDod7WkTOC", +"nickname": "테스트입니다", +"authority": "고객" +} User회원 비밀번호 수정PATCH/v1/users/{userId}/password
{
   "password": "password"
 }
-
{}
+
{
+"id": 1,
+"username": "test01",
+"password": "$2a$10$OZnji3urkIVAufCRiwIu8e4t.JWZUZMSe.dM4qAu9r95A7qzkpFN2",
+"nickname": "테스트입니다",
+"authority": "고객"
+}
User회원 리뷰 목록 조회GET/v1/users/{userId}/reviews -
정렬조건
+
{
   "data": [
     {
@@ -942,40 +1180,136 @@
   
   
     User회원 목록 조회GET/v1/users
-    
{ page=1&size=10&sort=createdAt,desc }
+
{
-  "data": [
-    {
-      "user1": {
-        "userId": "userId",
-        "username": "username"
-      }
-    }
-  ]
+"users": {
+"content": [
+{
+"id": 5,
+"username": "master1",
+"password": "$2a$10$yr4rLG.uvjLECZLn1AMP..5o1tRO0gzhEvf5a7LiZ5ImS9Mx4YW2y",
+"nickname": "owner",
+"authority": "최종 관리자"
+},
+…
+{
+"id": 1,
+"username": "test01",
+"password": "$2a$10$OZnji3urkIVAufCRiwIu8e4t.JWZUZMSe.dM4qAu9r95A7qzkpFN2",
+"nickname": "테스트입니다",
+"authority": "고객"
+}
+],
+"pageable": {
+"pageNumber": 0,
+"pageSize": 10,
+"sort": {
+"empty": false,
+"unsorted": false,
+"sorted": true
+},
+"offset": 0,
+"unpaged": false,
+"paged": true
+},
+"last": true,
+"totalPages": 1,
+"totalElements": 5,
+"first": true,
+"size": 10,
+"number": 0,
+"sort": {
+"empty": false,
+"unsorted": false,
+"sorted": true
+},
+"numberOfElements": 5,
+"empty": false
+}
 }
User회원 주문 목록 조회GET/v1/users/{userId}/orders -
{ page=1&size=10&sort=createdAt,desc }
+
{
-  "data": [
-    {
-      "orderId": "orderId",
-      "storeName": "storeName",
-      "price": "price",
-      "address": "address",
-      "createdAt": "2025-09-29T12:34:56",
-      "menu": [
-        { "name": "탕수육", "price": 20000, "quantity": 1 },
-        { "name": "깐풍기", "price": 25000, "quantity": 2 }
-      ]
-    }
-  ]
+"content": [
+{
+"id": "072979c1-8eaf-49c1-a36c-306c5c0792d7",
+"customerName": "test01",
+"storeName": "한식당",
+"price": 13000,
+"address": "string",
+"orderMenuList": [
+{
+"id": "56167b37-e9b1-4855-b1c9-f5ea54506fa8",
+"name": "칼국수",
+"price": 5000,
+"quantity": 1
+},
+{
+"id": "687e2748-1860-4c0a-bdd6-c77b0275f28b",
+"name": "김밥",
+"price": 2000,
+"quantity": 4
+}
+],
+"createdAt": "2025-10-18T13:34:46.945571",
+"orderStatus": "PENDING"
+},
+{
+"id": "c39792a8-60bd-4075-841e-6233bfe5c731",
+"customerName": "test01",
+"storeName": "한식당",
+"price": 15000,
+"address": "string",
+"orderMenuList": [
+{
+"id": "8093a169-80e7-43a5-8fd2-9a997c200f4f",
+"name": "칼국수",
+"price": 5000,
+"quantity": 3
+}
+],
+"createdAt": "2025-10-18T13:33:40.164406",
+"orderStatus": "PENDING"
+}
+],
+"pageable": {
+"pageNumber": 0,
+"pageSize": 10,
+"sort": {
+"empty": false,
+"unsorted": false,
+"sorted": true
+},
+"offset": 0,
+"unpaged": false,
+"paged": true
+},
+"last": true,
+"totalPages": 1,
+"totalElements": 2,
+"first": true,
+"size": 10,
+"number": 0,
+"sort": {
+"empty": false,
+"unsorted": false,
+"sorted": true
+},
+"numberOfElements": 2,
+"empty": false
 }
User매니저 권한 부여PATCH/v1/users/{userId}/authority -
{}
-
{}
+ +
{
+"id": 1,
+"username": "test01",
+"password": "$2a$10$OZnji3urkIVAufCRiwIu8e4t.JWZUZMSe.dM4qAu9r95A7qzkpFN2",
+"nickname": "테스트입니다",
+"authority": "관리자"
+}
From db8faf61f0bd00ede31e8c5491641f302681cc97 Mon Sep 17 00:00:00 2001 From: yeon-22k Date: Mon, 20 Oct 2025 05:58:51 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat(readme):=20readMe=20=ED=95=B5=EC=8B=AC?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(auth)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/README.md b/README.md index 466aad6..eec6bd8 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,64 @@ docker exec -it tdd-db psql -U test -d tdd-db - 공통 페이징 정책을 일괄 적용하기 위해 WebMvcConfig 에 정책 등록 - 기본 정렬 기준(`createdAt DESC`)을 지정하여 일관된 정렬 정책 유지 +### (2) Authentication + +**회원가입** + +- username, password를 이용한 신규 사용자 등록 +- 회원가입 시점에서 권한(CUSTOMER, OWNER, MANAGER, MASTER) 설정 가능 +- 즉시 로그인 처리되어 Access Token(Header), Refresh Token(Cookie) 자동 발급 +- 비밀번호는 BCryptPasswordEncoder를 통해 암호화 저장 + +**로그인** + +- username, password를 이용한 인증 처리 +- `PasswordEncoder.matches()`를 통한 비밀번호 검증 +- Access Token(Header), Refresh Token(Cookie) 발급 + +**로그아웃** + +- 현재 사용 중인 Access Token과 Refresh Token을 블랙리스트에 추가 +- 로컬 캐시 기반 블랙리스트 관리로 빠른 토큰 무효화 처리 +- Refresh Token 쿠키 무효화 + +**회원 탈퇴** + +- Soft Delete 방식으로 사용자 및 연관 데이터 삭제 +- 권한별 차등 삭제 로직: + - OWNER: 소유 가게 → 메뉴 → 리뷰 → 리뷰 댓글 등 연관 데이터 일괄 삭제 + - CUSTOMER: 리뷰, 주문 등 개인 데이터 삭제 +- 탈퇴 후 자동 로그아웃 처리 (토큰 블랙리스트 추가) + +### (3) JWT 기반 인증 시스템 + +**토큰 관리 전략** + +- Access Token: HTTP Authorization Header로 전달 (짧은 유효기간) +- Refresh Token: HttpOnly Cookie로 전달 (긴 유효기간) +- JWT 라이브러리: `jjwt` 사용 + +**토큰 재발급 (RTR 기법 적용)** + +- Refresh Token Rotation (RTR) 기법을 통한 보안 강화 + - 토큰 재발급 시 Refresh Token도 함께 재발급 + - 기존 Refresh Token은 블랙리스트에 추가 + - 탈취된 Refresh Token의 장기 사용 방지 +- Refresh Token의 최대 유효기간 유지 + - 재발급 시에도 원본 Refresh Token의 만료 시간을 그대로 적용 + - 무한 갱신 방지 및 주기적 재로그인 유도 +- Access Token, Refresh Token 모두 만료 시 재로그인 필요 + +**인증 필터 처리** + +- `JwtAuthenticationFilter`: + - 요청 헤더 및 쿠키에서 토큰 추출 + - 토큰 검증 및 사용자 인증 정보 생성 + - SecurityContextHolder에 인증 정보 저장 + - 토큰이 없는 경우 public URL로 판단 +- `JwtExceptionFilter`: JWT 필터에서 발생하는 예외 처리 +- `JwtTokenProvider`: 토큰 발급, 검증, 디코딩 담당 + ## 2. 트러블슈팅 ### (1) CustomPageableResolver Bean 등록 미적용 문제 From d39a27893e6927ebc78dbaad37ff786b4cca7fda Mon Sep 17 00:00:00 2001 From: yeon-22k Date: Mon, 20 Oct 2025 07:42:08 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat(readme):=20readMe=20=ED=95=B5=EC=8B=AC?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(coupon)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/README.md b/README.md index eec6bd8..16f7dd5 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,46 @@ docker exec -it tdd-db psql -U test -d tdd-db - `JwtExceptionFilter`: JWT 필터에서 발생하는 예외 처리 - `JwtTokenProvider`: 토큰 발급, 검증, 디코딩 담당 +### (3) Coupon + +![Coupon diagram](https://github.com/user-attachments/assets/8d1595ff-be64-4cad-b62c-baabb2bc6658) + +**Coupon** + +- 가게 쿠폰 목록 조회 + - 가게의 모든 쿠폰을 조회 +- Store 쿠폰 등록 + - 반드시 `SCOPE`가 `STORE`여야 함 + - `TYPE` 설정을 통해 고정된 할인(FIXED) 혹은 %할인(PERCENT) 선택 가능 + - OWNER는 반드시 해당 가게 OWNER만 가능함 +- Master 쿠폰 등록 + - 반드시 `SCOPE`가 `MASTER`여야 함 + - 해당 기능은 MASTER만 가능 +- 쿠폰 수정 + - 유저가 1회라도 발급받았다면, 쿠폰 수정은 불가 + - OWNER는 반드시 해당 가게 OWNER만 가능함 +- 쿠폰 삭제 + - 만료된 쿠폰은 스케쥴러를 통해 soft delete + - OWNER는 반드시 해당 가게 OWNER만 가능함 + +**UserCoupon** + +- 내 쿠폰 목록 조회 + - CouponStatus에 대하여 ACTIVE > USED/EXPIRED 순으로 정렬하되, 각 그룹에 대하여 생성일 순으로 정렬하여 조회 +- 쿠폰 발급 + - 쿠폰 발급 기능 + - exist 설정을 통해 중복 발급 방지 + - 사용자가 몰려 해당 기능을 수행할 경우를 대비해야 함(동시성 문제) + +**기타** + +- 모든 API에 대하여 @PreAuthorize를 통해 허용된 권한만 접근 가능하도록 설정 +- SRT 원칙을 지키며 각 클래스가 명확한 책임을 가지도록 메서드 단위를 최소화하여 개발 +- 추가 로직 + - UserCoupon 만료/만료된 Coupon 삭제 처리를 하나의 트랜잭션 작업으로 묶어서 매일 자정 스케쥴링 + - 만료된지 7일 된 UserCoupon에 대한 삭제 처리 스케쥴링 + - Order에서 주문 생성 시 쿠폰을 적용할 수 있는 로직 추가(최소 주문 금액에 맞는지 확인 및 총 결제금액 계산까지 확장 가능) + ## 2. 트러블슈팅 ### (1) CustomPageableResolver Bean 등록 미적용 문제