☰ 분류

버그 원인 범위 좁히는 프롬프트

추측으로 고치기 전에 무엇을 확인해 범위를 반으로 줄일지 정합니다.

분류개발 › 코딩
태그분석코드개발자
프롬프트 (영어 본문 · 답은 한국어로 옵니다)
Help me narrow down this bug.

*Do not propose a fix yet.*

Produce:
1. The possibility space — where the cause could be, grouped by layer.
2. Observations in order, each chosen to eliminate roughly half of what remains. For each: what to run or check, and what each outcome rules out.
3. What my symptom already rules out, before I run anything.
4. What the things I already tried tell us — including the ones that did not work, which are often more informative.
5. The single cheapest observation with the highest information value. Start there.

Rules:
- *Binary search over the possibility space, not a list of likely culprits.* A ranked guess list is what I am already doing and it is not working.
- Where my description is not specific enough to design a check, say what detail you need.
- Flag any assumption in my description that might itself be wrong. Bugs hide behind those.
붙여 넣으면 맨 아래에 채울 칸(증상 · 시도해본 것 · 시스템 구성)이 나옵니다

어떤 프롬프트인가

버그를 고치려고 여기저기 바꾸면 더 헤맨다. 이 프롬프트는 수정안 대신 "이걸 확인하면 가능성이 절반으로 준다"는 관찰을 순서대로 준다.

모델 비교

ChatGPT가 가정을 경계하며 가장 충실히 이진 탐색한다. Gemini는 구체적이나 과도한 단정과 DB 추적 오류가 있고, [C]는 제시되지 않았다.

ChatGPT종합 1위
44/ 50

+ 가장 값싼 관측부터 신중하게 범위를 좁힌다.

- 일부 단계는 정확한 반분보다 긴 점검표에 가깝다.

Gemini
34/ 50

+ 계층별 원인과 분기 검사를 구체적으로 연결했다.

- 근거 없이 원인을 배제·확정한 대목이 많다.

기준ChatGPTGemini앞선 모델
지시 준수97ChatGPT +29%
정확성94ChatGPT +125%
구체성99동점
구성·가독성98ChatGPT +13%
분량 적정성86ChatGPT +33%

gpt-5.6-sol가 모델 이름을 가린 채 1~10점으로 채점했습니다 (2026-09-24). 측정값이 아니라 AI 심사 결과입니다.

답 전문 보기

같은 입력을 세 모델에 넣어 받은 답을 그대로 옮겼습니다. 각 CLI(에이전트 하네스)에서 실행했습니다. ChatGPT·Claude 앱이나 웹에서는 답이 다를 수 있습니다.

넣은 입력
증상: 결제 완료 후에도 주문 상태가 'PENDING'에 멈춰 있는 케이스가 전체 결제의 약 2~3%에서 발생함. 사용자는 결제는 됐는데 주문 확인 페이지에서 계속 로딩 중으로 보임.

시도해본 것: 1) PG사 webhook 로그를 보면 결제 성공 콜백은 정상적으로 도착함. 2) orders 테이블 update 쿼리 자체는 로그에 남아있는데 status 컬럼이 그대로인 경우가 있어서 트랜잭션 커밋 문제로 의심해 재시도 로직(3회)을 추가했지만 발생 빈도가 그대로임. 3) 로컬/스테이징에서 부하 없이 재현 시도했지만 한 번도 재현 안됨. 4) 같은 시간대 로그를 보면 Redis idempotency 락 관련 에러가 몇 건 같이 찍혀있긴 한데 상관관계는 확실치 않음.

시스템 구성: Node.js 18 + Express 결제 서비스, PostgreSQL 15 (RDS), 결제 webhook 수신 후 Redis에 idempotency key로 중복 처리 방지, 서버 3대를 ALB로 로드밸런싱, PG사는 토스페이먼츠
gpt-5.6-luna (codex CLI · 에이전트 하네스) · 실행일 2026-09-24

