웹 개발에서 AI가 잘못하는 10가지

게시일: 2026년 11월 11일

이번 주에는 웹 플랫폼 전문 지식, 권장사항, 브라우저 호환성 데이터를 코딩 에이전트에 직접 삽입하는 기술 모음인 Modern Web Guidance 버전 1.0을 출시했습니다. Modern Web Guidance의 목표는 AI 코딩 에이전트를 사용하는 개발자가 최신 웹 플랫폼의 강력한 기능과 역량을 최대한 활용하고 궁극적으로 사용자의 웹 환경을 개선하도록 돕는 것입니다.

지난 5월 Modern Web Guidance의 초기 프리뷰를 처음 선보인 이후 코딩 에이전트는 계속해서 개선되었으며, 수많은 놀라운 새로운 프런티어 모델이 출시되었습니다. 가장 자주 받는 질문 중 하나는 이러한 새로운 프런티어 모델은 이미 웹 개발 권장사항을 따르고 있지 않나요?입니다.

안타깝게도 답은 '아니요'입니다. 테스트를 통해 '아니요'라는 답을 얻었습니다.

Modern Web Guidance의 각 가이드에는 가이드의 권장사항을 올바르게 따른 경우 충족해야 하는 일련의 기대치가 정의되어 있습니다. 그런 다음 평가 도구 모음은 Modern Web Guidance가 설치된 상태로 실행되는 에이전트(가이드가 제공되는 에이전트)와 Modern Web Guidance가 설치되지 않은 상태로 실행되는 에이전트(가이드가 제공되지 않는 에이전트)에 동일한 프롬프트를 제공하여 이러한 가이드를 테스트합니다. 두 실행의 출력이 가이드에 정의된 기대치와 비교하여 확인됩니다.

가이드가 있는 경우와 없는 경우의 통과율을 비교해 보면 AI 모델(최신 프런티어 모델 포함)이 최신 웹 개발 권장사항을 자체적으로 안정적으로 따르지 않는다는 것을 알 수 있습니다.

안내를 받지 않는 코딩 에이전트는 평가 도구 모음의 검사 중 평균 약 50% 만 통과하는 반면, Modern Web Guidance를 사용하는 경우 약 90% 를 통과합니다.

최첨단 코딩 에이전트의 평균 평가 검사 통과율을 비교하는 가로 막대 그래프(2026년 11월 9일 기준): Modern Web Guidance를 사용하지 않은 경우 52%, Modern Web Guidance를 사용한 경우 91% 입니다.

평가 대시보드에서 각 가이드에 대해 실행되는 구체적인 검사 전체 목록과 각 모델의 점수를 확인할 수 있습니다.

하지만 이 게시물에서는 웹 개발자가 일상 업무에서 가장 많이 접하는 접근성, 사용자 경험, 성능, 보안과 같은 주요 분야에서 AI 모델이 지속적으로 잘못 처리하는 가장 문제가 되는 10가지 영역을 개략적으로 살펴봅니다.

1. 접근성: 키보드 포커스 및 inert 관리

키보드나 스크린 리더를 사용하여 탐색하는 사용자의 경우 예측 가능한 포커스 순서가 있어야 대화형 UI를 사용할 수 있습니다. 그렇지 않으면 포커스가 숨겨진 백그라운드 콘텐츠로 이동하거나 수십 개의 추가 탭 정지에 갇힐 수 있습니다.

포커스 및 inert 관리는 Google의 평가에서 가장 큰 접근성 격차 중 하나이며 Modern Web Guidance를 통해 가장 많이 개선되는 영역 중 하나입니다. 가장 흔한 실수는 다음과 같습니다.

  • 새로 열린 뷰로 포커스를 이동하지 못하고 뒤로 이동할 때 트리거 요소로 포커스를 복원하는 것을 잊어버립니다.
  • 모달 <dialog>을 사용하거나 배경 콘텐츠를 inert로 표시하는 대신 맞춤 모달에서 수동 JavaScript 탭 트랩과 aria-modal="true"에 의존합니다.
  • 로빙 tabindex가 있는 단일 탭 정지를 사용하는 대신 툴바 또는 방향 그리드의 모든 항목을 별도의 탭 정지로 노출합니다.
  • 인접한 배경과 3:1 대비율이 부족한 맞춤 :focus-visible 윤곽선을 만듭니다.

2. 접근성: 모션 감소 지원

예상치 못한 페이지 움직임과 대규모 애니메이션은 주의를 산만하게 할 뿐만 아니라 전정 장애나 움직임 민감성이 있는 사용자에게 현기증, 메스꺼움, 두통을 유발할 수 있습니다.

