나는 500개의 AI 코딩 실수를 분석하고 이를 잡아내는 ESLint 플러그인을 만들었다

발행: (2026년 4월 4일 오전 10:49 GMT+9)
9 분 소요
원문: Dev.to

Source: Dev.to

번역할 텍스트를 제공해 주시면 한국어로 번역해 드리겠습니다.

아마도 본 적이 있을 패턴

const results = items.map(async (item) => {
  return await fetchItem(item);
});

괜찮아 보이죠, 그렇죠? 당신의 AI 어시스턴트가 작성했습니다. 테스트가 통과합니다. 코드 리뷰에서 승인받았습니다.

그런데 프로덕션에 배포되면 resultsPromise 배열이 됩니다 — 기대한 값이 아니라. 2번째 줄의 await는 아무 효과가 없습니다. Promise.all(items.map(...)) 혹은 for…of 루프가 필요했습니다.

이것은 TypeScript 버그가 아닙니다. 일반적인 LLM 코딩 실수이며 — 제가 AI‑생성 코드 품질을 연구하기 시작했을 때 발견한 수백 가지 중 하나입니다.

Source:

문제: AI가 작동하는 코드를 작성한다, 올바른 코드를 작성하지 않는다

LLM은 테스트를 통과하는 코드를 작성하는 데는 뛰어나지만, 모서리 사례를 처리하고 일관성을 유지하며 내부적으로 모범 사례를 따르는 코드를 작성하는 데는 형편없습니다.

여러 실증 연구(예: 333개의 버그 분석, PromptHub의 558개 잘못된 스니펫 연구)를 검토한 결과, 다음과 같은 뚜렷한 패턴이 나타났습니다:

버그 유형빈도
누락된 코너 케이스15.3 %
오해20.8 %
환각된 객체/API9.6 %
잘못된 조건높음
누락된 코드 블록40 %+

가장 답답한 점은? 이러한 버그들의 대부분이 lint 단계에서 예방될 수 있다는 것입니다.

Source:

솔루션: AI‑생성 코드 전용 ESLint 규칙

저는 eslint-plugin-llm-core 를 만들었습니다 — AI 코딩 어시스턴트가 가장 자주 저지르는 실수를 잡아내기 위해 설계된 20개의 규칙을 포함한 ESLint 플러그인입니다.

이 규칙들은 단순히 일반적인 베스트 프랙티스 규칙이 아니라, AI‑생성 코드베이스에서 반복적으로 보던 패턴을 목표로 합니다:

  • Async/await 오용
  • 일관성 없는 오류 처리
  • 누락된 null 검사
  • 이름이 없는 상수 대신 매직 넘버 사용
  • 조기 반환 대신 깊은 중첩
  • 오류를 삼키는 빈 catch 블록
  • 의도를 흐리는 일반적인 변수명

예시: Async 배열 콜백 함정

// ❌ AI가 자주 작성함
const userIds = users.map(async (user) => {
  return await db.getUser(user.id);
});
// userIds는 Promise[] — User[]가 아님

// ✅ 실제로 필요한 코드
const userIds = await Promise.all(
  users.map((user) => db.getUser(user.id))
);

플러그인은 no-async-array-callbacks 규칙으로 이를 잡아냅니다:

57:27  error  Avoid passing async functions to array methods  llm-core/no-async-array-callbacks

  This pattern returns an array of Promises, not the resolved values.
  Consider using Promise.all() or a for...of loop instead.

오류 메시지를 보셨나요? 이 메시지는 불평이 아니라 교육을 목표로 합니다. 개발자와 AI 어시스턴트가 잘못된 것인지 이해하도록 돕는 것이 목적입니다.

예시: 빈 catch 안티패턴

// ❌ AI가 자주 생성함
try {
  await processData(data);
} catch (e) {
  // TODO: handle error
}

no-empty-catch 규칙이 이를 잡아냅니다:

63:11  error  Empty catch block silently swallows errors  llm-core/no-empty-catch

  Unhandled errors make debugging difficult and can hide critical failures.
  Either handle the error, rethrow it, or log it with context.

예시: 조기 반환 대신 깊은 중첩

