# Rust에서 WIP 코드를 경고로 남기기

> Clean Markdown view of GeekNews topic #31182. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=31182](https://news.hada.io/topic?id=31182)
- GeekNews Markdown: [https://news.hada.io/topic/31182.md](https://news.hada.io/topic/31182.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-07-07T01:04:28+09:00
- Updated: 2026-07-07T01:04:28+09:00
- Original source: [blog.dureuill.net](https://blog.dureuill.net/articles/wip/)
- Points: 1
- Comments: 1

## Topic Body

- Rust 개발 중 모든 오류 경로와 소유권 문제를 즉시 정리하면 구현 흐름이 끊기기 쉬워, **happy path**를 먼저 만들고 남은 일을 **경고**로 추적하는 전략이 유용함
- 표준 Rust만으로도 `unwrap`, `clone`, `todo!`, 기본 컴파일러 경고, `///` 문서 주석 트릭을 조합해 미완성 코드를 표시할 수 있음
- 기존 방식은 `unwrap`과 `clone`이 경고를 내지 않고, `todo!`도 간접 경고에 의존하며, `///` 트릭은 위치에 따라 컴파일 오류나 의도치 않은 문서화로 이어질 수 있음
- `wip` crate는 `wip!`, `unwrap_wip`, `clone_wip`, `fixme!`를 통해 임시 구현, 임시 `unwrap`/`clone`, 나중에 고칠 메모를 **항상 경고**로 드러냄
- Meilisearch는 CI에서 `-D warning`으로 경고를 오류로 바꿔 WIP 코드 유입을 막고, 개발 중 추가한 `wip` 의존성은 리뷰 전 히스토리 정리 과정에서 제거하는 흐름을 사용함

---

### Rust 개발 중 경고가 필요한 이유
- Rust는 **정확성**을 중시하는 언어지만, 개발 도중 모든 오류 경로를 바로 처리하면 구현 속도와 집중이 떨어질 수 있음
- `rustc`가 `Result` 반환, 오류 처리, 엣지 케이스 구현 같은 이유로 컴파일을 막을 때 보통 두 선택지가 생김
  - 즉시 완전한 오류 처리를 구현하고, PR 준비 전까지 여러 번 리팩터링함
  - `TODO later` 성격의 표시를 남겨 문제를 **경고** 수준으로 낮추고 현재 작업을 이어감
- 경고는 개발을 막지 않으면서도 PR 병합 전 정리해야 할 신호로 남음
- Meilisearch는 CI에서 컴파일러를 `-D warning` 플래그로 실행해 모든 경고를 오류로 바꾸며, WIP 코드가 실수로 배포되지 않도록 함
- 코드 리뷰는 버그를 막고 팀 내 지식을 공유하는 기본 관행이지만, 사람의 기억과 주의력에만 의존하면 결국 실수가 생길 수 있어 **추가 가드레일**이 필요함

### 표준 Rust로 미완성 처리를 미루는 방법
- ## `unwrap`으로 오류 처리 미루기
  - `Result`나 `Option`에 `unwrap`을 사용하면 오류 경로에서 panic이 발생하는 대가로 내부 값을 꺼내 현재 구현을 계속할 수 있음
  - 나머지 코드가 준비된 뒤 임시로 추가한 `unwrap`을 골라 제거하고 적절한 오류 처리로 바꿀 수 있음
  - 이 방식은 Rust가 오류를 올바르게 다룰 구조를 제공하더라도, 그 처리를 **즉시** 끝낼 필요는 없다는 전제에 기반함
- ## `clone`으로 소유권 문제 미루기
  - 소유권 문제는 오류 처리보다 덜 흔하지만, 올바른 소유권 유지가 설계 변경을 요구할 수 있음
  - 이때 값을 이동시키는 대신 `clone`해 생명주기 문제를 임시로 피할 수 있음
  - 나중에 적절한 변수를 더 상위 스택으로 전달하는 방식으로 clone을 제거할 수 있는 경우를 경험적으로 식별할 수 있음
  - 다만 수정이 코드 구조 변경으로 이어질 수 있어, 오류 처리보다 미루기가 더 까다로움
- ## `todo!()`로 구현 자리만 확보하기
  - Rust 표준 라이브러리의 [`todo!()`](https://doc.rust-lang.org/std/macro.todo.html) 매크로는 미완성 코드를 나타내며, 프로토타이핑 중 타입 분석을 통과시키기 위한 자리표시자로 유용함
  - `todo!()`는 panic으로 diverge하기 때문에 대부분의 타입 위치에서 자리표시자 표현식으로 사용할 수 있음
  - 인터페이스를 잡는 중 함수 정의를 사용할 수 있게 하면서 실제 구현은 뒤로 미룰 수 있음
  - `todo!()`는 프로그램 사용자에게 기능 미구현을 알리기보다, 코드 작성자와 리뷰어를 위한 **WIP 편의 기능**에 가까움
  - 사용자에게 기능 미구현 상태를 알리는 용도에는 [`unimplemented!()`](https://doc.rust-lang.org/std/macro.unimplemented.html) 매크로가 따로 있음
- ## 기본 컴파일러 경고와 문서 주석 트릭
  - Rust 컴파일러의 기본 경고도 개발 중 미완성 상태를 드러내는 데 유용함
    - 사용하지 않는 바인딩
    - 불필요한 `mut`
    - 사용하지 않은 `Result`와 `Option`
  - 개발 중 이런 경고를 `_` 접두사나 다른 트릭으로 **임시 제거**하면 미완성 코드가 그대로 넘어갈 위험이 있음
  - 타입 시스템으로 추적되지 않는 엣지 케이스나 깨진 로직은 주석으로 남기는 경우가 있음
  - `///` 문서 주석처럼 보이게 주석을 달면, Rust가 문서화 대상이 없는 문서 주석에 대해 경고를 내므로 해당 위치의 문제를 경고로 추적할 수 있음

### 표준 방식의 한계
- 가장 큰 문제는 모든 기법이 해결해야 할 문제와 연결된 경고를 안정적으로 만들지 못한다는 점임
- `todo!()`는 자주 경고를 유발하지만, 함수 인자가 사용되지 않거나 필요한 mutation이 일어나지 않는 등 **간접 원인**에서 나오는 경우가 많음
- `unwrap`과 `clone`은 경고를 전혀 내지 않으므로, 최종 PR 전에 다시 읽으며 제거해야 함
- `unwrap`은 정상적으로 프로덕션에 남을 수도 있어, 임시 WIP 용도와 일반 용도가 섞일 수 있음
- `///` 문서 주석 트릭은 해킹에 가까우며, 표현식 위치에서는 문서 주석이 금지되어 컴파일 오류가 날 수 있음
- 실제로 문서화가 되는 위치에 `///`를 쓰면 기대한 경고가 나오지 않고, 배포 코드에 원치 않는 문서가 남을 수 있음

### `wip` crate가 제공하는 도구
- [`wip`](https://codeberg.org/dureuill/wip/) crate는 Rust 개발 중 “나중에 처리할 경고” 전략을 더 편하게 만들기 위한 도구 모음임
- 일반 crate처럼 추가할 수 있음
  - `cargo add wip`
- 필요한 파일에서는 prelude를 가져올 수 있음
  - `use wip::prelude::*;`
- `wip`는 Rust의 deprecation warning에 의존하므로, 에디터가 deprecated 함수 호출에 취소선을 긋는 기능은 끄는 편이 덜 거슬릴 수 있음
- ## `wip::wip!`
  - `wip!` 매크로는 `todo!`처럼 동작하지만, 사용할 때 항상 **경고**를 냄
  - 미완성 코드를 표시하기 위해 `todo!`를 쓰던 위치에 사용할 수 있음
  - 초기 버전에서는 `todo`라는 이름을 썼지만, 의존성이 `std` 매크로를 shadow하려 할 때 Rust가 이를 선호하지 않아 prelude 사용에 문제가 생겼음
- ## `unwrap_wip`와 `clone_wip`
  - `unwrap_wip`와 `clone_wip`는 `Result`와 `Option`에 대한 extension trait로 구현됨
  - scope 안에 있으면 일반 `unwrap`, `clone`처럼 동작하지만 사용 시 경고를 냄
  - 최종 코드에 `unwrap`이나 `clone`을 남기려는 의도가 아니라는 신호를 명확히 줄 수 있음
- ## `wip::fixme!`
  - `fixme!` 매크로는 `wip!`의 non-panicking 변형이며 표현식을 대신하지 않음
  - TODO 주석처럼 쓸 수 있지만, `///` 트릭 없이 안정적으로 경고를 생성함
  - 나중에 고쳐야 할 메모를 남기는 용도로 자주 사용됨

### 사용 흐름과 주의점
- `wip` crate는 최근 몇몇 PR에서 사용됐고, 큰 PR 중 하나인 [Meilisearch PR #6484](https://github.com/meilisearch/meilisearch/pull/6484)에도 사용됨
- 일반적인 흐름은 개발 중 워크스페이스의 crate에 `wip` 의존성을 추가하고, 리뷰 전 히스토리를 다시 쓰는 과정에서 제거하는 방식임
- `wip`는 happy path에 집중하면서 나중에 처리할 일을 잊지 않게 해줌
- 단점도 있음
  - `fixme`를 추가할 때 충분한 맥락을 쓰지 않으면 나중에 고치기 어려움
  - 큰 PR에서는 `wip`가 만든 경고 수가 많아져 부담스러울 수 있음
- 현재 API는 [docs.rs의 `wip` 문서](https://docs.rs/wip/latest/wip/)에서 확인할 수 있음

## Comments



### Comment 61339

- Author: neo
- Created: 2026-07-07T01:04:29+09:00
- Points: 1

###### [Lobste.rs 의견들](https://lobste.rs/s/qu1bwq/work_progress_rust) 
- 솔직히 `wip`의 근거를 보면, 도구를 원래 어떻게 써야 하는지 이해하지 못해서 **Clippy를 재발명**했다는 인상을 받음  
  도구체인 개발자들은 무엇이 `rustc` 경고가 되고 무엇이 Clippy 린트가 되는지 구분해 왔는데, `wip`는 기존 기능을 새 이름으로 다시 만들어 `cargo clippy`에 있던 린트를 `cargo check`와 `cargo build` 쪽으로 억지로 넘기는 듯함  
  예를 들어 `wip!` 설명은 새 의존성과 비표준 구문을 추가하는 대가로 `#![warn(clippy::todo)]`를 `cargo clippy` 밖에서도 뜨게 하는 것이고, `unwrap_wip`도 `#![warn(clippy::unwrap_used)]`와 비슷함  
  내게 `.unwrap()`은 프로토타이핑용이라 `#![warn(clippy::unwrap_used)]`를 켜고, `.expect("description of invariant")`는 `.unwrap_wip()` 대신 `.unwrap()`을 쓸 법한 프로덕션용이라 `#![warn(clippy::expect_used)]`는 켜지 않음. 예: `.expect("parse regex from const")`  
  `clone_wip`와 `fixme!`는 잘 모르겠고, 그런 사례를 개발자들이 어떻게 다루라고 의도했는지 답할 만큼 생각해 본 적은 없음
  - 솔직한 말일 수는 있지만 그렇게 친절한 표현은 아님 :-) 이 분야를 10년 동안 써 왔고, 물론 모든 걸 안다는 뜻은 아니지만 **선의의 해석**은 해줄 수 있지 않나 싶음  
    다만 글에서 Clippy 같은 기존 린터를 다루는 논의가 빠졌다는 점은 맞음. 개인적인 사용 방식과도 관련 있는데, 예전에는 저장할 때 Clippy를 돌렸지만 우리 프로젝트에서는 너무 느려서 저장 사이에 몇 분씩 걸려 그만뒀고, 지금은 CI와 푸시 전 훅에서만 돌림  
    경고를 `rustc`로 끌어오면 훨씬 빠르고, 반복 작업 중에 인라인 경고를 볼 수 있어서 이 워크플로의 핵심이 됨  
    Clippy 설정도 적어도 마지막으로 확인했을 때는 꽤 번거로웠음. 워크스페이스의 모든 크레이트에 린트를 코드로 한 번에 지정하는 방법이 없어서, 워크스페이스에서 거부하고 싶은 린트마다 CI에 명시적인 `-D`를 추가하거나 모든 크레이트에 전역 속성을 계속 넣어야 했음  
    `#![warn(clippy::todo)]`는 모든 크레이트에 필요하므로, 개발 중에만 크레이트를 추가/제거하는 것보다 우리에게 큰 이점은 아님. `todo!`는 기본적으로 `rustc`나 Clippy에서 경고해야 한다고 생각하고, 그렇게 되면 `wip`의 유용성이 줄어든다는 데 동의함  
    `unwrap_wip`가 `#![warn(clippy::unwrap_used)]`라는 말에는 두 가지 면에서 동의하지 않음. 첫째, 의도 측면에서 `unwrap`은 비프로덕션, `expect`는 프로덕션이라는 구분은 흔하지만 보편적이지 않음. 둘째, 기존 코드베이스에는 약 **4600개의 unwrap**이 있고, 전부 `expect`로 바꿀 수도 있지만 그럼 곳곳에 임시 메시지를 넣거나 “이 뮤텍스는 poison되지 않았다”, “이 작업 실패를 호출자에게 전파하고 싶다” 같은 전제 조건을 다시 구성하는 데 너무 많은 시간을 써야 함  
    `fixme!`는 매우 유용하고, 표준 라이브러리 등에 들어가면 좋겠음. 실제로 이 크레이트에서 가장 많이 쓰는 기능임  
    글에서는 이런 세부사항을 다루지 않았지만, 이 크레이트가 표준이 아니라 특화 라이브러리라서 `wip_iter` 같은 기능도 있음. 반복자의 반환 위치 `impl Trait` 함수에서 `todo` 같은 매크로를 쓸 수 있게 해주고, `wip_future`도 마찬가지임. 반환 타입 추론과 `todo`가 함께 동작하지 않아 억지로 타입을 만들어야 했던 일이 꽤 많았음. 누군가는 릴리스 모드에서 기본적으로 컴파일 오류를 내자고도 제안했고, 다른 개선 아이디어와 함께 살펴볼 예정임  
    새 의존성과 비표준 구문이라는 비용을 조금 균형 있게 보자면, 이건 개발 중에만 쓰고 끝날 때 보통 제거하는 단일 파일 의존성임

- `unwrap`으로 프로토타이핑할 때의 문제는 나중에 오류를 제대로 도입하려면 **반환 타입의 형태**가 바뀔 수 있고, 특히 그 함수들이 함수형 스타일로 쓰이면 연쇄 영향이 생긴다는 점임  
  오류 타입은 `Box&lt;dyn Error&gt;`, 문자열, `anyhow` 등을 써서 `Result`로 시작하는 걸 권함. 나중에 진짜 오류 열거형으로 바꾸는 건 가능하지만, 실패하지 않는 것처럼 생긴 코드를 나중에 `Result` 반환으로 리팩터링하기는 더 어려움  
  또한 LLM을 쓴다면 조심해야 함. LLM은 패턴 매칭 기계라서 “일부러 대충 했고 나중에 고치고 싶다”와 “이 프로젝트는 오류를 이렇게 처리해야 한다”를 구분하지 못함. 시간이 지나면 “일단 지금만”의 결정을 증식시킬 수 있으니, main에 주석 한두 개로 어디서/언제 느슨한 오류 처리가 괜찮은지 안내해 두면 도움이 됨

- `todo!()`와 `unimplemented!()`에 대한 해석에는 나와 문서가 동의하지 않음. 다만 생략한 다음 문단이 그 입장을 어느 정도 균형 있게 보완하긴 함  
  문서에는 `todo!`는 나중에 구현하겠다는 의도를 전달하고 메시지가 “not yet implemented”인 반면, `unimplemented!`는 그런 약속을 하지 않고 메시지가 “not implemented”라고 되어 있음  
  `todo!`가 [1.40.0에 추가됐을 때](https://doc.rust-lang.org/1.40.0/std/macro.todo.html)는 순전히 더 짧은 이름이었고, 이름 외에는 `unimplemented!`와 차이가 없었음  
  1.42.0에서야 `unimplemented` 메시지에서 “yet”이 빠졌음. 기억하기로는 두 매크로의 이런 의미를 정하는 것도 약간 논쟁적이었고, `todo!`를 `unimplemented!`의 짧은 표기로 배포한 것도 마찬가지였음  
  요즘 `unimplemented!()`는 기술적인 이유로 메서드 본문을 제공해야 하지만 절대 호출되어서는 안 되는 트레이트 구현 같은 곳에 권장됨. [문서의 예시](https://doc.rust-lang.org/std/macro.unimplemented.html)도 바로 그런 형태임

- 개발 중에는 워크스페이스의 크레이트에 `wip` 의존성을 추가했다가 리뷰 전에 히스토리를 다시 쓰며 제거한다는 흐름은 추가 도구로 좀 더 매끄럽게 만들 수 있을지 궁금함  
  PR 중간을 수정하면서 **락 파일 재작성**까지 해야 하는 건 조금 꺼려짐. 그 사이 의존성 트리가 다른 곳에서도 바뀌었다면 불필요한 충돌이 생길 수 있음. `jj fix`를 남용할 수 있을지도 모르겠음. 어쨌든 프로젝트 아이디어는 아주 좋고, 어디선가 쓰게 될 것 같음

- `wip::fixme!`처럼 패닉을 내지 않는 메모용 매크로는 정말 좋은 아이디어임. **문서 주석 트릭**도 기억해 둬야 할 깔끔한 방법임

- `rustc` 쪽 변화로 이어지면 좋을 흥미로운 아이디어들이 있음. `todo!()`는 정말 **기본 경고**가 떠야 함  
  개발자가 해킹 모드에 있을 때 이런 경고를 끌 수 있으면 더 좋겠음. 경고가 너무 많으면 압도적이고, 보통 IDE 아래쪽의 19줄 콘솔에서 `cargo`를 돌림. 개발 중 경고가 실제 오류와 더 중요한 경고를 묻어버림. 항상 오류를 마지막에 정렬하는 규율만 있어도 이 문제를 완화할 수 있음  
  또 `.unwrap_wip()`와 `.clone_wip()` 이름은 원래 이름보다 길어서 너무 김. “dev unwrap”과 “dev clone”의 줄임말로 `.du()`와 `.dc()` 같은 이름을 추천함. 빠른 프로그래밍에서 더 쓰기 쉬워질 것임

- 필요한 곳에 `.clone()`과 `.unwrap()`을 **자동으로 추가**해 줄 수는 없을까? 예를 들어 `#[wip(autoclone,autounwrap)]` 같은 플래그나 기능으로 말임. 아마 안 되겠지만, 꿈은 꿀 수 있음

- 새 프로젝트나 프로토타입을 시작할 때마다 `main.rs`에 다음 줄을 넣음:  
  `#![allow(dead_code)]`  
  `#![allow(unused_variables)]`  
  `#![allow(unreachable_code)]`  
  초기 단계에서 많은 잡음을 줄여줌
