Node.js 소스 코드에서 숨은 오타 찾기

개요

들어가기 앞서

다음의 문장을 속독으로 가볍게 읽어보자.

  • 캠릿브지 대학의 연결구과에 따르면, 한 단어 안에서 글자가 어떤 순서로 배되열어 있지는는 중하요지 않고, 첫 번째와 마지막 글자가 올바른 위치에 있는 것이 중하요다고 한다. 나머지 글들자은 완전히 엉진망창의 순서로 되어 있라을지도 당신은 아무 문제 없이 이것을 읽을 수 있다. 왜하냐면, 인간의 두뇌는 모든 글자를 하하나나 읽는 것이 아니라 단어 하나를 전체로 인하식기 때이문다.

윗 글을 다시 한 번 글자 단위로 세세하게 들여다보면 그저 빠르게 슥 훑었을 때와는 분명 다른 느낌이 들 것이다.

문장 속 단어들을 이루는 문자의 구성과 수가 같다면 인간은, 특히나 해당 언어에 익숙한 사람일 수록 문장 전체 뜻을 이해하는데 별다른 어려움을 겪지 않는다는 게 요지이다.

그러니 오류를 찾지 못 했다고 하여 너무 자책할 필요는 없다. 오히려 읽는 데 버벅였다거나 지적을 많이 한 경우가 그 언어를 더 어색하게 느끼거나 난독 증세가 있다는 의미니 말이다.

그런데 왜 난데없이 기술 블로그에 이런 소리를 하고 있는 걸까?

소스 코드 역시 결국은 사람이 작성하는 지라 클래스나 메소드, 변수 명 등을 선언 할 때 적지 않은 오탈자가 발생할 여지가 있고 유심히 바라보지 않는다면 사람이 직접 이런 오류를 처리하기가 매우 힘들다는 게 문제이다. 오탈자가 계속해서 누적된다면 변수 혹은 객체의 속성 key등이 의미하는 바가 퇴색되어 추후 해당 기능/속성을 참조하거나 다른 개발자가 이를 활용할 때 혼란과 오해를 발생시킬 가능성이 있다.

Do you know Typo?

Jetbrains에서 만든 PyCharm이나 IntelliJ 혹은 이를 기반으로 한 Android Studio 환경에서의 개발 경험이 있는 사람이라면 Typo라는 말을 한 번 이상은 들어봤을 것이다. Typo는 위에서 언급한 소스 코드 내 오탈자들을 보편적인 혹은 개인적으로 작성한 사전(Dictionary) 목록의 내용과 부합하는지를 검사하고 경고 메시지를 보내는 역할을 하는 코드 포맷터의 일종이다. 이에 대해 이미 아는 사람이라면 eslint의 spellcheck 플러그인이 무슨 역할을 하는지 납득하기가 더 편할 것이다. 다만 본 포스트는 Node.js에 관한 것이니 Typo에 대한 자세한 설명은 생략하겠다.

맛보기

Node.js 개발 환경에서도 Typo와 비슷한 역할을 하는 친구가 있는데, 바로 eslint의 플러그인인 eslint-plugin-spellcheck이다. 전체 설치 및 적용 방법에 앞서 이게 실제 환경에서 어떤 식으로 적용되는지 가볍게 핥아보도록 하자.

다음은 사용자의 이름, 성, 나이를 생성자 매개변수로 받아 private으로 저장하고 sayHello 라는 함수로 본인에 대해 소개하는 클래스의 선언문이다.

1
2
3
4
5
6
7
8
9
10
11
12
export class User {
constructor(
private name: string,
private famliyName: string,
private age?: number,
) {}
sayHello() {
console.log(
`Hello, this is ${this.name} ${this.famliyName}. Nice to maet you!`,
);
}
}

위 스크립트를 찬찬히 살펴보자. 혹시 문제점이 보이는가?

2가지 있는데 이는 TSC의 문법적 오류가 아닌 앞서 언급했던 영어 오탈자에 해당하는 것이다. lint에 spellcheck 플러그인을 적용한 환경에서 위 문장을 다시 보면 다음과 같이 나온다.

요약하자면 다음과 같다.

  • famliy 라는 단어를 찾을 수 없습니다.
  • maet 라는 단어를 찾을 수 없습니다.