AI 모델은 일반적으로 표준 CSS 전환 또는 키프레임 애니메이션을 추가할 때 prefers-reduced-motion를 확인해야 한다는 점을 기억합니다. 하지만 부드러운 스크롤, 뷰 전환, 스크롤 기반 애니메이션과 같은 최신 기능을 사용할 때는 모션 감소를 잊어버리거나 구현할 때 실수를 하는 경우가 많습니다.

  • 루트 요소에 전역적으로 무조건 scroll-behavior:smooth 설정
  • 모션 감소 검사 없이 스크롤 기반 CSS 애니메이션을 사용합니다.
  • 캐러셀 항목에서 transition 지속 시간을 삭제하여 스크롤 중에 움직임을 줄였지만 변환 속성도 삭제하는 것을 잊어버려 스크롤 중에 항목 크기가 갑자기 커집니다.
  • 움직임 줄이기가 활성화되면 *, *::before, *::after에 일괄적으로 0.01ms 지속 시간 재설정이 적용되어 필수적인 비움직임 전환(UI 상태 변경을 전달하는 데 필요)도 모두 삭제됩니다.
  • 움직임 줄이기가 활성화된 경우 @view-transition 규칙을 움직임 줄이기 검사 뒤에 게이팅하는 대신 교차 문서 뷰 전환을 무조건 사용 설정하고 ::view-transition-* 애니메이션을 재정의합니다.

3. 접근성: 양식 유효성 검사, 형식 힌트, 자동 완성 상태

양식은 웹에서 가장 중요한 사용자 여정의 초석입니다. 결제 양식의 사소한 문제가 비즈니스의 수익에 큰 영향을 미칠 수 있습니다.

양식을 작성할 때 AI 모델은 많은 기본 사항을 올바르게 처리합니다. 하지만 유효성 검사 오류, 자리표시자 및 형식 힌트의 적절한 배치, 비밀번호 관리자 및 자동 완성 기능과의 올바른 통합과 같은 작지만 중요한 세부정보에서 실패하는 경우가 많습니다.

  • 커스텀 JavaScript blur 유효성 검사로 CSS :user-invalid를 다시 구현합니다. 이 경우 사용자가 탭을 지나치자마자 수정되지 않은 required 필드에 플래그가 지정되는 경우가 많습니다.
  • aria-invalid가 눈에 보이는 오류와 동기화되지 않도록 하거나, blur에서 aria-invalid="true"를 조기에 설정하거나, JavaScript에서 aria-invalid를 동기화하지 않고 CSS에서 :user-invalid의 스타일을 지정합니다.
  • aria-describedby와 연결된 영구 힌트를 렌더링하는 대신 사라지는 placeholder 텍스트를 사용하여 필수 형식(예: 카드 만료 입력의 MM/YY)을 표시합니다.
  • 형식 힌트와 비밀번호 규칙을 <input> 위가 아닌 아래에 배치하여 사용자가 읽어야 할 때 모바일 키보드와 자동 완성 팝오버로 가려질 수 있습니다.
  • 레이아웃을 변경하지 않는 비색상 신호(예: 인셋 box-shadow)와 색상을 페어링하는 대신 테두리 색상만 변경하여 :autofill 입력을 강조 표시합니다.

4. 사용자 환경: 스와이프 및 터치 동작

에이전트는 명령형 JavaScript를 사용하여 터치 동작을 구현하고 자체 맞춤 물리 로직을 작성하는 데 능숙합니다. 이러한 구현은 작동하지만 JavaScript 기반 터치 상호작용은 사용자가 네이티브 앱에서 기대하는 만큼 원활하게 느껴지지 않는 경우가 많습니다.

이러한 상호작용 중 상당수는 CSS 및 네이티브 스크롤 API를 사용하여 구현할 수 있으므로 브라우저가 기본 스레드 외부에서 동작 물리학을 실행할 수 있습니다. 하지만 Google의 평가에서 AI 모델은 다음과 같은 기능을 간과하는 경우가 많습니다.

  • CSS 스크롤 스냅을 사용하는 대신 pointermove 또는 touchmove 리스너를 사용하여 스와이프하여 닫기 또는 스와이프하여 표시 동작을 구현합니다.
  • 탭 스와이프 인터페이스에서 overscroll-behavior-x:none를 생략하면 오버 스와이프 중에 실수로 브라우저 뒤로 또는 앞으로 탐색이 트리거될 수 있습니다.
  • 스크롤 기반 애니메이션을 사용하는 대신 JavaScript 콜백의 기본 스레드에서 스와이프 진행률 효과를 구동합니다.
  • 스크롤 기반 스와이프 작업 패널에서 키보드 포커스를 제대로 지원하지 않습니다.

