프로그래밍Rust 애플리케이션/라이브러리 아키텍트

러스트의 매크로는 무엇인가요? 매크로의 종류, 프로세듀럴과 선언적 매크로의 차이점, 각각을 언제 선택하는 것이 좋은지, 사용 시 발생할 수 있는 위험은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변

러스트의 매크로는 컴파일 단계에서 코드를 생성할 수 있게 해주는 메타 프로그래밍, 보일러플레이트 감소, DSL 구현을 위한 강력한 도구입니다. 주요 매크로 종류는 다음과 같습니다:

  • 선언적 매크로 (매크로 규칙, macro_rules!): match와 유사한 구문으로 정의되며, 패턴->대체 원리에 따라 작동합니다. 가장 전형적인 유형은 macro_rules!입니다.
  • 프로세듀럴 매크로: crate의 외부 함수로 선언되며, AST (TokenStream)를 받아 수정된 코드를 반환합니다. #[derive], 속성 (#[some_macro]), 함수와 유사한 (custom_macro!())으로 구분됩니다.

선언적 매크로는 템플릿 코드에 사용하기 더 간편하고, 프로세듀럴 매크로는 구문 및 토큰 분석에서 더 많은 제어권을 제공합니다.

선언적 매크로 예시:

macro_rules! vec_of_strings { ($($x:expr),*) => { { let mut v = Vec::new(); $(v.push($x.to_string());)* v } }; } let v = vec_of_strings!("a", "b"); // => Vec<String>

프로세듀럴 매크로 (derive 예시):

#[derive(Debug, Clone)] struct MyStruct; // 자체 derive Debug는 std의 프로세듀럴 매크로로 구현되었습니다.

함정 질문

러스트 매크로가 구문적으로 잘못된 코드나 런타임 오류가 있는 코드를 생성할 수 있나요?

답변: 네, 매크로는 작성 시 전개 유효성을 검사하지 않기 때문에, 오류는 컴파일러가 매크로를 치환한 후에만 나타날 수 있습니다. 선언적 매크로는 의외의 구문 오류를 초래할 수 있습니다. 프로세듀럴 매크로는 잘못되거나 취약한 코드를 생성할 수 있기 때문에 반드시 철저히 테스트해야 합니다.

예시:

macro_rules! make_error { () => { let x = ; // 매크로 사용 시 구문 오류가 발생합니다. } }

주제에 대한 지식 부족으로 인한 실제 오류 사례


이야기

대규모 프로젝트에서 보일러플레이트 감소를 위해 macro_rules!를 사용했는데 모든 패턴 경우를 커버하지 않았습니다. 사용자가 매크로에 지원되지 않는 표현식을 전달하여 이해할 수 없는 컴파일 오류가 발생했으며, 그 원인을 추적하기 어려웠습니다.


이야기

크레이트 간에 프로세듀럴 매크로를 이동하는 과정에서 TokenStream API 버전 간의 호환성 문제가 발생하여 IDE가 중단되었고, 오류는 no_std 빌드에서만 나타났습니다.


이야기

프로세듀럴 매크로를 사용하여 구성 파일을 위한 DSL을 작성하는 과정에서 입력 토큰의 안전하지 않은 구문 분석(유형 검증 없이)이 구현되어 런타임 오류, 취약점 및 새로운 기능 일부의 올바른 배포가 불가능하게 되었습니다.