# move 클로저

클로저는 주변 스코프의 변수들을 참조하거나 소유권을 이전받아 사용할 수 있는 특별한 형태의 익명 함수다. 일반적인 클로저는 상황에 따라 Fn, FnMut, FnOnce 트레이트를 충족시키며, 이는 클로저가 캡처하는 방식(불변 참조, 가변 참조, 소유권 이동)에 따라 달라진다. 하지만 Rust는 클로저가 어떤 방식으로 캡처해야 할지를 직접 명시할 수도 있게 해준다. 이때 사용하는 키워드가 move다. move 키워드를 붙인 클로저, 즉 move 클로저는 외부 스코프의 변수에 대한 소유권을 명시적으로 클로저 내부로 이동시키도록 만든다.

move 클로저를 사용하면 클로저 정의부가 끝나는 순간, 즉시 캡처 대상으로 정해진 변수들의 소유권이 클로저로 이동한다. 이는 클로저가 나중에 실행될 때 외부 스코프와는 독립적으로 변수를 가지고 있어야 할 필요가 있을 때 유용하다. 예를 들어, 스레드를 생성할 때 클로저 안에서 외부 변수를 안전하게 사용하고 싶다면 move를 사용해야 한다.

아래 코드를 살펴보자.

```rust
fn main() {
    let s = String::from("Hello, Rust");

    let closure = move || {
        println!("클로저 내부: {}", s);
    };

    // 여기서는 더 이상 s를 사용할 수 없다.
    // println!("{}", s); // 컴파일 에러

    closure(); // 클로저 내부 호출
}
```

위 코드는 move 키워드가 없었다면 클로저는 s를 참조로 캡처하게 된다. 하지만 스레드를 예로 들면, 새로운 스레드는 원래 스코프가 이미 종료된 후에 동작할 수 있으므로 참조로만 사용하면 안전하지 않다. 따라서 move를 붙여 클로저가 s의 소유권을 가져가도록 함으로써 클로저가 독립적으로 s를 사용하게 만든다.

move 키워드는 클로저의 정의 시점에 캡처해야 할 변수를 결정해 소유권을 빼앗아 온다. 이때 "필요한 변수"만을 가져간다. 부분 이동(Partial Move)도 지원된다. 예를 들어 구조체가 여러 필드를 갖고 있을 때, 클로저에서 필요한 필드만을 소유권 이동해서 사용할 수 있다. Rust는 이를 정교하게 계산해서 사용하지 않는 변수에 대해서는 소유권을 이동하지 않는다. 다만, 클로저가 구조체 전체를 참조해야 한다면 전체를 캡처해 갈 수도 있다.

move 클로저를 사용할 때는 Fn, FnMut, FnOnce 등의 트레이트가 어떻게 결정되는지도 중요하다. 클로저는 캡처 방식에 따라 다음과 같이 분류된다.

```rust
FnOnce: 클로저가 캡처한 자원을 반드시 한 번 소모해야 하는 경우 (소유권 이동 등)
FnMut: 클로저가 캡처한 자원을 가변 참조로 사용할 수 있는 경우
Fn: 클로저가 캡처한 자원을 불변 참조로만 사용하는 경우
```

move 클로저라도 어떤 자원을 단지 불변 참조로만 쓰면 Fn으로, 가변 참조로 쓰면 FnMut로, 소유권을 완전히 이전해야 한다면 FnOnce로 동작하게 된다. 다만 move가 붙어 있다면 기본적으로 소유권을 가져오는 형태로 캡처가 일어난다는 점이 차이점이다. 예를 들어 불변 참조로만 읽는 것 같아도, move를 붙이면 외부 변수가 move 클로저 쪽으로 이동된 후 불변 참조로 사용될 수 있다.

다음 코드는 move 키워드로 인해 소유권이 넘어가는 과정을 조금 더 명확히 볼 수 있는 예시다.

```rust
fn main() {
    let mut data = vec![1, 2, 3];

    let closure = move || {
        // data의 소유권이 이 클로저로 넘어온다
        for val in &data {
            println!("값: {}", val);
        }
    };

    // data에 더 이상 접근할 수 없음
    // data.push(4); // 컴파일 에러

    closure();
}
```

move를 쓰지 않았다면 클로저는 외부에서 data를 가변 참조로 캡처할 가능성이 생긴다. 이 경우 클로저가 실행되기 전까지는 data를 계속 다룰 수도 있다. 하지만 외부 스코프에서 data를 다루다가, 클로저와 동시에 접근한다면 스레드 안전성을 보장할 수 없게 된다. move 키워드를 사용함으로써 소유권을 강제 이동시켜 이러한 잠재적 충돌을 제거할 수 있다.

move 클로저는 멀티스레드 환경에서 더욱 빈번히 접한다. Rust에서 std::thread::spawn 함수를 사용할 때는 대부분 클로저에 move를 붙여야 한다. 스레드 안에서 사용할 데이터를 클로저가 가진다면, 그 데이터는 스레드의 생명주기 동안 안전하게 유지되어야 한다. 그렇지 않으면 외부 스코프가 사라진 뒤에 해당 변수가 존재하지 않을 수 있어 문제가 발생한다. 따라서 별도의 소유권을 생성된 스레드가 갖도록 하기 위해 move 키워드를 사용한다.

아래 코드는 std::thread::spawn의 간단한 예시다.

```rust
use std::thread;
use std::time::Duration;

fn main() {
    let s = String::from("스레드 예시");

    let handle = thread::spawn(move || {
        println!("스레드 내부: {}", s);
        thread::sleep(Duration::from_secs(1));
    });

    // 메인 스레드가 여기서 종료되어도 새로운 스레드는 s를 안전하게 사용 가능
    // 만약 move가 없었다면 외부 스코프의 s를 참조하게 되어 위험할 수 있다.

    handle.join().unwrap();
}
```

여기서 만약 move 키워드를 빼면, s는 메인 스레드에서 생성된 변수이므로 스레드가 종료되기 전에 s가 스코프를 벗어날 수도 있다. Rust는 이런 상황을 미리 막기 위해 thread::spawn에 전달하는 클로저에는 move를 명시적으로 요구하는 편이며, 컴파일러는 move 없이 작성된 클로저에 대해서도 에러를 발생시킨다.

정리하면 move 클로저는 외부 스코프의 변수 소유권을 클로저 내부로 옮김으로써, 해당 변수의 생존 범위와 안전성 문제를 해결하기 위해 사용된다. 멀티스레드나 비동기 환경에서 자원을 안전하게 클로저 내부로 넘기는 방식이 필요할 때 주로 사용하며, 클로저 정의 시점에 어떤 변수를 소유해야 하는지, 어떤 것은 참조로 남길 수 있는지 등을 컴파일러가 자동으로 결정하여 최적화한다. 이를 잘 이해하면 더 복잡한 스레드 기반 코드를 작성할 때도 소유권 문제를 쉽게 다룰 수 있게 된다.
