Testing/Accessibility tests

김동현·2026년 3월 22일

접근성 테스트

웹 접근성(Web accessibility)은 장애가 있거나 사용하는 기술 환경과 관계없이, 모든 사람이 웹사이트와 앱을 접근하고 포괄적으로 사용할 수 있도록 만드는 것을 말합니다. 즉, 키보드 네비게이션, 스크린 리더(화면 읽어주기) 지원, 충분한 색상 대비 등 다양한 요구사항을 지원하는 것을 의미하죠.

접근성은 단순히 '옳은 일'일뿐만 아니라, 점점 더 법적으로도 의무화되고 있는 추세예요. 예를 들어, 유럽 접근성 법안(European accessibility act)은 2025년 6월에 발효될 예정입니다. 미국에서도 미국 장애인법(ADA)재활법 508조(Section 508) 같은 법률들이 대중을 위한 여러 서비스에 널리 적용되고 있죠. 이러한 법률들 대부분은 웹 콘텐츠를 접근성 있게 만들기 위한 표준 가이드라인인 WCAG (Web Content Accessibility Guidelines)를 기반으로 하고 있습니다.

접근성 테스트는 렌더링된 DOM을 WCAG 규칙이나 업계에서 인정받는 모범 사례들을 바탕으로 꼼꼼히 감사(audit)하는 과정입니다. 아주 명백한 접근성 위반 사항들을 잡아내는 QA(품질 보증)의 첫 번째 방어선 역할을 톡톡히 해내죠.

애드온 설치하기 (Install the addon)

스토리북은 여러분의 컴포넌트 접근성을 쉽게 확보할 수 있도록 접근성(a11y) 애드온을 제공합니다. 이 애드온은 Deque 시스템즈의 axe-core 라이브러리를 기반으로 만들어져서, 자동으로 최대 57%의 WCAG 위반 이슈를 척척 잡아냅니다.

프로젝트에 이 애드온을 설치하고 설정하려면 아래 명령어를 실행해 주세요:

npx storybook add @storybook/addon-a11y

ℹ️ 스토리북의 add 명령어는 애드온의 설치와 설정을 모두 자동으로 처리해 줍니다. 수동으로 설치하고 싶으시다면, 애드온 설치 공식 문서를 참고해 주세요.

설치가 완료되면 여러분의 스토리북에 컴포넌트 접근성을 체크할 수 있는 유용한 기능들이 추가됩니다! 툴바에는 다양한 시각 장애 환경을 시뮬레이션해 볼 수 있는 버튼이 생기고, 하단 패널에는 접근성 위반 사항을 꼼꼼하게 검사해 주는 'Accessibility(접근성)' 탭이 새롭게 나타날 거예요.

Storybook UI with accessibility features annotated

Vitest 애드온과의 통합 (Integration with Vitest addon)

접근성 애드온은 Vitest 애드온(Vitest addon)과 매끄럽게 통합되도록 설계되었습니다. 덕분에 컴포넌트 테스트를 실행할 때 접근성 테스트도 함께 나란히 실행(run accessibility tests)할 수 있어요.

이 기능을 활용하려면, 먼저 Vitest 애드온과 Vitest 자체를 설치하고 설정해 주는 아래 명령어를 실행하세요:

npx storybook add @storybook/addon-vitest

프로젝트 요구사항을 포함한 전체 설치 가이드는 Vitest 애드온 공식 문서에서 아주 자세히 확인하실 수 있습니다.

위반 사항 확인하기 (Check for violations)

스토리 화면에 들어가기만 하면, 자동화된 접근성 검사가 바로 실행되고 그 결과가 Accessibility 애드온 패널에 짠! 하고 나타납니다.

검사 결과는 다음 세 가지 하위 탭으로 깔끔하게 나누어져서 보여요:

  • Violations (위반): WCAG 규칙 및 접근성 모범 사례를 명백하게 위반한 사항들을 보여줍니다.
  • Passes (통과): 접근성 검사를 무사히 통과한, 문제없는 사항들입니다.
  • Incomplete (불완전): 자동화 도구만으로는 완벽하게 검사하기 어려워서 개발자가 직접 눈으로 확인해 봐야 하는 사항들을 짚어줍니다.

설정하기 (Configure)

이 애드온은 튼튼한 axe-core를 기반으로 구축되었기 때문에, 애드온 설정 역시 axe-core에서 제공하는 든든한 옵션들과 거의 똑같이 매핑됩니다.

