개발자/SupaBase && Flutter

04.(Dart 개념_노마드코더 강의) Functions

푸루닉 2024. 10. 23. 17:20

4.0 Defining a Function

Dart는 객체 지향 언어이기 때문에 함수도 객체이며, 타입은 Function입니다. 이를 통해 함수를 변수에 할당하거나 다른 함수의 인수로 전달할 수 있습니다. Dart에서는 함수 선언을 여러 방식으로 할 수 있습니다.

1. 기본적인 함수 선언

String sayHello(String name) {
  return "Hello ${name}, nice to meet you.";
}

num plus(num a, num b) {
  return a + b;
}

2. Fat Arrow Syntax(=>)

Fat arrow syntax는 하나의 표현식만 포함하는 함수에서 사용할 수 있는 단축 구문입니다. 함수의 본문을 한 줄로 표현할 때 사용되며, 표현식의 결과가 함수의 반환값이 됩니다.

// 일반 함수 선언을 fat arrow syntax로 변환
String sayHello(String name) => "Hello ${name}, nice to meet you.";

num plus(num a, num b) => a + b;
  • =>return을 의미하는 역할을 합니다. 즉, => 뒤의 표현식이 바로 반환됩니다.
  • 본문이 단 한 줄일 때만 사용 가능하며, 여러 줄이 필요하다면 {} 블록을 사용해야 합니다.

3.함수 사용 예시

void main() {
  print(sayHello("sugar"));  // 출력: Hello sugar, nice to meet you.
  print(plus(5, 10));        // 출력: 15
}
Fat Arrow Syntax의 장점
  • 간결함: 함수가 짧고 단순할 때 사용하면 코드가 훨씬 간결해집니다.
  • 가독성: 한 줄로 표현된 함수는 더 직관적으로 읽히기 때문에 가독성이 좋습니다.
주의 사항
  • Fat arrow syntax는 하나의 표현식만 포함할 수 있습니다. 여러 줄의 로직이 필요한 함수는 블록({})을 사용해야 합니다.

4.1 Named Parameters (Named Arguments)

Dart에서는 함수의 매개변수를 더 명확하고 유연하게 다루기 위해 Named Parameters를 지원합니다. 이는 특히 함수 호출 시 매개변수의 순서를 잊거나, 어떤 값이 어떤 매개변수를 의미하는지 헷갈릴 때 매우 유용합니다.

1. 기본 함수 선언 문제점

String sayHello(String name, int age, String country) {
  return "Hello $name, you are $age, and you come from $country";
}

void main() {
  print(sayHello('nico', 19, 'cuba'));
}
  • 위 코드는 매개변수가 순서에 의존합니다. 사용자가 순서를 잊거나 인자가 뜻하는 것이 무엇인지 헷갈릴 수 있습니다.

2. Named Parameters를 사용하여 해결

Named Parameters를 사용하면 함수 호출 시 매개변수의 이름을 명시적으로 지정할 수 있어, 순서에 상관없이 이해하기 쉬운 코드를 작성할 수 있습니다.

String sayHello({String name, int age, String country}) {
  return "Hello $name, you are $age, and you come from $country";
}

void main() {
  print(sayHello(
    age: 12,
    name: 'nico',
    country: 'cuba',
  ));
}
  • 중괄호({}) 를 사용하여 매개변수를 named parameters로 만들 수 있습니다.
  • 함수를 호출할 때 매개변수의 이름과 값을 함께 입력하므로, 순서를 지키지 않아도 됩니다.

3. Null Safety 문제 해결 방법

Dart는 null safety를 적용하므로, named parameters 중 하나가 null일 경우 문제가 발생할 수 있습니다. 이를 해결하는 두 가지 방법이 있습니다.

3.1 Default Value 지정

매개변수에 기본값을 지정하여, 값이 전달되지 않았을 때도 안전하게 처리할 수 있습니다.

