기존 코드
프로필 수정 폼에서 생년월일 입력 필드는 단순히 날짜를 텍스트로 입력받는 방식이었습니다. 이는 사용자 경험이 다소 불편할 수 있어, 달력 컴포넌트(Calendar)를 사용하여 날짜를 선택하는 방식으로 개선했습니다.
{/* 생년월일 입력 필드 */}
<FormField
control={form.control}
name="birth"
render={({ field }) => (
<FormItem>
<FormLabel className="text-gray-800">생년월일</FormLabel>
<FormControl className="text-gary-700 px-6 py-4 rounded-xl">
<Input className="h-full text-text-xl" type="date" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
수정된 코드
날짜 입력 필드를 개선하기 위해 Popover와 Calendar 컴포넌트를 사용했습니다. 달력에서 날짜를 선택하면 입력값이 자동으로 업데이트되며, 포맷팅된 날짜가 표시됩니다.
<FormField
control={form.control}
name="birth"
render={({ field }) => (
<FormItem>
<FormLabel className="text-gray-800">생년월일</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-full h-full text-text-xl text-left text-gray-800",
!field.value && "text-muted-foreground"
)}
>
{field.value ? format(field.value, "PPP") : <span>날짜 선택</span>}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) => date > new Date() || date < new Date("1900-01-01")}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
주요 변경 사항
- 사용자 경험 개선:
- Calendar와 Popover를 활용하여 날짜를 선택할 수 있는 UI를 구현했습니다.
- 선택한 날짜는 date-fns의 format 함수로 포맷팅되어 "PPP" 형식(예: "March 10th, 2024")으로 표시됩니다.
- 코드 간결화:
- 날짜 선택을 위한 버튼과 달력을 구성해 불필요한 텍스트 입력 대신 직관적인 인터페이스 제공합니다.
- 유효성 검증:
- 달력에서 오늘 이후의 날짜와 1900년 이전의 날짜는 비활성화하여 유효하지 않은 입력을 방지합니다.
오류 발생 1
1. 문제 설명: selected 속성 타입 불일치
- Calendar 컴포넌트의 selected 속성에 string 타입 값이 전달되어 다음과 같은 타입 오류가 발생했습니다.
Type 'string' is not assignable to type 'Matcher | Matcher[] | undefined'.ts(2322)
The selected day.
2. 원인 분석
- Calendar 컴포넌트의 selected 속성은 반드시 Date 객체를 받아야 합니다.
- 하지만 field.value는 string 값으로 설정되어 있어 타입 불일치 문제가 발생했습니다.
3. 해결 방법: Date 객체로 변환
- field.value를 Date 객체로 변환하여 selected 속성에 전달합니다.
- 선택한 날짜를 onSelect 핸들러를 통해 Date 타입으로 처리합니다.
오류 발생 2
1. 문제 설명: 선택한 날짜가 하루 전날로 표시되는 문제
- 캘린더에서 날짜를 선택했을 때, 표시되는 값이 하루 전날로 나타나는 문제가 발생했습니다. 이는 toISOString과 관련된 시간대(Timezone) 설정 문제로, 선택한 날짜가 UTC로 변환되면서 발생합니다.
2. 원인 분석
- toISOString 메서드는 UTC 기준 시간을 반환합니다.
- 예를 들어, 한국 시간(UTC+9)에서 2024-11-25를 선택하면 내부적으로 2024-11-25T00:00:00.000+09:00으로 저장됩니다. 이를 toISOString으로 변환하면 2024-11-24T15:00:00.000Z로 변환되어 날짜가 하루 전으로 보이는 문제가 생깁니다.
3. 해결 방법: 로컬 시간대 반영
- toISOString 대신 date-fns 라이브러리의 format 메서드를 사용해 YYYY-MM-DD 형식으로 날짜를 처리합니다.
- 시간대 문제를 해결하며, 사용자에게 로컬 시간대 기준 날짜를 정확히 표시할 수 있습니다.
수정된 코드
<FormField
control={form.control}
name="birth"
render={({ field }) => (
<FormItem>
<FormLabel className="text-gray-800">생년월일(필수)</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl className="text-gary-700 px-6 py-4 rounded-xl">
<Button
variant={"outline"}
className={cn(
"w-full h-full text-text-xl text-left text-gray-800",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(new Date(field.value), "yyyy-MM-dd") // 선택된 날짜 포맷
) : (
<span className="text-gray-800">생년월일을 선택해주세요.</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value ? new Date(field.value) : undefined} // `Date` 타입 변환
onSelect={(date) =>
field.onChange(date ? format(date, "yyyy-MM-dd") : undefined) // 로컬 시간대로 처리
}
disabled={(date) => date > new Date() || date < new Date("1900-01-01")}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
주요 변경 사항
- Date 객체로 변환:
- field.value ? new Date(field.value) : undefined를 통해 string 값을 Date 객체로 변환.
- format 사용:
- 날짜 선택 시 format(date, "yyyy-MM-dd")을 사용해 사용자에게 로컬 시간대 기준 날짜를 표시.
- onSelect 핸들러:
- 선택된 날짜를 처리할 때 Date 객체로 저장하여 타입과 시간대 문제를 동시에 해결.
오류 해결 과정에서 배운 점
타입스크립트의 타입 엄격성 이해
- TypeScript는 컴포넌트의 속성 타입을 엄격히 검사하여 개발 중에 발생할 수 있는 런타임 에러를 사전에 방지합니다. 이번 오류를 통해 타입 선언이 잘못되었을 때 TypeScript가 얼마나 유용하게 문제를 알려주는지 다시금 깨달았습니다.
- 특히, 외부 라이브러리(shadcn)의 컴포넌트를 사용할 때, 공식 문서를 꼼꼼히 읽고 올바른 타입을 전달해야 한다는 점을 배웠습니다.
시간대(Timezone) 관련 문제 이해
- 브라우저가 기본적으로 로컬 시간대를 기반으로 하지만, 일부 메서드(toISOString)는 UTC를 기준으로 동작한다는 점을 알게 되었습니다.
- 이로 인해 날짜가 변경되는 문제가 발생할 수 있음을 경험하며, 날짜/시간 데이터를 다룰 때 시간대가 중요한 요소임을 깨달았습니다.
협업 도구와 문서화의 중요성
- 이번 오류 해결 과정은 단순히 문제를 해결하는 것뿐만 아니라, 해결 과정을 정리하고 문서화하는 것이 나중에 팀원들과 지식을 공유하거나 유사한 문제를 해결할 때 큰 도움이 될 수 있음을 느꼈습니다.
'🚀 프로젝트 > 따꼼' 카테고리의 다른 글
| 리팩토링 방향 정리 (0) | 2025.03.15 |
|---|---|
| 최종프로젝트 그 후, 그리고 다시 시작하는 리팩토링 (0) | 2025.03.14 |
| 따꼼 트러블 슈팅 | 이미지 업로드 오류: InvalidKey 해결 방법 (0) | 2024.11.25 |
| 따꼼 트러블 슈팅 | React + Supabase 중복 데이터 삽입 문제 해결하기 (0) | 2024.11.20 |
| 따꼼 트러블 슈팅 | 아이카드에서 기본 프로필 이미지 오류 해결 과정 (0) | 2024.11.19 |