제3강: AI Chat 마스터하기

코드와 대화하며 문제를 해결하는 새로운 방법

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

학습 목표

  • Cursor AI Chat의 핵심 기능 이해하기
  • 효과적인 질문 방법 익히기
  • 코드 설명, 디버깅, 리팩토링에 활용하기
  • 컨텍스트를 활용한 대화 기법 마스터하기
  • Chat과 코드 에디터의 시너지 극대화하기

AI Chat이란?

Cursor의 AI Chat은 단순한 Q&A 도구가 아닙니다. 전체 프로젝트를 이해하고, 코드를 분석하며, 즉시 적용 가능한 솔루션을 제공하는 지능형 코딩 파트너입니다.

Chat의 주요 특징

프로젝트 인식

열려있는 파일, 프로젝트 구조, 의존성을 모두 이해합니다

즉시 적용

제안된 코드를 클릭 한 번으로 에디터에 적용할 수 있습니다

대화 기록

이전 대화를 기억하여 연속적인 작업이 가능합니다

코드 참조

특정 코드를 선택하여 질문하거나 수정을 요청할 수 있습니다

Chat 시작하기

Chat 열기

Cmd/Ctrl + L

가장 빠른 방법: 언제든지 Chat을 열 수 있습니다

사이드바 클릭

오른쪽 사이드바의 Chat 아이콘을 클릭합니다

첫 번째 대화

Cursor AI Chat

현재 프로젝트의 구조를 설명해줘

현재 프로젝트는 React 기반의 Todo 애플리케이션입니다:

프로젝트 구조:
├── src/
│   ├── components/
│   │   ├── TodoList.js
│   │   └── TodoItem.js
│   ├── App.js
│   └── index.js
├── package.json
└── README.md

주요 특징:
- React 18.2.0 사용
- 함수형 컴포넌트와 Hooks 활용
- 로컬 상태 관리 (useState)

Chat 활용 시나리오

1. 코드 설명 요청하기

복잡한 코드를 이해하고 싶을 때:

// 복잡한 reduce 함수
const result = data
  .filter(item => item.active)
  .reduce((acc, item) => {
    const key = item.category;
    if (!acc[key]) {
      acc[key] = { items: [], total: 0 };
    }
    acc[key].items.push(item);
    acc[key].total += item.value;
    return acc;
  }, {});

이 코드가 뭘 하는지 설명해줘

이 코드는 데이터를 카테고리별로 그룹화하고 집계하는 작업을 수행합니다:

  1. 필터링: active가 true인 항목만 선택
  2. 그룹화: category를 키로 사용하여 객체로 변환
  3. 집계: 각 카테고리별로 items 배열과 total 합계 계산

예시 결과:

{
  "electronics": { items: [...], total: 1500 },
  "clothing": { items: [...], total: 800 }
}

2. 버그 수정 요청

에러가 발생한 코드를 수정하고 싶을 때:

// TypeError: Cannot read property 'map' of undefined
function renderList(items) {
  return items.map(item => <li>{item.name}</li>);
}

이 함수에서 "Cannot read property 'map' of undefined" 에러가 발생해. 어떻게 수정하면 될까?

items가 undefined일 때 발생하는 에러입니다. 다음과 같이 수정하세요:

function renderList(items) {
  // 방법 1: 기본값 설정
  return (items || []).map(item => <li>{item.name}</li>);
}

// 방법 2: 조건부 렌더링
function renderList(items) {
  if (!items || !Array.isArray(items)) {
    return <li>No items available</li>;
  }
  return items.map(item => <li>{item.name}</li>);
}

// 방법 3: 옵셔널 체이닝 (최신 문법)
function renderList(items) {
  return items?.map(item => <li>{item.name}</li>) || [];
}

3. 코드 리팩토링

기존 코드를 개선하고 싶을 때:

원본 코드

