WCAG 성공 기준 · Level AAA

WCAG 2.2.5: 재인증

WCAG 2.2.5는 인증된 세션이 만료되었을 때, 사용자가 다시 인증을 하고 자신이 입력했던 어떤 데이터도 잃지 않은 채로 활동을 계속할 수 있어야 한다고 요구합니다. 이 기준은 작업을 완료하는 데 더 많은 시간이 필요할 수 있고, 자신의 작업을 지워 버리는 세션 타임아웃으로 인해 불이익을 받아서는 안 되는 장애가 있는 사용자들에게 특히 중요합니다.

이 규칙의 의미

WCAG 2.2.5 재인증(Re-authenticating)은 지침 2.2(충분한 시간)에 속하며 AAA 수준 요구사항이다. 이 기준은 다음과 같이 규정한다. “인증된 세션이 만료되었을 때, 사용자는 재인증 후 데이터 손실 없이 활동을 계속할 수 있어야 한다.” 실질적으로 이는 사용자가 인증이 필요한 플랫폼에서 양식을 작성하거나, 구매를 완료하거나, 메시지를 작성하거나, 여러 단계로 이루어진 작업을 수행하는 도중에 세션이 만료되더라도, 다시 로그인하여 이전에 입력한 모든 데이터가 보존된 상태로 정확히 중단했던 지점부터 이어서 진행할 수 있어야 함을 의미한다.

이 기준은 WCAG 2.2.1(시간 조절 가능, A 수준) 및 2.2.4(방해, AAA 수준)와 밀접하게 관련되어 있지만, 세션 경계가 넘어가는 특정 상황을 다룬다. 2.2.1은 사용자에게 충분한 시간을 제공하거나 세션을 연장할 수 있도록 요구하는 반면, 2.2.5는 시간 경과 이후에 무슨 일이 일어나는지, 즉 실패 상황을 다룬다. 두 기준은 함께 작동한다. 이상적으로는 세션을 연장해 주면서, 만약 세션이 만료되더라도 데이터를 보존해야 한다.

이 기준에서 통과하려면, 플랫폼이 재인증 이벤트 전후에 사용자가 입력한 모든 데이터 — 텍스트 입력, 선택된 옵션, 파일 첨부, 체크박스, 라디오 버튼 선택, 기타 모든 폼 상태 — 를 보존해야 한다. 사용자가 다시 로그인한 후에는, 수행 중이던 활동의 동일한 지점으로 돌아와야 하며, 데이터가 온전히 유지된 상태에서 작업을 반복 없이 완료할 수 있어야 한다.

실패는 세션 타임아웃으로 인해 다음과 같은 일이 발생할 때 일어난다. 사용자가 로그인 페이지로 리디렉션되고, 로그인에 성공한 후에도 세션이 만료되기 전 머물던 페이지가 아니라 홈페이지로 보내지는 경우; 타임아웃 전에 입력한 폼 데이터가 모두 사라져 사용자가 모든 내용을 다시 입력해야 하는 경우; 부분적으로 완료된 다단계 마법사(wizard)의 상태가 초기화되는 경우; 혹은 사용자가 단순히 로그아웃되고 재인증 및 재개할 수 있는 메커니즘이 전혀 제공되지 않는 경우 등이다.

공식 WCAG 명세는 이 기준에 대해 최소 세션 지속 시간을 정의하지 않는다. 타임아웃 기간이 길든 짧든 상관없이 적용된다. 다만, 보안상의 우려로 인한 데이터 손실에 대해서도 어떠한 예외도 제공되지 않는다는 점에 유의해야 한다. 구현은 사용자 식별 정보에 기반한 서버 측 세션 저장소나, 재인증 흐름을 거쳐도 유지되는 암호화된 클라이언트 측 토큰 등과 같이 상태를 안전하게 보존하는 방법을 찾아야 한다는 것이 전제이다.

왜 중요한가

데이터를 삭제하는 세션 타임아웃은 장애가 있는 사람들에게 불균형적으로 심각한 부담을 초래한다. 많은 장애인 사용자는 비장애인보다 디지털 작업을 완료하는 데 훨씬 더 많은 시간이 필요하며, 임의로 설정된 타임아웃을 “더 빨리 타이핑”하거나 “어떻게든 우회”하는 것이 불가능한 경우가 많다.

