행동을 이름 짓다 — 함수
이전 챕터에서 구구단을 출력하는 코드를 만들었습니다. 그런데 만약 프로그램 여러 곳에서 구구단이 필요하다면 어떻게 할까요. 같은 코드를 복사해서 붙여넣을 수밖에 없습니다. 나중에 출력 형식을 바꾸고 싶다면 복사한 모든 곳을 찾아서 수정해야 합니다.
함수는 이 문제를 해결합니다. 코드에 이름을 붙여두면 필요할 때 그 이름을 부르면 됩니다. 한 번 고치면 모든 곳에 반영됩니다.
함수 선언과 호출
Dart에서 함수를 선언하는 기본 형태입니다.
반환타입 함수이름(매개변수) {
// 함수 본문
}
직접 만들어봅시다.
void greet() {
print('안녕하세요!');
}
void main() {
greet(); // 함수 호출
greet();
greet();
}
greet라는 이름의 함수를 선언했습니다. void는 이 함수가 아무 값도 반환하지 않는다는 의미입니다. main()에서 greet()를 세 번 호출하면 인사말이 세 번 출력됩니다.
매개변수 — 함수에 값 전달하기
함수를 호출할 때 데이터를 전달하려면 매개변수를 사용합니다.
void greet(String name) {
print('안녕하세요, $name님!');
}
void main() {
greet('다트');
greet('플러터');
greet('코딩');
}
String name이 매개변수입니다. 함수를 호출할 때 전달한 값이 name에 담깁니다. 같은 함수지만 전달하는 값에 따라 다른 동작을 합니다.
매개변수는 여러 개 선언할 수 있습니다. 쉼표로 구분합니다.
void printInfo(String name, int age) {
print('이름: $name, 나이: $age');
}
void main() {
printInfo('김다트', 20);
printInfo('이플러터', 22);
}
반환값 — 결과를 돌려주기
함수가 계산한 결과를 호출한 곳으로 돌려줄 때 return을 사용합니다. 반환 타입을 선언해야 합니다.
int add(int a, int b) {
return a + b;
}
void main() {
int result = add(3, 5);
print('3 + 5 = $result'); // 3 + 5 = 8
// 반환값을 바로 사용할 수도 있습니다
print('10 + 20 = ${add(10, 20)}');
}
반환 타입과 실제 반환되는 값의 타입이 일치해야 합니다. Dart 컴파일러가 이를 검사해줍니다.
double circleArea(double radius) {
return 3.14159 * radius * radius;
}
void main() {
print('반지름 5인 원의 넓이: ${circleArea(5.0)}');
}
최상위 함수
Dart의 함수는 클래스 안에 속하지 않아도 됩니다. main()처럼 파일의 최상위 레벨에 선언된 함수를 최상위 함수(top-level function) 라고 합니다. 지금까지 작성한 모든 예제의 함수가 최상위 함수입니다.
// 최상위 함수들
String formatScore(int score) {
return '$score점';
}
String getGrade(int score) {
if (score >= 90) return 'A';
if (score >= 80) return 'B';
if (score >= 70) return 'C';
return 'D';
}
void printReport(String name, int score) {
print('$name: ${formatScore(score)} (${getGrade(score)}등급)');
}
void main() {
printReport('김다트', 88);
printReport('이플러터', 92);
printReport('박코딩', 65);
}
지역 함수 — 함수 안의 함수
함수 안에 또 다른 함수를 선언할 수 있습니다. 이를 지역 함수(nested function) 또는 로컬 함수라고 합니다. 해당 함수 내부에서만 사용할 수 있습니다.
void printMultiplicationTable(int n) {
// n단을 출력하는 데만 필요한 지역 함수
String formatLine(int multiplier) {
return '$n × $multiplier = ${n * multiplier}';
}
print('--- $n단 ---');
for (int i = 1; i <= 9; i++) {
print(formatLine(i));
}
}
void main() {
printMultiplicationTable(3);
printMultiplicationTable(7);
}
formatLine은 printMultiplicationTable 안에서만 사용됩니다. 외부에서는 이 함수의 존재를 알 수도 없고 호출할 수도 없습니다. 관련 코드를 함께 묶어두는 좋은 방법입니다.
함수도 값이다
Dart에서 함수는 일급 객체(first-class object) 입니다. 함수를 변수에 담고, 다른 함수에 인자로 전달하고, 함수에서 반환할 수 있습니다.
void sayHello() {
print('Hello!');
}
void executeFunction(void Function() fn) {
print('함수를 실행합니다.');
fn(); // 전달받은 함수 호출
}
void main() {
// 함수를 변수에 담기
var myFunc = sayHello;
myFunc(); // Hello!
// 함수를 인자로 전달하기
executeFunction(sayHello);
}
void Function() 은 "매개변수 없이 void를 반환하는 함수"의 타입입니다. 이처럼 함수도 타입이 있습니다.
이 개념은 Ch 05에서 익명 함수와 콜백을 배울 때 더 깊이 다룹니다. 지금은 "함수를 변수처럼 다룰 수 있다"는 사실만 기억해두세요.
전체 예제 — 성적 처리 함수 모음
int clampScore(int score) {
if (score < 0) return 0;
if (score > 100) return 100;
return score;
}
String getGrade(int score) {
int s = clampScore(score);
if (s >= 90) return 'A';
if (s >= 80) return 'B';
if (s >= 70) return 'C';
if (s >= 60) return 'D';
return 'F';
}
bool isPassed(int score) {
return clampScore(score) >= 60;
}
void printReport(String name, int score) {
String grade = getGrade(score);
bool passed = isPassed(score);
String result = passed ? '합격' : '불합격';
print('[$result] $name: $score점 / $grade등급');
}
void main() {
printReport('김다트', 88);
printReport('이플러터', 54);
printReport('박코딩', 100);
printReport('최러닝', -10); // clampScore가 0으로 보정
}
실행 결과입니다.
[합격] 김다트: 88점 / B등급
[불합격] 이플러터: 54점 / F등급
[합격] 박코딩: 100점 / A등급
[불합격] 최러닝: -10점 / F등급
함수를 작게 나누고 서로 조합했습니다. 각 함수는 한 가지 일만 합니다. 이렇게 작성된 코드는 이해하기 쉽고 수정하기도 쉽습니다.
다음 챕터에서는 함수의 매개변수를 더 유연하게 다루는 방법을 배웁니다. 매개변수를 생략하거나 이름으로 지정하는 기능은 Dart 코드를 훨씬 읽기 좋게 만들어줍니다.