Rust

43 posts
Zerobrew: Homebrew보다 최대 20배 빠른 패키지 매니저

Zerobrew: Homebrew보다 최대 20배 빠른 패키지 매니저

Homebrew를 쓰면서 느린 속도 때문에 답답했던 적 있으신가요? brew install 하나 실행했을 뿐인데 몇십 초씩 걸리고, brew update는 아예 커피 한 잔 타올 시간을 주기도 하죠. Homebrew는 macOS 개발 환경의 사실상 표준이지만 Ruby로 작성된 태생적 한계 때문에 성능은 늘 아쉬웠습니다. Zerobrew는 바로 이 문제를 풀려고 나온 프로젝트입니다. Rust로 작성했고 Homebrew의 패키지 생태계를 그대로 쓰면서도 설치 속도를 5배에서 최대 20배까지 끌어올렸는데요. Python 생태계에서 pip

Starship으로 터미널 프롬프트 꾸미기

Starship으로 터미널 프롬프트 꾸미기

터미널을 열 때마다 보이는 프롬프트가 밋밋하다고 느끼신 적 있으신가요? 기본 프롬프트는 현재 디렉터리 정도만 보여주다 보니 Git 브랜치를 확인하려면 git branch를 치고, Node.js 버전이 궁금하면 node -v를 입력해야 하죠. Starship은 이런 불편을 해결해주는 크로스 쉘 프롬프트입니다. Rust로 만들어져서 빠르고, Bash부터 Zsh, Fish, PowerShell까지 거의 모든 쉘에서 동작합니다. 이 글에서는 Starship을 설치하고 나만의 프롬프트를 설정하는 방법을 알아보겠습니다. Starship이란?

release-plz로 Rust 패키지 릴리스 자동화하기

release-plz로 Rust 패키지 릴리스 자동화하기

Rust로 라이브러리를 만들어서 crates.io에 배포하고 있다면 릴리스할 때마다 반복해야 하는 작업이 꽤 많다는 걸 느끼셨을 거예요. Cargo.toml의 버전 올리고 체인지로그 정리하고 cargo publish 실행하고 git 태그 만들고 GitHub Release도 작성하고... 🤔 매번 수작업으로 하다 보면 빼먹는 단계가 생기기 마련이죠. release-plz는 이 과정을 통째로 자동화해주는 도구인데요. Conventional Commits 기반으로 버전을 알아서 올려주고, 체인지로그도 자동 생성하고, PR 하나 머지하면

cargo semver-checks로 Rust API 호환성 자동 검사하기

cargo semver-checks로 Rust API 호환성 자동 검사하기

Rust 크레이트를 배포하다 보면 한 번쯤 이런 경험이 있을 거예요. 패치 버전만 올려서 배포했는데, 사실은 공개 함수의 시그니처를 바꿔버렸던 거죠. 의존하는 쪽에서는 갑자기 빌드가 깨지고, 이슈가 올라오고... 😅 시맨틱 버저닝(Semantic Versioning)은 API 변경의 종류에 따라 메이저, 마이너, 패치 버전을 구분하는 규칙인데, 사람이 매번 이걸 정확히 판단하기는 쉽지 않습니다. 구조체에 필드 하나 추가한 게 호환성을 깨는 변경인지, 트레이트에 기본 구현이 있는 메서드를 추가한 건 괜찮은 건지, 헷갈리는 경우가 많

cargo llvm-cov로 Rust 코드 커버리지 측정하기

cargo llvm-cov로 Rust 코드 커버리지 측정하기

테스트를 열심히 작성했는데 과연 우리 코드가 어디까지 테스트되고 있는 걸까요? "이 모듈은 테스트를 꽤 많이 짰으니까 괜찮겠지"라고 막연하게 생각하다가 정작 핵심 에러 처리 분기는 한 번도 실행된 적이 없었다는 걸 나중에 발견하는 경우가 종종 있습니다 😅 코드 커버리지는 이런 사각지대를 눈에 보이게 만들어주는 도구입니다. Rust에서는 cargo-llvm-cov가 이 역할을 맡고 있는데 LLVM의 소스 기반 커버리지 기능을 활용해서 정확한 측정 결과를 제공해요. 이번 글에서는 설치부터 기본 사용법, 여러 리포트 형식, CI 연동까

cargo fmt로 Rust 코드 스타일 자동 정리하기

cargo fmt로 Rust 코드 스타일 자동 정리하기