속성 (Property)기본값 (Default)설명 (Description)
parameters.a11y.context'body'axe.run 함수로 전달되는 Context입니다. DOM의 어떤 엘리먼트들을 검사 대상으로 할지 범위를 정의해요.
parameters.a11y.config(아래 내용 참조)axe.configure() 함수로 전달되는 설정 객체입니다. 주로 개별 규칙들을 활성화/비활성화(configure individual rules)할 때 가장 많이 쓰여요.
parameters.a11y.options{}axe.run 함수로 전달되는 Options입니다. 검사할 규칙 셋(ruleset) 기준을 변경할 때 유용하게 쓰입니다.
parameters.a11y.testundefinedVitest 애드온과 함께 실행될 때의 테스트 동작 방식을 결정합니다. 더 자세한 내용은 아래를 참고해 주세요.
globals.a11y.manualundefined이 값을 true로 설정하면 스토리에 방문했을 때 자동으로 분석이 실행되는 것을 막아줍니다. 더 자세한 내용은 아래를 참고해 주세요.
parameters.a11y.config의 기본값

기본적으로 스토리북은 axe-coreregion(랜드마크) 규칙을 비활성화해 둡니다. 스토리북 환경에서 렌더링되는 개별 컴포넌트들에게는 이 규칙이 잘 맞지 않아서, 불필요한 에러(false negatives)를 자주 뿜어내기 때문이에요.

{
  rules: [
    {
      id: 'region',
      enabled: false,
    }
  ]
}

이 설정 속성들을 어떻게 사용하는지 몇 가지 예시를 통해 보여드릴게요!

먼저, 프로젝트 전체 스토리에 일괄적으로 적용하고 싶다면 .storybook/preview.ts 파일에서 설정하시면 됩니다:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    a11y: {
      /*
       * Axe's context parameter
       * 자세한 내용은 [https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#context-parameter](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#context-parameter) 를 참고하세요.
       * 보통은 검사하고 싶은 DOM 부분의 CSS 선택자(selector)를 넣습니다.
       */
      context: 'body',
      /*
       * Axe's configuration
       * 사용 가능한 속성들은 [https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axeconfigure](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axeconfigure) 에서 확인하세요.
       */
      config: {},
      /*
       * Axe's options parameter
       * 사용 가능한 옵션들은 [https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter) 에서 확인하세요.
       */
      options: {},
      /*
       * Configure test behavior
       * 자세한 내용은 [https://storybook.js.org/docs/next/writing-tests/accessibility-testing#test-behavior](https://storybook.js.org/docs/next/writing-tests/accessibility-testing#test-behavior) 를 참고하세요.
       */
      test: 'error',
    },
  },
  globals: {
    a11y: {
      // 자동 검사를 막는 선택적 플래그입니다.
      manual: true,
    },
  },
} satisfies Preview;
 
export default preview;

물론 파일 내 모든 스토리(meta 사용)나 특정 개별 스토리에만 딱 꼬집어서 설정을 적용할 수도 있어요:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  parameters: {
    a11y: {
      // ...위에서 봤던 설정들을 여기서도 그대로 쓸 수 있어요!
    },
  },
  globals: {
    a11y: {
      // ...위에서 봤던 설정들을 여기서도 그대로 쓸 수 있어요!
    },
  },
} satisfies Meta<typeof Button>;
export default meta;
 
type Story = StoryObj<typeof meta>;
 
export const ExampleStory: Story = {
  parameters: {
    a11y: {
      // ...위에서 봤던 설정들을 여기서도 그대로 쓸 수 있어요!
    },
  },
  globals: {
    a11y: {
      // ...위에서 봤던 설정들을 여기서도 그대로 쓸 수 있어요!
    },
  },
};

규칙 셋 (Rulesets)

이 애드온은 앞서 말씀드렸듯 axe-core 라이브러리를 사용해서 검사를 진행합니다. 기본적으로는 WCAG 2.0과 2.1 가이드라인, 그리고 몇 가지 모범 사례들을 기반으로 한 규칙 셋을 검사해요:

이 규칙들의 상세한 내용이나 axe-core가 지원하는 다른 규칙 셋들이 궁금하시다면 axe-core 공식 문서를 방문해 보세요!