시각장애 또는 저시력 사용자는 화면 낭독기를 사용해 폼을 탐색하는데, 이는 시각적으로 훑어보는 것보다 본질적으로 느리다. 예를 들어, 시각장애인이 긴 보험 청구 양식을 작성하면서 필드 하나하나를 신중히 탐색하는 데 20~30분을 썼는데, 15분 세션 타임아웃이 발생하면서 작업 내용이 모두 사라질 수 있다. 그들은 다시 처음부터 시작해야 하며, 같은 일이 두 번째로 발생하지 않으리라는 보장도 없다.

운동장애가 있는 사람들 — 스위치 액세스 장치, 시선 추적 기술, 마우스 스틱 등을 사용하는 사람들 — 은 텍스트를 입력하고 폼을 탐색하는 데 평균보다 몇 배 더 오래 걸릴 수 있다. ALS나 척수성 근위축증이 있는 사용자가 중요한 의료 의뢰 양식을 작성하면서 분당 몇 글자만 입력할 수 있는 상황을 생각해 보자. 그들의 작업을 파괴하는 세션 타임아웃은 사소한 불편이 아니라 필수 작업을 완료하는 데 완전한 장벽이 될 수 있다.

인지장애가 있는 사용자, 예를 들어 난독증, ADHD, 외상성 뇌손상, 치매 등이 있는 사람들은 지침을 여러 번 다시 읽거나, 정보를 처리하기 위해 휴식을 취하거나, 단순히 여러 단계로 이루어진 프로세스를 더 천천히 진행해야 할 수 있다. 연구는 이 집단이 시간 압박과 데이터 손실의 영향을 불균형적으로 많이 받는다는 점을 일관되게 보여준다. 이 두 요소는 처음부터 다시 시작해야 한다는 인지적 부담을 더욱 가중시킨다.

구체적인 실제 시나리오를 생각해 보자. 다발성 경화증이 있는 사용자가 공공기관 웹 포털을 통해 정부 장애 급여를 신청하고 있다. 신청 절차는 8단계로 구성되어 있으며, 의료 문서 업로드, 고용 이력 입력, 개인 진술 작성이 필요하다. 이 사용자는 45분 동안 6단계를 진행한 후 세션이 만료되고, 입력한 모든 데이터가 사라진다. 이제 동일한 문서를 다시 모으고 전체 과정을 반복해야 하며, 결국 신청을 포기할 수도 있다. 이는 가상의 이야기가 아니라, 접근성 연구와 사용자 테스트에서 반복적으로 문서화된 패턴이다.

장애 여부를 떠나, 세션 타임아웃으로 인한 데이터 손실은 모든 사용자에게 영향을 미치며, 측정 가능한 비즈니스 영향을 초래한다. 장바구니 이탈, 미완료 가입, 잠재 고객 상실 등이 그 예다. 세션 상태를 보존하는 것은 접근성 측면에서 필수일 뿐 아니라, 건전한 UX 및 전환율 최적화 관행이기도 하다. Baymard Institute에 따르면, 폼 이탈은 전자상거래 장바구니 이탈률의 주요 원인 중 하나이며, 예기치 않은 데이터 손실은 사용자가 온라인 구매를 완료하지 않는 이유로 가장 많이 언급되는 요인 중 하나이다.

관련 Axe-core 규칙

