해당 공부는 유튜버 코드 팩토리의 수업 1시간만에 끝내는 Dart언어 기본기를 정리한 수업입니다.
중간에 다 적으려다가 너무 귀찮아서 그냥 기존에 몰랐던 부분들만 적으려고 합니다. 본인은 c언어를 하고 왔기 때문에 기본적인 c언어에서 나오는 부분은 제외하고 적을거니까 감안해주세요.
코드팩토리 - 1시간만에 끝내는 Dart언어 기본기
변수를 선언할 때에는 var를 사용해서 변수 선언을 해준다.
그러나 var는 해당 변수가 어떻게 사용되지 모를 때 사용해주는 것으로 기본적으로는 int처럼 직접 선언해주는 것이 좋다.
void main() {
var name = "코드 팩토리";
print(name);
}
<결과>
코드 팩토리
정수 int
실수 double
문자열 String
참거짓 bool
var은 변수를 사용할 때 해당 변수의 형태가 정해진다.
void main() {
var name = "코드 팩토리";
print(name);
}
해당 코드에서 name은 var로 선언되어 있지만 "코드 팩토리"라는 String값을 주었기 때문에 String으로 변경된다. 그리고 해당 변수 형태로 변경되면 다시 다른 변수 형태로 변경할 수는 없다.
void main() {
String name = "레드벨벳";
String name2 = "슬기";
print("${name}, ${name2}");
print("$name, $name2");
print("${name.runtimeType}, ${name2.runtimeType}");
}
위의 코드처럼 변수를 print에 넣어줄 수 있다. 이때, $를 사용해야 하는데 ${}가 기본형태이고 {}은 생략할 수 있다. 그러나 객체를 사용할 때 '.'을 사용하게 되면 $가 '.'이전만 인식하기 때문에 오류가 발생한다. 그렇기에 {}으로 묶어서 사용하게 된다. 나는 그냥 {}으로 다 묶어버리는게 보기 편한 것 같다.
void main() {
dynamic name = "레드벨벳";
print(name);
print(name.runtimeType);
name = 3;
print(name);
print(name.runtimeType);
}
<결과>
레드벨벳
String
3
int
dynamic은 var과 동일하게 미리 변수의 형태가 정해지지 않는 것이다. 그러나 var과 다른 것이 있다면 var은 한번 형태가 정해지면 변경이 불가능하지만 dynamic은 다른 변수 형태로도 변경이 가능하다.
void main() {
String name = "코드팩토리";
print(name);
String? name2 = "블랙핑크";
name2 = null;
print(name2);
}
<결과>
코드팩토리
null
변수 형태 뒤에 ?을 사용하게 되면 해당 변수는 null을 사용할 수 있게 된다. 기본적으로 Dart는 null을 사용할 수 없게 하지만 ?을 붙이면 사용할 수 있다.
void main() {
String? name2 = "블랙핑크";
print(name2!);
}
이렇게 null이 들어갈 수 있는 즉 ?이 사용된 변수에 !을 사용할 수 있다. 해당 표시는 연산자에서 not을 의미하는 것이 아닌 해당 변수는 지금 null로 사용되고 있지 않다를 의미한다.
void main() {
dynamic name = "레드벨벳";
print(name);
name = null;
print(name);
var name2 = null;
print(name2);
}
<결과>
레드벨벳
null
null
참고로 dynamic으로 선언된 변수는 ?를 붙이지 않아도 null을 자유롭게 사용가능하다.
var을 사용하는 변수도 ?를 선언하지 않아도 null이 사용가능하다.
final과 const로 선언하면 해당 변수를 선언한 이후 값을 변경할 수 없다.
void main() {
final String name = "코드 팩토리";
print(name);
const String name2 = "블랙핑크";
print(name2);
}
<결과>
코드 팩토리
블랙핑크
또한 final과 const로 선언한 변수는 변수타입 선언을 생략할 수 있다.
void main() {
final name = "코드 팩토리";
print(name);
const name2 = "블랙핑크";
print(name2);
}
final과 const는 DateTime을 사용할 때 차이를 알기 쉽다.
const는 빌드타입의 값을 알고 있어야 하고 final은 알지 못해도 된다. 그렇기에 DateTime의 경우 이를 사용하면 작동 이후에 시간이 측정 되기에 빌드타입이 만들어진 이후에 사용된다. 즉 const가 작동할 때에는 빌드 타입이 구축되지 않으므로 사용이 불가능하다는 뜻
void main() {
final DateTime now = DateTime.now();
const DateTime now = DateTime.now(); // 오류 발생
}
ex) 5 + 5 -> 10 + 더하기
ex) 5 - 5 -> 0 - 빼기
ex) 5 5 ->= 10 곱하기
ex) 5 / 5 -> 1 / 나누기
ex) 5 % 5 -> 0 % 나머지
ex) 5++ -> 6
ex) 5-- -> 4 참고로 ++5,--5도 가능
ex) 5 += 1 -> 6
ex) 5 -= 1 -> 4
ex) 5 *= 2 -> 10
ex) 6 /= 2 -> 3
??=는 해당 변수가 null일 경우 ??=뒤의 값으로 변경해주라는 뜻이다.
void main() {
double? number = 4.0;
print(number);
number = null;
print(number);
number ??= 3.0;
print(number);
}
<결과>
4
null
3
>,>=,<,<=,==,!=도 사용가능 이때에는 true,false로 결과가 나온다.
is를 사용하면 타입 비교도 가능하다.
is!를 쓰면 아닌지 물어보는 것
void main() {
int number1 = 1;
print(number1 is int);
print(number1 is! double);
}
<결과>
true
false
and와 or로 조건을 여러개 연동 시킬 수 있다.
and는 &&, or는 ||를 사용하면 된다.
List를 사용할 때에는 generic을 사용하여 해당 리스트에 무슨 변수 형태가 들어가는지 정해주어야 한다.
void main() {
List<String> blackPink = ["제니","지수"];
}
리스트에서 사용되는 기본 함수들
void main() {
List<String> blackPink = ["제니","지수"];
blackPink.add("코드팩토리");
print(blackPink);
blackPink.remove("지수");
print(blackPink);
print(blackPink.indexOf("제니"));
print(blackPink.length);
}
<결과>
[제니, 지수, 코드팩토리]
[제니, 코드팩토리]
0
2
Map도 리스트와 유사하게 generic에서 key와 value의 형식을 지정해주어야 한다. 그리고 적어준다.
void main() {
Map<String, String> dictonary = {
"해리포터" : "해리해리",
"론 위즐리" : "론론",
"헤르미온느" : "헤르헤르",
};
}
추가로 더 넣어주고 싶으면 addAll을 사용한다.
void main() {
Map<String, String> dictonary = {
"해리포터" : "해리해리",
"론 위즐리" : "론론",
"헤르미온느" : "헤르헤르",
};
print(dictonary);
dictonary.addAll({
"스파이더맨" : "스파스파",
"아이언맨" : "아이아이",
"헐크" : "헐헐"
});
print(dictonary);
}
<결과>
{해리포터: 해리해리, 론 위즐리: 론론, 헤르미온느: 헤르헤르}
{해리포터: 해리해리, 론 위즐리: 론론, 헤르미온느: 헤르헤르, 스파이더맨: 스파스파, 아이언맨: 아이아이, 헐크: 헐헐}
한개씩만 넣을 거면 이렇게 넣어도 된다.
dictonary["헐크"] = "헐헐";
<결과>
{해리포터: 해리해리, 론 위즐리: 론론, 헤르미온느: 헤르헤르, 헐크: 헐헐}
key를 통해서 value를 찾을 수 있다. value를 통해서는 찾을 수 없다.
void main() {
Map<String, String> dictonary = {
"해리포터" : "해리해리",
"론 위즐리" : "론론",
"헤르미온느" : "헤르헤르",
};
print(dictonary["론 위즐리"]);
}
<결과>
론론
키를 통해 값도 바꿀 수 있다.
Map<String, String> dictonary = {
"해리포터" : "해리해리",
"론 위즐리" : "론론",
"헤르미온느" : "헤르헤르",
};
print(dictonary);
dictonary["론 위즐리"] = "위즐위즐";
print(dictonary);
}
<결과>
{해리포터: 해리해리, 론 위즐리: 론론, 헤르미온느: 헤르헤르}
{해리포터: 해리해리, 론 위즐리: 위즐위즐, 헤르미온느: 헤르헤르}
remove 키를 통해서 해당 키와 value를 지울 수 있다.
void main() {
Map<String, String> dictonary = {
"해리포터" : "해리해리",
"론 위즐리" : "론론",
"헤르미온느" : "헤르헤르",
};
print(dictonary);
dictonary.remove("론 위즐리");
print(dictonary);
}
<결과>
{해리포터: 해리해리, 론 위즐리: 론론, 헤르미온느: 헤르헤르}
{해리포터: 해리해리, 헤르미온느: 헤르헤르}
키와 value값만도 가져올 수 있다.
void main() {
Map<String, String> dictonary = {
"해리포터" : "해리해리",
"론 위즐리" : "론론",
"헤르미온느" : "헤르헤르",
};
print(dictonary.keys);
print(dictonary.values);
}
List는 중복된 값이 들어갈 수 있지만 Set은 중복된 값이 들어갈 수 없다.
기본적인 함수는 List와 거의 동일하다.
참고로 Set는 인덱스를 통한 값을 찾는 것이 불가능하다.
for문에서 리스트를 활용한 해당 부분만 복습
void main() {
List<int> numbers = [1,2,3,4,5];
for(int number in numbers){
print(number);
}
}
<결과>
1
2
3
4
5
enum은 정확히 해당 값만 사용하겠다라는 것을 알리기 위해 혹은 오타가 발생하거나 실수할 경우 이를 빠르게 고치기 위하여 사용한다.
enum Status{
approved,
pending,
rejected,
}
void main() {
Status status = Status.approved;
if(status == Status.approved) {
print("승인입니다.");
} else if(status == Status.pending) {
print("대기입니다.");
} else{
print("거절입니다.");
}
}
<결과>
승인입니다.
기본적으로 함수는 positional parameter로 순서가 중요하다.
optional parameter - 있어도 되고 없어도 되는 파라미터
사용하지 않는 파라미터에 [] 대괄호를 넣으면 된다.
아래 코드는 실수로 인해서 오류가 발생한다. y랑 z가 사용 안해도 되는 변수가 되었지만 문제는 아무 값도 안들어가기에 null이 된다.
그렇지만 int만 사용하면 null값을 넣을 수 없으므로 int?로 쓰면 되지만 아래의 x+y+z에서 null을 더하게 되는 것이므로 또 오류가 발생한다.
void main() {
addNumber(30);
}
addNumber(int x, [int y, int z]) {
int sum = x + y + z;
}
이럴때에는 그냥 해당 파라미터 안에 쓰지 않는 변수에 값을 미리 정해주면 된다. 그러면 그 값이 고정적으로 설정되고 만약 함수를 사용하기 위해서 y나 z에 값을 넣어주게 되면 해당 값이 더 우선시 된다.
void main() {
addNumber(30);
}
addNumber(int x, [int y = 30, int z = 40]) {
int sum = x + y + z;
}
void main() {
addNumber(30,40,50); // 이러면 아래의 30,40이 아닌 40,50이 들어감
}
addNumber(int x, [int y = 30, int z = 40]) {
int sum = x + y + z;
}
named parameter - 이름이 있는 파라미터, 순서가 중요하지 않다.
required를 사용하면 해당 변수는 이름을 지정해서 직접 선언해주라는 의미이다. 그렇기에 해당 함수를 사용한다면 변수:값 이렇게 선언해주어야 한다. 선언을 해주는 것이기에 순서는 상관없다.
void main() {
addNumber(x:10, y:20, z:30);
addNumber(y:20, x:10, z:30);
}
addNumber({
required int x,
required int y,
required int z,
}) {
int sum = x + y + z;
}
그리고 required를 무조건 사용해야 하는 것은 아니기에 required를 선언 안할 수 있다. 그렇게 되면 해당 괄호 안에서 사용되면 required가 선언되지 않은 변수는 optional이 된다. 그렇기에 오류 방지를 위해서 해당 변수에 값을 미리 설정해 준다.
void main() {
addNumber(x:10, y:20,);
addNumber(y:20, x:10, z:30);
}
addNumber({
required int x,
required int y,
int z = 30,
}) {
int sum = x + y + z;
}
그리고 해당 방식들을 다 섞어줘서 positional parameter랑 optional, named parameter를 다 사용하고 싶으면 아래 예시 처럼 된다.
void main() {
addNumber(10, y:20,);
addNumber(10, y:20, z:30);
}
addNumber(int x,{
required int y,
int z = 30,
}) {
int sum = x + y + z;
}
arrowfunction은 화살표 함수로 함수를 더 편하게 사용하기 위한 코드이다. 위의 코드에서 return한 것과 동일한 코드가 된다.
void main() {
addNumber(10, y:20,);
addNumber(10, y:20, z:30);
}
int addNumber(int x,{
required int y,
int z = 30,
}) => x + y + z;
선언한 typedef와 사용되는 파라미터 변수가 모두 동일해야 한다.
void main() {
Operation operation = add;
int result = operation(10,20,30);
operation = subtract;
int result2 = operation(10,20,30);
}
typedef Operation = int Function(int x, int y, int z);
int add(int x, int y, int z) => x + y + z;
int subtract(int x, int y, int z) => x - y - z;
현실적으로 위의 형태로는 사용하지 않고 다음 형태로 사용한다.
void main() {
int result = calculate(30,40,50, add);
}
typedef Operation = int Function(int x, int y, int z);
int add(int x, int y, int z) => x + y + z;
int subtract(int x, int y, int z) => x - y - z;
int calculate(int x, int y, int z,Operation operation) {
return operation(x,y,z);
}
많이 나열하듯 작성한 거니 보기 힘들듯...