오랜만이다.
이번 시간엔 다트의 함수 문법에 대해 알아보긋다.
가장 흔하게 보이면서 필수인 main
도 다트 함수다. 앞에 void
, String
등의 함수 리턴 타입와 소괄호, 중괄호가 있으면 함수다.
void sayHello(String name) {
print("Hello $name nice to meet you!");
}
void main() {
sayHello('뿔소');
}
이렇게 말이다. 당연히 실행해주려면 main
함수에 넣어서 파라미터를 넣어준 후 실행해주자.
아까 본 void
를 칭한다. 예시를 보며 설명하겠다.
void sayHello(String name) {
print("Hello $name nice to meet you!");
}
// 에러
void sayHelloError(String name) {
return "Hello $name nice to meet you!";
}
String sayHelloString(String name) {
return "Hello $name nice to meet you!";
}
void main() {
...
}
차이점을 알겠는가? 2번째 sayHello
는 에러가 난다.
그 외 sayHello
들은 에러가 안났다. 위 사례를 보며 설명하겠다.
void
: 콘솔에 출력만 하는 부가적인 효과만 나왔다. 즉, 값을 반환하지 않는 함수를 의미한다. 주로 콘솔에 출력하거나 상태를 업데이트하는 등의 작업을 수행하는 함수로 사용된다.String
등 타입 지정 : 그 타입의 값을 반환해야하는 함수다. return
이 꼭 필요하다는 뜻.String sayHello(String name, int age, String country) {
return "Hello $name, you are $age, and you come from $country";
}
문자열을 리턴해야하는 함수 sayHello
를 예시로 들었다. 그와 더불어 파라미터는 문자열 name
, country
와 integer
를 받는 age
(num
은 double
도 받기에 int
로 쓴 모습)로 구성돼있다.
그러면 당연히 함수 호출을 할 때, 지정 null
값이 아닌 이상 파라미터를 전부 만들어줘야한다.
sayHello('뿔소', 25, "Korea");
파라미터 기본 형태이다. 함수 파라미터에 그냥 소괄호()만 있고 작성해준 것이다. 이때는 호출부의 Argument의 순서가 중요해진다.
String sayHello(String name, int age, String country) {
return "Hello $name, you are $age, and you come from $country";
}
sayHello('뿔소', 25, "Korea");
위처럼 말이다. 근데 문제가 순서를 알아야하고, 직관적이지 않다는 문제가 있다.
그래서 주관적으로 파라미터가 2개 이하면 Positional을 써도 되지만 3개가 넘어가면 Named Parameter를 쓴다. 위 문제들을 해결할 수 있는 파라미터 작성법이다.
위 sayHello
함수 호출부를 봤을 때 어떤 역할인지 잘 모르지 않는가? 직관적이지 않고, 호출할 때 복잡한 함수면 직접 찾아가서 뭐가 들어가는지 등등을 봐야하니까.
그럴 때 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(
age: 25,
name: '뿔소',
country: "Korea"
));
}
이렇게 말이다! 호출부에 순서를 선언부의 파라미터와 다르게 써도 괜찮다. 훨씬 직관적이고 편하다.
심지어 Hover 했을 때 저렇게 간단한 설명도 볼 수 있으니 어찌 안쓰랴!! 플러터에서 많이 애용하는 문법이다.
근데 만약 위 함수를 그대로 쓰면 에러가 날 것이다.
이렇게 말이다! 사용자가 null이나 함수 호출부에서 Argument를 입력하지 않는 불상사를 막기 위해서 저런 에러가 나는 것이다. 다양한 해결법이 있다.
가장 직관적인 방법이다.
String sayHello({
String name = "철수",
int age = 0,
String country = "Korea"
}) {
return "Hello $name, you are $age, and you come from $country";
}
void main() {
print(sayHello(name: '뿔소', age: 25, country: "Korea"));
}
저렇게 파라미터에 기본 값을 할당해주는 것이다. 즉, 아무것도 입력하지 않아도 null
이 되지 않아 에러가 안나는 것이다.
하지만 sayHello()
로 끝내도 아무런 문제가 없기도 하고, 값이 맨날 할당되기에 버그가 발생하기 좋은 환경이다. 다음 방법을 보자.
파라미터 앞에 required
를 지정해서 호출할 때 이 파라미터가 필수적으로 들어가야한다고 표시하는 기능이다.
String sayHello({
required String name,
required int age,
required String country
}) {
return "Hello $name, you are $age, and you come from $country";
}
이렇게 말이다. 즉, null
이 아니란 표시고, '호출부에 작성하지 않으면 에러나게 해주세요~'라는 표시다.
위 사진처럼 호출부에 에러를 줘 컴파일 되지 않게 막는 것이다. 되게 편한 기능!
3번째 방법은 전편에서 봤다싶이 변수 타입 지정자에 ?
를 붙이는 것이다. 아예 nullable
하다고 표시하면 들어가든 말든 상관 없으니 말이다. required
과 반대되는 개념이다.
어떤 느낌인지 딱 오지않는가?! ?
은 필요 없는 파라미터만 사용하자.
여기서 또 named냐, positional이냐가 나뉘는데 아래의 예시를 보자.
String sayHelloPositional(String? name, int? age, [String? country = "Korea"]) {
return "Hello $name, you are $age, and you come from $country";
}
String sayHelloNamed({String? name, int? age, String? country = "Korea"}) {
return "Hello $name, you are $age, and you come from $country";
}
void main() {
print(sayHelloPositional('뿔소', 25));
print(sayHelloNamed(name: '뿔소', age: 25));
}
이런 느낌이다. 근데 주관적으로 봤을 때 Named가 더 직관적이고 편하다.. 난 걍 Named 쓸래..
TS, JS 쟁이들은 화살표 함수를 어떻게 쓰는지 안다!
String sayHelloString(String name) => "Hello $name nice to meet you!";
void main() {
print(sayHelloString('뿔소'));
}
중괄호{}와 return을 생략하고 =>
를 써주면 된다! 대신 1줄 함수만 가능하다. 1줄로 쭉 쓰니까..
num plus(num a, num•b) => a + b;
이런 식으로 쓰면 된다.
제목 무슨일인가 싶지만 나도 어떻게 명칭을 써야할지 모르겠어서 이렇게 적었다.
바로 ??
와 ?=
연산자(Operator)를 배워보자. 다트 안 연산자가 앞 2개만 있는 건 아닌데 다트에서 특이한 연산자라 배우는 것이다. 나머지 기본 연산자는 다 있다. 부등호나 뭐 비교 연산자 등등
??
만약 아래와 같이 대문자로 바꿔주는 함수, capitalizeName
이 있다.
String capitalizeName(String? name) {
if (name != null) {
return name.toUpperCase();
}
return "NULL";
}
String capitalizeNamee(String? name) => name != null ? name.toUpperCase() : "NULL";
void main() {
print(capitalizeName("rhino"));
print(capitalizeName(null));
}
// RHINO
// NULL
null
처리를 해주려고 조건문을 써서 4줄의 코드로 함수를 완성했다. 삼항연산자로 하니 1줄로 끝낸다. 근데 여기서 ??
연산자를 써주면 더 짧게 할 수 있다.
String capitalizeName(String? name) => name?.toUpperCase() ?? "NULL";
이렇게 말이다.
즉, ??
연산자는
left ?? right
가 있을 때, left
가 null
이라면 right
를 리턴하는 아주 간단한 조건문이다.
??=
얘는 위 ??
랑 비슷한 매커니즘이다. 변수가 null일 경우에만 값을 할당하여 리턴하는 역할을 한다.
void main() {
String name;
// name 변수가 null이므로 'Guest' 문자열을 할당.
name ??= 'Guest';
print(name); // 출력: Guest
// name 변수가 이미 'Guest' 문자열을 가지고 있으므로 'John' 문자열을 할당하지 않음.
name ??= 'John';
print(name); // 출력: Guest
}
이렇게 말이다! 할당 후 리턴이 되므로 John
은 절대 나오지 않는 모습이다. John
이 나오게 하려면
void main() {
String name;
name ??= 'Guest';
print(name); // 출력: Guest
name = null;
name ??= 'John';
print(name); // 출력: John
}
이렇게 하면 John
을 볼 수 있을 것이다!
즉, 변수를 null-check 하고, 변수가 null일 경우에만 새로운 값으로 변수를 초기화할 때 유용하게 사용할 수 있다.
플러터에서 자주자주 쓰이니 꼭 기억해놓자. 약간 API 통신할 때 많이 쓸듯? 기본값들이 서버 통신하기 전 null
이니 말이다.
일단 설명하기 전 예시부터 보자. 숫자로 된 List를 반대로 뒤집어서 return하는 function을 작성해보겠다.
List<int> reverseListOfNumbers(List<int> list) {
var reversed = list.reversed;
return reversed.toList();
}
일단 먼저 얘기할 것은 list
를 뒤집은 reversed
toList
를 왜 해줬냐면 reverse
가 적용되면 iterable
이 돼서 toList
를 써줘 다시 파라미터 list
와 같은 자료인 리스트로 만들기 위해서다. 짚고 넘어가자.
여기서 이제 Typedef를 설명하자면
함수 타입의 별칭을 만들어주는 역할
즉, 자료형(함수)에 alias를 붙여준다.
그러니까 타입을 커스텀으로 정의 후 붙일 수 있다는 것이다.
typedef IntList = List<int>;
IntList reverseListOfNumbers(IntList list) {
var reversed = list.reversed;
return reversed.toList();
}
TS의 interface로 정의 후 객체데이터에 넣는 느낌으로 보면 된다. IntList
로 넣었고, 디테일을 보니 List<int>
가 뜨는 모습을 볼 수 있다. 다른 타입들도 가능하다.
또한 함수로도 사용할 수 있고 사용법이 무궁무진하다.
typedef myFunctionType = int Function(int, int);
int add(int x, int y) {
return x + y;
}
int multiply(int x, int y) {
return x * y;
}
void main() {
myFunctionType operation;
operation = add; // myFunctionType 타입 변수에 add 함수를 할당
print(operation(3, 4)); // 출력: 7
operation = multiply; // myFunctionType 타입 변수에 multiply 함수를 할당
print(operation(3, 4)); // 출력: 12
}
이런 식으로 모양을 잡아놓고도 할 수 있다. 쨌든 typedef
는 타입 별칭을 우리 맘대로 만들어서 붙일 수 있다는 거시다.
재밌네요 플러터도 공부하시는군요 근데 보니까 c++이랑 비슷한 느낌이네요 화이팅입니다