WCAG 2.2.5는 수동 테스트만을 요구한다. 이 기준 위반을 신뢰성 있게 감지할 수 있는 자동화된 axe-core 규칙은 없다. 다음은 자동화 도구가 부족한 이유와 그 대신 사람이 해야 할 일을 설명한다.

  • 세션 상태 보존에 대한 자동 규칙 부재: Axe-core 및 유사한 자동 접근성 엔진은 현재 DOM을 검사하고 색 대비 비율, ARIA 속성의 정확성, 대체 텍스트 누락 등 정적 또는 준정적 조건을 평가하는 방식으로 동작한다. 이들은 시간 경과를 시뮬레이션하고, 세션 만료 이벤트를 트리거하고, 재인증 자격 증명을 제출한 다음, 이전에 입력한 데이터가 복원되었는지 확인할 수 없다. 이 전체 시퀀스는 상태 기반이며 시간에 의존하는 동작으로, 사람이(또는 스크립트 기반 E2E 테스트가) 관찰해야 한다.
  • 세션 타임아웃 감지는 정적 분석 범위 밖: 자동 도구가 메타 리프레시 태그나 JavaScript setTimeout 호출을 읽어 페이지가 세션 타임아웃을 구현했다는 사실을 감지할 수 있다고 하더라도, 타임아웃이 발생한 이후 사용자 데이터에 무슨 일이 일어나는지 평가할 수는 없다. 데이터 보존 동작은 DOM 구조가 아니라 서버 측 세션 관리 로직에 존재하기 때문이다.
  • 재인증 흐름의 복잡성: 재인증 경험은 로그인 폼, MFA 프롬프트, 리디렉션 등 여러 페이지를 포함할 수 있으며, 상태 복원은 서버 측 로직, 쿠키, 로컬 스토리지, URL 파라미터 등에 의존할 수 있다. 단일 페이지 DOM 검사 도구로는 이러한 다중 페이지·다중 시스템 흐름을 추적할 수 없다.
  • 수동 테스트가 필수인 이유: 자격을 갖춘 테스터가 인증된 워크플로를 수동으로 시작하고, 의도적으로 세션 만료를 기다리거나 트리거한 뒤, 재인증 과정을 완료하고, 데이터 무결성을 확인해야 한다. 이것이 적합성을 테스트하는 유일하게 신뢰할 수 있는 방법이다. 자동 스캔은 2.2.1에서 다루는 세션 경고 메커니즘 누락과 같은 관련 문제를 표시하는 출발점으로 사용할 수 있지만, 2.2.5에 대해서는 수동 테스트를 대체할 수 없다.

테스트 방법

  1. 인증이 필요한 폼 또는 다단계 프로세스 워크플로를 식별한다. 인증이 필요하고 사용자 데이터 입력이 포함된 사이트 영역 — 결제 흐름, 프로필 편집기, 신청 양식, 예약 시스템, 메시지 인터페이스, 관리자 대시보드 등을 모두 매핑하는 것부터 시작한다. 이 영역들이 2.2.5 실패 위험이 가장 높은 곳이다.
  2. 세션 타임아웃 지속 시간을 파악한다. 서버 설정, 애플리케이션 설정을 확인하거나 개발팀에 문의하여 세션이 언제 만료되는지 알아본다. 또는 세션을 시작한 뒤 자동으로 로그아웃될 때까지 기다린다. 그 시간을 기록한다. 일반적인 타임아웃은 15분에서 2시간 사이이다.
  3. 작업을 시작하고 충분한 데이터를 입력한다. 로그인한 뒤 대표적인 폼(예: 다수의 필드로 구성된 가입 또는 구매 흐름)을 작성하기 시작한다. 텍스트 필드에 텍스트를 입력하고, 선택을 수행하며, 마법사 단계가 있다면 진행한다. 폼은 제출하지 않는다.
  4. 세션 만료를 트리거한다. 자연스러운 타임아웃이 발생할 때까지 기다리거나, 개발자 권한이 있다면 브라우저 개발자 도구(Application 탭 > Cookies > 세션 쿠키 삭제)에서 세션 쿠키를 무효화하거나, 서버 측에서 직접 세션을 만료시켜 인위적으로 세션을 종료한다.
  5. 재인증 프롬프트를 관찰한다. 사이트가 재인증을 요청하는지(통과) 아니면 아무 경고 없이 로그인 페이지로 단순히 리디렉션하는지(이는 관련 사용성 문제이지만, 2.2.5는 경고 자체가 아니라 데이터 보존에 초점을 둔다) 확인한다. 다시 로그인을 시도한다.
  6. 로그인 후 데이터 보존을 확인한다. 재인증에 성공한 후, 어디로 리디렉션되는지, 그리고 이전에 입력한 데이터가 온전히 유지되어 있는지 확인한다. 홈페이지로 보내지거나 폼 데이터가 사라졌다면, 이는 2.2.5 실패이다.
  7. 보조기술과 함께 테스트한다. NVDA+Firefox, JAWS+Chrome, VoiceOver+Safari 조합으로 위 테스트 시퀀스를 반복하여, 재인증 프롬프트가 화면 낭독기 사용자에게 접근 가능하며, 복원된 페이지 상태가 올바르게 안내되는지 확인한다. 시력이 있는 사용자는 복원된 데이터를 시각적으로 인지할 수 있지만, 화면 낭독기 사용자는 포커스가 올바르게 배치되고 복원된 콘텐츠가 읽기 순서에 포함되어야 한다.
  8. 키보드 전용 내비게이션으로 테스트한다. 세션 만료 알림부터 재로그인, 보존된 작업으로 돌아가기까지 전체 재인증 과정이 마우스 없이 키보드만으로 완료될 수 있는지 확인한다.
  9. 확장된 타임아웃 시뮬레이션으로 테스트한다. 가능하다면 개발 환경에서 세션 타임아웃을 1~2분으로 줄이고 전체 사용자 여정 테스트를 수행하여 테스트 사이클을 더 빠르고 반복 가능하게 만든다.

