본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 21. 11:42

AI가 제품의 기반 인프라(Product Plumbing)가 될 때 중요해지는 프론트엔드 기술

요약

AI가 제품의 핵심 인프라가 됨에 따라 프론트엔드의 역할이 단순한 렌더링을 넘어 불확실한 시스템을 중재하는 운영적 성격으로 변화하고 있습니다. 엔지니어는 채팅 UI 같은 외형적 요소보다 비동기 UX, 상태 모델링, 스트리밍, 실패 복구와 같은 복잡한 워크플로우 오케스트레이션 기술에 집중해야 합니다.

핵심 포인트

  • AI 제품의 프론트엔드는 확률적이고 중단 가능한 시스템을 관리하는 중재자 역할을 수행함
  • 단순한 UI 디자인보다 비동기 UX, 상태 모델링, 접근성, 실패 복구 기술이 더욱 중요해짐
  • 모델 엔드포인트는 재료일 뿐이며, 프론트엔드는 이를 사용자가 신뢰할 수 있는 결과물로 변환하는 계층임
  • UI 엔지니어는 분산 시스템을 다루는 시스템 엔지니어와 같은 사고방식을 가져야 함

AI가 등장했다고 해서 프론트엔드 작업의 중요성이 줄어드는 것은 아닙니다. 오히려 더 운영적인(operational) 성격이 강해지고 있습니다. 과거의 직무가 주로 애플리케이션 상태(application state)를 명확하게 렌더링하고 사용자를 결정론적 워크플로(deterministic workflows)를 통해 이동시키는 것에 집중했다면, 새로운 버전의 직무는 여전히 이를 포함하면서도 이제는 느리고, 확률적이며(probabilistic), 중단 가능하고, 때로는 틀릴 수도 있는 인간과 시스템 사이를 중재해야 합니다. 이는 어떤 기술이 여전히 중요한지를 변화시킵니다.

어디에 투자할지 결정하려는 풀스택 엔지니어(full stack engineer)라면, 저의 조언은 직설적입니다. AI 특화 UI 크롬(UI chrome)에 집착하기 전에 비동기 UX(async UX), 상태 모델링(state modeling), 폼(forms), 그리고 접근성(accessibility)에 배로 투자하십시오. AI 제품에서 가장 어려운 프론트엔드 문제는 채팅 버블(chat bubbles)이 아닙니다. 스트리밍(streaming), 재시도(retries), 구조화된 출력(structured output), 승인(approvals), 그리고 실패 복구(failure recovery)를 둘러싼 제품의 경계 문제입니다. 이것이 프론트엔드 컨퍼런스 강연들이 변하고 있는 이유입니다. 유익한 강연들은 디자인 시스템 연극(design-system theatre)에서 벗어나 더 어려운 질문으로 향하고 있습니다. 즉, 백엔드(backend)가 생각하는 동안 어떻게 일관성을 유지하는 인터페이스를 구축할 것인가 하는 문제입니다.

이제 프론트엔드는 AI가 제품이 되는 지점입니다. 모델 엔드포인트(model endpoint)는 제품이 아닙니다. 그것은 재료(ingredient)일 뿐입니다. 프론트엔드는 그 재료를 사용자가 신뢰할 수 있는 무언가로 바꾸는 계층입니다. 이는 프론트엔드가 이제 단순한 프레젠테이션(presentation) 이상의 것을 책임진다는 것을 의미합니다. 프론트엔드는 속도 조절(pacing), 신뢰도(confidence), 중단(interruption), 정보 공개(disclosure), 그리고 초안(draft)과 확정된 결과(committed result) 사이의 차이를 관리합니다. 이전의 앱 형태에서는 많은 화면을 유휴(idle), 로딩(loading), 성공(success), 에러(error)와 같은 소수의 상태로 설명할 수 있었습니다. 하지만 AI 기능은 이를 완전히 확장시켜 버립니다.

