유연한 매개변수 — 선택적, 이름 있는 매개변수
이전 챕터에서 만든 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 누락
}
email과 password는 required이므로 반드시 전달해야 합니다. 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 코드를 훨씬 날렵하게 만들어줍니다.