🚀 프로젝트/따꼼

따꼼 트러블 슈팅 | React + Supabase 중복 데이터 삽입 문제 해결하기

seheej 2024. 11. 20. 09:47

React와 Supabase를 사용해 다음 버튼을 눌렀을 때 데이터를 추가하는 기능을 구현하던 중, 중복 데이터가 삽입되는 문제를 발견했습니다. 이 포스팅에서는 문제의 원인과 이를 해결한 방법을 공유합니다.

 

1. 문제 상황

다음 버튼을 눌렀을 때, 사용자가 입력한 자녀 정보(이름, 나이 등)를 Supabase 데이터베이스에 저장한 후, 다음 단계로 이동하는 로직을 구현했습니다. 그러나 동일한 데이터를 여러 번 입력하면 데이터가 중복 삽입되는 문제가 발생했습니다.

 

2. 문제 원인

  1. 중복 확인 로직 부재
    데이터가 이미 존재하는지 확인하는 코드가 없어서, 매번 새로운 데이터를 삽입했습니다.
  2. UUID 무조건 생성
    새로운 데이터를 삽입할 때마다 uuidv4()를 통해 고유 ID를 생성했기 때문에 기존 데이터와 구분되지 않았습니다.

 

3. 수정 전 코드

문제가 발생했던 코드는 다음과 같습니다:

const handleNext = async () => {
  try {
    // 새로운 자녀 정보 생성
    const newChild = {
      id: uuidv4(), // 새로운 UUID 생성
      name: childInfo.name,
      age: childInfo.age,
    };

    // Supabase에 새로운 자녀 정보 삽입
    const { data, error } = await supabase.from('children').insert(newChild);

    if (error) throw error;

    // 다음 단계로 이동
    setStep(step + 1);
    setChildInfo({}); // 상태 초기화
  } catch (error) {
    console.error('Error inserting child:', error.message);
  }
};

 

4. 문제점 분석

  • 중복 데이터 확인 누락: childInfo의 이름과 나이가 동일한 데이터가 이미 존재하는지 확인하지 않았습니다.
  • 상태 초기화의 부작용: setChildInfo({})로 인해 이전 단계의 입력 값이 유지되지 않았습니다.

 

5. 수정 후 코드

문제를 해결하기 위해 중복 확인 로직을 추가한 코드입니다:

const handleNext = async () => {
  try {
    // 기존 데이터 확인
    const { data: existingChild, error: fetchError } = await supabase
      .from('children')
      .select('*')
      .eq('name', childInfo.name)
      .eq('age', childInfo.age)
      .single();

    if (fetchError && fetchError.code !== 'PGRST116') {
      throw fetchError; // 다른 에러 발생 시 처리
    }

    if (!existingChild) {
      // 데이터가 없을 경우에만 새로 삽입
      const newChild = {
        id: uuidv4(), // 새로운 UUID 생성
        name: childInfo.name,
        age: childInfo.age,
      };

      const { data, error: insertError } = await supabase
        .from('children')
        .insert(newChild);

      if (insertError) throw insertError;
    } else {
      console.log('Child already exists:', existingChild);
    }

    // 다음 단계로 이동
    setStep(step + 1);
  } catch (error) {
    console.error('Error handling next step:', error.message);
  }
};

 

6. 수정 후 주요 변경점

  1. 중복 확인 로직 추가
      • 데이터베이스에서 동일한 name과 age를 가진 데이터를 조회합니다.
      • 중복 데이터가 있을 경우, 새로운 데이터를 삽입하지 않고 로그를 출력합니다.
    const { data: existingChild, error: fetchError } = await supabase
      .from('children')
      .select('*')
      .eq('name', childInfo.name)
      .eq('age', childInfo.age)
      .single();
  2. 조건부 데이터 삽입
      • existingChild가 없을 때만 새 데이터를 삽입합니다.
    if (!existingChild) {
      const { data, error: insertError } = await supabase
        .from('children')
        .insert(newChild);
    }
  3. 상태 초기화 제거
    • setChildInfo({})를 삭제하여 이전 입력 값을 유지하도록 했습니다.
  4. 에러 처리 개선
    • 데이터가 없을 때 발생하는 PGRST116 에러를 허용하고, 그 외 에러는 처리하도록 코드를 수정했습니다.

 

7. 결과

  • 중복 데이터 방지: 동일한 데이터가 이미 존재할 경우 새로 삽입하지 않습니다.
  • 유지 보수성 향상: 코드가 더 명확하고 안정적으로 동작합니다.
  • 사용자 경험 개선: 이전 입력 값을 유지하여 다음 단계로 자연스럽게 이동할 수 있습니다.

 

8. 배운 점 및 느낀 점

이번 문제를 해결하면서 다음과 같은 점을 배울 수 있었습니다:

  1. 데이터베이스 조회의 중요성
    데이터를 삽입하기 전에 항상 기존 데이터가 존재하는지 확인하는 습관을 들이는 것이 중요하다는 것을 깨달았습니다. 특히, 중복 데이터를 방지하기 위해 데이터베이스의 조회 기능을 활용하는 방법을 익혔습니다.
  2. 에러 핸들링의 필요성
    Supabase의 에러 코드를 이해하고 적절히 처리함으로써, 예상치 못한 상황에서도 프로그램이 안정적으로 동작하도록 만드는 것이 중요하다는 것을 알게 되었습니다.
  3. 사용자 경험 고려하기
    상태 초기화를 삭제하면서 입력값이 유지되도록 수정한 점이 사용자 경험을 개선하는 데 큰 영향을 미쳤습니다. 앞으로는 기능 구현뿐 아니라 사용자의 편의성도 함께 고려하는 개발자가 되고 싶습니다.
  4. 코드 유지 보수성 향상
    조건부 삽입과 명확한 에러 처리 로직을 추가하면서 코드가 읽기 쉬워지고, 팀원과 협업할 때 이해하기 쉽게 만들 수 있다는 점을 배웠습니다.