미새문지

24.09.20 day68 작업 일지 본문

개발 TIL

24.09.20 day68 작업 일지

문미새 2024. 9. 20. 23:58
728x90

오랜만에 정글 멤버 중 전주에 사는 갱민이와 성주형을 만났다. 둘 다 전혀 변한게 없어 오히려 편안

재희님의 데이터 추가로 아이템에 브랜드와 용량 데이터를 추가하여 해당 데이터도 화면에 뿌려줬다.

 

그리고 그동안 루틴 추가를 할 때 제품을 item_key로 하여 제대로 작동이 되는지만 확인했으나 이번에 제품 검색을 구현하여 제대로 된 루틴 추가 기능을 구현했다.

이 부분을 구현하면서 너무 많은 시간이 걸렸는데 간단한 방법을 늦게 알게 되어 너무 빙빙 꼬아서 짜느라 시간 소모가 컸다.

현재 제품을 작성하는 입력칸을 item_key로 작성하던걸 item_name으로 변경하게 되며 해당 값의 타입이 맞지않아 발생했고

입력칸엔 제품 이름이 나오게 하되 나중에 api로 데이터를 보낼땐 item_key를 보내야 한다는 것이였다.

처음부터 그냥 리덕스의 루틴 단계 데이터에 제품 이름까지 추가해서 나중에 제품 이름만 빼고 보내면 되었는데 이걸 value값은 item_name으로 하되 해당 값을 다른 곳에 저장하여 전달하려 했기 때문에 에러가 많이 발생했었다.

이러한 원인때문에 제품을 찾아 클릭 시 변경이 불가능하게 되어 이거 고치느라 힘들었다.

  const dispatch = useDispatch();
  const grade = useSelector((state: RootState) => state.addRoutine.grade);
  const routineItems = useSelector(
    (state: RootState) => state.addRoutine.routineItem
  );
  const [allRoutineItems, setAllRoutineItems] = useState(
    new Array(grade).fill({
      step_number: 0,
      step_name: "",
      description: "",
      item_key: "",
      item_name: "",
    })
  );
  const [searchResults, setSearchResults] = useState<itemData[][]>(
    new Array(grade).fill([])
  );
  const [totalPrice, setTotalPrice] = useState<number>(0);
  const [searchQueries, setSearchQueries] = useState<string[]>(
    new Array(grade).fill("")
  );
  const backPort = process.env.REACT_APP_BACKEND_PORT;
  const inputRef = useRef<HTMLInputElement>(null);
  const [searchQuery, setSearchQuery] = useState<string>("");

  useEffect(() => {
    setAllRoutineItems(
      new Array(grade).fill(null).map((_, index) => ({
        step_number: index + 1,
        step_name: "",
        description: "",
        item_key: "",
        item_name: "",
      }))
    );
  }, [grade]);

  useEffect(() => {
    if (routineItems.length < grade) {
      setAllRoutineItems([
        ...routineItems,
        ...new Array(grade - routineItems.length)
          .fill(null)
          .map((_, index) => ({
            step_number: routineItems.length + index + 1,
            step_name: "",
            description: "",
            item_key: "",
            item_name: "",
          })),
      ]);
    } else {
      setAllRoutineItems(
        routineItems.map((item, index) => ({
          ...item,
          step_number: index + 1,
        }))
      );
    }
  }, [grade, routineItems]);

  const handleProductSelect = (index: number, product: itemData) => {
    const updatedItems = [...allRoutineItems];
    updatedItems[index] = {
      ...updatedItems[index],
      item_key: product.item_key,
      item_name: product.item_name,
    };

    setAllRoutineItems(updatedItems);
    dispatch(setRoutineItem({ index, item: updatedItems[index] }));
    const updatedResults = [...searchResults];
    updatedResults[index] = [];
    setSearchResults(updatedResults);
    const updatedQueries = [...searchQueries];
    updatedQueries[index] = "";
    setSearchQueries(updatedQueries);

    const resultTotalPrice = updatedItems.reduce((acc, item) => {
      const selectedProduct = searchResults[index].find(
        (searchItem) => searchItem.item_key === item.item_key
      );
      return acc + (selectedProduct ? selectedProduct.item_price : 0);
    }, 0);

    setTotalPrice(resultTotalPrice);

    if (inputRef.current) {
      inputRef.current.focus();
    }
  };

  const handleRoutineItemChange = (
    index: number,
    key: keyof (typeof allRoutineItems)[0],
    value: string
  ) => {
    const updatedItem = { ...allRoutineItems[index], [key]: value };
    const updatedItems = [...allRoutineItems];
    updatedItems[index] = updatedItem;

    setAllRoutineItems(updatedItems);
    dispatch(setRoutineItem({ index, item: updatedItem }));

    if (key === "item_name") {
      const updatedQueries = [...searchQueries];
      updatedQueries[index] = value;
      setSearchQueries(updatedQueries);

      if (value.length > 1) {
        searchItemData(value, index);
      } else {
        const delayDeboundceFn = setTimeout(() => {
          const clearedQueries = [...searchQueries];
          clearedQueries[index] = "";
          setSearchQueries(clearedQueries);
          const clearedResults = [...searchResults];
          clearedResults[index] = [];
          setSearchResults(clearedResults);
        }, 300);

        return () => clearTimeout(delayDeboundceFn);
      }
    }
  };

  const searchItemData = async (query: string, index: number) => {
    console.log(`단계 ${index + 1}에서 "${query}" 검색`);
    try {
      const response = await axios.get(`${backPort}/api/item/search/${query}`);
      console.log(response.data);

      const updatedResults = [...searchResults];
      updatedResults[index] = response.data || [];
      setSearchResults(updatedResults);
    } catch (error) {
      console.error("제품 검색 중 오류 발생", error);
    }
  };

 

