들어가며
반복되는 코드를 공용 유틸로 분리해도 비슷한 함수가 또 다시 생기곤 합니다. 함수를 만든 사람은 어디에 무엇이 있는지 알지만, 다른 개발자, 그리고 가끔씩 본인도 비슷한 동작을 하는 함수를 만들곤 합니다.
이번 글에서는 ESLint를 사용하여 유틸의 재사용성을 높이는 방법을 살펴보겠습니다.
1. 재사용하려면 먼저 발견해야 한다
1.1 여러 화면에서 반복되는 날짜 표시
서비스에서 목록의 생성일, 상세 화면의 수정일, 알림의 발생 시각처럼 날짜를 표시하는 곳은 많습니다.
이때 여러 화면에서 날짜를 다음 기준으로 표시해야 한다고 가정해보겠습니다.
- 기본 형식: 날짜는 2026.10.12처럼 표시한다.
- 기본 로케일: 요일과 월 이름은 한국어로 표시한다.
- 빈 값과 잘못된 날짜: 날짜를 표시할 수 없으면 ‘-’를 보여 준다.
- 화면별 차이: 날짜에 시각을 함께 표시하거나, 영어 요일이 필요하면 형식과 로케일을 지정한다.
날짜 형식을 바꾸는 데는 라이브러리를 활용할 수 있습니다. 여러 라이브러리 중 여기서는 date-fns를 예로 들어보겠습니다.
화면 내부에서 날짜와 표시 옵션을 받아 처리하면 다음과 같습니다.
import { format, isValid } from 'date-fns';
import type { FormatOptions } from 'date-fns';
import { enUS, ko } from 'date-fns/locale';
function getDateLabel(
date: Date | null | undefined,
pattern = 'yyyy.MM.dd',
options: FormatOptions = {}
): string {
if (date == null || !isValid(date)) return '-';
return format(date, pattern, { locale: ko, ...options });
}
const date = new Date(2026, 9, 12, 14, 30);
getDateLabel(date); // '2026.10.12'
getDateLabel(date, 'yyyy.MM.dd HH:mm'); // '2026.10.12 14:30'
getDateLabel(date, 'EEEE', { locale: enUS }); // 'Monday'이 처리가 여러 화면에 반복된다면, 기본 형식과 로케일, 날짜를 표시할 수 없을 때의 처리를 formatDate라는 공용 함수로 모을 수 있습니다. 화면에서는 날짜만 전달하고, 다른 형식이나 옵션이 필요할 때 추가로 지정하도록 만드는 것입니다.
1.2 공용 함수의 존재를 알릴 계기가 필요하다
공용 함수를 만들어 두어도, 다른 개발자는 그 함수가 있는지 모를 수 있습니다.
새 화면에서 날짜를 표시할 때 익숙한 date-fns의 format을 가져와 쓰다 보면, formatDate에 모아 둔 처리를 다시 작성하게 될 수 있습니다.
문서나 리뷰로 안내할 수도 있지만, 각각 한계가 있습니다.
| 안내 방법 | 발견으로 이어지기 어려운 이유 |
|---|---|
| 사용법 문서 | 코드 변경에 맞춰 문서도 갱신해야 하고, 개발자가 작업 중에 따로 찾아 읽어야 합니다. |
| 기존 사용 코드 | 참고할 수는 있지만, 개발자가 그 코드를 먼저 찾아야 합니다. |
| 코드 리뷰 | 안내받는 시점에는 이미 직접 구현을 마친 뒤입니다. |
그래서 안내하는 시점을 코드 작성 중으로 옮겨보려 합니다.
date-fns의
format을 import하는 순간, 공용formatDate의 이름과 경로를 알려 주자.
이 역할을 ESLint의 제한 규칙에 맡길 수 있습니다.
2. ESLint가 대안을 안내하게 만들기
앞서 정한 안내를 만들려면 라이브러리 함수를 직접 가져오는 코드를 검사하고, 진단 메시지에 대안을 담으면 됩니다. ESLint의 no-restricted-imports는 특정 경로나 이름의 import를 제한하면서 사용자 메시지를 덧붙일 수 있는 기본 규칙입니다.
2.1 검사할 대상과 안내 메시지 설정하기
date-fns에서 제공하는 format을 가져오면, 프로젝트의 공용 함수인 formatDate를 안내하도록 설정합니다.
이 동작을 만들기 위해 ESLint에 다음 내용을 알려 줍니다.
- 검사할 코드: date-fns의
format을 가져오는 코드를 찾습니다. 패키지에서 가져오는 방식과 개별 함수 경로로 가져오는 방식 모두 포함합니다. - 허용할 코드: 날짜 계산이나 파싱에 쓰는 다른 함수는 계속 사용할 수 있게 합니다. 라이브러리 전체의 사용을 막지는 않습니다.
- 보여 줄 안내: 직접 가져온
format에 오류를 표시하고, “날짜 표시는 @/shared/lib의 formatDate를 사용하세요”라는 메시지로 대안을 알려 줍니다.
검사할 파일의 범위도 함께 정합니다. 여기서는 src 아래의 TypeScript 파일을 검사하되, date-fns의 format을 사용해야 하는 공용 구현과 해당 테스트는 제외합니다. 기존 코드를 한 번에 옮기기 어렵다면 이전한 영역부터 적용할 수도 있습니다.
eslint.config.js 설정 예시
기존 ESLint flat config에 추가할 내용입니다. TypeScript 파서 등 나머지 ESLint 설정은 생략했습니다.
// 위반 위치에서 공용 함수의 이름과 import 경로를 안내합니다.
const formatMessage =
'날짜 표시는 기본 형식과 빈 값 처리가 적용된 @/shared/lib의 formatDate를 사용하세요.';
export default [
// TypeScript 파서 등 기존 ESLint 설정은 생략했습니다.
{
// src 아래의 TypeScript 파일에 적용합니다.
files: ['src/**/*.{ts,tsx}'],
// date-fns의 format을 사용하는 공용 구현과 해당 테스트는 제외합니다.
ignores: ['src/shared/lib/format-date.ts', 'src/shared/lib/format-date.test.ts'],
rules: {
'no-restricted-imports': [
'error', // 제한한 import를 오류로 보고합니다.
{
// 제한할 모듈 경로와 해당 경로의 안내 메시지를 지정합니다.
paths: [
{
name: 'date-fns',
// format만 제한하고 addDays, parseISO 등 다른 함수는 허용합니다.
importNames: ['format'],
message: formatMessage,
},
{
// 개별 함수 경로로 가져오는 경우도 제한합니다.
name: 'date-fns/format',
message: formatMessage,
},
],
},
],
},
},
];여기서 ignores는 이 설정 객체만 적용하지 않도록 하므로, 다른 설정 객체의 검사는 계속 적용됩니다. 이미 같은 규칙을 사용 중이라면 기존 paths나 patterns에 항목을 추가해 기존 제한을 유지해야 합니다.
이 예시는 명시한 두 경로의 정적 import를 검사합니다. 다른 별칭 export나 진입점, 동적 import()까지 검사하는 설정은 아니므로 프로젝트의 실제 사용 방식에 맞춰 보강해야 합니다.
2.2 진단 메시지에서 공용 함수로 연결하기
설정을 적용하면, 각 import는 다음과 같이 처리됩니다.
| import 방식 | 처리 |
|---|---|
import { format } from 'date-fns' | 공용 formatDate 안내 |
import { format } from 'date-fns/format' | 공용 formatDate 안내 |
import { addDays, parseISO } from 'date-fns' | 허용 |
import { formatDate } from '@/shared/lib' | 허용 |
이 중 date-fns에서 format을 가져오는 경우를 살펴보겠습니다.
import { format } from 'date-fns';ESLint가 연동된 VS Code에서는 해당 import에 오류가 표시됩니다. format에 마우스를 올리면 설정한 안내 메시지를 확인할 수 있습니다.

