캘린더 도메인 테크 스펙 - 100-hours-a-week/9-team-Devths-WIKI GitHub Wiki
1️⃣ 배경 (Background)
프로젝트 목표 (Objective)
- 취업 준비 과정(서류/코테/면접/개인일정)을 캘린더 + 일정 카드 + To-Do로 한 화면에서 관리하고, AI 자동 일정 생성까지 연결해 사용자의 입력 부담을 줄임
핵심 결과 (Key Result) 1
- 사용자가 월간/주간 전환 + 날짜 선택 + 일정 카드 확인의 핵심 흐름을 끊김 없이 수행함
- 전환/이동/선택에 따른 UI 상태 꼬임(잘못된 날짜 표시, 카드 목록 불일치) 0건
핵심 결과 (Key Result) 2
- 일정 CRUD(추가/조회/수정/삭제)가 안정적으로 동작함
- 저장 실패/중복 생성/삭제 후 잔존 등 주요 버그 0건
핵심 결과 (Key Result) 3
- AI 자동 추가 기능이 사용 가능한 수준으로 동작한다.
- 파일/텍스트 첨부 → 분석 요청 → 대기 → 결과 확인(CAL-M2)까지 성공률/복구 UX 확보
- 실패/취소 시 사용자가 어디까지 되었는지 이해 가능
문제 정의 (Problem):
- 일정이 많아지면 지금 뭐가 임박했는지 파악이 어려움
- 단계별(서류/코테/면접/개인일정)로 분류하지 않으면 우선순위가 흐려짐
- 채용 공고를 보고 일정을 일일이 입력하는 비용이 큼(특히 여러 전형)
가설 (Hypothesis)
- 캘린더 상단은 ‘날짜 탐색’, 하단은 ‘다가오는 일정/선택한 날짜 일정’을 카드로 보여주면 사용자가 빠르게 확인
- 단계/태그/날짜 범위 필터를 제공하면 원하는 일정만 좁혀볼 수 있음
- AI 자동 일정 생성(텍스트/이미지/PDF 기반)을 제공하면 입력 시간을 크게 줄일 수 있음
관련 자료
- 화면 설계서: CAL-001, CAL-001-1, CAL-M1
M6, CAL-M2M3 - 공통 정책: 공통 도메인 스펙
2️⃣ 목표가 아닌 것 (Non-goals)
이번 프로젝트에서 다루지 않는 내용
- 초 단위 리마인더/복잡한 반복 일정(RRULE) 같은 고급 캘린더 기능
- 일정 공유/권한 관리(다른 사람과 일정 공유, 편집 권한) 기능
3️⃣ 설계 및 기술 자료 (Architecture and Technical Documentation)
아키텍처 개요 (Architecture Overview)
- 화면 구성 핵심:
- 상단: 캘린더(월/주 뷰, 이동, 오늘 하이라이트, 일정 점/기간 선)
- 중단: 일정 카드 리스트(기본: 다가오는 일정 TOP5 / 선택 시: 해당 날짜 일정)
- 하단: To-Do(선택 날짜 기준)
- 상태 흐름 핵심:
viewMode(월/주) +focusedDate(현재 달/주 기준) +selectedDate(선택 날짜)filters(단계/태그/날짜 범위) → 카드 목록과 점/선 표시에도 영향
- 모달 흐름:
- 일정 추가(CAL-M1) → AI 분석 대기(CAL-M1-2) → AI 결과 확인(CAL-M2) → 저장
- 일정 상세(CAL-M4) → 삭제 확인(CAL-M5) / 수정(CAL-M3)
- 작성 취소 확인(CAL-M6)
4️⃣ 주요 페이지 / 컴포넌트 구조
페이지 (Pages)
페이지 (Pages)
- 페이지 이름:
CalendarPage(CAL-001)- 주요 기능:
- 월/주 토글
- 월/주 이동
- 날짜 선택
- 일정 유무 표시(점/기간선)
- 일정 카드 리스트(다가오는 일정/선택 날짜 일정)
- To-Do 리스트(추가/완료/수정/삭제, 진행률)
- 필터 토글(CAL-001-1)
- 일정 추가 모달(CAL-M1) 오픈
- 사용 컴포넌트:
CalendarHeader(월/주 토글 + 기간 표시 + 이동 버튼 + 필터 버튼 + 일정추가 버튼)MiniCalendar또는CalendarGrid(월/주 UI)ScheduleSectionHeader(“다가오는 일정”/“n월 n일의 일정”)ScheduleCardList/ScheduleCardTodoPanel(진행률 + 리스트)FilterPanel(CAL-001-1)GlobalModalHost(CAL-M1~M6)Toast(첨부 실패/성공, 삭제 성공 등)
- 데이터 로딩 시점:
- 페이지 진입 시: 해당 기간(월/주)의 일정 목록 조회 + 선택 날짜 To-Do 조회
viewMode/focusedDate변경 시: 해당 기간 일정 재조회selectedDate변경 시: 해당 날짜 To-Do 조회 + 카드 목록 전환- 필터 변경 시: 서버 필터링 or 클라이언트 필터링 정책에 따라 즉시 반영
- 라우팅:
/calendar(보호 라우트, AuthGate 뒤)
- 주요 기능:
주요 컴포넌트 (Components)
(1) CalendarHeader
- 역할
- 월/주 토글, 현재 기간(년월/주차) 표시, 이전/다음 이동, 필터 열기/닫기, 일정 추가 모달 오픈 버튼 제공
- Props
viewMode: 'month' | 'week'focusedDate: DateisFilterOpen: booleanhasActiveFilter: boolean(필터 적용 여부 뱃지/색상 표시용)onChangeView(mode)onPrev()onNext()onToggleFilter()onOpenCreateModal()
- 발생 이벤트 → 페이지에서 하는 일
- 토글 클릭 →
viewMode변경 +focusedDate재정렬(현재 월/주 기준) + 일정 재조회 - 이전/다음 →
focusedDate이동 + 일정 재조회 - 필터 버튼 →
isFilterOpen토글 - 일정추가 →
CAL-M1열기
- 토글 클릭 →
(2) MiniCalendar
- 역할
- 월/주 달력 UI 렌더링
- 오늘 하이라이트
- 일정 유무 점(단일 일정) / 기간선(기간 일정) 표시
- 날짜 선택 처리
- Props
viewModefocusedDateselectedDateevents: CalendarEvent[](해당 기간 일정)onSelectDate(date)onChangeFocusedDate(date)
- 발생 이벤트
- 날짜 클릭 →
selectedDate변경- 하단: “선택 날짜 일정” 카드 리스트로 전환
- To-Do: 해당 날짜 To-Do 조회
- 날짜 클릭 →
- UI 규칙
- 오늘 날짜 강조
- 당일 점은 개수 무관 1개
- 기간 일정은 선(두께 6px), 시작/끝 라운드 처리
- 겹침/hover 시 z-index 상단 노출
(3) ScheduleSectionHeader
- 역할
- 하단 카드 리스트의 제목 영역
- 기본: “다가오는 일정”
- 날짜 선택 시: “n월 n일의 일정”
- 일정 없으면 “일정이 없습니다” 문구는 여기서 처리 or 리스트에서 처리(둘 중 하나로 표준화)
- Props
mode: 'upcoming' | 'selectedDate'selectedDate?: DateisEmpty: boolean
(4) ScheduleCardList / ScheduleCard
- 역할
- 일정 목록을 카드 형태로 렌더링
- 카드 클릭 시 상세 모달(CAL-M4) 오픈
- Props(List)
items: CalendarEvent[]onClickItem(eventId)(상세 모달 오픈)variant: 'upcoming' | 'day'
- Props(Card)
- 표시 요소:
- 유형(태그/단계)
- 임박 뱃지(3일 이내)
- D-Day(D-3, D-Day)
- 제목/회사/날짜/장소
- 알림
- 표시 요소:
- 정렬 규칙
- 선택 날짜 일정:
- 시작 시간 없는 당일 일정 우선
- 시작 시간 기준 정렬
- 동일 조건이면 ㄱㄴㄷ
- 다가오는 일정: 시작일 기준 가까운 순 TOP 5
- 선택 날짜 일정:
(5) TodoPanel
- 역할
- 선택 날짜의 To-Do 관리(추가/완료/수정/삭제)
- 진행률(%) + 상태바 표시
- Props
selectedDate: Datetodos: TodoItem[]onAdd()/onAddWithTitle(title)onToggle(todoId)onUpdate(todoId, title)onDelete(todoId)
- 내부 UX 이벤트
- 추가 시 빈 항목 생성 + 자동 focus
- Enter → 아래에 새 항목 추가
- Esc / focus out → 내용 있으면 저장, 없으면 삭제
- 정렬
- 미완료 위, 완료 아래
- 생성 빠른 순
- 진행률 표시
completed / total- 100%면 “전체 완료” + Bold + 상태바 색상 변경
(6) FilterPanel (CAL-001-1)
- 역할
- 단계 필터(단일 선택), 태그 검색 + 자동완성, 날짜 범위 필터
- 활성 필터 칩 목록 표시 및 개별 삭제
- 초기화 버튼 제공
- Props
filterstagInputValuetagSuggestions: string[]onChangeStage(stage|null)onChangeTagInput(value)onAddTag(tag)onRemoveTag(tag)onChangeRange(range)onRemoveFilterChip(type)onReset()
- 중요
- 필터 변경은 즉시 반영
- 페이지에서
filters갱신하면 일정 리스트 쿼리키가 바뀌도록 설계(React Query invalidate/자동 refetch)
- 페이지에서
- 필터 변경은 즉시 반영
(7) GlobalModalHost (CAL-M1~M6)
- 역할
- 모든 모달을 한 곳에서 관리(열림/닫힘/스택)
- 페이지에서는 ‘모달 열기’만 호출하고, 실제 렌더링은 Host가 담당
- 포함 모달
- CAL-M1: 일정 추가(수동 + AI 입력)
- CAL-M1-2: AI 생성 대기(취소 가능)
- CAL-M2: AI 결과 확인/수정 후 생성
- CAL-M4: 일정 상세
- CAL-M3: 일정 수정
- CAL-M5: 삭제 확인
- CAL-M6: 작성 취소 확인
- Props
modalState(현재 열린 모달 타입 + payload)onClose()onConfirm(payload)(삭제/저장/생성 등)
5️⃣ 컴포넌트 간 관계
(1) 전체 트리
CalendarPage(도메인 오케스트레이터)CalendarHeaderCalendarGridFilterPanelScheduleCardListTodoPanelModalHostCreateScheduleModal(CAL-M1)AIWaitingModal(CAL-M1-2)AIGeneratedConfirmModal(CAL-M2)ScheduleDetailModal(CAL-M4)ScheduleEditModal(CAL-M3)DeleteConfirmModal(CAL-M5)
(2) 관계를 ‘역할’로 나누기
- 상태/데이터를 ‘결정’하는 곳
CalendarPageviewMode / focusedDate / selectedDatefilters / isFilterOpen- 서버데이터:
events(기간 일정),todos(선택 날짜)
- 즉, 아래 컴포넌트는 보여주기 중심이고, 상태 결정은 CalendarPage가 한다.
- 상태를 바꾸는 입력(UI 컨트롤)
CalendarHeaderCalendarGridFilterPanelTodoPanel(추가/수정/완료 토글은 입력임)ScheduleCardList(카드 클릭은 모달 입력임)- 입력이 발생하면 CalendarPage로 이벤트를 올림(onSomething)
- 모달은 상태에 따른 UI 레이어
ModalHostmodalState를 보고 어떤 모달을 렌더할지 결정- 모달 내부에서 저장/삭제/취소를 누르면 다시 CalendarPage로 이벤트 전달
(3) props 내려주기 관계 (CalendarPage → child)
CalendarPage → CalendarHeader- 내려주는 값
viewModefocusedDateisFilterOpenhasActiveFilter
- 내려주는 핸들러
onChangeView(mode)onPrev(),onNext()onToggleFilter()onOpenCreateModal()
- 내려주는 값
CalendarPage → CalendarGrid- 내려주는 값
viewModefocusedDateselectedDateevents(해당 기간 일정 목록)
- 내려주는 핸들러
onSelectDate(date)(선택 날짜 변경)
- 내려주는 값
CalendarPage → FilterPanel- 내려주는 값
isOpenfilterstagSuggestions
- 내려주는 핸들러
onChangeStage(stage|null)onAddTag(tag)/onRemoveTag(tag)onChangeRange({start,end})onReset()
- 내려주는 값
CalendarPage → ScheduleCardList- 내려주는 값
items(다가오는 일정 TOP5 or 선택 날짜 일정)variant(upcoming/day)
- 내려주는 핸들러
onClickItem(eventId)(상세 모달 열기)
- 내려주는 값
CalendarPage → TodoPanel- 내려주는 값
selectedDatetodosprogress(계산값이어도 됨: total/completed)
- 내려주는 핸들러
onAddTodo()onToggleTodo(todoId)onUpdateTodo(todoId, title)onDeleteTodo(todoId)
- 내려주는 값
CalendarPage → ModalHost- 내려주는 값
modalState- 예:
{ type: 'CREATE' | 'DETAIL' | 'EDIT' | 'DELETE' | 'DISCARD' | 'AI_WAIT' | 'AI_CONFIRM', payload: ... }
- 예:
- 내려주는 핸들러
onClose()onConfirm(payload)(저장/삭제/확정 등)
- 내려주는 값
(4) 이벤트 올라오기 관계 (child → CalendarPage)
CalendarHeader에서 올라오는 이벤트- 월/주 토글 →
setViewMode(mode)+ 일정 재조회 - 이전/다음 →
setFocusedDate(prev/next)+ 일정 재조회 - 필터 열기/닫기 →
toggleFilterOpen() - 일정 추가 →
openModal('CREATE')
- 월/주 토글 →
CalendarGrid에서 올라오는 이벤트- 날짜 선택 →
setSelectedDate(date)+ To-Do 재조회 + 카드 리스트 전환
- 날짜 선택 →
FilterPanel에서 올라오는 이벤트- stage/tags/range 변경 →
setFilters(next)+ 일정 재조회(또는 클라필터) - reset →
resetFilters()+ 일정 재조회
- stage/tags/range 변경 →
ScheduleCardList에서 올라오는 이벤트- 카드 클릭 →
openModal('DETAIL', { eventId })
- 카드 클릭 →
TodoPanel에서 올라오는 이벤트- 추가/수정/토글/삭제 → 투두 mutation → 성공 시
refetchTodos(selectedDate)또는 invalidate
- 추가/수정/토글/삭제 → 투두 mutation → 성공 시
ModalHost에서 올라오는 이벤트- CREATE 저장 → 일정 생성 mutation → 성공 시 일정 목록 invalidate
- DETAIL에서 “수정” →
openModal('EDIT', { eventId }) - EDIT 저장 → 일정 수정 mutation → 성공 시 invalidate
- DELETE 확인 → 일정 삭제 mutation → 성공 시 토스트 + invalidate
- DISCARD 확인 → 모달 닫기
- AI_WAIT 취소 → AI 요청 취소(가능하면) + 모달 닫기
- AI_CONFIRM 저장 → 일정 일괄 생성 mutation → 성공 시 invalidate
(5) 모달별 payload 관계 (ModalHost 내부 분기 기준)
CreateScheduleModal(CAL-M1)- payload:
{ initialDate?: Date }(선택 날짜 기준으로 기본값 주려면)
- payload:
AIWaitingModal(CAL-M1-2)- payload:
{ requestId: string }
- payload:
AIGeneratedConfirmModal(CAL-M2)- payload:
{ draftId: string }또는{ requestId: string }
- payload:
ScheduleDetailModal(CAL-M4)- payload:
{ eventId: string }
- payload:
ScheduleEditModal(CAL-M3)- payload:
{ eventId: string }
- payload:
DeleteConfirmModal(CAL-M5)- payload:
{ eventId: string }
- payload:
DiscardConfirmModal(CAL-M6)- payload:
{ from: 'CREATE' | 'EDIT', dirtyFields?: string[] }
- payload:
6️⃣ 상태 관리 전략 (State Management Strategy)
Global State (Zustand Stores)
calendarUIStoreviewMode(month/week)focusedDate(현재 보고 있는 기간 기준)selectedDate(선택한 날짜)isFilterOpenfiltersstage(서류/코테/1차/2차/개인일정 중 1개)tags: string[]range: { start?: string, end?: string }
modalStore(공통 모달 정책과 연결 가능)- 어떤 모달이 열려있는지
- 모달에 전달할 payload(선택한 일정 id 등)
Local State (React useState/useReducer)
- 모달 내부의 폼 입력값(제목/회사명/날짜/설명/태그/알림)
- To-Do 입력의 임시 편집 상태(“현재 편집 중인 항목”)
Server Cache State (React Query)
- 일정 목록(기간 단위)
- 선택 날짜 To-Do
- 태그 자동완성 후보
- AI 분석 결과(요청 후 결과 받아오기)
- 캐싱 키 예시
['schedules', viewMode, focusedDate, filters]['todos', selectedDate]['tag-suggestions', query]['ai-schedule-draft', requestId]
7️⃣ API 연동 (API Integration)
호출할 백엔드 API 목록
- 일정(Event)
GET /api/events?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD&step=String&tag=String- 기간 일정 목록 조회
startDate,endDate는 필수step,tag는 선택(각각 단일 값)
POST /api/events- 일정 생성
- 수동 입력 저장 / AI 결과 확정 저장에 사용
GET /api/events/{eventId}- 일정 상세 조회 (상세/수정 모달 초기값)
PUT /api/events/{eventId}- 일정 수정
DELETE /api/events/{eventId}- 일정 삭제 (성공 시
204)
- 일정 삭제 (성공 시
- To-Do
GET /api/todos?dueDate=YYYY-MM-DD- 선택 날짜 To-Do 조회
dueDate는 선택, 미입력 시 기본값: 현재 날짜
POST /api/todos- To-Do 추가
PATCH /api/todos/{todoId}- To-Do 수정 또는 완료 토글
- 같은 endpoint 공유 (body가 다름)
DELETE /api/todos/{todoId}- To-Do 삭제 (성공 시
204)
- To-Do 삭제 (성공 시
- AI 자동 일정 추출
POST /api/files/presigned- Presigned URL 발급 (S3 업로드용)
- 성공
200→presignedUrl,s3Key
POST /api/files- 파일 메타 등록 (fileId 획득)
- 성공
201→fileId
POST /api/ai/events/extraction- 일정 추출 요청(비동기 시작)
- body:
{ fileId } - 성공
202→taskId,status=PENDING
GET /api/ai/tasks/{taskId}- AI 작업 상태 조회(폴링)
- 진행 중
202 (PROGRESSING)/ 완료200 (COMPLETED + result)/ 실패200 (FAILED + failReason)
DELETE /api/files/{fileId}- 첨부 취소/정리용 파일 삭제 (성공 시
204)
- 첨부 취소/정리용 파일 삭제 (성공 시
API 호출 처리 정책
axios사용 (프로젝트 컨벤션)- 로딩 UI
- 캘린더/리스트: Skeleton 또는 Spinner
- AI 분석: 전용 대기 모달(CAL-M1-2)로 blocking 처리(배경 상호작용 불가)
- 에러 처리
- 전역 에러 핸들러로 토스트/모달 + 로깅
401→ AuthGate/리프레시 흐름403/404→ “권한 없음/대상 없음” 안내
React Query로 중복 호출 방지/캐시/자동 refetc
API 호출 시점 + 캐시 반영 정책
1) GET /api/events (기간 일정 목록)
- 호출 시점
/calendar진입 시viewMode변경 (월 ↔ 주)focusedDate변경 (이전/다음 이동)filters변경 (step/tag 등)
- QueryKey 예시
['events', startDate, endDate, step ?? null, tag ?? null]
- 비고(중요)
- 백엔드
tag는 단일 파라미터라서, 프론트에서 태그 다중 선택을 허용하면- 서버에는 대표 tag 1개만 전달 + 나머지는 클라이언트 필터링(정책 필요)
- 백엔드
2) GET /api/events (기간 일정 목록)
- 호출 시점
/calendar진입 시viewMode변경 (월 ↔ 주)focusedDate변경 (이전/다음 이동)filters변경 (step/tag 등)
- QueryKey 예시
['events', startDate, endDate, step ?? null, tag ?? null]
- 비고(중요)
- 백엔드
tag는 단일 파라미터라서, 프론트에서 태그 다중 선택을 허용하면- 서버에는 대표 tag 1개만 전달 + 나머지는 클라이언트 필터링(정책 필요)
- 백엔드
3) GET /api/events/{eventId} (일정 상세)
- 호출 시점
ScheduleCard클릭 →CAL-M4(상세 모달)오픈할 때CAL-M3(수정 모달)진입 시 초기 데이터로 사용
- 캐시 반영
- QueryKey 예:
['event', eventId] - 데이터는 상세 모달/수정 모달에 전달
- QueryKey 예:
4) PUT /api/events/{eventId} (일정 수정)
- 호출 시점
CAL-M3에서 저장 클릭 시
- 성공 시 캐시 반영
invalidateQueries(['events', ...])invalidateQueries(['event', eventId])(상세 캐시 갱신)
- UI 처리
- 성공: 모달 닫기 + 토스트
- 실패: 모달 유지 + 에러 안내
5) DELETE /api/events/{eventId} (일정 삭제)
- 호출 시점
CAL-M5에서 삭제 확인 클릭 시
- 성공 시 캐시 반영
invalidateQueries(['events', ...])removeQueries(['event', eventId])(선택: 상세 캐시 정리)
- UI 처리(스펙)
- 성공: 모달 닫기 → 토스트 “${일정명}이 정상적으로 삭제되었습니다”(3초)
6) GET /api/todos?dueDate=... (To-Do 조회)
- 호출 시점
/calendar진입 시 (기본: 오늘)selectedDate변경 시
- QueryKey 예시
['todos', dueDate]
7) POST /api/todos (To-Do 추가)
- 호출 시점
- TodoPanel 입력 후 Enter/포커스아웃 (내용이 있을 때만)
- 성공 시
invalidateQueries({ queryKey: ['todos', dueDate] })
8) PATCH /api/todos/{todoId} (수정 / 완료 토글)
- 호출 시점
- 인라인 편집 저장(Enter/포커스아웃)
- 체크박스 클릭(완료 토글)
- 성공 시
invalidateQueries({ queryKey: ['todos', dueDate] })
- 권장(실수 방지)
- 같은 endpoint 공유 → 프론트 함수 분리:
updateTodo(todoId, { title, dueDate })toggleTodo(todoId, { isCompleted })
- 같은 endpoint 공유 → 프론트 함수 분리:
9) DELETE /api/todos/{todoId} (To-Do 삭제)
- 호출 시점
- 삭제 버튼 클릭 또는 “내용 완전 삭제 후 저장” 같은 UX 정책에 따름
- 성공 시
invalidateQueries({ queryKey: ['todos', dueDate] })
10) POST /api/files/presigned (Presigned URL 발급)
- 호출 시점
- CAL-M1에서 파일 선택 직후(업로드 시작 전)
- 캐시
- 캐시보단 “업로드 세션” 성격 → 로컬 상태로 관리
11) POST /api/files (파일 메타 등록 → fileId 확보)
- 호출 시점
- S3 업로드 성공 직후
- 결과 사용
fileId를 다음 AI 추출 요청에 전달
12) POST /api/ai/events/extraction (추출 요청)
- 호출 시점
- CAL-M1에서 “AI 분석하기” 클릭 시
- 결과
taskId반환 → 즉시CAL-M1-2(AIWaitingModal)오픈
13) GET /api/ai/tasks/{taskId} (상태 조회 폴링)
- 호출 시점
AIWaitingModal이 열려있는 동안만 폴링
- 성공 시
status=COMPLETED+result수신 →CAL-M2(AIGeneratedConfirmModal)로 이동
- 실패/취소 시
- 토스트 안내 + CAL-M1로 복귀(첨부 유지)
14) DELETE /api/files/{fileId} (선택)
- 호출 시점
- 사용자가 첨부 취소/정리할 때
- 정책
- 서버/스토리지 비용 고려해서 “취소 시 삭제” 여부 팀 합의 필요
8️⃣ 라우팅 (Routing)
/calendar- 로그인 필요(보호 라우트)
- 모달은 라우팅이 아니라 “UI 레이어”로 관리(필요 시 URL 연동은 범위 밖)
9️⃣ 폼 처리 및 유효성 검증 (Forms & Validation)
유효성 검증 로직
- 클라이언트 측
- 제목: 15자 이하, 필수
- 회사명: 15자 이하, 필수
- 단계: 필수(라디오)
- 설명: 100자 이하(선택)
- 태그: 최대 5개
- 텍스트 입력(AI): 1000자 제한
- 파일 첨부:
- 이미지: jpg/jpeg/png/webp, 최대 5장, 각 2MB
- 파일: pdf/doc/docx, 최대 1개, 10MB
- 알림:
- 분(1
59) / 시간(123) / 일(1365) / 주(152)
- 분(1
- 서버 측(백엔드)에서도 별도 검증 로직 존재
- 서버가 최종 validation 수행(보안/정합성)
- 프론트는 “사용자 경험(즉시 피드백)” 목적의 1차 방어
이외 고려사항들 (Other Considerations) (Optional)
- 테스트 전략
- 단위 테스트: 날짜 계산(월/주 이동), 정렬 로직(일정/To-Do), 필터 적용
- 통합 테스트: 모달 흐름(CAL-M1 → 대기 → CAL-M2 → 저장)
- E2E:
- 월간/주간 전환 후 일정 카드가 일치하는지
- 일정 추가→저장→카드 반영
- 삭제 후 토스트 + 목록 제거
- AI 분석 취소/실패 시 복귀 UX
- 로깅 및 분석(Logging & Analytics)
- AI 분석 요청 성공/실패/취소 이벤트 로깅
- 일정 생성/수정/삭제 주요 이벤트 로깅(에러 원인 파악)
- 시간/타임존
- date/datetime-local 혼용(스펙):
- 시간 명시 일정: datetime
- 시간 없는 일정: date
- 서버와 클라이언트의 타임존 처리 기준 합의 필요
- date/datetime-local 혼용(스펙):
- 성능
- 월/주 변경 시 전체 재렌더 최소화
- 일정 점/기간선 렌더링 최적화(메모이제이션)
용어 정의
- viewMode(월간/주간): 캘린더가 월 단위로 보이냐, 주 단위로 보이냐
- focusedDate: 현재 “보고 있는 기간”의 기준 날짜(이전/다음 이동 기준)
- selectedDate: 사용자가 클릭해서 “선택한 날짜”(카드/To-Do가 이 날짜 기준으로 바뀜)
- 단일 일정: 하루 안에서 끝나는 일정(점으로 표시)
- 기간 일정: 시작~종료가 있는 일정(선으로 표시)
- 임박 일정: 날짜 기준 3일 이내 일정(임박 뱃지 + D-day)
- AI Draft: AI가 공고에서 추출해 만든 “임시 일정 세트”(CAL-M2에서 확인 후 저장)