티스토리 뷰
바이브코딩 디버깅: AI가 만든 three.js 코드가 틀렸을 때 고치는 법 [교량 BIM 바이브코딩 9편]
마구자바 2026. 10. 11. 20:10
1~8편에서 Claude가 만든 코드는 대부분 한 번에 돌아갔지만, 사실 그 뒤에는 수많은 실패가 있었다. 화면이 하얗게 나오거나, 모델이 새까맣거나, 교량이 넘어져 있거나, 교각이 엉뚱한 각도로 누워 있는 경우다. AI가 만든 코드는 그럴듯하지만 틀릴 수 있다.
9편에서는 바이브코딩에서 가장 중요한 기술인 디버깅을 다룬다. 코드를 직접 다 읽지 않아도, 증상을 관찰하고 F12 콘솔을 읽고 AI에게 정확히 설명하면 대부분 한두 번 만에 고쳐진다. 자주 만나는 버그 6가지를 직접 재현해 보면서 그 방법을 정리한다.
이번 편에서 만들 것
▲ 실제로 동작하는 데모. 버튼을 누르면 해당 버그가 재현되고, 콘솔 메시지·원인·AI에게 보낼 말·고친 코드가 나온다.
○ 버튼마다 실제 버그를 넣은 코드로 장면을 다시 만든다. 화면만 보면 원인을 알기 어렵지만, 검은 콘솔 창의 메시지와 아래 크기 진단 그래프를 보면 단서가 보인다.
○ 크기 진단 그래프는 Box3로 잰 모델 크기와 기대 크기를 비교한다. ③ mm 버그는 크기가 1000배, ⑤ Z-up 버그는 높이와 폭이 뒤바뀐 것이 바로 드러난다. (그래프 범위를 넘는 막대는 잘라서 그리고 실제 값은 숫자로 표시)
디버깅 5단계
○ AI에게 "안 돼, 고쳐줘"라고만 하면 AI는 원인을 추측해서 엉뚱한 곳을 고친다. 고치는 일은 AI에게 맡기더라도, 무엇이 잘못됐는지 알아내는 일은 사람이 순서대로 해야 한다.

| 단계 | 할 일 | 구체적으로 |
| ① 재현 | 같은 조작으로 다시 일어나는지 확인 | 새로고침(Ctrl+F5) 후에도 같은지, 특정 값에서만 그런지 |
| ② 관찰 | 화면과 콘솔에서 정상과 다른 점 찾기 | 빈 화면 / 검은색 / 크기 / 방향 / 콘솔 빨간 줄 |
| ③ 범위 좁히기 | 어느 부분 코드 문제인지 좁히기 | 축·격자·경계 상자 헬퍼 켜기, 의심 부분 주석 처리 |
| ④ AI에게 전달 | 증상·오류 전문·기대 결과를 함께 | 추측한 원인과 고칠 범위까지 적기 |
| ⑤ 검증 | 고쳐졌는지, 다른 곳이 망가지지 않았는지 | 처음 재현 조작을 다시 해 보기 |
F12 콘솔 읽는 법
○ 브라우저에서 F12(맥은 Cmd+Option+I)를 누르고 Console 탭을 연다. 바이브코딩 디버깅의 절반은 여기서 끝난다. 빨간 줄은 실행이 멈춘 오류, 노란 줄은 경고다.