만약 검사 기준을 WCAG 2.2 AA나 WCAG 2.x AAA 규칙으로 깐깐하게 바꾸고 싶다면, runOnly 옵션을 살짝 건드려주시면 됩니다:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    a11y: {
      options: {
        /*
         * WCAG 2.x AAA 규칙을 실행하도록 선택합니다.
         * 주의! 기본값들(아래 배열의 마지막 항목을 제외한 전부)을 명시적으로 다시 적어주셔야 합니다.
         * 더 자세한 내용은 [https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter-examples](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter-examples) 를 참고하세요.
         */
        runOnly: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice', 'wcag2aaa'],
      },
    },
  },
} satisfies Preview;
 
export default preview;

개별 규칙 제어하기 (Individual rules)

필요하다면 특정 규칙 하나하나를 직접 켜거나 끄고 커스텀할 수도 있습니다! parameters.a11y 객체 안의 config 속성을 활용하면 되는데요. 예를 들어볼게요:

// ...rest of story file
 
export const IndividualA11yRulesExample: Story = {
  parameters: {
    a11y: {
      config: {
        rules: [
          {
            // 입력한 CSS 선택자에 해당하는 엘리먼트에서는 autocomplete 규칙을 무시합니다.
            id: 'autocomplete-valid',
            selector: '*:not([autocomplete="nope"])',
          },
          {
            // enabled 속성을 false로 주면, 이 스토리에선 해당 규칙 검사를 아예 꺼버립니다.
            id: 'image-alt',
            enabled: false,
          },
        ],
      },
    },
  },
};

테스트 동작 방식 설정 (Test behavior)

parameters.a11y.test 파라미터를 설정하면, Vitest 애드온이나 test-runner를 통해 접근성 테스트를 돌릴 때 스토리가 어떻게 반응할지를 결정할 수 있습니다. 입력할 수 있는 값은 세 가지예요:

값 (Value)설명 (Description)
'off'이 스토리는 자동 접근성 테스트를 실행하지 않습니다. (하지만 스토리북 화면의 애드온 패널에서는 여전히 수동으로 검사해 볼 수 있어요.)
'todo'접근성 테스트를 실행하되, 위반 사항이 발견되면 실패 처리하지 않고 스토리북 UI에 부드러운 경고(warning) 메시지만 띄웁니다.
'error'접근성 테스트를 실행하고, 위반 사항이 발견되면 단호하게 테스트 실패(failing test) 처리합니다! 스토리북 UI와 CLI/CI 화면 모두에 뻘겋게 표시될 거예요.

다른 파라미터들처럼, 이것도 .storybook/preview.js|ts에서 프로젝트 전체 단위로 걸어두거나, 스토리 파일의 meta(default export)에서 컴포넌트 단위로, 또는 개별 스토리 단위로 아주 유연하게 정의할 수 있어요.

예를 들어, 파일 안의 다른 모든 스토리는 접근성 에러 시 실패 처리하게 만들고, 딱 하나의 스토리만 유하게 넘어가도록 설정하고 싶다면 이렇게 하시면 됩니다:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  parameters: {
    // 👇 파일 안의 모든 스토리에 적용됩니다.
    a11y: { test: 'error' },
  },
} satisfies Meta<typeof Button>;
export default meta;
 
type Story = StoryObj<typeof meta>;
 
// 👇 이 스토리는 'error' 설정을 받아서 위반 시 테스트가 얄짤없이 실패합니다.
export const Primary: Story = {
  args: { primary: true },
};
 
// 👇 이 스토리는 위반하더라도 테스트가 실패하지 않습니다!
//    (대신 테스트는 여전히 돌아가고 경고 메시지가 표시됩니다.)
export const NoA11yFail: Story = {
  parameters: {
    a11y: { test: 'todo' },
  },
};

ℹ️ 왜 값이 "warn(경고)"이 아니라 "todo"인지 궁금하시죠? 이 값은 여러분의 코드베이스에 문자 그대로 TODO 마커를 남기는 역할을 하도록 의도된 거예요. 당장 접근성에 문제가 있는 건 알지만, 지금 당장 고치기는 애매할 때! 그 스토리들을 나중을 위해 살포시 체크해 두는 용도랍니다.

반면, 'off' 값은 아예 접근성 테스트를 할 필요 자체가 없는 스토리(예: 일부러 안 좋은 코드 작성 패턴(antipattern)을 보여주기 위해 만든 스토리)에만 사용하는 것이 좋습니다.

