{"info":{"_postman_id":"a1213cc6-814c-4b7b-9e60-120f73034460","name":"대용량 트래픽 - 커피 주문","description":"<html><head></head><body><ol>\n<li><p>커피 메뉴 목록 조회 API</p>\n</li>\n<li><p>포인트 충전하기 API</p>\n</li>\n<li><p>커피 주문, 결제하기 API</p>\n</li>\n<li><p>인기 메뉴 목록 조회 API</p>\n</li>\n</ol>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[],"owner":"50311221","collectionId":"a1213cc6-814c-4b7b-9e60-120f73034460","publishedId":"2sBXiqEojG","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-04-06T05:15:29.000Z"},"item":[{"name":"커피 메뉴 목록 조회","event":[{"listen":"prerequest","script":{"id":"c9b50aaa-a6d8-47b2-ad88-2d84b976a26e","exec":[""],"type":"text/javascript","packages":{},"requests":{}}}],"id":"2fe3714f-c708-40fd-8b3c-09334c2631b5","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/menus","description":"<p>커피 정보(메뉴 id, 이름, 가격)를 목록으로 조회하는 API</p>\n<p>많은 메뉴를 한 번에 응답하는 것보다 페이지 형식으로 응답하여 성능 개선</p>\n<p>인덱스를 설계하여 DB 조회 속도를 증가시키고,</p>\n<p>캐싱을 설계하여 DB의 조회를 줄여 부하 감소로 인한 성능 개선</p>\n","urlObject":{"protocol":"http","port":"8080","path":["api","menus"],"host":["localhost"],"query":[],"variable":[]}},"response":[{"id":"06a266d2-e90f-4949-8c15-6d6dc62f9608","name":"커피 메뉴 목록 조회 성공","originalRequest":{"method":"GET","header":[],"body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/menus"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json","description":"","type":"text"}],"cookie":[],"responseTime":null,"body":"{\n    \"success\" : \"true\",\n    \"status\" : \"OK\",\n    \"message\" : \"메뉴 목록 조회 성공\",\n    \"data\" : {\n        \"content\" : [\n            {\n                \"menuId\" : 1,\n                \"name\" : \"아메리카노\",\n                \"price\" : 4500,\n                \"status\" : \"ON_SALE\"\n            }, \n            {\n                \"menuId\" : 2,\n                \"name\" : \"카페라떼\",\n                \"price\" : 5000,\n                \"status\" : \"ON_SALE\"\n            }\n        ],\n        \"page\" : 0,\n        \"size\" : 10,\n        \"totalElements\" : 2,\n        \"totalPages\" : 1\n    }\n}"}],"_postman_id":"2fe3714f-c708-40fd-8b3c-09334c2631b5"},{"name":"포인트 충전","id":"a694354d-8133-45f4-9b39-28929a93bc20","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"<token>"}]},"isInherited":false},"method":"POST","header":[{"key":"Idempotency-Key","value":"550e8400-e29b-41d4-a716-446655440000","type":"text"}],"body":{"mode":"raw","raw":"{\n    \"chargeAmount\" : 100000\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/points","description":"<p>결제는 포인트로만 가능하기 때문에, 포인트를 충전하는 API</p>\n<p>사용자 식별값, 충전 금액을 입력받아 포인트를 충전 (1원 = 1p)</p>\n<p>jwt를 통해 user id 추출하여 사용</p>\n<p>성공적으로 충전 시 포인트 이력 생성</p>\n<p>멱등성 키 설계하여 한 번의 요청을 두 번 이상 처리하는 문제 방지</p>\n","urlObject":{"protocol":"http","port":"8080","path":["api","points"],"host":["localhost"],"query":[],"variable":[]}},"response":[{"id":"d3b6cc01-6240-4f0c-9394-769f41438f0a","name":"포인트 충전 성공","originalRequest":{"method":"POST","header":[{"key":"Idempotency-Key","value":"550e8400-e29b-41d4-a716-446655440000","type":"text"},{"key":"","value":"","type":"text","disabled":true}],"body":{"mode":"raw","raw":"{\n    \"chargeAmount\" : 10000\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/points"},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json","description":"","type":"text"}],"cookie":[],"responseTime":null,"body":"{\n    \"success\" : \"true\",\n    \"status\" : \"CREATED\",\n    \"message\" : \"포인트 충전 성공\",\n    \"data\" : {\n        \"pointId\" : 1,\n        \"balance\" : 10000\n    }\n}"}],"_postman_id":"a694354d-8133-45f4-9b39-28929a93bc20"},{"name":"커피 주문/결제","id":"fe688f3f-d0e6-453e-9bc5-6a2472d99a13","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"<token>"}]},"isInherited":false},"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"menuId\": 1,\n      \"quantity\": 2\n    },\n    {\n      \"menuId\": 3,\n      \"quantity\": 1\n    }\n  ]\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/orders","description":"<p>jwt를 통한 사용자 식별값과 메뉴 id를 입력받아 주문을 하고 결제를 진행하는 API</p>\n<p>결제는 포인트로만 가능하며, 충전한 포인트에서 주문금액을 차감</p>\n<p>결제 성공 시 주문 내역을 kafka로 전송, 전송된 이벤트를 읽어 인기있는 메뉴 랭킹 집계</p>\n<p>DB/Redis만으로는 대용량 트래픽을 감당하기엔 한계가 있기에 kafka를 이용하여 거의 실시간으로 대규모 데이터를 처리</p>\n<p>인기있는 메뉴 랭킹 집계는 redis 명령어 zset을 이용한 이유는 원자적으로 점수를 증가시키고, 별도의 정렬 로직 구현없이 정렬되어있어 랭킹 집계가 편리하다.</p>\n<p>유저 단위로 설계한 키로 Redis 분산 락 Redisson 적용하여 같은 사용자의 포인트 차감 동시성 제어를 설계. kafka와 redis를 쓰고있고, 다수 서버 환경에서의 대용량 트래픽에 유리하기 떄문!</p>\n","urlObject":{"protocol":"http","port":"8080","path":["api","orders"],"host":["localhost"],"query":[],"variable":[]}},"response":[{"id":"6fee46e4-547a-4482-bddc-19ec00233fc5","name":"커피 주문/결제 성공","originalRequest":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"menu_id\": 1,\n      \"quantity\": 2\n    },\n    {\n      \"menu_id\": 3,\n      \"quantity\": 1\n    }\n  ]\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/orders"},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json","description":"","type":"text"}],"cookie":[{"expires":"Invalid Date","domain":"","path":""}],"responseTime":null,"body":"{\n    \"success\" : \"true\",\n    \"status\" : \"CREATED\",\n    \"message\" : \"주문 및 결제 성공\",\n    \"data\" : {\n        \"orderNumber\" : \"ORD-20260401-000001\",\n        \"paymentNumber\" : \"PAY-20260401-000001\",\n        \"totalAmount\" : 15000,\n        \"balance\" : 5000\n    }\n}"},{"id":"efcf9db5-3bed-4189-afbd-d47f2c5d4331","name":"커피 주문/결제 실패 - 잔액부족","originalRequest":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"menu_id\": 1,\n      \"quantity\": 2\n    },\n    {\n      \"menu_id\": 3,\n      \"quantity\": 1\n    }\n  ]\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/orders"},"status":"Bad Request","code":400,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json","description":"","type":"text"}],"cookie":[],"responseTime":null,"body":"{\n    \"success\" : \"false\",\n    \"status\" : \"BAD_REQUEST\",\n    \"message\" : \"포인트 잔액이 부족합니다\"\n}"},{"id":"f3e1ee1a-be44-40ad-af40-ef2f97a27dfb","name":"커피 주문/결제 실패 - 메뉴존재","originalRequest":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"menu_id\": 1,\n      \"quantity\": 2\n    },\n    {\n      \"menu_id\": 3,\n      \"quantity\": 1\n    }\n  ]\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/orders"},"status":"Not Found","code":404,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json","description":"","type":"text"}],"cookie":[{"expires":"Invalid Date","domain":"","path":""}],"responseTime":null,"body":"{\n    \"success\" : \"false\",\n    \"status\" : \"NOT_FOUND\",\n    \"message\" : \"존재하지 않는 메뉴입니다\"\n}"},{"id":"b118e4de-dd27-4738-8df4-009ebe3820d1","name":"커피 주문/결제 실패 - 요청검증","originalRequest":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"menu_id\": 1,\n      \"quantity\": 2\n    },\n    {\n      \"menu_id\": \"\",\n      \"quantity\": \"이만큼\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/orders"},"status":"Bad Request","code":400,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json","description":"","type":"text"}],"cookie":[],"responseTime":null,"body":"{\n  \"success\": false,\n  \"status\": \"BAD_REQUEST\",\n  \"message\": \"유효하지 않은 요청입니다.\",\n  \"data\": [\n    { \"field\": \"menu_id\", \"message\": \"메뉴 ID는 필수입니다\" },\n    { \"field\": \"quantity\", \"message\": \"수량은 1 이상이어야 합니다\" }\n  ]\n}"}],"_postman_id":"fe688f3f-d0e6-453e-9bc5-6a2472d99a13"},{"name":"인기 메뉴 목록 조회","id":"69daaea2-a105-4ff0-a6c6-c4131afe83c5","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"http://localhost:8080/api/menus/best","description":"<p>최근 7일간 인기있는 메뉴 3개를 조회하는 API</p>\n<p>메뉴별 주문 횟수를 정확히 집계하기 위해 주문번호를 통한 멱등키 설계하여 consumer가 수신할 시 키 체크</p>\n<p>인기있는 메뉴가 3개로 한정적이기에 리스트 형식으로 반환</p>\n","urlObject":{"protocol":"http","port":"8080","path":["api","menus","best"],"host":["localhost"],"query":[],"variable":[]}},"response":[{"id":"031575fb-d73e-4dd1-b2b3-1b01b651d748","name":"인기 메뉴 목록 조회 성공","originalRequest":{"method":"GET","header":[],"url":"http://localhost:8080/api/menus/best"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json","description":"","type":"text"}],"cookie":[],"responseTime":null,"body":"{\n    \"success\" : \"true\",\n    \"status\" : \"OK\",\n    \"message\" : \"인기메뉴 조회 성공\",\n    \"data\" : [\n        {\n            \"rank\" : 1,\n            \"menuId\" : 1,\n            \"name\" : \"아메리카노\",\n            \"price\" : 4500,\n            \"status\" : \"ON_SALE\",\n            \"order_count\" : 549\n        },\n        {\n            \"rank\" : 2,\n            \"menuId\" : 2,\n            \"name\" : \"카페라떼\",\n            \"price\" : 5000,\n            \"status\" : \"ON_SALE\",\n            \"order_count\" : 487\n        },\n        {\n            \"rank\" : 3,\n            \"menuId\" : 3,\n            \"name\" : \"카페모카\",\n            \"price\" : 5500,\n            \"status\" : \"ON_SALE\",\n            \"order_count\" : 343\n        }\n    ]\n}"}],"_postman_id":"69daaea2-a105-4ff0-a6c6-c4131afe83c5"},{"name":"회원가입","id":"bb0e23c5-1902-42b8-bfd0-cbd780d4aba7","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n    \"name\" : \"테스트\",\n    \"email\" : \"test@email.com\",\n    \"password\" : \"1234test!5678\",\n    \"phoneNumber\" : \"01012345678\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/users/signup","urlObject":{"protocol":"http","port":"8080","path":["api","users","signup"],"host":["localhost"],"query":[],"variable":[]}},"response":[{"id":"da638cef-f247-4a4e-82ae-7ab972f392ec","name":"회원가입 성공","originalRequest":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n    \"name\" : \"테스트\",\n    \"email\" : \"test@email.com\",\n    \"password\" : \"1234test!5678\",\n    \"phoneNumber\" : \"01012345678\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/users/signup"},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json","description":"","type":"text"}],"cookie":[],"responseTime":null,"body":"{\n    \"success\": true,\n    \"status\": \"201 CREATED\",\n    \"message\": \"회원가입 성공\",\n    \"data\": {\n        \"userId\": 1,\n        \"name\": \"테스트\",\n        \"email\": \"test@email.com\",\n        \"createdAt\": \"2026-04-06T11:36:34.580545\"\n    }\n}"}],"_postman_id":"bb0e23c5-1902-42b8-bfd0-cbd780d4aba7"},{"name":"로그인","id":"4c1642ba-53ea-4315-890f-eb8f5b970605","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n    \"email\" : \"test@email.com\",\n    \"password\" : \"1234test!5678\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/users/login","urlObject":{"protocol":"http","port":"8080","path":["api","users","login"],"host":["localhost"],"query":[],"variable":[]}},"response":[{"id":"85343459-be20-4694-8eeb-f55a6d9c84d3","name":"로그인","originalRequest":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n    \"email\" : \"test@email.com\",\n    \"password\" : \"1234test!5678\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:8080/api/users/login"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json","description":"","type":"text"}],"cookie":[{"expires":"Invalid Date","domain":"","path":""}],"responseTime":null,"body":"{\n    \"success\": true,\n    \"status\": \"200 OK\",\n    \"message\": \"로그인 성공\",\n    \"data\": {\n        \"accessToken\": \"eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJ0ZXN0QGVtYWlsLmNvbSIsInJvbGUiOiJVU0VSIiwiaWF0IjoxNzc1NDQyOTk2LCJleHAiOjE3NzU0NDQ3OTZ9.8xN8EFGWm2IbouHJfPU-sfOsFX9gmpyFR2GYbwmTmhEWRxEzg0SqYBAvdf-2QXn_\",\n        \"refreshToken\": \"eyJhbGciOiJIUzM4NCJ9.eyJpYXQiOjE3NzU0NDI5OTYsImV4cCI6MTc3NjA0Nzc5Nn0.7-i7oJFlmnOwMjptVkfc16s_uM9TCILsivxpDVWMQ9wFSajM8hKJrg5Qjo2jaCt9\"\n    }\n}"}],"_postman_id":"4c1642ba-53ea-4315-890f-eb8f5b970605"}]}