OpenUI, 생성형 UI를 JSON 대신 언어로 다루는 방법

포비·2026년 3월 30일

알아보자

목록 보기
86/118


LLM에게 답변만 맡기는 시대는 이미 지나가고 있다. 이제는 카드, 차트, 폼, 테이블 같은 UI 자체를 모델이 구성하게 만들려는 시도가 많다. 문제는 여기서부터다. 보통 JSON이나 JSON Patch 계열 포맷으로 구조를 넘기게 되는데, 길고 장황해지고 스트리밍 중간 상태를 다루기 까다로워진다.

thesysdev/openui는 이 지점을 정면으로 건드린다. 이 프로젝트는 생성형 UI를 "JSON을 잘 짜는 문제"로 보기보다, 아예 스트리밍을 전제로 한 언어인 OpenUI Lang으로 다룬다. 그리고 그 언어를 React에서 바로 렌더링할 수 있는 런타임, 채팅 UI, CLI까지 하나로 묶는다.

OpenUI가 정확히 뭘 하는 프로젝트인가

OpenUI는 공식적으로 OpenUI Lang, React runtime, built-in component libraries, ready-to-use chat interfaces, CLI를 포함한 full-stack Generative UI framework라고 소개한다.

핵심은 모델이 임의 HTML을 뱉게 두지 않는다는 점이다. 앱이 먼저 어떤 컴포넌트를 허용할지를 라이브러리로 정의하고, 그 라이브러리로부터 시스템 프롬프트를 생성한다. 그러면 모델은 허용된 컴포넌트 규약 안에서 OpenUI Lang을 생성하고, 클라이언트의 Renderer가 그 결과를 실제 React UI로 바꿔준다.

즉 흐름은 이렇다.

  1. 컴포넌트 라이브러리를 정의한다.
  2. 그 라이브러리에서 시스템 프롬프트를 생성한다.
  3. 모델이 OpenUI Lang을 스트리밍한다.
  4. Renderer가 이를 점진적으로 렌더링한다.

이 구조가 좋은 이유는 간단하다. 모델이 무엇을 만들 수 있는지 계약이 먼저 있고, 렌더링은 내 컴포넌트가 맡기 때문이다.

OpenUI의 핵심 구조는 의외로 단순하다

OpenUI 문서를 보면 이 프레임워크는 크게 네 덩어리로 이해할 수 있다.

  • Library: Zod 스키마와 React renderer로 정의한 컴포넌트 집합
  • Prompt Generator: 그 라이브러리를 시스템 프롬프트로 바꾸는 도구
  • Parser: OpenUI Lang을 스트리밍 친화적으로 파싱하는 레이어
  • Renderer: 파싱된 결과를 실제 React UI로 렌더링하는 컴포넌트

여기서 중요한 건 라이브러리가 단순한 UI 모음이 아니라는 점이다. 이 라이브러리는 곧 모델이 쓸 수 있는 UI의 계약서다. 어떤 컴포넌트를 허용할지, props는 무엇인지, 어떤 구조가 가능한지를 여기서 정한다.

왜 OpenUI가 흥미로운가

1. 생성형 UI를 "포맷"이 아니라 "언어"로 본다

OpenUI Lang은 line-oriented DSL이다. 예시를 보면 느낌이 바로 온다.

root = Stack([title, tbl])
title = TextContent("Employees (Sample)", "large-heavy")
tbl = Table(cols, rows)

JSON으로 같은 구조를 표현하는 것보다 훨씬 짧고, 문서에서도 이 특성을 streaming-first, token-efficient라는 말로 강조한다. 공식 README와 Benchmarks 페이지는 OpenUI Lang이 JSON 계열 표현보다 최대 67% 적은 토큰을 쓸 수 있다고 설명한다.

생성형 UI에서 이건 꽤 중요한 포인트다. UI가 복잡해질수록 모델이 구조를 표현하는 비용도 함께 커지기 때문이다.

2. 프롬프트를 사람이 손으로만 쓰지 않는다

