DatePicker
Import
<!-- CSS --> <link rel="stylesheet" href="/guide/css/base/index.css" /> <link rel="stylesheet" href="/guide/css/components/datepicker.css" /> <!-- jQuery + JS (body 닫기 전) --> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <script src="/guide/js/components/datepicker.js"></script>
Live
Single
일월화 수목금토
선택된 값 (hidden input): —
Range
일월화 수목금토
범위: —
States
Default
일월화 수목금토
Disabled
Specs
| 요소 | 값 | 토큰 |
|---|---|---|
| Input 폰트 | 16px Regular | var(--font-size-body-sm) |
| 팝업 너비 | 264px | — |
| 팝업 Border Radius | 12px | var(--radius-lg) |
| 팝업 Shadow | — | var(--shadow-md) |
| 날짜 셀 높이 | 34px | — |
| 날짜 폰트 | 14px Regular | var(--font-size-label) |
| 오늘 표시 | primary 테두리 1.5px | var(--color-primary-main) |
| 선택됨 배경 | primary | var(--color-primary-main) |
| 전송 값 형식 | YYYY-MM-DD (ISO 8601) | hidden input |
Code
HTML
<div class="datepicker" id="dp-1">
<div class="datepicker__input-wrap">
<input class="datepicker__input" type="text"
placeholder="날짜를 선택하세요" readonly
aria-label="날짜 선택" aria-haspopup="dialog" />
<input class="datepicker__hidden" type="hidden" name="date" />
<button class="datepicker__trigger" type="button"
aria-label="달력 열기" aria-expanded="false"
aria-controls="dp-1-popup">
<!-- 캘린더 SVG 아이콘 -->
<svg viewBox="0 0 20 20" fill="none">
<rect x="2.5" y="4" width="15" height="13" rx="2" stroke="currentColor" stroke-width="1.5"/>
<path d="M2.5 8h15" stroke="currentColor" stroke-width="1.5"/>
<path d="M6.5 2v3M13.5 2v3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
</button>
</div>
<div class="datepicker__popup" id="dp-1-popup"
role="dialog" aria-label="날짜 선택 달력" aria-modal="true">
<div class="datepicker__header">
<button class="datepicker__nav datepicker__nav--prev" type="button" aria-label="이전 달">
<svg viewBox="0 0 16 16" fill="none">
<path d="M10 12L6 8l4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<span class="datepicker__month-year"></span>
<button class="datepicker__nav datepicker__nav--next" type="button" aria-label="다음 달">
<svg viewBox="0 0 16 16" fill="none">
<path d="M6 12l4-4-4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
<div class="datepicker__weekdays">
<span>일</span><span>월</span><span>화</span>
<span>수</span><span>목</span><span>금</span><span>토</span>
</div>
<div class="datepicker__days"><!-- jQuery로 동적 생성 --></div>
<div class="datepicker__footer">
<button class="datepicker__today-btn" type="button">오늘</button>
<button class="datepicker__today-btn" type="button">초기화</button>
<button class="datepicker__confirm-btn" type="button">확인</button>
</div>
<button class="datepicker__close" type="button" aria-label="닫기"></button>
</div>
</div>
JavaScript 초기화
/* 기본 초기화 — HTML에 .datepicker 클래스가 있으면 자동 실행됨 */
$('.datepicker').datepicker();
/* 옵션: 오늘 이후만 선택 가능 */
$('#dp-1').datepicker({ minDate: new Date() });
/* 옵션: 특정 범위 */
$('#dp-1').datepicker({
minDate: new Date('2026-01-01'),
maxDate: new Date('2026-12-31')
});
/* 옵션: 날짜 선택 시 콜백 */
$('#dp-1').datepicker({
onChange: function (isoString) {
console.log('선택된 날짜:', isoString); /* '2026-06-05' */
}
});
/* data 속성으로 옵션 (HTML에서 설정) */
/* <div class="datepicker" data-min-date="2026-01-01" data-max-date="2026-12-31"> */
백엔드 연동
/* ──────────────────────────────────────────────────────────
백엔드 전송값: .datepicker__hidden input[type="hidden"]에
'YYYY-MM-DD' 형식으로 자동 저장됩니다.
────────────────────────────────────────────────────────── */
/* ① HTML form submit — hidden input이 자동으로 포함됩니다 */
<form method="POST" action="/api/reservation">
<div class="datepicker" id="dp-1">
...
<input class="datepicker__hidden" type="hidden" name="reservation_date" />
...
</div>
<button type="submit">예약하기</button>
</form>
/* → 서버에서 req.body.reservation_date 로 수신 (예: '2026-06-15') */
/* ② jQuery AJAX */
$('form').on('submit', function (e) {
e.preventDefault();
$.ajax({
url: '/api/reservation',
method: 'POST',
data: {
reservation_date: $('[name="reservation_date"]').val()
/* 값 예: '2026-06-15' */
},
success: function (res) {
console.log('저장 완료:', res);
},
error: function (err) {
console.error('오류:', err);
}
});
});
/* ③ fetch API (JSON) */
async function submitDate() {
const date = document.querySelector('[name="reservation_date"]').value;
/* 값 예: '2026-06-15' */
const res = await fetch('/api/reservation', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reservation_date: date })
});
const data = await res.json();
console.log('저장 완료:', data);
}
/* ④ 서버 수신 예시 */
/* Node.js (Express) */
app.post('/api/reservation', (req, res) => {
const date = req.body.reservation_date; /* '2026-06-15' */
/* DB 저장: await db.query('INSERT INTO reservations (date) VALUES (?)', [date]) */
res.json({ success: true });
});
/* PHP */
$date = $_POST['reservation_date']; /* '2026-06-15' */
/* $stmt = $pdo->prepare("INSERT INTO reservations (date) VALUES (?)");
$stmt->execute([$date]); */
/* Spring Boot (Java) */
/* @PostMapping("/api/reservation")
public ResponseEntity save(@RequestParam String reservation_date) {
// reservation_date = "2026-06-15"
} */
Range — HTML
<div class="datepicker datepicker--range" id="dp-range-1">
<div class="datepicker__range-inputs">
<div class="datepicker__input-wrap">
<input class="datepicker__input datepicker__input--start" type="text"
placeholder="시작일" readonly aria-label="시작일 선택" />
</div>
<span class="datepicker__range-sep" aria-hidden="true">~</span>
<div class="datepicker__input-wrap">
<input class="datepicker__input datepicker__input--end" type="text"
placeholder="종료일" readonly aria-label="종료일 선택" />
</div>
<input class="datepicker__hidden datepicker__hidden--start" type="hidden" name="start_date" />
<input class="datepicker__hidden datepicker__hidden--end" type="hidden" name="end_date" />
<button class="datepicker__trigger" type="button" aria-label="날짜 범위 선택" aria-expanded="false">
<!-- 캘린더 SVG -->
</button>
</div>
<div class="datepicker__popup" role="dialog" aria-label="날짜 범위 선택" aria-modal="true">
<div class="datepicker__header">...</div>
<span class="datepicker__range-phase"></span> <!-- '시작일을 선택하세요' / '종료일을 선택하세요' -->
<div class="datepicker__weekdays">...</div>
<div class="datepicker__days"></div>
<div class="datepicker__footer">
<button class="datepicker__reset-btn" type="button">초기화</button>
<button class="datepicker__confirm-btn" type="button">확인</button>
</div>
<button class="datepicker__close" type="button" aria-label="닫기"></button>
</div>
</div>
Range — JavaScript
/* 자동 초기화 — .datepicker--range 클래스가 있으면 자동 실행됨 */
$('.datepicker--range').datepickerRange();
/* onChange 콜백 */
$('#dp-range-1').datepickerRange({
onChange: function (startISO, endISO) {
console.log('범위:', startISO, '~', endISO);
/* 예: '2026-06-01' ~ '2026-06-30' */
}
});
/* 선택된 값 읽기 */
var start = $('[name="start_date"]').val(); /* '2026-06-01' */
var end = $('[name="end_date"]').val(); /* '2026-06-30' */
Range — 백엔드 연동
/* ① form submit */
<form method="POST" action="/api/search">
<input type="hidden" name="start_date" /> <!-- '2026-06-01' -->
<input type="hidden" name="end_date" /> <!-- '2026-06-30' -->
</form>
/* ② fetch */
const payload = {
start_date: $('[name="start_date"]').val(),
end_date: $('[name="end_date"]').val()
};
await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
/* ③ 서버 수신 (Node.js) */
app.post('/api/search', (req, res) => {
const { start_date, end_date } = req.body;
/* '2026-06-01', '2026-06-30' */
/* SQL: WHERE date BETWEEN ? AND ? → [start_date, end_date] */
});
Classes
| 클래스 | 타입 | 설명 |
|---|---|---|
| .datepicker | 루트 | 전체 컨테이너 (position: relative, max-width 320px) |
| .datepicker--disabled | 수정자 | 전체 비활성 — 클릭 불가, 흐릿한 스타일 |
| .datepicker__input-wrap | 요소 | input + 트리거 버튼 감싸는 박스 (H 44px) |
| .datepicker__input | 요소 | 텍스트 입력 (readonly) — 표시용 날짜 'YYYY년 M월 D일' |
| .datepicker__hidden | 요소 | 히든 input — 백엔드 전송값 'YYYY-MM-DD' |
| .datepicker__trigger | 요소 | 달력 아이콘 버튼 — 팝업 토글 |
| .datepicker__popup | 요소 | 달력 팝업 (absolute, opacity/transform 전환) |
| .datepicker__popup--open | 수정자 | 팝업 표시 — JS가 자동 추가/제거 |
| .datepicker__header | 요소 | 년/월 레이블 + 이전/다음 네비게이션 |
| .datepicker__month-year | 요소 | 현재 년/월 텍스트 — JS가 갱신 |
| .datepicker__nav | 요소 | 이전/다음 달 버튼 (원형) |
| .datepicker__nav--prev | 수정자 | 이전 달 |
| .datepicker__nav--next | 수정자 | 다음 달 |
| .datepicker__weekdays | 요소 | 요일 헤더 (일~토, 7열 그리드) |
| .datepicker__days | 요소 | 날짜 버튼 그리드 — JS가 동적 생성 |
| .datepicker__day | 요소 | 날짜 버튼 셀 (H 34px, 원형) |
| .datepicker__day--today | 수정자 | 오늘 — primary 테두리 |
| .datepicker__day--selected | 수정자 | 선택된 날짜 — primary 배경, 흰 텍스트 |
| .datepicker__day--disabled | 수정자 | 선택 불가 날짜 (minDate/maxDate 범위 밖) |
| .datepicker__day--empty | 수정자 | 1일 이전 빈 셀 (포인터 이벤트 없음) |
| .datepicker__footer | 요소 | 오늘/확인 버튼 영역 |
| .datepicker__today-btn | 요소 | 오늘 날짜로 이동 + 선택 |
| .datepicker__confirm-btn | 요소 | 선택 확인 후 팝업 닫기 |
| Range 전용 (.datepicker--range) | ||
| .datepicker--range | 수정자 | Range 모드 활성 — 두 날짜 입력 + 범위 강조 |
| .datepicker__range-inputs | 요소 | 시작일 input + 구분자 + 종료일 input + 트리거 버튼 행 |
| .datepicker__input--start | 수정자 | 시작일 표시 input (readonly) |
| .datepicker__input--end | 수정자 | 종료일 표시 input (readonly) |
| .datepicker__hidden--start | 수정자 | 시작일 hidden input — 백엔드 전송값 |
| .datepicker__hidden--end | 수정자 | 종료일 hidden input — 백엔드 전송값 |
| .datepicker__range-sep | 요소 | 시작~종료 구분자 (~) |
| .datepicker__range-phase | 요소 | 선택 단계 안내 텍스트 — JS가 자동 갱신 |
| .datepicker__day--range-start | 수정자 | 범위 시작일 — primary 왼쪽 반원 |
| .datepicker__day--range-end | 수정자 | 범위 종료일 — primary 오른쪽 반원 |
| .datepicker__day--in-range | 수정자 | 범위 내 날짜 — 연한 초록 배경, 직사각형 |
| .datepicker__day--range-hover-end | 수정자 | hover 중인 종료일 예정 — 점선 테두리 미리보기 |
| .datepicker__reset-btn | 요소 | 범위 초기화 버튼 (시작일·종료일 모두 삭제) |