가장 먼저 할 관찰은 **“실제로 PENDING인가, 아니면 읽는 경로만 PENDING으로 보이는가”**입니다.

1. 가능성 공간

A. 관측·조회 계층

  • 주문 확인 페이지가 캐시된 상태를 표시함
  • API가 PostgreSQL primary가 아닌 read replica를 조회함
  • replica lag 또는 failover 직후의 오래된 상태
  • frontend polling이 실패해 마지막 PENDING 상태를 계속 표시함
  • 주문 ID와 결제 ID가 다른 주문을 조회함

B. Webhook 수신·해석 계층

  • webhook이 도착했지만 다른 서버/프로세스에서 처리됨
  • 로그는 “도착”만 기록하고 실제 handler 진입은 실패함
  • Toss 이벤트의 결제 ID와 내부 주문 ID 매핑이 잘못됨
  • 성공 이벤트가 여러 개 있고, 오래된 이벤트가 뒤늦게 처리됨
  • 응답 타임아웃이나 프로세스 종료로 처리 도중 중단됨

C. Redis idempotency 계층

  • 유효한 webhook이 lock 획득 실패로 스킵됨
  • lock 오류를 “이미 처리됨”으로 잘못 해석함
  • lock TTL이 너무 길어 정상 재처리가 막힘
  • 서로 다른 webhook이 같은 idempotency key를 사용함
  • 서버 간 Redis 연결/시간/설정 차이가 있음

D. 애플리케이션 상태 전이 계층

  • UPDATE ... WHERE 조건이 맞지 않아 rowCount = 0
  • PENDING → PAID 전이 조건이나 상태명이 실제 데이터와 다름
  • SQL 실행 로그는 있지만 await되지 않았거나 결과가 확인되지 않음
  • 성공 처리 후 다른 코드가 다시 PENDING으로 덮어씀
  • 여러 webhook/worker가 순서 뒤바뀌어 처리됨
  • 예외가 SQL 이후, commit 이전 또는 commit 이후에 발생함

E. 트랜잭션·PostgreSQL 계층

  • transaction rollback
  • commit 실패 또는 commit 결과 미확인
  • deadlock, serialization failure, connection 종료
  • 다른 transaction의 lock 때문에 업데이트가 지연·취소됨
  • 애플리케이션이 서로 다른 DB/schema/환경을 보고 있음

F. 인프라·외부 결제 계층

  • 특정 서버만 잘못된 환경변수나 DB 연결을 사용함
  • ALB 라우팅 이후 프로세스가 재시작됨
  • RDS failover 또는 연결 장애
  • PG사가 결제 성공은 기록했지만 해당 webhook은 실제 주문 완료 이벤트가 아님

2. 관찰 순서

각 단계는 결과에 따라 남은 공간을 크게 두 갈래로 나눕니다.

관찰 1 — Primary DB의 실제 상태와 화면/API 상태 비교

문제가 된 주문 하나를 골라 다음 세 값을 같은 시점에 비교합니다.

SELECT id, status, updated_at
FROM orders
WHERE id = '<order_id>';

그리고:

  • 주문 확인 페이지가 호출하는 API 응답
  • 해당 API가 조회하는 DB endpoint가 primary인지 replica인지
  • frontend가 실제로 받은 응답

결과:

  • Primary DB가 PAID인데 API/화면이 PENDING
    → webhook, transaction, update 문제는 우선 제외하고 조회 경로·replica·캐시·frontend 쪽으로 좁힙니다.
  • Primary DB도 PENDING
    → 조회 문제는 제외하고 webhook 이후 처리 경로를 봅니다.
  • 문제 주문의 primary 상태 자체를 확인할 수 없음
    → 현재 “DB update가 안 됐다”는 전제가 아직 입증되지 않은 것입니다.

관찰 2 — “도착”과 “처리 시작”을 구분

