Rust 타입 시스템의 마법
Rust의 장점 중 하나는 강력한 타입 시스템을 지원한다는 것이다.
이러한 Rust의 특징을 이용해 Type-driven Design을 도입해볼 수 있는데, <Let's Get Rusty>라는 유튜브 채널의 영상 내용을 빌려왔다.
Invaraint
Invariant란 컴퓨터 프로그램의 실행중 항상 참이되는 조건이다.
다음과 같은 BankAccount 구조체가 있다고하면, balance를 사용하려할때마다 코드 곳곳에서 balance가 음수인지 체크하게 될 것이다. 한 곳에 모여있다면 그나마 다행이지만 여러 군데 흩뿌려져 있거나... 검사하고 또 검사하는 상황도 벌어진다.
struct BankAccount {
balance: i32,
}
BankAccount의 Invariant는 balance를 Unsigned Integer로 바꿈으로써 확보할 수 있다.
struct BankAccount {
balance: u32,
}
Type-driven Design
Type-driven Design은 타입 시스템을 활용하여 Invariant를 강제하는 프로그래밍 기조다. 이를통해 프로그램의 안정성과 신뢰성을 높이고, 오류 가능성을 줄인다.
#[derive(Debug)]
struct User {
pub email: String,
pub password: String,
}
영상에 나오는 코드 한 조각을 캡쳐했다. 사용자로 부터 유저 정보를 입력받아 DB에 저장하는 API인데,
Form의 내용을 신뢰할 수 없으니 유효성 검사를 하고있다.
나도 이렇게 개발하고 있고, 일반적으로는 거리낌없이 저렇게 코드를 작성한다.
악몽은 다음 부분에서 시작된다.
insert_user()는 DB에 넣기전에 "유효성 검사가 되어있다고 생각되는" User 구조체를 받는다.
이건 믿음의 영역이다. 여기에 들어온 User 구조체의 이메일과 비밀번호는 유효하다고, 앞단에서 검사했을 것이라고 가정해야한다. 혹여나 register_user() 함수를 리팩토링하다 빼먹을 수 있지만 말이다.
조금 더 꼼꼼하게 하고 싶은 프로그래머는 사진처럼 insert_user() 안에서 한 번 더 유효성 검사를 수행하기도 할 것이다. register_user()에서 실행하지만 말이다...
"Parse, don't validate"
Type-driven Design이 그래서 이 상황을 어떻게 해결하지? 궁금했다.
흥미롭게도 "파싱해라, 유효성 검사 말고"라는 신조에따르면 이렇게 전개된다.
#[derive(Debug)]
struct User {
pub email: Email,
pub password: Password,
}
pub struct Email(String);
put struct Password(String);
User 구조체에서 기존에 String이었던 이메일과 비밀번호는 Email, Password라는 새로운 타입으로 정의했다. (이런 패턴을 "Newtype Pattern"이라고 한다)
impl Email {
pub fn parse(email: String) -> Result<Email, AuthError> {
if !is_valid_email(&email) {
Err(AuthError::ValidationError("Email must be valid".to_string()))
} else {
Ok(Email(email))
}
}
}
impl Password {
pub fn parse(password: String) -> Result<Password, AuthError> {
if !is_valid_password(&password) {
Err(AuthError::ValidationError("Password must be valid".to_string()))
} else {
Ok(Password(password))
}
}
}
Email과 Password 구조체는 parse() 메소드를 가진다.
이로서 User 구조체는 유효성이 통과된 이메일과 비밀번호 데이터만 가질 수 있도록 Invariant가 확보되었다.
여기저기서 유효성 검사를 했던 이전 코드와는 달리, Type-driven Design을 도입하여 훨씬 안정성있는 형태가 되었다. 다소 강박적이라는 생각도 들 수 있지만, 타입 시스템이 주는 안정감은 이루말할 수 없다.