3P by GN⁺ | ★ favorite | 댓글 1개
  • Rwf는 Rust로 웹 애플리케이션을 만들기 위한 종합 프레임워크이며, 고전적인 MVC 패턴을 기반으로 빠르고 안전한 웹 앱 구축에 필요한 기능을 기본 제공함
  • 기본 기능에는 HTTP 서버, PostgreSQL 쿼리용 ORM, 동적 템플릿, 인증과 사용자 세션, 미들웨어, 백그라운드 작업과 예약 작업, 데이터베이스 마이그레이션이 포함됨
  • JSON 직렬화를 지원하는 내장 REST 프레임워크, WebSockets, 정적 파일 호스팅, 환경별 설정, 로깅과 메트릭, CLI도 제공함
  • Hotwired Turbo와 긴밀히 통합되어 백엔드 주도 SPA를 만들 수 있으며, Django/Flask 마이그레이션용 WSGI 서버와 Rails 마이그레이션용 Rack 서버를 제공함
  • 현재 베타 상태로 초기 도입자를 찾고 있으며, 대부분의 기능은 좋은 상태이고 문서는 사용할 수 있는 수준이라고 밝힘

Rust용 MVC 웹 프레임워크

  • Rwf는 Rust로 웹 애플리케이션을 구축하기 위한 종합 프레임워크임
  • 고전적인 MVC(model-view-controller) 패턴으로 작성됨
  • 빠르고 안전한 웹 앱을 쉽게 만들기 위해 필요한 기능을 기본 포함하는 것을 목표로 함
  • 문서는 공식 문서에서 제공됨

제공 기능

  • 서버와 요청 처리
  • 데이터와 애플리케이션 구조
    • PostgreSQL 쿼리를 쉽게 만들기 위한 사용자 친화적 ORM을 제공함
    • 데이터베이스 마이그레이션을 지원함
    • Dynamic templates를 제공함
    • Authentication과 내장 사용자 세션을 포함함
  • API와 작업 처리
  • 운영과 개발 도구
    • 환경별 설정을 지원함
    • 로깅과 메트릭을 제공함
    • CLI를 포함함

Hotwired Turbo와 마이그레이션 지원

  • Hotwired Turbo와 긴밀히 통합되어 backend-driven SPAs를 만들 수 있음
  • Django/Flask 앱에서 마이그레이션하기 위한 WSGI server를 제공함
  • Rails에서 마이그레이션하기 위한 Rack server를 제공함

빠른 시작

  • Rust 바이너리 애플리케이션을 만들고 rwf를 의존성에 추가해 사용함
cargo add rwf
  • 기본 앱은 rwf::prelude::*rwf::http::Server를 사용해 컨트롤러와 라우트를 정의함
use rwf::prelude::*;
use rwf::http::Server;

#[controller]
async fn index() -> Response {
    Response::new().html("<h1>Welcome to Rwf!</h1>")
}

#[tokio::main]
async fn main() {
    Server::new(vec![
        route!("/" => index),
    ])
    .launch()
    .await
    .unwrap();
}

예제와 프로젝트 상태

  • 일반적인 사용 사례는 examples에서 확인할 수 있음
  • Rwf는 현재 베타 상태이며 초기 도입자를 찾고 있음
  • 대부분의 기능은 좋은 상태이고 문서는 사용할 수 있는 수준이라고 밝힘
  • 기여는 환영되며, 가이드는 CONTRIBUTING, 코드 구조 안내는 ARCHITECTURE, 희망 기능 목록은 ROADMAP에 있음

댓글과 토론

