패턴

2025. 8. 31. 00:09·다트🎯

패턴(Patterns)은 Dart 3.0부터 쓸 수 있는 문법 범주야. 쉽게 말해, 값의 형태를 서술해서 그 값이 기대한 모양인지 매칭해 보고, 맞으면 그 값을 분해(디스트럭처링) 해서 구성 요소를 꺼내오는 방식이야. 어디서 쓸 수 있는지, 뭐가 좋은지 감만 잡자.


패턴이 하는 일

패턴은 상황과 생김새에 따라 두 가지를 해:

  1. 매칭: 값이 특정 모양인지, 특정 상수와 같은지, 특정 타입인지 등을 검사.
  2. 디스트럭처링: 매칭에 성공하면 그 값을 선언적으로 쪼개서 필요한 조각들을 변수로 바로 받기.

매칭은 패턴 종류에 따라 달라. 예를 들어 상수 패턴은 값이 그 상수와 같으면 매칭돼:

switch (number) {
  // Constant pattern matches if 1 == number.
  case 1:
    print('one');
}

여러 패턴은 하위 패턴을 재귀적으로 포함할 수 있어. 예를 들어 리스트 패턴의 각 칸에 상수 패턴이나 변수 패턴이 들어갈 수 있지:

const a = 'a';
const b = 'b';
switch (obj) {
  // List pattern [a, b] matches obj first if obj is a list with two fields,
  // then if its fields match the constant subpatterns 'a' and 'b'.
  case [a, b]:
    print('$a, $b');
}

무시하고 싶은 부분은 와일드카드로 건너뛰면 되고, 리스트면 rest 요소로 남은 걸 흡수할 수도 있어.


디스트럭처링

매칭이 되면 패턴이 그 값을 구성 요소로 분해해서 변수에 담을 수 있어:

var numList = [1, 2, 3];
// List pattern [a, b, c] destructures the three elements from numList...
var [a, b, c] = numList;
// ...and assigns them to new variables.
print(a + b + c);

패턴 안에 또 다른 패턴을 중첩할 수도 있어. 예를 들어 첫 번째 요소가 'a' 또는 'b' 인 두 칸짜리 리스트를 매칭하고 분해하는 패턴:

switch (list) {
  case ['a' || 'b', var c]:
    print(c);
}

패턴을 쓸 수 있는 곳

패턴은 지역 변수 선언/대입, for·for-in 루프, if-case와 switch의 case, 컬렉션 리터럴의 제어 흐름 요소에 쓸 수 있어. 자주 쓰는 케이스만 콕 집어 보자.

변수 선언

오른쪽 값과 매칭 + 분해해서 새 변수로 바인딩해:

// Declares new variables a, b, and c.
var (a, [b, c]) = ('str', [1, 2]);

항상 var 또는 final 다음에 패턴이 와야 해.

변수 대입

이미 있는 변수에 패턴 대입으로 값들을 한 번에 갱신할 수 있어. 임시 변수 없이 스왑하기도 쉽고:

var (a, b) = ('left', 'right');
(b, a) = (a, b); // Swap.
print('$a $b'); // Prints "right left".

switch 문/식

모든 case는 패턴이야. 매칭되면 분해된 값들이 그 case 본문에서만 보이는 지역 변수로 잡혀.

switch (obj) {
  // Matches if 1 == obj.
  case 1:
    print('one');

  // Matches if the value of obj is between the
  // constant values of 'first' and 'last'.
  case >= first && <= last:
    print('in range');

  // Matches if obj is a record with two fields,
  // then assigns the fields to 'a' and 'b'.
  case (var a, var b):
    print('a = $a, b = $b');

  default:
}

여러 케이스를 하나의 본문으로 묶고 싶으면 논리 OR 패턴이 좋아:

var isPrimary = switch (color) {
  Color.red || Color.yellow || Color.blue => true,
  _ => false,
};

