AI 코드 생성 도구가 빠르게 발전하면서 많은 프론트엔드 작업이 자동화되고 있습니다. 그런데 현장에서 실제로 구현해보면 이상한 감각이 생깁니다. 툴바 하나 붙이는 건 쉽게 뽑혔는데, 선택 영역에 따라 정확히 위치가 바뀌는 플로팅 UI는 자꾸 어긋나고, 백만 행 데이터를 XLSX로 내보내다가 브라우저가 멈추고, AI가 생성한 Vue 컴포넌트는 클릭해도 숫자가 바뀌지 않습니다. 공통점이 있습니다. 모두 상태와 흐름이 복잡하게 얽힌 인터랙션 레이어이고, AI 도구가 반복 패턴 수준으로 학습하기 어려운 영역입니다.
에디터: 조립은 쉽고, 디테일은 어렵다
velog의 Lexical 에디터 구현 사례를 보면 Meta가 만든 Lexical의 철학이 명확합니다. '미리 완성된 에디터'가 아니라 노드와 플러그인을 조합해 서비스 맞춤형 편집기를 만드는 구조입니다. LexicalComposer에 theme와 nodes를 선언하고, RichTextPlugin, HistoryPlugin, ImagePlugin 같은 플러그인을 조합하면 초기 구조는 비교적 빠르게 잡힙니다. 여기까지는 AI도 꽤 잘 생성합니다.
문제는 그 다음입니다. SelectionFloatingToolbar처럼 텍스트를 선택한 순간에만 등장하는 플로팅 UI를 제대로 구현하려면, SELECTION_CHANGE_COMMAND를 구독해 selection 상태를 읽고, getBoundingClientRect()로 화면 좌표를 계산하고, 뷰포트 경계에서 잘리지 않도록 좌우·상하 위치를 보정하는 로직이 필요합니다. 선택 영역 위쪽 공간이 부족하면 아래로 내리고, 화면 끝에 붙으면 마진을 확보해 안쪽으로 밀어내는 식입니다. 이 보정 하나가 있고 없고의 차이가 실제 사용감을 완전히 갈라놓습니다. AI가 생성한 코드에서 이 보정이 자연스럽게 포함되는 경우는 드뭅니다.
ImageNode와 ImagePlugin의 분리도 마찬가지입니다. 이미지를 '어떻게 저장하고 불러올지'의 규칙은 노드가, '언제 어떻게 삽입할지'의 흐름은 플러그인이 담당하도록 책임을 나누는 설계는, AI에게 "이미지 기능 추가해줘"라고 요청해서 나오는 코드와 구조적으로 다릅니다. Markdown export를 위해 노드 타입별로 직접 변환 규칙을 작성하는 것도 마찬가지입니다. Velog용, GitHub README용, 내부 CMS용 출력 규칙은 각각 다르고, 그 선택은 서비스 맥락을 이해하는 사람만 내릴 수 있습니다.
대용량 렌더링: 메모리 압박은 설계 문제다
dev.to의 DuckDB-WASM XLSX 내보내기 사례는 더 극적입니다. SheetJS와 ExcelJS는 소규모 데이터셋엔 잘 동작했지만, 1백만 행에서 진행률이 95%에 도달한 뒤 브라우저가 약 20초간 멈췄습니다. 변환이 끝나도 브라우저가 느려진 채로 유지되거나 완전히 크래시되기도 했습니다.
돌파구는 발상의 전환에서 나왔습니다. XLSX 파일이 실제로는 ZIP + XML 구조라는 사실, 그리고 DuckDB가 문자열 연산에 극도로 빠르다는 사실을 연결했습니다. 기존 흐름이 DuckDB → JS 객체 → 라이브러리 → XML → ZIP이었다면, 새 흐름은 DuckDB → SQL로 직접 XML 문자열 생성 → JSZip → ZIP입니다. 중간 JS 객체 변환을 통째로 건너뛰면서 GC 대상 객체가 급감했고, 브라우저 프리즈의 원인이었던 GC 일시정지가 사라졌습니다. 결과는 ExcelJS 대비 메모리 사용량 대폭 감소, 변환 후 브라우저 멈춤 현상 소멸이었습니다.
이 해결책은 AI가 "XLSX 내보내기 구현해줘"라고 요청했을 때 나오는 답변과는 거리가 멉니다. AI는 이미 알려진 라이브러리 사용 패턴을 조합합니다. 그러나 '왜 그 라이브러리가 이 규모에서 무너지는지', '파일 포맷의 내부 구조를 우회할 수 있는지'를 추론하고 실험하는 과정은 사람이 직접 설계한 것입니다. 성능 병목을 정확히 진단하고 아키텍처 수준에서 해법을 찾는 능력은, 현재 AI 도구의 범주 밖에 있습니다.
접근성과 반응성: AI가 가장 자주 놓치는 곳
dev.to의 Vue.js AI 한계 분석은 더 일상적인 수준의 함정을 다룹니다. AI가 Vue 컴포넌트를 생성할 때 반복적으로 저지르는 실수 다섯 가지 중, 특히 주목할 것은 반응성 시스템 오해와 접근성 처리입니다.
Vue의 Proxy 기반 반응성 시스템에서 ref 대신 let count = 0으로 상태를 선언하거나, 스토어에서 const cart = store.state.cart처럼 평평하게 복사하면 Vue는 변경을 추적하지 못합니다. toRef를 써야 cart.value의 변경이 감지됩니다. AI는 이런 뉘앙스를 자주 놓칩니다. nextTick 없이 DOM을 즉시 참조하거나, methods 블록에 화살표 함수를 써서 this 바인딩을 끊어버리는 패턴도 마찬가지입니다. 코드는 완성된 것처럼 보이지만, 클릭해도 숫자가 안 바뀌는 조용한 버그가 생깁니다.
접근성 문제는 더 심각합니다. AI는 div role="button" 같은 부적절한 ARIA 조합을 자주 사용하고, header, nav, main 같이 이미 시맨틱 의미를 가진 HTML5 요소에 불필요한 role을 덧붙이는 실수를 반복합니다. W3C가 불필요하다고 명시한 조합을 AI가 생성한다는 것은, 접근성 기준이 AI의 훈련 데이터에서 노이즈로 희석되어 있다는 신호이기도 합니다. 스크린 리더 사용자의 실제 경험은 AI가 생성한 코드를 그대로 배포했을 때 보장되지 않습니다.
시사점: AI가 빠를수록, 설계권은 사람이 쥐어야 한다
세 가지 사례를 관통하는 메시지는 단순합니다. AI는 알려진 패턴을 빠르게 조합하는 데 강하지만, 상태 흐름이 복잡하게 얽히거나, 성능 병목이 아키텍처 수준의 결정을 요구하거나, 사용자 경험의 디테일이 맥락 의존적일 때 일관되게 한계를 드러냅니다.
Lexical의 플로팅 툴바 위치 보정, DuckDB SQL로 XML을 직접 생성하는 역발상, Vue 컴포넌트의 반응성 참조 설계—이 세 가지는 모두 '어떻게 구현하는가'가 아니라 '왜 이렇게 설계해야 하는가'를 아는 사람만 만들 수 있는 결과물입니다. AI 도구가 초안을 뽑아주는 속도가 빨라질수록, 그 초안의 어디가 틀렸는지를 정확히 짚는 감각이 오히려 더 희소하고 더 가치 있어집니다.
전망: AI 워크플로우에서 프론트엔드 설계자의 역할
앞으로 AI 도구는 더 정교해지겠지만, 이 구조적 한계가 단기간에 해소될 것으로 보기는 어렵습니다. 에디터의 마이크로 인터랙션, 대용량 데이터의 렌더링 전략, 접근성 시맨틱 설계는 모두 사용자의 실제 경험과 시스템의 내부 동작 원리를 동시에 이해해야 하는 영역입니다. 이는 패턴 매칭이 아닌 인과 추론의 영역입니다.
결국 AI 코딩 도구를 가장 잘 활용하는 프론트엔드 개발자는, AI가 생성할 수 있는 코드와 없는 코드의 경계를 빠르게 파악하고—AI에게 초안을 맡기되, 인터랙션 레이어의 설계 결정권은 끝까지 자신이 쥐는 사람일 것입니다. 빠른 프로토타이핑이 가능해진 시대일수록, 그 프로토타입의 완성도를 결정하는 마지막 판단은 여전히 사람의 몫입니다.