Hacker News 의견들
  • 출시가 꽤 비현실적으로 느껴질 것 같고, 축하함. Rails로 웹 MVC를 오래 하면서 배운 가장 큰 점은 프레임워크가 모델 계층의 비즈니스 로직을 강하게 억제해야 한다는 것임
    데이터 모델에 Active Record가 말하는 콜백, 예를 들어 afterCreate 같은 훅은 넣지 않는 편이 좋다고 봄. 아직 ORM에 없지만 로드맵에 있다면 없는 편이 낫다고 생각함
    다만 이건 서비스, 즉 비즈니스 로직 계층을 강하게 권장할 때만 제대로 작동함. 내 Rails 앱들은 대부분 Interactor 같은 젬으로 커맨드/서비스 객체를 구성함
    MVC만으로, 따라서 Rails 기본 구성만으로는 서비스 계층 부재 때문에 운영 가능한 앱을 작성하기에 이상적이지 않다고 봄. 그리고 커뮤니티의 기존 ORM이나 쿼리 빌더를 활용하지 않은 이유도 궁금함. 아직 Rust는 한 줄도 써본 적 없지만, 점점 더 궁금해지고 있음

    • 이 관점이 어디서 나오는지 궁금함. 내 생각은 정반대로, 가능한 많은 비즈니스 로직은 모델에 있어야 함
      서비스는 거의 모두 모델에서 트리거되는 더 복잡한 특정 코드 조각이어야 한다고 봄. 얇은 컨트롤러, 두꺼운 모델이 디버깅·정리·발견 가능성 측면에서 가장 좋았고, 서비스를 많이 쓰면 경험상 스파게티 코드가 많이 생김
      순수 객체 지향 관점에서도 모델은 엔티티를 정의하는 기본 객체임. User는 자기 자신에 대해 알아야 할 것을 모두 알고, 다른 엔티티와는 메시지로 소통해야 함
      다만 콜백에는 동의함. 콜백은 이상한 부작용을 많이 만들고 디버깅을 정말 어렵게 함
    • 비즈니스 로직이 없으면 대체 모델이 뭔지 모르겠음. 구조체를 모델이라 부르고 절차를 서비스라 부르는 구성을 원하는 것처럼 들림
      그렇게 할 수는 있지만, 어떤 방식으로 엔티티가 갱신될 수 있는지 추론하기가 꽤 어려워짐. 아무 수의 절차가 갱신할 수 있고 각자 비즈니스 규칙을 따로 가질 수 있기 때문임. 그럴 거면 절차가 직접 데이터베이스에 쓰게 하고 모델을 없애도 됨
    • 모델에 비즈니스 로직이 없어야 한다는 데 동의함. Martin Fowler와 Robert C. Martin은 이런 것을 빈약한 도메인 모델이라고 부르고, 과거 객체 지향과 패턴이 무거운 엔터프라이즈 개발 방식과는 반대임
      하지만 20년 넘게 개발하면서 캡슐화는 기능이 아니라 부담이라는 결론에 이르렀고, 데이터는 그 데이터에 수행되는 동작과 분리되어야 한다고 봄. 이건 데이터 지향 설계 또는 데이터 지향 프로그래밍이라 불리고, 같은 결론에 도달한 사람이 나뿐만은 아님
    • MVC만으로 운영 수준 앱에 부족하다는 건 꽤 큰 주장임. 개인적으로는 서비스 객체를 싫어하는데, 여기저기 흩어져 발견 가능성을 해침
      잘 확장되는 다른 모델링 방식도 있고, 관련 글도 몇 개 있음. Basecamp 사람이 쓴 글로 https://dev.37signals.com/vanilla-rails-is-plenty/가 있음
      물론 이건 매우 객체 지향적이라 나도 크게 좋아하진 않음. 예를 들어 Elixir의 Phoenix 프레임워크는 관련 기능을 묶기 위한 컨텍스트를 쓰고, 간단히 말하면 파사드로 볼 수 있음
      서비스를 좋아하면 쓸 수는 있지만, MVC가 운영급에 충분하지 않다는 말은 조금 빗나갔다고 봄. 무거운 비즈니스 프로세스를 모델 콜백으로 처리하는 건 좋지 않지만, 데이터를 올바른 형태로 다듬는 작은 작업에는 꽤 괜찮음
    • 흥미로움. 회사에서 PHP ORM을 직접 만들었는데, Laravel 같은 오픈소스 라이브러리 사용이 금지되어 있었고 이 아주 유용하다고 느꼈음
      특히 내 프로그래밍 경험이 훅을 광범위하게 쓰는 Wordpress용 PHP에서 시작됐기 때문에 편향이 있을 수도 있음
      내가 만든 건 SQL 정의로 변환하거나 보기 좋게 포맷된 HTML 폼을 뱉을 수 있는 테이블 명세가 있음. 별도 컨트롤러가 모든 DB 연결과 CRUD 작업을 처리하고, 필요하면 다른 테이블과 제약을 쉽게 상호 참조할 수 있는 전후 훅이 있음
      꽤 잘 동작하지만, 가능하다면 즉시 Laravel로 갈아탈 것임
  • REST 프레임워크라면 기본적으로 Swagger/OpenAPI 정의 생성을 할 수 있을 거라고 기대함. FastAPI의 킬러 기능 중 하나가 그거라고 봄
    또 diesel.rs 같은 기존 ORM과 통합하지 않고 직접 ORM을 만든 이유, 그리고 인기 있는 기존 템플릿 엔진을 고르지 않고 자체 템플릿 언어를 만든 이유가 잘 이해되지 않음
    그 외에는 프로젝트가 정말 흥미로워 보이고 계속 지켜볼 생각임
    [0] https://diesel.rs/
    [1] https://crates.io/categories/template-engine

    • 몇 년 전 Diesel을 써봤는데 나한테는 너무 Rust스럽게 느껴졌음. 스키마를 두 번 정의해야 했고, 삽입과 조회에 같은 구조체를 쓸 수 없어서 사실상 세 번 정의하는 셈이었음
      전반적으로 데이터베이스 타입을 Rust 타입에 맞추는 좋은 첫 시도였지만, ORM은 더 유연하고 쓰기 편해야 함. 쿼리를 더 어렵게 하는 게 아니라 더 쉽게 써야 하니까 :)
      템플릿 쪽은 자기만의 언어를 만드는 게 30대 이상 너드 클럽 입문 의식에 가까움. 드래곤북은 읽지 않았지만 학교에서 그 수업을 꼭 들어보고 싶었음. 같은 것에도 항상 여러 구현이 있고, Rails의 ERB가 하는 일을 아주 가깝게 흉내 내는 이 구현을 쓰는 게 맞다고 느껴졌음
    • https://github.com/poem-web/poemSwagger 정의를 기본 제공하는 Rust 프레임워크임
    • 반대로 동작하는 비슷한 프레임워크를 아는지 궁금함. OpenAPI 정의를 진실의 원천으로 유지하고, 서버가 그걸 따르는지 검증하는 방식 말임
    • 서버에서 OpenAPI를 생성하는 건 100% 킬러 기능이라고 봄. 우리는 Dropshot으로 잘 쓰고 있음
    • 나도 REST 프레임워크라면 Swagger/OpenAPI 정의를 기본 생성할 거라 기대함. 다만 이해하기로는 이건 웹 API 구축용 프레임워크가 아니라 HTML을 반환하는 MVC 프레임워크
  • SRE 입장에서 https://levkk.github.io/rwf/migrating-from-python/에 관심이 갔음. 한편으로는 이걸 해낸 게 정말 멋지지만, 안정성을 보는 SRE 관점에서는 속으로 비명을 지르게 됨
    규모가 커지면 이건 Caddy, Nginx, Traefik 같은 리버스 프록시가 처리해야 함

    • 나도 같은 생각을 했지만, 이 방식이면 애플리케이션 엔지니어가 왔다 갔다 하지 않고 변경을 로컬에서 테스트할 수 있음. 결국 오래된 모놀리스 대 마이크로서비스 논쟁으로 돌아감
      안정적인 WSGI 서버를 작성하는 건 가능하고, 스레드 수나 vacuum, 예전 php-fpm처럼 n개 요청마다 재시작 같은 세부 사항에 조금만 주의하면 그리 어렵지 않음. 기본적으로 uwsgi가 가진 대부분의 옵션을 구현하면 올바른 길에 있는 셈임. Rwf를 Gunicorn 실행과 비교 가능하게 만드는 것이 로드맵에 있음
    • 작성자가 기술 독자라면 WSGI 서버 앞의 리버스 프록시 필요성을 굳이 말해주지 않아도 된다고 가정했을 것 같음
  • 이름 선택이 정말 대단함. 검색 결과 맨 위에 오르는 한 가지 방법이긴 함 :P
    진지하게는 잘 만들었음. 좋은 프레임워크를 만드는 건 놀랄 만큼 큰 작업이고, 사람들이 설계 공간을 탐색하며 새로운 아이디어를 시도하는 걸 보는 건 늘 좋음

    • Rust 프로젝트들은 이런 넓고 일반적이며 자기 과시적인 이름을 고르는 경향이 있는데, 솔직히 큰 실수라고 봄. Crates.io는 그럴듯한 이름을 선점한 버려진 프로젝트들의 묘지가 될 것임
    • “Row”도 좋은 이름이고, rwf보다 말하기 쉬울 것 같음
      RustOnWeb.com도 $10에 살 수 있음 :) 그냥 해본 말임
  • “Rails를 생각하라, Flask가 아니라”라는 표현이 마음에 듦. 정적 타입 언어에도 Rails/Django 같은 자기주장이 있는 프레임워크가 더 많거나 더 좋아질 필요가 있음

  • 잘 만들었음. https://www.arewewebyet.org/에 소개될 수 있도록 시도해볼 만함

    • 이 페이지에 loco.rs가 없는 게 꽤 놀라움. Rust 프레임워크 중 가장 “Rails”에 가까워 보이는데도 빠져 있음
  • 좋음! 최상급 언어로 만든 Django/Rails라니
    제안하자면 async-trait는 이제 안정화됐을 테니 매크로가 더는 필요 없을 수 있고, 기본으로 지표와 추적을 얻을 수 있게 OpenTelemetry 통합을 추가하면 좋겠음. Linux 대상에는 jemalloc도 쓰면 좋음

    • 고마움. 표준 비동기 트레이트를 써보려 했지만 Rwf가 광범위하게 쓰는 동적 디스패치를 지원하지 않음
      tracing 크레이트의 함수들에 OpenTelemetry 태그를 추가할 예정임. jemalloc은 Rwf를 사용하는 바이너리 앱에 추가할 수 있고, 라이브러리 의존성으로 넣을 필요는 없음
      [1] https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-trait...
    • Linux 대상에 jemalloc을 써야 하는 이유가 궁금함. 또는 왜 다른 대상에는 필요하지 않은지도 궁금함
  • Rust를 배우려 했지만 초반에 포기했음. C#, C/C++, Java, Python에서 왔더니 Rust는 이상하게 느껴졌음
    나를 꺾은 건 이상함 자체가 아니라, C#, Java, Python 같은 언어에 비해 한 작업을 끝내는 속도가 느린 점이었음
    요즘은 주로 웹 개발을 하다 보니 사용 편의성과 생산성이 최우선임. Rust가 처리량과 속도에서 조금 더 나을 수는 있지만, 최신 웹 프레임워크 벤치마크를 보면 C#과 Java도 이 면에서 꽤 잘함
    이걸 Rust 비판이나 MVC 프레임워크를 처음부터 만든 큰 성취를 깎아내리는 말로 읽지는 않았으면 함. “작업에 맞는 적절한 도구”라는 쪽에 더 가까움

    • 무슨 말인지 완전히 이해함. 나를 포함해 많은 사람이 처음에 비슷한 경험을 했음
      학습 곡선을 넘으려면 강한 동기가 필요함. 그 언어를 써야 한다거나, 타입 시스템과 안전성의 이점에 끌린다거나, 자기만의 구체적 이유가 있어야 함
      그런 동기가 없다면 전혀 괜찮음. 나도 처음엔 없었고 그래서 초반 학습 시도가 멀리 가지 못했음. 많은 사람이 느끼듯 Rust는 Java나 Python보다 일을 끝내는 데 더 오래 걸리고, 앞에서 더 많이 생각해야 함. 특정 상황에서는 가치 있지만, 그 트레이드오프를 왜 받아들일지 명확하지 않다면 계속할 강한 이유가 없음
  • 최근 webpack JS 로더를 버리고 50배 빠른 Rust 로더를 쓰면서 나도 Rust에 빠졌음. Rust는 성능 향상 효과가 크고, 지금은 C++와 Rust가 가장 좋아하는 언어임

    • 오, webpack을 버리고 무엇을 쓰게 됐는지 공유해줄 수 있음?
  • 백엔드 Rust 작업을 위해 흔히 거론되는 후보들을 모두 평가하고 작은 프로토타입을 만들어봤음. 지금까지는 원하는 것보다 저수준이긴 해도, 전반적으로 Axum이 최선이라는 집단적 결론에 마지못해 동의하게 됨
    다른 후보는 Loco와 Rocket이었음. Loco는 Rails와 너무 비슷했고, Rocket은 Rust를 더 많이 쓰고 나서야 매크로가 거슬리기 시작했음
    이 프레임워크는 “배터리 포함이지만 너무 규정적이지 않은” 내 기준에 완벽하게 맞아 보임. 생태계에 좋은 추가임

    • Actix는 어떨까? 기억하기로 예전에 HN에서 Actix와 Axum 관련 논의가 있었음