5. 사용자 환경: 대화상자, 팝오버, 최상위 레이어 전환

Google의 평가에서 모델은 모달 대화상자와 팝오버 주변의 스크롤을 애니메이션화하거나, 닫거나, 포함할 때 명령형 JavaScript 해결 방법으로 대체하는 경우가 많습니다.

에이전트는 최상위 레이어 오버레이를 위해 빌드된 최신 HTML 및 CSS 기능을 사용하는 대신 유지보수가 더 어렵고 미묘한 UI 버그가 발생하기 쉬운 진입 및 종료 상태 머신, 기록 후크, 본문 스크롤 잠금을 직접 구현하는 경우가 많습니다.

이러한 상호작용을 올바르게 구현하면 불안정한 환경을 사용자가 신뢰할 수 있는 세련된 환경으로 바꿀 수 있습니다. 가장 흔히 발생하는 실수는 다음과 같습니다.

  • popover를 사용하는 대신 가벼운 닫기를 위해 수동 클릭 외부 핸들러를 사용하여 맞춤 position:fixed 오버레이를 빌드합니다.
  • @starting-style 및 transition-behavior를 사용하여 순수 CSS에서 실행하는 대신 CSS 전환 클래스를 전환하고 requestAnimationFrame() 및 transitionend 리스너(setTimeout() 대체 포함)와 조정하여 진입 및 종료 애니메이션을 만듭니다.
  • <dialog> 요소를 사용하여 오버스크롤을 포함하는 대신 <html>에서 overflow:hidden를 설정하여 열린 모달 뒤의 페이지 스크롤을 잠급니다.
  • 모달 <dialog> 요소의 기본 뒤로 버튼 닫기 기능을 사용하는 대신 Android 뒤로 동작을 감지하고 열려 있는 모달을 닫는 popstate 리스너를 추가합니다.

6. 사용자 환경: 어두운 모드 및 테마 적응형 컨트롤

사용자는 눈의 피로와 눈부심을 줄이기 위해 어두운 모드를 사용 설정하므로, 페이지 로드 시 밝은 흰색 플래시, 테마가 지정되지 않은 양식 컨트롤 또는 OS 환경설정과 동기화되지 않는 전환 버튼은 즉시 환경을 저해합니다.

어두운 모드 전환은 간단해 보이지만, 평가에서 가이드가 없는 모델은 테마 전환과 기본 양식 컨트롤을 올바르게 구현하는 데 필요한 주요 플랫폼 세부정보를 지속적으로 놓칩니다.

  • <head>에 <meta name="color-scheme" content="light dark">가 누락되어 어두운 모드 사용자에게 외부 스타일시트가 로드되는 동안 흰색 캔버스가 깜박입니다.
  • color-scheme를 설정하지 않고 맞춤 색상으로 어두운 테마 또는 어두운 표면 구성요소의 스타일을 지정하여 네이티브 스크롤바와 양식 컨트롤이 밝은 모드에 고정됩니다.
  • 활성 color-scheme를 자동으로 따르는 테마 적응형 색상 토큰을 정의할 때 CSS light-dark() 함수를 간과합니다.
  • localStorage에서 명시적 재정의를 설정하는 바이너리 밝은 모드 또는 어두운 모드 전환만 제공하고 OS 기본값으로 돌아가는 옵션은 제공하지 않습니다.
  • storage 이벤트 리스너를 추가하는 것을 잊어버려 사용자가 테마를 변경할 때 다른 열려 있는 탭이 동기화되지 않습니다.

7. 성능: 런타임 응답성 및 원활성

사용자는 웹페이지가 로드된 후 대부분의 시간을 웹페이지에서 보내므로 상호작용이 느리고 전환이 불안정하면 초기 로드가 느린 것만큼 사용자 환경에 부정적인 영향을 미칠 수 있습니다.

평가에서 모델은 일반적으로 Lighthouse와 같은 도구에서 수년 동안 홍보해 온 기존의 로드 시간 권장사항을 따르며, 일반적으로 명백하게 무거운 루프를 웹 작업자에 오프로드하는 방법을 알고 있습니다. 개발자가 지속적으로 어려움을 겪는 부분은 INP를 최적화하는 데 필수적인 기본 스레드 응답성입니다. 즉, 레이아웃 작업을 격리하고, 우선순위에 따라 작업을 예약하고, 기본 스레드 외부에서 렌더링 작업을 실행하는 새로운 HTML 및 CSS 기능을 활용하는 것입니다.

