이전 프로젝트를 하면서, 모듈 함수를 구현할 때 마다, 언젠가 NPM
패키지로 배포해봐야지 하고 고민하곤 했었다. 내가 만든 서비스가 돌아가도록 하는 코드가 아닌, 다른 사람이 쉽고 유용하게 사용하는 코드를 만든다는 점에서, 도전해보고 싶은 분야였다.
7월, 마침 상반기 취업이 끝나가고 잠시 신규 채용 소식이 주춤하였고, 기회는 이때다! 하면서 부리나케 시도하게 되었다.
es5+
, node.js
, 타입스크립트
등 다양한 자바스크립트 환경을 지원하며, 자동완성 기능이 있고, 친절하게 예제도 담으려고 노력해보았다! @beberiche/validator 생애 첫 모듈 패키지의 개발 과정으로 렛츠게릿!
먼저 실제 패키지를 만들기 전에 NPM
패키지를 올리는 과정 및 어떤 식으로 배포가 되는지에 대해 알 필요가 있다. 아무 생각 없이 했다가, 며칠동안 봉변을 당한 입장으로써, 구글링이나 유튜브로 NPM
패키지 생성에 대해 찾아보고, 이런 저런 시행착오를 겪어보는 걸 추천하다.
유튜브의 생활코딩 님께서 간단한
NPM
패키지 생성에 대한 강의를 올려두셨다. 영상도 길지 않고NPM
패키지를 개발하는데 꽤 도움이 되었다.
필수적인 참고 사항은 아래에 작성해두었다.
NPM
에 패키지를 올리려 한다면, 회원가입과 이메일 인증을 진행해야한다. 이메일 인증을 진행하지 않는 경우 npm publish
진행 시 403 에러를 반환할 수 있다.
npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT https://registry.npmjs.org/json-range-slider-test - Forbidden
npm ERR! 403 In most cases, you or one of your dependencies are requesting
npm ERR! 403 a package version that is forbidden by your security policy, or
npm ERR! 403 on a server you do not have access to.
패키지를 올리기 이전 이름과 버전 사항을 정해야 한다. 단, 패키지 이름 선정 시 중복된 이름이 존재하는 경우, 올라가지 않을 수 있다. npm 홈페이지에 가서 동일한 이름이 없는지 검색해보거나 npm info [project_name]
을 통해 확인하는 것이 가능하다.
이름 선정 방법 중에, 스코프 패키지를 활용하는 방식이 있다. @types
와 같이 분류 그룹을 작성한다던지, @조직
혹은 @사용자
를 프로젝트 이름 앞에 덧붙여서, 중복 이름을 회피하는 것이 가능하다.
프로젝트 퍼블리싱 이전 커맨드를 통해 로그인을 진행하자. 원래는 커맨드를 통해 유저 이름과 패스워드를 작성하는 방식이었으나, 현재는 이메일을 통해 OTP
인증 코드 확인 방식의 로그인으로 바뀌었다.
npm login
일반 유저의 경우, public
형태로만 NPM
패키지를 퍼블리싱 할 수 있으며, private
패키지의 경우 매달 요금을 지출해야한다. package.json
에 특정 작업을 하지 않았다면, 기본 설정은 private
으로 되어있으므로, publish
시 public
설정을 해줘야 한다.
퍼블리싱 시 public
을 설정하는 방법은 다음과 같다.
npm publish access=publish
터미널에 다음과 같은 출력과, 성공했다는 이메일이 도착했다면 정상적으로 수행이 완료된 것이다.
만든 패키지를 설치하는 경우, module.export를 통해 나오는 객체 정보를 받게 된다. 다음과 같이 module.export를 통해 객체를 패키지에서 내본다고 하자.
module.exports = {
ko: "안녕",
en: "hi",
};
이 경우 해당 패키지를 인스톨 한 곳에서 require()
를 통해 데이터를 불러와 실행하면 다음과 같은 결과과 출력된다.
const obj = require("@beberiche/hi");
console.log(obj);
node index.js
// { ko: '안녕', en: 'hi' }
퍼블리싱한 패키지를 업데이트하기 위해서는 코드에 변경사항을 적용한 후 package.json
의 버전도 함께 변경하여야 한다. 만약 현재 NPM
리포지토리에 존재하는 버전을 업데이트 하는 경우, 퍼블리싱이 거절 당한다.
패키지를 재 업로드 하는 커맨드는 다음과 같다. 한번 public
리포지토리로 설정되면 다시 설정하지 않아도, 자동으로 public
으로 퍼블리싱 된다.
npm publish
NPM
모듈 패키지를 개발하는데 타입스크립트가 과연 필요한가 라고 묻는다면, 개인적으론 필수적이라고 생각한다. 타입스크립트를 적용하는 것만으로도 작업량이 줄어들기 때문이다.
개발을 해본 사람이라면 라이브러리를 사용할 때 이런 저런 실수를 해본 경험이 있을 것이다. 예컨데, 함수를 호출하는 데 있어 매개변수에 정의된 타입이 아닌 다른 값을 넣거나, 매개변수를 2개 보내야 하는데 1개만 보내는 등의 사례를 예로 들 수 있겠다.
일반 자바스크립트로 개발을 진행하면 이러한 예상가능한 에러를 처리하는 로직을 세울 필요가 있다. 패키지 배포자로써 어떤 점이 잘못되었는지를 사용자에게 안내할 필요가 있다. 이러한 문제를 타입스크립트를 적용시키는 것으로, 자체적으로 오류를 사용자에게 알려줄 수 있다는 장점이 있다.
물론, 사용자도 타입스크립트 프로젝트를 개발할 경우에 한하여 그렇다.
현재는 많은 프로젝트에서 타입스크립트를 사용하지만 만약 사용 접근성을node.js
나 기본 js 범위까지 늘릴거라면 예상 가능한 에러에 대한 작업도 필수적으로 처리해줘야 할 것이다.
아래의 코드는 개발에 사용한 함수인 lenLimitUnder()
로, 본래 2번째 매개변수에는 길이의 미만 값을 정하는 숫자 값이 들어가야 한다. 만약 자바스크립트로 작성하는 경우라면 다음과 같이 에러를 반환하도록 직접 처리해야한다.
하지만 타입스크립트를 적용하는 것으로 이렇게 빨간 줄로 오류 처리를 자동으로 제공해준다는 점에서 개발을 하는 입장에서도, 사용하는 사람의 입장에서도 훨씬 작업이 용이해진다.
이번 프로젝트의 경우, 패키지 배포에 주안점을 두었기 때문에, 타입스크립트에서는 타입 검사, 추론 정도의 최소한의 기능만 받아올 수 있는 환경으로 구성했다.
타입스크립트 개발 환경 설정 시 다음의 정보를 참조했다.
UZILOG
패키지 개발에 반영시킨 타입스크립트 설정은 다음과 같다.
최종적으로 올릴 패키지는 번들링한 js
파일 형태로 내보내질 예정이므로, --save-dev
형태로 설치했다.
npm i --save-dev typescript
typescript
개발에 필요한 정적 분석 도구를 활용했다. 중요한 부분은 아니므로 최소한으로만 설정하여 사용했다.
npm i --save-dev prettier tslint tslint-config-prettier
typescript
에서 cjs
기능을 사용하려면, node
타입에 대한 설치가 필요하다.
npm i --save-dev @types/node
기존 권장 사항에서 사용했으며, 예외사항은 추가하지 않았다.
{
"extends": ["tslint:recommended", "tslint-config-prettier"]
}
자동 저장에 따른 포맷팅만 사용할 것이기에 lint
와 동일하게 최소한만 설정했다.
{
"printWidth": 120,
"trailingComma": "all",
"singleQuote": true
}
package.json
에 정적분석도구 커맨드를 추가했다.
"scripts": {
"build" : "tsc",
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
"lint": "tslint -p tsconfig.json",
},
자바스크립트에는 모듈을 불러오는 방법이 2가지가 존재한다. node.js
에서 많이 사용하는 common.js
와 import
를 활용하는 es module
이 그 예이다. 두 기능의 특징 차이는 이전 포스팅한 내용이 있으니 참고해보시길 바란다.
중요한 건 어찌 되었든 두 가지 모두 모듈 패키지를 불러오는 방법이며, 내가 만드는 패키지가 되도록 일반 cjs
든, esm
이든 모두 불러오는 것이 가능하도록 하고 싶었다.
처음에는 cjs
, esm
전용의 두 개의 파일을 생성하여 파일별로 별도의 빌드작업을 진행하려 했으나 모듈 번들러를 활용하여, 하나의 파일만으로 다양한 환경의 여러 파일을 생성하는 방법이 있어, 이를 활용해보기로 했다. 이 프로젝트 에서는 rollup
을 활용했다.
우선 rollup
모듈을 설치하고, typescript
빌드 설정, node
모듈 기능, cjs
빌드 기능을 제공하는 플러그인들을 설치했다. cjs
빌드 설정의 경우, 관련 사례를 찾아보니 node
플러그인과 함께 사용해야 더 광범위한 호환이 가능하기 때문에 함께 사용할 것을 권장한다고 한다.
해당 프로젝트에서 사용한 rollup
모듈과 플러그인은 다음과 같다.
# 기본 rollup
npm i --save-dev rollup
# 타입스크립트 빌드 플러그인
npm i --save-dev rollup-plugin-typescript2
# node 모듈 플러그인
npm i --save-dev @rollup/plugin-node-resolve
# cjs 모듈 플러그인
npm i --save-dev @rollup/plugin-commonjs
rollup
을 설치했다면 rollup
관련 설정 내용을 작성해야한다. rollup.config.js
를 생성하여 다음의 설정을 작성해주었다. input
에 빌드할 파일의 경로를 output
에 빌드 결과물에 대한 경로를 작성한다.
특이사항으로는 package.json
의 일부 속성을 참조하여 output
에 반영할 수 있는데, package.json
을 import
하는 경우 반드시 단언 assert
을 통해 type : "json"
을 명시해야 한다.
// rollup.config.js
import typescript from 'rollup-plugin-typescript2';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import pkg from './package.json' assert { type: 'json' };
const extensions = ['.js', '.ts'];
/**
* @type {import('rollup').RollupOptions}
*/
const config = {
input: 'src/main.ts', // 변환시킬 파일
output: [ // 변환된 결과물
{
file: pkg.main, // 반환 경로 작성
format: 'cjs', // 확장자
sourcemap: true, // sourcemap 설정, 웹 브라우저 로드 시 개발자 도구에서 원본 소스 코드를 보여주며 디버깅이 가능하게 함
},
{
file: pkg.module,
format: 'esm',
sourcemap: true,
},
],
plugins: [
resolve({ extensions }),
commonjs(),
typescript({
tsconfig: 'tsconfig.json',
clean: true,
}),
],
};
export default config;
pkg
의 정보를 받아오는 만큼, package
에도 설정 양식이 필요하다. 기존 ts
컴파일 작업을rollup
으로 이관하여, cjs
, esm
에 대한 통합된 결과물을 받아오도록 할 것이다. package.json
의 scripts
항목에, rollup
명령문을 작성한다.
# 최상위 디렉토리의 rollup.config.js의 내용을 기반으로 빌드를 실행한다.
rollup -c
"scripts": {
//"build": "tsc",
"build" : "rollup -c"
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
"lint": "tslint -p tsconfig.json",
},
앞서 작성했던 rollup.config.js
에서 pkg
정보에서는 module
과 main
을 참조하고 있다. 반드시 module
, main
으로 써야 하는 것은 아니며, package.json
객체 내에 찾으려는 속성이 존재하여 해당 정보를 불러올 수 있으면 된다. package.json
내부의 해당 속성에 cjs
와 esm
파일을 반환할 경로를 작성한다.
"files": [
"dist/*.{js,ts,map}"
]
"main": "dist/main.cjs", // cjs로 반환한다.
"types": "dist/main.d.ts", // 타입스크립트로 설정한 타입들이 반환된다.
"module": "dist/main.esm.js", // esm이 반환된다.
rollup 필요한 설정은 끝이 났다. 이후 원하는 코드 작업을 진행하고 build
커맨드를 실행하면 된다, main.ts 를 input
경로로 설정하고, 해당 설정에 따라 build
커맨드를 실행하면 다음과 같은 결과물들이 나오게 된다. -esm.js
는 es module
을 사용하는 경우, 참조되는 파일이고, -.cjs
는 require()
를 사용하는 경우, 참조되는 파일이다.
각각 반환된 .cjs
와 esm.js
의 파일을 살펴보면 내보내기 형식이 다르다는 것을 알 수 있다.
다음은 실제 패키지를 프로젝트에 인스톨하여, 사용하는 모습이다. 보시다시피, import
로도, require
로도 모두 정상적으로 함수를 가져온다.
어느정도의 준비가 완료되었으니 이제 개발을 진행해보자!
만든 NPM 패키지는 @beberiche/validator 로, 사용자 입력 정보를 기반으로 유효성 검사를 진행하는 함수를 제공한다. 예를들어 현재 입력 값에 대한 길이 범위를 제한하거나, 입력 값이 이메일 형식인지, 전화번호 형식인지 등을 확인할 수 있다.
각각의 함수들은 유효성이 정상적으로 검증된 경우에는 true
값을 반환하며, 그렇지 않은 경우는 반환 값은 없고, Error
를 출력하도록 설정했다.
자세한 사항은 깃 리포지토리의 README.md
문서를 확인해보시기 바란다. 참고로 막 그렇게 대단한 건 없다. 엄청난 걸 만들기 보다는 "이전에 구현했던 코드를 패키지로 사용하도록 만들었다" 에 의의를 두는 프로젝트이다.
beberiche/npm-validator-module
개발을 진행하며, 깊이 고민했던 점 혹은 어려웠던 점에 대해 이야기해보도록 하겠다.
길이 제한을 확인하는 함수를 만들려고 한다. 함수에 필요한 것들을 찾아보니, 조건은 다음과 같았다.
다음의 코드를 구현하려니 함수 하나에 코드양이 엄청 커졌다.
// 길이제한
function lenLimit(inputValue, len1, len2 = undefined, obj = { trim: true }) {
if (inputValue == undefined || len1 == undefined) {
return {
error: "limitError",
msg: "입력 값 혹은 길이가 정해지지 않았습니다.",
};
}
const val = obj.trim ? inputValue.trim() : inputValue;
if (len1 > 0 && len2 > 0) {
if (lenStart == undefined || lenEnd == undefined) {
return {
error: "limitError",
msg: "이상, 미만의 값이 제대로 설정되어 있지 않습니다.",
};
}
if (len[0] > len[1]) {
return {
error: "limitError",
msg: "시작 값이 끝 값보다 커서는 안됩니다.",
};
}
if (val.length() < len[1] && len[0] <= val.length()) {
return true;
} else {
return {
error: "limitError",
msg: "시작 값보다 작거나, 끝 값보다 큽니다.",
};
}
} else if (obj.under) {
if (val.length >= len) {
return true;
} else {
return {
error: "limitError",
msg: "주어진 값보다 큽니다.",
};
}
} else {
...
...
}
}
module.exports = {
lenLimit,
};
이 경우에 가독성도 좋지 않고, 유지 보수나 확장도 힘들다. 더불어 구현하는데 생각이 너무 많아졌다.
광범위한 조건을 하나의 함수로만 해결하려고 했던 점이 코드가 비대해지는 원인이였다. 비대한 함수를 여러 갈래의 함수로 나눠보자. 해당 문제의 경우, 길이 이상 함수, 길이 미만 함수, 길이 범위 함수 등으로 나누어 길이 범위 함수를 사용하는 경우, 길이 이상 함수, 길이 미만 함수를 호출하여 두 가지 유효성 검사를 모두 통과하도록 유도했다.
// 길이 범위
function lenLimit(inputValue: string, len1: number, len2: number) {
let ret1 = lenLimitMore(inputValue, len1);
let ret2 = lenLimitMore(inputValue, len2);
if (ret1 === true && ret2 === true) return true;
return {
error: 'limitError',
msg: '주어진 제한 범위에 입력값이 해당되지 않습니다.',
};
}
// 길이 이상
function lenLimitMore(inputValue: string, len: number) {
if (inputValue.length >= len) return true;
return {
error: 'limitError',
msg: '주어진 제한 길이보다 입력 값이 작습니다.',
};
}
// 길이 제한
function lenLimitUnder(inputValue: string, len: number) {
if (inputValue.length < len) return true;
return {
error: 'limitError',
msg: '주어진 제한 길이보다 입력 값이 큽니다.',
};
}
module.exports = {
lenLimit,
lenLimitMore,
lenLimitUnder,
};
패키지가 유효성 검사인 만큼, 입력값이나 사용용도에 따라 에러 타입은 다양할 필요가 있다. 다만 사이드 프로젝트 정도의 패키지이고 시간도 많지 않아서, 최소 조건만 설정하도록 적용했다. 내가 생각한 Error
의 최소조건은 Error
객체를 상속하는 customError
클래스를 생성하는 것이다.
customError를 만드는 것으로, 다음 확장시, 또다른 customError 클래스를 만들거나, 클래스에 새로운 속성을 넣는 등, 다양한 확장 접근이 가능하기 때문에, Error 클래스를 생성을 최소조건으로 잡은 것이다.
Error
객체를 상속하는 Validation Error
를 만든다. 일반적으로 Error
객체는 메시지를 넣어 인스턴스를 생성하는데, Validation Error
의 경우, 메시지와 더불어 errorType
을 명시하도록 했다.
class ValidationError extends Error {
errorType: string;
constructor(errorType: string, message: string) {
super(message);
this.errorType = errorType;
}
}
errorType
명시를 통해, 다양한 에러를 다루는 것이 가능해진다. 예를 들어 타입에 대한 설정이 잘못 이루어지면 typeError
를 유효성 조건이 거짓인 경우는 validError
를 출력하도록 했다.
export function isPhoneNumber(inputValue: string): true | void {
if (typeof inputValue !== 'string')
throw new ValidationError('typeError', '정의된 데이터 타입과 일치하지 않는 인자가 존재합니다.');
try {
const str = inputValue.replace(/-/g, '').trim();
let regxPhoneNumber = /^(070|02|01[016789]{1}|0[3-9]{1}[0-9]{1})[0-9]{3,4}[0-9]{4}$/;
if (regxPhoneNumber.test(str.trim())) return true;
throw new ValidationError('validError', '올바른 휴대폰 번호 혹은 전화번호 형식이 아닙니다.');
} catch (e) {
console.log(e);
}
}
여러 유효성 함수를 받아, 순차적으로 실행하는 함수를 만들려고 한다. 함수별 인덱스 순서에 따라 우선순위 제어가 가능하며, 연속적인 유효성 검사를 한번의 실행으로 여러 유효성 검사를 실행하는 함수이다.
아이디어는 굉장히 좋았으나, 몇가지 문제가 발생했다.
// 이런 형태의 함수이다.
function go(funcs:Function[]): boolean {
for(const func of funcs) {
let ret = func();
if(ret === undefined) return ret;
}
return true;
}
연속적인 유효성 함수들을 인자로 집어넣어 한 번에 사용할 수 있도록 함수 배열로 받도록 했다. 그러나 유효성 함수에 따라 검증에 필요한 인자의 타입이나 수가 각각 다르다. 함수 배열을 통해, 여러 유효성 함수를 보내는 상황에서 함수에 대한 인자들을 어떻게 보내야 하는가를 고민했다.
현재 go()
함수는 유효성 함수들을 반복문으로 실행하기 위해, 함수 배열로 인자를 받으므로, 유효성 함수들이 사용할 인자 역시 배열로 만들어 보았다. 단, 인자가 2개, 3개, … n
개 들어가는 유효성 함수도 있을터이니 이를 해결하기 위해, 2차원 배열을 설정하여 보내도록 한다.
인자 배열과 유효성 함수 배열의 길이는 동일해야하며, 각 인덱스에 맞게 진행하기 때문에, 인자 배열 인덱스와 유효성 함수 배열의 인덱스는 동일해야 한다.
function go(args: Array<Array<String | number>>, funcs: Array<Function>) {
let ret: Result;
if (args.length !== funcs.length) return reportError('인수 배열의 길이와 함수 배열의 길이는 서로 일치해야 합니다');
for (let i = 0; i < funcs.length; i++) {
ret = funcs[i](...args[i]);
if (ret !== true) return ret;
}
return true;
}
// 실행 시
go([
["asdfasd",4],
["asdfasdf",5],
["asdasdf",4,5],
],[lenlimitUnder,lenlimitMore,lenlimit]);
만드는 것 까지는 좋았으나, 이 시점 부터 유용한 함수라기 보다는 오히려 긴 타이핑 덕분에 괜히 작성하기 꺼려지는 함수로 느껴지기 시작했다.
유효성 함수별로 지정한 타입이 들어오지 않거나, 예외 상황이 접촉되는 경우는 어떻게 할 것 인가? 인자와 함수에 대한 인덱스 순서도 맞아야 하며, 함수별 인자 수나 타입도 일치해야한다. 더불어 유효성 함수 자체가 많아질수록 제어해야하는 범위는 더욱 커질 것이다. 즉 에러처리를 진행해야할 부분이 너무나 광범위하다고 느껴졌다.
이런 방식보다는 사용자가 원할 때 그냥 2,3개의 유효성 검사를 호출하여 진행하는 것이 더욱 유용하고 깔끔한 사용방식이라고 판단하여 해당 함수 구현은 포기하게 되었다.
NPM
패키지를 설치하여 원본 코드를 탐색해본 사람이라면 이러한 특이한? 주석을 본 경험이 있을 것이다.
/**
* @constructor
* @param {number} data
*/
아는 사람들은 다 알겠지만 JSDoc
은 Javascript
소스 코드에 대한 설명을 하기 위해 사용되는 마크업 언어로 코드에 대한 문서화를 도와주는 도구이다.
사실 패키지dp 만들고 싶은 기능이 인텔리센스
이다. 바쁘다 바빠 한국 사회에서 짜잘한 패키지를 사용하는데 구글링을 하거나 공식문서를 읽어야하는 것은 프론트엔드 개발자로서 용납 할 수 없는 사용자 경험이다.
vscode
, webstorm
등 웹 프론트엔드에서 많이 사용하는 IDE
의 경우, 기본적으로 JSDoc
을 내장하여 자동완성 기능을 제공하고 있다. 이를 활용하여 나의 패키지를 보다 사용하기 쉽도록 사용자 경험을 높여보자.
JSDoc
으로 코드에 대한 여러가지 정의할 수 있지만, 반드시 필요하다고 생각되는 요소만 넣어 작성했다.
@param
: 매개변수의 타입 및 설명을 나타낸다.@returns
: 함수의 반환 값을 나타낸다.@description
: 함수에 대한 설명을 나타낸다.@example
: 해당 코드에 대한 예제를 보여준다.다음은 JSDoc
을 통해, 작성한 패키지 코드의 일부이다.
/**
*
*
* @param {string} inputValue 유효성 검사를 진행할 입력값 입니다.
* @param {number} len1 유효 길이에 대한 시작 값 입니다. (이상)
* @param {number} len2 유효 길이에 대한 끝 값 입니다. (미만)
*
* @returns {true | void}
*
* @description 길이 제한 유효성 함수로, 입력값이 유효 길이 범위 안에 속해 있는지 체크합니다.
*
* @example
* let ret = lenLimit('asdf', 2, 8); // 2이상 8미만
* console.log(ret); // true
* ret = lenLimit('sadfa', 6, 8);
* console.log(ret); // undefined, Error : 입력값이 주어진 범위에 포함되지 않습니다.
*
*/
export function lenLimit(inputValue: string, len1: number, len2: number): true | void {
try {
if (typeof inputValue !== 'string' || typeof len1 !== 'number' || typeof len2 !== 'number')
throw new ValidationError('typeError', '정의된 데이터 타입과 일치하지 않는 인자가 존재합니다.');
let ret1 = lenLimitMore(inputValue, len1);
let ret2 = lenLimitUnder(inputValue, len2);
if (ret1 === true && ret2 === true) return true;
else throw new ValidationError('validError', '입력값이 주어진 범위에 포함되지 않습니다.');
} catch (e) {
console.log(e);
}
}
이후 패키지를 프로젝트에 설치하여 만약 JSDoc을 작성한 함수에 마우스 포인터를 가져다 놓으면 다음과 같이 코드에 대한 정보를 제공한다.
뿐만 아니라 한번이라도 해당 패키지를 호출했다면 자동완성 기능도 제공한다. 확실히 이러한 편의기능이 있으니 한층 더 패키지스러운? 느낌이 든다.
JSDoc
의 정보를 바탕으로, 공식문서 처럼 자동으로 마크다운을 생성하는 것이 가능하다. 이를 위해서는 IDE
에서 제공하는 내장된 JSDoc
기능이 아닌 실제 JSDoc
패키지가 필요하다.
npm i --save-dev jsdoc
jsdoc.config.json
파일을 생성하자. JSDoc
에 대한 환경 설정을 통해, 현재 코드의 JSDoc
정보를 보다 한 눈에 보기 쉽게 제공하는 정적 페이지를 자동 구축해준다.
이 프로젝트에 반영한 설정 사항은 다음과 같다.
{
"plugins": ["plugins/markdown"], // 마크다운 플러그인을 생성한다.
"source": {
"include": ["./dist"], // 반영시킬 jsdoc 코드의 경로를 정한다.
"includePattern": ".+\\.js(doc|x)?$", // 해당 경로내에 포함시킬 파일을 설정한다, (js 파일 모두)
},
"sourceType": "module",
"opts": {
"encoding": "utf8", // 인코딩 설정
"readme": "README.md" // 페이지 맨처음에 REAEME.md의 정보를 포함시킨다.
}
}
이후 jsdoc
커맨드를 실행하면, out
폴더 형태의 정적 페이지가 결과물로 나타난다. jsdoc
커맨드를 사용하기 위해, package.json
에서 커맨드를 생성하자.
jsdoc -c jsdoc.config.json
"scripts": {
"test": "tsc",
"build": "rollup -c",
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
"lint": "tslint -p tsconfig.json",
"jsdoc": "jsdoc -c jsdoc.config.json"
},
out
폴더 내부의 .html
파일을 브라우저에 띄우면 다음과 같은 페이지를 볼 수 있다.
한편, JSDoc
은 해당 페이지 외에도 다양한 형태의 템플릿이 존재한다. 나의 경우, docdash
템플릿을 적용시켰다. 템플릿 적용을 위해, 템플릿 패키지를 설치한다.
npm i --save-dev docdash
이후, jsdoc.config.json
의 opts
객체에 template
속성을 추가하자.
"opts": {
"template": "node_modules/docdash",
"encoding": "utf8",
"readme": "README.md"
}
이후 다시 jsdoc
커맨드를 실행하여 결과물을 생성하면 다음과 같은 페이지를 볼 수 있다.
프로젝트의 끝에는 결국 문서화 작업이 필요하고, 이 문서화 작업에서 매우 중대한 영향을 미치는 것이 README.md
라고 생각한다.
이 영역은 회사나 개인에 따라 작성 방식이 천차만별 일 것이다. 특별히 왈가왈부 할 이야기는 없지만, 소신 발언을 해보자면 리포지토리의 가장 처음을 담당하는 영역인 만큼, 본인이 기여한 부분이 있다면 뭐라도 작성하는 것이 좋다고 생각한다.
자 이렇게 직접 패키지를 만들어 본 경험과, NPM
패키지 개발 시 느낀 중요한 점들을 얘기해보았다. 패키지의 기능을 설명하기 보다는, 패키지를 만들 때 필요하다고 생각되는 설정을 중점적으로 이야기 했다. 실제 NPM 패키지를 만들려고 시도하는 개발자들에게 도움이 되는 내용이었으면 한다.
부족함이 많은 유효성 패키지지만 해당 패키지가 궁금하다면 한번 프로젝트에 적용해보시길 바란다. 사용하시는 김에 star
도 한번 꾸욱 눌러주시길.
npm i --save-dev @beberiche/validator
이런 유용한 정보를 나눠주셔서 감사합니다.