팀에서 코드 리뷰를 하다 보면 로직과 무관한 스타일 논쟁에 시간을 쓸 때가 있어요. 중괄호를 같은 줄에 둘지 다음 줄에 둘지, use 구문을 어떻게 묶을지, 한 줄이 몇 글자를 넘으면 줄바꿈할지... 이런 토론이 쌓이면 정작 중요한 설계나 로직 리뷰에 집중하기 어렵습니다 😅 Rust 생태계에서는 이 문제를 rustfmt라는 공식 포매터로 깔끔하게 해결합니다. cargo fmt 한 번이면 코드 스타일이 일관되게 정리되니까 팀원들은 포매팅에 신경 쓸 필요 없이 코드 자체에만 집중할 수 있죠. 이번 글에서는 rustfmt의 설치부터 설

cargo clippy로 Rust 코드 품질 높이기

cargo clippy로 Rust 코드 품질 높이기

Rust 컴파일러는 워낙 꼼꼼해서 컴파일만 통과하면 꽤 안심이 되죠. 하지만 "컴파일은 되는데 이게 정말 좋은 코드인가?"라는 질문에는 컴파일러가 답해주지 않습니다. 불필요한 clone() 호출, 더 간결하게 쓸 수 있는 패턴, 성능을 깎아먹는 습관적인 코드... 이런 건 컴파일러의 관심사가 아니거든요. Clippy는 바로 이 영역을 담당하는 Rust의 공식 린트 도구입니다. 700개가 넘는 린트 규칙으로 코드를 분석해서 잠재적인 버그부터 스타일 개선 사항까지 짚어줍니다. 이번 글에서는 Clippy의 설치와 기본 사용법, 주요 린트

rustup으로 Rust 툴체인 관리하기

rustup으로 Rust 툴체인 관리하기

Rust를 처음 시작할 때 공식 사이트에서 안내하는 대로 설치 스크립트를 실행하면 rustup이라는 도구가 함께 깔립니다. 처음에는 "Rust 설치 프로그램"쯤으로 생각하고 넘어가기 쉬운데, 사실 rustup은 그보다 훨씬 많은 일을 해요. 컴파일러 버전을 바꾸거나 nightly 기능을 잠깐 써보거나 팀 전체의 Rust 버전을 맞추거나 WebAssembly로 크로스 컴파일하는 것까지 전부 rustup이 담당합니다. 이번 글에서는 rustup의 설치부터 툴체인 관리, 컴포넌트와 타겟 설정, 팀 단위 버전 고정까지 살펴보겠습니다. ru

Tauri로 가벼운 데스크톱 앱 만들기

Tauri로 가벼운 데스크톱 앱 만들기

웹 개발자라면 한 번쯤 "내가 만든 웹 앱을 데스크톱 앱으로 배포할 수 있으면 좋겠다"고 생각해 보신 적 있을 겁니다. Electron이 이 영역을 오랫동안 지배해 왔지만, 앱 하나에 Chromium 전체를 번들하다 보니 설치 파일이 100MB를 넘어가는 건 일상이었죠 😅 Tauri는 이 문제를 풀려고 만들어진 프레임워크입니다. 프론트엔드는 기존 웹 기술을 그대로 쓰되 백엔드를 Rust로 작성하고, OS의 네이티브 웹뷰를 활용해서 앱 크기를 수 MB 수준으로 줄여 줘요. 이번 글에서는 Tauri 2로 데스크톱 앱을 처음부터 만들어

Rust 기초: Cow로 불필요한 복사 줄이기

Rust 기초: Cow로 불필요한 복사 줄이기

문자열을 정규화하는 함수를 만든다고 해볼게요. 공백을 다듬고 소문자로 바꾸는 작업이 필요한데, 입력이 이미 깔끔할 수도 있습니다. 간단하죠. 그런데 한 가지 찜찜한 점이 있습니다. 입력이 이미 정규화된 상태라면 어떻게 될까요? 아무 변화도 없는데 to_lowercase()가 호출되는 순간 새로운 String이 힙에 할당됩니다. 초당 수백만 번 호출되는 코드라면 이 낭비가 꽤 아프게 다가오죠. "변경이 필요할 때만 복사하고, 그 외엔 빌린 채로 쓰자"는 아이디어를 타입으로 표현한 게 바로 Cow<'a, B>입니다. 이 글에서는 Cow

Rust 기초: Borrow와 ToOwned 트레이트