이제 현실적인 인터페이스는 다음과 같은 상태들을 다뤄야 합니다: 백그라운드 검색 (retrieval)이 이미 실행되는 동안 사용자가 여전히 프롬프트를 편집 중인 상태, 모델이 응답을 시작했지만 도구 실행 (tool execution)이 아직 진행 중인 상태, 구조화된 객체 (structured object)의 일부가 스트리밍되었지만 필수 필드가 아직 누락된 상태, 백엔드에서 양식을 수락했지만 생성된 콘텐츠가 아직 승인되지 않은 상태, 낙관적 UI (optimistic UI)가 이미 진행된 후 인간의 개입 (human override)이 발생한 상태, 부수 효과 (side effects)를 중복시키지 않으면서 의도를 보존해야 하는 재시도 (retry) 상태 등입니다. 이것은 단순히 "프론트엔드 + AI"가 아닙니다. 그것은 불확실성 속에서의 워크플로우 오케스트레이션 (workflow orchestration)입니다. 이것이 바로 제가 현재 많은 프론트엔드 조언들이 진부하게 느껴진다고 생각하는 이유입니다. 기존의 조언들은 여전히 인터페이스가 대부분 권위 있는 (authoritative) 백엔드 상태를 읽어온다고 가정합니다. AI 제품에서 인터페이스는 종종 잠정적이고, 부분적이며, 아직 신뢰할 수 없는 상태를 나타내야 합니다. 실질적인 함의는 UI 엔지니어가 시스템 엔지니어처럼 생각해야 한다는 것입니다. 분산 시스템 (distributed systems)에 대한 박사 학위가 필요한 것은 아니지만, 이벤트 순서 지정 (event sequencing), 변이 경계 (mutation boundaries), 취소 (cancellation), 백프레셔 (backpressure), 그리고 사용자가 어느 순간에 정확히 무엇을 믿어도 되는지에 대해 신경 써야 합니다. 만약 어떤 컨퍼런스 발표가 여전히 프론트엔드를 얇은 렌더링 셸 (rendering shell)로 취급한다면, 그것은 이미 뒤처진 것입니다. 상태 모델링 (State modeling)은 이제 데모와 제품을 구분 짓는 기술입니다. 대부분의 AI 인터페이스는 모델을 사용할 수 없어서 실패하는 것이 아닙니다. 상태 모델 (state model)이 게을러서 실패하는 것입니다. 데모 버전은 쉽습니다: 프롬프트를 보내고, 문자열에 토큰을 추가하고, 스피너를 보여주고, 답변을 렌더링하면 됩니다. 제품 버전은 더 어렵습니다. UI가 그 "추한 중간 단계 (ugly middle)"를 견뎌내야 하기 때문입니다. 그 추한 중간 단계가 바로 실제 제품의 동작이 살아있는 곳입니다. 스트림을 커지는 문자열이 아닌 이벤트로 모델링하세요. 만약 당신의 상태 형태 (state shape)가 단순히 시간이 지남에 따라 어시스턴트 메시지가 길어지는 messages[] 형태라면, 당신은 나중에 필요하게 될 구조를 버리고 있는 것입니다. 당신은 델타 (deltas), 도구 활동 (tool activity), 중재 플래그 (moderation flags), 인용 (citations), 그리고 최종 결과 (terminal outcomes)를 별도로 나타낼 수 있는 이벤트 기반 상태 모델 (event-driven state model)을 원해야 합니다.

import { useReducer } from 'react'; type AssistantEvent = | { type : 'response_started' ; id : string } | { type : 'text_delta' ; id : string ; chunk : string } | { type : 'tool_started' ; id : string ; tool : string } | { type : 'tool_result' ; id : string ; tool : string ; output : string } | { type : 'structured_patch' ; id : string ; patch : Record < string , unknown > } | { type : 'response_completed' ; id : string } | { type : 'response_failed' ; id : string ; error : string }; type AssistantState = { id : string ; text : string ; status : 'pending' | 'streaming' | 'complete' | 'failed' ; tools : Array < { name : string ; status : 'running' | 'done' ; output ?: string } > ; object : Record < string , unknown > ; error ?: string ; }; function reduceAssistant ( state : AssistantState , event : AssistantEvent ): AssistantState { switch ( event . type ) { case 'response_started' : return { ... state , id : event . id , status : 'streaming' }; case 'text_delta' : return { ... state , text : state . text + event . chunk , status : 'streaming' }; case 'tool_started' : return { ... state , tools : [... state . tools , { name : event . tool , status : 'running' }], }; case 'tool_result' : return { ... state , tools : state . tools . map (( tool ) => tool . name === event . tool ? { ... tool , status : 'done' , output : event . output } : tool ), }; case 'structured_patch' : return { ... state , object : { ... state . object , ... event . patch }, }; case 'response_completed' : return { ... state , status : 'complete' }; case 'response_failed' : return { ... state , status : 'failed' , error : event . error }; } } 이 패턴은 Vercel AI SDK, 일반 SSE(Server-Sent Events), 또는 Laravel Reverb와 같은 WebSocket 계층을 사용하든 중요합니다. 전송 방식이 아니라 아키텍처가 중요한 것입니다. 이벤트 모델이 핵심입니다.