function calculatePrice(price, discount, tax) {
  let discountedPrice = price;
  if (discount > 0) {
    discountedPrice = price - (price * discount / 100);
  }
  let finalPrice = discountedPrice;
  if (tax > 0) {
    finalPrice = discountedPrice + (discountedPrice * tax / 100);
  }
  return Math.round(finalPrice * 100) / 100;
}

이 함수를 더 깔끔하고 읽기 쉽게 리팩토링해줘

함수를 더 간결하고 읽기 쉽게 리팩토링했습니다:

function calculatePrice(price, discount = 0, tax = 0) {
  const discountMultiplier = 1 - (discount / 100);
  const taxMultiplier = 1 + (tax / 100);
  
  const finalPrice = price * discountMultiplier * taxMultiplier;
  
  return Math.round(finalPrice * 100) / 100;
}

// 더 나아가서, 유틸리티 함수로 분리
const toPercentageMultiplier = (percentage, isDiscount = false) => 
  isDiscount ? 1 - (percentage / 100) : 1 + (percentage / 100);

const roundToTwoDecimals = (num) => 
  Math.round(num * 100) / 100;

function calculatePrice(price, discount = 0, tax = 0) {
  const afterDiscount = price * toPercentageMultiplier(discount, true);
  const afterTax = afterDiscount * toPercentageMultiplier(tax);
  
  return roundToTwoDecimals(afterTax);
}

4. 새 기능 구현

새로운 기능을 추가하고 싶을 때:

현재 TodoList 컴포넌트에 검색 기능을 추가하고 싶어. 실시간으로 필터링되도록 구현해줘.

TodoList에 실시간 검색 기능을 추가하겠습니다:

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

function TodoList({ todos }) {
  const [searchTerm, setSearchTerm] = useState('');
  
  // useMemo로 검색 결과 최적화
  const filteredTodos = useMemo(() => {
    if (!searchTerm.trim()) {
      return todos;
    }
    
    return todos.filter(todo =>
      todo.text.toLowerCase().includes(searchTerm.toLowerCase())
    );
  }, [todos, searchTerm]);
  
  return (
    <div className="todo-list">
      <div className="search-box">
        <input
          type="text"
          placeholder="Search todos..."
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
          className="search-input"
        />
        {searchTerm && (
          <button 
            onClick={() => setSearchTerm('')}
            className="clear-button"
          >
            Clear
          </button>
        )}
      </div>
      
      <div className="results-info">
        {searchTerm && (
          <p>Found {filteredTodos.length} of {todos.length} todos</p>
        )}
      </div>
      
      <ul className="todo-items">
        {filteredTodos.map(todo => (
          <TodoItem key={todo.id} todo={todo} />
        ))}
      </ul>
      
      {searchTerm && filteredTodos.length === 0 && (
        <p className="no-results">No todos found matching "{searchTerm}"</p>
      )}
    </div>
  );
}

추가로 CSS도 제공합니다:

