iBetter Books
수정

유연한 매개변수 — 선택적, 이름 있는 매개변수

이전 챕터에서 만든 printReport('김다트', 88) 함수를 떠올려봅시다. 두 인자를 순서대로 전달했습니다. 이런 방식은 간단하지만, 매개변수가 많아지면 문제가 생깁니다.

createUser('김다트', 20, '서울', '[email protected]', true, false)

이 코드를 읽는 사람은 각 값이 무엇을 의미하는지 함수 선언을 찾아봐야 합니다. Dart는 이 문제를 해결하는 우아한 방법을 제공합니다.

위치 매개변수 — 순서가 중요한 기본 방식

지금까지 사용한 방식입니다. 매개변수 순서대로 값을 전달합니다.

void printInfo(String name, int age) {
  print('$name, $age세');
}

void main() {
  printInfo('김다트', 20); // 순서 중요: name=김다트, age=20
}

순서가 바뀌면 오류가 나거나 엉뚱한 결과가 나옵니다. 매개변수가 적을 때는 문제없지만, 많아질수록 불편합니다.

이름 있는 매개변수 — 명확한 의도 표현

중괄호 {}로 감싸면 이름 있는 매개변수(named parameters)가 됩니다.

void printInfo({String name = '', int age = 0}) {
  print('$name, $age세');
}

void main() {
  printInfo(name: '김다트', age: 20);
  printInfo(age: 22, name: '이플러터'); // 순서 바꿔도 됩니다
  printInfo(name: '박코딩'); // age는 기본값 0 사용
}

호출할 때 매개변수이름: 값 형태로 전달합니다. 순서에 구애받지 않습니다. 읽는 사람이 각 값이 무엇인지 바로 알 수 있습니다.

이름 있는 매개변수는 기본적으로 선택 사항입니다. 전달하지 않으면 기본값이 사용됩니다. 기본값이 없으면 null이 됩니다. 하지만 Dart의 Null Safety 덕분에 null이 될 수 있는 매개변수는 nullable 타입으로 선언해야 합니다.

void createUser({String? nickname, int age = 18}) {
  String display = nickname ?? '익명';
  print('$display ($age세)');
}

void main() {
  createUser(nickname: '다트러버', age: 25);
  createUser(age: 20); // nickname은 null → '익명'으로 대체
  createUser(); // 모두 기본값 사용
}

required — 반드시 전달해야 하는 이름 있는 매개변수

이름 있는 매개변수는 기본적으로 선택 사항이지만, required 키워드를 붙이면 반드시 전달해야 합니다. 전달하지 않으면 컴파일 오류가 발생합니다.

void createAccount({
  required String email,
  required String password,
  String nickname = '새 사용자',
}) {
  print('계정 생성: $email, 닉네임: $nickname');
}

void main() {
  createAccount(
    email: '[email protected]',
    password: 'secret123',
  );

  createAccount(
    email: '[email protected]',
    password: 'pass456',
    nickname: '플러터팬',
  );

  // createAccount(email: '[email protected]'); // 오류: password 누락
}

emailpasswordrequired이므로 반드시 전달해야 합니다. nickname은 기본값이 있으므로 생략할 수 있습니다.

required는 Flutter에서 위젯을 만들 때 매우 자주 사용되는 패턴입니다. 지금 잘 익혀두면 Flutter를 배울 때 코드가 훨씬 자연스럽게 읽힙니다.

선택적 위치 매개변수 — 생략 가능한 순서 매개변수

대괄호 []로 감싸면 선택적 위치 매개변수(optional positional parameters)가 됩니다. 위치 매개변수이므로 순서는 지켜야 하지만, 뒤에서부터 생략할 수 있습니다.

void greet(String name, [String greeting = '안녕하세요', String suffix = '님']) {
  print('$greeting, $name$suffix!');
}

void main() {
  greet('다트');                         // 안녕하세요, 다트님!
  greet('플러터', '반갑습니다');            // 반갑습니다, 플러터님!
  greet('코딩', '좋은 아침이에요', ' 선생'); // 좋은 아침이에요, 코딩 선생!
}

선택적 위치 매개변수는 반드시 필수 위치 매개변수 뒤에 위치해야 합니다. 그리고 중간을 건너뛸 수 없습니다. greeting을 생략하고 suffix만 전달하는 것은 불가능합니다.

이런 이유로 실제 코드에서는 이름 있는 매개변수를 더 많이 사용합니다. 선택적 위치 매개변수는 print()log()처럼 인자 개수가 자연스럽게 확장되는 유틸리티 함수에 적합합니다.

기본값 — 생략했을 때의 값

이름 있는 매개변수와 선택적 위치 매개변수 모두 기본값을 가질 수 있습니다. 기본값은 컴파일 타임 상수여야 합니다. 즉, const로 선언 가능한 값이어야 합니다.

void drawBox({
  int width = 80,
  int height = 24,
  String borderChar = '*',
}) {
  print('가로: $width, 세로: $height, 테두리: $borderChar');
}

void main() {
  drawBox();                               // 모두 기본값 사용
  drawBox(width: 120);                     // height, borderChar는 기본값
  drawBox(width: 60, height: 40, borderChar: '#');
}

전체 예제 — 유연한 사용자 생성 함수

지금까지 배운 내용을 하나로 묶습니다.

void createUser({
  required String username,
  required String email,
  int age = 0,
  String? bio,
  bool isAdmin = false,
}) {
  print('=== 사용자 생성 ===');
  print('아이디: $username');
  print('이메일: $email');
  if (age > 0) print('나이: $age세');
  if (bio != null) print('소개: $bio');
  print('관리자: ${isAdmin ? "예" : "아니오"}');
  print('');
}

void main() {
  // 필수 항목만 전달
  createUser(
    username: 'dartlover',
    email: '[email protected]',
  );

  // 모든 항목 전달
  createUser(
    username: 'flutterfan',
    email: '[email protected]',
    age: 25,
    bio: 'Flutter로 앱 만드는 것을 좋아합니다.',
    isAdmin: true,
  );

  // 일부 항목 전달
  createUser(
    username: 'newbie',
    email: '[email protected]',
    age: 19,
  );
}

실행 결과입니다.

=== 사용자 생성 ===
아이디: dartlover
이메일: [email protected]
관리자: 아니오

=== 사용자 생성 ===
아이디: flutterfan
이메일: [email protected]
나이: 25세
소개: Flutter로 앱 만드는 것을 좋아합니다.
관리자: 예

=== 사용자 생성 ===
아이디: newbie
이메일: [email protected]
나이: 19세
관리자: 아니오

이름 있는 매개변수 덕분에 호출 코드만 읽어도 어떤 값이 어디에 쓰이는지 명확히 알 수 있습니다.

다음 챕터에서는 함수를 더 간결하게 표현하는 방법을 배웁니다. 화살표 하나로 함수를 정의하고, 이름 없는 함수를 만들어 바로 전달하는 기법은 Dart 코드를 훨씬 날렵하게 만들어줍니다.