임시 상태(Provisional state)와 확정 상태(Committed state)의 분리
많은 AI UX가 혼란을 겪는 이유는 인터페이스가 생성된 출력물을 마치 이미 저장된 기록인 것처럼 취급하기 때문입니다. 이는 실수입니다. 생성된 출력물은 대개 제안 상태(Proposal state)입니다. 데이터베이스 쓰기는 확정 상태(Committed state)입니다. 도구 호출(Tool call) 결과는 보조 상태(Supporting state)일 수 있습니다. UI에서 이들을 하나로 평탄화(Flatten)해버리면, 사용자는 실제로 어떤 일이 일어났는지 파악할 수 없게 됩니다. 훌륭한 AI 프론트엔드는 이러한 구분을 명확하게 만듭니다:

  • 초안은 여전히 편집 가능함
  • 답변은 여전히 스트리밍(Streaming) 중임
  • 인용(Citation)은 아직 해결되지 않음
  • 작업은 대기 중이지만 실행되지는 않음
  • 최종 기록은 저장되었고 버전 관리됨
    이는 첫 번째로는 제품 신뢰(Product trust)의 문제이며, 두 번째로는 프론트엔드의 문제입니다. 하지만 그 신뢰가 유지되느냐 사라지느냐가 결정되는 곳은 바로 프론트엔드입니다.

취소(Cancellation)는 있으면 좋은 기능이 아닙니다.
만약 당신의 UI가 오래 걸리는 생성 작업을 시작할 수는 있지만 이를 깔끔하게 취소할 수 없다면, 당신은 값비싼 짜증 유발 기계를 출시하고 있는 것입니다. 취소는 비용, 지연 시간(Latency), 그리고 사용자 신뢰를 위해 중요합니다. 또한 취소 기능은 상태 설계(State design)에 규율을 강제합니다. 취소 기능을 추가하는 순간, 어떤 상태를 롤백(Rollback)할지, 어떤 상태를 유지할지, 그리고 부분적인 출력(Partial output)을 어떻게 표현해야 할지를 결정해야 합니다. 이는 건강한 압박입니다. 이는 대개 당신의 비동기(Async) 모델이 실질적인 것이었는지 아니면 단순히 겉치레였는지를 드러내 줍니다.

스트리밍 UX는 프론트엔드 옷을 입고 있는 인프라 작업입니다.
스트리밍은 많은 팀이 자신의 프론트엔드 스택이 라이브 워크플로(Live workflows)가 아니라 페이지 전환(Page transitions)에 최적화되어 있었다는 사실을 깨닫게 되는 지점입니다. 스트리밍의 얕은 버전은 타자기 효과(Typewriter effect)입니다. 유용한 버전은 시간을 흡수할 수 있는 UI입니다. 진지한 AI 제품 인터페이스는 응답이 도착하는 동안 다음과 같은 질문에 답할 수 있어야 합니다:

  • 사용자가 인접한 필드를 계속 채울 수 있는가?
  • 부분적으로 스트리밍된 콘텐츠를 벌써 편집할 수 있어야 하는가?
  • 도구 호출(Tool call)이 중간에 답변의 방향을 바꾸면 어떻게 되는가?
  • 소스 검색(Source retrieval) 상태를 답변 생성과 별도로 보여줘야 하는가?
  • 일부 부수 효과(Side effects)가 이미 완료되었다면 "재시도(Retry)"는 무엇을 의미하는가?

