자바스크립트에서 FormData 인터페이스를 사용할 때, 데이터가 키/값 쌍으로 추가되는데, 추가하는 키에 대한 타입 안전성을 강제하는 내장 방법이 없습니다. 이는 오타, 누락된 키 및 예기치 않은 런타임 오류로 이어질 수 있습니다. 그러나 TypeScript에서는 엄격한 키 유효성을 강제하여 이를 해결할 수 있습니다.

API에 폼 값을 보낼 때 스스로 이 솔루션이 필요했습니다. 후에 제가 페이로드에 추가하려고 시도한 여러 키/값 쌍에서 여러 오타를 발견했습니다. FormData는 키로 모든 문자열을 허용하기 때문에 잘못된 문자열을 전달하고 API 요청을 계속할 수 있었습니다.

이런 일이 일어난 후에는 TypeScript가 이러한 오류를 허용하지 않도록 하는 방법을 찾았습니다.

이 기사에서는 TypeScript를 사용하여 FormData 키를 타입 안전하게 만드는 방법을 보여줍니다.

전제 조건

이 기사를 최대한 활용하려면 다음에 대한 기본적인 이해가 있어야 합니다:

  1. 자바스크립트 프로그래밍

  2. 인터페이스, 타입 및 keyof 연산자가 작동하는 방법을 중점적으로 다루는 TypeScript 기본 사항

  3. FormData 인터페이스

TypeScript나 FormData를 처음 접하는 경우, 진행하기 전에 TypeScript 공식 문서MDN의 FormData 가이드를 확인하는 것을 추천합니다.

1단계: 허용할 키 정의하기

구식 방법

FormData로 데이터를 추가하는 기본 방법은 일반 문자열을 사용하여 수동으로 하는 것입니다:

const payload = new FormData();

payload.append("id", "1122");
payload.append("name", "Clark Kent");

payload.append("agge", "36"); // 키에 오타가 허용됩니다

위의 코드 조각에서 age 키를 정의할 때 오타가 있음을 알 수 있습니다. 하지만 TypeScript는 이를 오류로 표시하지 않으며, 이 데이터가 API 요청과 함께 전송될 때 오류를 유발할 수 있습니다.

더 나은 방법

키를 수동으로 입력하는 대신, TypeScript 인터페이스를 사용하여 객체 스키마에서 정의하세요.

interface MyAllowedData {
    id: number;
    name: string;
    age: number;
}

또는 타입으로 정의할 수도 있습니다:

type MyAllowedData = {
    id: number;
    name: string;
    age: number;
}

타입이나 인터페이스 중 어떤 것을 사용해도 상관없으며, 이는 개인 취향의 문제입니다. 이 공식 TypeScript 문서 플레이그라운드에서 그 차이에 대해 더 알아볼 수 있습니다.

다음으로, 인터페이스의 각 키에서 유니온 타입을 정의하세요.

type MyFormDataKeys = keyof MyAllowedData
// 이는 `type MyFormDataKeys = 'id' | 'name' | 'age'`와 동일합니다

keyof 연산자는 객체 타입의 키들의 유니온 타입을 생성하는 데 도움을 주므로, 많은 키를 가진 큰 객체에 대해 수동으로 유니온 타입을 정의하고 싶지 않을 때 매우 유용합니다.

2단계: Append 헬퍼 함수 생성하기

엄격하게 타입이 정의된 키를 설정한 후, 다음 단계는 유효한 키만 FormData에 추가되도록 보장하는 헬퍼 함수를 생성하는 것입니다.

function appendToFormData (formData: FormData, key: MyFormDataKeys, value: string) {
  formData.append(key, value);
};

appendToFormData 함수는 세 가지 인수를 받습니다. 작동 방식은 다음과 같습니다:

  • 첫 번째 인수인 formData는 FormData 객체의 인스턴스입니다. 여기서 키/값 쌍이 API 요청을 보내기 전에 추가됩니다.

  • 두 번째 인수인 key는 추가하고자 하는 필드의 키 이름입니다. 그 타입은 MyFormDataKeys로, 우리가 정의한 키만 FormData에 추가되도록 보장하기 위해 생성한 유니온 타입입니다.

  • 세 번째 인수는 키와 함께 추가될 값을 나타내는 문자열 value입니다.

다음에 유의하십시오 FormData는 string Blob 타입만 각 키/값 쌍의 값으로 허용합니다. 이 가이드에서는 문자열 값만 사용하고 있지만, API 요청에 파일을 추가하기 위해 blob 값을 사용할 수 있다는 점을 기억하십시오.

이제 함수를 테스트해 보겠습니다:

const payload = new FormData();

appendToFormData(payload, "id", "19282"); // ✅ 허용됨
appendToFormData(payload, "name", "Lenny Brown"); // ✅ 허용됨
appendToFormData(payload, "age", "20"); // ✅ 허용됨

appendToFormData(payload, "someOtherKey", "89"); // ❌ TypeScript 오류: 'someOtherKey' 타입의 인수가 할당될 수 없습니다.

3단계: 양식 제출 후 도우미 함수 사용하기

이제 API에 전송하기 전에 FormData에 필드를 추가해 보겠습니다.

const handleSubmitForm = () => {
  const payload = new FormData();
   appendToFormData(payload, "id", "19282");
   appendToFormData(payload, "name", "Lenny Brown");
   appendToFormData(payload, "age", "20");

  // API를 통해 페이로드 전송
  fetch("/api/submit", { method: "POST", body: payload });
};

객체에서 필드 추가하기

또는 이미 전체 페이로드가 객체에 있는 경우, 다음과 같이 함수를 구현하여 각 필드를 하나씩 추가할 필요 없이 추가할 수 있습니다:

const handleSubmitForm = () => {
  // 객체에 모든 필드
  const formValues: MyAllowedData = {
    id: 1123,
    name: 'John Doe',
    age: 56
  }
  const payload = new FormData();

  Object.entries(formValues).forEach(([key, value]) => {
    appendToFormData(payload, key as MyFormDataKeys, `${value}`); // 템플릿 리터럴을 사용하여 값을 전달합니다
  });

  // API를 통해 페이로드 전송
  fetch("/api/submit", { method: "POST", body: payload });
};

위의 코드 조각에서는 Object.entries를 사용하여 객체의 각 키/값 쌍을 반복하며 이를 FormData 객체에 추가합니다. 각 쌍의 값은 문자열이든 숫자든 템플릿 리터럴을 사용하여 문자열로 전달되어 TypeScript의 타입 불일치를 방지합니다.

결론

TypeScript의 keyof 연산자를 활용함으로써 FormData.append()를 완전히 타입 안전하게 만들 수 있습니다. 이 간단한 기법은 키 불일치를 방지하고 API 요청을 더 신뢰할 수 있게 만듭니다.

이 기사에 대한 여러분의 생각을 알려주시고, 제 솔루션을 개선할 수 있는 제안이 있다면 자유롭게 말씀해 주세요.

읽어 주셔서 감사합니다!