커스텀 ESLint 규칙 만들기

essem
6 min readOct 19, 2021

--

프로젝트를 진행하다 보면 해당 프로젝트에만 필요한 규칙들이 생긴다. 범용적인 코드 검사 도구를 사용할 수 있으면 좋지만, 범용적이지 않은 규칙이라면 자체 도구를 제작하여야 하고, 그마저도 어렵다면 코딩 컨벤션을 통해 규칙을 지킬 수 있도록 해야 한다.

만약 ESLint 에 커스텀 규칙을 추가할 수 있다면 VS Code 등에서 개발자가 즉시 피드백을 받을 수도 있으며 CI 에서 검증하기도 쉽다.

여기서는 Express.js 기반의 웹 서버 프로젝트에서 모든 route 들이 logger라는 미들웨어를 반드시 추가해 하는 상황을 가정하고 이를 위한 샘플 프로젝트를 만들어 본다.

플러그인 프로젝트 생성

ESLint 규칙을 작성하기 위해서는 앱 프로젝트와는 별개로 플러그인 프로젝트를 만들어야 한다. 플러그인 프로젝트의 뼈대를 생성해 주는 Yeoman 생성기를 사용할 수도 있지만 여기서는 워낙 단순한 규칙 하나를 추가할 예정이므로 프로젝트를 직접 만들어 본다.

플러그인 이름이 essem 이라고 하면 앞에 eslint-plugin-을 붙여서 프로젝트를 생성한다. (만약 scoped 프로젝트라면 디렉토리는 essem/eslint-plugin 패키지명은 @essem/eslint-plugin 처럼 만들 수 있다.)

mkdir eslint-plugin-essem
cd eslint-plugin-essem
npm init --yes

index.js 파일을 추가하고 다음과 같이 규칙을 작성한다.

module.exports.rules = {
"must-log": {
create(context) {
const functionNames = ["get", "post", "put", "delete"];
return {
CallExpression: (node) => {
if (!node.callee.object || node.callee.object.name !== "app") {
return;
}
if (
!node.callee.property ||
!functionNames.includes(node.callee.property.name)
) {
return;
}
const log = node.arguments.find(
(arg) =>
arg.type === "CallExpression" && arg.callee.name === "logger"
);
if (!log) {
context.report(node, "need logging middleware");
}
},
};
},
},
};

규칙의 의미는 app.get() 처럼 메소드를 부르는 객체의 이름이 app 이고 메소드 이름이 get, post, put, delete 중에 하나인 경우 파라메터중에 반드시 logger() 함수 호출이 있어야 한다는 의미이다.

예제: https://github.com/essem/eslint-plugin-essem

앱 프로젝트 생성

이제 위에서 생성한 규칙을 사용할 Express.js 기반의 테스트 앱 프로젝트를 만들어 보자.

mkdir eslint-plugin-test-app
cd eslint-plugin-essem
npm init --yes
npm install express --save

index.js 파일을 추가하고 내용을 작성한다.

const express = require("express");
const app = express();
const port = 3000;
const logger = () => (req, res, next) => {
console.log(`request ${req.path}`);
next();
};
app.get("/", logger(), (req, res) => {
res.send("Hello World!");
});
app.listen(port, () => {
console.log(`listening at http://localhost:${port}`);
});

이제 ESLint를 추가해 보자.

npm install eslint --save-dev

.eslintrc.js 파일을 추가하고 다음과 같이 작성한다.

module.exports = {
env: {
es6: true,
},
plugins: ["essem"],
rules: {
"essem/must-log": "warn",
},
};

예제: https://github.com/essem/eslint-plugin-test-app

의존성 추가

플러그인 프로젝트를 앱 프로젝트에서 npm install 해야 겠지만 publish 한 상태가 아니니 로컬에서 연결해 보자. (만약 scoped 프로젝트라면 플러그인 이름은 @essem 만 쓰면 되고, 규칙 이름도 @essem/must-log 라고 적으면 된다.)

우선 플러그인 프로젝트에서 링크를 생성한다.

cd eslint-plugin-essem
sudo npm link

다음 앱 프로젝트에서 위에서 생성한 링크를 추가한다.

cd eslint-plugin-test-app
npm link eslint-plugin-essem

테스트

우선 커맨드라인으로 잘 되는지 검사해보자.

npx eslint index.js

문제가 없어야 한다. 이제 코드에서 logger() 미들웨어를 지우고 다시 검사해 보자

npx eslint index.js/home/essem/work/essem/eslint-plugin-test-app/index.js
10:1 warning need logging middleware essem/must-log
✖ 1 problem (0 errors, 1 warning)

위와 같은 결과가 나와야 한다. (VS Code에서도 동일하게 동작해야 한다. 만약 잘 안되면 VS Code를 다시 실행해 본다.)

규칙 작성

ESLint 규칙은 다양한 방식으로 작성할 수 있는데 자바스크립트의 AST(Abstract Syntax Tree)를 사용한다면 상당히 세밀한 규칙을 만들 수 있다. 원하는 코드의 AST를 분석 할 때 AST Explorer 사이트를 사용하면 굉장히 편리하게 구문 분석을 할 수 있다.

참고 문서

--

--