엔지니어링 리뷰: 2025 에디션
Everett Pompeii
새로운 기술을 개발할 때, Bencher와 같은 기술을 개발할 때는 평범한 기술을 선택하고 싶어하는 욕구와 평균을 넘어서기 간의 근본적인 긴장이 존재합니다. 그 순간에는 이 줄다리기에서 정확히 어디에 서 있는지 알기 어려울 수 있습니다. 매 3년마다, Rust 프로그래밍 언어는 새로운 Rust 에디션을 발표합니다. 저는 이것이 좋은 리듬이라고 생각합니다. 실제로 진보가 이루어질 만큼 충분히 길고, 너무 멀리 벗어나지 않을 만큼 충분히 짧습니다. Bencher가 올해 봄에 3주년을 맞이하기에, 여기까지 오게 만든 모든 엔지니어링 결정을 되돌아보기에 좋은 시기라고 생각했습니다.
이 글에서는 지난 3년 동안 Bencher가 “혁신 토큰”을 어떻게 사용했는지 되돌아보려 합니다. Bencher는 오픈 소스 지속적인 벤치마킹 도구 모음입니다. Bencher 아키텍처의 프런트엔드에서 시작하여 스택 전체로 이동할 것입니다. 각 지점에서는 우리가 어떻게 여기에 이르렀는지 논의하고, 각 엔지니어링 결정이 어떻게 결과로 나타났는지에 대한 이진 평가를 할 것입니다.
프론트엔드
프론트엔드 라이브러리
C++ 개발자였던 시절을 회상하며, 저는 Rust를 매우 좋아합니다. 제 선택이 있었다면, Bencher를 전체 스택 Rust로 작성할 수 있었을 것입니다. [Bencher 리포][bencher github]의 깊숙한 곳을 파고들어 보면, 제가 정확히 그렇게 하려고 노력한 것을 볼 수 있습니다. 저는 [Yew][yew github], [Seed][seed github], [Sycamore][sycamore github]를 실험해보았습니다. 어떤 프로젝트에는 이 라이브러리들이 매우 훌륭할 수 있지만, 제가 극복할 수 없었던 큰 장애물이 하나 있었습니다: [JavaScript 상호 운용성][js ffi].
비록 JS 상호 운용이 Rust를 통한 WASM에서 가능하지만, 쉽지는 않을 것임을 알았습니다. 저는 Bencher가 매우 인터랙티브한 차트를 갖기를 원했습니다. 이것은 [D3][d3 github] 같은 라이브러리를 사용해야 한다는 것을 의미했고, 이로 인해 JS 상호 운용이 필요했습니다.
그렇다면, JavaScript를 사용해야 한다면, 어떤 라이브러리를 선택해야 할까요?
제가 실험했던 Rust 크레이트로 돌아가 보면, Yew는 [React Hooks][react hooks]에 대한 Rust 유사체입니다. 저는 과거에 React Hooks를 사용하여 프론트엔드를 구축하고 배포한 경험이 있어, 이 프레임워크에 대해 가장 많이 알고 있었습니다. 하지만 React Hooks의 라이프사이클은 매우 복잡하고, 예기치 못한 이상한 모서리 케이스가 많다는 것을 알았습니다.
저는 [함수형 반응 프로그래밍][functional reactive programming] (FRP)의 핵심 원칙을 정말 좋아했습니다. 이것은 저를 [Elm][elm]과 그 Rust 유사체인 Seed를 시도하게 만들었습니다. 불행히도, Elm을 사용하는 것은 Rust를 사용하는 것과 같은 문제를 겪게 됩니다. Elm은 자체 [JavaScript 상호 운용성][elm js interop]이 필요합니다. 저는 또한 [Elm 아키텍처][the elm architecture]가 제 취향에는 다소 제한적이라는 것을 알았습니다.
제가 시도한 Rust 프레임워크 중에서, 저는 Sycamore가 가장 마음에 들었습니다. Sycamore는 [Solid][solid github]에서 영감을 받았습니다. Solid에 대해 배우면 배울수록 더 좋아하게 되었습니다. React와 달리 Solid는 [가상 DOM][react virtual dom]을 사용하지 않습니다. 대신, 고전적인 JavaScript로 컴파일됩니다. 이것은 속도가 더 빠르고, 크기가 더 작으며, 작업하기 더 쉽다는 것을 의미합니다. Solid는 세밀한 반응성을 가능하게 하는 몇 가지 강력한 원칙들로 구성되어 있습니다. UI의 어떤 부분이 업데이트되면, 그것에 의존하는 코드만 다시 실행됩니다. 지난 3년 동안, 저는 Solid를 사용하는 것이 즐거웠습니다.
기술 평가 Yew ❌ Seed ❌ Sycamore ❌ Elm ❌ SolidJS ✅
프론트엔드 프레임워크
Solid 자체는 단지 라이브러리입니다. 현대적인 프론트엔드를 구축하기 위해서는 완전한 웹 앱 프레임워크를 사용해야 했습니다. 간단하게 유지하고 싶어서 모든 것을 Solid에 의존하기로 했고, 처음에는 SolidStart를 사용했습니다. 그 당시 SolidStart는 단일 페이지 앱(SPA)만 지원했습니다.
SPA는 시작하기에는 괜찮았습니다. 하지만 시간이 지나고 SEO와 같은 것들에 신경을 써야 했습니다. 훨씬 더 많은 벤처 문서를 작성하기 시작했고 사이트의 학습 섹션을 계획하고 있었습니다. 이는 클라이언트 사이드 렌더링(CSR)과 정적 사이트 생성(SSG) 모두가 필요하다는 것을 의미했습니다. SolidStart는 매우 신생 상태였고, 제가 필요한 모든 것을 충족시키지 못했습니다.
Astro에 대해 배우고 시도해 본 후에, Bencher 프론트엔드 전체를 SolidStart에서 Astro로 포팅하기로 결정했습니다. 이것에는 몇 가지 단점이 있었습니다. 가장 분명한 것은 관련된 노력이었습니다. 솔직히 말해서 그렇게 나쁘지 않았습니다. Astro는 아일랜드 아키텍처와 일류의 Solid 통합을 가지고 있습니다. Solid Router에서 필요한 많은 논리를 가져오는데, 그저 잘 작동했습니다.
오늘날까지도 존재하는 큰 타협점은 Bencher가 단일 페이지 앱에서 다중 페이지 앱으로 전환되었다는 것입니다. 콘솔에서 대부분의 클릭이 전체 페이지 재렌더링을 일으킵니다. 처음 전환했을 때 Astro는 뷰 트랜지션을 약속했었습니다. 시도해봤지만 오류가 많았습니다. 아직 다시 돌아가봐야 합니다.
그동안 SolidStart는 어느 정도 따라잡은 것으로 보입니다. 이제 CSR과 SSG 모두를 지원합니다. 제가 필요한 것처럼 동일한 사이트에서 둘 다 작동하는지는 확인하지 않았습니다. 이미 지나간 일입니다.
기술 평가 SolidStart ❌ Astro ✅
프론트엔드 언어
Astro는 내장 TypeScript 지원을 제공합니다. SolidStart에서 Astro로의 전환과 함께, JavaScript에서 TypeScript로의 전환도 시작했습니다. Bencher의 TypeScript 설정은 Astro의 strictest
설정으로 되어 있습니다. 그러나 Astro는 빌드 중에 타입 검사를 수행하지 않습니다. 작성 시점에, Bencher는 여전히 604
개의 타입 오류가 있습니다. 이러한 타입 오류는 코드를 편집할 때 힌트로 사용되지만, 빌드를 막지는 않습니다 (아직은).
Bencher의 Rust 데이터 타입을 TypeScript 프론트엔드와 동기화하기 위해 Typeshare도 추가했습니다. 이는 Bencher Console을 개발하는 데 매우 유용했습니다. 더 나아가, 사용자 이름, 이메일 등과 같은 항목의 모든 필드 검증기는 WASM을 통해 Rust 코드와 TypeScript 프론트엔드 간에 공유됩니다. SolidStart와 Astro에서 WASM을 작동시키는 데 약간 어려움이 있었습니다. 프론트엔드에서 보아온 가장 큰 유형의 오류는 WASM 함수가 호출되지만 WASM 모듈이 아직 로드되지 않은 경우입니다. 이를 해결하는 방법을 알아냈지만, 때때로 잊어버리면 다시 나타나곤 합니다.
Rust 코드에서 자동 생성된 공유 타입과 검증기를 모두 갖춤으로써 프론트엔드와의 인터페이스가 훨씬 쉬워졌습니다. 두 개가 모두 CI에서 확인되므로, 절대 싱크가 맞지 않는 경우가 없습니다. HTTP 요청이 잘 형성되었는지만 확인하면, 모두 잘 작동합니다. 이는 전체 스택 Rust를 사용할 수 없는 아쉬움을 조금 덜어줍니다.
기술 평가 Rust ❌ JavaScript ❌ TypeScript ✅ Typeshare ✅ WASM ✅
프론트엔드 호스팅
Solid에 “올인”하기로 한 초기 결정은 Solid의 창시자를 고용하여 풀타임으로 작업하게 한 Netlify에 의해 강하게 영향을 받았습니다. Netlify의 가장 큰 경쟁자는 Vercel입니다. Vercel은 Next.js를 만들고 유지 관리합니다. 그래서 Netlify가 Solid를 그들의 Next.js로 만들고 싶다고 생각했습니다. 따라서 SolidStart 사이트를 호스팅하기에 Netlify보다 더 좋은 곳은 없다고 생각했어요.
기본적으로, Netlify는 자체 빌드 시스템을 사용하도록 유도합니다. Netlify의 빌드 시스템을 사용하면 원자적 배포를 수행하기 매우 어렵습니다. 백엔드 파이프라인이 실패하더라도 Netlify는 여전히 프론트엔드를 게시합니다. 매우 나빠요! 이로 인해 프론트엔드를 백엔드와 동일한 CI/CD 환경에서 빌드하고 Netlify의 CLI를 사용하여 최신 버전을 업로드하도록 변경했습니다. SolidStart에서 Astro로 전환하면서 동일한 CI/CD 설정을 유지할 수 있었습니다. Astro에는 자체 Netlify 통합이 있습니다.
Bencher는 꽤 오랫동안 Netlify 무료 계층을 유지할 수 있었습니다. 하지만 Bencher의 인기가 증가함에 따라 몇 가지 무료 계층 한도를 초과하기 시작했습니다. Astro 사이트를 AWS의 sst
로 이전하는 것을 고려했지만, 현재로서는 비용 절감이 그 수고를 감당할 만큼 가치가 없어 보입니다.
기술 판단 Netlify Builds ❌ Netlify Deploys ✅
백엔드
백엔드 언어
Rust.
기술 평가 Rust ✅
HTTP 서버 프레임워크
러스트 HTTP 서버 프레임워크를 선택할 때 가장 중요하게 고려한 점 중 하나는 내장된 OpenAPI 사양 지원이었습니다. Typeshare 및 WASM을 프론트엔드에 설정한 이유와 같은 이유로, 저는 API 문서 및 클라이언트를 그 사양에서 자동 생성할 수 있는 기능을 원했습니다. 이 기능이 서드파티 추가 기능이 아닌, 내장되어 있는 것이 중요했습니다. 자동화가 실제로 가치 있기 위해서는 거의 100%에 가깝게 작동해야 합니다. 이는 유지보수 및 호환성 부담이 핵심 프레임워크 엔지니어들에게 있어야 함을 의미합니다. 그렇지 않으면, 결국 최악의 경계 상황에 빠지게 될 것입니다.
또 다른 중요한 고려 사항은 방치 위험이었습니다. 한때 유망했던 여러 러스트 HTTP 프레임워크들이 지금은 거의 방치되어 있습니다. 내장된 OpenAPI 사양 지원을 가지고 있으며 제가 신뢰할 수 있는 유일한 프레임워크는 Dropshot였습니다. Dropshot은 Oxide Computer에서 제작되어 현재도 유지보수되고 있습니다.
현재까지 Dropshot에서 크게 문제가 된 부분은 하나뿐이었습니다. API 서버에서 오류가 발생하면, 응답 헤더가 누락되어 프론트엔드에서 CORS 오류가 발생합니다. 이는 웹 프론트엔드가 사용자에게 도움이 되는 오류 메시지를 표시할 수 없다는 것을 의미합니다. 저는 수정사항을 업스트림에 기여하는 대신 Bencher를 더 쉽고 직관적으로 사용하도록 만드는 데 주력했습니다. 그러나 해결책은 100줄 미만의 코드였습니다. 웃긴 일이네요!
여담으로, axum
프레임워크는 제가 Bencher를 작업하기 시작했을 때 아직 출시되지 않았습니다. 그때 만약 출시되어 있었다면, 여러 서드파티 OpenAPI 추가 기능 중 하나와 짝짓기를 시도했을지도 모릅니다, 제 나름대로의 판단을 무시하며 말이죠. 다행히도 axum
은 저를 유혹하지 않았습니다. Dropshot은 훌륭한 선택이었습니다. 이 점에 대해 더 알아보려면 API 클라이언트 섹션을 참조하세요.
기술 결론 Dropshot ✅
데이터베이스
Bencher를 최대한 단순하게 유지하려고 노력했습니다. Bencher의 첫 번째 버전은 벤치마크 결과 자체를 URL 쿼리 매개변수를 통해 가져왔습니다. 모든 브라우저에 URL 길이에 제한이 있다는 것을 금방 알게 되었습니다. 상식적이죠.
다음으로, git
에 벤치마크 결과를 저장하고, 플롯과 결과가 포함된 정적인 HTML 파일을 생성하는 방법을 고려했습니다. 하지만 이 접근에는 두 가지 주요 단점이 있었습니다. 첫째, git clone
시간이 무거운 사용자에게는 결국 견디기 어려워집니다. 둘째, 모든 이력 데이터가 HTML 파일에 있어야 하므로 무거운 사용자에게는 초기 로드 시간이 매우 길어집니다. 개발 도구는 무거운 사용자를 사랑해야지 punishment 하면 안 되죠.
제 문제에 대한 해결책이 있다고 합니다. 그건 바로 데이터베이스입니다.
그렇다면, 단순히 Postgres를 불러와 사용하면 되지 않을까요? 사실, 저는 Bencher를 self-host 할 수 있기를 정말 원했습니다. 아키텍처를 간단하게 만들수록 다른 사람들이 self-host하기 쉽게 (그리고 저렴하게) 만들 수 있습니다. 이미 분리된 프론트엔드와 백엔드 때문에 두 개의 컨테이너가 필요할 예정이었습니다. 세 번째를 피할 수 있을까요? 네!
Bencher 이전에는 SQLite를 테스트 데이터베이스로만 사용해 왔습니다. 개발자 경험은 환상적이었지만, 실제 운영에서 사용하는 것은 고려하지 않았습니다. 그러다 Litestream을 접하게 되었습니다. Litestream은 SQLite를 위한 재난 복구 도구입니다. 백그라운드에서 실행되며, S3 또는 사용자가 선택한 다른 데이터 저장소로 변경 사항을 지속적으로 복제합니다. 이를 통해 사용하기 쉽고, 운영 비용이 매우 저렴해집니다. S3는 쓰기에 대한 요금이 없으므로, 작은 인스턴스의 경우 하루에 몇 센트가 필요합니다.
처음 Litestream을 접했을 때, 라이브 읽기 복제본이 곧 제공될 것이라는 약속도 있었습니다. 하지만 이는 결코 실현되지 않았습니다. 제안된 대안은 같은 개발자가 진행한 후속 프로젝트인 LiteFS 였습니다. 하지만 LiteFS에는 큰 단점이 있습니다. 모든 복제본이 다운되면 내장된 재난 복구 기능을 제공하지 않습니다. 여러 복제본을 가지려면, 애플리케이션 로직에 그것이 읽기 전용인지 또는 쓰기 전용인지의 개념을 주입해야 합니다. 그리고 결정적인 장애물은 이를 관리하기 위해 항상 Consul 인스턴스가 실행되어야 한다는 점이었습니다. SQLite를 사용하는 주된 이유는 또 다른 서비스를 피하려는 것이었습니다. 다행히도 Bencher Cloud와 LiteFS를 함께 사용하지 않았고, LiteFS Cloud는 출시 1년 만에 종료되었습니다, 현재 LiteFS는 사실상 거의 사라졌습니다.
현재 배포 중의 작은 다운타임은 Bencher CLI로 처리됩니다. 앞으로는 Kamal을 사용해 지연 없는 다운타임 배포로 이동할 계획입니다. Rails 8.0가 기본적으로 Kamal과 SQLite를 채택한 것에 따라 Kamal과 Litestream은 잘 어울릴 것이라고 확신합니다.
기술 평결 URL 쿼리 매개변수 ❌ git + HTML ❌ SQLite ✅ Litestream ✅ LiteFS ❌
데이터베이스 드라이버
데이터베이스에 가까워질수록, 더 강력한 타입을 원하는 욕구가 커집니다. 프론트엔드에서는 조금 느슨하게 해도 괜찮습니다. 만약 실수를 해도, 다음번 프로덕션 배포 때는 아무 문제 없이 해결될 것입니다. 하지만 데이터베이스가 손상되면 복구하는데 훨씬 더 큰 고생이 따릅니다. 이러한 점을 고려하여, 저는 디젤을 사용하기로 결정했습니다.
디젤은 러스트(Rust)용 강력한 타입의 객체 관계 매퍼(ORM) 및 쿼리 빌더입니다. 컴파일 시점에서 모든 데이터베이스 상호작용을 체크하여 런타임 오류를 방지합니다. 이러한 컴파일 시점 체크 덕분에 디젤은 SQL 위에서 추가 비용 없는 추상화를 제공합니다. 제가 성능 튜닝으로 1200배 더 빠르게 만드는 데 작은 버그가 발생한 것 외에는, 디젤을 사용할 때 런타임 SQL 오류가 발생한 적이 없습니다.
🐰 재미있는 사실: 디젤은 Bencher를 사용하여 지속적인 벤치마킹을 진행합니다!
기술 평가 디젤 ✅
백엔드 호스팅
제가 Solid를 사용했기 때문에 프런트엔드 호스팅으로 Netlify를 선택한 것과 마찬가지로, 백엔드 호스팅으로는 Litestream을 사용했기 때문에 Fly.io를 선택했습니다. Fly.io는 막 Litestream의 창시자를 풀타임으로 고용했었습니다. 위에서 언급했듯이, 이 Litestream의 작업은 결국 LiteFS에 의해 대체되었고, LiteFS는 이제 더 이상 존재하지 않습니다. 결국, 그 부분은 제가 기대했던 대로 풀리지 않았습니다.
카말로 전환할 때, Fly.io를 떠날 예정입니다. Fly.io는 Bencher를 반나절 동안 중단시킨 주요 장애가 몇 차례 발생했습니다. 하지만 가장 큰 문제는 Litestream을 사용하면서 발생하는 불일치입니다.
제가 Fly.io 대시보드에 로그인할 때마다, 이 경고를 봅니다:
ℹ 당신의 앱이 단일 머신에서 실행되고 있습니다
확장을 통해 고가용성을 보장하려면 한 명령어로 앱을 여러 머신에서 실행하십시오:
스케일링에 대한 자세한 내용은 문서를 확인하세요.
하지만 Litestream과 함께, 여전히 하나 이상의 머신을 사용할 수 없습니다! 약속한 것처럼 읽기 복제를 제공하지 않았습니다!
그래서 그 점이 조금 아이러니하고 답답합니다. 한때 libSQL과 Turso를 살펴본 적이 있습니다. 그러나 libSQL은 복제를 위해 특별한 백엔드 서버가 필요하며, 이는 Diesel과 호환되지 않게 만듭니다. 어쨌든 또 다른 서비스 종료를 피한 것 같습니다. Turso가 Limbo, 즉 SQLite의 Rust 재작성과 관련하여 무엇을 할지 매우 궁금합니다. 그러나 당분간 그 전환을 하지 않을 것입니다. 다음 목적지는 평범하고 안정적인 카말로 실행되는 가상 머신입니다.
Litestream 복제를 위한 AWS S3 백엔드는 흠잡을 데 없이 작동했습니다. Litestream 및 Fly.io의 혼란에도 불구하고, Bencher에 Litestream을 사용하는 것이 올바른 선택이었다고 생각합니다. Bencher Cloud에서 몇 가지 확장 문제에 직면하고 있지만, 이는 좋은 문제라고 할 수 있습니다.
기술 평가 Fly.io ❌ AWS S3 ✅
CLI
CLI 라이브러리
Rust CLI를 구축할 때, Clap은 사실상의 표준에 가깝습니다. 그래서 제가 Bencher를 처음으로 공개 시연했을 때, 정작 창시자 himself, Ed Page가 거기에 있었을 때의 충격을 상상해 보세요! 🤩
시간이 지남에 따라, Clap이 할 수 있는 점점 더 많은 유용한 것을 발견하고 있습니다.
조금 창피합니다만, 최근에야 the default_value
option을 발견했습니다.
이 모든 기능은 bencher
CLI에서 관리해야 하는 코드의 양을 줄이는 데 정말로 도움이 됩니다.
🐰 재미있는 사실: Clap은 Bencher를 사용합니다 이진 파일 크기를 추적하기 위해!
기술 결과 Clap ✅
API 클라이언트
Bencher의 HTTP 서버 프레임워크로 Dropshot을 선택한 주요 이유는 내장된 OpenAPI 스펙 생성 기능이었습니다. 언젠가 그 스펙으로부터 API 클라이언트를 자동 생성할 수 있기를 희망했습니다. 약 일 년 후, Dropshot의 제작자들은 Progenitor를 선보였습니다.
Progenitor는 Dropshot의 양에 대한 음입니다. Dropshot으로부터 OpenAPI 스펙을 사용하여, Progenitor는 Rust API 클라이언트를 아래와 같은 방식의 위치 기반 패턴으로 생성할 수 있습니다:
또는 빌더 패턴:
개인적으로 후자를 선호하며, Bencher는 이를 사용합니다. Progenitor는 또한 API와 상호작용할 수 있는 전체 Clap CLI를 생성할 수 있습니다. 하지만, 저는 이를 사용하지 않았습니다. 특히 bencher run
과 같은 명령에 대해 더 세밀한 제어가 필요했습니다.
생성된 타입과 관련하여 제가 찾은 유일한 주목할만한 단점은 JSON Schema의 제한 때문에 키가 없는 item
과 값이 null
인 item
키를 구별해야 할 때 Option<Option<Item>>
를 그냥 사용할 수 없다는 점입니다. double_option
과 같은 것을 사용하면 가능하지만, JSON Schema 수준에서는 동일하게 보입니다. nested 또는 untagged 내부 구조체 열거형은 Dropshot과 잘 맞지 않습니다. 제가 찾은 유일한 해결책은 상위 수준의, untagged 열거형을 사용하는 것이었습니다. 현재로선 API 전체에 두 개의 필드밖에 없으므로 큰 문제는 아닙니다.
기술 판단 Progenitor ✅
개발
개발자 환경
Bencher에서 작업을 시작할 때, 사람들은 localhost의 끝을 부르짖고 있었습니다. 저는 이미 새로운 개발 노트북이 필요할 만큼 오래되었으므로, 클라우드 개발 환경을 시도해보기로 결정했습니다. 그 당시 GitHub Workspaces는 제 사용 사례에 대해 일반적으로 사용 가능(GA)하지 않았기 때문에, Gitpod을 선택했습니다.
이 실험은 약 6개월 동안 지속되었습니다. 제 결론: 클라우드 개발 환경은 사이드 프로젝트에 적합하지 않습니다. 5분 동안 급히 작업을 하고 싶으신가요? 안 됩니다! 개발 환경이 1,000번째로 다시 초기화될 때까지 기다려야 할 것입니다. 아, 주말 오후 내내 작업에 몰두할 시간이 있으신가요? 안 됩니다! 개발 환경이 사용 중에 무작위로 작동을 멈춥니다. 계속해서 반복됩니다.
이 문제들은 유료 사용자로서 겪었습니다. 월 $25이면 5년마다 훨씬 더 좋은 사양의 새로운 M1 MacBook Pro를 살 수 있었습니다. Gitpod가 요금을 일정 요금제에서 사용 기반으로 변경한다고 발표했을 때, 저는 그저 그들 계획을 취소하고 apple.com으로 향했습니다.
이 모든 것이 Kubernetes를 사용하겠다는 Gitpod의 이제 포기된 결정 때문일지도 모릅니다.
그러나 저는 Bencher와 함께 다른 클라우드 개발 환경을 시도할 마음이 없습니다.
결국, 기여자들이 쉽게 시작할 수 있도록 Gitpod 구성을 dev 컨테이너로 포팅했습니다.
그러나 저는 localhost
를 고수할 것입니다.
기술 판결 Gitpod ❌ M1 MacBook Pro ✅
지속적 통합
Bencher는 오픈 소스입니다. 현대의 오픈 소스 프로젝트로서, GitHub에 있어야 합니다. 이는 지속적 통합 (CI)에서 가장 손쉬운 경로가 GitHub Actions임을 의미합니다. 여러 해가 지나면서, 저는 YAML 기반의 CI DSL에 염증을 느끼기 시작했습니다. 각 DSL마다 고유의 버그가 있고, GitHub 같은 대기업에서는 ❌ 아이콘 대신 ⚠️ 아이콘을 받는 문제가 몇 년 동안 방치되기도 했습니다.
이 때문에 Dagger를 시도해 보기로 했습니다. 그 당시에는 CUE라는 난해한 언어를 통해서만 Dagger를 사용할 수 있었습니다. 정말 노력했습니다. 주말 내내 시도했습니다. 아마 그때 ChatGPT가 있었다면, 성공했을지도 모릅니다. 하지만 저만 그런 게 아니었습니다. Dagger는 궁극적으로 더 합리적인 SDK를 위해 CUE를 포기했습니다. 하지만 그때는 이미 너무 늦었습니다.
Dagger에 패배한 저는 YAML CI DSL 운명을 받아들였고,
Bencher는 이제 GitHub Actions를 사용합니다.
게다가 저는 Bencher CLI GitHub Action까지 제작했습니다.
세상에서 변화 문제가 되고자 하는 사람이 되세요.
기술 평결 Dagger ❌ GitHub Actions ⚠️
결론
Bencher를 구축하면서 각 엔지니어링 결정에 따른 트레이드오프에 대해 많은 것을 배웠습니다. 지금이라면 다르게 선택할 몇 가지가 있지만, 이는 제가 그동안 몇 가지를 배웠다는 좋은 신호입니다. 전반적으로, 저는 현재 Bencher의 상태에 매우 만족합니다. Bencher는 저의 노트북 속 스케치에서 성장하는 사용자 기반, 활기찬 커뮤니티, 유료 고객을 가진 완전한 제품으로 발전했습니다. 앞으로의 3년이 더욱 기대됩니다!
스택 구성 요소 기술 결론 프론트엔드 프론트엔드 라이브러리 Yew ❌ Seed ❌ Sycamore ❌ Elm ❌ SolidJS ✅ 프론트엔드 언어 Rust ❌ JavaScript ❌ TypeScript ✅ Typeshare ✅ WASM ✅ 프론트엔드 호스팅 Netlify Builds ❌ Netlify Deploys ✅ 백엔드 백엔드 언어 Rust ✅ HTTP 서버 프레임워크 Dropshot ✅ 데이터베이스 URL Query Params ❌ git + HTML ❌ SQLite ✅ Litestream ✅ LiteFS ❌ 데이터베이스 드라이버 Diesel ✅ 백엔드 호스팅 Fly.io ❌ AWS S3 ✅ CLI CLI 라이브러리 Clap ✅ API 클라이언트 Progenitor ✅ 개발 개발자 환경 Gitpod ❌ M1 MacBook Pro ✅ 지속적 통합 Dagger ❌ GitHub Actions ⚠️
Bencher: 지속적인 벤치마킹
Bencher는 지속적인 벤치마킹 도구 모음입니다. 성능 회귀가 사용자에게 영향을 미친 경험이 있나요? Bencher가 그런 일이 일어나는 것을 막을 수 있었습니다. Bencher를 이용하면 성능 회귀를 상용 환경으로 이동하기 전에 탐지하고 예방할 수 있습니다.
- 실행: 기존 벤치마킹 도구를 사용하여 로컬 또는 CI에서 벤치마크를 실행합니다.
bencher
CLI는 기존 벤치마킹 하네스를 감싸고 결과를 저장합니다. - 추적: 벤치마크 결과를 시간이 지남에 따라 추적합니다. 소스 브랜치, 테스트 베드, 측정 기반의 Bencher 웹 콘솔을 사용하여 결과를 모니터링, 쿼리, 그래프로 만듭니다.
- 캐치: CI에서 성능 회귀를 잡아냅니다. Bencher는 최첨단, 사용자 정의 가능한 분석을 사용하여 상용 환경으로 가기 전에 성능 회귀를 탐지합니다.
단위 테스트가 CI에서 기능 회귀를 방지하기 위해 실행되는 것처럼, 벤치마크는 Bencher와 함께 CI에서 실행되어 성능 회귀를 방지해야 합니다. 성능 버그도 버그입니다!
CI에서 성능 회귀를 잡아내기 시작하세요 - Bencher Cloud를 무료로 시도해보세요.