타입스크립트 패턴 매칭
패턴매칭
- 특정 패턴에 맞는 데이터를 찾거나 추출하는 기능은 많은 함수형 프로그래밍 언어에서 사용되는 개념
- Haskell, OCaml, Erlang, Rust, Swift, Elixir, Rescript 등 많은 언어에서 이 기능을 지원
- 함수형 프로그래밍은 데이터를 조작하고 처리하기 위해 선언적인 방식을 채택
- 패턴 매칭이라는 메커니즘을 사용하여 특정 패턴을 가진 데이터를 찾거나 추출
- 패턴 매칭을 사용하여 리스트에서 특정 요소를 찾거나
- 특정 데이터 구조에서 원하는 값을 추출하는 등
Rust에서 패턴매칭
match
키워드를 사용
fn main() {
let p = Point { x: 0, y: 7 };
match p {
Point { x, y: 0 } => println!("x값은 x축 위에 있습니다. 값: {}", x),
Point { x: 0, y } => println!("y값은 y축 위에 있습니다. {}", y),
Point { x, y } => println!("좌표값: ({}, {})", x, y),
}
}
Point
구조체의 인스턴스p
를 여러 패턴으로 매칭시켜 해당 패턴에 따라 결과를 출력
패턴매칭 vs 조건문
주어진 조건 또는 패턴에 따라 동작을 분기시킨다는 역할이 동일하다 하지만 이 둘은 동작하고 사용되는 상황이 다르다
- 패턴매칭
- 데이터의 구조나 패턴을 중심으로 동작
- 데이터의 패턴 정의
- 주어진 패턴이나 구조와 비교하여 일치하는 경우에 특정 동작을 수행
- 주로 문자열, 리스트, 튜플 등과 같은 데이터 구조에서 패턴을 비교하고, 패턴에 맞는 데이터를 추출하거나 처리하는 데 사용
- 조건문
- 주어진 조건에 따라 프로그램의 흐름을 분기
- 주어진 조건이 참인 경우, 특정 코드 블록을 실행
- 조건이 거짓인 경우, 다른 코드 블록을 실행하거나 흐름을 제어
-> 패턴 매칭과 조건문은 목적과 사용 방식에서 차이가 있지만, 두 가지 모두 프로그램의 제어 흐름을 분기시키는 데 사용되는 기능
자바스크립트에서의 패턴 매칭
- TC39 (opens in a new tab)에 1단계까지 진행됨
@babel/plugin-proposal-pattern-matching
,xstat
,lodash
의_matchs
를 사용하면 패턴매칭을 자바스크립트에서도 구현할 수 있음
ts-pattern
- 타입스크립트에서 패턴 매칭을 구현할 수 있게 해주는 라이브러리
예시
import { match } from 'ts-pattern';
type Weather = '맑음' | '구름' | '비' | '눈';
const getWeatherDescription = (weather: Weather): string => {
return match<Weather, string>(weather)
.with('맑음', () => '오늘은 맑은 날이네요! 선크림을 꼭 챙기세요.🕶️')
.with('구름', () => '오늘은 구름이 낀 날이에요.🌥️')
.with('비', () => '오늘은 비가 오네요! 꼭 우산을 챙기세요.🌧️')
.with('눈', () => '오늘은 눈이 오네요! 눈사람 만들 준비가 되셨나요?☃️')
.exhaustive(() => '유효하지 않은 날씨입니다.');
};
const currentWeather = 'sunny';
const weatherDescription = getWeatherDescription(currentWeather);
console.log(weatherDescription); // 오늘은 맑은 날이네요! 선크림을 꼭 챙기세요.🕶️
- 주어진 weather 값에 따라 해당하는 날씨 설명을 반환
match
함수를 사용하여weather
값과 매칭되는 내용을 선택
또 다른 예시
- 기존 코드의 문제
declare let fetchState:
| { status: { label: "loading" }}
| { status: { label: "success" }, data: string }
| { status: { label: "error" }, message: string };
// switch 사용
switch (fetchState.status.label) {
case "loading":
console.log("로딩중..");
break;
case "success":
console.log("성공! 데이터: ", fetchState.data); //type error!
break;
case "error":
console.error("에러: ", fetchState.message); //type error!
break;
}
// if 사용
if (fetchState.status.label === "loading") {
console.log("로딩중..");
} else if (fetchState.status.label === "success") {
console.log("성공! 데이터: ", fetchState.data); //type error!
} else if (fetchState.status.label === "error") {
console.error("에러: ", fetchState.message); //type error!
}
-
if-else
로 조건문을 사용한 경우에는 타입 변경에 매우 취약 -
fetchState의 타입에 또 다른 상태(ex. { label: "idle" })가 추가되더라도, 코드에서 어떤 상태에 따라 분기하는 부분에서 에러가 발생하지 않음
-
ts-pattern을 이용하면
match(fetchState)
.with({ status: { label: "loading" } }, () => console.log("로딩중.."))
.with({ status: { label: "success" } }, ({ data }) =>
console.log("성공! 데이터: ", data)
)
.with({ status: { label: "error" } }, ({ message }) =>
console.error("에러: ", message)
)
.exhaustive();
- 타입 추론을 자연스럽게 할 수 있음
with
함수의 콜백에는 패턴에 맞는 데이터가 타입 추론이 된 채로 넘어오기 때문