첫 번째는 famliyName이라는 멤버에서 famliy라는 단어가 잘못되었다는 의미이다. l과 i의 위치가 바뀌어 있는데, 원래 의도대로 family로 고쳐야 한다.

두 번째는 maet인데, meet이라고 적으려다가 실수한 케이스이다.

코드 품질 관리 작업을 사람이 직접 해야 한다고 생각해보자. 매일 하루 8시간씩 모니터앞에 앉아 내가 이기나 컴퓨터가 이기나 눈싸움하며 소스 코드의 글자 하나하나를 일일이 다 확인하는 거다. 퇴직 각 날카롭다.

다행히 이런 건 그 어떤 개발자도 환영하지 않는 요소이다. 이와 같은 문제에 대한 해결책은 조금만 검색해 보면 보통 항상 준비되어 있다.

여담이지만 이 역시 넓게 보면 작성한 코드를 빌드하고 모아서 테스트 하고 서버에 배포되는 수순을 자동화 한 CI/CD 프로세스, 나아가 DevOps 엔지니어링 공법도 이러니 저러니 해도 결국 이러한 개발진들의 귀차니즘을 해결하고자 나온 결과가 아닌가 싶다.

설치 및 적용

먼저 vsc 스크립팅 환경은 평소 사용하는 기본 값에 맞추었다. 각 요소에 대한 설명이 모두 들어가면 포스트가 너무 길어지므로 spellcheck 플러그인의 설치와 적용에 대해서만 적었다. 아래는 환경 정보이다.

다음의 명령어로 플러그인을 프로젝트 의존성 모듈로 설치한다.

1
yarn add --dev eslint-plugin-spellcheck

.eslintrcspellcheck 플러그인을, 린터 규칙으로는 spellcheck/spell-checker 항목을 추가한다.

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
...
plugins: ['spellcheck' ...],
rules: {
...
'spellcheck/spell-checker': ['warn', {
// 여기에 옵션 추가
}],
}
...
};

이러면 기본적인 설정은 끝이 난다. 여담이지만 규칙 레벨을 error(2)로 빡빡하게 설정하는 분들도 종종 있는데, 맞춤법 검사 정도야 warn(1) 수준이면 충분할 것으로 본다.

규칙 옵션

여타 다른 eslint 플러그인들과 마찬가지로 스펠 체크 플러그인 역시 여러 규칙 설정 옵션들을 제공한다. 다음은 플러그인 소스 저장소NPM 패키지 페이지에서 제공되는 규칙 옵션들이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
module.exports = {
...
plugins: ['spellcheck' ...],
rules: {
...
'spellcheck/spell-checker': [
1,
{
comments: false,
/**
* 기본값 : true
* 타입 : boolean
* true로 설정 시 주석에 있는 문장(영어)들도 스펠 체킹을 실시합니다.
*/
strings: true,
/**
* 기본값 : true
* 타입 : boolean
* true로 설정 시 리터럴 string의 스펠 체킹을 실시합니다.
* 단 리터럴 string이란 quote ('' 혹은 "")로 구성된 문자열을 의미합니다.
* backtick (``) 으로 구성된 문자열은 리터럴 취급을 하지 않습니다.
*/

identifiers: true,
/**
* 기본값 : true
* 타입 : boolean
* true로 설정 시 식별자에 대한 스펠 체킹을 실시합니다.
* 여기서 식별자라는 건 변수명, 클래스명, 함수명 등을 의미합니다.
*/

ignoreRequire: false,
/**
* 기본값 : false
* 타입 : boolean
* require문에 들어가는 문장은 스펠 체킹에서 무시합니다.
*/

templates: true,
/**
* 기본값 : true
* 타입 : boolean
* ES6 템플릿에 맞춰 스펠 체킹을 실시합니다.
*/

lang: 'en_US',
/**
* 기본값 : en_US (영어 - 미합중국)
* 타입 : string (Locale String)
* 사용할 언어를 선택합니다. 사용 가능한 옵션은 다음과 같습니다.
* 1. en_US (영어 - 미합중국)
* 2. en_CA (영어 - 캐나다)
* 3. en_AU (영어 - 오스트레일리아 연방)
* 4. en_GB (영어 - 대영제국)
*/

langDir: '',
/**
* 기본값 : ''
* 타입 : string (Dir Path String)
* 언어 파일 경로입니다. 기본적으로 플러그인 내부의 디렉토리를 따릅니다.
*/

skipWords: ['lang', 'jsdoc'],
/**
* 기본값 : []
* 타입 : Array<string>
* 스펠 체킹에서 제외 할 항목들입니다. 이 곳에 명시한 단어는
* 맞춤법 검사를 진행하지 않습니다.
*/

skipIfMatch: ['http://[^s]*'],
/**
* 기본값 : []
* 타입 : Array<string> (Array<Regex String>)
* 스펠 체킹에서 제외 할 항목들을 정규표현식으로 지정하고자 할 때 사용합니다.
* 다만 주의사항으로 문장 전체를 하나의 요소로 인식하기 때문에 주석 등에서 사용 시 의도치 않게
* 동작할 수 있습니다.
* 위 적어둔 것과 같이 사용하면 https로 시작하는 url 정보에 대해서는
* 맞춤법 검사를 진행하지 않게 됩니다.
*/

skipWordsIfMatch: ['^[-\\w]+/[-\\w\\.]+$'],
/**
* 기본값 : []
* 타입 : Array<string> (Array<Regex String>)
* 스펠 체킹에서 제외 할 항목들을 정규표현식으로 지정합니다.
* 다만 이 경우 문장 전체가 아닌 단어별로 적용됩니다.
* 위 예시와 같이 적어두면 MIME 타입의 단어에 대해서는
* 맞춤법 검사를 진행하지 않습니다.
*/

minLenth: 1,
/**
* 기본값 : 1
* 타입 : number
* 맞춤법 검사를 진행하기 위한 최소한의 단어 길이입니다.
* 위 예시처럼 3으로 입력시 3글자 미만의 길이를 가진 단어는
* 스펠 체킹을 진행하지 않습니다.
*/
},
],
}
...
};

