제9강: 디버깅과 에러 해결

AI와 함께하는 스마트한 문제 해결 전략

난이도: 초급 예상 시간: 40분 카테고리: 기초

학습 목표

  • Cursor의 AI 기반 디버깅 도구 활용하기
  • 에러 메시지 분석과 해결 방법 익히기
  • 브레이크포인트와 변수 검사 마스터하기
  • 성능 문제 진단과 최적화 방법 이해하기
  • 효율적인 문제 해결 워크플로우 구축하기

AI 디버깅의 새로운 패러다임

Cursor의 AI 디버깅은 단순히 에러를 찾는 것을 넘어, 문제의 근본 원인을 파악하고 최적의 해결책을 제시합니다. 코드의 맥락을 이해하고 비슷한 패턴의 버그를 예방하는 방법까지 안내합니다.

AI 디버깅의 핵심 기능

자동 에러 감지

코드 작성 중 실시간으로 잠재적 문제 발견

스마트 해결책

에러의 원인과 여러 해결 방법 제시

성능 분석

코드 실행 시간과 메모리 사용량 최적화

예방적 디버깅

유사한 버그 패턴 학습 및 예방

에러 타입별 해결 전략

1. 구문 에러 (Syntax Errors)

function calculateTotal(items) {
    let total = 0;
    items.forEach(item => {
        total += item.price * item.quantity  // 세미콜론 누락
    )}  // 닫는 괄호 오류
    return total;
}
SyntaxError: Unexpected token '}'

AI 해결책

문제점 발견:

  • forEach 문의 닫는 괄호가 잘못됨
  • 세미콜론 누락 (선택사항)

수정된 코드:

function calculateTotal(items) {
    let total = 0;
    items.forEach(item => {
        total += item.price * item.quantity;
    });  // 수정됨
    return total;
}

2. 런타임 에러 (Runtime Errors)

function getUserData(userId) {
    const user = users.find(u => u.id === userId);
    return user.name.toUpperCase();  // user가 undefined일 수 있음
}
TypeError: Cannot read property 'name' of undefined

AI 해결책

옵션 1: 옵셔널 체이닝
function getUserData(userId) {
    const user = users.find(u => u.id === userId);
    return user?.name?.toUpperCase() || 'Unknown User';
}
옵션 2: 명시적 검사
function getUserData(userId) {
    const user = users.find(u => u.id === userId);
    if (!user) {
        throw new Error(`User with ID ${userId} not found`);
    }
    return user.name.toUpperCase();
}

3. 논리적 에러 (Logical Errors)

function calculateDiscount(price, discountPercent) {
    // 잘못된 할인 계산
    return price - discountPercent;  // 퍼센트를 직접 빼고 있음
}

테스트 결과:

calculateDiscount(100, 20) // 80 (예상: 80) ❌ calculateDiscount(50, 10) // 40 (예상: 45) ❌

AI 분석

할인 계산 로직이 잘못되었습니다. 퍼센트를 금액으로 변환해야 합니다.

function calculateDiscount(price, discountPercent) {
    const discountAmount = price * (discountPercent / 100);
    return price - discountAmount;
    // 또는 한 줄로: return price * (1 - discountPercent / 100);
}

Cursor 디버깅 도구 활용

1. 인라인 에러 표시

코드 작성 중 실시간으로 에러를 감지하고 표시:

1 2 3 4
const fetchData = async () => {
    const response = await fetch('/api/data');
    const data = response.json();  // await 누락
    return data;
}

⚠️ Promise를 반환합니다. await를 추가하세요.

2. 브레이크포인트와 디버거

디버깅 워크플로우

🔴
브레이크포인트 설정

의심되는 라인 옆을 클릭하여 브레이크포인트 추가

▶️
디버그 모드 실행

F5 또는 Run → Start Debugging

🔍
변수 검사

현재 스코프의 모든 변수 값 확인

⏭️
단계별 실행

F10(다음 줄), F11(함수 내부로)

3. AI 콘솔 디버깅

console.log 대신 AI가 스마트한 로깅을 제안:

기존 방식
console.log('user:', user);
console.log('data:', data);
console.log('result:', result);
AI 제안
console.table({ user, data, result });

// 또는 조건부 로깅
if (process.env.NODE_ENV === 'development') {
    console.group('API Response Debug');
    console.log('User ID:', user.id);
    console.log('Data received:', data.length, 'items');
    console.time('Processing');
    // ... 처리 로직
    console.timeEnd('Processing');
    console.groupEnd();
}

성능 문제 디버깅

성능 문제 감지와 해결

시나리오: 느린 리스트 렌더링

문제가 있는 코드
function SearchResults({ items, searchTerm }) {
    // 매 렌더링마다 필터링 수행
    const filteredItems = items.filter(item => 
        item.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
        item.description.toLowerCase().includes(searchTerm.toLowerCase())
    );

    // 매 렌더링마다 정렬 수행
    const sortedItems = filteredItems.sort((a, b) => 
        b.relevance - a.relevance
    );

    return (
        
    {sortedItems.map(item => (
  • {item.title}
  • ))}
); }
AI 최적화 제안
import { useMemo } from 'react';

function SearchResults({ items, searchTerm }) {
    // 검색어가 변경될 때만 필터링 재계산
    const filteredItems = useMemo(() => {
        const term = searchTerm.toLowerCase();
        return items.filter(item => 
            item.title.toLowerCase().includes(term) ||
            item.description.toLowerCase().includes(term)
        );
    }, [items, searchTerm]);

    // 필터링된 결과가 변경될 때만 정렬 재계산
    const sortedItems = useMemo(() => 
        [...filteredItems].sort((a, b) => b.relevance - a.relevance),
        [filteredItems]
    );

    return (
        
    {sortedItems.map(item => (
  • {item.title}
  • ))}
); }