String sayHello({
  String name = 'anon',
  int age = 99,
  String country = 'wakanda',
}) {
  return "Hello $name, you are $age, and you come from $country";
}

void main() {
  print(sayHello());  // 매개변수를 전달하지 않아도 기본값이 사용됨
}
  • 기본값을 지정하면, 함수 호출 시 인수를 생략해도 default value가 사용됩니다.
    3.2 Required Modifier 사용
  • required* 키워드를 사용하여 매개변수가 반드시 전달되어야 한다고 지정할 수 있습니다.
String sayHello({
  required String name,
  required int age,
  required String country,
}) {
  return "Hello $name, you are $age, and you come from $country";
}

void main() {
  // print(sayHello()); // 컴파일 오류 발생 (required 매개변수가 없으므로)

  print(sayHello(
    name: 'jisoung',
    age: 17,
    country: 'korea',
  ));  // 올바른 호출
}
  • required 를 사용하면 해당 매개변수는 반드시 값이 있어야 하며, 그렇지 않으면 컴파일 에러가 발생합니다.

4. 잘못된 코드 예시와 Named Parameters로 해결

문제: 인수의 의미를 쉽게 알 수 없음
String sayHello({
  required String name,
  required int age,
  required String country,
}) {
  return "Hello $name, age: $age, country: $country";
}

void main() {
  print(sayHello(
    name: "jisoung",
    age: 17,
    country: "korea",
  ));
}
  • Named Parameters를 사용하면 함수 호출 시 인수의 의미를 명확히 알 수 있어 가독성이 높아집니다.5. 정리
  • Named Parameters를 사용하면 매개변수의 순서를 지키지 않아도 되며, 매개변수의 의미를 명확히 할 수 있습니다.
  • Default Valuerequired 키워드를 통해 null safety를 보장할 수 있습니다.
  • 이를 통해 코드의 가독성을 높이고, 더 안전한 함수 호출이 가능합니다.

4.2 Optional Positional Parameters

Dart에서는 [] 를 사용하여 Optional Positional Parameters(옵셔널 위치 매개변수) 를 정의할 수 있습니다. 이때 매개변수는 선택적으로 제공될 수 있으며, 함수를 호출할 때 반드시 값을 전달하지 않아도 됩니다.

1. 기본 개념

  • nameage는 필수 매개변수로 함수가 호출될 때 반드시 값을 전달해야 합니다.
  • countryOptional Positional Parameter로, 제공하지 않으면 기본값을 사용하거나 생략할 수 있습니다.

2. 코드 예시

String sayHello(String name, int age, [String? country = ""]) {
  return 'Hello ${name}, You are ${age} from the ${country}';
}

void main() {
  var result = sayHello("sugar", 10);  // country 생략 가능
  print(result);  // 출력: Hello sugar, You are 10 from the 
}
  • [String? country = ""]: 대괄호([]) 안에 작성된 매개변수는 Optional Positional Parameter로, 제공되지 않으면 기본값 ""가 사용됩니다.
  • Optional 매개변수를 생략할 수 있기 때문에, sayHello("sugar", 10)와 같이 호출할 수 있습니다.3. Optional Positional Parameters의 특징
  • 매개변수의 순서를 반드시 지켜야 합니다.
  • 선택적 매개변수는 필수 매개변수 뒤에 위치해야 합니다.
  • 기본값을 설정하지 않으면, 생략된 경우 해당 매개변수는 null이 됩니다.
    String sayHello(String name, int age, [String? country]) {
    return 'Hello ${name}, You are ${age} from the ${country ?? 'nowhere'}';
    }
    여기서 ?? 연산자는 null인 경우 기본값 'nowhere'를 사용할 수 있게 합니다.

4.3 QQ Operator

Dart의 QQ Operator (Null-aware Operator)

Dart에서는 Null-safety를 위해 QQ Operator(Null-aware Operator)를 자주 사용합니다. 이를 통해 null 값이 발생할 때 대체 값을 쉽게 지정할 수 있으며, 코드의 가독성을 높여줍니다.

