[Flutter / Dart] Dart 기초문법(함수와 람다) (2)

2025. 4. 3. 01:06

1. 익명 함수와 람다 함수

익명함수와 람다함수는 둘 다 이름이 없음. 일회성으로 사용된다는 공통점이 있음.

통상적으로 많은 언어에서 익명 함수와 람다함수를 구분하지만, Dart언어에서는 구분하지 않음. 

 

익명 함수와 람다 함수의 표현 방식

익명 함수 람다 함수
(매개변수) {
  함수 바디
}
(매개변수) => 단 하나의 스테이트먼트

익명 함수에서 { }를 빼고 `=>` 기호를 추가한 것이 람다 함수임! 매개변수는 아예 없거나 하나 이상이어도 됨.

익명 함수와 달리 코드 블럭을 묶는 { }가 없는 람다는 함수 로직을 수행하는 Statement가 하나여야만 함.(한 줄이 아닌 명령 단위가 하나!)
람다 함수는 이름을 정하고 미리 선언할 필요가 없어서 글로벌 스코프로 다룰 필요가 없음.

더 나아가 하나의 Statement만 다루기 때문에 적절히 사용하면 간결하게 코드를 작성할 수 있고 가독성이 높음.

그렇기 때문에 콜백 함수나 리스트의 `map()`, `reduce()`, `fold()` 함수 등에서 일회성이 높은 로직을 작성할 때 주로 사용함. 

항목 익명 함수(Anonymous Function) 람다 함수(Lambda Expression)
정의 방법 중괄호 `{}`로 코드 블록을 감싸서 함수 형태로 정의 `=>` 기호 사용, 한줄로 축약
함수 이름 없음 (이름 없이 정의) 없음 (이름 없이 정의)
사용 가능 로직 여러 줄의 코드 가능, 복잡한 처리 적합 단 하나의 표현식만 가능. 간결한 로직 작성 적합
중괄호 사용 여부 사용 사용 안함
return 키워드 명시적으로 `return` 사용 가능 `=>` 뒤의 표현식이 자동으로 `return`됨
사용 예시 콜백, 이벤트 핸들러, 반복문 등 `map`, `reduce`, `fold`, 일회성 짧은 로직에 적합
가독성 로직이 길거나 복잡할 때 사용 짧고 간결한 경우 가독성 우수
// 익명 함수로 List의 모든 값들을 더하는 로직
void main() {
  List<int> numbers = [1, 2, 3, 4, 5];
  
  final allMembers = numbers.reduce((value, element) {
    return value + element;
  });
  
  print(allMembers);
}

/* 출력값
15
*/
// 람다 함수로 List의 모든 값들을 더하는 로직
void main() {
  List<int> numbers = [1, 2, 3, 4, 5];
  
  final allMembers = numbers.reduce((value, element) => value + element);
  
  print(allMembers);
}

/* 출력값
15
*/

위 코드를 보면 `return value + element`의 코드를 `=> value + element`로 줄인것을 볼 수 있음. `=>`이 return의 기능을 대신한다?로 보면 될거같음. 

하지만, 이렇게 간단한 로직은 어떤걸 쓰든 취향이지만 로직이 길어지고 복잡할 떄는 익명 함수를 사용해야한다는 점! 


2. typedef와 함수

`typedef` 키워드는 함수의 시그니처를 정의하는 값으로 보면 됨. 여기서 시그니처는 반환값 타입, 매개변수 개수와 타입 등을 말함.

즉, 함수 선언부를 정의하는 키워드임. 함수가 무슨 동작을 하는지에 대한 정의는 없음!

typedef Operation = void Function(int x, int y);

함수를 위에 선언하기는 했지만 무얼하는지 동작이 없음. 그럼 이걸 어떻게 사용해야 할까? 시그니처에 맞춘 함수를 만들어서 사용하면 됨!

아래 코드를 보겠음.

typedef Operation = void Function(int x, int y);

void add(int x, int y) {
  print("결과값 : ${x + y}");
}

void subtract(int x, int y) {
  print("결과값 : ${x - y}");
}

void main() {
  // typedef는 일반적인 변수의 type처럼 사용 가능
  Operation oper = add;
  oper(1, 2);
  
  // subtract() 함수도 Operation에 해당되는 시그니처 이므로 oper변수에 저장 가능
  oper = subtract;
  oper(1, 2);
}

/* 출력값
결과값 : 3
결과값 : -1
*/

Dart언어에서 함수는 일급 객체 이므로 함수를 값처럼 사용할 수 있음.

