디자인 QA 핸드오프 체크리스트: 개발자와 디자이너의 간극을 없애는 방법

UX 디자인

디자인 QA핸드오프Figma디자인 시스템개발자 협업

이 글은 누구를 위한 것인가

  • "구현이 디자인이랑 다르다"는 피드백을 반복적으로 주고받는 팀
  • Figma 링크만 넘겨주고 핸드오프가 끝났다고 생각하는 디자이너
  • 어떤 상태를 구현해야 할지 디자인에서 찾지 못해 임의로 결정하는 개발자

들어가며

핸드오프는 Figma 링크를 보내는 순간이 아니다. 개발자가 구현하다 막히는 순간마다 Slack으로 질문이 쏟아진다면, 핸드오프가 불완전한 것이다.

"호버 상태는요?", "에러 메시지 디자인이 없는데요?", "모바일에서 이 텍스트는 몇 줄까지 허용되나요?" — 이 질문들은 모두 사전에 정의됐어야 하는 것들이다.

이 글은 bluefoxdev.kr의 디자인-개발 협업 가이드 를 참고하고, 실전 QA 핸드오프 체크리스트 관점에서 확장하여 작성했습니다.


1. 핸드오프 전 디자인 완성도 체크

[레이어 1: 컴포넌트 상태 완전성]

버튼 컴포넌트 예시 — 필수 정의 상태:
✅ Default (기본)
✅ Hover (마우스 오버)
✅ Active / Pressed (클릭 중)
✅ Focused (키보드 포커스 — 접근성 필수)
✅ Disabled (비활성)
✅ Loading (비동기 처리 중)
✅ Success / Error (결과 상태)

인풋 필드 예시 — 필수 정의 상태:
✅ Empty (빈 상태 — placeholder 포함)
✅ Focused (입력 중)
✅ Filled (값 입력됨)
✅ Error (유효성 오류 + 에러 메시지)
✅ Disabled
✅ Read-only

❌ 가장 자주 빠지는 상태:
  - Loading 중 버튼 (이중 클릭 방지 필요)
  - 포커스 링 (Tab 키 접근성)
  - 비어있는 Empty State (데이터 없을 때)
  - 스켈레톤 로딩 상태

2. 컴포넌트별 QA 체크리스트

[카드 컴포넌트 QA]

레이아웃:
□ 이미지 없을 때 폴백 처리 (깨진 이미지, 대체 아이콘)
□ 텍스트 최대 줄 수 정의 (2줄? 3줄? 말줄임 처리)
□ 최소/최대 너비 정의
□ 이미지 비율 고정 여부 (aspect-ratio)

콘텐츠 변형:
□ 짧은 제목 (5자) vs 긴 제목 (50자) 처리
□ 가격이 없는 경우 레이아웃
□ 배지/태그가 없는 경우
□ 배지가 여러 개인 경우 (최대 몇 개?)

인터랙션:
□ 호버 시 어떤 변화? (그림자, 이동, 밝기)
□ 클릭 가능한 전체 영역 정의
□ 클릭 시 어디로? (전체 클릭 vs 버튼만)
[폼 QA]

유효성 검증:
□ 실시간 검증 vs 제출 후 검증
□ 에러 메시지 위치 (인풋 아래? 상단 요약?)
□ 에러 아이콘 + 텍스트 + 색상 모두 정의
□ 성공 상태 피드백

접근성:
□ 레이블과 인풋 연결 (for/id 매칭)
□ 에러 메시지가 aria-describedby로 연결
□ 필수 항목 표시 (* 또는 "필수" 텍스트)
□ 포커스 순서 (Tab 순서 자연스러운지)

반응형:
□ 모바일에서 필드 간격/크기
□ 키보드 팝업 시 레이아웃 변화
□ 1열 vs 2열 레이아웃 전환 기준

3. 반응형 동작 명세

[브레이크포인트별 동작 정의 예시]

네비게이션:
├── Desktop (1280px+)
│     → 수평 메뉴, 로그인/가입 버튼 우측
├── Tablet (768px–1279px)
│     → 수평 메뉴 유지, 보조 버튼 숨김
└── Mobile (–767px)
      → 햄버거 메뉴, 드로어 슬라이드

카드 그리드:
├── Desktop: 4열
├── Tablet: 2열
└── Mobile: 1열 (스와이프 캐러셀? 세로 목록?)

타이포그래피:
┌──────────────┬────────┬────────┬────────┐
│ 요소          │Desktop │ Tablet │ Mobile │
├──────────────┼────────┼────────┼────────┤
│ H1            │ 48px   │ 36px   │ 28px   │
│ H2            │ 32px   │ 28px   │ 22px   │
│ Body          │ 16px   │ 16px   │ 15px   │
│ Caption       │ 12px   │ 12px   │ 11px   │
└──────────────┴────────┴────────┴────────┘