그리고 루틴 추가 할 때 데이터유지를 위해 리덕스를 사용했는데 해당 상태 관리가 다른 페이지를 갔다와도 그대로 남아있어 이 부분을 고쳤다.

import { createSlice, PayloadAction } from "@reduxjs/toolkit";

interface RoutineItem {
  step_number: number;
  step_name: string;
  description: string;
  item_key: string;
  item_name: string;
}

interface addRoutineState {
  title: string;
  gender: string[];
  skin: number;
  age: number;
  problem: number[];
  grade: number;
  routineItem: RoutineItem[];
  tag: string[];
}

const initialState: addRoutineState = {
  title: "",
  gender: [],
  skin: 0,
  age: 0,
  problem: [],
  grade: 1,
  routineItem: new Array(1).fill({
    step_number: 1,
    step_name: "",
    description: "",
    item_key: "",
    item_name: "",
  }),
  tag: [],
};

const addRoutineSlice = createSlice({
  name: "addRoutine",
  initialState,
  reducers: {
    setTitle: (state, action: PayloadAction<string>) => {
      state.title = action.payload;
    },
    setGender: (state, action: PayloadAction<string[]>) => {
      state.gender = action.payload;
    },
    setSkin: (state, action: PayloadAction<number>) => {
      state.skin = action.payload;
    },
    setAge: (state, action: PayloadAction<number>) => {
      state.age = action.payload;
    },
    setProblem: (state, action: PayloadAction<number[]>) => {
      state.problem = action.payload;
    },
    setGrade: (state, action: PayloadAction<number>) => {
      state.grade = action.payload;
    },
    setRoutineItem: (
      state,
      action: PayloadAction<{ index: number; item: RoutineItem }>
    ) => {
      state.routineItem[action.payload.index] = action.payload.item;
    },
    setTag: (state, action: PayloadAction<string[]>) => {
      state.tag = action.payload;
    },
    resetAddRoutine(state) {
      return initialState;
    },
  },
});