Rust 기초: Borrow와 ToOwned 트레이트

Rust로 HashMap을 다루다 보면 한 번쯤 이런 의문이 생깁니다. 키로 String을 넣었는데, 조회할 때는 &str을 넘겨도 잘 찾아지는 게 신기하지 않나요? 이게 우연이 아니라 Borrow라는 트레이트가 만들어주는 보장 덕분인데요. 그리고 그 반대쪽, 즉 빌린 값을 다시 소유권이 있는 값으로 되돌리는 역할을 하는 게 ToOwned 트레이트입니다. 이 글에서는 이 두 트레이트가 어떤 계약을 맺고 있고, 왜 짝을 이루는지 알아보겠습니다. AsRef 트레이트와 비슷해 보이지만 미묘하게 다른 지점이 핵심입니다. Borrow 트레이

Rust의 에러 처리: Result, ? 연산자, map_err

Rust의 에러 처리: Result, ? 연산자, map_err

자바스크립트로 파일을 읽거나 파이썬으로 API를 호출해본 경험이 있으시다면, 아마 try/catch나 try/except로 예외를 잡는 방식이 익숙하실 겁니다. 그런데 Rust 코드를 처음 보면 이상한 점이 눈에 들어옵니다. 분명히 파일을 여는데 try도 없고, 함수 시그니처에 throws도 없고, 대신 Result<T, E>라는 낯선 타입이 반환됩니다. 🤔 이번 글에서는 다른 언어와 비교했을 때 Rust 에러 처리의 가장 특이한 지점들과, 실무에서 자주 쓰는 ? 연산자, map_err 같은 도구를 하나씩 풀어보겠습니다. 예외가

Zellij: Rust로 만든 차세대 터미널 멀티플렉서

Zellij: Rust로 만든 차세대 터미널 멀티플렉서

터미널에서 개발하다 보면 창이 부족해지는 순간이 오죠. 개발 서버 하나 돌리고, 로그 보면서, 또 다른 창에서 Git 작업하고, 테스트도 돌려야 하고... 결국 터미널 탭이 열 개쯤 열려 있는 자신을 발견하게 됩니다. 😅 이런 문제를 해결하려고 tmux 같은 터미널 멀티플렉서를 써보신 분들도 계실 텐데요. 솔직히 tmux는 진입 장벽이 꽤 높습니다. 키 바인딩을 외워야 하고, 설정 파일도 복잡하고, 처음 켜면 뭘 어떻게 해야 하는지 막막하죠. Zellij는 이런 고민을 깔끔하게 해결해주는 차세대 터미널 멀티플렉서입니다. Rust로

Rust #[non_exhaustive]로 깨지지 않는 API 만들기

Rust #[non_exhaustive]로 깨지지 않는 API 만들기

라이브러리를 만들다 보면 이미 공개한 열거형에 새 배리언트를 추가해야 할 때가 있습니다. 그런데 열거형에 배리언트를 하나 추가하는 순간 그 열거형을 match로 매칭하던 모든 사용자 코드에서 컴파일 에러가 터져요. 구조체도 마찬가지로 새 필드를 추가하면 구조체를 직접 생성하던 코드가 깨지죠. semver를 지키려면 이런 변경은 메이저 버전을 올려야 합니다. 하지만 에러 타입에 새 종류를 추가하거나 설정 구조체에 옵션 하나를 추가하는 게 정말 파괴적 변경일까요? 🤔 Rust는 이 문제를 해결하기 위해 #[non_exhaustive]

Rust의 dyn 키워드와 동적 디스패치(Dynamic Dispatch)

Rust의 dyn 키워드와 동적 디스패치(Dynamic Dispatch)

트레이트를 사용하다 보면 어느 순간 벽에 부딪히는 상황이 옵니다. 서로 다른 타입의 값을 하나의 벡터에 담고 싶은데 컴파일러가 허락하지 않는 거죠 😅 Dog과 Cat은 둘 다 Animal을 구현하지만 엄연히 서로 다른 타입입니다. Rust의 벡터는 모든 원소가 같은 타입이어야 하니까 이 코드는 동작하지 않죠. 이 문제를 해결하는 열쇠가 바로 dyn 키워드입니다. 그럼 dyn이 뭔지, 그리고 어떻게 쓰는 건지 알아볼까요? 정적 디스패치 Rust는 기본적으로 정적 디스패치(static dispatch)를 씁니다. dyn을 보기 전에