성능 개선:

  • 불필요한 재계산 방지
  • 메모이제이션으로 렌더링 최적화
  • 원본 배열 변경 방지 (spread 연산자)

비동기 코드 디버깅

Promise와 Async/Await 문제 해결

흔한 비동기 에러들

1. Unhandled Promise Rejection
async function fetchUserData(userId) {
    const response = await fetch(`/api/users/${userId}`);
    const data = await response.json();  // 에러 처리 없음
    return data;
}

AI 수정 제안:

async function fetchUserData(userId) {
    try {
        const response = await fetch(`/api/users/${userId}`);
        
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Failed to fetch user data:', error);
        
        // 에러를 상위로 전파하거나 기본값 반환
        throw new Error(`Unable to fetch user ${userId}: ${error.message}`);
    }
}
2. Race Condition
let latestQuery = '';

async function search(query) {
    latestQuery = query;
    const results = await api.search(query);
    
    // 문제: 이전 요청이 늦게 도착할 수 있음
    setSearchResults(results);
}

AI 수정 제안:

let abortController = null;

async function search(query) {
    // 이전 요청 취소
    if (abortController) {
        abortController.abort();
    }
    
    abortController = new AbortController();
    
    try {
        const results = await api.search(query, {
            signal: abortController.signal
        });
        
        setSearchResults(results);
    } catch (error) {
        if (error.name !== 'AbortError') {
            console.error('Search failed:', error);
        }
    }
}

실습: 버그가 있는 Todo 앱 디버깅

실제 버그들을 찾아 수정해보기

다음 Todo 앱 코드에는 여러 버그가 숨어있습니다. AI와 함께 찾아서 수정해보세요.

버그가 있는 코드

import React, { useState, useEffect } from 'react';

function TodoApp() {
    const [todos, setTodos] = useState([]);
    const [inputValue, setInputValue] = useState('');
    const [filter, setFilter] = useState('all');

    // 버그 1: localStorage 에러 처리 없음
    useEffect(() => {
        const saved = localStorage.getItem('todos');
        setTodos(JSON.parse(saved));
    }, []);

    // 버그 2: 무한 루프
    useEffect(() => {
        localStorage.setItem('todos', JSON.stringify(todos));
    });

    const addTodo = () => {
        // 버그 3: 빈 문자열 체크 없음
        const newTodo = {
            id: Date.now(),
            text: inputValue,
            completed: false
        };
        // 버그 4: 직접 배열 수정
        todos.push(newTodo);
        setTodos(todos);
        setInputValue('');
    };

    const toggleTodo = (id) => {
        // 버그 5: 잘못된 비교 연산자
        const todo = todos.find(t => t.id = id);
        todo.completed = !todo.completed;
        setTodos(todos);
    };

    const deleteTodo = (id) => {
        // 버그 6: filter 결과를 저장하지 않음
        todos.filter(todo => todo.id !== id);
    };

    // 버그 7: 필터링 로직 오류
    const filteredTodos = todos.filter(todo => {
        if (filter === 'completed') return todo.completed;
        if (filter === 'active') return todo.completed;
        return true;
    });

    return (
        

Todo List

setInputValue(e.target.value)} // 버그 8: Enter 키 처리 없음 />
    {filteredTodos.map(todo => ( // 버그 9: key prop 없음
  • toggleTodo(todo.id)} /> {todo.text}
  • ))}
); }

디버깅 단계

  1. 코드를 Cursor에 복사하여 에러 표시 확인
  2. 각 버그에 대해 AI Chat에 질문
  3. Cmd+K로 각 버그를 수정
  4. 브라우저 개발자 도구로 런타임 에러 확인
  5. 모든 기능이 정상 작동하는지 테스트

찾아야 할 버그들

  • localStorage 파싱 에러
  • useEffect 무한 루프
  • 상태 불변성 위반
  • 잘못된 연산자 사용
  • 필터링 로직 오류
  • 미반영되는 상태 업데이트
  • missing key prop 경고

디버깅 베스트 프랙티스

예방적 디버깅

  • TypeScript 사용으로 타입 에러 사전 방지
  • ESLint 규칙으로 일반적인 실수 감지
  • 단위 테스트로 로직 검증
  • 코드 리뷰 전 AI로 사전 검토

효율적인 디버깅

  • 에러 메시지를 정확히 읽고 이해하기
  • 문제를 재현 가능한 최소 코드로 격리
  • 이진 탐색으로 문제 범위 좁히기
  • AI에게 구체적인 컨텍스트 제공

디버깅 후 조치

  • 동일한 버그 재발 방지책 마련
  • 테스트 케이스 추가
  • 문서화 및 주석 업데이트
  • 팀과 해결 방법 공유

핵심 정리

AI가 문제를 먼저 발견

코드 작성 중 실시간으로 잠재적 문제를 감지하고 경고합니다.

맥락 기반 해결책

프로젝트 전체 컨텍스트를 고려한 최적의 해결 방법을 제시합니다.

학습하는 디버깅

비슷한 패턴의 버그를 인식하고 예방하는 방법을 학습합니다.

통합된 도구

브레이크포인트, 콘솔, 성능 분석이 AI와 완벽하게 통합되어 있습니다.

다음 강의 예고

다음 강의에서는 기초편의 마지막으로 단축키와 설정을 최적화하여 생산성을 극대화하는 방법을 배웁니다.

다음 강의로 이동
9/30 완료