export const {
  setTitle,
  setGender,
  setSkin,
  setAge,
  setProblem,
  setGrade,
  setRoutineItem,
  setTag,
  resetAddRoutine,
} = addRoutineSlice.actions;
export default addRoutineSlice.reducer;

리덕스에 기본값의 데이터로 바꿔주는 resetAddRoutine을 추가하여 

  const navigate = useNavigate();
  const dispatch = useDispatch();
  const location = useLocation();
  const fromPath = location.state?.from;
  const currentPath = window.location.pathname;

  useEffect(() => {
    if (fromPath === "/routine" && currentPath === "/add") {
      dispatch(resetAddRoutine());
    }
  }, [fromPath, currentPath, dispatch]);

루틴 추가 버튼은 루틴 페이지에서만 접근 가능하기 때문에 이전 페이지가 /routine이였을 경우 && 현재 페이지가 /add일 때 reset이 실행되게 해놨다. 즉, 루틴 추가 페이지에서 1, 2, 3페이지 이동하거나 다른 페이지에서 여기로 접근했을 땐 데이터가 유지되고 루틴 페이지에서 추가 버튼을 누를 때만 초기화가 되는 것이다.

다음은 루틴 상세 페이지에서 제품 데이터를 뿌려줘야 했는데 루틴 데이터를 뿌려줘야 하는 컴포넌트에서 api를 이용해 details에 있는 제품 데이터를 가져와 해당 item_key를 이용해 제품 데이터를 받아와서 만들어논 객체에 넣고 뿌려줬다.

import "../../scss/detail/detailInfo.scss";
import DetailProfile from "./detailProfile";
import DetailTitle from "./detailTitle";
import DetailCheck from "./detailCheck";
import DetailTag from "./detailTag";
import DetailGrade from "./detailGrade";
import DetailBtnBox from "./detailBtnBox";
import { useEffect, useState } from "react";
import axios from "axios";
import MainFooter from "../common/mainFooter";

interface routineDetails {
  description: string;
  item_key: number;
  routine_key: number;
  step_name: string;
  step_number: number;
}

interface reviews {
  review_key: number;
  user_key: number;
  rating: number;
  review_content: string;
}

interface routineData {
  average_rating: number;
  for_age: number;
  for_gender: string;
  price_total: number;
  reviews: reviews[];
  routineDetails: routineDetails[];
  routine_key: number;
  routine_name: string;
  steps: number;
  user: { username: string };
}

interface detailRoutineData {
  data: routineData;
}

const DetailInfo: React.FC<detailRoutineData> = ({ data }) => {

  return (
    <>
      {data ? (
        <div className="detailInfoWrapper">
          <h4>{data?.user.username}님의 루틴</h4>
          <DetailTitle
            reviewPoint={data?.average_rating}
            reviewMember={2}
          />
          <DetailProfile
            profileImg={data?.user.username}
            profileNickname={data?.user.username}
          />
          <DetailCheck
            check={data?.routineDetails.map((detail) => detail.description)}
          />
          <DetailTag
            tag={data?.routineDetails.map((detail) => detail.step_name)}
          />
          <DetailGrade
            routineGrade={data?.steps}
            routineList={data?.routineDetails || []}
            routineName={data?.routine_name}
          />
          <DetailBtnBox
            itemList={data?.routineDetails.map((detail) => detail.item_key)}
          />
        </div>
      ) : (
        <div>루틴 정보가 없습니다.</div>
      )}
      <MainFooter />
    </>
  );
};
export default DetailInfo;
728x90

'개발 TIL' 카테고리의 다른 글

24.09.24 day70 취업 설명회  (3) 2024.09.24
24.09.23 day69 작업 일지  (1) 2024.09.23
24.09.19 day67 작업 일지  (4) 2024.09.19
24.09.13 day66 작업 일지  (0) 2024.09.13
24.09.11 day65 작업 일지  (2) 2024.09.11