이번 글에서는 TypeScript에서 satisfies
를 활용하여 코드 내 불필요한 변수 선언을 줄이는 방법을 소개합니다.
satisfies
는 코드를 더 간결하고 효율적으로 만드는 데 유용한 기능입니다.
변수 선언 없이 타입을 지정하는 방법
타입 주석을 통해 변수 선언 시 타입을 지정할 수 있지만, 이로 인해 불필요한 변수를 선언해야 하는 경우가 많습니다.
satisfies
를 사용하면 이러한 불필요한 변수를 줄일 수 있습니다. 구체적인 예시를 통해 알아보겠습니다.
예시 1: 타입 포괄성 체크
switch
문에서 모든 가능한 타입을 포괄적으로 확인하는 방법으로 never
타입을 활용할 수 있습니다.
다음은 그 예시입니다.
type Animal = "dog" | "cat" | "pig";
const awesomeFunction = (animal: Animal) => {
switch (animal) {
case "cat":
return "meow";
case "dog":
return "bowwow";
case "pig":
return "oink oink";
default:
const _: never = animal;
}
};
이 코드는 모든 case
를 처리했기 때문에 default
절에 도달했을 때 animal
이 never
타입이 됩니다.
Animal
에 새로운 값이 추가되면 컴파일러가 이를 감지하여 알려줍니다.
하지만 위 코드는 불필요한 변수를 선언하고 있습니다. satisfies
를 이용해 이를 제거할 수 있습니다.
satisfies를 사용한 개선
satisfies
를 사용하여 불필요한 변수를 제거한 코드는 다음과 같습니다.
type Animal = "dog" | "cat" | "pig";
const awesomeFunction = (animal: Animal) => {
switch (animal) {
case "cat":
return "meow";
case "dog":
return "bowwow";
case "pig":
return "oink oink";
default:
return animal satisfies never;
}
};
이렇게 하면 불필요한 변수 선언 없이도 필요한 기능을 구현할 수 있습니다.
default
절은 남은 animal
이 없는지를 확인하는 역할을 하므로, 변수 선언이 필요하지 않습니다.
default
절에서 return animal
을 하면 awesomeFunction
의 타입은 다음과 같습니다.
typeof awesomeFunction = (animal: Animal) => "meow" | "bowwow" | "oink oink"
만약 return animal
을 하지 않는다면 타입에 undefined
가 포함됩니다. 따라서 return
을 명시하는 것이 좋습니다.
예시 2: 함수 인자에 적용하기
라이브러리에서 종종 "인자로 객체를 받지만, 키는 문자열이면 상관없다"는 함수를 보게 됩니다.
이럴 때도 satisfies
가 유용합니다. 예를 들어, zod
를 사용한 폼을 구현해보겠습니다.
satisfies 없이 zod 스키마 선언
아래는 기본적인 폼 구현입니다.
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
const awesomeFormSchema = z.object({
name: z.string(),
address: z.string(),
});
type AwesomeFormSchema = z.infer<typeof awesomeFormSchema>;
const AwesomeFormPage = () => {
const { register } = useForm<AwesomeFormSchema>({
resolver: zodResolver(awesomeFormSchema),
});
return (
<form>
<label>
이름
<input {...register("name")} />
</label>
<label>
주소
<input {...register("address")} />
</label>
</form>
);
};
여기서 "이름"과 "주소" 필드에 필수 입력과 오류 메시지를 추가해보겠습니다.
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
const awesomeFormSchema = z.object({
name: z.string().min(1, { message: "이름은 필수 항목입니다." }),
address: z.string().min(1, { message: "주소는 필수 항목입니다." }),
});
type AwesomeFormSchema = z.infer<typeof awesomeFormSchema>;
const AwesomeFormPage = () => {
const {
register,
formState: { errors },
} = useForm<AwesomeFormSchema>({
resolver: zodResolver(awesomeFormSchema),
});
return (
<form>
<label>
이름
<input {...register("name")} />
<p>{errors.name?.message}</p>
</label>
<label>
주소
<input {...register("address")} />
<p>{errors.address?.message}</p>
</label>
</form>
);
};
하지만 오류 메시지와 라벨에 사용된 문구가 각각 하드코딩되어 있는 점이 아쉽습니다.
이를 통일해보겠습니다.
const awesomeFormLabel = {
name: "이름",
address: "주소",
};
const awesomeFormSchema = z.object({
name: z.string().min(1, { message: `${awesomeFormLabel.name}은 필수 항목입니다.` }),
address: z.string().min(1, { message: `${awesomeFormLabel.address}은 필수 항목입니다.` }),
});
type AwesomeFormSchema = z.infer<typeof awesomeFormSchema>;
const AwesomeFormPage = () => {
const {
register,
formState: { errors },
} = useForm<AwesomeFormSchema>({
resolver: zodResolver(awesomeFormSchema),
});
return (
<form>
<label>
{awesomeFormLabel.name}
<input {...register("name")} />
<p>{errors.name?.message}</p>
</label>
<label>
{awesomeFormLabel.address}
<input {...register("address")} />
<p>{errors.address?.message}</p>
</label>
</form>
);
};
이렇게 하면 awesomeFormLabel
과 awesomeFormSchema
사이에 타입적인 연결이 없기 때문에 다른 키를 추가할 수 있습니다.
이를 방지하기 위해 satisfies
를 사용해봅시다.
satisfies로 개선하기
satisfies
를 사용하면 불필요한 변수를 없애고 필요한 것을 구현할 수 있습니다.
import { z, type ZodSchema } from "zod";
type SchemaKey = "name" | "address";
const awesomeFormLabel = {
name: "이름",
address: "주소",
} as const satisfies Record<SchemaKey, string>;
const awesomeFormSchema = z.object({
name: z.string().min(1, { message: `${awesomeFormLabel.name}은 필수 항목입니다.` }),
address: z.string().min(1, { message: `${awesomeFormLabel.address}은 필수 항목입니다.` }),
} satisfies Record<SchemaKey, ZodSchema>);
이렇게 하면 불필요한 변수를 없애고 awesomeFormLabel
과 awesomeFormSchema
간에 타입적 연결을 설정할 수 있습니다.
satisfies
를 사용하면 변수를 선언하지 않고도 타입을 강제할 수 있습니다.
이처럼 satisfies
는 코드를 간결하게 하고 타입 안전성을 높이는 데 유용합니다.
요약
이번 글에서는 satisfies
를 활용하여 불필요한 변수 선언을 줄이고 타입 안전성을 높이는 방법을 알아보았습니다.
다양한 상황에서 satisfies
가 유용하게 쓰일 수 있으니, 아직 사용해보지 않으셨다면 꼭 시도해보시기 바랍니다!
그럼.
'Javascript' 카테고리의 다른 글
lodash-es보다 가볍고 빠른 es-toolkit으로 전환하기 (0) | 2024.08.24 |
---|---|
TypeScript의 고급 타입 시스템과 React Hooks 활용법 (0) | 2024.08.24 |
TypeScript로 타입을 강화하는 방법: satisfies와 Uppercase<T> 활용하기 (0) | 2024.08.24 |
NextJS 앱 자체 호스팅: 실제 사례로 알아보는 최적의 방법 (0) | 2024.08.09 |
JavaScript 학습의 어려움과 해결 방법 (0) | 2024.08.05 |