2023.5.14.(일)
베리어 프리 공모전, <베프> 를 준비하고 있습니다. 기획은 마친 상태이고... 이제 정말 개발을 시작하여야 합니다. flutter 를 사용해서 개발을 할거고, flutter 를 쓰려면 dart 라는 언어를 알아야 하더군요.베프>
팀원 모두 개발 경험 없음… 절망적인 상황이지만, 아이디어가 있고 피그마로 도안을 뚜렷하게 그려놓았으므로 열심히 공부하며 개발하면 안될 것 없으리라 생각됩니다.
개발 기간이 한 달이 안되기 때문에 공부할 시간이 많이 없어서, 하루 날 잡고 노마드 코더의 Dart 무료 강의를 전부 들었습니다. 다행히도 Dart 는 크게 어렵지는 않았어요.
Dart 문법에 대해 정리해 보도록 하겠습니다.
VARIABLES
Var
void main() {
var name = 'jun';
name = 'hey';
}
var
키워드는 name 의 타입을 알아서 추론해 줍니다. 물론 name 을 처음에 String 으로 선언했다면 이후에 값은 변경할 때도 String 타입으로 값을 할당해 주어야 합니다.
Dynamic
void main() {
dynamic name;
name = 12;
name = 'jun';
}
dynamic
키워드는 name 의 타입을 자유자재로 변환해서 쓸 수 있습니다. 위에서의 var 에서 name 을 처음에 ‘jun’ 으로 할당해 주지 않고 var name; 으로만 선언해 주면 var 은 dynamic 으로 추론되어 name 의 타입을 자유자재로 사용할 수 있겠죠.
void main() {
dynamic name;
if (name is String) {
}
}
if (name is String) {}
처럼 name 이 String 이라는 것이 명확해 졌다면 String 에 관련된 여러 메서드들을 사용할 수 있게 됩니다.
Nullable Variables
void main() {
String name = 'jun';
name = null;
if (nico != null) {
nico.isNotEmpty;
}
}
위의 코드는 name 에 null 을 할당하려고 할 때 오류가 발생합니다. 그러면, name 이 null 값이 될 수도 있음을 표시해야 합니다. 바로 아래처럼요.
void main() {
String? name = 'jun';
name = null;
if (nico != null) {
nico.isNotEmpty;
}
}
이를 이렇게 쓸 수도 있습니다:
void main() {
String name = 'jun';
name = null;
name?.isNotEmpty;
}
Final Variables
void main() {
final name = 'jun';
}
final
키워드를 붙이면 더이상 name 값을 변경할 수 없음을 의미합니다. name 을 다른 값으로 바꾸려는 순간 컴파일 에러가 발생하죠.
void main() {
late final String name;
name = 'nico';
}
Late Variables
late
키워드는 나중에 변수를 할당하겠다는 뜻입니다. 만약 name 을 선언만 해두고 추후에 값을 할당하지 않는다면 컴파일 에러가 발생합니다.
void main() {
const API = 1212112;
}
Constant Variables
const
키워드는 컴파일 타임에 변경될 수 없는 값들을 할당할 수 있습니다. final
키워드는 사용자로부터 값을 입력 받든, 외부 API 로부터 값을 가지고 오든 값을 받은 후에 나중에 이 값을 변경할 수 없지만 const
키워드가 붙으면 컴파일 시에 이미 값을 컴퓨터가 알아야 합니다. 1221212, ‘jun’, … 처럼 말이죠.
void main() {
List<int> numbers = [1,2,3,4];
numbers.add(5);
print(numbers.first);
print(numbers.last);
var numbers2 = [4,3,2,1];
}
DATA TYPES
Lists
리스트
는 위와 같이 사용할 수 있습니다.
void main() {
var giveMeFive = true;
var numbers = [
1,
2,
3,
4,
if (giveMeFive) 5,
];
}
위와 같은 문법을 collection if
라고 합니다.
String Interpolation
void main() {
var name = 'jun';
var greeting = "Hello everyone, my name is $name, nice to meet you!!";
print(greeting);
}
문자열 안에 $를 넣고 이 옆에 변수명을 써주어 변수 값이 문자열 안에 들어갈 수 있도록 합니다. 이것이 String Interpolation
입니다.
void main() {
var age = 19;
var greeting = "Hello everyone, my name is ${age + 2}, nice to meet you!!";
print(greeting);
}
문자열 안에서 숫자를 계산하여 넣으려면 대괄호 {} 를 넣어주면 됩니다.
Collection For
void main() {
var oldFriends = ['jun', 'sun'];
var newFriends = [
'hi',
'this',
'is',
'cool',
for (var friend in oldFriends) "loving $friend",
];
}
위와 같은 문법을 collection for
라고 합니다.
Maps
void main() {
var player = {
'name': 'jun',
'age': 21,
'superpower': true,
};
Map<int, bool> player2 = {
1: true,
2: false,
3: true;
};
}
Map<key, value>
는 위와 같이 만들어 줍니다.
Sets
void main() {
Set<int> numbers = {1,2,3,4};
numbers.add(1);
numbers.add(1);
numbers.add(1);
print(numbers);
}
Set
에는 모든 값들이 유니크 합니다. 아무리 1을 계속 추가해 주어도 막상 print 하면 1은 오직 한 개뿐이죠.
FUNCTIONS
Defining a Function
String sayHello(String potato) {
return "Hello $potato nice to meet you!";
}
void main()
{
print(sayHello('jun'));
}
Name Parameters
String sayHello(String potato) => "Hello $potato nice to meet you!";
num plus(num a, num b) => a + b;
void main()
{
print(sayHello('jun'));
}
함수는 위와 같이 만들 수 있습니다.
String sayHello({String name, int age, String country}) {
return "Hello $name, you are $age, and you come from $country";
}
void main() {
print(sayHello('jun', 21, 'south korea'));
}
이 함수는 positional 함수입니다.
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: 'jun',
));
}
하지만 순서가 헷갈릴 수도 있으므로 위처럼 named parameter 를 쓸 수도 있습니다. 위와 같이 파라미터를 넘기면, 컴파일 에러가 납니다. name, age, country 가 null 일 가능성이 있기 때문입니다. 따라서 우리는 디폴트 값들을 아래처럼 넣어주거나
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,
country: 'cuba',
name: 'jun',
));
}
아래처럼 required
키워드를 각 파라미터 앞에 붙여 주어 애초에 각 파라미터에 값이 들어가지 않으면 컴파일이 되지 않도록 방지할 수 있습니다.
String sayHello({required String name = 'anon', required int age = 99, required String country = 'wakanda'}) {
return "Hello $name, you are $age, and you come from $country";
}
void main() {
print(sayHello(
age: 12,
country: 'cuba',
name: 'jun',
));
}
Optional Positional Parameters
String sayHello(
String name,
int age = 99,
[String? country = 'wakanda']) => "Hello $name, you are $age, and you come from $country";
void main() {
var results = sayHello('jun', 21);
}
하나의 파라미터에 대한 값을 써주고 싶지 않을 때는, 위와 같이 []를 써주면 됩니다.
QQ Operator
String capitalizeName(String? name) =>
name != null ? name.toUpperCase() : 'ANON';
void main() {
capitalizeName('jun');
capitalizeName(null);
}
위 함수에서 name 이 null 이 아니라면 name.toUpperCase() 를 name 에 할당할 테고, null 이라면 name 에 ‘ANON’ 을 할당합니다.
이를 더 짧게 만들면
String capitalizeName(String? name) =>
name.toUpperCase() ?? 'ANON';
void main() {
capitalizeName('jun');
capitalizeName(null);
}
좌항이 null 이 아니라면 name.toUpeerCase() 를 그대로 반환하고, 좌항이 null 이라면 ‘ANON’ 을 반환합니다.
void main() {
String? name;
name ??= 'jun';
name ??= 'another';
}
만약 name 이 null 이라면 name 에 ‘jun’ 을 할당합니다. name 이 현재 null 이므로 name 에는 ‘jun’ 이 할당 됩니다. 하지만 name 에 ‘jun’ 이 할당된 이상, name 은 더이상 null 이 아니므로 ‘another’ 은 할당되지 않습니다.
List<int> reverseListOfNumbers(List<int> list) {
var reversed = list.reversed;
return reversed.toList();
}
void main() {
reverseListOfNumbers(list);
}
Typedef
위의 함수를 아래와 같이 typedef
키워드를 써서 바꿀 수 있습니다.
typedef ListOfInts = List<int>;
ListOfInts reverseListOfNumbers(ListOfInts list) {
var reversed = list.reversed;
return reversed.toList();
}
void main() {
reverseListOfNumbers(list);
}
typedef UserInfo = Map<String, String>;
String sayHi(UserInfo userInfo) {
return "Hi ${userInfo['name']}";
}
void main() {
sayHi({'name': 'jun'});
}
CLASSES
Dart Class
class Player {
final String name = 'jun';
int xp = 1500;
void sayHello() {
var name = '121';
print("Hi my name is $name");
print("My name is ${this.name}");
}
}
void main()
{
var player = Player();
print(player.name);
player.name = 'haha';
print(player.name);
player.sayHello();
}
클래스는 위와 같이 만들 수 있습니다. 만약 메서드 안에 같은 이름의 변수가 없다면, this.name 이 아닌 name 으로 써도 Player 의 name 인 줄로 압니다.
Constructors
class Player {
late final String name;
late int xp;
Player(String name, int xp) {
this.name = name;
this.xp = xp;
};
void sayHello() {
print("Hi my name is $name");
}
}
void main() {
var player = Player('jun', 1500);
player.sayHello();
var player2 = Player('sun', 2000);
player2.sayHello();
}
위와 같이 constructor
를 만들어 줍니다.
class Player {
late final String name;
late int xp;
Player(this.name, this.xp);
void sayHello() {
print("Hi my name is $name");
}
}
void main() {
var player = Player('jun', 1500);
player.sayHello();
var player2 = Player('sun', 2000);
player2.sayHello();
}
아니면 이렇게 더 짧게 만들 수 있습니다.
Named Constructor Parameters
class Player {
final String name;
int xp;
String team;
int age;
Player( {
required this.name,
required this.xp,
required this.team,
required this.age,
});
}
void main() {
var player = Player(
name: 'jun',
xp: 1200,
team: 'blue',
age: 21,
);
var player2 = Player(
name: 'sun',
xp: 2500,
team: 'red',
age: 12,
);
}
Named Constructors
class Player {
final String name;
int xp;
String team;
int age;
Player( {
required this.name,
required this.xp,
required this.team,
required this.age,
});
Player.createBluePlayer({
required String name, required int age,
}) : this.age = age,
this.name = name,
this.team = 'blue',
this.xp = 1500;
Player.createRedPlayer(String name, int age) :
this.age = age,
this.name = name,
this.team = 'red',
this.xp = 2000;
void sayHello() {
print("Hi my name is $name");
}
}
void main() {
var player = Player.createBluePlayer(
name: 'jun',
age: 21,
);
var player2 = Player.createRedPlayer('jun', 21);
}
Cascade Notation
class Player {
final String name;
int xp;
String team;
int age;
Player( {
required this.name,
required this.xp,
required this.team,
required this.age,
});
Player.createBluePlayer({
required String name, required int age,
}) : this.age = age,
this.name = name,
this.team = 'blue',
this.xp = 1500;
Player.createRedPlayer(String name, int age) :
this.age = age,
this.name = name,
this.team = 'red',
this.xp = 2000;
void sayHello() {
print("Hi my name is $name");
}
}
void main() {
var jun = Player(name: 'jun', xp: 1500, team: 'red');
..name = 'hyojeong'
..xp = 120000
..team = 'blue';
..sayHello();
}
클래스의 각 값에 ..
를 이용하여 접근할 수 있습니다.
void main() {
var jun = Player(name: 'jun', xp: 1500, team: 'red');
var hyojeong = jun
..name = 'hyojeong'
..xp = 120000
..team = 'blue';
..sayHello();
}
Enum
enum Team {red, blue}
enum XPLevel {beginner, medium, pro}
class Player {
final String name;
XPLevel xp;
Team team;
int age;
Player( {
required this.name,
required this.xp,
required this.team,
required this.age,
});
Player.createBluePlayer({
required String name, required int age,
}) : this.age = age,
this.name = name,
this.team = Team.blue,
this.xp = XPLevel.medium;
Player.createRedPlayer(String name, int age) :
this.age = age,
this.name = name,
this.team = Team.red,
this.xp = XPLevel.pro;
void sayHello() {
print("Hi my name is $name");
}
}
void main() {
var jun = Player(name: 'jun', xp: XPLevel.medium, team: Team.red);
..name = 'hyojeong'
..xp = XPLevel.pro
..team = Team.blue;
..sayHello();
}
enum
은 위와 같이 사용할 수 있습니다.
Abstract Classes
abstract class Human {
void walk();
}
enum Team {red, blue}
enum XPLevel {beginner, medium, pro}
class Player extends Human {
final String name;
XPLevel xp;
Team team;
int age;
Player( {
required this.name,
required this.xp,
required this.team,
required this.age,
});
Player.createBluePlayer({
required String name, required int age,
}) : this.age = age,
this.name = name,
this.team = Team.blue,
this.xp = XPLevel.medium;
Player.createRedPlayer(String name, int age) :
this.age = age,
this.name = name,
this.team = Team.red,
this.xp = XPLevel.pro;
void sayHello() {
print("Hi my name is $name");
}
void walk() {
print("Player is walking");
}
}
class Coach extends Human {
void walk() {
print("Coach is walking");
}
}
void main() {
var jun = Player(name: 'jun', xp: XPLevel.medium, team: Team.red);
..name = 'hyojeong'
..xp = XPLevel.pro
..team = Team.blue;
..sayHello();
}
위와 같이 Coach 와 Player 가 walk 메소드를 구현하도록 강제할 수 있습니다. Human 은 여기서 abstract class 로써, Coach 와 Player 에서 extends 하여 강제할 수 있습니다.
Inheritance
class Human {
final String name;
Human(this.name);
void sayHello() {
print("Hi my name is $name");
}
}
enum Team {blue, red}
class Player extends Human {
final Team team;
Player({
required this.team,
required String name,
}) : super(name: name);
@override
void sayHello()
{
super.sayHello();
print("Hi I'm $age years old");
}
}
void main() {
var player = Player(
team: Team.red,
name: 'jun',
);
}
extends
키워드로 상속의 관계를 나타낼 수 있습니다. super
키워드는 부모 클래스를 의미하고요.
Mixins
enum Team {blue, red}
mixin Strong {
final double strengthLevel = 1500.99;
}
mixin QuickRunner {
void runQuick() {
print("ruuuuun!");
}
}
class Tall {
final double height = 1.99;
}
class Player with Strong, QuickRunner {
final Team team;
Player({
required this.team,
});
}
class Horse with Strong, QuickRunner {}
class Kid with Strong {}
void main() {
var player = Player(
team: Team.red,
);
player.runQuick();
}
mixin
은 단순히 mixin 내부의 프로퍼티와 메소드를 가져옵니다. 상속의 관계는 아닙니다.
Dart 기초 문법을 훑어 보았습니다. 바로 flutter 프레임워크 공부 + 개발 들어가 봅시다. 한달 동안은 flutter 앱개발에만 몰입해야 겠어요. 시간이 너무 촉박하다 보니… 열심히 달려가 봅시다.