| 콘솔 메시지 (예) | 뜻 | 흔한 원인 |
Uncaught SyntaxError: Unexpected token |
문법 오류. 파일 전체가 실행 안 됨 | 괄호·따옴표 짝이 안 맞음, 코드 일부만 복사 |
Failed to resolve module specifier "three" |
import 이름을 못 찾음 | importmap 누락, importmap이 module 스크립트보다 아래에 있음 |
GET ... 404 (Not Found) |
파일 주소가 틀림 | examples/js/ 경로(옛 버전), 버전 번호 오타 |
Cannot read properties of undefined |
없는 객체의 속성을 읽음 | 변수 이름 오타, 만들기 전에 사용 |
THREE.Geometry is not a constructor |
사라진 기능 사용 | AI가 오래된 버전 코드를 줌 |
| 오류 없음 + 화면 이상 | 코드는 돌지만 값이 틀림 | 단위·좌표축·회전·카메라 설정 → 헬퍼로 확인 |
○ 오류 메시지는 요약하지 말고 전문을 그대로 복사해서 AI에게 준다. 메시지 오른쪽의 파일명:줄번호를 클릭하면 문제 줄로 바로 이동한다.
자주 만나는 버그 6가지
○ 데모에서 재현한 버그들이다. 앞의 4개는 콘솔에 오류가 없는 버그라서 더 찾기 어렵다. 코드는 정상으로 돌지만 값이 틀렸기 때문이다.
| 증상 | 원인 | 확인 방법 | 해결 |
| ① 배경색만 보임 | 카메라 far 값이 모델까지 거리보다 작음 | 카메라 거리 vs near/far 출력 | far를 충분히 크게 (1000) |
| ② 모델이 새까맣다 | MeshStandardMaterial인데 조명 없음 | scene.children에 Light 있는지 |
조명 추가, scene.add 확인 |
| ③ 모델이 너무 큼 | 치수를 mm로 입력 | Box3 크기가 1000배 | 단위 m로 통일, 도면 값은 /1000 |
| ④ 부재가 비스듬함 | 회전에 도(degree)를 넣음 | rotation 값이 90, 180 같은 큰 수 | MathUtils.degToRad() |
| ⑤ 교량이 넘어짐 | CAD처럼 Z축이 위인 좌표 | Box3에서 높이가 Z에 있음 | (x, y, z) → (x, z, −y) |
| ⑥ 하얀 화면 + 빨간 오류 | import 경로·importmap 오류 | 콘솔 404 / module specifier | importmap과 addons 경로 확인 |
디버깅 도우미: 헬퍼와 Box3
○ 콘솔에 오류가 없는데 화면이 이상하면, 눈에 보이는 기준을 장면에 추가한다. three.js에는 디버깅용 헬퍼가 기본으로 들어 있다.
const DEBUG = true; // 배포할 때는 false
if (DEBUG) {
scene.add(new THREE.AxesHelper(20)); // 빨강 X · 초록 Y · 파랑 Z (길이 20 m)
scene.add(new THREE.GridHelper(200, 20)); // 200 m 범위, 10 m 간격 격자
const bbox = new THREE.Box3().setFromObject(bridge);
scene.add(new THREE.Box3Helper(bbox, 0xff7a1a)); // 모델 경계 상자 (주황)
const size = bbox.getSize(new THREE.Vector3());
console.log('모델 크기', size.x.toFixed(1), size.y.toFixed(1), size.z.toFixed(1));
window.scene = scene; // F12 콘솔에서 scene 직접 조사
}
| 도우미 | 알 수 있는 것 | 이런 버그에 |
AxesHelper |
원점 위치와 X·Y·Z 방향 | Z-up 좌표, 부호 반대, 엉뚱한 원점 |
GridHelper |
바닥 기준과 대략의 크기 (칸 = 10 m) | 단위 오류, 모델이 땅속에 묻힘 |
Box3 + Box3Helper |
모델 전체 크기와 범위 (숫자) | mm·m 혼동, 일부 부재가 멀리 튐 |
mesh.name + console.table |
부재별 위치를 표로 확인 | 한 부재만 위치가 틀린 경우 |
window.scene |
콘솔에서 객체를 직접 조사 | scene.children.length 등 즉석 확인 |
화면에 오류 띄우기
○ 태블릿이나 휴대폰에서는 F12를 열기 어렵다. 아래 코드를 <head>에 넣어 두면 오류가 화면 아래에 빨간 상자로 표시된다. module 스크립트보다 먼저 실행되는 일반 스크립트라서 import 오류도 잡는다.
<pre id="err" style="position:absolute;bottom:0;left:0;right:0;margin:0;display:none;
background:rgba(120,0,0,.9);color:#fff;font:13px Consolas,monospace;padding:8px"></pre>
<script>
function showErr(msg) {
const box = document.getElementById('err');
box.style.display = 'block';
box.textContent += msg + '\n';
}
addEventListener('error', (e) => showErr('[error] ' + (e.message || e.target.src)), true);
addEventListener('unhandledrejection', (e) => showErr('[promise] ' + e.reason));
</script>
AI에게 디버깅을 요청하는 법
○ 같은 버그라도 요청하는 방법에 따라 한 번에 고쳐지기도 하고, 열 번을 주고받아도 안 고쳐지기도 한다. 4편의 프롬프트 원칙을 디버깅에 그대로 적용한다.