수정 방법

세션 타임아웃이 있는 단순 로그인 폼 — 잘못된 예

<!-- BAD: Session expires silently. On re-login, user is sent to homepage.
     All entered data in the checkout form is lost. -->
<form action='/checkout' method='POST' id='checkout-form'>
  <input type='text' name='full_name' placeholder='Full Name' />
  <input type='text' name='address' placeholder='Address' />
  <button type='submit'>Place Order</button>
</form>

<!-- Server-side: session expires after 10 minutes of inactivity.
     No state saved. POST redirect on login goes to /dashboard. -->

세션 타임아웃이 있는 단순 로그인 폼 — 올바른 예

<!-- GOOD: Before session expires, form state is saved server-side
     (or to sessionStorage as a fallback). After re-auth, the user
     is redirected back to the checkout page with data restored. -->

<!-- 1. JavaScript saves form state periodically to the server -->
<script>
  function saveFormState() {
    const formData = new FormData(document.getElementById('checkout-form'));
    const data = Object.fromEntries(formData.entries());
    // Save to server-side session keyed to authenticated user identity
    fetch('/api/save-draft', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ formId: 'checkout', data })
    });
  }
  // Auto-save every 60 seconds and on input change
  setInterval(saveFormState, 60000);
  document.getElementById('checkout-form')
    .addEventListener('input', saveFormState);
</script>

<!-- 2. On the server: when user re-authenticates,
     redirect to /checkout?restore=true
     which reloads saved draft data into the form -->
<form action='/checkout' method='POST' id='checkout-form'>
  <!-- Form fields are pre-populated from saved draft -->
  <input type='text' name='full_name' value='[restored value]'
         aria-label='Full Name' />
  <input type='text' name='address' value='[restored value]'
         aria-label='Address' />
  <button type='submit'>Place Order</button>
</form>

진행 상황을 잃는 다단계 마법사 — 잘못된 예

<!-- BAD: Multi-step form uses only client-side state.
     Session expiry wipes wizard progress completely.
     Re-login sends user to step 1 with no data. -->

<div id='wizard'>
  <div class='step active' id='step-3'>
    <h2>Step 3 of 5: Employment History</h2>
    <textarea name='employment_history'>Typed content here...</textarea>
  </div>
</div>

<script>
  // State is only in JavaScript variables — lost on session expiry
  let wizardState = { step: 3, data: {} };
</script>

진행 상황을 잃는 다단계 마법사 — 올바른 예

<!-- GOOD: Wizard state is persisted server-side at each step
     and linked to the user's account. On re-authentication,
     the server restores the user to their last saved step
     with all data intact. A visible confirmation is shown. -->

<div id='wizard'>
  <div class='step active' id='step-3'
       aria-live='polite' aria-label='Step 3 of 5: Employment History'>
    <p role='status'>
      Your progress has been restored from your last session.
    </p>
    <h2>Step 3 of 5: Employment History</h2>
    <!-- Data is pre-populated from server-side draft -->
    <textarea name='employment_history'
              aria-label='Describe your employment history'>
      Previously entered content restored here...
    </textarea>
    <!-- Wizard nav saves progress before moving to next step -->
    <button type='button' onclick='saveAndProceed()'>Next Step</button>
  </div>
</div>

