JSDoc은 JavaScript API 문서화 도구입니다. 이 도구는 개발자가 코드 가독성을 높이고, 협업 시 다른 개발자가 코드 의도를 쉽게 이해하는 데 도움이 되는 문서화 기능을 제공하죠. 10여 년 전, RESTful API 문서화 도구인 Swagger가 등장하면서 백엔드 진영에서는 JSDoc의 활용도가 낮아졌습니다.

JSDoc으로 만든 html 코드 문서 | 인포그랩 GitLab
JSDoc으로 만든 html 코드 문서

JSDoc은 JavaScript 코드의 문서화에 중점을 두는데요. 그렇다 보니 API 문서화에 특화된 기능이 부족합니다. 예를 들어, JSDoc을 사용하면 주석을 수동으로 추가해야 하는데요. 이는 API 스펙을 자동으로 생성하는 Swagger와 비교하면 아쉽죠. 이러한 이유로 백엔드 API 개발에서 Swagger가 선호되기도 합니다.

그래도 프론트엔드 진영에서 유용하게 쓸만한 JSDoc 기능은 여전히 많습니다. 지금도 저는 JSDoc을 실무에 활용하며 도움을 받고 있는데요. 이 글에서는 프론트엔드 진영에서 사용하기에 좋은 JSDoc 기능과 효과적인 활용 방법을 알아보겠습니다.

JSDoc 문법 살펴보기

JSDoc 기능을 설명하기에 앞서 이 도구의 문법부터 간단히 짚고 가겠습니다. JSDoc의 문법을 알아야 이 도구의 유용성을 잘 이해할 수 있기 때문입니다.

@태그 {타입} 변수명 - 설명

먼저 JSDoc에서는 위와 같이 @태그를 사용해 주석 영역을 구분합니다. 여기서 @태그블록 태그인라인 태그로 나뉘는데요. @link@tutorial 태그를 제외한 모든 태그는 블록 태그입니다. 블록 태그는 주석을 의미 있는 단위로 분리하며 영역을 차지하고요. 인라인 태그는 블록 태그 영역 안에서 텍스트와 같은 방식으로 배치되며 영역을 차지하지 않습니다.

아울러 JSDoc에서는 @태그 다음에

  • 타입{} 안에 입력합니다.
  • 이어 변수명설명을 순서대로 작성합니다.(태그별로, 상황별로 생략될 수 있습니다)

이제 JSDoc의 문법을 기억하며 프론트엔드에서 사용하기 좋은 이 도구의 기능을 자세히 살펴보겠습니다.

프론트엔드서 매력적인 JSDoc 기능

타입 힌트

JSDoc을 사용하면 주석을 토대로 인자의 타입 힌트를 쉽게 받을 수 있습니다. 그 방법을 살펴보겠습니다.

2024-11-06-js-doc-1 | 인포그랩 GitLab

TypeScript가 아닌 환경에서는 타입 정보를 명시적으로 확인할 수 없는데요. 위 예시처럼 첫 번째 인자(arg1)로 숫자(number)를 받고 두 번째 인자(arg2)로 문자열(string)을 받는 함수가 있다고 가정합시다. 현재 IDE에서는 타입을 유추할 수 없고, 매개변수명이나 함수명만 보고 ‘어떤 인자를 받아야 할지’ 알기 어려운데요(combineArgs(arg1: any, arg2: any): string).

2024-11-06-js-doc-2 | 인포그랩 GitLab