이것들은 인터랙션 디자인 (Interaction design) 문제인 동시에, 상태 (State) 및 전송 (Transport) 문제이기도 합니다. 워크플로우에 부합하는 가장 단순한 전송 방식을 선택하세요. 많은 팀이 너무 일찍 과도하게 구축하는 경향이 있습니다. 만약 여러분의 인터랙션이 일방향 모델 출력과 가끔 발생하는 상태 업데이트로 구성된다면, 서버 전송 이벤트 (Server-Sent Events, SSE)만으로도 충분한 경우가 많습니다. SSE는 단순하고, 추론하기에 캐시 친화적이며, 일반적인 HTTP 인프라를 통해 디버깅하기가 더 쉽습니다. WebSockets는 협업 에이전트 워크스페이스, 여러 서비스로부터의 실시간 도구 스트림, 풍부한 커서 또는 프레즌스 (Presence) 의미론, 혹은 지속적인 명령 채널과 같이 진정으로 양방향 세션 동작이 필요할 때 비로소 비용을 들일 가치가 있습니다. 많은 CRUD-plus-AI 제품의 경우, 전송 계층의 단계는 다음과 같아야 합니다: 짧고 결정론적인 동작을 위해 요청-응답 (Request-response) 방식으로 시작하세요. 사용자가 점진적인 피드백을 필요로 할 때 SSE를 추가하세요. 인터랙션이 진정으로 세션 형태를 띨 때만 WebSockets를 추가하세요. 이 순서는 지루하게 들릴 수 있지만, 바로 그 점 때문에 대개 정답입니다.

스트리밍은 단순한 움직임이 아니라 구조를 드러내야 합니다
팀들은 스트림이 이해 가능한지 여부는 무시한 채, 토큰 (Tokens)이 빠르게 나타나게 만드는 데만 집착할 때가 있습니다. 사용자는 움직임의 느낌보다는 시스템이 현재 수행 중인 작업을 이해할 수 있는지에 더 관심이 많습니다. 강력한 스트리밍 UI는 기저의 워크플로우를 읽기 쉽게 만듭니다: “문서 검색 중”은 “답변 생성 중”과 다릅니다. “결제 도구 호출 중”은 “요약 작성 중”과 다릅니다. “응답 초안 작성 중”은 “저장 준비 완료”와 다릅니다. 즉, 프론트엔드는 단순히 텍스트를 스트리밍해서는 안 됩니다. 의미 있는 단계 (Phases)를 스트리밍해야 합니다. 많은 현대적 AI API와 SDK는 가공되지 않은 토큰보다 더 풍부한 이벤트 스트림을 노출할 수 있습니다. 그것을 활용하세요. 타자기 효과 (Typewriter effect)가 제품이 아닙니다. 상태 전이 (State transitions)가 제품입니다.

의도가 중요하기 때문에 폼 (Forms)은 여전히 중요합니다
AI 제품 디자인에서 가장 혼란스러운 견해 중 하나는 폼이 사라질 것이라는 주장입니다. 그렇지 않습니다. 많은 경우, 폼은 더욱 중요해지고 있습니다. AI는 모호함을 증가시키지만, 폼은 모호함을 줄여줍니다.

훌륭한 폼(Form)은 사용자에게 무엇을 원하는지, 어떤 제약 조건(Constraints)이 중요한지, 어떤 필드가 필수적인지, 그리고 어떤 절충안(Tradeoffs)이 허용 가능한지를 시스템에 알려줍니다. 백엔드(Backend)가 생성(Generating), 추론(Inferring), 또는 결정(Deciding)을 수행할 때 이러한 폼의 가치는 더욱 높아집니다. 단순히 데이터를 수집하는 용도가 아니라, 의도(Intent)를 고정하는 닻(Anchor)으로 폼을 사용하세요. AI 보조 워크플로우(AI-assisted workflows)에서 폼은 상호작용 중 명시적으로 유지되어야 하는 부분들을 포착해야 합니다:

  • 작업 목표 (Task objective)
  • 허용된 도구 또는 데이터 소스 (Allowed tools or data sources)
  • 승인 요구 사항 (Approval requirements)
  • 출력 형식 (Output format)
  • 모델이 임의로 변경해서는 안 되는 엄격한 제약 조건 (Hard constraints the model must not improvise around)

