iBetter Books
수정

스프레드와 컬렉션 if·for

Dart에는 다른 언어에서 보기 어려운 독특한 문법이 있습니다. 리스트를 만들 때 리스트 안에서 바로 조건문이나 반복문을 쓸 수 있습니다. Flutter에서 UI를 구성할 때 특히 강력한 도구입니다.

이번 챕터에서는 세 가지를 배웁니다. 다른 컬렉션을 펼쳐 넣는 스프레드 연산자, 리스트 안에서 조건을 처리하는 컬렉션 if, 반복을 처리하는 컬렉션 for입니다.

스프레드 연산자 — ... 로 펼치기

스프레드 연산자 ...는 리스트를 "펼쳐서" 다른 리스트 안에 끼워 넣습니다.

void main() {
  var a = [1, 2, 3];
  var b = [4, 5, 6];

  // 두 리스트 합치기
  var combined = [...a, ...b];
  print(combined); // [1, 2, 3, 4, 5, 6]

  // 중간에 끼워 넣기
  var inserted = [0, ...a, ...b, 7];
  print(inserted); // [0, 1, 2, 3, 4, 5, 6, 7]

  // 기존 리스트에 요소 추가한 새 리스트
  var fruits = ['사과', '바나나'];
  var moreFruits = [...fruits, '딸기', '포도'];
  print(moreFruits); // [사과, 바나나, 딸기, 포도]
  print(fruits);     // [사과, 바나나] — 원본 변경 없음
}

addAll과 비슷하지만 차이가 있습니다. 스프레드는 새 리스트를 만들고, addAll은 기존 리스트를 변경합니다.

Set과 Map에도 씁니다.

void main() {
  var set1 = {1, 2, 3};
  var set2 = {3, 4, 5};

  // Set 합치기 (자동으로 중복 제거)
  var unionSet = {...set1, ...set2};
  print(unionSet); // {1, 2, 3, 4, 5}

  // Map 합치기
  var defaults = {'theme': 'light', 'lang': 'ko'};
  var overrides = {'theme': 'dark'};
  var merged = {...defaults, ...overrides};
  print(merged); // {theme: dark, lang: ko}
}

Map을 합칠 때 같은 키가 있으면 뒤에 오는 것이 우선합니다. 기본값 위에 사용자 설정을 덮어쓰는 패턴으로 자주 씁니다.

null 안전 스프레드 연산자 — ...?

합치려는 컬렉션이 null일 수도 있을 때는 ...?을 씁니다. null이면 아무것도 추가하지 않고 건너뜁니다.

void main() {
  List<String>? extras = null;
  var base = ['기본1', '기본2'];

  // ...extras 는 null이라 오류 발생
  // var wrong = [...base, ...extras]; // 오류!

  // ...? 는 null이면 그냥 무시
  var safe = [...base, ...?extras];
  print(safe); // [기본1, 기본2]

  // null이 아닐 때는 정상 동작
  extras = ['추가1', '추가2'];
  var withExtras = [...base, ...?extras];
  print(withExtras); // [기본1, 기본2, 추가1, 추가2]
}

컬렉션 if — 리스트 안의 조건문

리스트를 만들 때 조건에 따라 요소를 포함하거나 제외할 수 있습니다.

void main() {
  bool isAdmin = true;
  bool isLoggedIn = true;

  var menu = [
    '홈',
    '검색',
    if (isLoggedIn) '마이페이지',
    if (isAdmin) '관리자',
  ];

  print(menu); // [홈, 검색, 마이페이지, 관리자]

  // isAdmin이 false일 때
  bool isAdmin2 = false;
  var menu2 = [
    '홈',
    '검색',
    if (isLoggedIn) '마이페이지',
    if (isAdmin2) '관리자',
  ];

  print(menu2); // [홈, 검색, 마이페이지]
}

if 조건이 false이면 해당 요소는 리스트에 포함되지 않습니다. 없는 것처럼 취급됩니다.

else 분기도 쓸 수 있습니다.

void main() {
  int score = 75;

  var report = [
    '이름: 홍길동',
    '점수: $score점',
    if (score >= 60) '결과: 합격' else '결과: 불합격',
    if (score >= 90) '우수상 수상',
  ];

  for (String line in report) {
    print(line);
  }
}

출력 결과입니다.

이름: 홍길동
점수: 75점
결과: 합격

90점 이상이 아니므로 "우수상 수상"은 포함되지 않습니다.

컬렉션 for — 리스트 안의 반복문

리스트를 만들 때 반복을 통해 요소를 생성합니다.

