Skip to content

Commit

Permalink
[Feature] - Immer을 사용한 코드 구조 개선 (#495)
Browse files Browse the repository at this point in the history
* refactor(useTravelogueDays): immer을 활용한 로직 구조 개선

* refactor(useTravelPlanDays): transformTravelPlanDays 리팩터링

* refactor(travelTransform): 여행기 변환 타입 수정(TravelTransformDays)

* refactor: query 내 중첩 객체 구조 Immer로 개선
  • Loading branch information
jinyoung234 authored Oct 7, 2024
1 parent 9ca4b40 commit 33aeb8a
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ const TravelogueEditPage = () => {
iconType="plus"
position="left"
css={S.addButtonStyle}
onClick={() => onAddDay()}
onClick={onAddDay}
>
<Text textType="bodyBold">일자 추가하기</Text>
</IconButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ const TravelogueRegisterPage = () => {
iconType="plus"
position="left"
css={S.addButtonStyle}
onClick={() => onAddDay()}
onClick={onAddDay}
>
<Text textType="bodyBold">일자 추가하기</Text>
</IconButton>
Expand Down
22 changes: 9 additions & 13 deletions frontend/src/hooks/pages/useTravelPlanDays.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import { useCallback } from "react";

import { produce } from "immer";
import { useImmer } from "use-immer";
import { v4 as uuidv4 } from "uuid";

import type { TravelPlanDay, TravelPlanPlace } from "@type/domain/travelPlan";
import type { TravelTransformPlaces } from "@type/domain/travelTransform";
import type { TravelTransformDays } from "@type/domain/travelTransform";

import { FORM_VALIDATIONS_MAP } from "@constants/formValidation";

const transformTravelPlanDays = (days: TravelTransformPlaces[]) => {
return [...days].map((day) => ({
...day,
places: day.places.map((place) => {
return {
...place,
todos: [],
};
}),
}));
};
const transformTravelPlanDays = (travelTransformDays: TravelTransformDays[]) =>
produce(travelTransformDays, (newTravelTransformDays) => {
newTravelTransformDays.forEach(
(day) => (day.places = day.places.map((place) => ({ ...place, todos: [] }))),
);
});

export const useTravelPlanDays = (days: TravelTransformPlaces[]) => {
export const useTravelPlanDays = (days: TravelTransformDays[]) => {
const [travelPlanDays, setTravelPlanDays] = useImmer<TravelPlanDay[]>(() =>
transformTravelPlanDays(days),
);
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/hooks/pages/useTravelPlanForm.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useState } from "react";

import { TravelTransformPlaces } from "@type/domain/travelTransform";
import { TravelTransformDays } from "@type/domain/travelTransform";

import { useTravelPlanDays } from "@hooks/pages/useTravelPlanDays";

import { FORM_VALIDATIONS_MAP } from "@constants/formValidation";

import getInitialTravelTitle from "@utils/getInitialTravelTitle";

const useTravelPlanForm = (transformDays: TravelTransformPlaces[]) => {
const useTravelPlanForm = (transformDays: TravelTransformDays[]) => {
const [startDate, setStartDate] = useState<Date | null>(null);

const onSelectCalendar = (date: Date, handleCloseCalendar: () => void) => {
Expand Down
196 changes: 87 additions & 109 deletions frontend/src/hooks/pages/useTravelogueDays.ts
Original file line number Diff line number Diff line change
@@ -1,127 +1,105 @@
import { useCallback, useState } from "react";
import { useCallback } from "react";

import { produce } from "immer";
import { useImmer } from "use-immer";
import { v4 as uuidv4 } from "uuid";

import type { TravelTransformPlaces } from "@type/domain/travelTransform";
import type { TravelTransformDays } from "@type/domain/travelTransform";
import type { TravelogueDay, TraveloguePlace } from "@type/domain/travelogue";

const MIN_DESCRIPTION_LENGTH = 0;
const MAX_DESCRIPTION_LENGTH = 300;

export const useTravelogueDays = (days: TravelTransformPlaces[]) => {
const transformedDetailData = [...days].map((day) => {
return {
...day,
places: day.places.map((place) => {
return {
...place,
description: "",
photoUrls: [],
};
}),
};
});

const [travelogueDays, setTravelogueDays] = useState<TravelogueDay[]>(transformedDetailData);

const onChangeTravelogueDays = useCallback((newDays: TravelogueDay[]) => {
setTravelogueDays(newDays);
}, []);

const onAddDay = useCallback((dayIndex?: number) => {
setTravelogueDays((prevTravelDays) =>
dayIndex
? Array.from({ length: dayIndex }, () => ({ id: uuidv4(), places: [] }))
: [...prevTravelDays, { id: uuidv4(), places: [] }],
);
}, []);

const onDeleteDay = (targetDayIndex: number) => {
setTravelogueDays((prevTravelDays) =>
prevTravelDays.filter((_, dayIndex) => dayIndex !== targetDayIndex),
const transformTravelogueDays = (travelTransformDays: TravelTransformDays[]) =>
produce(travelTransformDays, (newTravelTransformDays) => {
newTravelTransformDays.forEach(
(day) =>
(day.places = day.places.map((place) => ({ ...place, description: "", photoUrls: [] }))),
);
};

const onAddPlace = (
dayIndex: number,
traveloguePlace: Pick<TraveloguePlace, "placeName" | "position">,
) => {
setTravelogueDays((prevTravelDays) => {
const newTraveloguePlaces = [...prevTravelDays];
newTraveloguePlaces[dayIndex].places.push({
...traveloguePlace,
id: uuidv4(),
photoUrls: [],
description: "",
});
return newTraveloguePlaces;
});
};

const onDeletePlace = (dayIndex: number, placeIndex: number) => {
setTravelogueDays((prevTravelDays) => {
const newTraveloguePlaces = [...prevTravelDays];
newTraveloguePlaces[dayIndex] = {
...newTraveloguePlaces[dayIndex],
places: newTraveloguePlaces[dayIndex].places.filter((_, index) => index !== placeIndex),
};
});

return newTraveloguePlaces;
export const useTravelogueDays = (days: TravelTransformDays[]) => {
const [travelogueDays, setTravelogueDays] = useImmer<TravelogueDay[]>(() =>
transformTravelogueDays(days),
);

const onChangeTravelogueDays = useCallback(
(newTravelogueDays: TravelogueDay[]) => {
setTravelogueDays(newTravelogueDays);
},
[setTravelogueDays],
);

const onAddDay = useCallback(() => {
setTravelogueDays((newTravelogueDays) => {
newTravelogueDays.push({ id: uuidv4(), places: [] });
});
};

const onChangePlaceDescription = (
e: React.ChangeEvent<HTMLTextAreaElement>,
dayIndex: number,
placeIndex: number,
) => {
const newTraveloguePlaces = [...travelogueDays];
newTraveloguePlaces[dayIndex].places[placeIndex].description = e.target.value.slice(
MIN_DESCRIPTION_LENGTH,
MAX_DESCRIPTION_LENGTH,
);
setTravelogueDays(newTraveloguePlaces);
};
}, [setTravelogueDays]);

const onChangeImageUrls = (dayIndex: number, placeIndex: number, imgUrls: string[]) =>
setTravelogueDays((prevTravelDays) =>
prevTravelDays.map((day, dIndex) => {
if (dIndex !== dayIndex) return day;

return {
...day,
places: day.places.map((place, pIndex) => {
if (pIndex !== placeIndex) return place;

return {
...place,
photoUrls: [...(place.photoUrls || []), ...imgUrls],
};
}),
};
}),
);
const onDeleteDay = useCallback(
(targetDayIndex: number) => {
setTravelogueDays((newTravelogueDays) => {
newTravelogueDays.splice(targetDayIndex, 1);
});
},
[setTravelogueDays],
);

const onAddPlace = useCallback(
(dayIndex: number, traveloguePlace: Pick<TraveloguePlace, "placeName" | "position">) => {
setTravelogueDays((newTravelogueDays) => {
newTravelogueDays[dayIndex].places.push({
...traveloguePlace,
id: uuidv4(),
photoUrls: [],
description: "",
});
});
},
[setTravelogueDays],
);

const onDeletePlace = useCallback(
(dayIndex: number, placeIndex: number) => {
setTravelogueDays((newTravelogueDays) => {
newTravelogueDays[dayIndex].places.splice(placeIndex, 1);
});
},
[setTravelogueDays],
);

const onChangePlaceDescription = useCallback(
(event: React.ChangeEvent<HTMLTextAreaElement>, dayIndex: number, placeIndex: number) => {
setTravelogueDays((newTravelogueDays) => {
newTravelogueDays[dayIndex].places[placeIndex].description = event.target.value.slice(
MIN_DESCRIPTION_LENGTH,
MAX_DESCRIPTION_LENGTH,
);
});
},
[setTravelogueDays],
);

const onChangeImageUrls = useCallback(
(dayIndex: number, placeIndex: number, imgUrls: string[]) => {
setTravelogueDays((newTravelogueDays) => {
const place = newTravelogueDays[dayIndex].places[placeIndex];
place.photoUrls = [...(place.photoUrls || []), ...imgUrls];
});
},
[setTravelogueDays],
);

const onDeleteImageUrls = (dayIndex: number, targetPlaceIndex: number, imageIndex: number) =>
setTravelogueDays((prevTravelDays) => {
return prevTravelDays.map((day, dIndex) => {
if (dIndex !== dayIndex) return day;
const onDeleteImageUrls = useCallback(
(dayIndex: number, targetPlaceIndex: number, imageIndex: number) => {
setTravelogueDays((newTravelogueDays) => {
const place = newTravelogueDays[dayIndex].places[targetPlaceIndex];

return {
...day,
places: day.places.map((place, placeIndex) => {
return placeIndex !== targetPlaceIndex
? place
: {
...place,
photoUrls: place.photoUrls
? place.photoUrls.filter((_, index) => index !== imageIndex)
: [],
};
}),
};
place.photoUrls?.splice(imageIndex, 1);
});
});
},
[setTravelogueDays],
);

return {
travelogueDays,
Expand Down
25 changes: 14 additions & 11 deletions frontend/src/queries/usePostTravelogue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AxiosError, AxiosResponse } from "axios";
import { produce } from "immer";

import { useMutation, useQueryClient } from "@tanstack/react-query";

Expand All @@ -12,6 +13,8 @@ import { API_ENDPOINT_MAP } from "@constants/endpoint";
import { ERROR_MESSAGE_MAP } from "@constants/errorMessage";
import { QUERY_KEYS_MAP } from "@constants/queryKey";

import { convertImageUrlConfig } from "@utils/queryFunction";

export const usePostTravelogue = () => {
const queryClient = useQueryClient();

Expand All @@ -21,17 +24,17 @@ export const usePostTravelogue = () => {
TraveloguePayload,
unknown
>({
mutationFn: (travelogue: TraveloguePayload) =>
authClient.post(API_ENDPOINT_MAP.travelogues, {
...travelogue,
days: travelogue.days.map((day) => ({
...day,
places: day.places.map((place) => ({
...place,
photoUrls: place.photoUrls?.map((url) => ({ url })),
})),
})),
}),
mutationFn: (traveloguePayload) =>
authClient.post(
API_ENDPOINT_MAP.travelogues,
produce(traveloguePayload, (newTraveloguePayload) => {
newTraveloguePayload.days.forEach((day) => {
day.places.forEach((place) => {
place.photoUrls = place.photoUrls?.map(convertImageUrlConfig);
});
});
}),
),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS_MAP.travelogue.me(),
Expand Down
23 changes: 13 additions & 10 deletions frontend/src/queries/usePutTravelogue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AxiosError, AxiosResponse } from "axios";
import { produce } from "immer";

import { useMutation, useQueryClient } from "@tanstack/react-query";

Expand All @@ -11,6 +12,8 @@ import { authClient } from "@apis/client";
import { API_ENDPOINT_MAP } from "@constants/endpoint";
import { QUERY_KEYS_MAP } from "@constants/queryKey";

import { convertImageUrlConfig } from "@utils/queryFunction";

interface MutationFnVariables {
travelogue: TraveloguePayload;
id: number;
Expand All @@ -26,16 +29,16 @@ export const usePutTravelogue = () => {
unknown
>({
mutationFn: ({ travelogue, id }) =>
authClient.put(API_ENDPOINT_MAP.travelogueDetail(id), {
...travelogue,
days: travelogue.days.map((day) => ({
...day,
places: day.places.map((place) => ({
...place,
photoUrls: place.photoUrls?.map((url) => ({ url })),
})),
})),
}),
authClient.put(
API_ENDPOINT_MAP.travelogueDetail(id),
produce(travelogue, (newTraveloguePayload) => {
newTraveloguePayload.days.forEach((day) => {
day.places.forEach((place) => {
place.photoUrls = place.photoUrls?.map(convertImageUrlConfig);
});
});
}),
),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS_MAP.travelogue.me(),
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/types/domain/travelTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ export type TravelTransformPlace = Pick<
keyof TravelPlanPlace & keyof TraveloguePlace
>;

export interface TravelTransformPlaces {
export interface TravelTransformDays {
id: string;
places: TravelTransformPlace[];
}

export type TravelTransformDetail = { days: TravelTransformPlaces[] };
export type TravelTransformDetail = { days: TravelTransformDays[] };
Loading

0 comments on commit 33aeb8a

Please sign in to comment.