
AI와 함께 나의 첫 npm 패키지를 출시했습니다 — 그리고 그것은 이미 프로덕션에서 사용 중입니다
요약
프론트엔드 개발자가 Claude Code를 활용하여 React 날짜 선택기 패키지인 'daterly'를 개발하고 출시한 경험담입니다. 기존 라이브러리의 스타일링 및 react-hook-form 통합 문제를 해결하기 위해 AI 도구를 사용하여 효율적으로 개발한 과정을 다룹니다.
핵심 포인트
- Claude Code를 활용한 npm 패키지 개발 및 프로덕션 적용
- 기존 라이브러리의 스타일링 및 react-hook-form 통합 문제 해결
- AI가 개발 과정을 가속화하지만 한계점도 존재함을 명시
- 특정 기술 스택(RHF, date-fns)에 최적화된 도구 구축의 중요성
저는 약 3년의 경력을 가진 프론트엔드 개발자입니다. 몇 달 전까지만 해도 "npm 패키지 발행"은 저의 '언젠가 할 일' 목록에만 머물러 있었습니다. 실제 가지고 있는 것보다 빌드 도구(build tooling)와 더 깊은 관계를 맺어야만 가능할 것 같은 그런 종류의 일 말이죠. 그러다 제가 하나를 만들었습니다. 이름은 daterly이며, React 날짜 선택기(date picker)이고, 이미 제가 다니는 회사의 내부 프로젝트에서 실행되고 있습니다.
반전은 이렇습니다: 저는 그것의 대부분을 AI, 구체적으로는 Claude Code와 더 넓은 Claude 도구 세트를 사용하여 작성했습니다. 이것은 그 과정이 어떠했는지에 대한 솔직한 버전입니다. "AI가 하룻밤 사이에 내 스타트업을 만들었다"는 식의 이야기가 아니라, AI가 저를 어디까지 이끌어 주었는지, 어디서 저를 느리게 만들었는지, 그리고 이 경험에서 실제로 무엇을 남길 것인지에 대한 실제 기록입니다.
왜 또 다른 날짜 선택기를 만드는가
인터넷에는 또 다른 React 날짜 선택기가 필요하지 않습니다. 저도 압니다. 하지만 우리에게는 _이것_이 필요했습니다.
직장(sima-land)에서 우리는 어디에서나 react-hook-form에 의존합니다. 이것은 우리가 프로젝트 전반에서 폼(form)을 처리하는 방식의 중추입니다. 우리가 사용하던 날짜 선택기인 react-datepicker는 계속해서 그 흐름을 방해했습니다. 두 가지 사항이 우리를 매우 힘들게 했습니다:
- 스타일링(Styling)이 전쟁이었습니다. 우리의 디자인 시스템에 맞게 외관을 덮어쓰는 작업은 몇 개의 변수를 설정하는 것이 아니라, 항상 명시도(specificity)와 싸우는 것처럼 느껴졌습니다.
- react-hook-form 통합이 어색했습니다. 이를 RHF에 깔끔하게 연결하는 작업은 매번 필요 이상의 글루 코드(glue code)를 요구했습니다.
동일한 사소한 문제(papercut)가 프로젝트마다 반복해서 나타나면, 그것은 더 이상 사소한 문제가 아니게 됩니다. 우리는 의도적으로 하나의 기술 스택을 사용합니다. 즉, 우리 중 누구라도 기초를 다시 배우지 않고 프로젝트 사이를 이동할 수 있도록 공유되고 반복 가능한 방식으로 무언가를 구축하는 것입니다. 우리의 스타일링 방식과 폼 라이브러리 모두와 충돌하는 날짜 선택기는 그 일관성에 생긴 균열이었습니다.
따라서 진짜 동기는 "패키지를 출시하면 멋지겠지"가 아니었습니다. 그것은 바로: 우리의 스택을 유창하게 구사하는 단 하나의 도구를 원한다는 것이었습니다. 일류 수준의 RHF (react-hook-form) 지원, 어려움 없이 유연하게 적용되는 스타일링, 그리고 우리가 실제로 필요로 하는 로케일(locale)/포맷(format) 동작(러시아 시장: ru 로케일, 기본값 dd.MM.yyyy)이 필요했습니다. 이것이 제가 계속해서 되새기는 핵심입니다. 이 패키지는 npm 다운로드 수를 쫓기 위해서가 아니라, 일하는 방식을 보호하기 위해 존재합니다.
결과물은 무엇인가
daterly는 react-day-picker v9 (headless)와 date-fns v4를 기반으로 구축되었습니다. 주요 특징은 다음과 같습니다:
- 단일 날짜 및 범위 선택, 제어형(controlled) 또는 비제어형(uncontrolled) 방식 지원
- 사용자가 달력을 클릭하는 대신 날짜를 직접 입력할 수 있는 마스크 텍스트 입력(masked text input)
- props를 통한 임의의 로케일(locale) 및 포맷(format) 지원 — 입력 마스크는 전달된
dateFormat에 따라 스스로 재생성됩니다 - 선택 사항인 시간 선택 기능
- 별도의
daterly/rhf엔트리 포인트를 통한 오버헤드가 없는 react-hook-form 통합 — RHF를 사용하지 않는다면 그에 따른 비용(overhead)을 지불할 필요가 없습니다 --daterly-*CSS 변수와data-*상태 속성을 통한 스타일링, 따라서 JavaScript를 건드리지 않고도 테마를 지정할 수 있습니다
RHF 래퍼(wrapper)는 폼의 값 타입에 대해 제네릭(generic)하게 설계되어 있어, name 속성에 자동 완성 및 타입 안정성(type-safety)이 제공됩니다:
import { RHFDatePicker } from 'daterly/rhf';
<RHFDatePicker<BookingFormValues>
...
솔직히 말해서, 저 코드 조각이 이 프로젝트가 존재하는 이유의 전부입니다.
AI와 함께 구축하기: 좋은 점들
저는 시각적인 부분은 Claude Design을 사용하고, 거의 모든 작업은 Claude Code 내부에서 진행했습니다. AI가 진정으로 빛을 발한 부분은 까다롭고 독립적인 알고리즘들이었습니다. 직접 손으로 쓰기에는 번거롭지만, 명세(specification)를 정의하기는 쉬운 그런 부분들 말이죠.
**입력 마스크 (input mask)**가 가장 좋은 예시입니다. 우리는 별도의 마스킹 라이브러리를 가져오는 대신 숫자 전용 마스크를 직접 구현했는데, 여기에는 정말 미묘한 부분들이 있습니다:
- 매 키 입력(keystroke)마다 원시 숫자(raw digits)로부터 마스킹된 문자열을 다시 구축합니다.
- React가 입력을 다시 렌더링(re-render)한 후
requestAnimationFrame을 사용하여 커서 위치를 복구합니다. 그렇지 않으면 캐럿(caret)이 끝으로 튀어버려 타이핑이 끊기는 느낌을 줍니다. - 구분자(separator)에서 백스페이스(Backspace)를 누르면 해당 구분자를 건너뜁니다.
- 붙여넣기(paste) 시 마스킹을 하기 전에 숫자가 아닌 문자를 제거합니다.
- **왕복 검사 (round-trip check)**를 통해 유효성을 검사합니다:
format(parse(masked)) === masked. 이 방식은32.01.2024와 같은 사례를 우아하게 잡아냅니다. 파싱(parse)은 되지만, 다시 포맷팅(format)했을 때 동일한 문자열로 돌아오지 않으므로 유효하지 않음을 알 수 있습니다.
이 왕복 검사 트릭이야말로 AI가 제안하여 제가 "오, 이거 괜찮은데"라고 느끼게 만든 종류의 것이었습니다. 엣지 케이스(edge cases)를 평이한 언어로 설명하고 작동하는 첫 번째 초안을 돌려받는 것은 정말 큰 가속제였습니다. 보일러플레이트(boilerplate)가 많은 작업도 마찬가지였습니다. ESM/CJS 이중 빌드 설정, TypeScript 선언(declaration) 설정, 테스트 스캐폴딩(scaffolding) 등 말이죠. 처음부터 설정하기에는 지루하지만, 생성한 뒤 검토하기에는 매우 빠릅니다.