OpenUI의 인상적인 부분은 컴포넌트 라이브러리에서 시스템 프롬프트를 생성한다는 점이다.

보통 이런 류의 시스템은 결국 프롬프트 유지보수가 지옥이 되기 쉽다. 어떤 컴포넌트를 쓸 수 있는지, props는 뭔지, 언제 어떤 패턴을 써야 하는지를 사람이 계속 설명해야 하기 때문이다.

반면 OpenUI는 defineComponent, createLibrary, Zod 스키마를 기반으로 컴포넌트 계약을 만들고, 여기서 시스템 프롬프트를 생성한다. 이 방식은 "허용된 컴포넌트 집합"과 "모델이 따라야 할 규칙"을 같은 소스에서 관리하게 만든다.

이게 별거 아닌 것처럼 보여도, 생성형 UI에서 가장 귀찮은 부분을 꽤 많이 줄여준다.

3. 스트리밍 경험을 진짜로 신경 쓴다

OpenUI 문서를 보면 <Renderer />는 응답을 한 번에 다 받은 뒤 그리는 방식이 아니다. 줄 단위로 도착하는 OpenUI Lang을 실시간으로 파싱하고 렌더링한다. 공식 소개 문서도 이 과정을 "structure first, then data fills in progressively"라고 설명한다.

이 프로젝트가 흥미로운 이유는 여기서 끝나지 않는다. 공식 엔지니어링 블로그에는 openui-lang parser를 Rust/WASM에서 TypeScript로 다시 작성하면서 per-call 성능 2.2배에서 4.6배, 전체 스트림 누적 파싱 비용 2.6배에서 3.3배 개선했다고 정리한 글도 있다.

생성형 UI는 멋진 데모 하나 만드는 것보다, 스트리밍 중간 상태를 얼마나 자연스럽게 처리하느냐가 훨씬 중요하다. 그런 면에서 OpenUI는 꽤 현실적인 문제를 보고 있는 프로젝트로 느껴진다.

4. 기본 UI가 있고, 디자인 시스템도 바꿔 끼울 수 있다

OpenUI는 완전히 로우레벨 라이브러리만 제공하지 않는다. 문서상 @openuidev/react-ui 패키지에는 Copilot, FullScreen, BottomTray 같은 채팅 레이아웃과 기본 컴포넌트 라이브러리가 들어 있다.

동시에 shadcn/ui 예제도 따로 제공한다. 이 예제의 요지는 분명하다. 모델은 OpenUI Lang이라는 추상 구조를 내보내고, 실제로 그 구조를 어떻게 렌더링할지는 클라이언트의 라이브러리가 결정한다는 것이다.

즉 OpenUI는 "우리 디자인 시스템을 버리고 새 프레임워크로 갈아타라"는 도구라기보다, 모델이 생성한 UI 구조와 내가 이미 가진 컴포넌트 체계를 연결하라는 쪽에 가깝다.

직접 써볼 때 중요한 포인트

여기서 놓치기 쉬운 부분이 하나 있다. 프론트에서 componentLibrary만 넘긴다고 바로 생성형 UI가 되는 건 아니다.

OpenUI 문서도 이 점을 분명히 짚는다. 프론트에는 렌더링용 componentLibrary가 필요하고, 백엔드에는 모델이 무엇을 출력해야 하는지 알려주는 generated system prompt가 필요하다. 둘 중 하나라도 빠지면 모델이 일반 텍스트로 답하거나, 화면이 렌더링하지 못하는 출력을 만들 수 있다.

이건 OpenUI를 이해할 때 중요한 포인트다. 결국 이 프레임워크의 핵심은 예쁜 컴포넌트 묶음이 아니라, 프론트의 렌더러와 백엔드의 프롬프트 생성이 한 세트로 맞물려야 한다는 점이다.

또 하나는 현재 문서와 빠른 시작 흐름이 꽤 분명하게 React/Next.js 중심이라는 점이다. 빠르게 써보려면 이 선택지가 오히려 장점이지만, React 생태계 바깥이라면 도입 난이도는 조금 더 올라갈 수 있다.

