Conversation
📝 WalkthroughWalkthroughRedis Sorted Sets 개념 문서, Black Friday 테스트 데이터 자동 설정 HTTP 스크립트, 최대 동시성 탐색을 위한 k6 로드 테스트 스크립트를 추가한다. Changes
예상 코드 리뷰 난도🎯 3 (Moderate) | ⏱️ ~25 minutes 운영 관점의 주의사항seed-black-friday.http 파일 검토 필요:
k6/breakpoint.js 파일 검토 필요:
추가 운영 관점:
🚥 Pre-merge checks | ❌ 3❌ Failed checks (3 warnings)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@http/commerce-api/seed-black-friday.http`:
- Around line 27-33: The POST body hardcodes "brandId": 1 which can fail if the
created brand ID differs; update the .http flow to capture the created brand's
id from Step 1 and inject it into Step 2 (or define a top-level `@brandId`
variable for manual override) so the product creation JSON uses the captured
`@brandId` instead of 1; ensure the instructions reference Step 1 (brand
creation), Step 2 (product creation JSON payload), and the variable name
`@brandId` so testers can run against both empty and prepopulated DBs.
- Around line 53-64: The SQL INSERT/UPDATE examples for seeding Stock are
missing the BaseEntity timestamp columns, causing NOT NULL failures; update the
INSERT statements used for Stock to include created_at and updated_at (e.g., use
NOW() or equivalent) and update the UPDATE statements to set updated_at as well
so the Stock records (id, created_at, updated_at) required by the Stock entity
and BaseEntity lifecycle are populated; target the SQL snippets that reference
Stock, product_id, quantity, created_at, and updated_at and adjust both INSERT
and UPDATE examples accordingly.
In `@k6/breakpoint.js`:
- Around line 61-83: In setup(), validate each user creation by checking the
http.post response status and body and abort the test on any failed registration
(e.g., log the response and call fail/stop/throw) so setup doesn't silently
continue; additionally validate PRODUCT_ID before returning by parsing with
parseInt and verifying it's a positive integer (not NaN or <=0) and abort with a
clear error if invalid; reference the setup function, the http.post calls that
create users, and the parseInt(PRODUCT_ID) usage when making these checks and
early-exit behaviors.
- Around line 95-101: The POST body in the k6 script is missing required card
fields so OrderFacade.createOrder rejects requests; update the JSON payload sent
in the http.post call (the object built where items uses data.productId) to
include cardType and cardNo (e.g., cardType: "VISA", cardNo: "4111111111111111"
or values derived from the test data), ensuring the payload matches the
server-side validation and allows the request to proceed rather than returning
the "카드 정보는 필수입니다." BAD_REQUEST error.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e4e16265-02ab-4f48-ba6b-5cb415ce28f1
⛔ Files ignored due to path filters (3)
CONTEXT.mdis excluded by!**/*.mdand included by**docs/black-friday-simulator.mdis excluded by!**/*.mdand included by**docs/concepts/README.mdis excluded by!**/*.mdand included by**
📒 Files selected for processing (3)
docs/concepts/01-sorted-set-basic.mermaidhttp/commerce-api/seed-black-friday.httpk6/breakpoint.js
| { | ||
| "brandId": 1, | ||
| "name": "한계점테스트상품", | ||
| "price": 10000, | ||
| "description": "breakpoint.js 전용 - 재고 10000", | ||
| "thumbnailImageUrl": "https://example.com/bp.png" | ||
| } |
There was a problem hiding this comment.
brandId: 1 하드코딩은 기존 데이터 존재 시 실패하거나 잘못된 브랜드에 연결될 수 있다.
Step 1에서 생성한 브랜드의 ID가 반드시 1이라는 보장이 없다. 기존 브랜드가 있거나 auto_increment 시퀀스가 리셋되지 않은 환경에서는 다른 ID가 반환된다.
운영 관점에서 테스트 데이터 설정이 실패하면 부하 테스트 전체가 무효화된다.
수정안:
- Step 1 응답에서
brandId를 캡처하여 Step 2에서 사용하도록 안내 추가 - 또는
.http파일 상단에@brandId변수를 정의하고 수동 입력 유도
추가 테스트: 빈 DB와 기존 데이터가 있는 DB 양쪽에서 실행하여 브랜드 연결 확인 필요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@http/commerce-api/seed-black-friday.http` around lines 27 - 33, The POST body
hardcodes "brandId": 1 which can fail if the created brand ID differs; update
the .http flow to capture the created brand's id from Step 1 and inject it into
Step 2 (or define a top-level `@brandId` variable for manual override) so the
product creation JSON uses the captured `@brandId` instead of 1; ensure the
instructions reference Step 1 (brand creation), Step 2 (product creation JSON
payload), and the variable name `@brandId` so testers can run against both empty
and prepopulated DBs.
| ### 3. 재고 등록 (Admin API에 재고 수정 엔드포인트 없으므로 직접 SQL 사용) | ||
| ### MySQL 접속: localhost:3307 / root / root | ||
| ### | ||
| ### 한계점 테스트 상품 재고 (productId는 2-A 응답에서 확인): | ||
| ### INSERT INTO stock (product_id, quantity) VALUES ({productId_A}, 10000); | ||
| ### | ||
| ### 블랙프라이데이 상품 재고 (productId는 2-B 응답에서 확인): | ||
| ### INSERT INTO stock (product_id, quantity) VALUES ({productId_B}, 50); | ||
| ### | ||
| ### 재고 리셋 (테스트 재실행 시): | ||
| ### UPDATE stock SET quantity = 50 WHERE product_id = {productId_B}; | ||
| ### UPDATE stock SET quantity = 10000 WHERE product_id = {productId_A}; No newline at end of file |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: BaseEntity 컬럼 정의 및 default 설정 확인
echo "=== BaseEntity 필드 확인 ==="
fd -i "BaseEntity.java" --exec cat {}
echo ""
echo "=== Stock 테이블 DDL 또는 Flyway 마이그레이션 확인 ==="
rg -l "CREATE TABLE.*stock" --iglob "*.sql"
fd -i "*.sql" --exec rg -A 20 "CREATE TABLE.*stock" {}Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java
Length of output: 2150
🏁 Script executed:
#!/bin/bash
# Stock 테이블 관련 SQL 파일 검색
echo "=== SQL 마이그레이션 파일 목록 ==="
find . -name "*.sql" -type f | head -20
echo ""
echo "=== Stock 테이블 생성 쿼리 검색 ==="
find . -name "*.sql" -type f -exec grep -l -i "stock" {} \;
echo ""
echo "=== Stock 테이블 DDL 상세 확인 ==="
find . -name "*.sql" -type f -exec grep -A 30 -i "create table.*stock" {} +Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java
Length of output: 387
🏁 Script executed:
#!/bin/bash
# Flyway 마이그레이션 파일 확인
echo "=== Flyway 마이그레이션 디렉토리 탐색 ==="
find . -path "*/db/migration*" -name "*.sql" 2>/dev/null | head -30
echo ""
echo "=== Stock 엔티티 클래스 확인 ==="
fd -i "Stock.java" --exec cat {}
echo ""
echo "=== fixture SQL에서 stock 관련 내용 확인 ==="
grep -i "stock\|created_at\|updated_at" ./docs/scripts/fixture/product_performance_100k.sql 2>/dev/null | head -50Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java
Length of output: 2568
직접 SQL INSERT 문에 created_at, updated_at 컬럼 추가 필수.
Stock이 BaseEntity를 상속하므로 id, created_at, updated_at 컬럼이 필수이다. BaseEntity의 @PrePersist 라이프사이클 콜백은 JPA를 통한 INSERT에서만 자동으로 created_at, updated_at을 설정한다. 직접 SQL을 실행할 경우 이 콜백이 작동하지 않아 NOT NULL 제약 위반으로 INSERT가 실패한다.
아래와 같이 수정한다:
수정 예시
### 한계점 테스트 상품 재고 (productId는 2-A 응답에서 확인):
### INSERT INTO stock (product_id, quantity, created_at, updated_at) VALUES ({productId_A}, 10000, NOW(), NOW());
### 블랙프라이데이 상품 재고 (productId는 2-B 응답에서 확인):
### INSERT INTO stock (product_id, quantity, created_at, updated_at) VALUES ({productId_B}, 50, NOW(), NOW());
### 재고 리셋 (테스트 재실행 시):
### UPDATE stock SET quantity = 50, updated_at = NOW() WHERE product_id = {productId_B};
### UPDATE stock SET quantity = 10000, updated_at = NOW() WHERE product_id = {productId_A};UPDATE 문에도 updated_at 컬럼을 반드시 함께 수정하여 감사 추적을 유지한다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@http/commerce-api/seed-black-friday.http` around lines 53 - 64, The SQL
INSERT/UPDATE examples for seeding Stock are missing the BaseEntity timestamp
columns, causing NOT NULL failures; update the INSERT statements used for Stock
to include created_at and updated_at (e.g., use NOW() or equivalent) and update
the UPDATE statements to set updated_at as well so the Stock records (id,
created_at, updated_at) required by the Stock entity and BaseEntity lifecycle
are populated; target the SQL snippets that reference Stock, product_id,
quantity, created_at, and updated_at and adjust both INSERT and UPDATE examples
accordingly.
| export function setup() { | ||
| console.log(`[setup] 테스트 유저 ${TOTAL_USERS}명 등록 시작 (이미 존재하면 무시)`); | ||
|
|
||
| const headers = { 'Content-Type': 'application/json' }; | ||
|
|
||
| for (let i = 1; i <= TOTAL_USERS; i++) { | ||
| const loginId = `${USER_PREFIX}${String(i).padStart(3, '0')}`; | ||
| http.post( | ||
| `${BASE_URL}/api/v1/users`, | ||
| JSON.stringify({ | ||
| loginId, | ||
| password: PASSWORD, | ||
| name: `부하테스트${i}`, | ||
| birthDate: '19900101', | ||
| email: `${loginId}@loadtest.com`, | ||
| }), | ||
| { headers } | ||
| ); | ||
| } | ||
|
|
||
| console.log('[setup] 유저 등록 완료. 테스트 시작.'); | ||
| return { productId: parseInt(PRODUCT_ID) }; | ||
| } |
There was a problem hiding this comment.
Setup 단계에서 유저 등록 실패 시 테스트 전체가 무의미해진다.
현재 setup()은 유저 등록 결과를 확인하지 않는다. 등록 실패 시 VU 함수에서 모든 주문이 인증 오류(401/403)로 실패하고, 이를 sysError로 분류하여 실제 한계점 데이터가 오염된다.
또한 parseInt(PRODUCT_ID)가 유효하지 않은 입력에서 NaN을 반환하면 모든 주문 요청이 실패한다.
수정안:
- 유저 등록 응답 상태 확인 및 실패 시 조기 종료
PRODUCT_ID유효성 검증 추가
export function setup() {
console.log(`[setup] 테스트 유저 ${TOTAL_USERS}명 등록 시작 (이미 존재하면 무시)`);
+ const productId = parseInt(PRODUCT_ID);
+ if (isNaN(productId) || productId <= 0) {
+ throw new Error(`PRODUCT_ID 환경변수가 유효하지 않음: ${PRODUCT_ID}`);
+ }
+
const headers = { 'Content-Type': 'application/json' };
+ let registeredCount = 0;
for (let i = 1; i <= TOTAL_USERS; i++) {
const loginId = `${USER_PREFIX}${String(i).padStart(3, '0')}`;
- http.post(
+ const res = http.post(
`${BASE_URL}/api/v1/users`,
JSON.stringify({
loginId,
password: PASSWORD,
name: `부하테스트${i}`,
birthDate: '19900101',
email: `${loginId}@loadtest.com`,
}),
{ headers }
);
+ if (res.status === 201 || res.status === 409) { // 201 생성, 409 이미 존재
+ registeredCount++;
+ }
}
- console.log('[setup] 유저 등록 완료. 테스트 시작.');
- return { productId: parseInt(PRODUCT_ID) };
+ console.log(`[setup] 유저 등록 완료: ${registeredCount}/${TOTAL_USERS}명. 테스트 시작.`);
+ if (registeredCount === 0) {
+ throw new Error('유저 등록 전체 실패. 테스트 중단.');
+ }
+ return { productId };
}추가 테스트: 잘못된 PRODUCT_ID (예: abc, -1)로 실행하여 조기 실패 확인 필요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@k6/breakpoint.js` around lines 61 - 83, In setup(), validate each user
creation by checking the http.post response status and body and abort the test
on any failed registration (e.g., log the response and call fail/stop/throw) so
setup doesn't silently continue; additionally validate PRODUCT_ID before
returning by parsing with parseInt and verifying it's a positive integer (not
NaN or <=0) and abort with a clear error if invalid; reference the setup
function, the http.post calls that create users, and the parseInt(PRODUCT_ID)
usage when making these checks and early-exit behaviors.
| const res = http.post( | ||
| `${BASE_URL}/api/v1/orders`, | ||
| JSON.stringify({ | ||
| items: [{ productId: data.productId, quantity: 1 }], | ||
| }), | ||
| { headers, timeout: '10s' } | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: OrderV1Dto.CreateOrderRequest 필드의 nullable/validation 어노테이션 확인
echo "=== CreateOrderRequest DTO 정의 ==="
ast-grep --pattern 'record CreateOrderRequest($$$) {
$$$
}'
echo ""
echo "=== OrderV1Dto 전체 검색 ==="
rg -A 30 "CreateOrderRequest" --type java | head -100Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java
Length of output: 9886
🏁 Script executed:
cat -n apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Dto.java | head -50Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java
Length of output: 1781
🏁 Script executed:
# Check for validation annotations around the record definition
rg -B 5 -A 10 "record CreateOrderRequest" apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Dto.java
# Check for `@Valid` or validation on the controller parameter
rg -B 5 -A 15 "@RequestBody.*CreateOrderRequest" apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderController.javaRepository: Loopers-dev-lab/loop-pack-be-l2-vol3-java
Length of output: 1466
🏁 Script executed:
# Check orderFacade.createOrder implementation
rg -A 20 "createOrder\(" apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java | head -50
# Check if there are any global validators or validation annotations
rg "@NotNull|@Valid|@Validated" apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/ --type java
# Check the test script to see what it's actually sending
head -120 k6/breakpoint.js | tail -40Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java
Length of output: 3346
주문 요청에서 필수 필드 cardType, cardNo 누락으로 모든 요청 실패.
OrderFacade.createOrder()에서 다음과 같이 검증한다:
if (cardType == null || cardNo == null || cardNo.isBlank()) {
throw new CoreException(ErrorType.BAD_REQUEST, "카드 정보는 필수입니다.");
}현재 스크립트(95-101줄)는 items만 전송하므로 모든 요청이 400 에러로 실패하며, 에러 메시지가 "카드 정보는 필수입니다."가 되어 테스트의 sysError 판별 로직(재고 부족 여부 확인)과 일치하지 않는다. 결과적으로 실제 성능 테스트 결과가 전무하게 되어 운영 가치가 없다.
요청 바디에 cardType과 cardNo를 추가해야 한다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@k6/breakpoint.js` around lines 95 - 101, The POST body in the k6 script is
missing required card fields so OrderFacade.createOrder rejects requests; update
the JSON payload sent in the http.post call (the object built where items uses
data.productId) to include cardType and cardNo (e.g., cardType: "VISA", cardNo:
"4111111111111111" or values derived from the test data), ensuring the payload
matches the server-side validation and allows the request to proceed rather than
returning the "카드 정보는 필수입니다." BAD_REQUEST error.
📌 Summary
🧭 Context & Decision
문제 정의
선택지와 결정
🏗️ Design Overview
변경 범위
주요 컴포넌트 책임
ComponentA:ComponentB:ComponentC:🔁 Flow Diagram
Main Flow