Rust 기초: HashMap으로 키-값 데이터 다루기

Rust 기초: HashMap으로 키-값 데이터 다루기

전화번호부를 떠올려보세요. 이름을 알면 전화번호를 바로 찾을 수 있죠. 프로그래밍에서도 이런 식으로 어떤 키를 가지고 그에 대응하는 값을 빠르게 찾고 싶을 때가 많습니다. 다른 언어에서는 딕셔너리(dictionary)나 맵(map)이라고 부르는 이 자료 구조를 Rust에서는 HashMap이라고 합니다. 이 글에서는 HashMap을 생성하고, 데이터를 넣고 꺼내고, 수정하고 삭제하는 기본 연산부터 Entry API와 소유권 이슈까지 정리해보겠습니다. HashMap 생성 HashMap은 표준 라이브러리의 std::collections

Rust HTTP 클라이언트: reqwest 크레이트 사용법

Rust HTTP 클라이언트: reqwest 크레이트 사용법

웹 API를 호출하거나 외부 서비스와 통신해야 하는 상황은 어떤 언어로 개발하든 빈번하게 마주치게 됩니다. Rust에서는 이런 HTTP 통신을 위해 reqwest라는 크레이트가 사실상 표준처럼 사용되고 있는데요. Python의 requests 라이브러리처럼 직관적인 API를 제공하면서도 Rust답게 타입 안전성과 비동기 처리를 지원하는 것이 특징입니다. 이 글에서는 reqwest 크레이트의 기본적인 사용법부터 실무에서 자주 쓰이는 패턴까지 예제와 함께 살펴보겠습니다. reqwest란? reqwest는 Rust 생태계에서 가장 널리

asciinema로 터미널 녹화하기: 텍스트 기반 경량 스크린캐스트

asciinema로 터미널 녹화하기: 텍스트 기반 경량 스크린캐스트

CLI 도구 사용법을 누군가에게 설명할 때 어떻게 하시나요? 스크린샷을 여러 장 찍어서 붙이거나 OBS로 화면을 녹화해서 영상 파일을 만들 수도 있겠죠. 근데 솔직히 터미널 화면 녹화하자고 동영상 편집 프로그램까지 꺼내는 건 좀 과하잖아요. asciinema(아스키네마)는 이 고민을 깔끔하게 풀어줍니다. 터미널에서 일어나는 입출력을 텍스트로 녹화해서 MP4 같은 무거운 동영상 파일 대신 몇 KB짜리 텍스트 파일을 만들어주거든요. 녹화된 내용에서 텍스트를 복사할 수도 있고 웹 페이지에 임베드하거나 GIF로 변환하는 것도 간단합니다.

Rust 데이터 직렬화: Serde 라이브러리 사용법

Rust 데이터 직렬화: Serde 라이브러리 사용법

Rust는 시스템 프로그래밍 언어이기 때문에 데이터를 외부로 안전하게 내보내거나 받아오는 일이 많은데요. 이러한 데이터 직렬화/역직렬화를 위해서 사실상 표준처럼 사용되는 라이브러리가 Serde입니다. 거의 모든 Rust 프로젝트가 사용되는 크레이트(Crate)라고 봐도 과언이 아니죠. 이 글에서는 왜 Serde 라이브러를 어떻게 사용하는지 예제와 함께 살펴보겠습니다. Serde란? 직렬화는 데이터를 Rust 자료형에서 JSON이나 YAML, TOML 등의 형식으로 변화하는 과정을 의미하고, 역직렬화는 변환된 데이터를 다시 원래대로

Rust 기초: Vec으로 동적 배열 다루기

Rust 기초: Vec으로 동적 배열 다루기

Rust에서 배열([T; N])은 크기가 컴파일 시점에 고정됩니다. [i32; 5]라고 선언하면 딱 5개만 담을 수 있고, 나중에 6번째 요소를 추가할 수가 없죠. 사용자 입력을 모으거나 파일에서 데이터를 읽어오거나 API 응답을 파싱하는 상황처럼 실행 시점에 데이터 개수가 정해지는 경우에는 고정 크기 배열로 해결이 안 됩니다. 이럴 때 필요한 것이 Vec<T>입니다. Vec<T>는 힙에 데이터를 저장하는 동적 배열로, 요소를 자유롭게 추가하고 제거할 수 있습니다. Rust에서 가장 많이 쓰이는 컬렉션 타입이기도 하고요. 이 글에서

Discord