인터랙션까지 고려한 구조라는 점도 눈에 띈다

생성형 UI가 정말 제품이 되려면 보기만 하는 카드에서 끝나면 안 된다. 폼이 있어야 하고, 버튼 클릭이 이어져야 하고, 후속 액션도 연결돼야 한다.

OpenUI 문서는 이 부분도 따로 다룬다. Renderer는 폼 상태를 자동으로 관리하고, onAction 콜백으로 구조화된 액션 이벤트를 전달할 수 있다. 즉 "모델이 UI를 만든다"에서 끝나는 게 아니라, 그 UI가 다시 사용자 입력과 액션으로 이어지는 흐름까지 설계돼 있다.

그래서 OpenUI는 단순한 렌더링 실험이라기보다, 생성형 인터페이스를 실제 상호작용 가능한 제품 화면으로 가져가려는 시도에 더 가깝다.

시작은 얼마나 빠를까

공식 Quick Start는 꽤 직관적이다.

npx @openuidev/cli@latest create --name genui-chat-app
cd genui-chat-app
echo "OPENAI_API_KEY=sk-your-key-here" > .env.local
npm run dev

생성된 앱은 대략 이런 구조를 갖는다.

src/
  app/
    page.tsx
    api/chat/route.ts
  library.ts
  generated/
    system-prompt.txt

여기서 중요한 건 system-prompt.txt다. 이 파일은 openui generate로 자동 생성되고, 서버가 모델 호출 시 이 프롬프트를 함께 붙인다. 프론트는 FullScreen 같은 레이아웃과 렌더링을 맡고, 백엔드는 시스템 프롬프트와 메시지를 합쳐 스트리밍 응답을 만든다.

즉 OpenUI는 단순히 "컴포넌트를 예쁘게 보여주는 UI 킷"이 아니라, 모델 출력 형식과 렌더링 파이프라인을 같이 설계하는 도구다.

누가 보면 특히 재미있을까

OpenUI는 이런 팀에게 특히 잘 맞아 보인다.

  • 텍스트 답변만으로는 부족하고 카드, 차트, 폼, 표 같은 구조화된 응답이 필요한 팀
  • 사내용 코파일럿, 데이터 대시보드, 리포트 UI, 워크플로우 자동화처럼 결과 화면이 계속 달라지는 제품을 만드는 팀
  • 이미 React 디자인 시스템이 있고, 그 위에 생성형 UI를 얹고 싶은 팀
  • JSON 스트리밍이나 커스텀 파서, 렌더러를 매번 직접 만들기 싫은 팀

반대로 텍스트형 챗봇 하나만 빠르게 붙이는 수준이라면 OpenUI는 다소 큰 선택일 수 있다. 이 프로젝트의 진짜 가치는 LLM 응답을 UI로 다루는 제품에서 나온다.

마무리

OpenUI를 보면서 가장 인상적이었던 건, 생성형 UI를 단순히 "모델이 JSON 잘 뱉게 만들기"로 보지 않았다는 점이다.

이 프로젝트는 더 근본적으로 묻는다.

  • 모델이 어떤 UI를 만들 수 있어야 하는가
  • 그 계약을 어디서 정의할 것인가
  • 스트리밍 중간 상태를 어떻게 안전하게 렌더링할 것인가
  • 기존 디자인 시스템과 어떻게 연결할 것인가

그리고 그 답을 OpenUI Lang, 컴포넌트 라이브러리, 시스템 프롬프트 생성, 스트리밍 Renderer라는 형태로 꽤 일관되게 풀어낸다.

생성형 UI 쪽을 보고 있다면, 이 레포는 단순한 데모 저장소라기보다 LLM이 UI를 만들 때 프로토콜을 어떻게 설계할 것인가를 고민하게 만드는 프로젝트에 가깝다.

참고 자료

profile
무엇이든 필요한 것을 합니다. https://mint-middle-1e5.notion.site/2b7655e8316980ad9422d96a6f3947de

0개의 댓글