claude code를 활용해 개발을 하는데 error가 계속 발생하여 직접 해결한 내용이다.
rust로 axum framework를 사용하여 api 서버를 만들고 있는데 route 등록 시
error[E0277]: the trait bound `fn(State<DbClient>, ...) -> ... {download_board_pdf}: Handler<_, _>` is not satisfied
--> src\main.rs:44:38
|
44 | .route("/api/board-pdf", get(handlers::download_board_pdf))
| --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(State<DbClient>, Query<...>) -> ... {download_board_pdf}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
= help: the following other types implement trait `Handler<T, S>`:
`MethodRouter<S>` implements `Handler<(), S>`
`axum::handler::Layered<L, H, T, S>` implements `Handler<T, S>`
note: required by a bound in `axum::routing::get`
--> C:\Users\PC\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\axum-0.8.8\src\routing\method_routing.rs:441:1
위와 같은 괴로운 에러가 발생했다. 클로드 코드가 뱉어준 코드라서 어떻게 내부가 동작하는지 확인하지 않아서 당황스러웠다. 구글링을 통해서 axum crates features에 macros를 추가하여 #[axum::debug_handler]을 붙여주면 문제점을 더 디테일하게 볼 수 있다는 것을 알았다.
[참조 사이트]
https://stackoverflow.com/questions/79406748/axum-the-trait-handler-is-not-implemented-for-fn-item

cargo.toml 파일에 설정해 주었다.

문제가 발생한 handler에 어트리뷰트를 설정해주었고 빌드를 해보았다.
error: future cannot be sent between threads safely
--> src\handlers.rs:290:1
|
290 | #[axum::debug_handler]
| ^^^^^^^^^^^^^^^^^^^^^^ future returned by `download_board_pdf` is not `Send`
|
= help: within `impl futures::Future<Output = impl IntoResponse>`, the trait `std::marker::Send` is not implemented for `Rc<UnsafeCell<ReseedingRng<rand_chacha::chacha::ChaCha12Core, OsRng>>>`
note: future is not `Send` as this value is used across an await
--> src\crawler.rs:69:43
|
64 | let mut rng = rand::thread_rng();
| ------- has type `ThreadRng` which is not `Send`
...
69 | sleep(Duration::from_secs(delay)).await;
| ^^^^^ await occurs here, with `mut rng` maybe used later
뱉어낸 에러를 확인하니 src\crawler.rs:69:43 에 있는 random_delay함수가 문제라고 알려주었다.
/// 랜덤 대기 (IP 밴 방지)
async fn random_delay(&self) {
let mut rng = rand::thread_rng();
let delay = rng.gen_range(self.config.min_delay_secs..=self.config.max_delay_secs)
sleep(Duration::from_secs(delay)).await;
}
rand::thread_rng() 함수를 타고 들어가니 내부적으로
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))]
#[derive(Clone, Debug)]
pub struct ThreadRng {
// Rc is explicitly !Send and !Sync
rng: Rc<UnsafeCell<ReseedingRng<Core, OsRng>>>,
}
Rc를 사용하는 것을 볼 수 있다. Rc는 단일 스레드에서 사용하는 스마트 포인터이므로 Send trait이 구현되지 않았다.
handler 함수는 async로 처리되므로 future로 래핑이 되어서 반환이 되는데 내부에 Send가 구현되지 않은 변수가 있어서 문제가 발생한 것 같다.
rng 객체를 통해 랜덤 값을 얻은 후 바로 drop이 되도록 scope를 활용하였다.
/// 랜덤 대기 (IP 밴 방지)
async fn random_delay(&self) {
let delay = {
let mut rng = rand::thread_rng();
rng.gen_range(self.config.min_delay_secs..=self.config.max_delay_secs)
};
sleep(Duration::from_secs(delay)).await;
}
문제없이 빌드가 되는 것을 확인하였다.
claude code를 활용하여 바이브 코딩을 하고 있는데 완벽하지는 않은 것 같다. 만약 rust에 익숙하지 않거나 에러를 직접 해결해 본 경험이 없다면 쉽지 않은 문제였을 것 같다. 계속 개발만 시켰는데 중간중간 코드에 대해 이해하는 시간이 필요할 것 같다. 그리고 문제가 발생했을 때 직접 작성한 코드가 아니라서 문제를 파악하는 것보다 코드를 파악하는 시간이 더 오래 걸리는 것 같다. 아직은 온전히 ai agent에게 맡기는 것보다는 기능 하나씩 구현 후 검증하는 시간을 가지는 것이 좋을 것 같다.