// ❌ AI는 중첩을 좋아함
function processData(data: Data | null) {
  if (data) {
    if (data.items) {
      if (data.items.length > 0) {
        return data.items.map(processItem);
      }
    }
  }
  return [];
}

// ✅ 조기 반환이 더 깔끔함
function processData(data: Data | null) {
  if (!data?.items?.length) return [];
  return data.items.map(processItem);
}

prefer-early-return 규칙은 더 평탄한 패턴을 권장합니다.

규칙 뒤의 연구

RuleBug Pattern Addressed
no-async-array-callbacksPromise.all 누락, 잘못된 async 흐름
no-empty-catch오류를 조용히 무시
no-magic-numbers유지보수가 어려운 상수
prefer-early-return깊은 중첩, 불명확한 제어 흐름
prefer-unknown-in-catchany 타입의 catch 매개변수
throw-error-objectsError 인스턴스 대신 문자열을 throw
structured-logging일관성 없는 로그 형식
consistent-exports혼합된 default/named 내보내기
explicit-export-types공개 함수에 반환 타입 누락
no-commented-out-code죽은 코드 누적

Full rule documentation: github.com/pertrai1/eslint-plugin-llm-core#rules

Why Not Just Use typescript-eslint?

Great question. typescript-eslint is excellent — this plugin is designed to complement it, not replace it.

typescript-eslinteslint-plugin-llm-core
FocusTypeScript 언어 정확성AI 코딩 패턴 방지
Error messages기술적이며, 사양 중심교육적이며, 맥락이 풍부함
Rule design언어 사양 준수관찰된 LLM 버그 패턴

You should use both. typescript-eslint catches TypeScript‑specific issues. llm-core catches patterns that LLMs repeatedly get wrong — regardless of whether they’re technically valid TypeScript.

Getting Started

npm install -D eslint-plugin-llm-core
// eslint.config.js
import llmCore from 'eslint-plugin-llm-core';

export default [
  {
    plugins: {
      'llm-core': llmCore,
    },
    rules: {
      // Enable the recommended set
      ...llmCore.configs.recommended.rules,
    },
  },
];

이제 평소처럼 ESLint를 실행하면, 플러그인이 AI‑특화 함정들을 찾아내고 더 안전하고 유지보수가 쉬운 코드를 배포하도록 도와줍니다.

module.exports = [
  {
    files: ["**/*.js"],
    rules: {
      // ...your rules here
      "llm-core/no-async-array-callback": "error",
      // other rules…
    },
  },
];

그게 전부입니다. 권장 규칙 집합에 대해 별도의 설정이 필요 없습니다.

더 큰 그림: AI에게 더 나은 습관 가르치기

이 규칙들은 단순히 실수를 잡아내는 것이 아니라 가르칩니다.

AI 어시스턴트가 다음 오류 메시지를 볼 때:

Avoid passing async functions to array methods.
This pattern returns an array of Promises, not the resolved values.
Consider using Promise.all() or a for...of loop instead.

그것은 학습합니다. 다음 번에는 올바른 패턴을 작성합니다.

반복적인 에이전트 워크플로우에서 — AI가 코드를 반복적으로 작성하고, 테스트하고, 수정하는 경우 — 이 피드백 루프가 누적됩니다. 각 린트 오류가 교육의 순간이 됩니다.

다음 단계

플러그인은 초기 단계이지만 기능합니다. 현재 집중 영역:

  • 자동 수정 가능한 규칙에 대해
  • 더 많은 로깅 라이브러리 감지 (Pino, Winston, Bunyan)
  • 추가 규칙 지속적인 연구 기반
  • 증거 수집 규칙이 실제로 AI‑생성 코드 품질을 향상시키는지 여부에 대해

AI 코딩 어시스턴트(예: Cursor, Claude Code, Copilot 등)를 사용하고 있다면, 그들이 잘못하는 패턴에 대한 여러분의 피드백을 듣고 싶습니다.

사용해 보기

npm install -D eslint-plugin-llm-core

이걸 만들었나요? 마음에 안 드나요? 놓친 규칙에 대한 아이디어가 있나요? 이슈를 열거나 연락 주세요. AI가 이상한 코드를 작성하는 것을 본 기여자를 적극적으로 찾고 있습니다.

0 조회
Back to Blog

관련 글

더 보기 »