1. Null-safe 코드 작성

다음 코드는 null 값을 허용하지 않기 때문에 오류가 발생할 수 있습니다.

String upperName(String name) => name.toUpperCase();

void main() {
  upperName('jisoung');
  upperName(null);  // Error 발생! 
}

2. 전통적인 null 체크 방식

null 값을 허용하고, null이 아닐 경우에만 특정 작업을 수행하는 코드를 작성할 수 있습니다.

String upperName(String? name) {
  if (name != null) {
    return name.toUpperCase();
  }
  return 'NONE';
}

3. 삼항 연산자로 코드 간결화

위 코드를 삼항 연산자를 사용하여 더 짧게 쓸 수 있습니다.

String upperName(String? name) => name != null ? name.toUpperCase() : "NONE";

4. QQ Operator로 더 간결하게 작성

Dart에서는 ?? (Null-coalescing operator) 를 사용하여 코드를 더 짧고 간결하게 작성할 수 있습니다.

String upperName(String? name) => name?.toUpperCase() ?? "NONE";
  • ?.: null일 수 있는 객체에 접근할 때 사용됩니다. null이 아닐 때만 메서드를 호출하고, null일 경우 null을 반환합니다.
  • ??: 왼쪽 값이 null이면 오른쪽 값을 반환합니다.5. QQ Equal (??=) Operator
  • ??=*변수에 값이 없을 때만 값을 할당하는 연산자입니다.
void main() {
  String? name;
  // 만약 name이 null이면 'jisoung'을 할당
  name ??= 'jisoung';
  print(name);  // 출력: jisoung
}
  • ??=: 왼쪽의 값이 null이면 오른쪽 값을 할당합니다.

6. 정리

  • ?.: null일 수 있는 객체에 안전하게 접근할 때 사용합니다.
  • ??: 왼쪽 값이 null일 경우 오른쪽 값을 반환합니다.
  • ??=: 값이 null일 때만 변수를 할당할 때 사용됩니다.

4.4 Typedef

Dart에서 typedef자료형에 사용자가 원하는 별명(alias)을 붙일 수 있는 기능입니다. 이를 통해 복잡한 타입을 더 간단한 이름으로 정의하거나, 특정 자료형에 대한 별명을 만들어 코드의 가독성을 높일 수 있습니다.

1. Typedef의 기본 사용법

typedef를 사용하여 자료형에 새로운 별명을 정의할 수 있습니다. 아래는 리스트(List)의 별명을 ListOfInts로 정의하는 예시입니다.

typedef ListOfInts = List<int>;

ListOfInts reverseListOfNumbers(ListOfInts list) {
  var reversedList = list.reversed.toList();
  return reversedList;
}
  • typedef ListOfInts = List<int>; : List<int> 타입을 ListOfInts라는 별명으로 정의했습니다.
  • 이를 통해 List<int>를 더 간결하게 ListOfInts로 표현할 수 있습니다.

2. Typedef의 장점

  • 코드 가독성 향상: 복잡한 타입을 짧고 명확한 별명으로 정의할 수 있어 가독성이 향상됩니다.
  • 유지보수 용이: 코드 변경 시 여러 곳에서 사용된 자료형을 쉽게 관리할 수 있습니다.

3. 함수 타입에 대한 typedef 사용

typedef는 함수의 시그니처에 대한 별명을 정의하는 데도 사용할 수 있습니다. 예를 들어, 특정 함수 타입을 별명으로 정의하면, 동일한 함수 타입을 여러 곳에서 간결하게 사용할 수 있습니다.

typedef IntOperation = int Function(int, int);

int add(int a, int b) => a + b;

void main() {
  IntOperation operation = add;
  print(operation(2, 3));  // 출력: 5
}

typedef IntOperation = int Function(int, int);: 두 개의 int를 받아 int를 반환하는 함수 타입에 IntOperation이라는 별명을 부여했습니다.