4. Figma 핸드오프 주석 작성법

[개발자에게 필요한 Figma 주석 유형]

1. 동작 주석 (Behavior Note)
   형식: [동작] 설명
   예시: [호버] 0.2s ease로 배경색 #F5F5F5로 전환
         [클릭] 눌리는 느낌 (transform: scale(0.98))
         [포커스] 2px 파란 아웃라인 (#0066FF)

2. 제약 조건 주석 (Constraint Note)
   형식: [제약] 설명
   예시: [제약] 최대 2줄, 초과 시 "..." 처리
         [제약] 이미지 없을 때 회색 플레이스홀더 표시
         [제약] 모바일에서 sticky 헤더

3. 데이터 주석 (Data Note)
   형식: [데이터] 설명
   예시: [데이터] 실시간 재고 수량, 0이면 "품절" 표시
         [데이터] 날짜 형식: YYYY년 MM월 DD일

4. 접근성 주석 (A11y Note)
   형식: [A11y] 설명
   예시: [A11y] aria-label="장바구니에 추가"
         [A11y] role="alert"로 에러 메시지 즉시 읽기

5. 핸드오프 산출물 체크리스트

// 핸드오프 완성도 자동 체크 스크립트 (Figma Plugin 개념)

interface HandoffChecklist {
  component: string;
  checks: CheckItem[];
}

interface CheckItem {
  category: 'states' | 'responsive' | 'accessibility' | 'content' | 'interaction';
  item: string;
  required: boolean;
  passed: boolean;
}

const BUTTON_CHECKLIST: HandoffChecklist = {
  component: 'Button',
  checks: [
    // 상태
    { category: 'states', item: 'Default 상태 정의', required: true, passed: true },
    { category: 'states', item: 'Hover 상태 정의', required: true, passed: false },
    { category: 'states', item: 'Focus 상태 정의 (접근성)', required: true, passed: false },
    { category: 'states', item: 'Disabled 상태 정의', required: true, passed: true },
    { category: 'states', item: 'Loading 상태 정의', required: false, passed: false },

    // 반응형
    { category: 'responsive', item: '모바일 터치 영역 44px 이상', required: true, passed: true },
    { category: 'responsive', item: '텍스트 오버플로우 처리', required: true, passed: false },

    // 접근성
    { category: 'accessibility', item: 'aria-label 명세', required: true, passed: false },
    { category: 'accessibility', item: '색상 대비율 4.5:1 이상', required: true, passed: true },

    // 콘텐츠
    { category: 'content', item: '아이콘 only 버전 정의', required: false, passed: false },
    { category: 'content', item: '긴 텍스트 처리 방법', required: true, passed: false },
  ],
};

function generateHandoffReport(checklist: HandoffChecklist): string {
  const required = checklist.checks.filter(c => c.required);
  const passed = required.filter(c => c.passed);
  const failed = required.filter(c => !c.passed);

  const report = [
    `## ${checklist.component} 핸드오프 리포트`,
    `완성도: ${passed.length}/${required.length} (${Math.round(passed.length / required.length * 100)}%)`,
    '',
    '### 미완성 항목 (필수)',
    ...failed.map(f => `- ❌ [${f.category}] ${f.item}`),
  ];

  return report.join('\n');
}

6. 개발자-디자이너 QA 리뷰 프로세스

[2단계 QA 프로세스]

1단계: 디자이너 셀프 체크 (핸드오프 전)
  ├── 모든 컴포넌트 상태 Figma에 존재하는가?
  ├── 반응형 동작 명세가 텍스트로 기록됐는가?
  ├── 엣지 케이스 (빈 상태, 에러, 오버플로우) 정의됐는가?
  └── 접근성 주석 (aria-label, role) 추가됐는가?

2단계: 개발 완료 후 시각적 QA
  ├── [도구] Figma Compare, Pixel Perfect, VisBug
  ├── [체크] 폰트 크기/무게/행간
  ├── [체크] 색상 (정확한 hex 값)
  ├── [체크] 간격 (4px 그리드 기준)
  ├── [체크] 보더 반경, 그림자 값
  └── [체크] 인터랙션 타이밍 (transition)

브라우저별 QA:
  □ Chrome (기준)
  □ Safari (WebKit 렌더링 차이)
  □ Firefox
  □ iOS Safari (모바일)
  □ Chrome Android

마무리

핸드오프의 품질은 Figma 파일의 완성도가 아니라, 개발자가 구현하다 막히는 횟수로 측정된다. 막히는 횟수가 0에 가까울수록 좋은 핸드오프다.

체크리스트를 처음부터 완벽하게 채우려 하지 말고, 반복되는 QA 피드백을 체크리스트에 추가해나가는 방식으로 점진적으로 발전시켜라. 팀만의 핸드오프 기준이 생기면 속도가 크게 달라진다.