카테고리 없음

Dart 시작하기 - #3 Functions

샤크데브 2025. 7. 28. 11:54

 

#3.0 Defining a Function

 

void는 이 함수가 아무것도 return 하지 않고 콘솔에 출력하기만 한다는 뜻을 가진다. 

 

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

void main() {

}

 

만약 출력대신 return을 한다면 오류가 나는데, void는 return을 하지 않는 함수이기 때문에 그런 것이며, 

이 경우 void를 return을 받고 있는 String으로 업데이트 해주면 된다.

 

sayHello는 String을 return 하는 함수 / 하나의 parameter을 가지며 이름은 $name.

꼭 void main 안에 있을 필요는 없다. 

 

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

void main() {
  print(sayHello('dart'));
}

 

main안에 sayHello를 호출해주어야 한다.

 

 

곧바로 return하는 function을 가질 때 fat arrow syntax를 이용하여 코드를 변경 할 수 있다.

 

String sayHello(String name) => "Hello $name nice to meet you";


void main() {
  print(sayHello('dart'));
}

 

fat arrow syntax는 코드가 한줄일 경우에 유용하게 사용 가능하다. (긴 경우에는 중괄호를 사용하고 마지막 코드를 return하는 형식으로)

 

num plus(num a, num b) => a + b;

 

예를 들어 a와 b를 더하는 함수를 선언했을때 위와 같이 사용 가능하다. 

 

 

#3.1 Named Parameter

 

dart의 함수는 named parameter 라는 것을 지원한다. (플러터에서 자주 사용된다)

 

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

void main() {
  print(sayHello('dart', 19, 'cuba'));
}

 

예를 들어 위와 같이 name, age, country 3가지 값을 출력하는 함수를 만들었을 때

해당 함수를 실행하기 위해 print(sayHello('dart', '19', 'cuba')); 는 작성자가 헷갈리기 쉽기 때문에 named argument를 사용하면 훨씬 나아진다.

 

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, 
  	country: 'cuba', 
  	name: 'dart'
  ));
}

 

사용방법은 위와 같다. 먼저 함수 선언 쪽에서 중괄호를 추가하고, 아래에서는 해당 변수에 대한 값을 하나씩 정해준다.

하지만 이 경우에도 오류가 나는 것을 확인 할 수 있다. 그 이유는 numm saftey가 적용되어 있기 때문인데, 위처럼 3가지 값에 모두 값이 지정되지 않는 경우를 dart는 고려하고 있기 때문이다.

 

void main() {
  print(sayHello(
  	age: 12, 
  ));
}

 

예를 들어 유저가 나이만 입력하는 경우 등을 고려하고 있는 것이다. 이 문제를 해결하는 2가지 방법이 있다.

 

 

 

named argument에 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(
    	age: 12,
    ));
}

 

코드를 이렇게 작성하면 값을 모두 입력하지 않아도 문제 없이 출력된다.

 

 

하지만 default value 없이 유저에게 실제 data를 받아야 한다면 어떻게 해야 할까?

 

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(
    age: 12, 
    country: 'cuba', 
    name: 'dart'
  ));
}

 

named parameter가 사실 required라고 명시해준다. 이런식으로 required를 이용하면, sayHello가 호출될 때 반드시 name, age, country 값이 필요하다는 것을 게 된다. 

required modifier을 이용하여 필수 값으로 만들어주는 것이다.

 

 

#3.2 Recap

 

positional parameter

parameter의 순서가 String, String, int 일때 이 순서대로 값을 입력해주어야 하기 때문에 번거롭다. 

sayHello("name", "country", "age") 이런식으로 각각의 위치에 어떤 값이 있는지 기억해야 한다.

선언되어 있는 함수와 호출되는 값 모두 확인해야 하기 때문에 2번씩 확인해야 한다.

 

named parameter

named parameter은 그럴 필요 없이 이름과 함께 입력해주기 때문에 번거롭지 않다.

 

print(sayHello(
    age: 12, 
    country: 'cuba', 
    name: 'dart'
  ));

 

위처럼 parameter와 값을 함께 입력하여 한쪽만 봐도 알 수 있도록 한다.

 

required

유저가 값을 넣지 않는 경우를 방지하기 위하여 required를 이용할 수 있다. 

 

defalut value

값이 없는 것을 대비하여 미리 값을 입력하는 default value를 이용할 수 있다.

 

 

#3.3 Optional Positional Parameters

 

만약 named argument를 적용하고 싶지 않은데 country는 required 하지 않게 하려면 어떻게 해야 할까?

 

