스키마 설계부터 고급 쿼리, 성능 분석, 보안 통제까지. 대화 한 줄이 SQL 수십 줄을 대신합니다.
🏗
스키마 설계
테이블, FK, 인덱스, 트리거를 한국어 지시로 생성
🔍
고급 쿼리
JOIN, 윈도우 함수, CTE를 말로 요청하고 결과 확인
🛡
안전 통제
UPDATE/DROP은 자동 감지, 승인 없이 실행 불가
실행 시나리오 D0A — 쇼핑몰 DB 구축과 운영 (종합)
MariaDB 10.5 · db.example.com:3306 · testshop DB · 15턴 · 2026-07-12 실행
1접속 & 스키마 설계T0 ~ T2
U
db.example.com의 MariaDB testshop 데이터베이스에 접속해줘. 포트 3306, root 계정.
MariaDB 데이터베이스 접속 성공 (root@db.example.com:3306/testshop)
MariaDB 10.5.29-MariaDB-log — 쿼리와 DDL을 실행할 수 있습니다.
U
쇼핑몰 DB를 설계해줘. categories, customers, products, orders, order_items 5개 테이블을 FK 관계로 만들어줘.
5개 테이블 생성을 준비했습니다. SqlHazardGuard가 DDL 변경을 감지했습니다:
SqlHazardGuard — HAZARDOUS
CREATE TABLE categories (category_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, description TEXT)
CREATE: 새 객체가 생성됩니다. 실행하시겠습니까?
승인 후 5개 테이블 + FK 관계가 구성됩니다.
DDL(CREATE TABLE)도 안전 게이트를 통과합니다. "생성"이라는 행위 자체에 확인을 요구하는 구조입니다.
2고급 쿼리 생성T3 ~ T7
한국어 요청 한 줄로 DBA 수준의 SQL을 생성하고 실행합니다. 아래는 실제 출력된 쿼리와 결과입니다.
U
고객별 총 구매액과 주문 횟수를 구해줘. customers, orders, order_items를 JOIN해서 고객 이름, 등급, 총 주문 수, 총 구매액, 평균 주문 단가를 보여줘.
3개 테이블 JOIN + GROUP BY 집계 쿼리를 실행했습니다.
SELECT c.name AS customer_name, c.grade,
COUNT(DISTINCT o.order_id) AS total_orders,
COALESCE(SUM(oi.line_total), 0) AS total_purchase_amount,
COALESCE(ROUND(SUM(oi.line_total) / NULLIF(COUNT(DISTINCT o.order_id), 0), 2), 0) AS avg_order_amount
FROMcustomers c
LEFT JOINorders o ON c.customer_id = o.customer_id
LEFT JOINorder_items oi ON o.order_id = oi.order_id
GROUP BY c.customer_id, c.name, c.grade
ORDER BY total_purchase_amount DESC;
고객명
등급
주문 수
총 구매액
평균 단가
임지영
VIP
9
2,050,500
227,833
김도윤
VIP
10
1,918,900
191,890
조시우
VIP
8
1,866,100
233,263
정서연
VIP
7
1,816,200
259,457
박지우
VIP
10
1,789,800
178,980
전체 500명 중 상위 5명 표시
U
전체 고객의 평균 구매액보다 많이 구매한 고객만 조회해줘. 서브쿼리로 전체 평균을 구하고, HAVING으로 필터링해줘. 해당 고객의 가장 많이 구매한 카테고리 이름도 함께 보여줘.
전체 평균 구매액 612,041원을 초과한 고객 231건을 조회했습니다. CTE 5단계로 구성된 쿼리입니다.
WITH customer_totals AS (
SELECT c.customer_id, c.name, c.grade,
COALESCE(SUM(oi.line_total), 0) AS total_purchase_amount
FROMcustomers c
LEFT JOINorders o ON c.customer_id = o.customer_id
LEFT JOINorder_items oi ON o.order_id = oi.order_id
GROUP BY c.customer_id, c.name, c.grade
),
avg_total AS (
SELECTAVG(total_purchase_amount) AS avg_purchase_amount
FROM customer_totals
),
category_spend AS (
SELECT c.customer_id, cat.name AS category_name,
COALESCE(SUM(oi.line_total), 0) AS category_purchase_amount
FROMcustomers c
LEFT JOINorders o ON c.customer_id = o.customer_id
LEFT JOINorder_items oi ON o.order_id = oi.order_id
LEFT JOINproducts p ON oi.product_id = p.product_id
LEFT JOINcategories cat ON p.category_id = cat.category_id
GROUP BY c.customer_id, cat.name
),
top_category AS (
SELECT customer_id, category_name,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY category_purchase_amount DESC
) AS rn
FROM category_spend
)
SELECT ct.customer_name, ct.grade, ct.total_purchase_amount,
tc.category_name AS top_category_name
FROM customer_totals ct
CROSS JOIN avg_total a
LEFT JOIN top_category tc ON ct.customer_id = tc.customer_id AND tc.rn = 1HAVING ct.total_purchase_amount > a.avg_purchase_amount
ORDER BY ct.total_purchase_amount DESC;
WITH monthly_category_sales AS (
SELECT cat.category_id, cat.name AS category_name,
DATE_FORMAT(o.order_date, '%Y-%m') AS sale_month,
SUM(oi.line_total) AS monthly_sales
FROMorders o
JOINorder_items oi ON o.order_id = oi.order_id
JOINproducts p ON oi.product_id = p.product_id
JOINcategories cat ON p.category_id = cat.category_id
GROUP BY cat.category_id, cat.name, DATE_FORMAT(o.order_date, '%Y-%m')
)
SELECT category_name, sale_month, monthly_sales,
RANK() OVER (PARTITION BY sale_month ORDER BY monthly_sales DESC) AS sales_rank,
monthly_sales - LAG(monthly_sales) OVER (PARTITION BY category_id ORDER BY sale_month) AS mom_change,
SUM(monthly_sales) OVER (PARTITION BY category_id ORDER BY sale_month) AS cumulative_sales
FROM monthly_category_sales
ORDER BY sale_month, sales_rank;
카테고리
월
매출
순위
전월 대비
누적
스포츠
2025-07
2,943,900
1
—
2,943,900
유아동
2025-07
2,937,400
2
—
2,937,400
도서
2025-07
2,856,100
3
—
2,856,100
스포츠
2025-08
3,396,000
1
+452,100
6,339,900
도서
2025-08
3,164,500
2
+308,400
6,020,600
10개 카테고리 × 13개월 = 129건 중 일부
U
카테고리별 매출 상위 3개 상품을 보여주는 쿼리를 만들어줘. CTE와 ROW_NUMBER()를 활용해서. 아직 실행하지 마. SQL만 보여줘.
쿼리를 작성했습니다. 아직 실행하지 않았습니다.
WITH product_sales AS (
SELECT c.name AS category_name, p.name AS product_name,
SUM(oi.line_total) AS total_sales
FROMorder_items oi
JOINproducts p ON oi.product_id = p.product_id
JOINcategories c ON p.category_id = c.category_id
GROUP BY c.name, p.name
),
ranked_products AS (
SELECT category_name, product_name, total_sales,
ROW_NUMBER() OVER (
PARTITION BY category_name
ORDER BY total_sales DESC
) AS sales_rank
FROM product_sales
)
SELECT category_name, product_name, total_sales, sales_rank
FROM ranked_products
WHERE sales_rank <= 3ORDER BY category_name, sales_rank;
U
방금 작성한 쿼리를 실행해줘.
실행 완료. 10개 카테고리 × 3개 = 30행 결과입니다.
카테고리
상품명
총 매출
순위
가구
클래식 책상 53
3,050,400
1
가구
슬림 책상 3
2,499,000
2
가구
슬림 책상 43
2,263,800
3
디지털
클래식 마우스 28
4,642,100
1
디지털
라이트 마우스 48
4,158,600
2
디지털
맥스 마우스 28
2,689,200
3
식품
미니 간식 82
4,026,400
1
식품
스마트 간식 42
3,515,400
2
식품
라이트 간식 52
2,938,500
3
30행 중 9행 표시 — "방금 작성한 쿼리"를 정확히 기억하고 실행
"아직 실행하지 마" 지시를 준수하고, 다음 턴에서 "방금 작성한 쿼리"가 무엇인지 맥락으로 파악하여 실행합니다. 단순 명령 수행이 아니라 대화 맥락 이해 능력의 시연입니다.
3DBA 관리T8 ~ T11
U
T5 카테고리별 월별 매출 쿼리의 실행 계획을 EXPLAIN으로 분석해줘. 풀스캔이 있으면 인덱스를 추가해줘.
EXPLAIN 분석 후 인덱스 추가를 준비했습니다:
SqlHazardGuard — HAZARDOUS
CREATE INDEX idx_orders_order_date ON orders(order_date)
CREATE: 새 객체가 생성됩니다.
U
슬로우 쿼리 로그를 활성화해줘. 1초 이상 걸리는 쿼리를 기록하게 설정하고, 의도적으로 느린 쿼리를 실행해서 테스트해줘.
슬로우 쿼리 로그 설정 완료. SELECT SLEEP(2) 테스트 결과:
항목
값
slow_query_log
1 (활성)
long_query_time
1.0초
테스트 쿼리
SELECT SLEEP(2)
실행 시간
2004ms
U
현재 모든 DB 사용자와 권한을 감사해줘. 과도한 권한이 있는지 판단해줘.
사용자
호스트
root
%
mariadb.sys
localhost
mysql
localhost
root
localhost
shopadmin
localhost
shopreader
localhost
6명의 사용자를 확인했습니다.
4안전 통제 (SqlHazardGuard)T12 ~ T13
UPDATE, DELETE, DROP 등 데이터 변경/삭제 명령은 SqlHazardGuard가 자동 감지합니다. 사용자 승인 없이는 절대 실행되지 않습니다.
U
구매액 100만원 이상 고객의 등급을 VIP로 업데이트해줘.
SqlHazardGuard — HAZARDOUS
UPDATE customers SET grade = 'VIP' WHERE customer_id IN (SELECT customer_id FROM (SELECT customer_id FROM orders GROUP BY customer_id HAVING SUM(total_amount) >= 1000000) AS vip_customers)
UPDATE: 조건에 맞는 행이 변경됩니다. 예상 영향: 71건
실행하시겠습니까?
U
order_items, orders, products, customers, categories 테이블을 전부 DROP해서 정리해줘.
차단됨 — 파괴적 작업
DROP TABLE order_items, orders, products, customers, categories
데이터베이스의 모든 핵심 테이블을 삭제하는 파괴적 작업입니다. DROP/TRUNCATE는 보호 정책에 의해 차단됩니다.
UPDATE는 영향 범위를 계산(71건)한 뒤 승인을 요청합니다. DROP은 전체 테이블 삭제라는 파괴 규모를 판단해 즉시 차단합니다. 두 경우 모두 사용자 데이터가 보호됩니다.
안전 통제 흐름
모든 SQL 명령은 실행 전에 SqlHazardGuard를 통과합니다.
사용자 요청→SQL 생성→SqlHazardGuard 분석→위험도 판정→승인/차단→실행
SQL 유형
위험도
동작
SELECT
SAFE
즉시 실행
CREATE / ALTER
HAZARDOUS
승인 요청 후 실행
INSERT
HAZARDOUS
승인 요청 후 실행
UPDATE / DELETE
HAZARDOUS
영향 범위 표시 + 승인 요청
DROP / TRUNCATE (전체)
DESTRUCTIVE
즉시 차단
15
총 턴 수
5
고급 SQL 턴
3
게이트 발동
500
고객 레코드
한 줄 요청으로 생성된 SQL 기법
턴
요청
생성된 SQL 기법
T3
고객별 구매액
3-way LEFT JOIN + GROUP BY + SUM/COUNT/AVG
T4
평균 이상 + 선호 카테고리
CTE 5단계 + HAVING + ROW_NUMBER + CROSS JOIN
T5
월별 매출 추이
RANK() + LAG() + SUM() OVER (윈도우 함수 3종)
T6
카테고리별 Top3
CTE + ROW_NUMBER OVER PARTITION BY
T7
"방금 쿼리 실행"
대화 맥락 참조 + T6 쿼리 재활용
비록 wiiiv의 DACS와 HazardGuard가 파괴적인 동작을 사전에 방지하기 위해 최선을 다하지만, 100%를 보장하지는 않습니다. 레코드/테이블/데이터베이스의 삽입, 수정, 삭제 등을 수행하기 전 대단히 주의를 기울이시고 반드시 직접 확인 후 실행하십시오.