오늘은 Dart를 마무리하고 다음 강좌로 넘어갈거다. 강의에서 깊이 있게 다루지는 않기 때문에 복잡하게 파고들며 공부하지 않아도 돼서 가볍게 익히기 좋은 것 같다. 오늘도 노마드 코더의 "Dart 시작하기로 이어서 공부해보자! 아자아자
class에 대해서 제대로 배우고 문법을 마스터하는 것이 중요하다고 한다. 또한 다른 언어에서의 class와 어떻게 다른지에 대해서도 이해하고 있어야 한다고 한다.
자 그럼 클래스에 대해 공부를 예제 코드로 시작해보자!
class Player {
String name = 'Son';
int xp = 1500;
//앞에 late을 붙여주면 선언만 해두고 값은 나중에 할당할 수 있게 된다.
}
void main(){
var player = Player();
//여기에 Player()앞에는 new를 붙여도 되고 안 붙여도 상관 없다.
print(player.name);
player.name = 'Lee';
print(player.name);
}
이런 예제 코드가 있고, 출력은 이렇게 된다.
Son
Lee
자, 근데 만약 Player이라는 클래스에서 String name 앞에 final을 붙이면?
에러가 난다. final variable은 수정할 수 없기 때문. 당연한거다..
class를 만드는 방법과 class에서 property와 method를 정의하는 법을 배웠다. 사실 오늘 학교 전공 과목 중 객체지향 프로그래밍 과목에서 JAVA class를 배웠는데 아주 많이 유사한 것 같아서 코드만 봐도 이해가 잘 되는 것 같다. 무튼 !! 이번에는 player들과 xp들을 다양하게 사용할 수 있게 argument로 전달해서 새로운 player를 생성할 수 있게 해보겠다.
constructor method를 이해해야 하는데 일단 이 놈의 이름은 class의 이름과 같아야 한다. 따라서 지금
class Player {
final String name;
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("SON", 1500);
}
자 이렇게 내가 main함수 안에서 Plyer()을 통해 이름과 xp를 보내주면 그거에 맞는 객체들을 만들고 싶은데 일단 이렇게 하면 신나게 에러가 뜬다. final property를 사용했지만 아직 아무 값도 할당되지 않았기 때문이다. 맨 첫 코드 주석에 달았듯 이럴 때 사용할 수 있는 것이 late이다.
late는 변수들의 값을 나중에 받아올거라는 것을 의미한다. 변수 선언은 지금 하고있지만, 값은 나중에 할당하겠다는 것이다.
자 그럼 late로 에러를 잡았으니 main함수에서 여러개의 player를 생성해보자.
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("SON", 1500);
player.sayHello();
var player2 = Player("LEE", 1450);
player2.sayHello();
var player3 = Player("KIM", 1100);
player3.sayHello();
}
실행 결과는 다음과 같다.
Hi my name is SON
Hi my name is LEE
Hi my name is KIM
Player()이라는 클래스를 불러오는만큼 내가 원하는 새로운 변수에 내용을 추가하여 함수까지 적용시켜 출력할 수 있음을 확인했다.
그렇다면 더 간결하고 효율적으로 코드를 수정해보자.
일단 late를 모두 지워준다. this부분도 지워준다.
대신, 사용자가 보낸 첫번째 argument는 this.name이 된다고 말해주고, 두번째 argument는 this.xp가 된다고 수정해준다.
class Player {
late final String name;
late int xp;
Player(String name, int xp) {
this.name = name;
this.xp = xp;
}
위에를 아래처럼 바꿔준다 !
class Player {
final String name;
int xp;
Player(this.name, this.xp) {
}
출력해보면 결과는 방금 전과 동일하다는 것을 알 수 있다. 훨씬 코드가 간결해졌다.
정리해보자면, main함수에서 Player()클래스를 호출하는데 아까와 다르게 Player()의 첫번째 인자로 이름을, 두번째 인자로 xp를 바로 넘겨줬다. 이 때 주의해야하는 것은 Dart가 이 인자들의 type을 알고 있어야 한다는 것이다. 최상위에 Player이라는 class를 선언할 때 name은 String type이라는 것과 xp는 int타입이라는 것을 알려줬기에 에러 없이 작동한 것이다.
이게 바로 constructure이다!
마치 함수를 사용하는 것처럼 이해할 수 있지만 정확히는 positional parameters(argument)라고 한다. 근데 이러식으로 사용하는 것은 금방 통제가 어려워질 수 있다. class의 규모가 커질수록 더욱 그렇다.
예를 들어서 Player class에 name과 xp뿐만 아니라, team, age, weight 등의 변수들이 더 추가되었다고 해보자. 위에서 강조했듯 결국 인자들의 순서와 위치에 의해 매칭되기 때문에 내가 하나하나 다 기억하지 못한다면 굉장히 복잡해질 수 있다.
자 그렇다면 해결방안이 뭘까!? 코드로 해보장.
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 sayHello() {
print("Hi my name is $name");
}
}
void main(){
var player = Player(
name: "SON",
xp: 1200,
team: '토트넘',
age: 31,
);
var player2 = Player(name:'LEE',xp:2500,team:'PARIS',age:23,);
}
자 일단 Player클래스를 선언하는 맨 윗 부분에서 변수들을 선언해준다. 초기값을 따로 지정하지 않아도 되는데 만약 에러뜨면 late 써주면 되니깐 괜찮! 그리고 아래 Plyer()에서 중괄호를 추가해서 인자 여러개를 this.argument식으로 적어준다. 그러면 main함수 속에서 player이라는 변수를 만들 때 위 코드와 같은 식으로 만들 수 있어서 아까처럼 굳이 내가 argument들의 위치나 순서를 굳이 생각하지 않아도 된다. 하지만 그렇게하면 에러가 뜬다. 그래서 required를 붙여준건데, name, xp, team, age 값이 NULL이 될 수도 있기 때문에 에러가 발생하는 것!그래서 requried을 써줬다.
정리하자면 최상단에서 타입을 선언해주고 그것들을 required로 만들어준다면 데이터가 많은 클래스들을 만들 때도 argument들의 위치를 기억하지 않다도 된다. 각각을 kaey와 value쌍으로 만들어나갈 수 있다는 것!
Flutter를 배울 때 정말 많이 사용될 것이라고 하니까 잘 이해하고 다음 강의로 넘어가자 !
이번에는 Flutter에서 constructor을 만들 때 많이 쓰는 named constructorts에 대해 배운다.
자 계속 실습하고 있는 코드들을 통해서 보자.
예를 들어서, 내가 두 개의 Constructor를 만들고 싶다고 하자. 하나는 xp를 기본값 0으로 초기화 시킨 blue팀을 가진 Player를 만들게 하고 하나는 xp를 0으로 초기화시킨 red팀의 Player를 만들고 싶다.
이렇게 사용자가 name과 age만 보내도록 main함수를 수정해보자.
void main(){
var bluePlayer = Player.creatBluePlayer(
name: "nico",
age: '83',
);
var redPlayer = Player.creatRedPlayer(
name: "ian",
age: '22',
);
}
그리고 새로운 메서드를 만들어서 인자들을 채워주면 !?
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=0; //얘도 초기화 !!!
void sayHello() {
print("Hi my name is $name");
}
}
void main(){
var bluePlayer = Player.createBluePlayer(
name: "nico",
age: '83',
);
var redPlayer = Player.createRedPlayer(
name: "ian",
age: '22',
);
}
이렇게 완성된다. 자 createBluePlyaer이라는 새로운 메서드를 만들었다. name과 age라는 두개의 파라미터를 받고있다. 그리고 이 코드에서 가장 중요한 것! 콜론을 사용한거다!! 콜론을 넣어준 게 무슨 의미일까? 콜론(:)을 넣어줌으로써 우리는 Dart에게
"여기서 Player 객체를 초기화할거야 !"
라고 말해준 거다. 근데 사실 잘 이해가 안 된다. 그냥 내가 초기값을 넣어주면 그게 초기화 시키는 것이지 굳이 콜론을 쓰고 안 쓰고의 차이가 뭐가 있는걸까 ... ?
일단 이렇게 아까처럼 위치가 기준이 아니라 이름을 기준으로 arguments들을 통해 값을 넘겨 원하는대로 객체들을 만들 수 있는 방법이 named constructors이다!
자 여기서 기억해야 할 건 되게 간단하다.
void main(){
var ian = Player(name:'ian',xp:1200,team:'red',age:20);
ian.name = 'Edyn';
ian.xp = 12345678910;
ian.team = 'blue';
ian.age = 19;
}
자 이렇게 named constructors을 이용해서 ian을 만들고 있다고 치자. 4개 뿐이라 이렇게 칠 수 있지만 똑같이 ian.뭐시기 ian.저시기 ian.뻴렐ㄹ레 계속 반복하는 대신에 쓸 수 있는 방법이 있다.
이렇게!
void main(){
var ian = Player(name:'ian',xp:1200,team:'red',age:20)
..name = 'Edyn'
..xp = 12345678910
..team = 'blue'
..age = 19;
}
자 훨씬 간단하고 보기 좋아졌다.
Flutter에서 정말 많이 사용하게 될 거라고 한다.
점 두 개 중에서 앞에 있는 점은 ian을 가리킨다.
따라서 ..이 ian.과 동일하게 작용한다는 것!
그렇다면 하나 더 ! dot두개를 쓸 때 앞에 있는 점이 ian을 가리킨다면 만들어뒀던 sayHello()함수도
ian.sayHello() -> ..sayHello()로 쓸 수 있지 않을까? 라고 생각했는데 바로 강의에서 이거도 설명해주네...? 가능하다고 한다 !! 이건 진짜 좀 유용한듯! 이 기능이 Cascade Notation이다.
니꼬쌤은 이런게 즐겁고 너무너무 멋지다고 한다. 음 흥미롭긴한데 재미...까지는 모르겠다ㅋ 롤체하러 가고싶다.
아무튼 class를 만들 때 이렇게 쓰는 걸 익혀두면 나중에 유용할 것 같으니 기억하자 !!!
자 Enum도 Flutter에서 많이 쓴다고 한다.
내 실수를 보조해주는 옵션으로 이해하면 된다.
예를들어 flex를 felx로 쓴다거나, blue를 bleu라고 쓴다거나 등등 이런 개발자의 실수를 도와준다고 한다.
Enum을 만들기 위해서는 코드 최상단에 enum이라고 적어주면 되는데 예를들면 다음과 같다.
enum Team {red, blue}
enum XPLevel {beginner, medium, pro}
세미콜론이나 따옴표 없이 이렇게만 넣어주면 된다.
void main(){
var ian = Player(name:'ian',xp:1200,team:'red',age:20)
..name = 'Edyn'
..xp = XPLevel.pro
..team = Team.blue
..age = 19;
}
그리고 변수에 값 넣을 때 이렇게 사용하면 된다. 세미콜론 안 쓰는 거 매우 불편하다 ...
Abstract Class. 말 그대로 추상화 클래스이다.
일단! 추상화 클래스로는 객체를 생성할 수 없다. 이거 생각하면서 다음 진도 고고
추상화 클래스는 다른 클래스들이 어떤 청사진을 가지고 있어야 하는지 정의해주는 역할을 해서 매우 유용하게 쓸 수 있다. 예를 들어보자.
abstract calss Human {
void walk();
}
이렇게 선언하면 되는건데 의미는 간단하다. Human이라는 추상화 클래스는 walk라는 메소드를 가지고, 이 walk라는 메소드는 void를 반환한다는 의미다. 위에서 이렇게 추상화 클래스를 선언해놓고
적용시킬 class에다가 extends Human이라고 이적어주면 된다. 근데 이렇게 하면 에러가 뜬다 ! 왜냐하면 Human이라는 클래스는 walk란 메소드를 가지는데 Player는 walk란 메소드를 가지고 있지 않기 때문이다. 그리고 8번째 줄은 Human를 상속받는 Player 클래스! 라는 의미도 있다. (나 아직 상속 모르는데 ...)
어쨌든 다시 돌아와서, 추상화 클래스는 특정 메소드를 구현하도록 강제해준다. 그래서 우린 Player클래스가 walk메소드를 가지고 있다고 확실할 수 있게 된다. 안 그러면 에러 띄워주니깐 !
근데 그렇게 중요하거나 많이 쓰이거나 하진 않는다고 한다 ^_^
강의 초반에 대충 배우긴 했지만 갑자기 이걸 그래서 어디에 써먹을 수 있을까 싶어서 GPT에게 Dart의 활용 분야를 물어봤는데 생각보다 흥미롭다.
모바일 앱 개발: Dart는 Google의 Flutter 프레임워크와 함께 사용되어 안드로이드와 iOS용 크로스 플랫폼 모바일 앱을 개발하는 데 매우 유용합니다. Flutter는 Dart를 기반으로 하며, 빠르고 아름다운 네이티브 앱을 만들 수 있는 강력한 도구입니다.
웹 개발: Dart는 웹 애플리케이션 개발을 위한 Dart 웹 프레임워크를 사용하여 활용할 수 있습니다. Dart로 작성된 웹 애플리케이션은 빠르고 안전하며, JavaScript로 변환되어 모든 주요 웹 브라우저에서 실행될 수 있습니다.
서버 개발: Dart는 서버 측 애플리케이션 개발을 위한 Dart 서버 프레임워크를 사용하여 활용할 수 있습니다. 이를 통해 백엔드 로직을 구현하고 API를 제공할 수 있습니다.
데이터 분석 및 과학: Dart를 사용하여 데이터 분석 및 과학 작업을 수행할 수 있습니다. 라이브러리와 도구를 활용하여 데이터 처리 및 시각화를 수행할 수 있습니다.
게임 개발: Dart를 사용하여 2D 및 3D 게임을 개발할 수 있습니다. 게임 개발을 위한 여러 라이브러리와 엔진이 있어서 다양한 게임을 만들 수 있습니다.
IoT (사물 인터넷) 개발: Dart는 경량화된 임베디드 시스템에서도 사용될 수 있으므로 IoT 디바이스 및 응용 프로그램을 개발하는 데도 활용될 수 있습니다.
Dart는 다양한 분야에서 활용 가능하며, 특히 Flutter 프레임워크와의 결합으로 모바일 앱 및 웹 애플리케이션 개발에 강력한 도구로 자리 잡았습니다. 개발자들은 Dart를 사용하여 다양한 플랫폼에서 애플리케이션을 개발하고 실행할 수 있으며, 컴파일된 코드의 성능도 뛰어납니다.
확실히 강의에서는 dart에 대해서 가볍게 다루는 듯 하다. 이렇게나 할 수 있는게 많았다고 ...? dart로 서버를 개발할 때 쓰이는 기본적인 sample code를 가져와봤다.
import 'dart:io';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
void main() async {
// 포트 8080에서 웹 서버 시작
final server = await io.serve(handler, 'localhost', 8080);
print('웹 서버가 http://${server.address.host}:${server.port}에서 실행 중입니다.');
}
// HTTP 요청을 처리하는 핸들러 함수
Response handler(Request request) {
// 요청 경로에 따라 다른 응답을 반환
if (request.url.path == '/') {
return Response.ok('안녕하세요! Dart 웹 서버입니다.');
} else if (request.url.path == '/about') {
return Response.ok('저는 Dart 웹 서버에 대한 정보입니다.');
} else {
return Response.notFound('페이지를 찾을 수 없습니다.');
}
}
뭐.. 그렇다고 한다.
객체지향의 꽃, 상속이 드디어 상속이 등장했다.
상속을 하고 super를 이용해 부모 클래스의 생성자를 호출할 수 있다.
class Human {
final String name;
Human(this.name); // 호출 받는다.
void sayHello(){
print("Hello! $name");
}
}
class Player extends Human {
Player({
required this.team,
required String name
}) : super(name: name);
// Human의 생성자 함수를 호출한다.
}
@override를 이용해 부모 클래스의 객체를 받아올 수 있다.
// 생략
void sayHello(){
super.sayHello();
}
강의 comments에 참 이해하기 쉽게 정리를 잘 해놓은 사람들이 많아서 도움이 된다,,
super클래스 앞에 콜론을 찍은 걸 잘 보자. 또 하나의 새로운 문법이다. super 클래스는 확장한 부모 클래스를 의미하는 거라고 한다.
생성자가 없는 클래스를 의미하는 Mixins에 대해서 보자. 강의의 막바지다!
클래스에 프로퍼티들을 추가하거나 할 때 사용한다.
class Strong {
final double strengthLevel = 1500.99;
}
class QuickRunner {
void runQuick(){
print("rrrrrrrunnnnn!!!!!");
}
}
자 이렇게 두 개의 class를 만들었다고 치자.
with으로 연결해주면 한 마디로 다른 클래스의 property나 method를 그냥 긁어다 오는 것이다. 이걸 위해서 상속받을 필요가 없어서 유용하다.
이렇게 with을 사용해서 위에 만들어놓은 클래스들의 프러퍼티나 메서드들을 긁어오는 방법이 바로 Mixins이다.
주의! extend랑은 다른거임!
extend를 하게 되면 확장한 그 클래스는 부모 클래스가 되는거고, 자식 클래스는 부모 클래스를 super를 통해서 접근할 수 있는 거였잖아? 그 순간 부모 클래스의 인스턴스가 되는거였고!
Mixin은 다름 !! Mixin은 with라는 키워드를 사용해서 단순히 Mixin내부의 프러퍼티와 메소드들을 긁어오는 것 뿐이다. 부모 클래스가 되거나 하는 게 아니다. Mixin의 조건은 생성자가 없는 클래스여야 한다는 것 기억하자!
Nomad Corder의 Dart 강의가 끝났다.
느낀 점은 뭐랄까.. Dart를 사용해서 할 수 있는 방대한 분야에 비해 정말 심플하게 필요한 부분만 공부한 것 같다. 근데 이번 팀 스터디의 목적은 Flutter를 사용해서 앱을 만들어보는 것이지 Dart를 통해 서버를 구축하고 뭐 그런건 아니니까 너무 깊게 들여다 볼 필요 없이 딱 이정도만 이해하고 넘어가도 충분할 것 같다.
Dart는 Java랑 굉장히 비슷한 것 같다. C++과도 마찬가지. 결국 객체지향은 객체지향인 것 같다. 약간의 문법적 차이가 있어도 결국 class를 다루고 상속시키는 구조는 똑같은 것 같다.