void main() {
  // 1부터 5까지의 제곱수 리스트
  var squares = [
    for (int i = 1; i <= 5; i++) i * i,
  ];
  print(squares); // [1, 4, 9, 16, 25]

  // 다른 리스트에서 변환
  var names = ['alice', 'bob', 'charlie'];
  var greetings = [
    for (String name in names) '안녕, ${name[0].toUpperCase()}${name.substring(1)}!',
  ];
  print(greetings);
  // [안녕, Alice!, 안녕, Bob!, 안녕, Charlie!]

  // 조건 포함
  var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  var evenSquares = [
    for (int n in numbers)
      if (n % 2 == 0) n * n,
  ];
  print(evenSquares); // [4, 16, 36, 64, 100]
}

컬렉션 for 안에 컬렉션 if를 중첩할 수 있습니다. 짝수만 골라 제곱한 결과를 한 줄로 표현했습니다.

스프레드, if, for 조합하기

세 가지를 섞어서 쓸 수 있습니다.

void main() {
  var fixed = ['고정항목1', '고정항목2'];
  var dynamic = ['동적항목1', '동적항목2', '동적항목3'];
  bool showDynamic = true;
  bool showExtra = false;

  var list = [
    ...fixed,
    if (showDynamic) ...dynamic,
    if (showExtra) '추가항목',
    for (int i = 1; i <= 3; i++) '번호항목$i',
  ];

  for (String item in list) {
    print(item);
  }
}

출력 결과입니다.

고정항목1
고정항목2
동적항목1
동적항목2
동적항목3
번호항목1
번호항목2
번호항목3

Flutter 위젯 리스트 구성 패턴

이 문법이 가장 빛나는 곳은 Flutter입니다. Flutter에서 화면은 위젯의 트리로 구성됩니다. Column이나 Rowchildren은 위젯의 List입니다. 여기서 컬렉션 if와 for가 자연스럽게 쓰입니다.

아래 예제는 실제 Flutter 코드는 아니고, 같은 패턴을 순수 Dart로 표현한 것입니다. Flutter를 배울 때 익숙하게 보게 될 패턴입니다.

// Flutter 스타일 위젯 리스트 구성 패턴 (순수 Dart 시뮬레이션)
void main() {
  bool isLoggedIn = true;
  bool hasNotification = true;
  List<String> menuItems = ['홈', '검색', '즐겨찾기'];

  // Flutter의 Column children 구성과 동일한 패턴
  var widgets = [
    // 헤더는 항상 표시
    'AppBar: 내 앱',

    // 로그인 상태에 따라 다른 위젯
    if (isLoggedIn)
      '환영합니다, 홍길동님!'
    else
      '로그인 버튼',

    // 알림 배지 (조건부)
    if (hasNotification) '알림: 새 메시지 3개',

    // 메뉴 목록 동적 생성
    for (String item in menuItems) 'MenuItem: $item',

    // 하단 고정
    'BottomBar',
  ];

  for (String widget in widgets) {
    print(widget);
  }
}

출력 결과입니다.

AppBar: 내 앱
환영합니다, 홍길동님!
알림: 새 메시지 3개
MenuItem: 홈
MenuItem: 검색
MenuItem: 즐겨찾기
BottomBar

실제 Flutter에서는 'AppBar: 내 앱' 자리에 AppBar(title: Text('내 앱')) 같은 위젯이 들어갑니다. 컬렉션 if와 for를 쓰면 조건부 UI와 동적 UI를 선언적으로 깔끔하게 표현할 수 있습니다.

PART 04 정리

이번 파트에서 Dart 컬렉션의 전체 그림을 익혔습니다.

List는 순서가 있고 중복을 허용하는 목록입니다. 인덱스로 접근하고, sort로 정렬하고, add와 remove로 수정합니다.

Set은 중복 없는 집합입니다. 합집합, 교집합, 차집합 같은 집합 연산이 가능하고, contains가 빠릅니다.

Map은 키-값 쌍의 사전입니다. 키로 빠르게 값을 찾고, entries로 순회합니다.

고급 기법map, where, reduce, fold, any, every를 쓰면 반복문 없이 선언적으로 컬렉션을 다룰 수 있습니다.

스프레드와 컬렉션 if·for는 리스트를 만들 때 다른 컬렉션을 펼쳐 넣고, 조건과 반복을 인라인으로 표현합니다.

다음 파트에서는 클래스와 객체지향 프로그래밍으로 넘어갑니다. 지금까지 Map<String, dynamic>으로 흉내 내던 "사람", "상품", "주문" 같은 개념을 클래스로 제대로 표현하게 됩니다.

Ch 05. 스프레드와 컬렉션 if·for — 소설처럼 읽는 Dart | iBetter Books