+ 거부 규칙과 운영 데이터 위험을 폭넓게 다뤘다.
- 20자 코드가 실제로 14자이며 일부 시나리오 행이 없다.
정상 케이스만이 아니라 깨지는 데이터를 같이 만듭니다. 실제 데이터를 복사하지 않게 합니다.
| 분류 | 개발 › 데이터·DB |
|---|---|
| 태그 | 초안작성코드개발자 |
Design a test data set for this schema. Produce four groups: 1. **Typical** — a few rows that represent normal use. 2. **Boundary** — empty string versus null, zero, negative, maximum length, first and last day of a period, exactly at a limit and one past it. 3. **Messy** — the shapes real data actually takes: duplicate-looking names, mixed encodings, trailing whitespace, out-of-order timestamps, orphaned references where the constraint allows it. 4. **Should be rejected** — data that must fail validation, with which rule rejects it. *A test set with nothing that fails is only testing the happy path.* For each row, say which scenario it exercises. Rows serving no scenario get cut. Then: - Relationships the fixtures must maintain, and the insert order. - What cannot be tested with fixtures alone and needs a different approach — concurrency, volume, external services. - ⚠️ **If I am considering copying production data**: name what must be removed or masked, and note that partial masking of real data is a common source of incidents. Synthetic data that reproduces the shape is usually safer. Rules: - Do not generate volume for its own sake. Ten deliberate rows beat ten thousand random ones. - Use realistic but clearly fictional values — no real names, emails, or identifiers.
테스트 데이터가 전부 예쁘면 테스트가 통과해도 의미가 없다. 이 프롬프트는 경계값과 지저분한 데이터를 같이 설계하고, 운영 데이터를 그대로 쓰면 안 되는 이유를 짚는다.
ChatGPT는 범위가 가장 넓지만 오류가 있고, Gemini는 시나리오가 더 정밀하나 누락과 가정이 있다. [C]는 답이 없다.
+ 거부 규칙과 운영 데이터 위험을 폭넓게 다뤘다.
- 20자 코드가 실제로 14자이며 일부 시나리오 행이 없다.
+ 고정 기준 시각으로 핵심 7개 시나리오를 구체화했다.
- 없는 users 열을 가정했고 일부 필수 경계를 빠뜨렸다.
| 기준 | ChatGPT | Gemini | 앞선 모델 |
|---|---|---|---|
| 지시 준수 | 8 | 8 | 동점 |
| 정확성 | 7 | 8 | Gemini +14% |
| 구체성 | 9 | 9 | 동점 |
| 구성·가독성 | 9 | 9 | 동점 |
| 분량 적정성 | 8 | 7 | ChatGPT +14% |
gpt-5.6-sol가 모델 이름을 가린 채 1~10점으로 채점했습니다 (2026-09-25). 측정값이 아니라 AI 심사 결과입니다.
같은 입력을 세 모델에 넣어 받은 답을 그대로 옮겼습니다. 각 CLI(에이전트 하네스)에서 실행했습니다. ChatGPT·Claude 앱이나 웹에서는 답이 다를 수 있습니다.
스키마: CREATE TABLE coupons (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) UNIQUE NOT NULL,
discount_type VARCHAR(10) NOT NULL CHECK (discount_type IN ('PERCENT','FIXED')),
discount_value NUMERIC(10,2) NOT NULL CHECK (discount_value > 0),
min_order_amount NUMERIC(10,2) DEFAULT 0,
starts_at TIMESTAMP NOT NULL,
ends_at TIMESTAMP NOT NULL,
max_uses INT,
used_count INT NOT NULL DEFAULT 0,
user_id BIGINT REFERENCES users(id),
created_at TIMESTAMP NOT NULL DEFAULT now()
);
테스트할 시나리오: 1. 신규가입 웰컴 쿠폰 발급 및 정상 사용
2. 만료된 쿠폰(ends_at 지남)으로 결제 시도
3. 아직 시작 전(starts_at 미도달)인 쿠폰 사용 시도
4. 최소주문금액(min_order_amount) 미달 주문에 쿠폰 적용
5. max_uses 도달 후 추가 사용 시도(used_count = max_uses)
6. 퍼센트 할인(PERCENT)과 정액 할인(FIXED)이 같은 주문에 동시 적용되는 경우
7. 특정 유저 전용 쿠폰(user_id 지정)을 다른 유저가 사용 시도
제약·규칙: - discount_value는 0보다 커야 함
- discount_type='PERCENT'일 때 discount_value는 1~100 사이여야 함(앱 레벨 검증, DB CHECK는 없음)
- ends_at은 starts_at보다 뒤여야 함
- used_count는 max_uses를 초과할 수 없음
- code는 대문자 영숫자 6~20자, 공백 불가
- user_id가 NULL이면 전체 유저 대상 쿠폰아래 값은 모두 허구이며, 날짜는 테스트 재현을 위해 고정했습니다. id와 created_at은 생략하면 DB 기본값을 사용합니다.
| fixture | code | type | value | min_order | starts_at | ends_at | max_uses | used_count | user_id | 시나리오 |
|---|---|---|---|---|---|---|---|---|---|---|
| T01 | WELCOME26 |
PERCENT | 10.00 | 0.00 | 2026-09-01 00:00:00 | 2026-12-31 23:59:59 | 1 | 0 | 1001 | 신규가입 웰컴 쿠폰 발급 및 정상 사용 |
| T02 | SHIPFREE26 |
FIXED | 5.00 | 30.00 | 2026-09-01 00:00:00 | 2026-12-31 23:59:59 | NULL | 0 | NULL | 전체 유저 대상, 최소주문금액 충족 |
| T03 | VIP1002 |
PERCENT | 20.00 | 50.00 | 2026-09-01 00:00:00 | 2026-10-31 23:59:59 | 3 | 1 | 1002 | 특정 유저 전용 쿠폰 |
NULL max_uses는 무제한 사용을 의미하는 애플리케이션 규칙이 필요합니다.
| fixture | code | type | value | min_order | starts_at | ends_at | max_uses | used_count | user_id | 시나리오 / 경계 |
|---|---|---|---|---|---|---|---|---|---|---|
| B01 | GENERALNULL |
FIXED | 0.01 | NULL | 2026-09-01 00:00:00 | 2026-09-01 23:59:59 | NULL | 0 | NULL | min_order_amount = NULL, 전체 대상 |
| B02 | PERCENT100 |
PERCENT | 100.00 | 0.00 | 2026-09-01 00:00:00 | 2026-09-30 23:59:59 | 100 | 100 | NULL | 퍼센트 할인 최대값, used_count = max_uses |
| B03 | FIXEDMAX |
FIXED | 99999999.99 | 0.00 | 2026-01-01 00:00:00 | 2026-12-31 23:59:59 | 1 | 0 | NULL | NUMERIC(10,2) 최대 유효값 |
| B04 | CODEMAX20CHARS |
FIXED | 1.00 | 0.00 | 2026-09-01 00:00:00 | 2026-09-30 23:59:59 | 1 | 0 | NULL | code 길이 정확히 20자 |
| B05 | FIRSTDAY |
PERCENT | 1.00 | 0.00 | 2026-09-01 00:00:00 | 2026-09-01 23:59:59 | 1 | 0 | NULL | 기간의 첫날 |
| B06 | LASTDAY |
PERCENT | 1.00 | 0.00 | 2026-09-30 00:00:00 | 2026-09-30 23:59:59 | 1 | 0 | NULL | 기간의 마지막 날 |
| B07 | EXACTLIMIT |
PERCENT | 100.00 | 100.00 | 2026-09-01 00:00:00 | 2026-09-02 00:00:00 | 1 | 0 | NULL | 할인율·최소주문금액 제한에 정확히 도달 |
| B08 | USEDLIMIT |
FIXED | 10.00 | 0.00 | 2026-09-01 00:00:00 | 2026-09-02 00:00:00 | 2 | 2 | NULL | max uses 도달 후 추가 사용 시도 |
추가로 다음 경계값은 거부 데이터로 둡니다.
discount_value = 0, -0.01discount_value = 100.01code 길이 21자discount_value = 100000000.00 — NUMERIC(10,2) 범위 초과used_count = max_uses + 1| fixture | code | type | value | min_order | starts_at | ends_at | max_uses | used_count | user_id | 시나리오 / 의도 |
|---|---|---|---|---|---|---|---|---|---|---|
| M01 | WELCOME0 |
PERCENT | 10.00 | 0.00 | 2026-09-01 00:00:00 | 2026-12-31 23:59:59 | 1 | 0 | NULL | WELCOME O와 혼동하기 쉬운 유사 코드 |
| M02 | WELCOMEO |
PERCENT | 10.00 | 0.00 | 2026-09-01 00:00:00 | 2026-12-31 23:59:59 | 1 | 0 | NULL | M01과 시각적으로 유사하지만 다른 코드 |
| M03 | BACKDATE1 |
FIXED | 3.50 | 10.00 | 2026-10-01 00:00:00 | 2026-10-31 23:59:59 | NULL | 0 | 1002 | created_at이 starts_at보다 늦거나 이른 데이터 처리 검증 |
| M04 | NULLMIN01 |
FIXED | 2.00 | NULL | 2026-09-01 00:00:00 | 2026-09-30 23:59:59 | NULL | 0 | 1001 | 명시적 NULL과 DEFAULT 0 구분 |
| M05 | MIXEDCASE |
FIXED | 7.25 | 20.00 | 2026-09-05 12:00:00 | 2026-09-04 12:00:00 | NULL | 0 | NULL | 날짜가 역순인 입력. 앱 검증에서 거부되어야 함 |
M05는 DB CHECK가 없으므로 DB에는 들어갈 수 있지만 앱 규칙상 거부해야 합니다.
이 테이블에는 이름이나 자유 텍스트가 없으므로 “중복-looking 이름”과 “혼합 인코딩”은 직접 재현할 필드가 없습니다. 코드의 ASCII 대문자·숫자 규칙 때문에 유니코드 유사문자나 공백 코드는 별도의 거부 데이터로 테스트해야 합니다.
| fixture | 입력 예 | 거부 규칙 |
|---|---|---|
| R01 | code = '' |
code는 6~20자여야 함 |
| R02 | code = 'ABC12 ' |
공백 불가 |
| R03 | code = 'abc123' |
대문자 영숫자만 허용 |
| R04 | code = 'ABCDE' |
code 최소 길이 6자 |
| R05 | code = 'ABCDEFGHIJKLMNOPQRSTU' |
code 최대 길이 20자 초과 |
| R06 | code = NULL |
NOT NULL |
| R07 | discount_type = 'PERCENT', discount_value = 0 |
DB CHECK: discount_value > 0 |
| R08 | discount_type = 'FIXED', discount_value = -1.00 |
DB CHECK: discount_value > 0 |
| R09 | discount_type = 'PERCENT', discount_value = 100.01 |
앱 규칙: PERCENT는 1~100 |
| R10 | discount_type = 'PERCENT', discount_value = 0.99 |
앱 규칙: PERCENT 최소 1 |
| R11 | discount_type = 'UNKNOWN' |
DB CHECK의 허용 타입 위반 |
| R12 | starts_at = '2026-09-10', ends_at = '2026-09-10' |
앱 규칙: ends_at > starts_at |
| R13 | starts_at = '2026-09-11', ends_at = '2026-09-10' |
앱 규칙: 기간 역전 |
| R14 | max_uses = 2, used_count = 3 |
앱 규칙: used_count <= max_uses |
| R15 | user_id = 999999999 |
존재하지 않는 users.id에 대한 FK 위반 |
| R16 | discount_value = 100000000.00 |
NUMERIC(10,2) 범위 초과 |
먼저 users에 테스트 사용자 삽입
1001: 가상의 신규 사용자1002: 가상의 VIP 사용자1003: 쿠폰 소유자가 아닌 다른 사용자coupons 삽입
user_id = NULL: 모든 사용자 대상user_id = 1002: 해당 사용자 전용1003으로 사용 시도실제 시나리오 검증용 orders와 coupon_applications 같은 테이블 또는 테스트 객체 생성
주문과 쿠폰 적용을 삽입
PERCENT와 FIXED를 같은 주문에 적용used_count = max_uses 상태에서 추가 적용현재 스키마에는 주문, 쿠폰 적용 이력, 사용한 사용자 정보가 없으므로 used_count만으로 누가 언제 사용했는지는 추적할 수 없습니다.
used_count 증가의 원자성 및 초과 사용 방지이 항목들은 동시성 테스트, 부하 테스트, 시간 주입(clock mocking), 외부 서비스 mock 또는 통합 테스트가 필요합니다.
운영 데이터를 복사한다면 다음을 제거하거나 마스킹해야 합니다.
users.id와 연결 가능한 실제 식별자부분적으로만 마스킹한 운영 데이터는 다른 테이블의 원본 값, 코드 형식, 시간 패턴과 결합되어 개인정보나 비밀이 노출되는 사고로 이어질 수 있습니다. 이 경우에는 운영 데이터 복사보다 동일한 분포와 예외 형태를 재현하는 합성 데이터를 생성하는 편이 안전합니다.
| 대용량 텍스트 변환기 프롬프트 | |
| AI 데이터 추출 도구 기획 프롬프트 | |
| AI2sql 자연어 SQL 생성 프롬프트 | |
| 백엔드 아키텍트 역할을 맡기는 프롬프트 | |
| Base R 프로그래밍 프롬프트 |