이때 JSDoc으로 주석을 달아 타입을 명시하면 타입 정보를 명확히 파악하는 데 도움이 됩니다. 대부분의 IDE에서는 /**를 입력하고 Enter를 누르면 JSDoc 주석이 자동으로 작성되는데요. 위 예시와 같이 @param 태그를 넣고 앞서 설명한 JSDoc 문법대로 주석을 작성합니다(@param {number} arg1 숫자를 넣어주세요, @param {string} arg2 문자열을 넣어주세요).

이어서 함수를 호출할 때 IDE에서 타입 힌트를 요청하니 앞서 작성한 주석을 토대로 각 인자의 타입 정보와 설명(combineArgs(arg1: number, arg2: string): string 숫자를 넣어주세요)이 제공되는데요. JSDoc을 사용하면 이러한 방식으로 인자의 타입 힌트를 받을 수 있고요. 이는 오류를 막는 데 도움이 됩니다.

타입 정의, 가져오기

JSDoc을 사용할 때 @typedef 태그로 타입을 정의하면, 타입을 코드 전반에 일관되고 쉽게 관리 및 사용할 수 있습니다. 이 정의는 하나의 파일에서 중앙 관리해야 유지 보수에 효과적입니다(@typedef의 문법은 JSDoc 공식 문서를 참조하세요).

예시와 함께 @typedef 태그를 활용한 타입 정의 방법과 유의 사항을 알아보겠습니다.

// types.jsx

/**
* 서버 타입입니다.
* @typedef {('server1' | 'server2' | 'server3')} Server
*/

/**
* @typedef {Object} Person
* @property {string} name - 사람의 이름
* @property {number} age - 사람의 나이
*/

export { Server, Person };

위 예시는 types.jsx 파일에서 @typedef 태그로 타입을 정의하는 방법을 보여줍니다.

// ServerButton.jsx

import React from "react";
import { Server } from "./types.jsx";

/**
* 서버 버튼을 생성하는 함수입니다.
* @param {Server} server - 서버 이름
*/
const ServerButton = (server) => {
return <button>{server}</button>;
};

export default ServerButton;
// AnotherSection.jsx

import ServerButton from "./ServerButton.jsx";
import { Person } from "./types.jsx";

/**
* @param { { person: Person } }
*/
const AnotherSection = ({ person }) => {
return (
<section>
<h2>유저 정보</h2>
<p>이름: {person.name}</p>
<p>나이: {person.age}</p>
<ServerButton server="server1" />
</section>
);
};

export default AnotherSection;

위 두 가지 예시는 컴포넌트 파일에서 Server, Person 타입을 각각 가져와(import) 사용하는 방법입니다. 이렇게 가져온 타입을 주석의 {}에 넣어 사용합니다(@param {Server} server - 서버 이름, @param { { person: Person } }).

Person 타입 힌트 | 인포그랩 GitLab
Person 타입 힌트

ServerButton 컴포넌트의 prop 타입 힌트 | 인포그랩 GitLab
ServerButton 컴포넌트의 prop 타입 힌트

참고로 JSDoc을 사용하면, 가져온 타입의 힌트도 위 예시와 같이 IDE에서 제공받을 수 있는데요. 이로써 함수 호출, 객체 사용 시 각 속성과 타입 정보를 미리 확인할 수 있고요. 코드를 편리하게 작성할 수 있습니다.

참고
  1. @typedef 태그로 정의된 모든 선언은 보통 자동으로 내보냅니다(export). 따라서 중복을 막으려면 한 곳에서 타입을 정의하고 관리하는 게 좋습니다. 특히 @typedef 태그는 덮어쓰기 할 수 있으므로 의도치 않게 기존 타입을 재정의하지 않도록 주의해야 합니다.
  2. 프레임워크마다 지원하는 타입 가져오기 방식은 다릅니다. 이에 @typedef 태그로 정의된 타입을 가져와 사용하는 방법을 프레임워크별로 학습해야 합니다.
  3. JSDoc 타입은 타입 강제화를 할 수 없어 컴파일 시점에 타입 오류를 잡지 못합니다. 따라서 타입을 꼼꼼히 체크하고, 코드 리뷰나 테스트를 수행해 예상치 못한 타입 오류가 발생하지 않도록 유의해야 합니다.

함수 정보 제시

JSDoc을 사용하면 다양한 태그로 함수의 사용 목적, 설명, 반환 값, 예시 등을 작성할 수 있습니다. 이로써 함수 정보를 풍부하게 전달할 수 있죠.