그래서 플러터에서는 `typedef`로 선언한 함수를 다음과 같이 매개변수로 넣어 사용함.

typedef Operation = void Function(int x, int y);

void add(int x, int y) { // add라는 함수를 선언, 파라미터는 int x, int y, 반환 타입은 : void(아무것도 리턴하지 않음) 
  print("결과 값 : ${x + y}");
}

void calculate(int x, int y, Operation oper) { // 이곳에 있는 int x, int y, Operation oper는 파라미터
  oper(x, y); // oper는 아까 전달한 add 함수 자체임 즉, oper == add
              // 즉, oper == add라는 뜻, 결국엔 add(x, y)인 것임 여기에있는 x, y는 아규먼트
}

void main() {
  calculate(1, 2, add); // 여기에 있는 1, 2, add도 아규먼트
}

/* 출력값
결과값 : 3
*/

처음 쓰다보니 이해가 잘안되서 한줄씩 해석해 보겠음!

typedef Operation = void Function(int x, int y);
  • `Operation`이라는 함수 타입 별칭을 만듦
  • "입력은 `int x, int y`고, 반환값은 `void`인 함수"를 우리는 `Operation`이라고 부르기로 정했다.
void add(int x, int y) { ... }
  • `add`라는 함수를 만듦, 매개변수(파라미터)는 `int x`, `int y`, 반환 타입은 `void`(아무것도 리턴하지 않음) 
  • `(int, int)`를 받아서 `x + y`를 출력함
void calculate(int x, int y, Operation oper) {
  oper(x, y);
}
  • 세 번쨰 파라미터로 `Operation 타입 함수`를 전달받고 그걸 `oper(x, y)`로 실행함
  • 즉, 넘겨받은 함수를 바로 실행하는 구조임.
void main() {
  calculate(1, 2, add);
}
  • `x = 1`, `y = 2`, `oper = add`
  • 이걸로 `calcalate(1, 2, add)`가 실행됨 -> 내부에서 `add(1, 2)`가 호출됨
  • 그러면 `print("결과값 : ${1 + 2}")`가 실행 -> `결과 값 : 3` 출력

그럼 갑자기 의문이 드는 것, 물론 내 지식이 모자라는 거지만,, 어디는 왜 파라미터고 어디는 왜 아규먼트지?

용어 설명
파라미터(매개변수) 함수를 정의할 때 괄호 안에 쓰는 변수
아규먼트(인자) 함수를 호출할 때 실제 전달하는 값

라고 보면 됨..! 어우 헷갈려. 마지막만 남았다.


3. try ... catch

`try...catch`문의 목적은 특정 코드의 실행을 시도(try)해보고 문제가 있다면 에러를 잡는다(catch)는 뜻임.

`try...catch`문은 `try`와 `catch` 사이의 괄호에 에러가 없을 떄 실행할 로직을 작성하고 `catch`가 감싸는 괄호에 에러가 났을 때 실행할 로직을 작성하면 됨. 만약 `try`에서 에러가 발생할 시 바로 `catch`로직으로 넘어감!

// 에러가 없을 때의 try...catch문
void main() {
  try{
    // 에러가 없을 때 실행할 로직
    final String name = "이도서의 코딩 노트";
    
    print(name); // 에러가 없으면 "이도서의 코딩 노트" 출력
  } catch(error) {
    //에러가 있을 때 로직
    print(error);
  }
}

/* 출력값
이도서의 코딩 노트
*/

그러면 이제 에러가 났을 떄의 코드를 보겠음 `throw`키워드를 사용해 에러를 만들어 보겠음

// 에러 있는 코드
void main() {
  try {
    final String name = "이도서의 코딩 노트";
    
    throw Exception("이름이 잘못됐음!"); // 고의로 에러 발생
    
    print(name);
  } catch(error) {
    
    // try에서 에러가 발생해서 catch로직이 실행됨.
    print(error);
  }
}

/* 출력값
Exception: 이름이 잘못됐음!
*/

`throw`키워드는 에러를 발생시키는 키워드임 일단 기억만 해두는 걸로!!



https://github.com/Leedoseo/Flutter_DartBasic

 

GitHub - Leedoseo/Flutter_DartBasic

Contribute to Leedoseo/Flutter_DartBasic development by creating an account on GitHub.

github.com

 

 

 

https://www.notion.so/Dart-Flutter-Study-1cf9fd9f157980a5ab7efad394810871?pvs=4

 

Dart / Flutter Study | Notion

Made with Notion, the all-in-one connected workspace with publishing capabilities.

www.notion.so

 

BELATED ARTICLES

more