AI로 구축하기: 그리 좋지 않은 부분들
이것은 마법이 아니었으며, 그렇지 않은 척하는 것은 정직하지 못한 일일 것입니다.
한계와 지연. 저는 Pro 플랜을 사용 중이었지만, 작업 흐름이 올라타서 멈추고 싶지 않은 바로 그 순간에 사용량 제한에 여러 번 걸렸습니다. 게다가 컨텍스트(context)가 커지면 눈에 띄게 속도가 느려져 리듬이 깨졌습니다. 실제 프로젝트에서 AI에 의존할 계획이라면, 단순히 글을 쓰는 것뿐만 아니라 _기다림_과 _배분_에서 오는 마찰에 대해서도 예산을 고려해야 합니다.
AI는 승객이 아니라 운전자가 되어야 합니다. AI는 그럴듯한 코드 조각을 만들어내는 데 매우 뛰어납니다. 하지만 그 조각이 정확한지, 기존 패턴에 부합하는지, 그리고 세 파일 뒤에서 무언가를 조용히 재발명하고 있지는 않은지는 여전히 저의 몫입니다. 가장 가치 있는 기술은 프롬프팅 (prompting)이 아니었습니다. 그것은 바로 작성자를 신뢰하지 않는 코드 리뷰어처럼 모든 디프 (diff)를 읽어내는 능력이었습니다. 출력물을 평가할 수 없는 주니어 개발자였다면, 여기서 미묘하게 고장 난 동작을 배포했을 것입니다.