<script>
  async function saveAndProceed() {
    await fetch('/api/wizard/save', {
      method: 'POST',
      body: JSON.stringify({ step: 3, data: collectStepData() }),
      headers: { 'Content-Type': 'application/json' }
    });
    goToStep(4);
  }
</script>

데이터 보존 없는 세션 만료 경고 — 잘못된 예

<!-- BAD: Site shows a countdown warning but does not save data.
     If the user misses the warning (e.g., screen reader user
     focused elsewhere), they lose all their work. -->
<div id='timeout-warning' style='display:none'>
  <p>Your session will expire in 2 minutes. <a href='/extend'>Extend</a></p>
</div>

데이터 보존 없는 세션 만료 경고 — 올바른 예

<!-- GOOD: Warning uses aria-live so screen readers announce it.
     Data is saved server-side regardless of whether the user
     extends the session — so re-auth always restores state. -->
<div id='timeout-warning'
     role='alertdialog'
     aria-live='assertive'
     aria-labelledby='warning-title'
     aria-describedby='warning-desc'
     hidden>
  <h2 id='warning-title'>Session Expiring Soon</h2>
  <p id='warning-desc'>
    Your session will expire in 2 minutes. Your work has been
    saved automatically. You can extend your session or log in
    again to continue exactly where you left off.
  </p>
  <button onclick='extendSession()'>Extend Session</button>
  <button onclick='dismissWarning()'>Dismiss</button>
</div>

자주 발생하는 실수

  • 재인증 후 원래 페이지가 아닌 홈페이지로 리디렉션하는 경우: 가장 흔한 실패 패턴이다. 로그인 후 애플리케이션이 사용자를 세션이 만료되기 전 머물던 URL이 아니라 /dashboard 또는 /로 보내, 사용자가 작업으로 다시 이동해야 할 뿐 아니라, 그 사이 데이터는 이미 사라진다.
  • 폼 상태를 JavaScript 메모리나 React/Vue 컴포넌트 상태에만 저장하는 경우: 세션 만료로 로그인 페이지로 리디렉션되면서 발생하는 페이지 새로고침 이후에는 클라이언트 측 프레임워크 상태가 유지되지 않는다. 상태는 서버 측이나, 돌아왔을 때 신뢰성 있게 복원할 수 있는 저장 메커니즘(예: localStorage)에 저장해야 한다.
  • “리턴 URL”만 저장하고 폼 데이터는 저장하지 않는 경우: 일부 구현은 로그인 후 리디렉션할 URL만 저장하고 실제 폼 필드 값은 저장하지 않는다. 사용자는 올바른 페이지로 돌아오지만 필드는 비어 있으며, 이는 여전히 2.2.5 위반이다.
  • 탭이 닫히면 지워지는 sessionStorage에 자동 저장하는 경우: 세션 만료로 인해 브라우저 탭이 다른 페이지(예: 로그인 페이지)로 이동하면 sessionStorage는 지워진다. 사용자 키를 네임스페이스로 사용하는 localStorage나, 가능하면 서버 측 임시 저장을 사용해야 한다.
  • 파일 업로드 입력을 테스트하지 않는 경우: 보안상의 이유로 파일 입력은 HTML로 미리 채울 수 없다. 세션 만료 전에 파일 업로드를 완료한 폼이 있다면, 파일 참조는 사라진다. 애플리케이션은 업로드된 파일 참조를 서버 측에 저장하고, 사용자가 해당 파일이 여전히 첨부되어 있음을 확인할 수 있도록 처리해야 한다.
  • 재인증 직후 저장된 임시 데이터를 즉시 삭제하는 경우: 일부 구현은 사용자가 복원된 데이터를 실제로 보고 확인하기 전에 로그인하는 순간 임시 데이터를 삭제한다. 임시 데이터는 작업이 명시적으로 완료되거나 취소될 때까지 유지해야 한다.
  • 복원된 데이터를 화면 낭독기 사용자에게 알리지 않는 경우: 재인증 및 리디렉션 후, 화면 낭독기 사용자는 데이터가 복원되었고 중단한 지점부터 계속할 수 있다는 사실을 알려주는 aria-live 영역이나 포커스 기반 안내가 필요하다. 그렇지 않으면 자신의 작업이 저장되었는지 알 수 없다.
  • AJAX 기반 세션을 페이지 기반 세션과 다르게 취급하는 경우: 토큰 기반 인증(JWT 만료 등)을 사용하는 단일 페이지 애플리케이션은 토큰이 만료되었을 때 재인증 및 재개 기회를 제공하지 않고 API 호출을 조용히 실패시키는 경우가 많다. 재인증 메커니즘은 SPA 아키텍처에서도 동일하게 견고해야 한다.
  • <meta http-equiv='refresh'>를 사용해 사전 데이터 저장 없이 강제 로그아웃하는 경우: 타임아웃 시 실행되는 메타 리프레시는 서버 측 상태 보존 없이 즉시 사용자를 리디렉션하여 데이터 복구를 불가능하게 만든다. 세션 관리는 적절한 상태 보존과 함께 애플리케이션 레벨에서 처리해야 한다.
  • 세션이 짧으면 이 기준이 필요 없다고 가정하는 경우: 5분짜리 세션 타임아웃조차 느리게 타이핑하는 사용자, 지침을 다시 읽는 인지장애 사용자, 혹은 보호자에게 방해받은 사람을 쉽게 덮칠 수 있다. 타임아웃의 길이는 재인증 간 데이터 보존 의무를 바꾸지 않는다.