String sayHello(
  String name, 
  int age, 
  [String? country = 'cuba']
) => 
  'Hello $name, you are $age years old from $country';


void main() {
  sayHello('dart', 12);
}

 

대괄호를 씌워주고 country는 not required 라고 표시한다. default value를 부여한다.

 

이렇게 코드를 작성하면 마지막 argument를 보내지 않아도 sayHello 함수를 부를 수 있게 되었다.

 

 

 

#3.4 QQ Operator 

 

이름을 대문자로 return 하는 함수 만들기

 

String capitalizeName(String? name) => name.toUpperCase();


void main(){
  capitalizeName('dart');
  capitalizeName(null);
}

 

위처럼 String? 를 이용하여 코드를 작성하면 null 부분에서는 오류가 나지 않지만, null일 수도 있는 값에 toUpperCase()를 호출할 수 없기 때문에 오류가 난다.

 

 

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

void main(){
  capitalizeName('dart');
  capitalizeName(null);
}

 

if문을 작성하여 null이 아닌 경우에만 함수가 실행되도록 다음과 같이 작성할 수 있다.

 

이를 더 간단히 작성할 수 있는 방법이 있는데 fat arrow를 사용하는 것이다.

 

String capitalizeName(String? name) => name != null ? name.toUpperCase() :  'ANON';


void main(){
  capitalizeName('dart');
  capitalizeName(null);
}

 

물론 더 짧아졌지만 동일하게 실행되기 때문에 결과 값은 같다. name이 null이 아니 nametoUpperCase()를 return하고 null이라면 'ANON' 을 return한다.

이를 더 간단하게 줄이는 법이 바로 QQ Operator이다.

 

QQ(question question) Operator

left ?? reight 

좌항이 null 이면 우향을 return 한다.
좌항이 null이 아니라면 그대로 좌항을 return 한다.

 

String capitalizeName(String? name) => name.toUpperCase() ?? 'ANON';


void main(){
  capitalizeName('dart');
  capitalizeName(null);
}

 

 

하지만 이 경우에도 오류가 나는데, 그 이유는 name 자체가 null인 경우 toUpperCase를 호출 할 수 없기 때문이다.

 

String capitalizeName(String? name) => name?.toUpperCase() ?? 'ANON';


void main(){
  capitalizeName('dart');
  capitalizeName(null);
}

 

이 경우 name 뒤에 ?를 붙여 쉽게 해결 할 수 있다.

 

 

QQ equals / QQ assignment operator

 

void main(){
  String? name;
  name ??= 'dart';
  name ??= 'another';
  print(name);
}

 

다음 코드를 실행 하면 dart라는 결과와 경고가 함께 출력된다. 이는 당연한 것인데, name이 dart라는 값을 받은 이후에는 null이 될 일이 없기 때문이다.

 

void main(){
  String? name;
  name ??= 'dart';
  name = null;
  name ??= 'another';
  print(name);
}

 

중간에 null을 넣어주면 경고 없이 another이 출력되는 것을 확인 할 수 있다.

 

 

#3.5 Typedef

 

자료형이 헷갈릴 때 도움이 될 alias를 만드는 방법이다.

 

숫자로 된 List를 반대로 뒤집어서 return 하는 함수를 만들 때

List<int> reverseListOfNumbers(List<int> list){
  var reversed = list.reversed;
  return reversed.toList();
}

void main(){
}

 

다음과 같이 작성 할 수 있다. typedef를 이용하면 긴 코드에서 더 유용하게 사용 할 수 있으며, 해당 코드에서는 사용하면 아래와 같다.

 

typedef ListofInt = List<int>;

ListofInt reverseListOfNumbers(ListofInt list){
  var reversed = list.reversed;
  return reversed.toList();
}

void main(){
  print(reverseListOfNumbers([1,2,3]));
}

 

 

결과가 잘 실행되는 것도 확인 할 수 있다.

 

 

Map의 typedef를 만들고 싶을 수 있다. 예를 들어 사용자에게 인사하는 함수를 만드는데, 사용자가 함수에게 주는 값이 더 구체적으로 구조를 만들고 싶은 경우인데,

 

typedef UserInfo = Map<String, String>;

String sayHi(UserInfo userInfo){
  return "Hi${userInfo['name']}";
}

void main(){
  sayHi({"name": 'dart'});
}

 

만약 구조화된 데이터의 형태를 지정하고 싶다면 class를 제작해야 한다.