간결한 문법 — 화살표 함수와 익명 함수
코드를 읽다 보면 중괄호와 return으로 가득 찬 단순한 함수들을 자주 만납니다.
int double(int n) {
return n * 2;
}
이 함수는 하는 일이 딱 하나입니다. n * 2를 반환하는 것. 세 줄이나 쓰기엔 아깝습니다. Dart는 이런 경우를 위한 간결한 문법을 제공합니다.
화살표 함수 — 한 줄로 줄이기
함수 본문이 단일 표현식(expression) 하나일 때 => 를 사용할 수 있습니다.
int double(int n) => n * 2;
=> 표현식은 { return 표현식; }과 완전히 동일합니다. 읽기도 쉽고 쓰기도 간결합니다.
String greet(String name) => '안녕하세요, $name님!';
bool isEven(int n) => n % 2 == 0;
double circleArea(double r) => 3.14159 * r * r;
void sayHi() => print('Hi!'); // void도 가능합니다
void main() {
print(double(5)); // 10
print(greet('다트')); // 안녕하세요, 다트님!
print(isEven(4)); // true
print(circleArea(3.0)); // 28.27431
sayHi(); // Hi!
}
주의할 점은 =>뒤에는 표현식만 올 수 있다는 것입니다. if 문이나 for 문 같은 문장(statement) 은 올 수 없습니다. 값을 계산하거나 반환하는 한 줄짜리 로직에 적합합니다.
익명 함수 — 이름 없는 함수
함수에 이름을 붙이지 않고 바로 사용할 수도 있습니다. 이를 익명 함수(anonymous function) 또는 람다(lambda)라고 합니다.
void main() {
// 익명 함수를 변수에 담기
var multiply = (int a, int b) {
return a * b;
};
print(multiply(3, 4)); // 12
print(multiply(7, 8)); // 56
}
(int a, int b) { return a * b; } 부분이 익명 함수입니다. 이름이 없지만 multiply 변수에 담아 호출할 수 있습니다.
화살표 형태로도 쓸 수 있습니다.
void main() {
var add = (int a, int b) => a + b;
print(add(10, 20)); // 30
}
함수를 매개변수로 전달하기 — 콜백
익명 함수의 진짜 힘은 다른 함수에 전달할 때 드러납니다. 함수를 인자로 전달받아 실행하는 패턴을 콜백(callback) 이라고 합니다.
void repeat(int times, void Function() action) {
for (int i = 0; i < times; i++) {
action();
}
}
void main() {
repeat(3, () {
print('반복 실행!');
});
}
() { print('반복 실행!'); } 는 repeat 함수에 전달되는 익명 함수입니다. repeat 내부에서 action()을 호출하면 이 익명 함수가 실행됩니다.
화살표 형태로 더 간결하게 쓸 수 있습니다.
void main() {
repeat(3, () => print('간결하게!'));
}
매개변수를 받는 콜백도 자연스럽게 동작합니다.
void processNumbers(List<int> numbers, void Function(int) handler) {
for (int n in numbers) {
handler(n);
}
}
void main() {
List<int> scores = [85, 92, 67, 78, 55];
processNumbers(scores, (score) {
String grade = score >= 60 ? '합격' : '불합격';
print('$score점: $grade');
});
}
클로저 — 주변 변수를 기억하는 함수
익명 함수는 자신이 선언된 위치의 변수를 기억합니다. 이 특성을 클로저(closure) 라고 합니다.
Function makeCounter() {
int count = 0; // 이 변수를 내부 함수가 기억합니다
return () {
count++;
print('카운트: $count');
};
}
void main() {
var counter = makeCounter();
counter(); // 카운트: 1
counter(); // 카운트: 2
counter(); // 카운트: 3
var anotherCounter = makeCounter(); // 새로운 count 변수
anotherCounter(); // 카운트: 1 (독립적입니다)
}
makeCounter가 반환한 익명 함수는 count 변수를 "닫아서(close over)" 가지고 있습니다. makeCounter 함수가 종료된 뒤에도 count는 살아있습니다. 두 번째 makeCounter() 호출은 새로운 count를 가진 독립적인 카운터를 만듭니다.
클로저를 활용하면 상태를 가진 함수를 만들 수 있습니다. 간단한 경우에 클래스를 만들지 않아도 됩니다.
Function makeAdder(int base) {
return (int n) => base + n;
}
void main() {
var add10 = makeAdder(10);
var add100 = makeAdder(100);
print(add10(5)); // 15
print(add10(20)); // 30
print(add100(5)); // 105
}
makeAdder(10)은 "10을 더하는 함수"를 반환합니다. base 값 10을 클로저로 기억합니다.
typedef — 함수 타입에 이름 붙이기
void Function(int) 같은 함수 타입을 매번 쓰는 것은 번거롭습니다. typedef로 이름을 붙여 재사용할 수 있습니다.
// 함수 타입에 이름 붙이기
typedef ScoreHandler = void Function(int score);
typedef Transformer = int Function(int value);
void processScore(int score, ScoreHandler handler) {
handler(score);
}
int applyTwice(int value, Transformer transform) {
return transform(transform(value));
}
void main() {
// ScoreHandler 타입의 함수 전달
processScore(85, (score) {
print('${score >= 60 ? "합격" : "불합격"}: $score점');
});
// Transformer 타입의 함수 전달
int result = applyTwice(3, (n) => n * 2);
print('3을 두 번 두 배로: $result'); // 12
}
typedef는 코드의 의도를 명확히 전달합니다. ScoreHandler라는 이름만 봐도 "점수를 처리하는 함수"라는 것을 알 수 있습니다.
전체 예제 — 점수 처리 파이프라인
지금까지 배운 모든 개념을 조합합니다.
typedef Filter = bool Function(int score);
typedef Formatter = String Function(int score);
// 화살표 함수로 간결하게 정의
bool isPassed(int score) => score >= 60;
bool isHighScore(int score) => score >= 90;
String formatScore(int score) => '$score점 (${score >= 60 ? "합격" : "불합격"})';
void processScores(
List<int> scores, {
required Filter filter,
required Formatter formatter,
}) {
for (int score in scores) {
if (filter(score)) {
print(formatter(score));
}
}
}
void main() {
List<int> scores = [45, 72, 91, 58, 88, 95, 33, 76];
print('--- 합격자 목록 ---');
processScores(
scores,
filter: isPassed,
formatter: formatScore,
);
print('');
print('--- 우수 성적자 ---');
processScores(
scores,
filter: isHighScore,
formatter: (score) => '우수: $score점',
);
// 클로저로 기준점 동적 생성
print('');
print('--- 80점 이상 ---');
int threshold = 80;
processScores(
scores,
filter: (score) => score >= threshold,
formatter: (score) => '$score점',
);
}
실행 결과입니다.
--- 합격자 목록 ---
72점 (합격)
91점 (합격)
88점 (합격)
95점 (합격)
76점 (합격)
--- 우수 성적자 ---
우수: 91점
우수: 95점
--- 80점 이상 ---
91점
88점
95점
화살표 함수로 짧게 정의하고, 익명 함수로 즉석에서 로직을 만들고, typedef로 타입을 명확히 하고, 클로저로 외부 변수를 활용했습니다. PART 03에서 배운 핵심 개념들이 하나로 어우러집니다.
이것으로 PART 03이 끝났습니다. 조건문으로 흐름을 나누고, 반복문으로 같은 일을 반복하고, 함수로 코드에 이름을 붙이고, 다양한 매개변수로 유연하게 설계하고, 화살표와 익명 함수로 간결하게 표현하는 법을 배웠습니다.
다음 PART에서는 여러 값을 한꺼번에 다루는 컬렉션을 알아봅니다. 리스트, 세트, 맵을 자유자재로 다루게 되면 지금까지 배운 반복문과 함수가 진짜 힘을 발휘합니다.