예를 들어, @returns 태그로 반환 값을 설명하거나, @example 태그로 함수 사용 예시를 추가할 수 있는데요. 이러한 정보는 VS Code나 Cursor와 같은 IDE에서 자동으로 해석돼 함수 세부 사항을 직관적으로, 보기 좋게 제시합니다.

/**
* 숫자와 문자열을 합쳐주는 함수입니다.
* @param {number} arg1 숫자를 넣어주세요.
* @param {string} arg2 문자열을 넣어주세요.
* @returns {string} 숫자와 문자열을 합친 결과를 반환합니다.
* @example
* combineArgs(2, '배') // '2배'
* combineArgs(3, '킬로미터') // '3킬로미터'
*/
const combineArgs = (arg1, arg2) => {...}
2024-11-06-js-doc-5 | 인포그랩 GitLab

위 두 가지 예시는 JSDoc 태그를 코드(위)와 IDE 화면(아래)으로 각각 보여주는데요. 특히 IDE에서 함수 위에 마우스를 올리거나 호출하면, 함수 설명과 예시, 반환 값 정보를 한눈에 확인할 수 있죠. 이로써 다른 개발자가 코드를 쉽게 읽고, 코드 의도를 명확히 이해할 수 있습니다.

정보 연결, 구분

JSDoc에서는 @link 태그로 다양한 정보를 효과적으로 연결할 수 있습니다. @link 태그를 사용하면 코드 활용 예제를 넣거나, 외부 리소스와 연결해 함수나 UI 요소의 시각 정보를 제공할 수 있죠. @link로 스토리북, Figma, 노션 링크를 추가하면 해당 요소의 UI나 디자인 스펙을 외부에서 바로 확인할 수 있습니다.

관련 문법을 살펴보겠습니다.

{@link URL 변수명}

위 예시와 같이 텍스트 사이에 중괄호 {}를 열고 그 안에 @link 태그와 URL , 변수명을 순서대로 넣습니다. 변수명이 파란색으로 표시되면 이를 클릭해 해당 url로 이동할 수 있습니다(자세한 내용은 JSDoc 공식 문서를 참조하세요).

@link는 인라인 태그로 별도 영역을 차지하지 않는데요. 따라서 다른 블록 태그와 함께 사용하며 내용을 구분해야 합니다. 효과적인 @link 태그 활용 방법을 살펴보겠습니다.

/**
* 서버 버튼을 생성하는 함수입니다.
* @param {Server} server - 서버 이름
* 예제는 {@link https://github.com 여기서} 확인할 수 있습니다.
*/
const ServerButton = (server) => {
return <button>{server}</button>;
};

export default ServerButton;
2024-11-06-js-doc-6 | 인포그랩 GitLab

위 두 가지 예시는 블록 태그로 아직 구분하지 않은 주석 코드(위)와 해당 코드의 IDE 화면(아래)입니다. @link 태그가 @param 블록 안에 들어가 매개변수 설명의 일부처럼 보여 혼란스러운데요.

이때 @see 태그*를 함께 사용하면 @param 태그와 구분돼 주석을 정확히 이해할 수 있습니다.

/**
* 서버 버튼을 생성하는 함수입니다.
* @param {Server} server - 서버 이름
* @see 예제는 {@link https://github.com 여기서} 확인할 수 있습니다.
*/
const ServerButton = (server) => {
return <button>{server}</button>;
};

export default ServerButton;
2024-11-06-js-doc-7 | 인포그랩 GitLab

위 두 가지 예시는 @see 태그로 @param 태그와 분리한 주석 코드(위)와 해당 코드의 IDE 화면(아래)입니다. 각 태그와 관련 정보가 직관적으로 구분돼 주석 내용을 명확히 파악할 수 있습니다.

*@see 태그: 외부 참조 문서나 내부 심볼 요소에 링크를 연결할 때 사용하는 독립적인 블록 태그

유용한 태그 모음