이는 단순히 "몇 가지 입력을 수집한다"는 역할보다 훨씬 강력한 역할입니다. 폼을 안전성(Safety)과 정확성(Correctness) 이야기의 일부로 만드는 것입니다. React에서는 useFormStatus와 같은 프리미티브(Primitives)가 유용합니다. 왜냐하면 대기 상태(Pending state)가 전체 트리(Tree)에 영향을 미치는 대신 제출 경계(Submission boundary) 근처에 머물 수 있게 해주기 때문입니다.

import { useFormStatus } from 'react-dom';

function GenerateButton() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? ' Generating draft...' : ' Generate draft '}
    </button>
  );
}

export function ContentBriefForm({ action }: { action: (data: FormData) => Promise<void> }) {
  return (
    <form action={action} className="space-y-4">
      <textarea name="brief" required placeholder="What should the model produce?" />
      <select name="tone" defaultValue="direct">
        <option value="direct">Direct</option>
        <option value="formal">Formal</option>
        <option value="playful">Playful</option>
      </select>
      <label>
        <input type="checkbox" name="allow_web_search" /> Allow external research
      </label>
      <GenerateButton />
    </form>
  );
}

이는 Laravel 및 PHP 팀에게 더욱 중요합니다. 왜냐하면 이들 중 상당수는 내구성이 있는 비즈니스 워크플로우(Business workflow)가 여전히 서버에 위치하는 제품을 구축하고 있기 때문입니다. 그러한 환경에서는 AI 보조 기능 아래에 지루하지만 신뢰할 수 있는 폼 경로(Form path)를 보존하는 것이 현명합니다. AI가 구성을 돕고, 요약하고, 분류하거나 초안을 작성하게 하세요. 하지만 AI가 명시적인 제출 경계(Submission boundary)를 지워버리게 두어서는 안 됩니다.

백엔드 변이 모델(Backend mutation model)이 프론트엔드 폼 모델(Frontend form model)을 형성해야 합니다. 많은 팀이 바로 이 지점에서 문제를 겪습니다. 그들은 AI 기능이 풍부한 클라이언트 흐름을 먼저 구축한 뒤, 나중에야 백엔드가 미리보기(Preview), 저장(Save), 승인(Approve), 게시(Publish), 재시도(Retry)를 안전하게 구분할 수 있는지 자문합니다. 이는 순서가 뒤바뀐 것입니다. 백엔드 변이 모델이 깔끔하다면 프론트엔드는 정상적인 상태를 유지할 수 있습니다. 만약 백엔드가 모든 것을 모호한 “생성(Generate)” 엔드포인트 하나로 뭉뚱그려 놓는다면, 프론트엔드는 이를 보완하기 위해 지저분한 로컬 예외 처리(Local exceptions)를 쌓아 나가게 될 것입니다. 저의 관점은 단순합니다. 워크플로우 동사(Workflow verbs)를 명시적으로 만드세요. “초안 생성(Generate draft)”, “답변 승인(Approve answer)”, “수정 사항 저장(Save revision)”, “결과 게시(Publish result)”는 단순히 버튼 라벨만 다른 동일한 작업처럼 느껴져서는 안 됩니다.

AI UI가 끊임없이 변하기 때문에 접근성(Accessibility)이 더 어려워졌습니다. AI 제품에서의 접근성은 마지막 QA 단계가 아닙니다. 그것은 핵심적인 상호작용 디자인 제약 사항(Interaction design constraint)입니다. 전통적인 프론트엔드 접근성 작업은 이미 키보드 흐름(Keyboard flow), 라벨(Labels), 대비(Contrast), 시맨틱(Semantics)을 고려해 왔습니다. AI 인터페이스는 새로운 유형의 실패를 추가합니다. 사용자가 내용을 이해하려고 노력하는 동안 화면이 계속해서 변하는 것입니다. 의도적으로 설계하지 않는다면 이는 위험합니다. 스트리밍(Streaming)은 쉽게 적대적인 환경이 될 수 있습니다. 미숙한 스트리밍 구현은 보조 기술(Assistive technology)을 압도할 수 있습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0