Topcoat is pushing the boundary of server applications with Rust
Topcoat, Rust 서버 애플리케이션의 경계를 넓히다
Tokio 팀이 Rust 풀스택 프레임워크 Topcoat 0.9와 ORM Toasty의 새 기능을 소개합니다. 서버 렌더링을 기본으로 두면서 브라우저 반응성, 서버 푸시, 타입 안전한 데이터베이스 작업을 지원하는 설계와 구현을 설명합니다.
- 주제
AI 요약
Tokio 팀이 Rust 풀스택 웹 프레임워크 Topcoat의 0.9 버전을 공개하고, 데이터베이스 클라이언트 Toasty의 업데이트를 소개합니다. 글쓴이는 Ruby on Rails 코어 팀에서 일한 경험을 바탕으로, Rust에서도 보일러플레이트를 줄이고 빠르게 애플리케이션을 만들 수 있는 프레임워크를 지향한다고 설명합니다. AI 기반 개발에서도 관례와 추상화가 코드 생성의 속도와 오류 감소에 도움을 줄 수 있다는 점을 배터리 포함(batteries-included) 프레임워크를 만드는 이유로 듭니다.
서버 렌더링에 브라우저 반응성 더하기
Topcoat은 서버 사이드 렌더링(SSR)을 웹 앱의 기본값으로 삼되, 필요한 부분은 브라우저에서 즉시 반응하도록 설계합니다. view! 매크로 안에서 사용하는 런타임 표현식은 타입 검사를 거친 Rust 문법 일부를 JavaScript로 변환합니다. 예를 들어 버튼 클릭으로 카운터를 바꾸는 작업은 서버 왕복 없이 브라우저에서 처리합니다.
마크업 구조까지 바꿔야 하는 경우에는 shard를 사용합니다. 브라우저의 시그널 값이 바뀌면 해당 UI 조각을 서버에서 다시 렌더링합니다. 검색어가 바뀔 때 서버에서 데이터베이스를 조회해 결과 목록을 갱신하는 식입니다. Topcoat 0.8부터는 서버 렌더링 과정에서 시그널을 읽을 수 있으며, 그 값에 의존하는 부분만 다시 가져옵니다. 갱신된 HTML은 기존 요소를 모핑해 입력 포커스나 입력 상태를 유지합니다.
스트리밍 UI와 서버 푸시
live!와 emit! 매크로는 서버에서 UI 업데이트를 연속으로 보냅니다. 데이터 로딩 전에는 로딩 표시를 먼저 내보내고, 로딩이 끝나면 실제 콘텐츠로 바꾸는 스트리밍 SSR을 구현할 수 있습니다. 데이터를 나눠 읽으며 진행률을 여러 차례 갱신하는 사례도 제시합니다.
Topcoat 0.9은 초기 페이지 로딩뿐 아니라 WebSocket을 이용한 서버 푸시도 지원합니다. 채팅 예제에서는 서버의 채팅 상태를 렌더링한 뒤 변경 알림을 기다립니다. 연결이 유지되는 동안 새 변경 사항이 생기면 채팅 UI를 다시 렌더링합니다.
Toasty의 데이터베이스 기능
Toasty는 Rust 프로시저 매크로를 활용해 데이터 조회와 변경 코드의 보일러플레이트를 줄입니다. 새 update! 매크로는 레코드를 먼저 읽지 않고 데이터베이스에서 필드를 갱신합니다. 예제에서는 이름을 바꾸면서 login_count를 SQL의 login_count = login_count + 1에 해당하는 방식으로 증가시킵니다.
문서형 데이터도 지원합니다. #[document] 필드에 구조체를 지정하면 PostgreSQL에서는 해당 값을 JSONB로 저장하고, 중첩 필드인 settings.theme를 조건으로 조회할 수 있습니다. 글에는 이 조건이 users.settings->>'theme' = $1로 변환되는 SQL도 실렸습니다.
다형 관계는 enum과 일반 관계를 조합해 표현합니다. #[shared(id)]는 enum 변형마다 있는 ID를 같은 데이터베이스 컬럼에 연결하고, #[index(id)]는 해당 컬럼에 인덱스를 지정합니다. 예제에서는 프로젝트 소유자를 사용자나 팀으로 저장하고, 소유권 조회와 이전을 처리합니다.
프로젝트가 제시하는 방향
글쓴이는 Rust가 모든 개발자의 최종 선택이 될지는 모르지만, 적은 메모리를 쓰면서 빠르고 완성도 높은 애플리케이션을 만드는 데 Rust를 계속 활용하겠다고 말합니다. Topcoat은 Rails식 생산성을 Rust에 가져오려는 시도이며, SSR과 클라이언트 상호작용을 섞는 방식, Toasty의 ORM 접근이 프로젝트의 주요 설계 선택입니다.
Reddit 반응
- @u/greyblake — “Rust가 다른 현대 언어만큼 우아하지 않다고 먼저 말하겠지만, 그래도 Rust로 일하는 편이 눈에 산을 붓는 것보다는 낫다”는 말이 DHH를 가리키나요? 하하. 추신: definitely에 오타가 있네요.
- @u/carllerche — 네, 그 말은 그를 가리킨 게 맞습니다. definitely요. 감사합니다. 맞춤법 검사기를 포함해 AI를 전혀 쓰지 않았습니다. :)
- @u/JustBadPlaya — Rust에 Rails식 개발 경험을 가져오려는 시도는 반갑습니다. 하지만 Topcoat이 Rust의 장점에서 너무 멀어지는 것 같기도 합니다. 라우팅 속성은 귀엽지만, 디렉터리 구조를 이용한 암묵적 라우팅은 어색합니다. 컨텍스트 전달이 쉬운 점은 좋지만 타입이 없는 방식은 Axum의 State API보다 크게 후퇴한 느낌입니다. ORM도 SQLx를 쓸 수 있는 언어에서 개인적으로 선호하지 않아 아직 확신이 없습니다. 그래도 시도 자체는 정말 멋집니다.
- @u/PikachuIsBoss — 모듈 기반 라우팅은 선택 사항입니다. 원한다면 경로를 직접 지정할 수 있습니다. 타입이 없는 컨텍스트에 대한 우려도 이해합니다. 개선 방법을 고민하고 있지만, 처음에는 사용 편의성에 집중하려 했습니다.
- @u/crusoe — Rust는 컴파일이 느린 언어인데 런타임 오류까지 생긴다면 사용 편의성이 좋다고 보기 어렵습니다.
- @u/pokemonplayer2001 — 좋은 릴리스 같네요. ORM의 매력은 영원히 이해하지 못할 것 같습니다. 🤷
- @u/EmperorOfCanada — 대규모 팀이 ORM을 큰 기술 부채로 떠안고 고생하는 모습을 봤습니다. 제가 본 사례에서는 직접 SQL을 작성한다고 개발이 막히지 않았습니다. 시스템 곳곳에 SQL 호출이 수천 개 있어도 단위 테스트와 통합 테스트를 잘 만들면 문제가 있는 쿼리를 찾아낼 수 있습니다. 또 인상 깊었던 설계 방식은 SQL을 테이블을 섞고 데이터를 처리하는 만능 도구처럼 쓰지 않는 것이었습니다. 데이터베이스가 잘하는 일에만 DB를 쓰고, 결과를 메모리로 가져와 C++이나 Rust의 속도로 처리합니다. 빠른 자료구조를 활용하면 데이터베이스보다 빠르게 동작하고, DB에서는 너무 느려 불가능한 기능도 만들 수 있습니다. 이런 구조에서는 SQL이 대체로 단순합니다. ORM 옹호자들이 피할 수 있다고 말하는 복잡한 SQL은 어떤 경우에도 피해야 할 쿼리라고 생각합니다.
- @u/standard_revolution — 저도 SQL을 직접 쓰는 편이지만, MVP를 빨리 만들고 SQL에 익숙하지 않다면 ORM을 찾는 이유는 이해합니다. SQL 문법은 여러 면에서 낯설고, 꽤 경직돼 있으며 오류 메시지도 Rust와 비교하면 좋지 않을 때가 있습니다. AI 덕분에 깨진 쿼리를 LLM에 넣고 쉽게 피드백을 받는 식으로 이런 점이 어느 정도 나아졌습니다. 데이터베이스를 제대로 배운 적이 없어 DB가 무엇을 할 수 있는지 모르는 사람도 많습니다. 언젠가 SQL로 컴파일되는, 조금 더 쓰기 편한 쿼리 언어를 만들고 싶습니다. 다만 ORM을 쓰면 SQL에 익숙해지지 않는다는 점은 있습니다.
- @u/carllerche — 사람마다 우선순위가 다릅니다. 앱을 만들 때 제가 가장 신경 쓰고 싶지 않은 건 SQL입니다.
- @u/pokemonplayer2001 — 네, ORM은 제 취향이 아닐 뿐입니다.
- @u/maria_la_guerta — ORM은 특정 개인을 위한 게 아니라 큰 팀을 위한 것입니다.
- @u/Upstairs-Attitude610 — Gleam과 Lustre 조합과 비슷할지 궁금합니다. JavaScript 번들도 더 작으면 좋겠습니다. 기억이 맞다면 Lustre 번들은 아주 나쁘진 않지만, 아주 작은 번들이라면 멋질 것 같습니다.
- @u/PikachuIsBoss — Lustre 번들이 얼마나 큰가요? 써본 적은 없습니다. Topcoat 런타임은 압축 전 34KB입니다. 아직 번들 크기를 줄이는 작업을 하지 않아 개선 여지가 많을 것 같습니다.
- @u/crusoe — Rust 코드에 HTML을 넣으면 개발이 매우 느려지고 취약해지는 점이 정말 싫습니다.
- @u/carllerche — 이해합니다. 처음에는 저도 어색했습니다. 하지만 뷰를 동시 렌더링용으로 컴파일하거나
live!같은 기능을 만드는 등 흥미로운 일도 가능합니다.
- @u/carllerche — 이해합니다. 처음에는 저도 어색했습니다. 하지만 뷰를 동시 렌더링용으로 컴파일하거나
- @u/anxxa — 취미 프로젝트를 Leptos로 만들었고 Topcoat이 나온 직후 옮기려 했습니다. 요즘 제 업무 분야가 저수준에 가까워 사이트 대부분을 AI 에이전트로 만들었습니다. 이식 이유는 호환성이 더 넓어지길 바라서였습니다. 보안 업무를 하는 저와 동료들은 Lockdown Mode를 켜서 WASM을 차단하는 경우가 있기 때문입니다. 제가 API를 직접 다루지 않아서 경험담이 큰 의미는 없겠지만, 처음에는 몇 가지 제한을 겪었습니다. 에이전트에 정리를 부탁했지만 LLM이 만들어낸 헛소리일 수도 있어, 조치나 답변을 기대하지는 않습니다. WASM을 쓰지 않는 대안과 다른 설계 방향을 제공하려는 작업은 고맙게 생각하고 진행 상황을 지켜보겠습니다. JavaScript로 컴파일하는 점은 꽤 멋지고 프로젝트가 성숙하는 모습도 기대합니다.
원문: Tokio Blog / 번역·요약: Trawling