오류 메시지·스택 트레이스를 정확히 읽고, 재현→격리(이분탐색)→수정→검증의 절차로 원인을 규명하며, 맥락과 함께 AI에 붙여 넣어 안전하게 고치는 실전 문제해결 역량.
4컷으로 보기 — 디버깅은 반려된 민원을 처리하는 과정과 같다

- 11컷: 반려된 민원서류, 당황한 공무원
- 22컷: 반려 사유서 정독하며 결재선 역추적
- 33컷: 서류 항목 하나씩 격리해 원인 찾기(이분탐색)
- 44컷: 보완 후 재접수, 통과 도장 쾅!
왜 중요한가
비개발 실무자가 AI로 무언가를 만들 때 가장 자주 포기하는 지점이 '오류가 났는데 뭘 어떻게 해야 할지 모를 때'다. 오류 메시지를 읽지 못하면 AI에 "안 돼요"만 반복하게 되고, AI는 맥락 없이 추측성 수정을 제안하며, 그 수정이 또 다른 것을 깨뜨리는 악순환에 빠진다. 이 역량이 없으면 결과물은 '한 번 되면 다행, 깨지면 방치'가 되고, 회귀(잘 되던 게 깨짐)를 통제하지 못해 신뢰할 수 없는 자동화가 된다. 반대로 오류를 유형으로 분류하고 재현·격리·검증 절차를 갖추면, AI를 '추측 기계'가 아니라 '정확한 수리공'으로 부릴 수 있다. 공공 실무에서는 디버깅 과정에서 실제 자료·개인정보를 무심코 오류 로그째로 외부 AI에 붙여 넣는 사고가 빈발하므로, 안전 게이트와의 정합도 이 모듈의 핵심이다.
행정 실무 비유
디버깅은 반려된 민원을 처리하는 과정과 같다. '왜 반려됐는지' 반려 사유서(오류 메시지)를 먼저 정확히 읽고, 어느 서류·어느 항목이 문제였는지 사안을 격리해 원인을 규명하고, 보완 후 같은 창구에서 다시 접수(재현·검증)해 통과 여부를 확인한다. 스택 트레이스는 결재선을 역순으로 따라가는 처리 이력이며, 회귀는 '지난주엔 통과되던 서류가 이번 주엔 반려된' 상황이고, 근본 원인 규명은 표면 증상이 아니라 반려를 유발한 진짜 규정·데이터를 찾는 감사 작업이다. AI에 물을 때는 '안 돼요'가 아니라 반려 사유서 원문·해당 서식·직전에 바꾼 것을 함께 첨부해야 정확한 보완 지시가 나온다.
🖐 실무 따라하기 — 복붙해서 따라 하면 됩니다
- 1재현 — 오류를 눈앞에서 다시 낸다
실습용 폴더를 만들고, 아래 두 파일을 그대로 만든다. 먼저 합성 데이터 minwon.csv(개인정보는 전부 가짜)를 저장하고, 이어서 집계 스크립트 tally.py를 저장한다. 그다음 python3 tally.py로 실행해 '오류가 실제로 나는 것'을 먼저 확인한다. (고치기 전에 재현부터 하는 이유: 재현 안 되는 버그는 고쳤는지 확인할 수 없다.)
터미널 명령# 1) 데이터 파일 생성 (터미널에 그대로 붙여넣기) cat > minwon.csv <<'CSV' 접수번호,민원인,연락처,처리기간(일) MW-2026-0001,홍길동,010-0000-0000,10 MW-2026-0002,김철수,010-0000-0000, MW-2026-0003,이영희,010-0000-0000,14 MW-2026-0004,박민수,010-0000-0000,미접수 MW-2026-0005,최지현,010-0000-0000,15 CSV # 2) 집계 스크립트 생성 cat > tally.py <<'PY' import csv total = 0 count = 0 with open("minwon.csv", newline="", encoding="utf-8") as f: for row in csv.DictReader(f): days = int(row["처리기간(일)"]) # <-- 여기서 죽는다 total += days count += 1 print("평균 처리기간:", total / count, "일") PY # 3) 실행 python3 tally.py→ 결과 빨간 Traceback이 출력되고 마지막 줄에 대략 'ValueError: invalid literal for int() with base 10: ''(빈 값)가 뜬다. 위쪽 스택에 File "tally.py", line 8, in <module> / days = int(row["처리기간(일)"]) 가 보인다 → 오류가 실제로 재현됨을 확인. (참고: 앞의 두 cat 블록은 파일만 만들고 화면에 아무 출력도 없는 게 정상이다. 내용 확인은 cat minwon.csv / cat tally.py로.)
- 2스택 트레이스에서 '내 줄' 찾기
Traceback을 아래→위로 읽는다. 맨 아래는 '오류 종류'(ValueError…), 그 위 블록들은 '어느 파일 몇 번째 줄'인지다. 남의 라이브러리(csv 등) 줄은 건너뛰고, 파일명이 tally.py인 줄 = 내가 고칠 줄을 찾는다. 여기서는 line 8의 days = int(...) 이다. 셸에서 그 줄만 뽑아 눈으로 확인한다.
터미널 명령# tally.py의 8번째 줄만 번호와 함께 출력해서 '내 줄' 확인 nl -ba tally.py | sed -n '8p'
→ 결과 '8 days = int(row["처리기간(일)"])' 처럼 8번 줄이 표시된다. 이 줄이 스택 트레이스가 가리킨 '내 코드 줄'이며, 문자열을 int()로 강제 변환하는 지점이 범인 후보임을 특정.
- 3격리 — 이분탐색으로 '어느 행'이 죽이는지 좁힌다
원인이 데이터의 어느 행인지 모를 때는 절반씩 잘라 실행한다. 먼저 위 절반(1~2행)만, 죽으면 그 절반 안에, 안 죽으면 아래 절반에 원인이 있다. 아래 명령은 헤더+처음 N개 데이터행만 임시 파일로 만들어 돌려보는 방식이다. head 개수를 3→5로 바꿔가며 어느 행 추가 시 죽는지 좁힌다.
터미널 명령# 헤더 포함 위에서 3줄(=데이터 2행)만 남겨 실행 → 죽는지 본다 head -n 3 minwon.csv > _probe.csv python3 - <<'PY' import csv for row in csv.DictReader(open("_probe.csv", encoding="utf-8")): print(row["접수번호"], "->", repr(row["처리기간(일)"]), "| int 변환:", end=" ") try: print(int(row["처리기간(일)"])) except Exception as e: print("실패:", type(e).__name__, e) PY→ 결과 MW-2026-0001은 10으로 변환 성공, MW-2026-0002는 값이 '' (빈칸)이라 '실패: ValueError'로 출력된다. head 개수를 5로 늘리면 MW-2026-0004의 '미접수'도 실패. → 죽이는 행은 '빈칸' 행과 '미접수'(숫자 아님) 행 두 종류로 격리 완료.
- 4비식별 후 AI에 질문 (개인정보 제거가 핵심)
이제 AI에 물어 고친다. 단, 원본 CSV·로그를 그대로 붙이지 말 것. 아래 프롬프트는 이미 이름·연락처를 가짜로 바꾸고 개인정보 컬럼을 뺀 '최소 재현 예시'만 담았다. 이 전문을 ChatGPT 또는 Claude 웹에 그대로 붙여넣는다. (맥락=오류 메시지+내 줄+격리 결과를 함께 줘야 AI가 정확히 답한다.)
프롬프트 (복사해서 AI에 입력)파이썬 초급자입니다. 아래 CSV 집계 코드가 특정 행에서 ValueError로 멈춥니다. 원인과 수정 코드를 알려주세요. [상황] '처리기간(일)' 열의 평균을 구하려 합니다. 그런데 이 열에는 빈칸('')이나 숫자가 아닌 값('미접수')이 섞여 있습니다. [오류] ValueError: invalid literal for int() with base 10: '' [내 코드 줄] days = int(row["처리기간(일)"]) [비식별 최소 재현 데이터] (실제 민원인 정보 아님, 합성) 접수번호,처리기간(일) MW-2026-0001,10 MW-2026-0002, MW-2026-0003,14 MW-2026-0004,미접수 MW-2026-0005,15 [요구사항] 1. 빈칸/'미접수' 같은 비숫자 값은 평균 계산에서 제외(결측 처리)할 것. 2. 제외한 건수도 함께 세어 출력할 것. 3. 초급자용으로 주석 달고, try/except로 안전하게 처리한 전체 코드로 줄 것.→ 결과 AI가 원인을 '빈 문자열/문자값을 int()로 변환 불가'로 설명하고, int() 대신 try/except 또는 값 검사로 건너뛰는 수정 코드를 준다. 주의: AI가 float() 사용·컬럼명 오타·결측 개수 미출력 등 요구를 일부 빠뜨릴 수 있으니 다음 step에서 반드시 실행 검증.
- 5수정 적용 — 안전하게 건너뛰는 코드로 교체
AI 답을 그대로 믿지 말고, 우리 요구(결측 개수 출력·나눗셈 0 방지)를 충족하는 검증된 버전으로 tally.py를 통째로 덮어쓴다. 아래는 그 최소 정답 코드다. (AI가 준 코드와 비교해 빠진 요구사항이 있으면 이쪽을 기준으로 삼는다.)
터미널 명령cat > tally.py <<'PY' import csv total = 0 # 유효 처리기간 합 count = 0 # 유효 건수 skipped = 0 # 빈칸/비숫자로 제외한 건수 with open("minwon.csv", newline="", encoding="utf-8") as f: for row in csv.DictReader(f): value = row["처리기간(일)"].strip() try: days = int(value) # 숫자면 성공 except ValueError: skipped += 1 # 빈칸('')·'미접수' 등은 건너뜀 continue total += days count += 1 if count == 0: print("평균 처리기간: 계산 불가 (유효 데이터 0건)") else: avg = total / count print(f"평균 처리기간: {avg:.1f}일 (유효 데이터 {count}건, 결측 {skipped}건 제외)") PY→ 결과 명령이 조용히 끝나며(오류 없음, 화면 출력 없음이 정상) tally.py가 새 내용으로 교체된다. int() 대신 try/except로 감쌌고, count==0일 때 0으로 나누는 사고도 막았음을 코드에서 확인. 내용 확인은 cat tally.py.
- 6재검증 — 고쳐졌는지 + 회귀(멀쩡하던 것 안 깨졌는지) 확인
수정본을 실행해 오류 없이 결과가 나오는지 본다. 그리고 '숫자만 있는 정상 데이터'에서도 여전히 옳은 평균을 내는지(회귀 확인) 별도 데이터로 한 번 더 돌린다. 반려 민원을 보완해 재접수했으면 접수처가 다시 검토하듯, 고친 코드는 원래 케이스와 새 케이스를 둘 다 통과해야 한다.
터미널 명령# 1) 원래 문제 데이터로 재실행 (고쳐졌는지) python3 tally.py # 2) 회귀 확인: 숫자만 있는 정상 데이터로도 맞는지 cat > minwon.csv <<'CSV' 접수번호,민원인,연락처,처리기간(일) MW-2026-0001,홍길동,010-0000-0000,10 MW-2026-0002,김철수,010-0000-0000,20 CSV python3 tally.py
→ 결과 1) '평균 처리기간: 13.0일 (유효 데이터 3건, 결측 2건 제외)' 가 오류 없이 출력(원래 문제 해결). 2) 정상 데이터에서는 '평균 처리기간: 15.0일 (유효 데이터 2건, 결측 0건 제외)' 로 정확한 평균을 내며, 결측 처리 로직이 정상 데이터를 망가뜨리지 않음(회귀 없음)을 확인 → 완주.
스택 내 위치
5계층 스택(프롬프트·컨텍스트·하네스·헤르메스·메타) 위에 얹히는 '실전' 모듈. 무언가를 만들다 반드시 마주치는 '깨짐'을 다루며, 하네스(검증 루프)·평가(무엇이 통과인가)·환경 준비(env-setup)와 직접 맞물린다. 앞선 계층이 '어떻게 잘 만드는가'라면 이 모듈은 '안 될 때 어떻게 되돌리는가'를 책임진다.
핵심 개념 (15)
프로그램이 실패했을 때 '무엇이·어디서·왜' 잘못됐는지 알려주는 진단문. 대개 오류 유형 이름 + 설명 + 위치로 구성된다. 반려 사유서에 해당하며, 겁내지 말고 끝까지(특히 첫 줄과 마지막 줄) 읽는 것이 디버깅의 출발점이다.
💡 오류 메시지를 읽지 못하면 AI에 '안 돼요'만 반복하게 되어 추측성 수정만 받는다.
오류가 발생하기까지 함수·코드가 서로를 호출한 경로를 역순으로 쌓아 보여주는 이력. 결재선을 역방향으로 따라간 처리 이력과 같다. 맨 아래(또는 위, 언어별 상이)가 실제 터진 지점이고, 내 코드 파일명이 등장하는 줄이 대개 손대야 할 곳이다.
💡 어느 파일·몇 번째 줄이 문제인지 트레이스가 정확히 짚어주므로, 무작정 전체를 뒤지지 않아도 된다.
정상 흐름으로 처리할 수 없는 사건이 생겨 프로그램이 실행을 중단하고 알리는 것. 예: 없는 파일 열기, 숫자가 와야 할 곳에 글자. '예외를 던진다(throw)'고 표현하며, 붙잡지 못하면 프로그램이 멈춘다.
💡 예외 이름(TypeError, FileNotFoundError 등) 자체가 오류 유형을 알려주는 1차 분류 신호다.
오류를 원할 때마다 똑같이 다시 일으킬 수 있게 만드는 것. '어떤 입력·어떤 순서·어떤 환경에서 100% 다시 나는가'를 확정하는 단계. 재현이 안정되기 전에는 고쳐도 고쳐진 건지 알 수 없다.
💡 재현되지 않는 버그는 수정 여부를 검증할 수 없고, 재현 조건 자체가 원인의 절반을 알려준다.
문제 범위를 절반씩 잘라 원인을 좁히는 방법. 코드·데이터·설정을 반으로 나눠 '어느 쪽에서 나는가'를 반복 확인하면, 100줄 중 문제의 1줄을 최소 시도로 찾는다. git의 커밋 이력을 반씩 잘라 회귀 커밋을 찾는 것도 같은 원리다.
💡 전체를 한꺼번에 보면 원인을 못 찾지만, 절반씩 배제하면 몇 번 만에 근원에 도달한다.
이전에 잘 작동하던 기능이 어떤 변경 이후 다시 깨진 현상. '지난주엔 통과되던 서류가 이번 주엔 반려'된 상황. 접근의 핵심 질문은 '무엇이 바뀌었나(코드·데이터·설정·환경)'이다.
💡 '작동하던 게 깨졌다'는 거의 항상 최근 변경으로 환원되므로, 변경 이력을 되짚으면 빠르게 원인을 잡는다.
프로그램이 실행 중 남기는 기록(로그)과, 브라우저·터미널에서 그 기록·오류를 보여주는 창(콘솔). 화면에 안 보이는 실패의 진짜 원인은 대개 서버 로그나 브라우저 콘솔에 찍힌다.
💡 '흰 화면'처럼 증상만으로는 알 수 없는 실패도, 콘솔·로그를 열면 정확한 오류 메시지가 드러난다.
프로그램을 특정 지점에서 잠시 멈춰 그 순간의 값·상태를 들여다보게 하는 표식. 결재 도중 서류를 멈춰 세우고 각 칸의 값을 확인하는 것과 같다. 값이 예상과 다른 지점이 곧 버그의 위치다.
💡 '왜 이 값이 이렇게 나오지'를 추측 대신 실제 값으로 확인하게 해준다.
브라우저에 내장된 진단 창(F12). Console(오류·로그), Network(요청·응답과 상태 코드), Elements(화면 구조)를 본다. 웹에서 '왜 안 되지'의 답은 대개 이 세 탭 안에 있다.
💡 비개발 실무자도 F12만 열 줄 알면 '흰 화면·안 눌리는 버튼'의 원인 단서를 스스로 확보한다.
실무에서 반복되는 오류의 분류: 문법(SyntaxError), 모듈 없음(ModuleNotFound), 타입(TypeError), 비동기(await 누락·타이밍), 환경변수(.env 누락), 포트 충돌(EADDRINUSE), 권한(Permission denied). 유형마다 조치가 완전히 다르다.
💡 오류를 유형으로 먼저 분류하면 '코드를 고칠지·설치할지·설정할지·권한을 줄지'가 즉시 갈린다.
코드가 필요로 하는 외부 부품(라이브러리·패키지)이 설치 안 됨·버전 불일치로 나는 오류. 'No module named …', 'Cannot find module …'가 신호. 조치는 코드 수정이 아니라 설치·버전 맞추기다.
💡 코드는 멀쩡한데 부품이 없어서 나는 오류를 코드 문제로 오인하면 헛수정을 반복한다.
코드 자체가 아니라 실행되는 '환경'의 차이로 나는 오류. 환경변수 누락, 파일 경로 차이, 포트 충돌, 권한, 인터넷 차단(망분리) 등. '내 컴퓨터에선 되는데 서버에선 안 됨'의 전형적 원인.
💡 공공 청사의 망분리·권한 제약 환경에서는 코드보다 환경 오류가 훨씬 흔하다.
표면 증상이 아니라 그 증상을 낳은 진짜 원인. '흰 화면'은 증상이고, 그 뒤의 '환경변수 미설정'이 근본 원인. 근본 원인을 못 잡으면 증상만 눌러 두다 다른 곳에서 재발한다. '왜?'를 몇 번 더 물어 도달한다.
💡 증상만 임시로 가리는 수정은 회귀와 재발을 부르므로, 감사하듯 원인까지 파고들어야 진짜로 해결된다.
수정이 '진짜로' 문제를 해결했는지 확인하는 단계. 처음의 재현 절차를 그대로 다시 밟아 이번엔 통과하는지, 그리고 그 수정이 다른 것을 깨뜨리지 않았는지(회귀) 함께 본다. 하네스의 검증 루프와 직결.
💡 'AI가 고쳤다니 됐겠지' 하고 넘기면 안 고쳐졌거나 다른 것을 깨뜨린 채로 배포된다.
AI가 제안한 수정이 오히려 문제를 키우거나 엉뚱할 때의 대처. 한 번에 하나만 바꿔 검증, 되돌리기(git) 확보, 증상이 아닌 오류 원문·재현 조건을 다시 정확히 제시, 두세 번 실패하면 접근 자체를 의심하고 사람에게 escalate.
💡 AI는 맥락이 부족하면 그럴듯한 오답을 자신 있게 내므로, 검증 없이 연쇄 적용하면 상태가 더 나빠진다.
학습 목표
- 오류 메시지에서 오류 유형 이름·발생 위치(파일·줄)를 찾아 소리 내어 읽을 수 있다
- '안 돼요' 대신 오류 원문·직전에 한 행동·기대했던 결과를 함께 정리해 AI에 붙여 넣을 수 있다
- 브라우저 개발자도구(F12)의 Console·Network 탭을 열어 오류 여부를 확인할 수 있다
- 흔한 오류 유형 7가지(문법·모듈없음·타입·비동기·환경변수·포트충돌·권한)의 이름을 보고 대략적 성격을 구분한다
- 오류를 안정적으로 재현하는 조건(입력·순서·환경)을 명세하고, 이분 탐색으로 문제 범위를 절반씩 좁힐 수 있다
- '작동하던 게 깨졌다'를 만나면 '무엇이 바뀌었나'를 변경 이력·환경 차이로 되짚어 회귀 원인을 찾는다
- 오류 로그를 AI에 붙여 넣기 전 PII 스캐너가 걸러야 할 실제 개인정보·경로·자격증명을 스스로 마스킹한다
- AI가 제안한 수정을 한 번에 하나씩만 적용·검증하고, 실패 시 되돌린 뒤 다른 접근을 시도한다
- 증상이 아닌 근본 원인에 도달하는 '왜' 5회 추적과 최소 재현 예제(minimal repro)를 만들어 낼 수 있다
- 의존성·환경 오류를 코드 오류와 분리 진단하고, 망분리·권한 제약 환경에 맞는 조치를 설계한다
- 재현→격리→수정→검증→회귀확인의 절차를 팀 표준(체크리스트·재현 노트 양식)으로 문서화한다
- AI에 붙여 넣을 '디버깅 맥락 패키지'(오류 원문+관련 코드+재현 조건+비식별 규칙)를 재사용 템플릿으로 자산화한다
세션 구성
| 회차 | 주제 | 시수 | 내용 | 산출물 |
|---|---|---|---|---|
| 1 | 오류를 읽는 법 — 반려 사유서 정독 | 1.5h | 오류 메시지의 구조(유형 이름·설명·위치)를 해부한다. 스택 트레이스를 결재선 역순 이력으로 읽는 훈련: 어느 줄이 '내 파일'이고 어느 줄이 '도서관(라이브러리)'인지 구분. 예외의 개념과, 예외 이름만으로 1차 유형 분류하기. '첫 줄과 마지막 줄을 먼저 본다' 원칙. 겁내지 않고 끝까지 읽기. | 샘플 오류 5건에 대해 '유형·위치·한 줄 요약'을 채운 오류 판독표 |
| 2 | AI에게 제대로 묻기 — 맥락과 함께, 안전하게 | 1.5h | '안 돼요'가 실패하는 이유. 좋은 디버깅 질문의 4요소: ①오류 원문(전체) ②관련 코드/설정 ③재현 조건(무엇을 하니 이렇게 됨) ④기대 결과. 오류 로그에 섞여 나오는 실제 개인정보·파일 경로·토큰을 붙여 넣기 전에 비식별하는 습관 — 이 플랫폼의 입력단 PII 스캐너(주민번호·전화·계좌·이메일·주소 탐지)와 서버측 재검증이 왜 있는지, 무엇을 걸러 주고 무엇은 사람이 걸러야 하는지. | 실제 개인정보를 마스킹해 완성한 '디버깅 질문 템플릿' 1장 |
| 3 | 재현→격리(이분 탐색)→수정→검증 4단 절차 | 2h | 버그 수리의 표준 절차를 손에 익힌다. 재현 조건 명세 → 이분 탐색으로 범위 절반씩 축소 → 한 번에 하나만 수정 → 처음 재현 절차로 검증. git으로 되돌리기를 안전망 삼아 과감히 시도하기. AI가 틀린 수정을 낼 때: 한 번에 하나·검증·되돌리기·두세 번 실패하면 접근 재의심. 하네스의 검증 루프·평가의 '무엇이 통과인가'와 연결. | 주어진 결함 코드를 절차대로 고치고 각 단계를 기록한 '수리 로그' |
| 4 | '작동하던 게 깨졌다' — 회귀와 근본 원인 | 1.5h | 회귀의 핵심 질문 '무엇이 바뀌었나'(코드·데이터·설정·환경). 변경 이력 되짚기와 git bisect 개념. 증상 vs 근본 원인 구분, '왜' 5회로 파고들기. '로컬은 되는데 배포만 깨짐'의 대표 원인인 환경 차이. 임시로 증상만 가린 수정이 재발·연쇄 붕괴를 부르는 이유. | 회귀 사례 1건에 대한 '무엇이 바뀌었나' 추적 노트 + 근본 원인 진단 |
| 5 | 흔한 오류 유형별 처방전 — 의존성·환경·권한 | 2h | 유형별 조치 매핑: 문법(코드 수정)·모듈없음(설치)·타입(값/형 확인)·비동기(await·타이밍)·환경변수(.env)·포트충돌(사용 중 포트 정리/변경)·권한(실행권한·소유자). 코드 오류와 환경 오류를 분리 진단하는 법. 망분리 청사에서 인터넷 차단·설치 제약·권한 제약이 만드는 특유의 오류와 대처. env-setup 모듈과 직접 연계. | 7대 유형×(신호·조치·검증) 개인용 처방전 카드 |
| 6 | 종합 실습 — 브라우저 devtools로 실제 버그 잡기 | 2h | 흰 화면·안 눌리는 버튼·저장 안 됨 같은 실전 증상을 F12 개발자도구(Console·Network)로 진단. 중단점·콘솔 로그로 값 확인. 앞 5회차 절차를 하나의 사례에 종합 적용해 재현부터 검증·회귀확인까지 완주. 팀 표준 체크리스트로 마무리. | 실전 버그 1건을 처음부터 끝까지 해결한 종합 케이스 리포트 |
실습 랩
랩 1 — 오류 판독: 스택 트레이스에서 '내 줄' 찾기
목표 · 실제 오류 텍스트를 겁내지 않고 끝까지 읽어 유형·위치·한 줄 요약으로 분해한다.
- 이 플랫폼 실습(/playground 또는 샌드박스)에서 일부러 오류가 나는 요청을 넣어 실제 오류/응답을 발생시킨다(합성 데이터만 사용).
- 나온 오류 텍스트에서 '맨 첫 줄'과 '맨 마지막 줄'에 형광펜을 친다 — 대개 이 둘이 핵심이다.
- 오류 유형 이름(예: TypeError, ModuleNotFoundError, SyntaxError)을 찾아 적는다.
- 스택 트레이스 각 줄에서 '내가 만든 파일명이 있는 줄'과 '라이브러리 내부 줄'을 구분해 표시한다.
- '유형 / 파일·줄 / 한 줄 요약' 세 칸짜리 판독표를 완성한다.
✅ 성공 기준 · 오류 5건 각각에 대해 유형·위치·요약 세 칸이 모두 채워지고, '내 줄' 최소 1개를 정확히 지목했다.
↗ 확장 · 같은 오류 5건을 유형별로 그룹핑하고, 각 유형이 '코드 수정/설치/설정/권한' 중 어디에 속하는지 분류한다.
랩 2 — 안전한 디버깅 질문 만들기: 비식별 후 AI에 붙이기
목표 · 오류 로그에 섞인 실제 개인정보·자격증명·경로를 걸러 내고, 4요소를 갖춘 디버깅 질문을 완성한다.
- 제공된 '오염된' 샘플 오류 로그(주민번호·휴대전화·이메일·계좌·서버 경로가 섞인 합성 로그)를 연다.
- 붙여 넣기 전, 개인정보로 보이는 항목을 스스로 찾아 [주민등록번호]·[휴대전화번호]처럼 라벨로 치환한다.
- 이 플랫폼 입력단으로 붙여 넣어 PII 스캐너가 무엇을 추가로 잡아내는지(차단 vs 경고) 확인한다 — 스캐너가 놓치는 '이름+주소 조합' 같은 항목은 사람이 걸러야 함을 체감한다.
- 질문에 4요소를 채운다: ①오류 원문(비식별본) ②관련 코드/설정 ③재현 조건 ④기대 결과.
- 완성된 질문을 AI에 붙여 넣어, '안 돼요'만 보냈을 때와 답변 품질을 비교한다.
✅ 성공 기준 · 전송 전 사람이 최소 1건을 마스킹했고, 스캐너의 block/warn 결과를 확인했으며, 4요소가 모두 들어간 질문으로 실행 가능한 수정 지시를 받았다.
↗ 확장 · 이 질문을 부서에서 재사용할 '디버깅 질문 표준 서식'으로 정리하고, 비식별 체크리스트를 상단에 붙인다.
랩 3 — 이분 탐색으로 회귀 커밋 격리하기
목표 · '작동하던 게 깨진' 상황에서 이분 탐색으로 문제를 만든 변경을 최소 시도로 찾아낸다.
- '지난 버전은 되고 지금은 깨지는' 재현 조건을 먼저 100% 안정적으로 확정한다.
- 전체 변경 이력(또는 코드 블록)을 절반으로 나눠, 앞 절반만 적용된 상태에서 재현되는지 확인한다.
- 재현되면 앞 절반, 안 되면 뒤 절반으로 범위를 좁혀 같은 판단을 반복한다(git bisect의 원리).
- 문제를 만든 단일 변경에 도달하면, 그 변경의 무엇이 원인인지('왜?'를 3회 이상) 규명한다.
- 증상만 가리는 임시 수정이 아니라 근본 원인을 고치고, 처음 재현 절차로 검증한다.
✅ 성공 기준 · 몇 번의 절반 나누기만으로 원인 변경 1개를 특정했고, 근본 원인을 한 문장으로 서술했으며, 수정 후 재현이 사라졌다.
↗ 확장 · 같은 사례를 '무엇이 바뀌었나' 4분류(코드·데이터·설정·환경)로 정리해 회귀 대응 체크리스트를 만든다.
사례 연구
상황 · 어느 시군의 민원 안내 실습 웹앱(합성 데이터판)을 금요일에 소규모 문구 수정 후 배포했더니, 월요일 접속 시 화면이 전체 흰 화면이 되고 콘솔에 500 오류가 떴다. 담당자는 개발자가 아니며 '지난주엔 됐는데'만 반복하다 AI에 엉뚱한 수정을 받았다.
먼저 재현이 확실한지 확인했다(새로고침·다른 브라우저·시크릿 창 모두 흰 화면 → 100% 재현). 브라우저 개발자도구 Network 탭에서 흰 화면의 직접 원인이 /api/answer 요청의 500 응답임을, 그리고 Console이 아니라 서버 로그를 봐야 함을 파악했다. 서버 스택 트레이스 맨 윗줄은 'ReferenceError: FAQ_PATH is not defined'였고, 트레이스가 가리킨 파일·줄은 금요일에 만진 파일이었다. 여기서 '회귀'로 접근: 배포 전후 무엇이 달라졌나를 이분 탐색했다. 금요일 커밋을 되돌린 임시본을 로컬에서 띄우니 정상 → 원인이 그 커밋 안에 격리됐다. 변경 파일을 보니 환경변수 이름을 FAQ_FILE에서 FAQ_PATH로 바꿨는데 배포 서버의 .env는 옛 이름 그대로여서, 배포 서버에서만 값이 비어 있었다(로컬은 우연히 둘 다 세팅돼 있었다). AI에는 '안 돼요'가 아니라 ①스택 트레이스 원문 ②해당 코드 3줄 ③.env.example의 키 이름 ④'로컬은 되고 배포만 500'이라는 재현 조건을 함께 붙여, 정확히 '변수명 불일치'를 지적받았다. .env의 키를 맞추고 재배포한 뒤, 같은 재현 절차로 정상 응답을 검증했다.
교훈 · '작동하던 게 깨졌다'는 거의 항상 '무엇이 바뀌었나'로 환원된다. 이분 탐색으로 문제 커밋을 격리하고, '로컬은 되는데 배포만 깨지는' 격차는 열에 아홉 환경(환경변수)의 차이다. AI에는 증상이 아니라 스택 트레이스·변경 이력·재현 조건을 함께 줘야 추측이 아닌 정답이 나온다.
상황 · 회계 담당자가 AI에게 받은 정산 대사용 파이썬 스크립트를 처음 돌렸더니 빨간 글씨가 여러 줄 쏟아졌다. 겁먹고 전체를 통째로 AI에 붙여 넣기 전에, 오류를 '읽는' 법을 적용했다.
파이썬 오류는 대개 마지막 줄이 결론이다. 마지막 줄은 'ModuleNotFoundError: No module named pandas'였다. 이는 문법 오류가 아니라 의존성(라이브러리 미설치) 오류라는 유형 신호다 — 코드가 틀린 게 아니라 실행 환경에 부품이 없다는 뜻. 조치는 코드 수정이 아니라 'pip install pandas' 한 줄. 설치 후 다시 돌리니 이번엔 'FileNotFoundError: 정산_2월.xlsx'가 떴다 — 또 다른 환경 문제(파일 경로). 파일을 스크립트와 같은 폴더에 두거나 전체 경로를 지정하니 통과했다. 담당자는 '오류가 바뀌었다 = 한 겹 벗겨졌다'는 진전 신호를 배웠다. 망분리 청사였다면 pip 설치 자체가 인터넷 차단·권한으로 막혔을 것이므로, 그 경우는 코드가 아니라 환경(승인된 오프라인 설치·정보화 담당 협조)의 문제임을 함께 짚었다.
교훈 · 오류에는 유형이 있고 유형마다 처방이 다르다. ModuleNotFound=설치, FileNotFound=경로, SyntaxError=코드 문법. 마지막 줄(파이썬) 한 줄만 정확히 읽어도 조치가 갈린다. '오류가 바뀌었다'는 실패가 아니라 전진이며, 망분리 환경에서는 같은 오류라도 원인이 코드가 아닌 환경일 수 있다.
흔한 실패 · 안티패턴
- 오류 메시지를 '겁나는 빨간 글씨'로만 보고 읽지 않은 채 통째로 AI에 던진다 — 첫 줄·마지막 줄만 읽어도 절반은 풀린다.
- 재현을 확정하지 않고 고치기 시작한다 — 안정적 재현이 없으면 고쳐도 고쳐졌는지 검증할 수 없다.
- 한 번에 여러 곳을 동시에 수정한다 — 무엇이 효과였는지 모르게 되고, AI의 연쇄 수정과 겹치면 상태가 더 나빠진다.
- AI가 '고쳤습니다'라고 하면 검증 없이 배포한다 — 안 고쳐졌거나 다른 것을 깨뜨린(회귀) 채로 나간다.
- 증상만 임시로 가린다(에러를 그냥 숨김) — 근본 원인이 남아 다른 곳에서 재발하고, 감사 시 설명 불가.
- 환경 오류를 코드 오류로 오인한다 — 코드는 멀쩡한데 환경변수·설치·권한·포트 문제인 것을 코드만 붙들고 헛수정.
- '로컬은 되는데'로 끝낸다 — 배포·서버·망분리 환경 차이를 확인하지 않으면 실제 사용 환경에서만 깨진다.
- 오류 로그를 비식별 없이 그대로 외부 AI에 붙여 넣는다 — 로그에 섞인 실제 개인정보·파일 경로·토큰이 제3자로 유출된다(입력단 스캐너가 있어도 스캐너가 놓치는 조합은 사람이 걸러야 함).
- AI의 첫 답을 정답으로 확신한다 — 맥락이 부족하면 AI는 그럴듯한 오답을 자신 있게 낸다. 두세 번 실패하면 접근 자체를 의심하고 사람에게 escalate.
평가
| 평가 기준 | 초급 | 능숙 | 전문 |
|---|---|---|---|
| 오류·스택 트레이스 판독 | 오류를 '빨간 글씨'로만 인식하고 유형·위치를 못 짚는다 | 오류 유형 이름과 발생 파일·줄을 찾고 '내 줄'을 구분한다 | 트레이스로 근본 원인 지점을 특정하고 유형에 맞는 조치를 즉시 매핑한다 |
| AI에 맥락·안전하게 질문 | '안 돼요'만 보내거나 로그를 비식별 없이 통째로 붙인다 | 오류 원문·코드·재현조건·기대결과 4요소를 갖추고 개인정보를 마스킹한다 | 재사용 가능한 비식별 디버깅 질문 템플릿을 만들고 스캐너 사각지대를 사람이 보완한다 |
| 재현→격리→수정→검증 절차 | 재현 없이 곧장 여러 곳을 동시에 고친다 | 재현을 확정하고 이분 탐색으로 좁혀 한 번에 하나씩 고친 뒤 재현 절차로 검증한다 | 최소 재현 예제를 만들고 절차를 팀 체크리스트로 표준화한다 |
| 회귀·근본 원인 규명 | '지난주엔 됐는데'에서 멈추고 증상만 임시로 가린다 | '무엇이 바뀌었나'를 되짚어 회귀 원인을 찾고 근본 원인을 고친다 | '왜' 5회로 근본 원인에 도달하고 로컬·배포·환경 격차까지 분리 진단한다 |
| AI 틀린 수정·환경 오류 대처 | AI 수정을 검증 없이 연쇄 적용하고 환경 오류를 코드 문제로 오인한다 | 수정을 하나씩 검증하고 실패 시 되돌리며, 코드/의존성/환경/권한을 구분한다 | 실패 접근을 판단해 escalate하고 망분리·권한 제약 환경의 조치를 설계한다 |
- 다음 중 '오류를 AI에 물을 때 가장 나은 방식'은? (가) '안 돼요'라고만 보낸다 (나) 오류 원문·관련 코드·무엇을 하니 이랬는지·기대한 결과를 함께 보낸다 (다) 오류를 스크린샷으로만 찍어 보낸다 (라) 오류를 무시하고 다른 방법을 시켜 본다
- 'ModuleNotFoundError: No module named pandas'가 떴다. 가장 적절한 첫 조치는? (가) 코드의 문법을 고친다 (나) 필요한 라이브러리를 설치한다 (다) 파일 경로를 바꾼다 (라) 권한을 준다 — 그리고 이 오류가 '코드 문제'인지 '환경 문제'인지도 답하라.
- '지난주엔 잘 되던 화면이 오늘 깨졌다.' 가장 먼저 던져야 할 질문 한 문장을 쓰라.
- 오류 로그를 외부 AI에 붙여 넣기 전에 반드시 해야 할 안전 조치와, 이 플랫폼의 입력단 PII 스캐너가 잡아 주는 것 vs 사람이 걸러야 하는 것을 각각 한 가지씩 쓰라.
- 다음 상황을 '재현→격리(이분탐색)→수정→검증' 절차로 어떻게 풀지 각 단계별로 한 줄씩 쓰라: '버튼을 누르면 저장이 안 되는데, 어떤 입력에서만 그렇다.'
- 'AI가 제안한 수정을 적용했더니 다른 기능이 깨졌다.' 이때 취해야 할 행동을 순서대로 3가지 쓰라(되돌리기·한 번에 하나·검증 개념을 포함할 것).
읽을거리 · 도구
- 각 언어·프레임워크의 공식 '오류/디버깅' 문서 — 내가 쓰는 도구(파이썬·자바스크립트/Node·브라우저 등)의 공식 문서에서 '에러 처리·예외·디버깅' 항목을 읽어 두라 (버전이 자주 바뀌므로 특정 URL 암기 대신 '공식 문서 → error/exception/debugging 검색'으로 항상 최신본을 확인하는 습관을 들일 것)
- 브라우저 개발자도구 공식 가이드(Chrome/Firefox DevTools) — Console·Network·Sources(중단점) 탭의 사용법을 손으로 따라 하며 익히라 — 웹 오류 진단의 90%가 여기서 끝난다 ('무엇을 왜' 보는지가 핵심: Console은 오류·로그, Network는 요청 상태 코드(200/404/500), Sources는 값 확인용 중단점)
- git bisect 사용법(git 공식 문서) — '작동하던 게 깨진' 회귀 커밋을 이분 탐색으로 자동 격리하는 도구. 개념과 명령 흐름을 읽어 두라 (명령 세부는 버전마다 다를 수 있으니 'git bisect' 공식 페이지에서 현재 문법을 확인해 사용할 것)
- '좋은 버그 리포트/재현 예제 만들기' 원칙(오픈소스 커뮤니티의 minimal reproducible example 안내) — 최소 재현 예제(minimal repro)를 만드는 사고법 — AI에게 묻든 동료에게 묻든 통하는 보편 원칙 (핵심은 '남이 그대로 재현할 수 있게' 불필요한 것을 걷어내는 것. 특정 사이트가 아니라 이 원칙 자체를 체화할 것)