이밖에 JSDoc에서 태그를 적재적소에 활용하면 컴포넌트 사용 방법을 효과적으로 제시하고, 컴포넌트 버전도 투명하게 추적할 수 있는데요. 아래는 실무에 유용한 JSDoc 태그입니다.

  • @deprecated: 나중에 제거할 함수나 컴포넌트를 명확히 표시합니다. 이 태그를 추가하면 전체 코드에서 해당 컴포넌트를 쉽게 검색해 한 번에 삭제할 수 있죠. 또 제거 이유나 삭제 예정일을 덧붙여 추후 변경에 대비할 수 있습니다.
  • @example: 컴포넌트 사용 방법을 분명하게 제시합니다. 주석에 예제를 제공해 개발자가 컴포넌트 용도와 사용법을 바로 이해할 수 있습니다.
  • @todo: 다음 작업에 가져가야 할 사항을 표시합니다. 특히 VS Code의 ‘Todo Tree 확장 프로그램’과 함께 사용하면 편리하게 관리할 수 있습니다.
  • @version: 컴포넌트의 버전을 추적합니다. 이 태그를 사용하면 ‘단순 수정인지, 대규모 변경인지’ 쉽게 파악할 수 있습니다. 이로써 변경 내용을 원활히 관리하고 편리하게 유지 보수할 수 있습니다.

여러분이 TypeScript를 사용 중이라면, 지원하지 않는 JSDoc 태그가 있을 수 있는데요. TypeScript 공식 문서를 참조해 지원 태그와 제한 사항을 확인하시길 권합니다.

JSDoc 활용 예시

/**
* @description Enter key의 동작은 막고, 다음 input 요소로 포커싱을 이동합니다.
* @param formRef - 컨트롤 하고자하는 form의 ref를 넣어주세요.
* @function `onEnterKey` - 제공한 ref내에 동작을 원하는 input 요소의 onKeyDown속성에 넣어주세요.
*/
const useFocusNextInputOnEnter = (formRef: React.RefObject<HTMLFormElement>) => {
const onEnterKey = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter' && formRef.current) {
event.preventDefault()
const formEl = formRef.current // form 요소 찾기
const inputElList = Array.from(formEl.elements) // 폼 내의 모든 입력 요소 찾고, 배열로 만들기
const targetInputIndex = inputElList.indexOf(event.target as HTMLElement) // 이벤트가 발생한 입력 요소의 인덱스
const nextInputEl = formEl.elements[targetInputIndex + 1] as HTMLElement
if (nextInputEl) {
nextInputEl.focus()
}
}
}

return { onEnterKey }
}

export default useFocusNextInputOnEnter
2024-11-06-js-doc-8 | 인포그랩 GitLab

위 두 가지 예시는 제가 사용한 JSDoc 주석 코드(위)와 해당 코드의 IDE 화면에서 본 hook 정보(아래)입니다. 제 프로젝트는 TypeScript를 사용했는데요. 프로젝트에서 반복 사용하는 함수의 주석을 상세히 작성하면 다른 개발자가 기능을 쉽게 이해하고 사용할 걸로 기대하고 JSDoc을 활용했습니다.

참고로 IDE에서 주석에 코드 블록을 넣고 싶으면 백틱을 사용하면 됩니다(@param의 변수명은 자동으로 코드 블록 처리됩니다).

맺음말

JSDoc은 잘 활용하면 지금도 훌륭한 문서화 도구이자 커뮤니케이션 도구가 될 수 있습니다. 이 도구는 다양한 태그와 기능으로 사용 방법, 의도, 유지 보수 정보를 효과적으로 전달해 협업에 유용합니다. 저는 JSDoc을 사용해 코드를 직접 열어보지 않아도 주석만으로 hook이나 컴포넌트를 이해하고 활용하도록 만들고자 하는데요. 이 목표를 달성하거나, 새로운 JSDoc 모범 사례를 발견하면 또 공유하겠습니다. 감사합니다.

참고 자료

  1. JSDoc 공식 문서 사이트, https://jsdoc.app/
  2. Typescript 공식 문서 사이트, https://www.typescriptlang.org/ko/docs/handbook/2/basic-types.html

DevOps와 GitLab을 효과적으로 도입하는 방법, 지금 인포그랩에 문의하세요.