Communitygithub.com

sakilanwar12/nextjs-typescript-skills

react-hook-form과 zod를 사용한 폼 구축 시 활용 — 스키마 우선 검증, 타입이 지정된 필드, 서버 액션 제출, 접근성 오류 및 파일 입력 지원.

nextjs-typescript-skills란 무엇인가요?

이 스킬은 Next.js 및 TypeScript 환경에서 react-hook-form과 zod를 결합하여 강력하고 타입 안전한 폼을 구축하는 방법을 정의합니다. 핵심 원칙은 스키마 우선 접근법으로, 먼저 zod 스키마를 정의하고 `z.infer`로 타입을 추론하여 클라이언트와 서버가 동일한 단일 진실 공급원을 공유하게 합니다. `@hookform/resolvers/zod`의 `zodResolver`를 사용하고, UX 향상을 위해 `onTouched` 모드를 설정하며, 서버 액션에서 동일한 스키마로 재검증합니다. 또한 `aria-invalid`, `aria-describedby`, `role="alert"`와 같은 접근성 속성을 적용하고, Server Action을 통한 제출, pending 상태 및 서버 에러 처리를 포함한 완전한 패턴 예제를 제공합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/sakilanwar12/nextjs-typescript-skills/tree/HEAD/skills/nextjs-forms

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

폼 (react-hook-form + zod)

규칙

  1. 스키마 우선: zod 스키마를 정의하고 z.infer로 타입을 파생시킵니다. 클라이언트와 서버가 공유하는 단일 진실 공급원을 만듭니다.
  2. @hookform/resolvers/zod (zodResolver)를 사용합니다. UX를 위해 mode를 onTouched로 설정하고, 액션에서 서버 측에서도 동일한 스키마로 재검증합니다.
  3. 폼 타입을 지정합니다: useForm<FormValues>. 필드 값에 any를 사용하지 않습니다.
  4. 접근성 오류: aria-invalid, aria-describedby, role="alert"를 메시지에 사용합니다. <label htmlFor>를 연결합니다.
  5. Server Action (formAction 또는 서버 함수를 호출하는 action)을 통해 제출합니다. pending 상태 및 서버 오류 상태를 표시합니다.

패턴

'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const Schema = z.object({
  email: z.string().email('유효한 이메일을 입력하세요'),
  password: z.string().min(8, '최소 8자 이상'),
});
type Values = z.infer<typeof Schema>;

export function LoginForm({ onSubmit }: { onSubmit: (v: Values) => Promise<{ ok: boolean; error?: string }> }) {
  const { register, handleSubmit, formState: { errors, isSubmitting }, setError } = useForm<Values>({
    resolver: zodResolver(Schema),
    mode: 'onTouched',
  });

  return (
    <form onSubmit={handleSubmit(async (v) => {
      const r = await onSubmit(v);
      if (!r.ok) setError('root', { message: r.error ?? '실패' });
    })}>
      <label htmlFor="email">이메일</label>
      <input id="email" type="email" autoComplete="email"
        aria-invalid={!!errors.email} aria-describedby={errors.email ? 'email-err' : undefined}
        {...register('email')} />
      {errors.email && <p id="email-err" role="alert">{errors.email.message}</p>}

      <label htmlFor="password">비밀번호</label>
      <input id="password" type="password" autoComplete="current-password" {...register('password')} />
      {errors.password && <p role="alert">{errors.password.message}</p>}

      {errors.root && <p role="alert">{errors.root.message}</p>}
      <button disabled={isSubmitting}>{isSubmitting ? '로그인 중…' : '로그인'}</button>
    </form>
  );
}

체크리스트

  • zod 스키마 + z.infer 타입?
  • zodResolver 연결됨?
  • 모든 필드에 레이블 + aria 속성 적용?
  • 서버에서 동일한 스키마로 재검증?
  • 비활성화된 pending 상태 및 루트/서버 오류 표시?

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

관련 스킬