터키의 접근성 규정과의 관계

터키의 대통령령 2025/10(2025년 6월 21일 관보 제32933호에 게재)은 디지털 접근성에 대한 국가적 프레임워크를 수립하고 WCAG 2.2를 기술 표준의 기준선으로 참조한다. 이 대통령령은 터키에서 운영되는 광범위한 주체 — 공공기관 및 공공 단체, 전자상거래 플랫폼, 은행 및 금융 서비스 제공자, 병원 및 민간 의료 제공자, 200,000명 이상의 가입자를 보유한 통신사, 여행사, 민간 운송 회사, 그리고 교육부(MoNE)의 인가를 받은 민간 교육기관 — 에게 적합성을 의무화한다.

WCAG 2.2.5 재인증은 AAA 수준 기준으로, 대통령령이 목표로 하는 A 및 AA 수준 적합성(최소 법적 요구사항)에 포함되지는 않는다. 그러나 접근성 분야에서 선도적 위치를 입증하고자 하거나, 장애인을 포함한 다양한 사용자 집단을 지원하고자 하거나, 최고 수준의 디지털 서비스 제공자로 자리매김하고자 하는 조직은 특히 2.2.5처럼 실질적인 영향이 큰 AAA 수준 기준을 추구할 가치가 있는 목표로 간주해야 한다.

특정 범주의 대상 기관은 2.2.5를 구현해야 할 이유가 특히 강하다. 은행 및 금융 플랫폼은 대출 신청, 계좌 개설, 투자 상품 등을 위해 긴 양식을 요구하는 경우가 많으며, 보안을 이유로 한 짧은 세션 타임아웃은 데이터 보존 의무와 직접적인 긴장을 일으킨다. 병원 및 의료 포털은 예약, 건강 설문 작성 등 중요한 작업 중에 환자를 데이터 손실에 노출시키며, 이는 연속적인 진료에 실제적인 영향을 줄 수 있다. 공공기관이 운영하는 전자정부 서비스 — 세금 신고, 허가 신청, 급여 청구 등 — 는 복잡한 정부 양식을 작성하는 데 더 많은 시간이 필요한 장애인 시민을 대상으로 한다.

AAA 수준 준수가 법적으로 요구되지 않는 경우에도, 터키의 조직은 규제 기관, 시민사회 단체, 장애인 권리 옹호자들이 디지털 접근성 구현의 품질과 완성도를 점점 더 면밀히 검토하고 있다는 점을 인식해야 한다. 2.2.5와 같은 AAA 수준 기준에 대한 적합성을 문서화하는 것은 조직의 접근성 성명서를 강화하고, 소송 위험을 줄이며, 최소한의 형식적 준수를 넘어선 진정한 포용적 디자인 의지를 보여준다. 특히 터키와 함께 EU 시장에서도 활동하는 국제적 범위의 기관의 경우, AAA 수준 기준은 유럽 접근성법(EAA) 및 관련 국가별 이행 규정의 요구사항과 교차할 수 있다.