프론트엔드 개발에서 AI는 이미 '쓸 것인가 말 것인가'의 문제가 아니다. 진짜 질문은 이것이다. AI가 만든 코드와 아티팩트를 어느 지점에서, 어떤 방식으로 사람이 받아서 책임지는가. 최근 dev.to에서 나온 세 편의 글—60Hz 실시간 스트리밍 버그를 Sentry와 Gemini로 해결한 의료 AI 사례, AI 에이전트가 생성한 HTML 프레젠테이션을 안전하게 배포하는 워크플로우, 그리고 순수 CSS만으로 음식 한 접시를 쌓아올린 챌린지 제출기—은 각각 독립된 주제처럼 보이지만, 묘하게 같은 방향을 가리킨다. 생성과 진단, 배포는 하나의 파이프라인이 아니라 분리된 책임 경계가 있어야 한다는 것.
프로덕션이 무너질 때, AI는 어떻게 써야 하는가
의료 AI 헬스 플랫폼에서 60Hz ECG 텔레메트리 스트리밍을 처리하던 중 프론트엔드가 완전히 쓰러졌다. CPU 사용률 98%, 힙 메모리 1.4GB, 프레임 레이트 6FPS. 원인은 놀랍도록 고전적인 React 실수였다. useEffect의 dependency array에 state setter가 참조하는 상태 배열 자체를 집어넣은 것이다. 새 데이터가 들어올 때마다 상태가 바뀌고, 상태가 바뀌면 이펙트가 재실행되고, 재실행되면 스트림에 다시 구독하는 무한 루프. 수천 개의 고아 이벤트 리스너가 쌓이면서 메모리는 수직 상승했다.
여기서 흥미로운 지점은 버그 자체가 아니라 진단 방식이다. Sentry Performance Tracing이 render_ecg_canvas 트랜잭션이 평균 842ms를 초과한다는 것을 잡아냈고, 64MB/sec을 넘는 Canvas 메모리 할당 경고를 브레드크럼으로 기록했다. 14,000건의 'React Maximum update depth exceeded' 예외가 단일 알럿으로 묶여 올라왔다. AI 도구가 코드를 짤 때는 이런 조용한 킬러를 심어놓기 쉽다—문법 오류가 아니라 런타임 패턴의 문제이기 때문이다. Sentry 같은 텔레메트리가 없었다면 무엇이 원인인지조차 모른 채 화면만 들여다봤을 것이다.
해결책은 React 렌더 사이클에서 고빈도 데이터를 완전히 분리하는 것이었다. useRef 버퍼에 데이터를 직접 쌓고 requestAnimationFrame으로 캔버스를 그리는 방식으로 바꾸자, CPU는 1.4%로, 메모리는 42MB로, 프레임 레이트는 60FPS로 돌아왔다. 동시에 Gemini 2.5 Flash와 responseSchema를 조합해 흉부 X선 분석 API의 Time-To-First-Token을 8,400ms에서 1,120ms로 줄이고, 할루시네이션을 구조적으로 차단했다. 이 사례가 주는 교훈은 명확하다. 고빈도 스트림은 React 렌더 사이클 밖에서 다뤄야 하고, AI API는 스키마로 계약을 강제해야 한다. 그리고 그 사실을 Sentry가 먼저 알려줬다.
AI가 만든 아티팩트, 배포는 별개의 스테이지다
Claude Code, Cursor, Codex 같은 에이전트가 HTML 프레젠테이션을 뚝딱 만들어내는 시대다. 그런데 생성이 끝난 다음 1분이 진짜 문제다. 'Slidesfly' 개발자가 dev.to에 공개한 워크플로우는 이 간극을 어떻게 설계할 것인가에 대한 실용적인 답이다. 핵심은 단순하다. 생성과 배포를 같은 에이전트 컨텍스트에서 처리하지 말라.
에이전트는 소스 파일인지 빌드 아웃풋인지 구분하지 못한다. src/index.html을 올려야 할 곳에 dist/index.html을 올리거나, localhost에서만 동작하는 스크립트가 포함된 껍데기를 게시할 수 있다. 이 글이 제안하는 네 단계 경계—생성 → 로컬 검증 → 정확한 경로 지정 배포 → 반환된 URL 직접 확인—는 기술적으로 단순하지만 설계적으로는 중요하다. 각 스테이지는 독립적으로 실패할 수 있어야 하고, 실패가 다음 스테이지를 자동으로 트리거해서는 안 된다. 배포 실패가 에이전트에게 "덱을 다시 만들어"라는 신호로 이어지는 순간, 루프가 시작된다.
특히 주목할 부분은 크리덴셜 경계다. API 키나 클레임 토큰이 절대로 대화 로그나 소스 컨트롤에 남아서는 안 된다는 원칙, 그리고 HTTP 업로드 성공이 곧 '동작하는 결과'를 의미하지 않는다는 검증 철학은, AI 워크플로우를 안전하게 운용하는 데 있어 코드 품질만큼 중요한 설계 요소다. 배포가 성공했다고 해서 검증 언어를 약화시키지 말라는 경고는 AI 생성 코드를 다루는 모든 팀에게 적용되는 원칙이다.
순수 CSS 챌린지가 가르쳐준 '아트 내부 단위 설계'
여기에 조금 다른 결의 사례를 붙여보자. dev.to 프론트엔드 챌린지에 제출된 퓨틴(poutine) CSS 아트 작품은, 이미지도 SVG도 캔버스도 없이 오직 CSS로 감자튀김 50개, 치즈 커드 13개, 그레이비 11패치를 쌓아올린 작업이다. 흥미로운 건 구현 자체가 아니라 작가가 솔직하게 공개한 세 가지 실수다.
첫째, vmin으로 모든 요소를 사이징했다가 아트워크가 고정 크기에서 멈춰도 요소들이 뷰포트를 따라 계속 커지는 문제를 겪었다. 해결책은 씬 자체의 엣지 길이를 --u: min(86vw, 78vh, 620px)로 정의하고 내부 단위를 --px: calc(var(--u) / 100)로 파생시키는 것이었다. 아트 내부의 단위는 윈도우가 아니라 아트 자체에 상대적이어야 한다. 이건 CSS 아트만의 이야기가 아니다. 컴포넌트 기반 UI에서 내부 요소의 크기 기준을 컨테이너에서 끊어내는 문제와 구조적으로 같다.
둘째, 그레이비를 하나의 큰 블롭으로 얹었더니 '뚜껑처럼' 보였다. 소스는 음식 위에 앉는 게 아니라 틈새에 고인다는 사실을 CSS로 표현하려면, 레이어를 세 단계로 나누고 mask-image로 각 패치의 경계를 페더링해야 했다. 셋째, 회전값이 위치와 상관관계를 가지면 눈이 패턴을 찾아낸다. 감자튀김이 중심에서 바깥으로 뻗는 방사형 대칭을 만들어버린 것이다. 무작위성은 코드가 아니라 상관관계를 끊는 의도적 설계에서 나온다.
세 사례가 가리키는 하나의 설계 원칙
표면적으로 이 세 이야기는 전혀 다르다. 의료 AI 플랫폼 성능 위기, AI 에이전트 배포 워크플로우, CSS 아트 챌린지. 그러나 공통된 구조가 있다. 각 스테이지를 명확하게 분리하고, 이전 스테이지의 실패가 다음 스테이지를 오염시키지 않도록 경계를 설계하는 것. React 렌더 사이클에서 스트리밍 버퍼를 분리한 것, 생성과 배포를 독립적인 단계로 나눈 것, 뷰포트 단위와 아트 내부 단위를 분리한 것—모두 같은 패턴이다.
AI가 코드 생성 속도를 비약적으로 높이는 지금, 오히려 '어디서 사람이 개입하고 어디서 기계에 맡기는가'의 경계 설계가 더 중요해지고 있다. AI가 만든 코드가 무엇을 놓치는지를 Sentry가 잡아주고, AI가 만든 아티팩트가 어느 경로로 배포되는지를 사람이 검증하고, AI가 제안한 CSS 구조에서 상관관계 버그는 렌더링해봐야만 보인다. 빠른 프로토타이핑은 AI에게, 진단과 검증의 설계는 여전히 사람에게—그 분업이 실전에서 품질을 가른다.