| 요소 | 내용 | 예 (⑤ Z-up 버그) |
| 증상 | 눈에 보이는 현상을 그대로 | 교량이 옆으로 넘어져 바닥판이 벽처럼 서 있다 |
| 기대 | 원래 어떻게 나와야 하는지 | 바닥판이 수평이고 Y축이 위 |
| 근거 | 콘솔 오류 전문, 출력한 값 | Box3 크기 93.2 × 13.0 × 9.8 |
| 추측 | 짐작 가는 원인 (틀려도 됨) | Z-up 좌표로 넣은 것 같다 |
| 범위 | 고칠 곳과 건드리지 말 곳 | 좌표 변환만 고치고 나머지는 그대로 |
○ 특히 "범위"가 중요하다. 범위를 정하지 않으면 AI가 버그와 상관없는 부분까지 다시 짜서, 잘 되던 기능이 망가지는 일이 생긴다.
AI가 같은 실수를 반복할 때
| 상황 | 해결 |
| 같은 수정을 계속 되풀이 | 새 대화를 열고 현재 전체 코드와 증상을 처음부터 다시 준다 |
| 코드가 길어 일부만 고쳐 옴 | "수정한 함수 전체를 다시 보여줘" 또는 "전체 파일로 줘" |
| 옛 버전 문법을 계속 씀 | "three.js 0.186.1 기준, examples/js가 아닌 addons(jsm) 경로로"를 명시 |
| 원인을 못 찾음 | 버그가 있는 부분만 떼어 낸 최소 예제로 다시 질문 |
| 고칠수록 더 망가짐 | 마지막으로 잘 되던 버전으로 돌아가 한 번에 하나씩 변경 |
○ three.js는 버전이 자주 바뀐다. 예를 들어 THREE.Geometry는 r125에서 없어졌고, examples/js 폴더는 r148에서 없어졌다. AI가 이런 코드를 주면 버전을 알려 주고 다시 요청한다.
전체 코드
○ 디버깅 도우미가 모두 들어간 템플릿이다. 메모장에 붙여넣고 bridge9.html로 저장해 실행하면 축·격자·경계 상자와 함께 모델 크기·조명 수·카메라 거리가 왼쪽 위에 표시되고, 오류가 생기면 화면 아래에 빨간 상자로 나온다. 새 모델을 만들 때 이 파일에서 시작하면 디버깅이 쉬워진다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>three.js 디버깅 도우미 - 교량 모델</title>
<style>
body { margin: 0; overflow: hidden; }
#err { position: absolute; left: 0; right: 0; bottom: 0; max-height: 40%; overflow: auto; margin: 0;
background: rgba(120, 0, 0, 0.9); color: #fff; font: 13px Consolas, monospace; padding: 8px 12px; display: none; }
#dbg { position: absolute; top: 10px; left: 10px; background: rgba(255,255,255,0.9); padding: 8px 12px;
border-radius: 6px; font: 13px Consolas, monospace; line-height: 1.6; }
</style>
<!-- ① 오류를 화면에 표시: module보다 먼저 실행되는 일반 script -->
<script>
function showErr(msg) {
const box = document.getElementById('err');
box.style.display = 'block';
box.textContent += msg + '\n';
}
addEventListener('error', (e) => showErr('[error] ' + (e.message || e.target.src || e.target.href)), true);
addEventListener('unhandledrejection', (e) => showErr('[promise] ' + e.reason));
</script>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.186.1/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.186.1/examples/jsm/"
}
}
</script>
</head>
<body>
<pre id="err"></pre>
<div id="dbg">debug</div>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const DEBUG = true; // 배포할 때는 false
// 1. 장면 기본 설정
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xdfe8f0);
const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 1000);
camera.position.set(60, 38, 75);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
scene.add(new THREE.HemisphereLight(0xffffff, 0x6b7f63, 0.9));
const sun = new THREE.DirectionalLight(0xffffff, 2.0);
sun.position.set(40, 70, 45);
scene.add(sun);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 4, 0);
controls.update();
// 2. 교량 (3경간 × 30 m, 폭 12 m) - 단위 m, Y축 위
const P = { nSpan: 3, L: 30, B: 12, n: 4, H: 8 };
const h = P.L / 20, Lt = P.nSpan * P.L, x0 = -Lt / 2;
const bridge = new THREE.Group();
scene.add(bridge);
const mat = (c) => new THREE.MeshStandardMaterial({ color: c });
function box(name, w, hh, d, color, x, y, z) {
const m = new THREE.Mesh(new THREE.BoxGeometry(w, hh, d), mat(color));
m.name = name; // 이름을 붙여 두면 디버깅이 쉽다
m.position.set(x, y, z);
bridge.add(m);
}
box('DECK', Lt, 0.25, P.B, 0xb8bcc2, 0, P.H + h + 0.125, 0);
for (let i = 0; i < P.n; i++) box(`G${i + 1}`, Lt - 0.2, h, 0.4, 0x3f6e9e, 0, P.H + h / 2, -(P.B / 2 - 1) + i * (P.B - 2) / (P.n - 1));
for (let k = 1; k < P.nSpan; k++) box(`P${k}`, 1.8, P.H, 1.8, 0xa9adb3, x0 + k * P.L, P.H / 2, 0);
for (const [id, s] of [['A1', -1], ['A2', 1]]) box(id, 2, P.H, P.B + 1, 0x9da2a8, s * (Lt / 2 + 0.6), P.H / 2, 0);
// 3. 디버깅 도우미
if (DEBUG) {
scene.add(new THREE.AxesHelper(20)); // 빨강 X · 초록 Y · 파랑 Z
scene.add(new THREE.GridHelper(200, 20, 0x666666, 0xaaaaaa)); // 10 m 간격 바닥 격자
const bbox = new THREE.Box3().setFromObject(bridge);
scene.add(new THREE.Box3Helper(bbox, 0xff7a1a)); // 모델 경계 상자
const size = bbox.getSize(new THREE.Vector3());
const lights = scene.children.filter((o) => o.isLight).length;
const dist = camera.position.distanceTo(bbox.getCenter(new THREE.Vector3()));
document.getElementById('dbg').innerHTML =
`모델 크기: ${size.x.toFixed(1)} × ${size.y.toFixed(1)} × ${size.z.toFixed(1)} m<br>` +
`조명 수: ${lights}<br>카메라 거리: ${dist.toFixed(1)} m (near ${camera.near}, far ${camera.far})`;
console.table(bridge.children.map((m) => ({
name: m.name, x: m.position.x.toFixed(2), y: m.position.y.toFixed(2), z: m.position.z.toFixed(2),
})));
window.scene = scene; // F12 콘솔에서 scene 직접 확인 가능
}
renderer.setAnimationLoop(() => { controls.update(); renderer.render(scene, camera); });
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>
Claude에게 이렇게 요청했다
three.js 교량 모델에서 버그가 있어. 아래 정보를 보고 원인을 찾아 고쳐줘.
[증상] 교량이 옆으로 넘어져 바닥판이 벽처럼 서 있어. 콘솔 오류는 없어.
[기대] 바닥판이 수평이고 Y축이 위여야 해. (three.js 0.186.1, 단위 m)
[근거] Box3로 잰 모델 크기가 93.2 × 13.0 × 9.8 m야. 높이가 Z 방향에 있어.
[추측] 좌표를 CAD처럼 Z축이 위인 기준으로 넣은 것 같아.
[범위] 좌표 변환 부분만 고치고, 재질·조명·카메라 코드는 바꾸지 마. 수정한 함수 전체를 보여줘.
○ 이 다섯 줄 틀(증상·기대·근거·추측·범위)을 메모장에 저장해 두고 빈칸만 채워서 쓰면 된다.
정리
○ 디버깅은 재현 → 관찰 → 범위 좁히기 → AI에게 전달 → 검증 순서로 한다.
○ F12 콘솔의 빨간 줄은 전문 그대로 AI에게 준다. 오류가 없으면 값(크기·각도·좌표)을 의심한다.
○ AxesHelper · GridHelper · Box3를 켜면 단위·좌표축·크기 버그가 눈에 보인다.
○ AI에게는 증상·기대·근거·추측·범위를 함께 보낸다.
다음 편 예고
10편은 연재의 마지막 편이다. 지금까지 만든 교량 모델을 glTF(.glb) 파일로 내보내서 Blender나 윈도우 3D 뷰어, 웹 뷰어에서 열고, 7편에서 넣은 부재 속성(userData)이 파일에 함께 저장되는지 확인한다.
관련 글
→ [교량 BIM 바이브코딩 1편] three.js 입문: Claude 바이브코딩으로 3D 교량 만들기
→ [교량 BIM 바이브코딩 2편] three.js 기초: 장면·카메라·렌더러
→ [교량 BIM 바이브코딩 3편] OrbitControls·조명·그림자
→ [교량 BIM 바이브코딩 4편] AI 코딩 프롬프트 작성법: Claude에게 3D 모델 정확히 요청하기
→ [교량 BIM 바이브코딩 5편] three.js ExtrudeGeometry: I형 거더 단면 3D 모델링
→ [교량 BIM 바이브코딩 6편] three.js 파라메트릭 모델링: 슬라이더로 바뀌는 교량
→ [교량 BIM 바이브코딩 7편] three.js 교량 BIM 모델: 클릭하면 속성이 나오는 부재 조립
→ [교량 BIM 바이브코딩 8편] three.js 곡선교 모델링: 곡선 반경 R과 편경사로 휘는 교량
→ [교량 BIM 바이브코딩 10편] three.js glTF 내보내기: 교량 BIM 모델을 GLB 파일로 저장하고 속성까지 공유하기
'엔지니어링 코딩 > three.js·AI 코딩' 카테고리의 다른 글
| three.js glTF 내보내기: 교량 BIM 모델을 GLB 파일로 저장하고 속성까지 공유하기 [교량 BIM 바이브코딩 10편] (0) | 2026.10.11 |
|---|---|
| three.js 곡선교 모델링: 곡선 반경 R과 편경사로 휘는 교량 [교량 BIM 바이브코딩 8편] (0) | 2026.10.09 |
| three.js 교량 BIM 모델: 거더·바닥판·교각 조립하고 클릭해서 속성 보기 [교량 BIM 바이브코딩 7편] (0) | 2026.10.09 |
| three.js 파라메트릭 모델링: 슬라이더로 바뀌는 교량 [교량 BIM 바이브코딩 6편] (0) | 2026.10.09 |
| AI 코딩 프롬프트 작성법: Claude에게 3D 모델 정확히 요청하기 [교량 BIM 바이브코딩 4편] (0) | 2026.10.07 |