예를 들어 상담사가 다음과 같은 행동을 하는 경우가 많습니다.

  • 브라우저에서 지원하는 경우 scheduler.postTask()를 사용하여 사용자에게 표시되는 작업의 우선순위를 지정하거나 scheduler.yield()를 사용하여 작업을 계속하는 대신 백그라운드 또는 우선순위가 낮은 작업을 위해 커스텀 setTimeout() 큐를 롤링합니다.
  • content-visibility:auto 및 contain-intrinsic-size를 적용하지 않고 복잡한 레이아웃(예: 밀도가 높은 데이터 그리드)을 빌드합니다. 즉, 사용자 상호작용 후 업데이트가 전체 페이지 리플로우를 트리거합니다.
  • content-visibility:hidden(inert와 페어링됨)를 사용하는 대신 display:none를 사용하여 비활성 SPA 뷰를 숨기거나 해체합니다. 전자는 캐시된 렌더링 상태를 소멸시키므로 다시 전환하면 전체 레이아웃과 페인트가 트리거됩니다.
  • contentvisibilityautostatechange 이벤트와 content-visibility:auto를 페어링하는 대신 Intersection Observer 콜백을 사용하여 화면 밖 <canvas> 애니메이션 및 백그라운드 루프를 일시중지하면(가능한 경우) 요소가 뷰로 스크롤되기 전에 브라우저의 사전 렌더링 여백이 누락됩니다.
  • 가능한 경우 CSS 스크롤 상태 쿼리를 사용하는 대신(Intersection Observer 대체 사용) 연속 레이아웃 계산을 강제하는 scroll 이벤트 리스너를 사용하여 스크롤 어포던스 UI 힌트를 빌드합니다.

8. 성능: 레이아웃 변경 및 시각적 깜박임

예기치 않은 레이아웃 변경은 독자의 집중을 방해하고 사용자가 예기치 않은 버튼을 클릭하게 만들 수 있으며, 때로는 되돌릴 수 없는 결과를 초래할 수 있습니다.

모델은 레이아웃 변경을 방지하기 위해 표준 <img> 요소에 width 및 height 속성을 안정적으로 추가하지만, <video> 요소, 웹 글꼴 스왑, 모바일 브라우저 툴바, 클라이언트 측 실험에서 발생하는 다른 일반적인 레이아웃 변경 및 사전 페인트 플래시 소스를 지속적으로 놓칩니다. 이 모든 것이 CLS에 영향을 미칠 수 있습니다.

  • <video> 요소에서 명시적인 width 및 height 속성을 생략하거나(로드되지 않은 플레이어는 기본적으로 300×150로 설정되고 메타데이터가 로드되면 레이아웃이 변경됨) 미디어 요소에 직접 aspect-ratio 및 object-fit를 설정하는 대신 미디어 래퍼 요소의 스타일을 지정합니다.
  • font-size-adjust 없이 웹 글꼴을 시스템 대체 글꼴과 페어링하여 웹 글꼴이 교체될 때 단락과 제목이 눈에 띄게 재배치됩니다.
  • 동적 또는 작은 표시 영역 단위(dvh, svh) 대신 정적 vh 단위를 사용하여 히어로 섹션과 전체 화면 레이아웃의 크기를 조정하면 모바일 브라우저 툴바가 확장 및 축소될 때 콘텐츠가 점프합니다.
  • 가능한 경우 blocking="render"가 포함된 스크립트를 사용하는 대신 기존 opacity:0 또는 visibility:hidden 깜박임 방지 스니펫과 클라이언트 측 A/B 테스트를 위한 다초 시간 제한을 사용하여 콘텐츠를 숨깁니다(LCP를 지연시키고 보이지 않는 요소를 클릭 가능한 상태로 유지).
  • rel="expect" 및 blocking="render"를 사용하여 첫 번째 렌더링을 유지하는 대신 중요한 스크롤 없이 볼 수 있는 부분 마크업이 파싱되기 전에 교차 문서 뷰 전환을 트리거하여 전환 중간에 절반만 파싱된 요소가 표시됩니다.

9. 보안: DOM XSS 방어 및 HTML 삭제