실패 사례 20~50건에 대해 같은 correlation key로 다음 순서를 맞춥니다.

  1. ALB 수신
  2. webhook handler 진입
  3. payload 검증 및 주문 매핑
  4. Redis lock 결과
  5. transaction 시작
  6. UPDATE 실행
  7. commit 결과
  8. HTTP 응답

결과:

  • ALB/수신 로그는 있으나 handler 진입이 없음
    → ingress, 라우팅, 프로세스 종료, 파싱 전 오류 쪽을 제외할 수 없습니다.
  • handler까지 진입하지만 매핑 또는 validation에서 끝남
    → DB와 Redis는 우선 제외됩니다.
  • 매핑 이후 transaction까지 도달
    → ingress와 기본 payload 문제는 상당 부분 제외됩니다.

현재의 “webhook 로그에 도착했다”는 관찰만으로는 handler가 끝까지 실행됐다는 뜻이 아닙니다.

관찰 3 — UPDATE의 실제 영향 행 수 확인

각 실패 사례의 SQL 로그에서 단순히 SQL 문자열이 아니라 다음을 확인합니다.

  • bound parameter
  • rowCount
  • transaction ID 또는 request ID
  • WHERE 조건
  • 실행 직전 주문 상태

결과:

  • rowCount = 0
    → commit 문제보다 주문 ID, 조건, 이미 변경된 상태, 상태 전이 조건 문제입니다.
  • rowCount = 1인데 primary DB가 PENDING
    → commit/rollback, 다른 writer의 덮어쓰기, 잘못된 DB 연결을 봅니다.
  • rowCount 기록 자체가 없음
    → 현재 SQL 실행 로그는 성공적인 상태 변경의 증거가 아닙니다.

특히 UPDATE에 다음과 같은 조건이 있다면 중요합니다.

WHERE order_id = $1
  AND status = 'PENDING'

이 경우 SQL이 실행됐다는 사실과 실제로 행이 변경됐다는 사실은 다릅니다.

관찰 4 — commit 결과와 rollback 여부 비교

rowCount = 1인 실패 사례만 골라 transaction 로그를 대조합니다.

결과:

  • commit 성공 확인됨
    → 단순 transaction commit 실패는 제외하고, 이후 덮어쓰기·다른 DB·조회 경로를 봅니다.
  • rollback 또는 commit 실패
    → PostgreSQL/connection/transaction 계층으로 좁힙니다.
  • commit 로그가 없음
    → commit 미대기, 프로세스 종료, 예외, 로깅 누락 중 하나입니다. “SQL 실행됨”만으로는 성공을 판단할 수 없습니다.

재시도 3회가 빈도를 바꾸지 않았다는 점은 일시적인 commit 실패만이 주원인일 가능성을 낮춥니다. 다만 재시도도 같은 lock, 같은 조건, 같은 서버 상태를 반복했다면 구조적 실패는 그대로 남습니다.

관찰 5 — Redis 결과를 실제 처리 결과와 대조

Redis 에러가 있었던 시간대의 사례를 다음 세 그룹으로 나눕니다.

  • lock 획득 성공 후 처리
  • lock이 이미 존재해 스킵
  • Redis 오류로 처리 중단 또는 성공으로 간주

결과:

  • 실패 주문 대부분이 lock 실패/Redis 오류 그룹
    → Redis 계층이 유력한 경계가 됩니다.
  • 성공적으로 lock을 획득한 실패 주문도 같은 비율로 존재
    → Redis 에러는 상관관계일 가능성이 커집니다.
  • Redis 에러 로그가 있지만 해당 주문의 key와 연결되지 않음
    → 시간적 동시성만으로는 원인으로 볼 수 없습니다.

관찰 6 — 이후 상태를 쓴 모든 writer 확인

상태 변경 감사 로그, CDC, application write 로그, PostgreSQL audit/WAL 기반 기록이 있다면 주문별로 모든 상태 변경을 시간순으로 봅니다.