특정 규칙만 상황에 안 맞는 경우라면 아까 배운 대로 개별 규칙 비활성화(disable individual rules) 기능을 사용하는 편이 훨씬 좋고요!

자동 검사 완전히 끄기 (Disable automated checks)

자동 접근성 검사를 꺼버리면, 스토리에 들어갈 때나 Vitest 애드온으로 테스트를 돌릴 때(run the tests with the Vitest addon) 애드온이 나서서 테스트를 실행하지 않습니다. (물론 Accessibility 애드온 패널에서 버튼을 눌러 수동으로 검사할 수는 있어요.) 접근성 규칙을 굳이 따를 필요가 없는 스토리, 예를 들어 의도적인 안티패턴(antipattern)을 보여주거나 아주 특이한 상황을 재현한 스토리에서 유용하게 쓰일 수 있겠죠.

특정 스토리나 컴포넌트에서 자동 접근성 검사를 끄려면, globals 속성에 아래와 같이 설정해 주세요:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { MyComponent } from './MyComponent';
 
const meta = {
  component: MyComponent,
} satisfies Meta<typeof MyComponent>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const NonA11yStory: Story = {
  globals: {
    a11y: {
      // 이 옵션 하나로 이 스토리에 대한 모든 자동 a11y 검사를 완전히 끕니다!
      manual: true,
    },
  },
};

특정 엘리먼트 검사 제외하기 (Excluded elements)

가끔은 특정 엘리먼트를 접근성 검사 대상에서 빼고 싶을 때가 있습니다. 이럴 땐 커스텀 context를 정의해서 검사에 포함시킬지, 뺄지를 콕 집어 선택할 수 있어요.

예를 들어, 아래 스토리는 no-a11y-check라는 클래스(class)를 가진 엘리먼트는 모른 척 넘어가고 검사하지 않습니다:

// ...rest of story file
 
export const ExampleStory: Story = {
  parameters: {
    a11y: {
      /*
       * Axe's context parameter
       * 자세한 내용은 [https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#context-parameter](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#context-parameter) 를 참고하세요.
       */
      context: {
        include: ['body'],
        exclude: ['.no-a11y-check'],
      },
    },
  },
};

접근성 테스트 실행하기 (Run accessibility tests)

Vitest 애드온과 함께 사용하기 (With the Vitest addon)

Vitest 애드온(Vitest addon)을 사용 중이시라면, 컴포넌트 테스트를 실행할 때 접근성 테스트도 아래 두 가지 환경에서 함께 실행할 수 있습니다:

스토리북 UI 안에서 바로 접근성 테스트를 실행하려면, 먼저 사이드바에 있는 테스팅 위젯을 펼친 다음 'Accessibility' 체크박스에 체크해 주세요. 이제 'Run component tests' 버튼을 누르면, 설정해 둔 다른 테스트들과 함께 접근성 테스트도 시원하게 돌아갑니다.

Test widget, expanded, with accessibility checked

테스트가 끝나면 사이드바에서 각 스토리 옆에 테스트 상태 표시기(indicator)가 나타나는 걸 볼 수 있어요. 이 표시기를 클릭하면 접근성 테스트 결과가 담긴 메뉴가 열립니다. 거기서 결과를 누르면 해당 스토리로 바로 이동하면서 접근성(Accessibility) 패널이 열리게 되는데, 여기서 각 위반 사항에 대한 상세한 설명과 어떻게 고치면 좋을지 친절한 제안을 확인할 수 있습니다.

Storybook showing a failing accessibility test in both the sidebar story menu and the Accessibility panel

만약 경고(warnings)나 실패(failures)가 뜬 테스트가 있다면, 테스팅 위젯에 그 개수가 표시됩니다. 이 숫자를 클릭하면 사이드바에서 경고나 실패가 발생한 스토리들만 쏙쏙 골라서 필터링해 볼 수도 있어요.

CI 환경에서는 Vitest 테스트를 실행할 때 parameters.a11y.test = 'error'로 설정된 스토리들에 한해 접근성 테스트가 자동으로 깐깐하게 실행됩니다.

테스트 러너와 함께 사용하기 (With the test-runner)

test-runner를 사용하면 터미널이나 CI 환경에서 접근성 테스트를 간편하게 실행할 수 있습니다.

