
ν μ€ Frontend Fundamentals λͺ¨μκ³ μ¬ 2νμ°¨μ 1νμ°¨μ μ΄μ΄ μ°Έμ¬νμ΄μ.
μ£Όμ΄μ§ νμμ€ μμ½ μμ€ν μ½λλ₯Ό 리ν©ν λ§νλ κ² μ΄λ² κ³Όμ μμ΄μ. μ½λλ₯Ό μ§μ κ°μ νκ³ , μλ‘μ PRμ 리뷰νλ©΄μ "μ’μ μ½λλ 무μμΈκ°"μ λν΄ κΉμ΄ κ³ λ―Όν μ μλ μκ°μ΄μμ΄μ. μ΄λ² κΈμμλ μΈμ μμ μΈμ κΉμλ λ΄μ©κ³Ό μ κ° μ§μ κ³ λ―Όνλ κ²λ€μ ν¨κ» μ 리ν΄λ΄€μ΄μ.
λͺ¨μκ³ μ¬ λ¦¬λ·°μμ κ°μ₯ κΈ°μ΅μ λ¨λ λ§μ΄μμ΄μ.
"λλ λͺ¨λ κ²μ μ λ ₯λ°κ³ ν΄μνλ κ² μλλΌ, μ΄λ μ λλ μμΈ‘νλ€."
μ½λλ₯Ό λ³Ό λ ν μ€ ν μ€ μ½κΈ°λ³΄λ€λ, ν¨μλͺ μ΄λ ꡬ쑰λ₯Ό λ³΄κ³ "μ΄ λ€μμ μ΄λ κ² λκ² μ§" νκ³ μμΈ‘νλ©΄μ μ½μμμ. κ·Έ μμΈ‘μ΄ λ§μλ¨μ΄μ§μλ‘ μ½κΈ° νΈνκ³ , λΉλκ°λ©΄ "μ΄?" νκ³ λ©μΆκ² λκ³ μ.
PR 리뷰μμλ μ¬λ¬ μ½λ μ€ λͺ κ°μ§ ν¨ν΄μ λ½μμ κ°μ΄ μ΄μΌκΈ°νμ΄μ. "μ μ΄λ° κ² μμΈ‘μ΄ μ λλ 거ꡬλ" μΆμλ κ²λ€μ΄μμ.
setMessage β μ¬μ©μ²μ λ©μ΄μ§μλ‘ μ΄λ¦μ λΆλ΄μ΄ 컀μ§λ€κ·Έμ€ νλκ° message μνμλλ°, ν₯λ―Έλ‘μ λ 건 μ΄λ¦λ³΄λ€λ μ λ¬ λ°©μμ΄μμ΄μ. λ©μμ§λ₯Ό μ€μ λ‘ μ¬μ©νλ μ»΄ν¬λνΈ κ°κΉμ΄μ λ¬λ λμ ν
λ°, setMessageκ° propsλ‘ κ³μ λ΄λ €λ°λ ννμκ±°λ μ.
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
const handleCancel = async (id: string) => {
try {
await cancelMutation.mutateAsync(id);
setMessage({ type: 'success', text: 'μμ½μ΄ μ·¨μλμμ΅λλ€.' });
} catch {
setMessage({ type: 'error', text: 'μ·¨μμ μ€ν¨νμ΅λλ€.' });
}
};
setMessageλΌλ μ΄λ¦λ§ λ΄μλ μ΄λ€ λ§₯λ½μ λ©μμ§μΈμ§ β μ±ν
μΈμ§, μλ¦ΌμΈμ§, νΌλλ°±μΈμ§ β μ μΈλΆκΉμ§ μ¬λΌκ°μ type: 'success' | 'error' ꡬ쑰λ₯Ό νμΈν΄μΌλ§ μ μ μμ΄μ. μ¬μ©μ²μμ λ©μ΄μ§μλ‘ μ΄λ¦μ΄ μ ΈμΌ νλ λΆλ΄μ΄ 컀μ§λ κ±°μμ.
λ°μ΄ν°λ₯Ό μ°λ κ³³ κ°κΉμ΄μ λλ κ²λ§μΌλ‘λ, μ΄λ¦μ΄ μ£Όλ λΆλ΄μ΄ μμ°μ€λ½κ² μ€μ΄λ€ μ μκ² λ€ μΆμμ΄μ.
setDate β propsλ λ΄λΆλ§ λ°λΌλ΄μΌ νλ€λ λ€λ₯Έ ν¨ν΄μ DatePickerμ date, setDateλ₯Ό μ§μ λκΈ°λ ννμμ΄μ.
// νΈμΆνλ μͺ½μ λ§₯λ½μ΄ κ·Έλλ‘ λ
ΈμΆλλ€
<div>
<Text as="label">λ μ§</Text>
<input
type="date"
value={date}
onChange={e => setDate(e.target.value)}
min={formatDate(new Date())}
/>
</div>
μΈμ μμ μ΄λ° μ΄μΌκΈ°κ° λμμ΄μ.
"ν¨μκ° μμ μ΄ νΈμΆλλ μͺ½μ λ§₯λ½μ κ°μ§λ μκ° μ¬μ¬μ©μ±μ΄ λ¨μ΄μ§λ€."
setDateλΌλ μ΄λ¦μ "λΆλͺ¨μ dateλΌλ stateκ° μκ³ κ·Έκ±Έ setνλ€"λ λ§₯λ½μ κ·Έλλ‘ λλ¬λ΄μ. DatePicker μ
μ₯μμλ "κ°μ΄ λ°λμλ€"λ κ²λ§ μλ©΄ λλλ°, νΈμΆνλ μͺ½μ μ¬μ κΉμ§ μκ³ μλ κ±°μ£ .
valueμ onChangeλΌλ μΌλ°μ μΈ μΈν°νμ΄μ€λ₯Ό λ°λ₯΄λ©΄ ν¨μ¬ μμ°μ€λ¬μμ. labelμ DatePickerμ κ΄μ¬μ¬κ° μλλκΉ λ°κΉ₯μμ μ°λ μͺ½μ΄ μ±κΈ°λ©΄ λκ³ , DatePickerλ λ μ§ μ
λ ₯ κ·Έ μ체μλ§ μ§μ€νλ©΄ λΌμ.
// β
DatePickerλ λ μ§ μ
λ ₯λ§ μ±
μμ§λ€
interface DatePickerProps {
value?: string;
defaultValue?: string;
min?: string;
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
onBlur?: (e: FocusEvent<HTMLInputElement>) => void;
name?: string;
}
export const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(
({ value, defaultValue, min, onChange, onBlur, name }, ref) => {
const inputProps = value !== undefined ? { value } : { defaultValue };
return (
<input ref={ref} type="date" {...inputProps} min={min}
onChange={onChange} onBlur={onBlur} css={inputStyle} />
);
}
);
onDateChangeμ²λΌ κ΅³μ΄ μ€λ³΅λ λ§₯λ½μ λ£μ νμλ μμ΄μ. μ΄λ―Έ DatePickerλΌλ μ»΄ν¬λνΈ μ΄λ¦μ΄ λ§₯λ½μ μΆ©λΆν μ€λͺ
νκ³ μμΌλκΉμ.
navigate('/', { state: { message } }) β μ¨κ²¨μ§ λ°μ΄ν° μ λ¬μ΄κ²λ κ°μ΄ μ΄μΌκΈ°νλ ν¨ν΄μ΄μμ.
navigate('/', { state: { message: 'μμ½μ΄ μλ£λμμ΅λλ€!' } });
navigateμ stateλ‘ λ©μμ§λ₯Ό λκΈ°λ©΄ "νμΌλ‘ μ΄λνλ€"κΉμ§λ μμΈ‘λλλ°, "κ·Έλ¬λ©΄μ μ±κ³΅ λ©μμ§λ₯Ό μ λ¬νλ€"λ μ¨κ²¨μ Έ μμ΄μ. λ°λ μͺ½μμλ location.stateλ₯Ό κΊΌλ΄μ νμΈνκ³ , history.replaceStateλ‘ μλ μ 리κΉμ§ ν΄μΌ ν΄μ.
// λ°λ μͺ½: μ¨κ²¨μ§ λ‘μ§μ΄ νμνλ€
const locationState = location.state as { message?: string } | null;
const [message, setMessage] = useState(
locationState?.message ? { type: 'success', text: locationState.message } : null
);
useEffect(() => {
if (locationState?.message) {
window.history.replaceState({}, ''); // state μλ μ 리κΉμ§ νμ
}
}, [locationState]);
stateλ λΈλΌμ°μ νμ€ν 리μ 묻νλ²λ¦¬λ λ°μ΄ν°λΌ λλ²κΉ
λ μ΄λ ΅κ³ , λ°λ μͺ½μλ μ¨κ²¨μ§ λ‘μ§μ΄ νμν΄μ Έμ.
κ²°κ΅ μ΄λ¦μ΄ μν μ μ¨κΈ°κ±°λ, λ°μ΄ν°κ° 보μ΄μ§ μλ κ²½λ‘λ‘ μ λ¬λλ©΄ μ½λ μ¬λ μ μ₯μμ μμΈ‘μ΄ κΉ¨μ§λ€λ κ±°μμ΄μ.
μΆμνλ₯Ό ν λ "μ΄λκΉμ§ κ³μΈ΅μ μ ν κ±°λ, μ΄λκΉμ§ μλλ₯Ό λ΄μμ 보μ¬μ€ κ±°λ"κ° μ€μνλ€λ μ΄μΌκΈ°κ° λμμ΄μ. κ²°κ΅ λ΄κ° μ€κ³νλ μ»΄ν¬λνΈκ° μ΄λκΉμ§λ₯Ό μκΈ° κ΄μ¬μ¬λ‘ λ³Ό κ²μΈκ°λ₯Ό μ νλ κ±°λλΌκ³ μ.
RoomBookingPage (μ‘°μ¨μ)
βββ BookingFilterSection (νν° μ
λ ₯)
β βββ DatePicker
β βββ TimeSelect
β βββ AttendeesInput
β βββ FloorSelectField
β βββ EquipmentSelector
βββ AvailableRoomList (κ²°κ³Ό νμ + λ°μ΄ν° νμΉ)
βββ SubmitButton (μ μΆ)
κ°μ μκΈ° λ°μ€ μμ μΌλ§ μ± μμ§λ©΄, λμ€μ νν° UIκ° μ¬μ΄λλ°λ‘ λ°λμ΄λ, λͺ©λ‘μ΄ μΉ΄λνμΌλ‘ λ°λμ΄λ μλ‘ μν₯μ μ μ€μ.
filterPanelλ μ½λ 리뷰μμ λμλ μ΄μΌκΈ°μμ. μ¬λ¬ νν°λ€μ νλμ panelλ‘ λ¬Άμ ꡬ쑰μλλ°, λ°κΉ₯μμ 보면 νν° μμμ΄λΌλ 건 μκ² λλ° μμ μ΄λ€ μ λ³΄κ° μλμ§λ κ°λ μ΄ μ μ λμ΄μ.
κ·Έλ¬λ©΄μ μ€νλ € "κ΅³μ΄ μ¨κΈΈ νμκ° μμλ?"λΌλ μκ°μ΄ λ€μμ΄μ. μΆμν κ³μΈ΅μ΄ νλ λ μκΈ°λ©΄ λ°κΉ₯μμ μμΈ‘μ΄ μ¬μμ ΈμΌ νλλ°, μμ΄ μ 보μ΄λ μ±λ‘ λ¬Άμ΄κΈ°λ§ νλ€λ©΄ λΉμ© λλΉ μ»λ κ² μλμ§ λ°μ Έλ΄μΌ νλ κ±°λκΉμ.
μ¨κΈ°λ κ² λͺ©μ μ΄ μλλΌ, μ½λ μ¬λμ μμΈ‘μ λλ κ² λͺ©μ μ΄λΌλ κ² μ΄ λλͺ©μμλ λ§λΏμ μμμ΄μ.
μ½λ 리뷰 μκ°μ λμ¨ μ§λ¬Έμ΄μμ. νμ presentational μ»΄ν¬λνΈλ‘ λ§λ€μ΄μΌ ν μ§, μλλ©΄ λ΄λΆμμ νμΉνλλ‘ ν΄μΌ ν μ§ κ³ λ―Όλλ€λ μ΄μΌκΈ°μμ΄μ.
κ²°κ΅ presentationalμ΄λ λ΄λΆ νμΉμ΄λ보λ€, μ΄ μ»΄ν¬λνΈμ ν΅μ¬ κ΄μ¬μ¬κ° λμ§λ₯Ό λ¨Όμ λ°μ§λ κ² λ§λ κ² κ°μμ. λλ©μΈ λ§₯λ½μ μ₯κ³ μμ΄μΌ νλ μ»΄ν¬λνΈλΌλ©΄ λ΄λΆμμ μ§μ κ°μ Έμ€λ κ² λ μμ°μ€λ½κ³ , μμνκ² UIλ§ μ± μμ§λ μ»΄ν¬λνΈλΌλ©΄ propsλ‘ λ°λ κ² λ§κ³ μ. μ΄λ μͺ½μ΄ νμ μ³λ€κΈ°λ³΄λ€, μ»΄ν¬λνΈκ° 무μμ μμμΌ νλμ§μ λ°λΌ λ¬λΌμ§λ κ±°μμ.
jotaiλ zustand κ°μ μ μ μν λΌμ΄λΈλ¬λ¦¬λ₯Ό μ°λ©΄ νΈνλκΉ μκΎΈ μ¬κΈ°μ μνλ₯Ό λ£κ³ μΆμ μ νΉμ΄ μκΈ°μμμ. κ·Όλ° νλ² λ¬Όμ΄λ΄μΌ ν΄μ. "μ΄ μν, μ§μ§ λ€λ₯Έ νμ΄μ§μμλ μ°λ?"
λλΆλΆμ μλμμ. νμμ€ μμ½ νμ΄μ§μ νν° κ°μ κ·Έ νμ΄μ§μμλ§ μλ―Έκ° μμ§, ν νλ©΄μ΄λ μμ½ νν© νμ΄μ§μμ μ νμκ° μκ±°λ μ. κ·Έλ° μνκΉμ§ μ μ storeμ λ£μΌλ©΄ "μ΄ atomμ΄ μ΄λμ μ°μ΄λ κ±°μ§?" νκ³ μΆμ ν΄μΌ νλ λΉμ©μ΄ μ겨μ.
μ μ μνμ λ΄κΈ° μ ν©ν 건 μ΄λ° κ²λ€μ΄μμ.
λ°λλ‘ νΉμ νμ΄μ§μ νΌ κ°, νν° μ‘°κ±΄, λͺ¨λ¬ μ΄λ¦Ό μν κ°μ 건 κ·Έ νμ΄μ§ μμμ ν΄κ²°νλ κ² λ§μμ. URL searchParamsλ , μ§μ stateλ , μ¬μ©μ²μμ κ°κΉμ΄ κ³³μ λλ κ±°μ£ .
λ§μ§λ§μΌλ‘ κ°μ₯ μλΏμλ μ΄μΌκΈ°μμ.
"μ½λλ₯Ό μμ±ν μ¬λμΌλ‘μ κ·Όκ±°λ₯Ό μ μΆν μ μμΌλ©΄ λλ€."
"μ λ΅μ΄ μλλκΉ. μ£Όμ₯μ΄ μ€μνλ€. μ λ΅μ΄λΌκ³ λ―Ώκ³ λ°λΌκ°λ©΄ μ λλ€."
κ°λ°νλ€ λ³΄λ©΄ "μ΄κ² λ§λ 건κ°?" μΆμ λκ° λ§μμμ. κ·Όλ° μ€μν 건 μ λ΅μ μ°Ύλ κ² μλλΌ, λ΄κ° μ μ΄λ κ² νλμ§ μ€λͺ ν μ μλλλΌλ κ±°μμ.
μ²μ RoomBookingPageλ 402μ€μ§λ¦¬ λ¨μΌ νμΌμ΄μμ΄μ. μν μ μΈ, URL λκΈ°ν, API νΈμΆ, νν° κ²μ¦, νν°λ§ λ‘μ§, λ λλ§μ΄ μ λΆ ν κ³³μ μμκ±°λ μ.
// β Before: μλ³Έ RoomBookingPage (402μ€)
export function RoomBookingPage() {
const [date, setDate] = useState(searchParams.get('date') || formatDate(new Date()));
const [startTime, setStartTime] = useState(searchParams.get('startTime') || '');
// ... μν 8κ°
useEffect(() => {
// 6κ° μνλ₯Ό URLκ³Ό μΌμΌμ΄ λκΈ°ν
setSearchParams(params, { replace: true });
}, [date, startTime, endTime, attendees, equipment, preferredFloor]);
// νν°λ§ λ‘μ§, μΆ©λ κ²μ¬κΉμ§ μ λΆ μΈλΌμΈμΌλ‘...
return (
<div css={css`background: ${colors.white}; padding-bottom: 40px;`}>
{/* 300μ€ μ΄μμ JSX */}
</div>
);
}
μΈμ μμ "κΈ°λ₯μ λν μ λ³΄κ° μλλΌλ©΄ μλ΄ νμ§νμ²λΌ μΈλΆμμ μλ΄νκ³ , μΈλΆ μ 보λ μλμ λ¨κΈ΄λ€"λ μ΄μΌκΈ°κ° λμλλ°, λ± νμ΄μ§ μ»΄ν¬λνΈνν ν΄λΉλλ λ§μ΄λΌκ³ λκΌμ΄μ.
리ν©ν λ§νλ©΄μ νμ΄μ§ μ»΄ν¬λνΈλ₯Ό νλ¦λ§ 보μ¬μ£Όλ μλ΄νμΌλ‘ λ§λ€μμ΄μ.
// β
After: μμμ μλλ‘ μ½νλ μ μΈμ ꡬ쑰
export function RoomBookingPage() {
const navigate = useNavigate();
const queryClient = useQueryClient();
const {
isFilterValid, errorMessage, isPendingBooking,
selectedRoomId, handleRoomSelect, handleFilterChange, handleSubmit,
} = useBookingForm();
return (
<div css={pageStyle}>
<button onClick={() => navigate(ROUTES.HOME)}>β μμ½ νν©μΌλ‘</button>
<Top.Top03>μμ½νκΈ°</Top.Top03>
{errorMessage && <Text color={colors.red500}>{errorMessage}</Text>}
<BookingFilterSection onFilterChange={handleFilterChange} />
{!isFilterValid ? (
<Text>{'μμ½ μ‘°κ±΄μ λ¨Όμ μ νν΄ μ£ΌμΈμ.'}</Text>
) : (
<ErrorBoundary onReset={() => queryClient.resetQueries({ queryKey: ['availableRooms'] })}>
<Suspense fallback={<AvailableRoomList.Loading />}>
<AvailableRoomList selectedRoomId={selectedRoomId} onSelectRoom={handleRoomSelect} />
</Suspense>
</ErrorBoundary>
)}
<Button onClick={handleSubmit} disabled={!isFilterValid || isPendingBooking}>
{isPendingBooking ? 'μμ½ μ€...' : 'νμ '}
</Button>
</div>
);
}
νμΌ μ΄μμ λ "νν° μ ννκ³ β λͺ©λ‘ λ³΄κ³ β νμ νλ νμ΄μ§κ΅¬λ"κ° λ°λ‘ 보μ¬μ. μΈλΆ ꡬνμ΄ κΆκΈνλ©΄ νκ³ λ€μ΄κ°λ©΄ λκ³ μ.
λ€λ§ μ§κΈ μ½λλ₯Ό λ€μ 보면 μμ¬μ΄ λΆλΆλ μμ΄μ. AvailableRoomList μμ "μμ½ κ°λ₯ νμμ€ Nκ°" κ°μ νμ΄νμ΄λ λΉ μν λ©μμ§μ²λΌ μ΄ νμ΄μ§μ λ§₯λ½μ λνλ΄λ λ΄μ©μ΄ μ»΄ν¬λνΈ λ΄λΆμ λ€μ΄κ° μκ±°λ μ. λ€μ μμ±νλ€λ©΄ κ·Έλ° λΆλΆμ νμ΄μ§μμ μ§μ λ΄λΉνκ³ , μ»΄ν¬λνΈλ μ λ§ λͺ©λ‘ λ λλ§μλ§ μ§μ€νλλ‘ λ μ’νμ κ² κ°μμ.
μ΄ λΆλΆμ μ½λλ₯Ό μ§λ©΄μ μ λ κ½€ κ³ λ―Όνλ μ§μ μ΄μμ.
μ²μμ react-hook-formμΌλ‘ νΌ μνλ₯Ό κ΄λ¦¬νλλ°, μ΄ νμ΄μ§μμλ νν° μ‘°κ±΄μ΄ URLμλ λ°μλΌμΌ νμ΄μ. λ€λ‘κ°κΈ°λ λμ΄μΌ νκ³ , λ§ν¬λ‘ 곡μ λ κ°λ₯ν΄μΌ νλκΉμ.
λ¬Έμ λ react-hook-formμ΄ κ΄λ¦¬νλ μνμ URL searchParamsλ₯Ό λκΈ°ννλ useEffectκ° λ°λ‘ νμνλ€λ κ±°μμ. μνκ° λ κ΅°λ°μ μμΌλκΉ μκΎΈ μ±ν¬κ° μ΄κΈλλ λλμ΄μμ΄μ.
// β useState 8κ° + useEffectλ‘ URL λκΈ°ν = μ΄μ€ κ΄λ¦¬
const [date, setDate] = useState(searchParams.get('date') || formatDate(new Date()));
const [startTime, setStartTime] = useState(searchParams.get('startTime') || '');
// ... 6κ° λ
useEffect(() => {
const params: Record<string, string> = {};
if (date) params.date = date;
if (startTime) params.startTime = startTime;
// ...
setSearchParams(params, { replace: true });
}, [date, startTime, endTime, attendees, equipment, preferredFloor, setSearchParams]);
κ·Έλμ μμ URL searchParams μ체λ₯Ό μνλ‘ μ°κΈ°λ‘ νμ΄μ.
// β
URL searchParamsκ° κ³§ μν (useBookingParams.ts)
export function useBookingParams() {
const [searchParams] = useSearchParams();
return {
date: searchParams.get('date') ?? dayjs().format('YYYY-MM-DD'),
start: searchParams.get('start') ?? '',
end: searchParams.get('end') ?? '',
attendees: Number(searchParams.get('attendees')) || 1,
equipment: searchParams.get('equipment')?.split(',').filter(Boolean) ?? [],
preferredFloor: searchParams.get('floor') ? Number(searchParams.get('floor')) : null,
};
}
// β
κ° λ³κ²½λ ν κ³³μμ (useUpdateBookingParam.ts)
export function useUpdateBookingParam() {
const [, setSearchParams] = useSearchParams();
return (partial: Record<string, unknown>) => {
setSearchParams(prev => {
// partialμ κ°λ€μ searchParamsμ λ°μ
}, { replace: true });
};
}
μΆμ²λ₯Ό νλλ‘ μ’νλκΉ λκΈ°ν μ κ²½ μΈ κ² μμ΄μ‘μ΄μ. useState 8κ° + useEffect λκΈ°νκ° ν΅μ§Έλ‘ μ¬λΌμ§ μ μ΄μμ.
μ΄λ κ² νλ©΄μ μ€μ€λ‘ λ΄λ¦° κ·Όκ±°λ "URLμ΄ single source of truthλκΉ μ΄μ€ κ΄λ¦¬λ₯Ό μμ μ"μμ΄μ. νν° λ³κ²½ β URL λ³κ²½ β 쿼리 리νμΉλΌλ λ¨λ°©ν₯ νλ¦μ΄ λ μμΈ‘ κ°λ₯νλ€κ³ λκΌκ³ μ.
κ·Έλ°λ° μΈμ
μ΄νμ useBookingFormμ μΈν°νμ΄μ€μ λν μΆκ° νΌλλ°±μ λ°μΌλ©° λ κ³ λ―Όν΄λ³΄κ² λμ΄μ.
const {
isFilterValid, errorMessage, isPendingBooking,
selectedRoomId, handleRoomSelect, handleFilterChange, handleSubmit,
} = useBookingForm();
formμ΄ μ΄λ¦μ λ€μ΄κ°λ μκ° μΈλΆ ꡬνμ λ³΄μ§ μμλ react-hook-formμ useFormμ²λΌ λμν κ±°λΌλ κΈ°λλ₯Ό μ¬μ΄μ£Όκ² λλ€λ κ±°μμ΄μ. μ€μ μΈν°νμ΄μ€μ μ΄κΈλλ€ λ³΄λ μμΈ‘μ κΉ¨λ μ΄λ¦μ΄ λ κ² κ°κΈ°λ νκ³ μ.
μκ°ν΄λ³΄λ useBookingFormμ΄ νν° μ‘°κ±΄(searchParams κ°λ€)κ³Ό μμ½ μ‘μ
(λ£Έ μ ν, μ μΆ, μλ¬ μ²λ¦¬)μ νκΊΌλ²μ μ₯κ³ μλ€λ κ² λ¬Έμ μΈ κ² κ°μμ. useBookingFilterμ useRoomBookingμ²λΌ κ΄μ¬μ¬λ³λ‘ λΆλ¦¬νλ€λ©΄ κ° hookμ΄ λ¬΄μμ λ°ννλμ§ μ΄λ¦λ§μΌλ‘λ λ λͺ
ννκ² μμΈ‘ν μ μμ κ² κ°μμ. μμ§ κ΅¬μ²΄μ μΈ ννλ κ³ λ―Ό μ€μ΄μ§λ§, λΆλ¦¬νλ λ°©ν₯μΌλ‘ κ°μ ν΄λ³΄κ³ μΆμ΄μ.
μ΄λ² λͺ¨μκ³ μ¬λ₯Ό ν μ€λ‘ μμ½νλ©΄, "μ μ΄λ κ² μ§°μ΄?"μ λλ΅ν μ μλ μ½λλ₯Ό μ°μμΈ κ² κ°μμ.
μμΈ‘ κ°λ₯ν μ΄λ¦, μνμ μΆμ²λ₯Ό νλλ‘, λ°μ΄ν°λ μ°λ κ³³ κ°κΉμ΄μ, μ μ μνλ μ§μ§ μ μμΈ κ²λ§. λ€ κ²°κ΅ "μ½λ μ¬λμ΄ μμΈ‘ν μ μκ²"λΌλ νλμ μμΉμμ λμ€λ μ΄μΌκΈ°μμ΄μ.
μμΌλ‘λ μ½λ μ§€ λ "μ΄κ±° λ€λ₯Έ μ¬λμ΄ λ³΄λ©΄ μμΈ‘ κ°λ₯νκ°?" νλ² λ μκ°ν΄λ³΄κ² λ κ² κ°μμ.
μ΄λ¦ νλ, λ°μ΄ν° μμΉ νλκ° μ½λ μ¬λμ νλ¦μ λκΈ°λ νκ³ μμ°μ€λ½κ² μ΄μ΄μ£ΌκΈ°λ νλκΉμ.
κ·Έ μ§λ¬Έ νλλ₯Ό μμΌλ‘λ μ΅κ΄μ²λΌ κ°μ Έκ°κ³ μΆμ΄μ.
κ°μ μ½λλ₯Ό μμ±νκ³ μλ‘μ μ νμ λν΄ "μ?"λ₯Ό λ¬»κ³ λ΅νλ μκ°μ΄ λ°λμλ μλ―Έ μλ μκ°μ΄μμ΅λλ€..!
3νμ°¨λ κΈ°λλλ€μ!! π
μ μ½μμ΅λλ€~ μ λ μνλμ΄ λ¨κ²¨μ£Όμ κΈμ λ³΄κ³ λ μκ° λ¨κ²¨λ΄μ!
navigateμ stateλ₯Ό μ΄μ©ν΄μ λ€λ₯Έ νμ΄μ§μ κ°μ λ겨주λ μ½λλ message λΌλ μ΄λ¦μ΄ λ§€μ° μΌλ°μ μΈ μ΄λ¦μ΄κΈ° λλ¬Έμ μλκ° λλ¬λμ§ μλ κ² κ°μμ λ¬Έμ μΈ κ² κ°μμ. A -> B νμ΄μ§λ‘ μ΄λνλ€κ³ ν λ messageλ₯Ό B νμ΄μ§κ° μΈλΆλ‘ λ ΈμΆνλ κ³μ½μ΄λΌκ³ μκ°νλ€λ©΄ B νμ΄μ§λ‘ μ΄λν λ λ°°λλ₯Ό λ ΈμΆνκ³ μΆμ κ²½μ°λ©΄ ν΄λΉ κ°μ λ€κ³ λΌμ°ν νλ κ²μ΄κΈ° λλ¬Έμ ν¬κ² μ΄μνμ§ μλ€κ³ μκ°νκ±°λ μ. url queryλ₯Ό ν΅ν΄μ νΉμ νμ΄μ§μ μ§μ νμ λ λͺ¨λ¬μ λμ°λ λ±μ ν¨ν΄λ λΉμ·νλ€κ³ μκ°νꡬμ. κ·Έλ¦¬κ³ μμ±ν΄μ£Όμ λ°λ μͺ½μ λ‘μ§μ νλμ ν μΌλ‘ μΆμνλ₯Ό ν΄λ³Ό μ μμ κ² κ°μμ. const message = useOneTimeQueryParam('message'); μ΄ ν μ κ°μ μ½μ΄μμ λ©λͺ¨λ¦¬μ μ μ₯νκ³ , λ°λ‘ μ 리νλ λλμΌλ‘μ. μλ νΉμ κ°μ²΄(B νμ΄μ§)μ μ μ₯μμλ "λ€λ₯Έ κ°μ²΄λ‘λΆν° νΉμ λ©μμ§λ₯Ό μ λ¬λ°μμ κ·Έμ λ§λ νλμ νκ² λ€" λΌλ μ½μμ νλ κ²μ΄κΈ°μ μ¬κΈ°μλ B νμ΄μ§μ μ± μμ΄λ κ΄μ¬μ¬λ₯Ό μ μ ν΄λλ©΄ λ¬΄λ¦¬κ° μλ κ² κ°μμ.
FilterPanel μ΄λΌλ μ»΄ν¬λνΈλ μμ μ΄λ€ μ λ³΄κ° μλμ§ μμμΌ νκΈ° μ μλ 미리 μ νμλ μλ€κ³ λλμλ μλ€κ³ μκ°ν΄μ. νν° ννμ UIκ° μκ³ νλ©΄μ 보μ¬μ§λ λ°μ΄ν°κ° νν°λ§μ κ±°μ³μ νλ©΄μ 보μ¬μ§ μ μλ€λΌλ λ§₯λ½μ μλ κ²μΌλ‘ μΆ©λΆν μ μλ€κ³ 보거λ μ. λ§μ½ λͺ¨λ νν°κ° μΈλΆμ λ ΈμΆλμ΄ μμλ€λ©΄ μ λ "κ΅³μ΄ λ ΈμΆν νμκ° μμλ?"λΌλ μκ°μ μμΌλ‘ ν μ μμ κ² κ°μμ.
리ν©ν°λ§μ ν΄μ£Όμ μ½λμμ useBookingParams, useUpdateBookingParamμ νλμ ν μμ κ΄λ¦¬νλ©΄ μ’κ² λ€λ μκ°μ΄ λ€μμ΄μ. url queryλΌλ νλμ λ°μ΄ν°λ₯Ό μ½κ³ μ°λ μ± μμ κ°μ΄ λ΄λΉν μ μλλ‘μ΄μ. λ λλ§ κ΄μ μμ μ₯μ μ μ»μ μ μλ€κ³ λ³Ό μ μμ§λ§, search param νΉμ±μ ν° μ₯μ μ λλΌκΈ°λ μ΄λ €μΈ κ² κ°μμ. μ€νλ € 2κ°μ ν μ μ¬μ©ν΄μΌ νλ€λ³΄λ DX κ΄μ μμμ μμ¬μμ΄ μμ κ² κ°μμ.
useBookingFormμ κ°μΈμ μΌλ‘λ μ λ useFormμ μΈν°νμ΄μ€κ° κΈ°λλμ§λ μκ³ μμ½ formμ λν λ°μ΄ν°μ λ©μλλ₯Ό κ΄λ¦¬ν κ²μ΄λΌκ³ κΈ°λνκ² λλλ°μ. λ€λ§, λ¬Έμ λΌκ³ λκ»΄μ§λ μ§μ μ μκΈ°ν΄μ£Όμ κ²κ³Ό λΉμ·νκ² formμ ν΅ν΄μ μ μ μκ² μ λ ₯λ°μ κ°μ μ΄λμ μ¬μ©ν κ²μΈμ§λ λ€μνκ² νμ₯λ μ μλλ°, νμ¬λ κ·Έ μ¬μ©νλ λ‘μ§κΉμ§ κ°μ΄ μΆμνκ° λμ΄ μλ€λ³΄λ ν μ μ΄λ¦κ³Ό λ§μ§ μλ μ± μμ κ°κ³ μλ€λ κ²κ³Ό μ΄ νΌμ μ λ ₯λ κ°μ λ€λ₯Έ λͺ©μ μΌλ‘ μ°κΈ°μ ν΄λΉ ν μ μ¬μ©νλ κ²μ κ·Έ μ± μμ΄ κ³Όνκ² λ€λ λλμ΄ λλ κ² κ°μμ.