"내 컴퓨터에서는 되는데요"에서 실제 패키지로
이 부분은 제가 과소평가했던 부분이자, 건너뛰지 않아서 가장 다행이라고 생각하는 부분입니다. 작동하는 컴포넌트에서 설치 가능하고 유지보수 가능한 무언가로 넘어가는 것은 완전히 별개의 두 번째 프로젝트와 같습니다.
- tsup을 통해 빌드하며, 적절한 타입 선언 (type declarations)을 포함한 Dual ESM + CJS 출력 지원.
- **Changesets를 이용한 버전 관리 (versioning)**를 통해 모든 변경 사항이 의도적이며 변경 로그 (changelog)가 자동으로 작성되도록 함.
- CI, 코드 커버리지 (code coverage), 그리고 패키지 크기가 조용히 부풀어 오르지 않도록 하는 번들 크기 예산 (bundle-size budget) (
size-limit) 설정. - Playwright와 Vitest를 이용한 컴포넌트 테스트 (component tests) — 마스크 입력 (masked input)은 엣지 케이스 (edge cases)가 충분히 많기 때문에 반드시 확실히 잡아두어야 합니다.
이 중 어느 것도 화려하지는 않지만, 이것이 단순한 gist와 다른 사람들이 의존할 수 있는 패키지 사이의 차이점입니다.

0.4.0 사건
작고 매우 인간적인 교훈입니다. 초기에 Changesets CLI를 대화형으로 실행하다가 — 주의가 산만해진 나머지 — patch 대신 minor를 선택했습니다. 버전이 0.3.1 대신 0.4.0으로 뛰어버렸습니다. 이미 배포된 버전은 되돌릴 수 없습니다. 그냥 그 간극을 안고 살아가야 합니다.
그 해결책은 제가 이제 누구에게나 권장하는 프로세스의 변화였습니다. 바로 버전을 대화형(interactively)으로 올리지 마세요. 저는 프로젝트의 AI 지침서에도 이 내용을 적어두었습니다. changeset 파일을 수동으로 생성하고, 버전을 신중하게 결정한 뒤(patch는 수정 사항, minor는 하위 호환이 가능한 새로운 props, major는 파괴적 변경 사항), 그제야 배포하는 것입니다. 30초짜리 실수가 그 어떤 튜토리얼보다 저에게 릴리스 규율(release discipline)에 대해 더 많은 것을 가르쳐 주었습니다.

제가 실제로 얻은 교훈
만약 여러분이 저와 같은 상황에 처한 프론트엔드 개발자라면, 세 가지를 기억하세요.
- 스타(stars)를 위해서가 아니라, 여러분의 스택을 위해 만드세요. 라이브러리를 만드는 가장 좋은 이유는 매일 겪는 마찰(friction)을 제거하기 위해서입니다. daterly의 유일한 목적은 우리의 표준 스택인 react-hook-form, 우리의 스타일링 방식, 우리의 로케일(locale) 기본값들이 마치 네이티브처럼 느껴지게 만드는 것입니다. npm 배지(badge)는 부수적인 결과일 뿐입니다.
- AI는 지루한 부분을 압축할 뿐, 판단력을 대신하지 않습니다. AI는 많은 양의 정확한 코드를 빠르게 작성했습니다. 하지만 동시에 AI가 틀린 부분을 잡아내고, 제한된 사용량을 조절하며, 느린 순간들을 견뎌내는 과정이 필요했습니다. AI를 자동 조종 장치(autopilot)가 아닌, 빠른 주니어 페어(junior pair)로 대하세요.
- 패키징(packaging)이 진짜 작업입니다. 컴포넌트를 만드는 데는 며칠이 걸렸습니다. 하지만 빌드 설정(build config), 버전 관리(versioning), CI, 테스트, 그리고 문서화(docs)에는 더 오랜 시간이 걸렸습니다. 그리고 바로 그 작업들이 저 이외의 다른 사람들도 이 라이브러리를 사용할 수 있게 만들어 줍니다.
daterly는 MIT 라이선스이며 npm에서 확인할 수 있습니다. 코드와 문서는 GitHub에 있습니다. 만약 여러분이 데이트 피커(date picker)의 스타일링이나 RHF(react-hook-form) 통합 문제로 고군분투하고 있다면 한 번 살펴보세요. 만약 여러분이 직접 무언가를 만든다면 그것은 더 좋은 일입니다. 그것이 바로 핵심이니까요.
저는 또한 프리랜서 작업과 협업에도 열려 있습니다. 만약 여러분이 무언가를 만들고 있고 도움이 필요하거나, 제가 만든 다른 것들을 보고 싶다면 artemy.tech에서 모두 확인하실 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기