기타 유용한 라이브러리
UI·HTTP·폼·날짜·유틸리티·차트 요구사항별 대표 라이브러리를 비교하고 직접 구현할지 도입할지 판단합니다.
앞 글들에서는 React의 기초와 상태 관리 라이브러리로 복잡한 데이터 흐름을 다루는 방법을 익혔습니다. 하지만 실제 애플리케이션을 만들다 보면 UI 컴포넌트, HTTP 요청, 폼, 날짜 처리, 차트처럼 여러 프로젝트에서 반복되는 기능이 필요합니다. 이런 기능을 매번 처음부터 만들면 시간이 오래 걸리고 버그가 생기기도 쉽습니다.
이미 많은 개발자가 겪은 문제를 풀어 둔 코드가 패키지로 제공되므로, 알맞은 라이브러리를 쓰면 핵심 기능에 더 집중할 수 있습니다. 이 글에서는 프런트엔드에서 자주 쓰는 라이브러리를 분야별로 소개하고, 각각이 푸는 문제와 고를 때 확인할 점을 정리합니다. 마지막에는 라이브러리를 들일지 판단하는 기준도 살펴봅니다.
UI 컴포넌트 라이브러리
버튼, 입력란, 모달, 드롭다운 같은 UI 컴포넌트의 스타일과 동작을 모두 직접 만드는 데는 많은 시간이 듭니다. UI 컴포넌트 라이브러리는 미리 디자인된 컴포넌트와 상호작용 API를 제공해, 일관된 화면을 빠르게 만들게 도와줍니다. 다만 라이브러리를 쓴다고 접근성이 저절로 갖춰지지는 않으므로, 레이블과 오류 안내, 포커스 이동은 실제 화면에서 확인해야 합니다.
MUI(Material UI)는 Google의 Material Design 지침을 바탕으로 한 React 컴포넌트와 테마 API를 제공합니다. Ant Design은 폼과 테이블처럼 업무용 화면에 많이 쓰는 컴포넌트와 디자인 토큰을 제공하고, Chakra UI는 컴포넌트에 스타일 속성(style props)을 바로 넘기는 방식과 테마 구성을 씁니다. React-Bootstrap은 CSS 프레임워크 활용에서 본 Bootstrap의 CSS 규칙과 컴포넌트를 React에서 쓰게 해 줍니다.
고를 때는 필요한 상호작용 API가 있는지, 키보드와 스크린 리더로 조작하는 흐름이 요구 사항대로 동작하는지 공식 문서와 작은 시험 화면(prototype)으로 확인합니다. 라이브러리의 초기화 CSS나 전역 스타일, 테마가 이미 쓰는 디자인 토큰과 충돌하지 않는지도 함께 봅니다.
HTTP 클라이언트와 서버 상태 라이브러리
Fetch API와 AJAX에서 fetch()로 서버와 통신하는 방법을 배웠습니다. 모든 요청에 공통으로 헤더를 붙이거나, 응답을 공통으로 처리하거나, 시간 제한을 두는 일이 많아지면 전용 HTTP 클라이언트가 편리합니다.
Axios는 Promise 기반의 HTTP 클라이언트로, 브라우저와 Node.js에서 모두 쓸 수 있습니다. 요청과 응답을 가로채 공통 처리를 하는 인터셉터(interceptor), 요청 취소, JSON 자동 변환, 응답 시간 제한, 업로드 진행률 같은 기능을 제공합니다. 요청 방법과 Fetch API와의 차이는 React 교재의 axios 라이브러리 소개에서 자세히 비교합니다.
import axios from 'axios';
axios.get('/api/users')
.then(response => console.log(response.data))
.catch(error => console.error(error));
// POST 요청
axios.post('/api/users', { name: 'Alice', age: 30 })
.then(response => console.log(response.data))
.catch(error => console.error(error));서버의 데이터를 여러 화면에서 함께 쓰고 최신으로 유지해야 한다면, 요청을 보내는 것보다 캐시를 관리하는 일이 더 중요해집니다. SWR과 TanStack Query(예전 React Query)는 서버 상태의 캐시와 다시 확인하기(revalidation), 오류와 재시도를 React 컴포넌트에서 관리하게 해 줍니다. SWR은 useSWR Hook으로 데이터를 가져와 캐시된 값을 먼저 보여 준 뒤 백그라운드에서 새 데이터를 확인하는 방식(stale-while-revalidate)을 쓰고, TanStack Query는 쿼리 키(query key)를 기준으로 캐시와 무효화, 재시도, 상태 변화를 구성합니다. 쓰기 전에 캐시를 얼마나 유지할지, 언제 다시 확인하고 재시도할지, SSR과 어떻게 연결하고 오류를 어디서 처리할지를 먼저 정합니다.
고를 때는 요청만 보내면 되는지, 공통 인터셉터와 취소 규칙이 필요한지, 여러 화면이 서버 상태의 캐시를 함께 써야 하는지를 나눠 봅니다.
요청 전송과 서버 상태 캐시 구분하기
상품 상세 화면이 /api/products/7을 한 번 요청하는 경우부터 시작해 봅니다. fetch()로도 JSON을 읽을 수 있지만, HTTP 오류 상태와 네트워크 실패는 다르게 처리해야 합니다.
async function loadProduct() {
const response = await fetch('/api/products/7');
if (!response.ok) {
throw new Error(`상품 요청 실패: HTTP ${response.status}`);
}
return response.json();
}서버가 404를 응답하면 fetch()는 Response를 돌려주므로 ok를 직접 검사합니다. 네트워크 실패는 fetch()의 Promise가 거부되는 것으로, JSON 본문이 잘못된 경우는 response.json()이 실패하는 것으로 처리됩니다(MDN의 Fetch API 사용하기).
Axios는 기본 설정에서 2xx 범위 밖의 HTTP 상태를 거부하며, validateStatus 옵션으로 이 규칙을 바꿀 수 있습니다(Axios의 요청 설정 문서). 다만 요청 도구를 바꾼다고 여러 화면의 응답 캐시나 다시 확인하는 규칙이 저절로 생기지는 않습니다. 상품 목록과 상세가 같은 서버 데이터를 함께 쓰고, 저장한 뒤 갱신해야 한다면 SWR이나 TanStack Query의 캐시 키와 무효화 규칙을 검토합니다.
재시도할 때는 요청의 의미도 살펴야 합니다. 조회 요청과 결제를 만드는 요청은 같지 않습니다. 결제 응답을 받지 못했다고 바로 다시 보내면 결제가 두 번 처리될 수 있으므로, 서버가 중복을 막는 방법과 재시도 규칙을 함께 정합니다.
폼 관리 라이브러리
폼은 웹 애플리케이션에서 아주 흔하지만, 입력값 관리, 유효성 검사, 오류 메시지 표시가 겹치면 코드가 생각보다 복잡해집니다. 폼 관리 라이브러리는 이 과정을 간단하게 해 줍니다.
React Hook Form은 React가 값을 일일이 관리하지 않는 비제어 컴포넌트(uncontrolled component)를 중심으로, 입력을 등록하고 제출하고 오류 상태를 다루는 API를 제공합니다. Yup이나 Zod 같은 스키마 검증 라이브러리와 연결할 수도 있습니다. React Hook Form과 Yup으로 회원가입 폼 전체를 만드는 과정은 React 교재의 회원가입 폼 만들기에서 다룹니다.
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email", { required: "이메일은 필수입니다." })} />
{errors.email && <p>{errors.email.message}</p>}
<button type="submit">제출</button>
</form>
);
}register("email", ...)가 입력란을 폼에 등록하고, handleSubmit()은 검사를 통과했을 때만 onSubmit을 호출합니다. 이 예제의 required는 빈 값만 검사하므로, 이메일 형식이 맞는지, 서버에 이미 있는 주소인지, 입력란의 레이블과 오류 메시지가 연결되어 있는지는 따로 구성해야 합니다. 브라우저에서 검사를 통과한 요청도 서버가 다시 검증해야 합니다.
Formik은 값, 입력란을 거쳤는지(touched), 오류, 제출 상태를 명시적인 폼 상태로 관리합니다. 고를 때는 제어 입력과 비제어 입력 가운데 어느 방식을 쓸지, 조건에 따라 나타나는 입력란과 검증이 얼마나 복잡한지, 팀이 유지할 상태 모델을 먼저 정하고, 실제 폼에서 렌더 횟수와 번들 크기를 측정합니다.
날짜와 시간 라이브러리
자바스크립트에 내장된 Date 객체는 날짜 계산과 표시에 한계가 있습니다. 하지만 날짜 형식 맞추기, 문자열 해석, 시간대(time zone) 처리, 날짜 사이의 계산은 필요한 범위가 서로 다르므로, 먼저 Intl.DateTimeFormat 같은 내장 API로 충분한지 확인하고 필요할 때 전용 라이브러리를 검토합니다.
date-fns는 넘겨받은 Date를 바꾸지 않는 함수로 날짜를 계산하며, Date 객체에 메서드를 덧붙이지 않습니다.
import { format, addDays, differenceInDays } from 'date-fns';
const today = new Date();
const formattedDate = format(today, 'yyyy년 MM월 dd일'); // 예: '2026년 11월 01일' (실행한 날짜에 따라 다름)
const nextWeek = addDays(today, 7); // 7일 뒤의 날짜
const daysDiff = differenceInDays(nextWeek, today); // 7필요한 함수만 import하므로, 빌드 도구가 쓰지 않는 함수를 빼 번들을 줄일 수 있습니다. Moment.js는 값을 바꿀 수 있는(mutable) 날짜 객체 API를 제공하며, 시간대 처리는 Moment Timezone을 따로 구성합니다. 공식 문서가 Moment.js를 유지보수 모드의 레거시 프로젝트로 안내하므로 새 프로젝트의 기본 선택으로 쓰기 전에 다른 라이브러리를 검토하고, 이미 쓰는 시스템이라면 필요한 지원 범위와 교체 비용을 살핍니다(Moment.js 공식 문서). Day.js는 Moment.js와 비슷한 API와 플러그인 구성을 제공하므로, 필요한 지역(locale)과 시간대 플러그인이 어떻게 동작하고 번들에 무엇이 들어가는지 확인합니다.
고를 때는 값을 바꿀 수 있는지, 어느 지역과 시간대를 지원해야 하는지, 어떤 형식의 문자열을 해석해야 하는지, 실행 환경이 어디인지를 먼저 정한 뒤 공식 문서와 실제 번들 결과를 비교합니다.
더 깊이: Date를 대신할 표준 Temporal
날짜 라이브러리가 많이 쓰인 데는 Date 자체의 한계가 있습니다. Date의 setter는 모두 객체를 그 자리에서 바꾸고, 날짜 문자열의 형식은 일관되게 해석하기 어려우며, 시간대는 UTC와 기기의 지역 시간 두 가지만 다룰 수 있습니다. 시간대가 없는 달력 날짜나 매일 8시 같은 벽시계 시각을 나타낼 방법도 없습니다. 이 문제를 언어 차원에서 풀려고 만든 것이 Temporal로, MDN은 이를 Date를 완전히 대신하도록 설계된 전역 이름 공간이라고 설명합니다.
Temporal은 쓰임마다 타입을 나눕니다. 시간대와 상관없는 한 순간은 Temporal.Instant, 특정 시간대의 날짜와 시각은 Temporal.ZonedDateTime, 시간대 없는 날짜와 시각은 Temporal.PlainDate, Temporal.PlainTime, Temporal.PlainDateTime, 두 시점의 차이는 Temporal.Duration이 맡습니다. add()나 with() 같은 메서드는 원래 객체를 바꾸지 않고 새 객체를 돌려주므로, date-fns의 불변 방식과 비슷하게 쓸 수 있습니다. 문자열은 ISO 8601을 바탕으로 시간대 이름까지 적는 RFC 9557 형식으로 주고받으며, 2025-03-15T09:00[Europe/Paris]처럼 IANA 시간대 이름을 직접 쓸 수 있습니다.
TC39의 완료된 제안 목록에 따르면 Temporal은 4단계를 마쳐 ECMAScript 2027에 실릴 예정입니다. 하지만 MDN의 Temporal 설명은 아직 널리 쓰이는 일부 브라우저가 지원하지 않는 기능으로 분류하므로, 지금 브라우저에서 쓰려면 지원 여부를 확인하고 필요하면 제안 작성자들이 만든 @js-temporal/polyfill 같은 폴리필을 함께 씁니다.
유틸리티 라이브러리
배열, 객체, 문자열, 함수를 다루는 범용 함수를 모아 둔 라이브러리도 있습니다. Lodash는 명령형과 함수형 스타일의 컬렉션과 객체 함수를 제공하고, Ramda는 순수한 함수형 프로그래밍에 초점을 맞춰 데이터를 마지막 인수로 받는 커리(curried) 함수를 제공합니다.
import _ from 'lodash'; // 또는 import { debounce, sortBy } from 'lodash-es';
const users = [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }];
const sortedUsers = _.sortBy(users, ['user']); // 사용자 이름으로 정렬
console.log(sortedUsers);
const debouncedFunction = _.debounce(() => {
console.log('디바운스된 함수 실행');
}, 500); // 마지막 호출 뒤 0.5초 동안 다시 호출되지 않으면 한 번 실행debounce()는 검색어 입력처럼 짧은 시간에 여러 번 일어나는 호출을 묶어, 입력이 멈춘 뒤 한 번만 실행하게 합니다. 지금의 자바스크립트에는 Array.prototype.toSorted()나 structuredClone()처럼 예전에 라이브러리로 하던 일을 하는 내장 기능도 많으므로, 먼저 내장 기능으로 충분한지 봅니다. 라이브러리를 쓴다면 실제로 반복을 줄여 주는 함수만 들이고, 위 주석의 lodash-es처럼 필요한 함수만 가져오는 형태로 써서 빌드 결과에 무엇이 들어가는지 확인합니다.
차트 라이브러리
대시보드나 통계 페이지처럼 데이터를 시각적으로 보여 줘야 할 때는 차트 라이브러리를 씁니다. Chart.js는 Canvas API로 그리는 간단하고 유연한 차트 라이브러리로 여러 기본 차트를 지원합니다. Recharts는 React 컴포넌트를 조합해 차트를 만드는 선언형 API를, Nivo는 React 기반의 차트 컴포넌트와 반응형 배치를 제공합니다.
고를 때는 Canvas로 그리는지 SVG로 그리는지, 키보드와 스크린 리더로 데이터를 읽을 수 있는지, 필요한 차트 종류와 데이터의 양을 정하고, 실제 데이터로 렌더링 비용을 측정합니다. Canvas로 그린 차트는 픽셀일 뿐이므로, 데이터를 표나 문장으로도 함께 제공하는 방법을 생각해 둬야 합니다.
라이브러리를 들이기 전에
분야와 관계없이 라이브러리를 들이기 전에는 같은 기준으로 확인하는 것이 좋습니다.
DECISION CRITERIA
라이브러리 도입 판단 기준
반복 구현을 줄이는 이점과 통합·접근성·유지 비용을 같은 조건에서 비교한다. 아래 다섯 축에 실제 확인 결과를 기록한다.
| 판단 축 | 확인할 질문 | 도입 전 확인 |
|---|---|---|
| 문제 적합성 | 반복 구현이나 복잡한 검증을 실제로 줄이는가? 브라우저 내장 API나 더 작은 패키지로 충분하지 않은가? | 넘길 책임과 직접 구현·내장 API·작은 대안의 비용을 같은 조건에서 비교한다. |
| 상호작용·접근성 | 필요한 상호작용 API가 있으며 키보드 조작, focus trap, ARIA 흐름이 요구사항대로 동작하는가? | 실제 UI 흐름을 키보드와 스크린 리더로 검증한다. |
| 통합성 | TypeScript와 SSR을 지원하고 design token을 연결할 수 있으며 reset·전역 CSS 충돌이 없는가? | 기존 빌드, hydration, 테마와 스타일 계약 안에서 별도 우회 없이 통합한다. |
| 도입 비용 | bundle 증가와 팀의 학습·업그레이드 비용이 해결할 문제에 비례하는가? | 실제 로딩 경로의 크기를 측정하고 학습·업그레이드 책임을 기록한다. |
| 유지보수 | 문서, 릴리스 기록, 지원 버전과 보안 대응 경로를 계속 확인할 수 있는가? | 확인 주기, 업데이트 담당자와 교체 조건을 팀 규칙으로 정한다. |
- 문제 적합성
- 질문 반복 구현이나 복잡한 검증을 실제로 줄이는가? 브라우저 내장 API나 더 작은 패키지로 충분하지 않은가?
- 도입 전 확인 넘길 책임과 직접 구현·내장 API·작은 대안의 비용을 같은 조건에서 비교한다.
- 상호작용·접근성
- 질문 필요한 상호작용 API가 있으며 키보드 조작, focus trap, ARIA 흐름이 요구사항대로 동작하는가?
- 도입 전 확인 실제 UI 흐름을 키보드와 스크린 리더로 검증한다.
- 통합성
- 질문 TypeScript와 SSR을 지원하고 design token을 연결할 수 있으며 reset·전역 CSS 충돌이 없는가?
- 도입 전 확인 기존 빌드, hydration, 테마와 스타일 계약 안에서 별도 우회 없이 통합한다.
- 도입 비용
- 질문 bundle 증가와 팀의 학습·업그레이드 비용이 해결할 문제에 비례하는가?
- 도입 전 확인 실제 로딩 경로의 크기를 측정하고 학습·업그레이드 책임을 기록한다.
- 유지보수
- 질문 문서, 릴리스 기록, 지원 버전과 보안 대응 경로를 계속 확인할 수 있는가?
- 도입 전 확인 확인 주기, 업데이트 담당자와 교체 조건을 팀 규칙으로 정한다.
위 표는 라이브러리를 들이기 전에 확인할 다섯 가지 기준입니다. 반복 구현을 줄이는 이점과 통합, 접근성, 유지 비용을 같은 조건에서 비교하고, 각 기준에 실제로 확인한 결과를 기록합니다. 후보가 얼마나 유명한지보다 프로젝트가 라이브러리에 맡길 일과 감당할 비용을 보는 것이 핵심입니다.
첫째는 문제에 맞는지입니다. 반복 구현이나 복잡한 검증을 실제로 줄여 주는지, 브라우저 내장 API나 더 작은 패키지로 충분하지는 않은지를 직접 구현했을 때와 같은 조건에서 비교합니다. 둘째는 상호작용과 접근성입니다. 필요한 상호작용 API가 있고, 키보드 조작과 포커스 가두기(focus trap), ARIA 흐름이 요구 사항대로 동작하는지 실제 화면을 키보드와 스크린 리더로 검증합니다. 셋째는 통합성입니다. 타입스크립트와 SSR을 지원하고, 디자인 토큰을 연결할 수 있으며, 초기화 CSS나 전역 스타일이 충돌하지 않는지 기존 빌드와 하이드레이션, 테마 안에서 별도의 우회 없이 통합되는지 확인합니다.
넷째는 도입 비용입니다. 번들 크기가 늘어나는 정도와 팀이 배우고 버전을 올리는 비용이 해결할 문제에 견줘 알맞은지, 실제 로딩 경로의 크기를 측정해 봅니다. 다섯째는 유지보수입니다. 문서와 릴리스 기록, 지원 버전, 보안 문제에 대응하는 경로를 계속 확인할 수 있는지 보고, 확인 주기와 업데이트 담당자, 교체할 조건을 팀 규칙으로 정해 둡니다. 표 아래의 설명처럼 시험 화면에서 확인한 결과를 기록하고, 요구 사항마다 통과 조건과 감당할 비용을 정해 둡니다.
좋은 라이브러리 선택은 유명한 패키지를 많이 아는 것이 아니라, 지금 코드가 맡겨도 되는 일과 나중에 되돌릴 비용을 함께 판단하는 일입니다.
정리
- UI 컴포넌트 라이브러리는 일관된 화면을 빠르게 만들어 주지만, 접근성과 스타일 충돌은 실제 화면에서 확인해야 합니다.
- Axios는 공통 요청 처리를, SWR과 TanStack Query는 여러 화면이 함께 쓰는 서버 상태의 캐시와 다시 확인하기를 맡습니다.
fetch()는 HTTP 오류에서 거부되지 않고 Axios는 기본으로 거부하며, 요청 도구를 바꿔도 캐시 규칙은 따로 정해야 합니다.- 폼, 날짜, 유틸리티 라이브러리는 내장 API로 충분한지 먼저 보고, 검증과 시간대 같은 규칙은 라이브러리와 별개로 정합니다.
- 차트는 그리는 방식과 접근성, 데이터 양을 기준으로 고르고 실제 데이터로 비용을 측정합니다.
- 라이브러리는 문제 적합성, 접근성, 통합성, 도입 비용, 유지보수의 다섯 기준으로 같은 조건에서 비교해 들입니다.
연습 문제
1. 404와 잘못된 JSON
loadProduct()에서 서버가 404와 JSON 본문을 함께 돌려줬습니다. return response.json()까지 실행될까요? HTTP 상태가 200이지만 JSON이 잘못되었다면 어디서 실패할까요?
해설 보기
실행되지 않습니다. 404이면 response.ok가 false이므로 직접 던진 오류로 끝나고, 본문은 읽지 않습니다.
200이면 상태 검사는 통과하지만 response.json()이 JSON을 해석하다 실패해 Promise가 거부됩니다. HTTP 상태 검사와 응답 본문 해석을 따로 다뤄야 두 원인을 구별할 수 있습니다.
2. 같은 시점의 두 시각
예약 시각이 2026-10-09T00:30:00Z입니다. UTC로 보여 줄 때와 UTC+9(한국 표준시)로 보여 줄 때의 시각은 각각 얼마일까요? 날짜 라이브러리를 고르기 전에 무엇을 정해야 할까요?
해설 보기
UTC로는 10월 9일 00:30, UTC+9로는 같은 날 09:30입니다. 같은 하나의 시점을 어느 시간대로 보여 줄지 정해야 합니다.
또 생일처럼 시각과 시간대가 없는 달력 날짜와, 예약처럼 정확한 시점을 구별하고, 해석할 입력 형식과 표시할 지역도 정해야 합니다. 라이브러리 이름만으로 이런 규칙이 정해지지는 않습니다.
3. 포커스가 돌아오지 않는 모달
어떤 모달 라이브러리가 디자인과 번들 크기의 요구는 만족하지만, 닫았을 때 포커스가 열기 버튼으로 돌아오지 않습니다. 바로 들여도 될까요? 확인할 흐름을 적어 보세요.
해설 보기
아직 들이면 안 됩니다. 필요한 키보드 흐름을 통과하지 못했기 때문입니다. 열기 버튼에 포커스를 두고 모달을 연 뒤, 모달 안의 첫 포커스와 Tab 이동, 닫기 동작, 닫은 뒤 포커스가 열기 버튼으로 돌아오는지를 확인합니다.
요구 사항에 있다면 Escape로 닫히는지와 스크린 리더가 읽는 이름도 확인합니다. 작은 시험 화면에서 고칠 수 있는지와 통합 비용을 확인한 뒤, 그 결과를 도입 판단의 기록으로 남깁니다.
이것으로 11장을 마칩니다. 11장에서는 SPA의 개념에서 시작해 React의 컴포넌트와 JSX, 상태와 props, 상태 관리 라이브러리, 그리고 분야별 프런트엔드 라이브러리와 도입 기준까지 살펴봤습니다. 다음 장의 서버와 클라이언트 관계 이해부터는 화면 뒤에서 데이터를 제공하는 백엔드로 넘어가, Node.js와 Express로 서버를 만들고 RESTful API를 설계하며 데이터베이스의 기초를 다룹니다.