Programming/Rust

러스트에서의 방어적 프로그래밍 패턴

2mukee 2025. 12. 20. 16:59
320x100
320x100

방어적 프로그래밍의 개요

// this shoud never happen 주석이 붙은 지점은 암묵적 불변식이 깨지는 위치임

대부분의 경우 개발자가 모든 경계 조건이나 미래 코드 변경을 고려하지 않음

러스트 컴파일러는 메모리 안정성을 보장하지만, 비즈니스 로직 오류는 여전히 발생 가능

 

 

 

 

Code Smell

겉보기엔 정상적으로 동작하지만, 앞으로 문제가 발생할 수 있는 코드를 의미

 

 

 

 

Code Smell: 벡터 인덱싱

if !vec.is_empty() { let x = &vec[0]; } 형태는 길이 확인과 인덱싱이 분리되어 런타임 패닉 위험 존재

슬라이스 패턴 매칭(match vec.as_slice())을 사용하면 컴파일러가 모든 상태를 강제 검사

빈 벡터, 단일 원소, 중복 원소 등 모든 경우를 명시적으로 처리 가능

컴파일러가 불변식을 보장하도록 설계하는 대표적 예시

 

 

 

 

Code Smell: Default의 무분별한 사용

..Default::default()는 새 필드 추가 시 누락 위험과 암묵적 값 설정 문제를 초래

모든 필드를 명시적으로 초기화하면 컴파일러가 새 필드 설정을 강제

let Foo { field1, field2, .. } = Foo::default(); 형태로 기본값 구조 해체 후 선택적 재정의 가능

기본값 유지와 명시적 오버라이드의 균형 확보

 

 

 

 

Code Smell: 취약한 Trait 구현

_ => {} 와 같은 catch-all 패턴은 새 variant 추가 시 누락 위험

모든 variant를 명시적으로 나열하면 컴파일러가 새 케이스 처리 누락을 경고

동일 로직은 Variant3 | Variant4 형태로 그룹화 가능

 

 

 

 

Code Smell: _ 플레이스 홀더 남용

_만 사용하면 어떤 변수가 생략됐는지 불명확

has_fuel: _, has_crew: _처럼 명시적 이름으로 가독성 향상

 

 

 

 

Code Smell: 불리언 매개변수

fn process_data(..., compress: bool, encrypt: bool, validate: bool) 형태는 의미가 불명확하고 순서 오류에 위험이 있음

enum Compression, enum Encryption 등으로 의도를 명시적 표현

여러 옵션이 있는 경우 파라미터 구조체(Params struct) 사용

ProcessDataParams::production() 등 사전 설정 메서드로 재사용성 향상

새 옵션 추가 시 기존 호출부 영향 최소화

 

 

 

 

Pattern: 임시 가변성

데이터가 초기화 중에만 가변이어야 할 때, let mut data = ...; data.sort(); let data = data; 형태 사용

블록 스코프를 활용하면 임시 변수의 외부 노출 방지

let data = { let mut d = get_vec(); d.sort(); d };

여러 임시 변수를 사용하는 초기화 과정에서 명확한 범위 구분 가능

 

 

 

 

Pattern: 생성자 검증 강제

구조체 생성 시 검증 로직을 반드시 거치도록 강제

_private: () 필드 추가 시 외부에서 직접 생성 불가

#[non_exhaustive] 속성은 크레이트 외부 생성 차단 및 미래 확장 신호

내부 모듈에서도 강제하려면 비공개 타입(Seal)을 가진 중첩 모듈 구조 사용

Seal이 내부에만 존재해 new() 외 직접 생성 불가

필드를 비공개로 두고 getter 제공 시 불변 상태 유지

 

 

 

 

Patern: #[must_use] 속성 활용

#[must_use]는 중요한 반환값 무시를 방지

#[must_use = "Configuration must be applied to take effect"]

사용자가 반환 값을 무시하면 컴파일러 경고 발생

Result 등 표준 라이브러리에서도 널리 사용되는 간단하지만 강력한 방어 수단

 

 

 

 

Clippy Lints로 자동화

주요 방어 패턴을 Clippy 린트로 자동 검사 가능

#![deny(clippy::...)] 또는 Cargo.toml 설정으로 프로젝트 전역 적용 가능

 

- indexing_slicing

직접 인덱싱 금지

 

- fallible_impl_from

From 대신 TrypFrom 권장

 

- wildcard_enum_match_arm

_ 패턴 금지

 

- fn_params_excessive_bools

불리언 매개변수 과다 경고

 

- must_use_candidate

#[must_use] 후보 제안

 

 

 

 

 

Reference

https://news.hada.io/topic?id=24880

 

Rust에서의 방어적 프로그래밍 패턴 | GeekNews

러스트의 타입 시스템과 컴파일러를 적극 활용해 버그를 사전에 차단하는 코딩 습관을 소개벡터 인덱싱, Default 남용, 불완전한 match, 불필요한 불리언 매개변수 등 취약한 코드 냄새(Code Smell) 사

news.hada.io

 

300x250
728x90