접근성 애드온이 설치되어 있고 parameters.a11y.test 설정값이 'off'가 아닌 다른 값으로 되어 있다면, 테스트를 실행할 때 접근성 테스트도 알아서 포함되어 돌아갑니다.

접근성 위반 디버깅하기 (Debug accessibility violations)

접근성 테스트를 실행하고 나면 그 결과가 스토리북 UI에 예쁘게 리포트됩니다. 목록에서 위반 사항을 하나 클릭해 보면, 어떤 규칙을 어겼는지, 그리고 어떻게 수정해야 할지 팁을 알려주는 상세 정보를 볼 수 있어요.

또한, 스토리북 UI에서 하이라이트 기능(highlighting)을 켜면 화면에서 정확히 어느 엘리먼트가 문제의 원인인지 시각적으로 확인할 수 있습니다. 하이라이트된 엘리먼트를 클릭하면 팝오버 메뉴가 뜨면서 해당 위반 사항의 디테일을 한 번 더 보여준답니다.

Storybook UI with a highlighted element with a popover menu showing accessbility violation details

CI에서 자동화하기 (Automate with CI)

Vitest 애드온으로 접근성 테스트를 실행하고 계신가요? 그럼 CI 환경에서 테스트를 실행하는 것만으로도 자동화가 아주 쉽게 끝납니다! 좀 더 자세한 세팅 방법이 궁금하시다면 CI에서 테스트하기 가이드(testing in CI guide)를 꼭 확인해 보세요.

⚠️ 주의사항: CI 환경에서 접근성 테스트가 에러를 발생시켜 파이프라인을 멈추게 하려면, 반드시 parameters.a11y.test 값을 'error'로 설정해 두셔야 합니다. 만약 'todo'로 설정해 두었다면, CI에서는 접근성 관련 에러나 경고가 출력되지 않고 조용히 넘어가요. (물론 로컬에서 스토리북을 띄워 테스트를 돌려보면 스토리북 UI상에서는 경고(warnings)로 잘 나타납니다.)

Vitest 애드온을 쓰기 어려운 상황이라도 걱정 마세요. test-runner를 사용하면 여전히 CI에서 테스트를 훌륭하게 자동화할 수 있습니다.

설정을 단계적으로 조절해 가며 점진적으로 UI 접근성을 개선해 나가는 전략을 추천해 드려요. 예를 들자면, 처음에는 'error'로 빡빡하게 설정해서 접근성 위반 시 무조건 실패하게 만들었다가, 아직 고치기 힘든 컴포넌트들은 일단 'todo'로 살짝 바꿔서 마킹만 해두고, 나중에 모든 스토리가 접근성 테스트를 완벽하게 통과하면 그때 'todo'를 지워나가는 식이죠:

  1. 프로젝트 설정을 업데이트해서 접근성 위반 시 테스트가 실패하도록 만듭니다. parameters.a11y.test'error'로 설정하는 거죠. 이렇게 하면 앞으로 만들어질 모든 새로운 스토리들은 무조건 접근성 표준을 통과해야만 합니다!
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    // 👇 위반 사항이 발견되면 모든 접근성 테스트를 실패(fail) 처리합니다!
    a11y: { test: 'error' },
  },
};
export default preview;
  1. 막상 돌려보면 아마 엄청나게 많은 컴포넌트에서 접근성 실패가 뜰 거예요. (처음엔 좀 당황스러울 수 있습니다!)
  2. 접근성 문제가 있는 컴포넌트들을 파악한 다음, 임시로 'todo' 파라미터 값을 적용해서 실패(failures)를 경고(warnings) 수준으로 낮춰줍니다. 이렇게 하면 당장의 개발 흐름을 막지 않으면서도 접근성 이슈가 있다는 사실을 계속 시각적으로 인지할 수 있죠. 이때 커밋(commit)을 한 번 해두면, 앞으로의 개선 작업에 대한 훌륭한 기준점(baseline)이 됩니다.
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import { Meta } from '@storybook/your-framework';
 
import { DataTable } from './DataTable';
 
