Lobsters

Hanami, Why?: Introductions

Hanami, 왜 써야 할까요? — 소개

Hanami 유지보수자가 자신의 웹사이트를 만들며 선택한 프레임워크와 기본 구조를 소개합니다. Action, Relation, Repo, Operation으로 책임을 나누는 방식과 직접 구현해야 하는 기능, Rails보다 적은 참고 자료 같은 단점도 함께 설명합니다.

AI 요약

글쓴이는 블로그처럼 보이는 웹사이트의 백엔드에 여러 기능을 직접 만들었습니다. Bluesky와 Mastodon 동시 게시, 비공개 일지, 분석 엔진, 문의 메시지 처리, Linear와 GitHub Issues를 동기화하는 작업 관리 도구, 활동 피드가 들어 있습니다. 활동 피드는 GitHub 커밋, 일지, 게시물, 작업을 모아 특정 기간에 무엇을 했는지 확인하도록 돕습니다. 애플리케이션은 오픈소스로 공개했습니다.

글쓴이는 Rails의 정해진 방식에 답답함을 느껴 다른 선택지를 찾았고, RubyConf 2024에서 Hanami를 제대로 접했습니다. 현재는 Hanami와 dry-rb, ROM을 만드는 Hanakai 팀의 유지보수자로 일합니다. 이 글은 Hanami의 차이를 설명하는 연재의 첫 편입니다.

Hanami의 기본 구조

Hanami는 작고 한 가지 책임만 맡는 추상화를 지향합니다. 글쓴이가 꼽는 주요 구성 요소는 Actions, Relations, Repos, Operations입니다. Action은 HTTP 엔드포인트 하나를 담당합니다. Relation은 모델 계층에서 데이터베이스 질의를 맡고, Repo는 Relation 위에서 여러 관계를 다루며 영속성 계층 대신 Struct 같은 데이터 객체를 돌려주는 선택적 계층입니다. Operation은 dry-operation으로 구현한 Command 패턴으로, 복잡한 데이터 변경을 처리하고 Success 또는 Failure 결과를 반환합니다.

예시에서 Action은 입력 형태를 선언하고 검증한 뒤 CreateUser Operation을 호출합니다. Operation은 이메일 중복을 확인하고 비밀번호를 해시한 다음 Repo를 통해 사용자를 생성합니다. 성공하면 Action이 사용자 데이터를 JSON으로 응답하고, 검증이나 생성에 실패하면 오류와 함께 422 상태 코드를 돌려줍니다. 클래스에는 dry-system의 Deps 모듈을 사용해 필요한 의존성을 주입합니다. dry-types는 입력 검증과 정규화를 맡고, dry-monads의 결과 객체와 패턴 매칭은 성공·실패 처리를 정리합니다.

이 구조의 모든 계층을 반드시 써야 하는 것은 아닙니다. Operation을 생략하고 Action에서 직접 처리하거나, Repo 대신 Relation을 호출할 수도 있습니다. 글쓴이는 Hanami가 계층을 제안하되 애플리케이션에 맞게 선택하도록 한다고 설명합니다.

감수해야 할 점

Hanami에서는 관리 화면이나 인증처럼 흔히 쓰는 기능도 직접 만들어야 하는 경우가 많습니다. 촉박한 일정에 팀이 이런 기능을 당장 필요로 한다면 부담이 됩니다. 글쓴이는 의존성 트리가 짧아지면 위험도 낮아진다고 보고, 직접 구현하면서 각 부분을 이해할 수 있다는 점을 장점으로 듭니다. 유용한 구현을 만들었다면 gem으로 공개해 커뮤니티에 보탤 수도 있습니다. 글쓴이는 phlex-hanami와 hanami-settings-stores를 예로 듭니다.

복잡한 예외 상황을 다루는 자료도 Rails만큼 풍부하지 않습니다. Hanami에는 오랜 기간 쌓인 블로그 글과 Stack Overflow 답변이 적고, AI 코딩 도구도 Rails 코드보다 Hanami 코드를 덜 접했습니다. 검색만으로 해결하는 데 익숙한 개발자는 초반에 시간이 더 걸릴 수 있습니다. 글쓴이는 Discord, 토론 포럼, GitHub 이슈에서 도움을 구하고, 막힌 부분을 문서 개선으로 이어가자고 권합니다.

Lobsters 반응

  • @MatheusRich — Hanami가 실제 서비스에서 쓰이는 모습을 보는 건 언제나 좋습니다. 저도 비슷한 앱을 직접 만들었는데, 그 앱도 오픈소스로 공개해야겠습니다.

원문: Aaron Mallen / 번역·요약: Trawling