교차 사이트 스크립팅(XSS) 결함이 하나만 있어도 공격자가 사용자의 브라우저에서 임의의 코드를 실행하여 세션 데이터를 도용하거나 사용자를 대신하여 작업을 수행할 수 있습니다. 이것이 바로 안전한 HTML 살균, 신뢰할 수 있는 유형, 엄격한 CSP와 같은 브라우저 적용 방어가 중요한 이유입니다.

모델이 이러한 보안 문제를 사전에 해결하는 경우는 드뭅니다. 예를 들어 전체 평가 제품군의 모든 작업에서 모델은 명시적인 프롬프트 없이 콘텐츠 보안 정책(CSP) 또는 교차 출처 격리 헤더를 추가하지 않았습니다.

프롬프트가 보안을 직접 요청하는 경우에도 가이드가 없는 모델은 보안 검사의 절반 이상을 통과하지 못하며, 취약한 맞춤 대체 또는 불완전한 정책에 의존합니다. 이는 DOM XSS를 방어할 때 명확하게 드러납니다.

  • 네이티브 정제를 사용할 수 없는 경우 DOMPurify와 같은 검증된 라이브러리를 사용하는 대신 setHTML() 대체로 또는 신뢰할 수 있는 유형 정책 내에서 직접 작성한 DOMParser 허용 목록 정제기를 작성합니다.
  • 지원되는 경우 네이티브 setHTML()를 사용하고 DOMPurify를 대체로만 지연 로드하는 대신 페이지 로드마다 DOMPurify를 적극적으로 번들링합니다.
  • 싱크 적용을 약화시키는 허용적인 (input) => input 패스 스루 신뢰할 수 있는 유형 정책을 등록합니다.
  • SHA-256 해시로 인라인 스크립트를 잠그는 대신 정적 페이지의 신뢰할 수 있는 유형을 호스트 허용 목록 script-src 'self' CSP와 페어링합니다.
  • nonce 기반 script-src 콘텐츠 보안 정책을 구성할 때 'strict-dynamic' 및 'report-sample' 생략

10. 보안: 패스키 로그인, 재인증, 사용자 인증 정보 수명 주기

패스키는 사용자의 보안을 개선하고 비즈니스의 로그인 전환율을 높이지만, 에이전트의 도움을 받더라도 구현하기가 어렵습니다.

WebAuthn에는 특정 직렬화, 기능 감지, 신호 API가 있는데, 가이드가 없는 모델은 평가에서 이를 거의 완전히 놓칩니다. AI 모델에 패스키 로그인, 관리 또는 단계적 재인증을 추가하도록 요청하면 수동 바이트 인코딩 도우미를 작성하거나, 관리 UI에서 기능 확인을 건너뛰거나, 사용자 인증 정보를 비밀번호 관리자와 동기화하지 않는 경우가 많습니다.

  • parseCreationOptionsFromJSON(), parseRequestOptionsFromJSON(), credential.toJSON()와 같은 기본 제공 직렬화 메서드를 사용하는 대신 atob() 및 btoa() Base64URL 변환 도우미를 기본 경로 또는 중복 대체 상용구로 직접 작성합니다.
  • 서버에서 알 수 없는 사용자 인증 정보 ID를 보고할 때 로그인 중에 PublicKeyCredential.signalUnknownCredential()를 생략하여 삭제되거나 취소된 패스키를 사용자의 비밀번호 관리자에 남겨둡니다.
  • 패스키 생성 및 관리 UI를 렌더링하기 전에 PublicKeyCredential.getClientCapabilities()을(를) 건너뜁니다.
  • 승격 패스키 재인증 요청을 시작할 때 AbortController 및 AbortSignal를 생략합니다.
  • 간소화된 로그인 메시지를 위해 uiMode:"immediate" 대신 오리진 트라이얼 mediation:"immediate" 옵션을 전달합니다.

격차 해소

이 10가지 영역은 전체 목록이 아니며, 시간이 지남에 따라 구체적인 지식 격차가 달라질 것으로 예상됩니다. 모델이 따라잡고 웹 플랫폼은 계속 발전할 것입니다.

다행히 Modern Web Guidance는 이러한 모든 격차를 해소하며 플랫폼과 함께 계속 발전할 것입니다.

Modern Web Guidance를 시작하려면 다음 명령어를 실행하거나 에이전트의 플러그인 마켓플레이스에서 검색하세요.

npx modern-web-guidance@latest install

모델이 다른 웹 개발 작업을 잘못 수행하는 것을 본 적이 있나요? Google에 알려주세요. Modern Web Guidance GitHub 저장소에서 문제를 제기하거나 프로젝트에 기여하여 다른 개발자를 위해 이러한 격차를 해소하는 데 도움을 주세요.