const meta = <{
  component: DataTable,
  parameters: {
    // 👇 이 컴포넌트의 접근성 테스트는 실패하지 않습니다.
    //    대신, 스토리북 UI에 경고(warnings)를 띄워줍니다.
    a11y: { test: 'todo' },
  },
} satisfies Meta<typeof DataTable>;
export default meta;
  1. 방금 'todo'로 마킹해둔 컴포넌트들 중에서 가장 만만한 녀석(예를 들어 다른 컴포넌트에서 널리 쓰이면서도 구조가 단순한 Button 같은 거요!)을 하나 골라 첫 타겟으로 삼습니다. 애드온 패널이 제시해 주는 꿀팁들을 참고해서 문제들을 싹 고쳐준 다음, 드디어 접근성 테스트를 통과하게 되면 그 지긋지긋한 파라미터를 당당하게 지워버리세요!
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import { Meta } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  parameters: {
    // 👇 모든 스토리가 접근성 테스트를 통과했다면 이 줄을 과감히 지워주세요!
    // a11y: { test: 'todo' },
  },
} satisfies Meta<typeof Button>;
export default meta;
  1. 이제 다른 컴포넌트를 하나 고르고, 모든 컴포넌트를 정복해서 접근성 마스터(accessibility hero)가 될 때까지 이 과정을 계속 반복하시면 됩니다!

자주 묻는 질문 (FAQ)

브라우저 기반(browser-based) 접근성 테스트와 린터 기반(linter-based) 접근성 테스트의 차이점은 무엇인가요?

스토리북에서 사용하는 것과 같은 브라우저 기반 접근성 테스트는 화면에 '실제로 렌더링된 DOM'을 평가하기 때문에 훨씬 더 높은 정확도를 자랑합니다. 아직 컴파일도 되지 않은 코드를 분석(audit)하는 린터 방식은 실제 동작 환경과 한 단계 떨어져 있어서, 사용자가 겪게 될 모든 문제를 샅샅이 잡아내지는 못하거든요.

왜 제 테스트들이 환경마다 다르게 실패(fail)할까요?

Vitest 애드온(Vitest addon)을 사용하면, 여러분의 테스트는 프로젝트 설정에 따라 Vitest 환경 내의 Playwright Chromium 브라우저에서 실행됩니다. 이 때문에 스토리북 UI에서 보는 결과와 CLI에서 보는 결과가 서로 불일치하는 현상이 생길 수 있어요. 이런 불일치는 주로 axe-core가 브라우저 버전이나 설정 같은 환경 차이에 따라 다른 결과를 내놓기 때문에 발생합니다. 만약 이런 문제를 겪고 계신다면, 저희 공식 소통 채널(GitHub discussionsGithub issues)을 통해 언제든 편하게 질문을 남겨주세요.

애드온 패널에 나와야 할 위반 사항들이 안 보여요!

요즘 모던 React 컴포넌트들은 복잡한 데이터 페칭과 렌더링을 처리하기 위해 SuspenseReact Server Components (RSC) 같은 비동기(asynchronous) 기술을 참 많이 쓰죠. 이런 컴포넌트들은 UI의 최종 상태를 즉각적으로 렌더링하지 않아요. 문제는 스토리북이 언제 이 비동기 컴포넌트의 렌더링이 완전히 끝났는지 자체적으로 알 방법이 없다는 겁니다. 결과적으로 접근성 검사(a11y checks)가 렌더링이 다 끝나기도 전에 너무 일찍 휙 돌아가 버려서, 실제로는 위반 사항이 있는데도 없다고 나오는 '거짓 음성(false negatives)' 현상이 발생할 수 있어요.

이 문제를 해결하기 위해 저희가 developmentModeForBuild라는 기능 플래그(feature flag)를 도입했습니다. 이 플래그를 켜면 빌드된 스토리북 내부의 process.env.NODE_ENV 값이 'development'로 설정되는데, 이렇게 하면 보통 프로덕션 빌드에서는 꺼져있는 개발(development) 관련 최적화 기능들이 활성화됩니다. 그중 하나가 바로 React의 act 유틸리티인데요. 이 녀석이 테스트와 관련된 모든 업데이트가 완전히 처리되고 적용될 때까지 기다렸다가 접근성 검사 같은 단언(assertions) 작업이 수행되도록 꽉 잡아주는 역할을 합니다.

이 플래그를 활성화하려면 .storybook/main.js|ts 파일에 아래 설정을 추가해 주시면 됩니다:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
 
const config: StorybookConfig = {
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  features: {
    developmentModeForBuild: true,
  },
};
 
export default config;

더 유용한 테스팅 관련 자료들

profile
프론트에_가까운_풀스택_개발자

0개의 댓글