React 부트캠프 TIL 19

정다롱·2024년 9월 2일

내일배움캠프 TIL

목록 보기
18/39

🖥️ supabase에서 CRUD의 C 하기

이번 프로젝트에서 내가 담당한 부분은 바로 작성 페이지다.
사용자가 입력한 값을 지정한 형식에 맞춰 데이터베이스에 보내는 로직을 구현해야 했다.

접근 방식

레이아웃 이렇게 구성되어 있고, 재료 추가와 순서 추가의 경우 빈 객체가 들어있는 배열을 생성해서 state로 관리하고 버튼을 누르면 빈 객체가 추가되도록 만들어 두었다. 인풋 그룹은 map 메소드로 배열을 돌며 빈 배열 갯수만큼 출력된다.

테이블은
레시피 기본 정보를 저장하는 recipe_info,
재료 정보를 저장하는 recipe_ingredient,
레시피 순서를 저장하는 recipe
총 세개로 나누어져 있다.

테이블이 세개로 나누어져 있기 때문에 추후 상세페이지에서 레시피를 연결해서 불러오려면 세 데이터 모두 공통의 값을 갖고 있어야 했고, 나의 경우에는 recipe_info 테이블에 데이터를 보낸 뒤, 방금 보낸 데이터를 가져와서 해당 행의 id값을 재료와 순서에 추가했다.

그리고 재료와 순서는 인풋 개수가 고정된 게 아닌 추가하면 늘어나는 형식이라 어떻게 데이터를 입력해야하나 고민이 많이 됐는데 튜터님과 얘기하면서 대충 방향성이 잡혀서 얼른 코드를 짜보기 시작했다!

먼저 재료 추가, 순서 추가 부분 코드를 보자!

// 초기값
  const initIngInfo = [
    {
      RECIPE_ID: 0, // recipe_info 연결을 위한 id 부분
      ING_NAME: "",
      ING_VOL: "",
    },
    {
      RECIPE_ID: 0,
      ING_NAME: "",
      ING_VOL: "",
    },
  ];

  const initRecipeCont = [
    {
      RECIPE_ID: 0,
      RECIPE_STEP: 0,
      RECIPE_CONT: "",
    },
  ];
  
 // 인풋 추가할 때 사용하는 state
  const [ingredientGroups, setIngredientGroups] = useState(initIngInfo);
  const [recipeContGroups, setRecipeContGroups] = useState(initRecipeCont);

  // 재료 그룹 추가하는 onClick 함수
  const addIngGroup = () => {
    setIngredientGroups([...ingredientGroups, {}]);
  };

  // 재료 그룹 삭제하는 onClick 함수
  const removeIngGroup = (index) => {
    if (ingredientGroups.length === 2) {
      alert("최소 두 개의 재료는 추가되어야 합니다.");
      return;
    }
    const newGroups = ingredientGroups.filter((_, idx) => idx !== index);
    setIngredientGroups(newGroups);
  };

  // 레시피 순서 추가하는 onClick 함수
  const addRecipeGroup = () => {
    setRecipeContGroups([...recipeContGroups, {}]);
  };

  // 재료 그룹 삭제하는 onClick 함수
  const removeRecipeGroup = (index) => {
    if (recipeContGroups.length === 1) {
      alert("최소 한 개의 레시피는 작성되어야 합니다.");
      return;
    }
    const newGroups = recipeContGroups.filter((_, idx) => idx !== index);
    setRecipeContGroups(newGroups);
  };

버튼을 누르면 배열 안에 객체가 추가되거나 삭제되고 이 배열을 페이지 안에서 map을 통해 출력한다고 했다. 각 배열 안에 value 값을 저장하기 위해서는 지금 입력한 값이 어떤 값인지(ING_NAME인지 ING_VOL인지) 확인을 위한 type이 필요했고 배열 안의 몇번째 객체인지 알 수 있는 index가 필요했다.

첫번째 재료 칸에 당근 / 1개 라고 입력한다면

ingredientGroups[0] =
	{RECIPE_ID : id,
	ING_NAME : "당근",
	ING_VOL : "1개"}

이런 모양이 되기를 원했다.

그렇게 작성한 코드

  const ingInfoChange = (value, type, index) => {
    ingredientGroups[index] = { ...ingredientGroups[index], [type]: value };
  };

인자로 받아온 index를 통해 해당 객체에 접근해서 내부 값을 변경해준다. 그런데 입력할때마다 값이 초기화되면 안되니까 스프레드연산자로 기존 값을 유지하고 type에 해당하는 value만 변경되도록 했다.

레시피 순서 부분도 똑같이 구현했다.


받아온 데이터 supabase로 보내기!

  1. recipe_info 테이블로 기본 정보 보내기

     // 사용자 id 가져오기
      const user = await supabase.auth.getUser();
      const id = user.data.user.id;
    
      // recipe_info 테이블에 레시피 정보 삽입
      const { data } = await supabase
        .from("recipe_info")
        .insert([{ ...recipeInfo, USER_ID: id }])
        .select();
    
      const recipeId = data[0].RECIPE_ID;
    • 누가 작성했는지 알기 위해 현재 로그인한 유저의 고유 id값을 가져왔다. getUser를 통해 쉽게 접근할 수 있다.
    • 기존에 state를 구성할 때 각 테이블의 컬럼명과 객체의 속성명을 일치시켰기 때문에 하나하나 입력한 필요 없이 스프레드 연산자로 레시피 정보를 펼친 후 id만 지정해주었다.
    • insert 직후에 select를 통해서 방금 저장한 레시피 정보의 ID 값을 갖는recipeId 변수를 만들었다.

  1. 재료와 순서 객체 안의 RECIPE_ID 값을 recipeId로 변경하기

      // ingInfo 배열 안에서 모든 재료 객체 RECIPE_ID 변경
      const updatedIngInfo = recipeContGroups.map((ingredient) => ({
        ...ingredient,
        RECIPE_ID: recipeId,
      }));
    
      const updateRecipeCont = recipeContGroups.map((cont) => ({
        ...cont,
        RECIPE_ID: recipeId,
      }));
    • 연결을 위해 각 그룹을 map으로 순회하며 모든 객체의 RECIPE_ID를 recipeId로 지정해주었다. 그리고 반환받은 update---가 최종적으로 데이터 베이스에 전송될 배열이다.


  2. 데이터 전송하고 state 초기화하기

    await supabase.from("recipe_ingredient").insert(updatedIngInfo);
    await supabase.from("recipe_flow").insert(updateRecipeCont);
    
      // 상태 초기화
      setRecipeInfo(initRecipeInfo);
      setIngredientGroups(initIngInfo);
      setRecipeContGroups(initRecipeCont);
    • 완성된 데이터 보내고 상태 초기화 해주면 끝!


이제 빈칸이 있는 경우 alert을 띄워주거나 하는 유효성 검사만 마무리하면 된다...

0개의 댓글