또, 여러 케이스에 공통 가드(when) 를 쓰고 싶을 때도 유용해:

switch (shape) {
  case Square(size: var s) || Circle(size: var s) when s > 0:
    print('Non-empty symmetric shape');
}

가드는 case의 일부로 조건을 평가하는데, 거짓이면 switch를 탈출하지 않고 다음 케이스로 넘어가게 할 수 있어:

switch (pair) {
  case (int a, int b):
    if (a > b) print('First element greater');
  // If false, prints nothing and exits the switch.
  case (int a, int b) when a > b:
    // If false, prints nothing but proceeds to next case.
    print('First element greater');
  case (int a, int b):
    print('First element not greater');
}

for / for-in 루프

루프 변수 자리에 패턴을 써서 컬렉션의 요소를 반복하면서 분해할 수 있어:

Map<String, int> hist = {'a': 23, 'b': 100};

for (var MapEntry(key: key, value: count) in hist.entries) {
  print('$key occurred $count times');
}

게터 이름과 같은 변수로 바인딩하는 건 너무 흔해서, 축약 표기도 돼:

for (var MapEntry(:key, value: count) in hist.entries) {
  print('$key occurred $count times');
}

패턴이 빛나는 쓰임새

여러 값 반환(Records) 즉시 분해

레코드가 여러 값을 묶어서 반환해 주면, 패턴으로 바로 풀어 받으면 돼:

var info = userInfo(json);
var name = info.$1;
var age = info.$2;

대신 한 줄로:

var (name, age) = userInfo(json);

이름 있는 레코드도 바로 분해 가능:

final (:name, :age) =
    getData(); // For example, return (name: 'doug', age: 25);

클래스 인스턴스 분해

오브젝트 패턴은 특정 타입을 매칭하고, 그 타입이 노출하는 게터로 프로퍼티를 분해해:

final Foo myFoo = Foo(one: 'one', two: 2);
var Foo(:one, :two) = myFoo;
print('one $one, two $two');

대수적 데이터 타입(ADT) 스타일

관련된 타입 군을 한꺼번에 다루고, 한 함수에서 서브타입별 동작을 스위치로 분기하는 패턴이 잘 맞아:

sealed class Shape {}

class Square implements Shape {
  final double length;
  Square(this.length);
}

class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(radius: var r) => math.pi * r * r,
};

들어오는 JSON 검증 + 분해

맵/리스트 패턴으로 구조를 검증하면서 동시에 분해할 수 있어:

var data = {
  'user': ['Lily', 13],
};
var {'user': [name, age]} = data;

외부 데이터는 보통 신뢰할 수 없으니, 패턴으로 한 번에 선언적으로 확인하자. 장황한 if 연쇄 대신:

if (data case {'user': [String name, int age]}) {
  print('User $name is $age years old.');
}

이 한 줄로

  • 맵인지 확인(= null 아님),
  • 'user' 키 존재,
  • 값이 길이 2인 리스트,
  • 첫 값 String, 둘째 값 int,
  • 그리고 name, age 변수 바인딩
    을 전부 만족시켜야 통과해.

'다트🎯' 카테고리의 다른 글

관리 흐름 - 반복  (0) 2025.08.31
패턴의 종류  (0) 2025.08.31
타입 - 타입 시스템  (0) 2025.08.31
타입 - 타입 별칭  (0) 2025.08.31
타입 - 제너릭  (0) 2025.08.31
'다트🎯' 카테고리의 다른 글
  • 관리 흐름 - 반복
  • 패턴의 종류
  • 타입 - 타입 시스템
  • 타입 - 타입 별칭
flutters
flutters
  • flutters
    fts
    flutters
  • 전체
    오늘
    어제
  • 공지사항

    • 분류 전체보기 (23)
      • 자바스크립트 🍌 (0)
      • 다트🎯 (22)
      • 플러터🦋 (0)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

    • Github🌐
  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
flutters
패턴
상단으로

티스토리툴바