결과:

  • PAID 이후 다시 PENDING 기록이 있음
    → webhook 처리 실패가 아니라 경쟁 writer 또는 순서 역전입니다.
  • PAID 기록 자체가 없음
    → update/commit 이전 단계에 남습니다.
  • PAID 기록은 있고 현재 DB가 PENDING
    → 다른 DB 조회, rollback으로 보이는 기록, 후속 overwrite를 구분해야 합니다.

3. 증상이 이미 배제하는 것

현재 설명만으로 어느 정도 배제되는 것은 다음입니다.

  • 모든 결제가 실패하는 문제는 아님
  • Toss 결제 승인 자체가 전부 실패하는 문제는 아님
  • 주문 페이지의 로딩 UI가 모든 주문에서 깨진 문제는 아님
  • webhook이 항상 전혀 오지 않는 문제는 아님
  • 단순한 저부하 환경에서 항상 재현되는 결정적 로직 오류는 아님

반대로 아직 배제되지 않은 것:

  • webhook “수신 로그”와 실제 처리 완료의 차이
  • 특정 서버에서만 발생하는 문제
  • row count 0인 UPDATE
  • commit 이전/이후 rollback 또는 overwrite
  • replica/cache/frontend의 오래된 값
  • Redis lock 오류와 처리 스킵의 인과관계
  • 특정 주문 ID 매핑 오류
  • 이벤트 순서 역전

4. 지금까지 시도가 말해주는 것

  1. PG webhook 성공 콜백이 도착한다
    PG 결제 성공과 내부 주문 상태 반영은 분리되어 있다는 뜻입니다. 다만 webhook handler가 성공적으로 완료됐다는 증거는 아닙니다.

  2. UPDATE SQL 로그가 남지만 상태가 그대로다
    SQL 발행과 상태 변경은 다릅니다. rowCount, commit, 이후 overwrite를 확인해야 합니다. 현재 증거만으로 transaction commit 문제라고 결론 내릴 수 없습니다.

  3. 3회 재시도 후 빈도 변화가 없다
    일시적 DB 오류 하나가 원인일 확률은 낮아집니다. 재시도가 lock 실패, 잘못된 조건, 잘못된 ID, 같은 경쟁 상황을 반복했을 가능성도 있습니다.

  4. 로컬/스테이징에서 재현되지 않는다
    부하가 없어서만 발생하는 문제라고 단정할 수 없습니다. 서버 3대, ALB, Redis, RDS topology, 실제 webhook 중복/순서, production 데이터 분포 중 하나 이상이 필요할 수 있습니다.

  5. Redis lock 에러가 같은 시간대에 있다
    시간적 상관관계만 있습니다. 실패 주문의 idempotency key에 대해 실제로 lock 획득 실패 또는 처리 스킵이 있었는지 연결해야 합니다.

5. 가장 싼 동시에 정보량이 큰 관찰

실패한 주문 한 건을 골라 primary PostgreSQL의 orders.status와 주문 확인 API의 응답을 비교하세요.

  • primary가 PAID, API가 PENDING이면 처리/write 경로를 즉시 크게 배제할 수 있습니다.
  • primary가 PENDING이면 그때 webhook handler → Redis → UPDATE row count → commit 순서로 추적하면 됩니다.

이 관찰을 하려면 다음 정보가 필요합니다.

  • 주문 확인 API가 primary와 replica 중 어디를 조회하는지
  • orders 조회에 캐시가 있는지
  • 실패 주문의 order_id, webhook의 payment/order 식별자
  • SQL 로그에 rowCount, commit/rollback 로그가 있는지
  • 상태 변경을 기록하는 감사 로그나 CDC가 있는지
  • Redis 오류가 해당 주문의 idempotency key와 연결되는지

같은 분류의 프롬프트

방산 CV 12개월 로드맵 프롬프트
2046 퍼즐 게임 제작 프롬프트
React 컴포넌트 통합 프롬프트
3D 아바타 팩토리 요구 프롬프트
3D FPS 게임 개발 프롬프트