.search-box {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

.search-input {
  flex: 1;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 16px;
}

.clear-button {
  padding: 10px 20px;
  background: #ff4444;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.results-info {
  color: #666;
  margin-bottom: 10px;
}

.no-results {
  text-align: center;
  color: #999;
  padding: 20px;
}

고급 Chat 기능

1. 코드 선택과 함께 질문하기

특정 코드를 선택한 후 Chat을 열면 해당 코드에 대해 질문할 수 있습니다:

const memoizedValue = useMemo(() => {
  return expensiveCalculation(a, b);
}, [a, b]);

↑ 이 코드를 선택하고 Cmd/Ctrl + L

선택한 코드에서 useMemo를 사용하는 이유가 뭐야?

2. 파일 참조하기

@파일명으로 특정 파일을 참조할 수 있습니다:

@TodoList.js 파일의 성능을 개선할 방법을 제안해줘

3. 멀티턴 대화

이전 대화를 기반으로 연속적인 작업이 가능합니다:

User 타입을 TypeScript로 정의해줘

interface User {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
}

여기에 role과 permissions도 추가해줘

interface User {
  id: string;
  name: string;
  email: string;
  role: UserRole;
  permissions: Permission[];
  createdAt: Date;
}

enum UserRole {
  ADMIN = 'admin',
  USER = 'user',
  GUEST = 'guest'
}

interface Permission {
  resource: string;
  actions: string[];
}

효과적인 프롬프트 작성법

좋은 프롬프트 vs 나쁜 프롬프트

❌ 피해야 할 프롬프트

  • "코드 고쳐줘" - 너무 모호함
  • "에러 났어" - 정보 부족
  • "더 좋게 만들어줘" - 구체성 없음

✅ 권장하는 프롬프트

  • "이 함수의 시간 복잡도를 O(n)으로 개선해줘"
  • "useState 대신 useReducer를 사용하도록 리팩토링해줘"
  • "이 API 호출에 에러 처리와 로딩 상태를 추가해줘"

프롬프트 템플릿

🐛 디버깅 요청

"[에러 메시지]가 발생하는데, [상황 설명]. 어떻게 해결하면 될까?"

🔧 리팩토링 요청

"이 코드를 [목적/원칙]에 맞게 리팩토링해줘. 특히 [관심사항]을 개선하고 싶어."

📚 설명 요청

"[코드/개념]이 어떻게 작동하는지 설명해줘. 특히 [궁금한 부분]이 이해가 안 돼."

✨ 기능 추가

"현재 코드에 [기능]을 추가하고 싶어. [제약사항]을 고려해서 구현해줘."

실습: Chat으로 미니 프로젝트 완성하기

Weather App 만들기

AI Chat만을 사용해서 날씨 앱을 만들어봅시다.

Step 1: 기본 구조 생성

"React로 날씨 앱의 기본 구조를 만들어줘. OpenWeatherMap API를 사용할 예정이야."

Step 2: API 연동

"OpenWeatherMap API를 호출하는 함수를 만들어줘. API 키는 환경변수로 관리하고 싶어."

Step 3: UI 개선

"날씨 정보를 보여주는 카드 컴포넌트를 만들어줘. 온도, 습도, 날씨 아이콘을 표시하고 싶어."

Step 4: 에러 처리

"API 호출 실패, 도시를 찾을 수 없음, 네트워크 에러 등을 처리하는 코드를 추가해줘."

Step 5: 기능 추가

"최근 검색한 도시 5개를 localStorage에 저장하고 빠르게 선택할 수 있는 기능을 추가해줘."

🏆 보너스 도전

  • 5일 날씨 예보 추가
  • 현재 위치 기반 날씨 표시
  • 다크 모드 지원
  • 날씨에 따른 배경 이미지 변경

Chat 활용 팁

💡 컨텍스트 활용

현재 열려있는 파일들이 자동으로 컨텍스트에 포함됩니다. 관련 파일을 미리 열어두세요.

💡 단계별 접근

복잡한 작업은 여러 단계로 나누어 질문하세요. AI가 더 정확한 답변을 제공합니다.

💡 코드 적용

AI가 제안한 코드 블록의 "Apply" 버튼을 클릭하면 현재 커서 위치에 즉시 삽입됩니다.

💡 히스토리 활용

이전 대화 내용을 참고하여 연속적인 작업을 수행할 수 있습니다.

핵심 정리

대화형 코딩

AI Chat은 코드를 이해하고, 설명하고, 개선하는 강력한 대화형 도구입니다.

컨텍스트가 핵심

프로젝트 전체를 이해하고 있어 더 정확하고 적절한 솔루션을 제공합니다.

즉시 적용 가능

제안된 코드를 바로 에디터에 적용할 수 있어 작업 흐름이 끊기지 않습니다.

학습 도구

코드를 설명받고 이해하는 과정에서 자연스럽게 학습이 이루어집니다.

다음 강의 예고

다음 강의에서는 Cmd+K 기능을 통해 선택한 코드를 즉시 편집하고 변환하는 방법을 배웁니다.

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