Dart 언어에서 함수 역시 객체로 취급되는데, 이는 다른 프로그래밍 언어와 약간 차이가 있습니다.
함수는 일급 객체
Dart는 완전한 객체지향 언어로, 함수 자체가 객체이며 타입은 Function입니다.
변수에 할당하거나 다른 함수의 인자로 전달할 수 있습니다.
클래스 인스턴스를 함수처럼 직접 호출할 수 있음
1 표준 함수 정의
// 반환값과 매개변수 타입 어노테이션을 모두 작성한 완전 형식
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
Code language: Dart (dart)
전체 예시
// 희가스 원자번호 매핑 테이블
final Map<int, String> _nobleGases = {
2: 'He',
10: 'Ne',
18: 'Ar',
36: 'Kr',
54: 'Xe',
86: 'Rn',
};
// 반환값, 매개변수 타입 어노테이션 완전 적용
// int는 널 불가 타입이므로 null을 허용하려면 int?로 변경
bool isNoble(int? atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
void main() {
print(isNoble(null)); // null은 맵 키에 없음 → false
print(isNoble(2)); // 희가스 존재 → true
print(isNoble(9)); // 존재하지 않음 → false
print(isNoble(86)); // 존재 → true
}
Code language: Dart (dart)
false
true
false
trueCode language: JavaScript (javascript)
2 타입 생략
Effective Dart 코딩 가이드라인에 따르면 외부에 노출하는 공개 API에는 모든 타입을 직접 명시해야 합니다.
오직 내부에서만 사용하는 함수는 타입을 생략할 수 있습니다.
예시
final Map<int, String> _nobleGases = {
2: 'He',
10: 'Ne',
18: 'Ar',
36: 'Kr',
54: 'Xe',
86: 'Rn',
};
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
void main() {
print(isNoble(null)); // null은 맵 키에 없음 → false
print(isNoble(2)); // 희가스 존재 → true
print(isNoble(9)); // 존재하지 않음 → false
print(isNoble(86)); // 존재 → true
}
Code language: Dart (dart)
위 예시의 isNoble 함수는 반환 타입 bool과 매개변수 int? 타입을 제거했지만 정상적으로 작동합니다.
타입을 작성하지 않으면 Dart가 자동으로 타입 추론을 실행합니다:
- 매개변수
atomicNumber:명시적 타입 없음 → 추론 타입은dynamic(동적 타입으로 숫자, 문자열, null 등 어떤 값이든 전달 가능) - 반환값:앞에 타입을 적지 않으면 Dart가
return우측 표현식을 보고 반환 타입이bool임을 추론하며 코드에 따로 표시되지 않을 뿐입니다.
공개 API에 타입을 반드시 적어야 하는 이유
가독성:다른 개발자가 함수를 호출할 때 매개변수와 반환값 타입을 한눈에 파악할 수 있어 내부 코드를 읽고 추측할 필요가 없습니다.
타입 안정성:dynamic 동적 타입은 정적 타입 검사를 비활성화하므로 잘못된 타입을 전달해도 컴파일 오류가 발생하지 않아 런타임 크래시가 자주 발생합니다.
IDE 자동 완성:타입을 명시하면 편집기에서 자동 완성과 매개변수 힌트를 제공하며, 타입이 없으면 IDE가 정확한 힌트를 보여주지 못합니다.
타입을 생략해도 괜찮은 경우
오직 파일 내부 비공개 코드에만 권장하며, 함수가 같은 파일 안에서만 사용되고 외부에서 호출되지 않을 때 해당합니다.
화살표 축약 문법 (=>)
함수 본문({ } 안 코드)이 반환 표현식 한 줄로만 구성될 경우 화살표 함수로 축약해서 작성할 수 있습니다.
예시
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
Code language: Dart (dart)
=> expr는{ return expr; }와 동일합니다.- 화살표와 세미콜론 사이에는 오직 표현식만 작성 가능하며 if 같은 구문은 쓸 수 없고 삼항 연산자로 대체해야 합니다.
// 희가스 원자번호 매핑 테이블
final Map<int, String> _nobleGases = {
2: '헬륨',
10: '네온',
18: '아르곤',
36: '크립톤',
54: '제논',
86: '라돈',
};
// 화살표 축약 함수: 한 줄 표현식으로 바로 반환, return이 있는 블록 함수와 동일
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
void main() {
// 존재하는 희가스 테스트
print(isNoble(2)); // true
print(isNoble(18)); // true
// 일반 원소 테스트
print(isNoble(6)); // false (탄소)
print(isNoble(99)); // false (해당 원소 없음)
}
Code language: Dart (dart)

Parameters 매개변수
함수는 필수 위치 매개변수를 지원하며 그 뒤에는 명명 매개변수 또는 선택적 위치 매개변수 중 하나만 사용할 수 있고 혼용할 수 없습니다.
매개변수를 정의하거나 호출할 때 후행 쉼표 ,를 사용해 코드 가독성을 높일 수 있습니다.
1. 명명 매개변수 Named parameters
{}로 감싸며 기본적으로 선택 사항이고 required 키워드를 붙이면 필수로 전달해야 합니다.
1) 기본 널 허용 명명 매개변수 (기본값 없음, 기본값은 null이므로 타입에 ?를 붙여 널 허용해야 함)
void enableFlags({bool? bold, bool? hidden}) {}
// 호출 시 매개변수 이름을 명시
enableFlags(bold: true, hidden: false);
Code language: Dart (dart)
완전 예시
/// 텍스트 스타일 플래그 활성화: 굵게, 숨기기
void enableFlags({bool? bold, bool? hidden}) {
print('굵게: $bold, 숨기기: $hidden');
}
void main() {
// 모든 명명 매개변수 전달
enableFlags(bold: true, hidden: false);
// 한 개만 전달하고 나머지는 null
enableFlags(bold: true);
// hidden만 전달
enableFlags(hidden: true);
// 아무 매개변수도 전달하지 않으면 두 값 모두 null
enableFlags();
}
Code language: Dart (dart)
실행 결과
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
굵게: true, 숨기기: false
굵게: true, 숨기기: null
굵게: null, 숨기기: true
굵게: null, 숨기기: nullCode language: JavaScript (javascript)
2) 컴파일 타임 상수 기본값 설정 (널 허용 타입이 필요하지 않음, 기본값이 있으므로)
void enableFlags({bool bold = false, bool hidden = false}) {}
enableFlags(bold: true); // hidden은 자동으로 false 적용
Code language: Dart (dart)
예시
/// 텍스트 스타일 플래그 활성화: 굵게, 숨기기
void enableFlags({bool bold = false, bool hidden = false}) {
print('굵게: $bold, 숨기기: $hidden');
}
void main() {
enableFlags();//매개변수를 전달하지 않으면 두 매개변수 모두 기본값 false 적용 → 굵게: false, 숨기기: false
}
Code language: Dart (dart)
3) 필수 강제 명명 매개변수 required (참고: Flutter 위젯 생성자에서 자주 사용) 아래 예시는 Flutter 코드이며 자세한 내용은 Flutter 강의에서 다룹니다.
const Scrollbar({super.key, required Widget child});
// required 매개변수에도 널 허용 타입 사용 가능
const Scrollbar({super.key, required Widget? child});
Code language: Dart (dart)
Dart는 명명 매개변수를 매개변수 목록 맨 뒤에 적어야 한다는 규칙이 없어 목록 어느 위치에나 삽입할 수 있습니다
repeat(times: 2, () {})Code language: CSS (css)
2. 선택적 위치 매개변수 Optional positional parameters
[]로 감싸며 전달하지 않으면 기본값 null이고 상수 기본값을 설정할 수 있습니다.
// 기본값 없고 타입에 ? 추가
String say(String from, String msg, [String? device]) {
var result = '$from says $msg';
if (device != null) result = '$result with a $device';
return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
assert(say('Bob', 'Howdy', 'smoke signal') == 'Bob says Howdy with a smoke signal');
Code language: Dart (dart)
완전 예시
// [String? device] 선택적 위치 매개변수로 기본값 없고 전달하지 않으면 null
String say(String from, String msg, [String? device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device'; //device가 null이 아닐 경우 문장 뒤에 기기 정보 추가
}
return result;
}
void main() {
// 세 번째 선택 매개변수 미전달
String res1 = say('Bob', 'Howdy');
print(res1);
// 세 번째 선택 위치 매개변수 전달
String res2 = say('Bob', 'Howdy', 'smoke signal');
print(res2);
// 선택 매개변수에 직접 null 전달
String res3 = say('Alice', 'Hi', null);
print(res3);
}
Code language: Dart (dart)
기본값 설정하기:
String say(String from, String msg, [String device = 'carrier pigeon']) {
return '$from says $msg with a $device';
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');
Code language: Dart (dart)
완전 예시
// 선택적 위치 매개변수에 carrier pigeon(비둘기 편지) 기본값 설정
String say(String from, String msg, [String device = 'carrier pigeon']) {
return '$from says $msg with a $device';
}
void main() {
// 세 번째 선택 매개변수를 전달하지 않으면 자동으로 기본값 carrier pigeon 사용
String res1 = say('Bob', 'Howdy');
print(res1); //Bob says Howdy with a carrier pigeon
// 직접 기기를 전달해 기본값 덮어쓰기
String res2 = say('Bob', 'Howdy', 'smoke signal');
print(res2); //Bob says Howdy with a smoke signal
String res3 = say('Alice', 'Hello', 'mobile phone');
print(res3); // Alice says Hello with a mobile phone
String res4 = say('Jack', 'Tom', 'website foxdevelop.com');
print(res4); // Jack says Tom with a page
}
Code language: Dart (dart)
실행 결과
Built firstdart:firstdart.
Bob says Howdy with a carrier pigeon
Bob says Howdy with a smoke signal
Alice says Hello with a mobile phone
Jack says Tom with a website foxdevelop.comCode language: JavaScript (javascript)
main () 진입점 함수
모든 Dart 프로그램에는 최상위 레벨 main() 함수가 필수이며 프로그램의 유일한 실행 진입점입니다.
- 매개변수 없는 기본 버전
void main() {
print('Hello, wellcome to foxdevelop.com!');
}
Code language: Dart (dart)
Hello, wellcome to foxdevelop.com!Code language: CSS (css)
- 명령줄 프로그램 실행 인자 수신
// 실행 명령어: dart run firstdart.dart 1 test
void main(List<String> arguments) {
print(arguments); // [1, test]
}
Code language: Dart (dart)
복잡한 명령줄 인자 파싱은 공식 args 라이브러리를 사용할 수 있습니다.
위 코드의 firstdart.dart는 본인의 dart 파일 이름으로 교체해야 합니다.

일급 객체로서의 함수 First-class objects
1. 함수를 매개변수로 전달
void printElement(int element) => print(element);
var list = [1, 2, 3];
list.forEach(printElement); // 함수를 직접 전달
Code language: Dart (dart)
완전 예시
void main() {
void printElement(int element) => print(element);
var list = [1, 2, 3];
list.forEach(printElement); // 함수를 직접 전달
}
Code language: Dart (dart)
출력 결과
1
2
3
2. 익명 함수를 변수에 할당
void main() {
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
print(loudify('hello')); //!!! HELLO !!!
}
Code language: Dart (dart)
함수 타입 Function Type
함수 변수의 타입을 명시적으로 선언할 수 있습니다.
형식:반환타입 Function(매개변수 목록, {명명매개변수})
void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
// 함수 타입 변수 선언
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');
Code language: Dart (dart)
typedef로 함수 타입에 별칭을 지정해 재사용하고 가독성을 높일 수 있습니다.
{String greeting}) → 한 개의 명명 매개변수 greeting이며 타입은 String
void main() {
void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
// 함수 타입 변수 선언
void Function(String, {String greeting}) g = greet;
g('Jack', greeting: 'Howdy');
}
Code language: Dart (dart)
출력 결과
Howdy Jack!
익명 함수 (Lambda / Closure 클로저)
함수 이름이 없는 함수로 두 가지 작성 방식이 있습니다:
- 블록 문법 (여러 줄)
void main() {
const list = ['apples', 'bananas'];
var uppercaseList = list.map((item) {
return item.toUpperCase();
}).toList();
print(uppercaseList);
}
Code language: Dart (dart)
위 코드의 (item) { return item.toUpperCase(); }가 익명 함수(lambda) 입니다.
출력:[APPLES, BANANAS]
- 화살표 축약 문법 (한 줄 표현식)
void main() {
const list = ['fox', 'develop','foxdevelop.com'];
var uppercaseList = list.map((item) => item.toUpperCase()).toList();
uppercaseList.forEach((item) => print('$item: ${item.length}'));
}
Code language: Dart (dart)
출력 결과
FOX: 3
DEVELOP: 7
FOXDEVELOP.COM: 14Code language: HTTP (http)
어휘 범위 Lexical scope
Dart는 정적 어휘 범위를 사용합니다: 변수의 범위는 코드 중괄호 계층으로 결정되며 내부 범위에서 외부의 모든 변수에 접근할 수 있습니다.
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction() {
var insideNestedFunction = true;
// 모두 접근 가능
assert(topLevel && insideMain && insideFunction && insideNestedFunction);
}
}
}
Code language: Dart (dart)
어휘 클로저 Lexical closures
클로저: 함수 객체가 외부 범위의 변수를 포착하고 유지할 수 있어 원본 범위를 벗어난 뒤에도 접근 가능합니다.
Function makeAdder(int addBy) {
// 외부 변수 addBy 포착
return (int i) => addBy + i;
}
void main() {
var add2 = makeAdder(2);
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
Code language: Dart (dart)
위 코드의 실행 과정을 단계별로 분석
1단계:var add2 = makeAdder(2);
makeAdder실행,addBy = 2전달- 익명 함수 생성:
(int i) => 2 + i - 이 함수를 변수
add2에 할당. 즉 add2는 함수 객체가 되며 본문은(int i) => 2 + i로 int형 인자 i를 받아 i+2를 반환합니다. makeAdder함수 실행이 끝나도 익명 함수는addBy=2를 기억하고 유지합니다.
2단계:var add4 = makeAdder(4);
makeAdder를 다시 실행,addBy = 4전달- 완전히 독립적인 새로운 익명 함수 생성:
(int i) => 4 + i add4에 할당되며 자체적으로addBy=4를 저장하고 add2와 간섭하지 않음. 즉 addBy는 두 함수 객체 add2, add4에 개별 기록되어 한쪽은 2, 다른 한쪽은 4로 저장됩니다.
makeAdder(2)실행 시 독립적인 클로저가 생성되고 개별적으로addBy = 2가 바인딩되어add2에 할당됨;makeAdder(4)실행 시 또 다른 새 클로저가 생성되고 개별적으로addBy = 4가 바인딩되어add4에 할당됨;add2와add4는 서로 완전히 분리된 함수 객체로 각자 포착한addBy를 사적으로 저장하며 서로 영향을 주지 않음.
3단계:add2(3) 호출
내부 함수 실행:2 + 3 = 5, 어서션 성립
4단계:add4(3) 호출
내부 함수 실행:4 + 3 = 7, 어서션 성립
함수 티어오프 Tear-offs
함수/메서드를 호출할 때 괄호를 붙이지 않으면 티어오프(가벼운 클로저)가 생성되어 직접 작성하는 익명 람다를 대체하며 코드가 간결해집니다.
권장 작성법으로 함수를 그대로 전달하기만 하면 됩니다.
var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();
charCodes.forEach(print); // 함수 티어오프
charCodes.forEach(buffer.write); // 메서드 티어오프
Code language: Dart (dart)
불필요하게 긴 작성법
charCodes.forEach((code) => print(code));
charCodes.forEach((code) => buffer.write(code));
Code language: Dart (dart)
함수 동일성 판단
- 최상위 함수, 정적 메서드: 함수명이 완전히 같으면
==로 참; - 인스턴스 메서드: 동일한 객체 인스턴스에 바인딩된 메서드 티어오프만 동일한 것으로 판단.
void foo() {}
class A {
static void bar() {}
void baz() {}
}
void main() {
var x = foo;
assert(foo == x);
x = A.bar;
assert(A.bar == x);
var v = A(), w = A(), y = w;
x = w.baz;
assert(y.baz == x); // 동일 인스턴스이므로 동일
assert(v.baz != w.baz); // 다른 인스턴스이므로 다름
}
Code language: Dart (dart)
반환값 Return values
- 모든 함수는 반드시 반환값을 가짐:명시적인
return이 없으면 암시적으로return null;이 맨 끝에 추가됨
foo() {}
assert(foo() == null);
Code language: Dart (dart)
- 여러 값 반환: Record 레코드 튜플 사용
(String, int) foo() {
return ('something', 42);
}
Code language: Dart (dart)
Getter & Setter 접근자
모든 속성 읽기/할당은 본질적으로 getter/setter 호출입니다. 일반 변수는 암시적 접근자가 자동 생성되며 직접 명시적으로 정의할 수도 있습니다.
장점: 외부에서는 통일된 .속성명으로 호출하고 내부 로직은 언제든 수정해도 외부 코드를 고칠 필요가 없음.
String _secret = 'Hello';
// Getter 읽기용
String get secret {
print('getter 호출됨');
return _secret.toUpperCase();
}
// Setter 할당용
set secret(String newMsg) {
print('setter 호출됨');
if (newMsg.isNotEmpty) _secret = newMsg;
}
void main() {
print(secret); // 읽기, getter 실행 → HELLO
secret = 'Dart is fun'; // 할당, setter 실행
print(secret); // DART IS FUN
}
Code language: Dart (dart)
* 제너레이터 Generators
필요할 때 지연적으로 시퀀스를 생성하며 동기, 비동기 두 종류가 있고 yield로 값을 배출합니다.
- 동기 제너레이터
sync*, 반환 타입Iterable
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
Code language: Dart (dart)
- 비동기 제너레이터
async*, 반환 타입Stream
Stream<int> asyncNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
Code language: Dart (dart)
- 재귀 제너레이터 최적화
yield*(다른 제너레이터에 위임해 성능 향상)
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
Code language: Dart (dart)
Function