나는 500개의 AI 코딩 실수를 분석하고 이를 잡아내는 ESLint 플러그인을 만들었다
Source: Dev.to
번역할 텍스트를 제공해 주시면 한국어로 번역해 드리겠습니다.
아마도 본 적이 있을 패턴
const results = items.map(async (item) => {
return await fetchItem(item);
});괜찮아 보이죠, 그렇죠? 당신의 AI 어시스턴트가 작성했습니다. 테스트가 통과합니다. 코드 리뷰에서 승인받았습니다.
그런데 프로덕션에 배포되면 results는 Promise 배열이 됩니다 — 기대한 값이 아니라. 2번째 줄의 await는 아무 효과가 없습니다. Promise.all(items.map(...)) 혹은 for…of 루프가 필요했습니다.
이것은 TypeScript 버그가 아닙니다. 일반적인 LLM 코딩 실수이며 — 제가 AI‑생성 코드 품질을 연구하기 시작했을 때 발견한 수백 가지 중 하나입니다.
Source: …
문제: AI가 작동하는 코드를 작성한다, 올바른 코드를 작성하지 않는다
LLM은 테스트를 통과하는 코드를 작성하는 데는 뛰어나지만, 모서리 사례를 처리하고 일관성을 유지하며 내부적으로 모범 사례를 따르는 코드를 작성하는 데는 형편없습니다.
여러 실증 연구(예: 333개의 버그 분석, PromptHub의 558개 잘못된 스니펫 연구)를 검토한 결과, 다음과 같은 뚜렷한 패턴이 나타났습니다:
| 버그 유형 | 빈도 |
|---|---|
| 누락된 코너 케이스 | 15.3 % |
| 오해 | 20.8 % |
| 환각된 객체/API | 9.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 규칙은 더 평탄한 패턴을 권장합니다.
규칙 뒤의 연구
| Rule | Bug Pattern Addressed |
|---|---|
no-async-array-callbacks | Promise.all 누락, 잘못된 async 흐름 |
no-empty-catch | 오류를 조용히 무시 |
no-magic-numbers | 유지보수가 어려운 상수 |
prefer-early-return | 깊은 중첩, 불명확한 제어 흐름 |
prefer-unknown-in-catch | any 타입의 catch 매개변수 |
throw-error-objects | Error 인스턴스 대신 문자열을 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-eslint | eslint-plugin-llm-core | |
|---|---|---|
| Focus | TypeScript 언어 정확성 | 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- GitHub: https://github.com/pertrai1/eslint-plugin-llm-core
- npm: https://www.npmjs.com/package/eslint-plugin-llm-core
이걸 만들었나요? 마음에 안 드나요? 놓친 규칙에 대한 아이디어가 있나요? 이슈를 열거나 연락 주세요. AI가 이상한 코드를 작성하는 것을 본 기여자를 적극적으로 찾고 있습니다.