ESLint 진단 메시지에 공용 함수의 이름과 import 경로가 표시됩니다.
이 메시지에는 제한한 이유와 함께 대신 사용할 함수 이름과 import 경로가 들어 있습니다. 공용 함수의 존재를 미리 몰랐더라도 안내받은 경로에서 함수를 가져와 사용할 수 있습니다.
import { formatDate } from '@/shared/lib';
formatDate(date);
formatDate(date, 'yyyy.MM.dd HH:mm');함수를 따로 찾아보거나 리뷰를 기다리는 대신, date-fns의 format을 가져오려던 시점에 프로젝트의 사용 방법을 확인하는 흐름입니다. CLI 검사에서도 같은 안내를 받을 수 있습니다.
3. 팀의 다른 개발 규칙에도 활용하기
같은 방식으로 이미 정해진 개발 규칙을 코드를 작성하는 시점에 안내할 수 있습니다.
| 상황 | 제한할 사용 | 안내할 대안 |
|---|---|---|
| 라이브러리 교체 | 이전 라이브러리 import | 대체 라이브러리와 마이그레이션 문서 |
| 디자인 시스템 사용 | 외부 UI 컴포넌트 직접 import | 팀에서 제공하는 컴포넌트 |
| 모듈 경계 유지 | 다른 모듈의 내부 파일 import | 해당 모듈의 Public API |
특정 모듈이나 import 이름은 paths로, 경로 패턴은 patterns로 제한할 수 있습니다. 적용할 때는 디자인 시스템의 구현 파일처럼 외부 라이브러리를 직접 사용해야 하는 곳이나, 모듈 내부 참조가 허용되는 범위를 예외로 정해야 합니다.
4. 제한할 때 대안도 함께 알려 주기
메시지가 “직접 사용하지 마세요”로 끝나면 개발자는 무엇을 써야 하는지 다시 찾아봐야 합니다. 대신 사용할 함수나 모듈의 경로를 적고, 이유가 필요하면 짧은 설명이나 문서 위치도 덧붙이는 편이 좋습니다.
다만 제한에 앞서 다음을 확인해야 합니다.
- 대체 가능성: 공용 함수가 제한하려는 사용 사례를 지원하는가?
- 적용 범위: 라이브러리 함수를 직접 사용해야 하는 구현과 예외가 정해져 있는가?
- 검사 결과: 제한할 코드에서는 진단이 나오고, 허용할 코드는 통과하는가?
대체할 수 없는 코드를 일괄 제한하면 예외와 규칙 비활성화가 늘어날 수 있습니다. 리뷰에서 같은 함수나 import 경로를 반복해서 안내하고 있다면, 공용 함수가 필요한 사용 사례를 지원하는지 확인한 뒤 그 안내를 ESLint 메시지에 담아볼 수 있습니다.
마치며
공용 유틸이 있어도 개발자가 그 존재를 모르면 같은 처리가 다시 구현될 수 있습니다.
ESLint의 no-restricted-imports를 활용하면 라이브러리나 함수를 가져오는 시점에 프로젝트에서 제공하는 대안을 안내할 수 있습니다. 문서나 코드 리뷰에서 전달하던 내용을 코드 작성 중에도 확인할 수 있게 됩니다.
제한 메시지에 대안 함수와 경로를 함께 적어 두면, 개발자가 기존 구현을 발견하고 재사용하는 데 도움이 됩니다. 팀에서 반복해서 안내하는 개발 규칙이 있다면, 적용 대상과 대안을 정해 ESLint 메시지로 전달해 볼 수 있습니다.