여기서 눈 여겨 볼 옵션들이 몇 가지 보인다.

적용 대상

일단 identifiers옵션은 아주 특별한 상황이 아니면 기본 값인 true 로 두는 게 적합하다.

commentsstrings은 정말로 옵셔널한데, 이 부분은 개발자 본인의 취향에 맞춰 선택하면 되겠다. 보통 commentsfalse로 (주석을 한글로 다는 경우가 많은데 한글은 당연하게도 맞춤법 검사 안 해준다.) stringstrue로 설정하는 경우가 많다.

옵션 명 타입 기본값 설명 추천
comments boolean true 주석에 맞춤법 검사 적용 여부 false
strings boolean true 문자열에 맞춤법 검사 적용 여부 true
identifiers boolean true 식별자(변수명, 클래스 명 등)에 맞춤법 검사 적용 여부 true

제외 대상

ignoreRequire는 문자 그대로 require문 안에 있는 문자열(모듈 명 혹은 경로 명)을 제외할 것인가에 대한 옵션이다. 기본값이 false로 되어 있는데, 경험 상 true로 해주는 편이 정신 건강에 이롭다.

skipWords는 맞춤법 적용 대상에서 제외 할 항목들이다. 의외로 JS나 TS에서 자주 사용하는 용어들이 맞춤법 검사에 적용되는 경우가 많다. 당장 방금 언급 한 JS, TS 라는 단어 자체도 검사기에 걸린다. 영어사전에 등재된 항목만 통과한다는 이야기다. 이는 각자 경험에 비추어 검사에서 제외해야 할 단어들을 한 곳에 저장 해 두고 관리하는 것을 추천한다.

skipIfMatchskipWordsIfMatch 두 항목은 모두 정규 표현 식을 사용한다. 기본적으로 어떤 단어/문장 등을 검사 할 때 작성한 정규 표현 식을 만족하는 경우 제외하겠다는 의미인데, skipIfMatch는 문장 전체에, skilWordsIfMatch는 단어 별로 적용된다.

minLenth는 검사를 수행할 단어의 최소 길이를 의미한다. 값을 3으로 설정하면 1~2글자로 이루어진 단어는 맞춤법 검사에 걸리지 않는다.

옵션 명 타입 기본값 설명 추천
ignoreRequire boolean false require 문 안에 있는 문자열 검사 적용 제외 true
skipWords Array [] 검사에서 제외시킬 단어 목록
skipIfMatch Array [] 검사에서 제외할 문자열의 정규 표현식 목록
skipWordsIfMatch Array [] 검사에서 제외할 단어의 정규 표현식 목록
minLenth number 1 검사를 적용할 최소 단어/문장 길이 3