본문 바로가기
Insights/Troubleshooting

[swimX] react-hook-form + Supabase 타입 불일치 해결하기 🛠️

by suyeonnie 2025. 4. 18.

 

작성 폼 / 레인 선택 컴포넌트

 

Next.js 프로젝트에서 react-hook-form, zod, Supabase를 사용해서 일기 작성 폼을  구현하던 중

폼과 데이터베이스 타입 간 불일치 문제가 발생했다.

 

그중 `string enum`과 `number` 타입 간 충돌을 타입 변환 처리를 통해 해결한 과정을 정리했다.

 

문제 상황

수영 일기 작성 흐름은 아래와 같다 :

사용자 입력
  ↓
React Hook Form (SwimFormData, zod 스키마 검사)
  ↓
변환 함수 (toSwimLog)
  ↓
SwimLog 구조로 전환 (중첩 구조 포함)
  ↓
Supabase 저장 (insertSwimLog), zustand 저장 (setLog)

 

react-hook-form을 통해 수집된 폼 데이터는 SwimFormData 타입으로 들어오며, 이 타입은 zod로 검증된다.

 

여기서 lane 필드는 `'25' | '50' | '기타'`중 하나의 문자열(enum)이고,

`'기타'` 선택 시 `customLane` 필드를 통해 사용자가 직접 레인 단위를 입력할 수 있다.

 

SwimFormData
(React hook form + zod 스키마 검사)
SwimLog 
(DB 저장용 타입)
Supabase 테이블 컬럼
(swim_logs)
lane: '25' | '50' | '기타';
customLane?: string;
lane: number; lane: integer not null;

 

💡 SwimFormData 스키마는 src/ schemas/ logSchema.ts 에서 관리

💡 SwimLog 타입은 src/ types /log.ts 에서 관리

    → time, heartRate, pace 등의 중첩 객체로 구성됨

    → 해당 타입은 폼 데이터를 DB에 저장하는 insertSwimLog 함수에서 사용

 

 

오류 발생

 

 

하지만 Supabase에 데이터를 저장하는 과정에서 lane 필드의 타입 불일치로 인해 오류가 발생했다.

 

원인 분석

 

  • `SwimFormData.lane`은 문자열 ('25', '50', '기타')
  • `SwimLog.lane` 및 DB 컬럼은 숫자 (integer) 타입
  • 변환 함수 `toSwimLog()`에서 lane 값의 타입 변환이 빠짐
  • 결과적으로 `insertSwimLog()` 호출 시 타입 오류 발생

 

 

해결 방법

 

타입 불일치를 해결하기 위해 `toSwimLog()` 함수에서 다음과 같이 조건부 변환 로직을 추가했다.

 

[SwimFormData → SwimLog 구조 변환 함수]

export const toSwimLog = (data: SwimFormData): SwimLog => {
  const lane =
    data.lane === '기타' && data.customLane
      ? Number(data.customLane) // 기타 선택 시 사용자 입력값(customLane) 사용
      : Number(data.lane);      // 나머지는 string → number 변환

  return {
    date: data.date,
    lane,
    // ... 
  };
};

 

💡 변환 로직 src/ utils / format.ts 에서 관리 중

 

이제 변환된 lane 값은 SwimLog 타입에 맞게 정상적으로 Supabase 테이블에 저장된다.

 

 

결과

  • 타입 불일치 오류 해결
  • Supabase에 데이터 정상 저장
  • 레인 토글 그룹에서 '기타' 선택 시 사용자가 원하는 숫자 레인을 기록할 수 있음
  • 타입 시스템에 맞춘 구조 설계의 중요성

 

회고

  • 타입스크립트의 친절함🙃
  • 프론트엔드 ↔ 백엔드 간 데이터 스키마 일치는 정말 중요하다!!
  • 특히 form 데이터는 문자열이 많기 때문에 중간에서 타입을 변환해 주는 함수(toSwimLog)가 핵심 역할을 함
  • 구현 중 `shadcn/ui`를 도입하면서 `Form`컴포넌트가 `react-hook-from`과 `zod` 기반이라는 사실을 알게 되었음
    • 나중에 UI 폼 전체를 shadcn/ui 컴포넌트로 리팩토링해보자!