마저 정리해보자!

[무료 프로그래밍 강의] 1시간만에 끝내는 객체지향 프로그래밍 - YouTube


6. Override

메서드 오버라이드 개념을 배워보자!

void main(){

  TimesTwo tt = TimesTwo(2);
  print(tt.calculate());

}

// method - class 내부에 있는 함수를 지칭한다. 
// override - 덮어쓰다.( 우선시 하다.)

class TimesTwo{

  final int number;

  TimesTwo(
  this.number,
  );

 // method 
 int calculate(){
   return number *2; 
 }


}

숫자를 넣으면 2배 곱 값을 주는 메서드(class 내부에 있는 함수)를 만들자

4를 리턴한다.


class TimesFour extends TimesTwo{

  TimesFour(int number)
  :super(number);

}

TimesTwo 를 extends 해서 TimesFour를 만든다.

void main(){

  TimesTwo tt = TimesTwo(2);
  print(tt.calculate());

  TimesFour tf = TimesFour(2);
  print(tf.calculate());

}

똑같이 4가 출력된다.

calculate를 부모 클래스의 메서드라서 당연히 사용가능

만약 TimesFour에서 calculate 메서드를 다르게 정의하고 싶다면?

@override 키워드를 사용한다

class TimesFour extends TimesTwo{

  TimesFour(int number)
  :super(number);

  @override
  int calculate(){
   return super.number*4;
  }

}

TimesFour 생성자에 int number를 넣으면 부모,본인 클래스에 number 가 있다. 

super를 통해서 number를 가져오고 곱하기 4 연산을 하고 넘겨주면 이제 4배 곱이 넘겨진다. 

super에도 number 가 있고 

TimesFour 본인에게도 number 있으니까 this.number 로 할 수도 있고 number로된 다른 변수가 없으니까 그냥 number 이렇게 해도 된다.

 

부모 클래스의 calculate 값을 사용하는 방식으로 override를 해보자!

class TimesFour extends TimesTwo{
  
  TimesFour(int number)
  :super(number);
  
  @override
  int calculate(){
    return super.calculate()*2;
  }

}

super의 calculate를 가져와서 또 곱하기 2해서 보내 줄 수도 있다.

 

7. Static 키워드

Static은 인스턴스에 귀속되는 것이 아닌 class 에 귀속된다. 

void main(){
  
  Employee seulgi = Employee('슬기');
  Employee chorong = Employee('초롱');
}



class Employee {
  
  // Static은 인스턴스에 귀속되는 것이 아닌 class에 귀속된다.
  static String? building;
  // 알바생 이름
  final String? name;
  
  Employee(
  this.name,
  );

  
  void printNameAndBuilding(){
  print('제 이름은 $name 입니다. $building 건물에서 근무하고 있습니다.');
  
  }
  
  static void printBuilding(){
    print('저는 $building 건물에서 근무중입니다.');
  }
  
}

이렇게  Employee 클래스를 만든다. 

name을 final 로 선언했기 때문에

이름을 변경할 수 없다. 

이런 경우 name은 인스턴스에 귀속되어있다.' 라고 표현한다. 

printNameAndBuilding을 한다면 

building 이름은 아직 안정했기 때문에 Null 이뜬다. 이런 경우도 인스턴스에 귀속되어있다고 표현.

Employee에서 building 을 초기화해보자 

class에서 building 이름을 선언했는데 인스턴스에서도 값을 공유한다.

static은 class에 귀속된다.  

8. Interface

다른 언어에서는 interface 키워드가 있지만 dart에서는 class 이름으로 구현할 수 있다. 

void main(){
  
}

class IdolInterface{
  
  String name;
  
  IdolInterface(this.name);
  
  void sayName(){
    
  }
}

class BoyGroup implements IdolInterface{
  
  String name;
  
  BoyGroup(this.name);
  
  void sayName(){}
}

IdolInterface 라는 이름으로 Interface를 만들었다. IdolInterface를 사용하는 BoyGroup.

인터페이스를 사용할때는 implements 라는 키워드를 사용한다. 안에 아무것도 선언 안하면 오류가 생긴다.

 

Missing concrete implementations of 'IdolInterface.sayName', 'getter IdolInterface.name', and 'setter IdolInterface.name'.

IdolInterface에 있는 변수와 메서드를 가져와라는 뜻이다. 

 

interface는 실제로 값을 상속하는 것이 아닌 메서드, 맴버 틀?을 제공한다고 생각하면 된다.

abstract 키워드

만약 interface를 누가 인스턴스화하고 사용한다면? 문제는 없지만 방지하는 것이 좋다. 

그럴 땐 abstract 키워드를 이용하여 interface의 instance 화를 막을 수 있다. 

abstract class IdolInterface{
  
  String name;
  
  IdolInterface(this.name);
  
  void sayName();
}

 

instance 화 할 수 없기 때문에 메서드의 바디도 지워주자 

상속받은 interface 인스턴스의 타입은 부모 인터페이스와 같은 타입이라고 뜬다.

9. Generic

타입을 외부에서 받을때 사용한다? 

이때까지 생성자의 특징은 메서드나 맴버나 타입이 미리 선언되었다는 점이다. 만약 id의 타입을 외부에서 받아서 변경하고 싶다면? 

class Lecture<T> {
  final T id;
  final String name;
  
  Lecture(this.id, this.name);
  
  void printIdType(){
    print(id.runtimeType);
  }
  
  
}

<> 꺽쇠괄호를 열고 안에 타입을 정의한다.  T는 임의로 정한 타입이름이다. 타입을 받으면 id의 타입은 T 가 된다. 

void main(){
  
  Lecture<String> lecture1 = Lecture('123','lecture1');
  
  lecture1.printIdType();
}

타입을 출력하는 printIdType 메서드를 사용하면 

String 이라고 출력된다.

10. 모든 class는 object의 extends 이다 .

 

모든 class는 4개의 메서드르 기본으로 갖고 있다. 

object의 extends 이다. 

프로젝트를 만들면서 어떻게 하면 코드를 좋게 짤까? 여러 생각이 들었다. 

그래서 1번. 남들이 만든 코드를 보고 배운다. 

2번. 기본 개념을 더 파고들자. 

 

그래서 이번 글은 dart 기본 개념 공부이다.


 

[무료 프로그래밍 강의] 1시간만에 끝내는 객체지향 프로그래밍 - YouTube

 

이번 포스팅은 영상 내용의 날먹이다. 

좋은 자료 만들어주신 '코드팩토리'님께 감사할 따름이며, 좋아요 구독 해드리자. 


1. 객체지향 프로그래밍 OOP (Object Oriented Programming)

클래스 키워드를 사용하여 프로그래밍 하는 것! 

클래스는 변수와 함수로 구성

이런 클래스들을 사용해서 인스턴스를 만들면 쉽게 아이돌을 구현할 수 있다. 

 

2. 생성자

클래스의 효율성을 비교하기 위해서 아래와 같이 예시를 만들었다. 

 

 

class Idol{
  String name = '블랙핑크';
  List <String> members = ['지수', '제니', '리사', '로제'];
  
  void sayHello(){
    print('안녕하세요 블랙핑크 입니다.');
  }
   
  void introduce(){
    print('저희 맴버는 지수,제니,리사,로제 입니다.');
  }
  
}

위처럼 블랙핑크라는 클래스 하나를 만들고,

void main() {
 Idol blackPink = Idol();
//  Idol blackPink = new Idol(); 
// dart 언어에서는 new 생성자 있는거 없는거 차이가 없다. 
 
 print(blackPink.name);
 print(blackPink.members);
 blackPink.sayHello();
 blackPink.introduce(); 
  
}

main 함수 안에서 blackPink 인스턴스를 만들었다 .

문제는 모든 인스턴스가 blackPink 똑같은 내용을 가진다. 

매개변수를 받아서 클래스를 다르게 사용할려면? 

constructor 생성자를 사용

 

클래스에서 매개변수를 받을수 있게 변경해보자

  
  Idol(String name, List<String> members)
    :this.name= name,
     this.members = members;

idol 클래스 안에 생성자를 넣는다 . 

  Idol(String name, List<String> members)
    :this.name= name,
     this.members = members;
  
  Idol(String name, List<String>members){
    this.name= name;
    this.members= members;
  }
  
  Idol(this.name, this.members);

물론 위에 생성자는 다 같은 뜻을 의미한다.

 

 

Idol 안에 매개변수를 받는다 .

여기서 this.name 에서 name은 idol 클래스의 name을 의미한다.

 

 

 

메서드에서도 매개변수 사용하기

class Idol{
  String name ;
  List <String> members ;
  
  Idol(String name, List<String> members)
    :this.name= name,
     this.members = members;
  
  
  void sayHello(){
    print('안녕하세요 ${this.name} 입니다.');
  }
   
  void introduce(){
    print('저희 맴버는 ${this.members} 입니다.');
  }
  
}

 

named Constructor 사용하기 

  Idol.fromList(List value)
    :this.name= value[0],
     this.members= value[1];

하나의 리스트로 매개변수를 받는다. 

물론 두가지 방법 같이 써도 된다. 

Immutable 프로그래밍?

요즘은 한번 값을 정하면 변하지 않는 immutable 프로그래밍을 많이 한다 .

만약 blackPink.name 이렇게 임의로 값을 변경하면 초기화된다.

fromList에서 초기한 값을 다시 초기화 시키는건 지양 해야한다. 

 

fianl 키워드를 사용한다 .

한번만 초기화 할 수 있도록! 

class Idol{
  final String name ;
  final List <String> members ;
  
//   Idol(String name, List<String> members)
//     :this.name= name,
//      this.members = members;
  
  Idol(this.name, this.members);
  
  Idol.fromList(List value)
    :this.name= value[0],
     this.members= value[1];

  void sayHello(){
    print('안녕하세요 ${this.name} 입니다.');
  }
   
  void introduce(){
    print('저희 맴버는 ${this.members} 입니다.');
  }
  
}

final은 한번 설정하면 바꿀수 없다. 

언제 선언이 될까? => 인스턴스 생성할 때!

'name' can't be used as a setter because it's final

name을 초기화 할 수 없다 ! final 로 선언했기 때문!

 

const 로 선언한다면? 

const 로 생성자를 만들거나, 그냥 생성자를 만들거나 별 차이는 없다. 

다른 점은 인스턴스를 만들때 const 로 만들 수 있다는 점! 

const Idol(this.name, this.members);

인스턴스를 const 로 만들 수 있다. 

중요한건 const는 빌드 타임 (컴파일 타임과 동의어 인지 잘모르겠습니다. ) 에 선언되기 때문에 값이 지정되 있어야한다.

만약 런 타임에 값이 정해지는 DateTime.now 같은 객체? 를 넣으면 에러가 뜬다.

arguments of a constant creation must be constant expressions.

상수 값 매개변수는 상수 표현이어야한다? 

만약 fromList 에 const 키워드를 넣으면?

Evaluation of this constant expression throws an exception.

상수 표현의 평가가 예외를 쓰로우 합니다. 

왜? 

찾아보니 array 는 빌드 타임에 메모리에 선언되고

list는 런 타임에 선언되어서 사용이 불가능하다. 

인스턴스 비교

위처럼 blackpink, blackpink2 이름으로 인스턴스를 생성해보자.

비교 해보면, 

blackpink와 blackpink2는 같습니까?: false

결과는 false 이다. 

인스턴스는 생성될 때마다 새로운 메모리에 올라간다. 

클래스는 참조형 데이터이기 때문에 값이 아니라 값을 가지고 있는 메모리 주소를 가르키기 때문

 

[Chapter 1 변수] 7. 참조형 변수(Reference Variable)의 기본개념 (tistory.com)

 

[Chapter 1 변수] 7. 참조형 변수(Reference Variable)의 기본개념

우리가 지금까지 변수에 대해서 알아보면서 중점적으로 건드렸던 것은 변수 중에서도 기본형 변수(Primitive Variable)였다. 그런데 앞서 변수의 타입에 대해서 알아볼 때 우리가 배우는 자바(Java)에

colossus-java-practice.tistory.com

여기 설명이 잘되어있다.

하지만 인스턴스를 똑같은 값으로 하고 const 로 선언했을때는 ?

blackpink와 blackpink2는 같습니까?: true가 뜬다.

매우 중요하다!  

만약 내용을 한글자라도 다르게 하면 false 가 뜬다. 

 

3. Getter and Setter 

getter

class Idol{
 String name ;
 List <String> members ;
  
 Idol(this.name, this.members);
  
 Idol.fromList(List value)
    :this.name= value[0],
     this.members= value[1];

  void sayHello(){
    print('안녕하세요 ${this.name} 입니다.');
  }
   
  void introduce(){
    print('저희 맴버는 ${this.members} 입니다.');
  }
  
  
  String get firstMember {
    return this.members[0];
  }
}

먼저 getter 를 만들어보자 .

데이터 타입, get, 메서드 명으로 

아래와 같이 간단하게 구현할 수 있다. 

setter 에서 값을 변경해야하기 때문에 final , const 키워드는 일단 다 지웠다. 

void main() {
 Idol blackPink = Idol(
 '블랙핑크',
 ['지수',' 제니', '리사', '로제'] 
 );
 
  
  print(blackPink.firstMember);

  
}

메서드이기 때문에 인스턴스명.firstMember 로 간단하게 출력할 수 있다. 

특이한건 메서드 인데 괄호를 사용하지 않는다는점. 

setter 

  String get firstMember {
    return this.members[0];
  }
  
  set firstMember(String name){
    this.members[0]= name;
  }

set, 메서드 이름, 매개변수 

매개변수는 하나만 들어간다. 

void main() {
 Idol blackPink = Idol(
 '블랙핑크',
 ['지수',' 제니', '리사', '로제'] 
 );
 
  
  print(blackPink.firstMember);
  
  blackPink.firstMember = '코코';
  print(blackPink.firstMember);
  
  
}

setter는 그냥 안에 대입 시키면 된다. 

firstmember는 코코가 된다. 

get 을 왜 쓸까 ?

  String getFirstMember(){
      return this.members[0];
  }

이렇게 메서드로 지정하고 return 하면 되는거 아닌가 ?

뭐가 다른가 ?

기능적으로 차이가 없다.

가볍게 쓸거면 getter 를 쓰고 뭐 변경하는 로직이 필요하면 메서드를 써라

 

원래 final 이면 런 타임에 선언후 변경이 되지 않는다. => setter 가 먹히지 않는다. 

하지만 list 타입은 final 이어도 최기화 후 setter 로 변경할 수 있다.

하지만 아예 members list 를 매개변수로 받아서 this.members를 초기화 할 수 는 없다. 

따라서 setter는 잘 안쓰인다 . => immutable 프로그래밍에 지양되는 행위다. 

4. private 변수 

dart에서 private 변수는 같은 dart 페이지인 경우 사용할 수 있다. 

다른 페이지에 선언된 private 값은 사용할 수 없다. 

그냥 변수 이름에 '_' d언더 스코어로 선언하면 된다.

해당 dart 파일에서 private 변수를 사용하고 싶다면 언더 스코어로 불러내면 된다. 

맴버, 메서드 다 private 기호를 사용할 수 있다. 

 

5. 상속 inheritance 

oop 의 꽃 상속을 잘해야한다. 

void main(){
  print('----Idol----');
  Idol apink = Idol(name: '에이핑크', memberCount: 5);
  
  apink.sayName();
  apink.sayMemberCount();
  
}

// 상속 - inheritance
//
// 상속을 받으면 부모 클래스의 모든 속성을
// 자식 클래스가 부여받는다 .

class Idol {
  
  String name;
  
  int memberCount;
  
  Idol({
    required this.name,
    required this.memberCount
  });
  
  void sayName(){
    print('저는 ${this.name}입니다.');
  }
  
  void sayMemberCount(){
    print('${this.name}은 ${this.memberCount}명의 맴버가 있습니다.');
  }
  
}

이렇게 idol 클래스와 인스턴스를 다시 선언하고

 

상속을 통해서 남자아이돌, 여자아이돌로 나눤보자 

idol 로부터 extends 해보자 .

일단 오류가 뜬다.

The superclass 'Idol' doesn't have a zero argument constructor. 

상위 클래스 idol 은 zero 매개변수 생성자가 없다 

곧 상속을 받으면 상위 클래스의 생성자도 똑같이 선언해줘야한다. 

 

위에서 했던 것처럼 생성자를 만들었는데 오류가 뜬다.

'memberCount' isn't a field in the enclosing class.

this.name 의 name 과  this.memberCount 의 memberCount 가 해당 필드안에서 존재하지 않는다 ? 

 

부모 클래스를 지칭하는건 this 키워드가 아니라 super 이다 .

super()는 곧 상위 클래스의 생성자를 의미한다. 

idol 생성자의 모습

idol named construtor를 이용했으니까 이 방법을 그대로 적용해서 (이름에 맞게) 생성자를 초기화해야한다. 

이렇게 생성자를 구성하면 된다.

자식 클래스 초기화 하기 

sayMembersCount는 부모 클래스에 있는 메서드이지만 상속을 했기 때문에 여기서도 사용할 수 있다.

자식 클래스에 맴버,메서드를 추가할 수 있지만 추가한 걸 부모 클래스로 올려보낼 수 없다.

sayMale 메서드를 추가해서 사용할 수 있지만 해당 메서드가 부모 클래스로 올려보내져 사용되는 일은 없다. 

 

타입 체크

부모 클래스는 오직 부모 클래스 이다.

자식 클래스는 부모 클래스 이다.

자식 클래스는 자식 클래스 이다.

 

자식 클래스는 부모,자식 클래스 둘 다 가능하다는 점! 

 

 


2부에 계속~ 

 

타자로 타이핑 한 것 같은 효과를 내보자


내가 만든 예제

여기 공구 관리 시스템 문자가 너무 심심해보인다.

있어 보이는 것처럼 하기위해 타이핑 효과를 추가해보자!

animated_text_kit

animated_text_kit (Flutter Package of the Week) - YouTube

  1. installing
dependencies:
  animated_text_kit: ^4.2.1

pubspec.yaml 파일에 추가한다.

flutter pub get 한후

 

2. usage

class CourseDetails extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 600,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            '공구관리 시스템.\n 새롭게',
            style: TextStyle(
                fontWeight: FontWeight.w800, height: 1.2, fontSize: 80),
          ),
          SizedBox(
            height: 30,
          ),
          Text(
            'NFC 태그를 이용한 공구 추적 \n'
            '관리자로서 손쉽게 공구 관리하세요.',
            style: TextStyle(fontSize: 21, height: 1.7),
          )
        ],
      ),
    );
  }
}

위의 코드에서 

  Text(
            '공구관리 시스템.\n 새롭게',
            style: TextStyle(
                fontWeight: FontWeight.w800, height: 1.2, fontSize: 80),
          ),

해당 부분에 애니메이션을 적용할 것이다. 

import 'package:animated_text_kit/animated_text_kit.dart';

맨위에서 패키지를 import 해온다 .

class CourseDetails extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 600,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          // Text(
          //   '공구관리 시스템.\n 새롭게',
          //   style: TextStyle(
          //       fontWeight: FontWeight.w800, height: 1.2, fontSize: 80),
          // ),
          SizedBox(
            height: 90,
            child: AnimatedTextKit(
              animatedTexts: [
                TypewriterAnimatedText(
                  '공구관리 시스템\n새롭게',
                  textStyle: const TextStyle(
                    fontSize: 32.0,
                    fontWeight: FontWeight.bold,
                  ),
                  speed: const Duration(milliseconds: 200),
                ),
              ],
              repeatForever: true,
              pause: const Duration(milliseconds: 100),
              displayFullTextOnTap: true,
              stopPauseOnTap: false,
            ),
          ),
          SizedBox(
            height: 30,
          ),
          Text(
            'NFC 태그를 이용한 공구 추적 \n'
            '관리자로서 손쉽게 공구 관리하세요.',
            style: TextStyle(fontSize: 21, height: 1.7),
          )
        ],
      ),
    );
  }
}

이렇게 적용했다

주의해야하는 점은 두줄 일 경우 높이가 변하기 떄문에 sized 박스에 묶어서 보내는게 좋을 것 같다. 

 

 

뭐 대충 이렇게 했지만 

animated_text_kit | Flutter Package (pub.dev)

 

animated_text_kit | Flutter Package

A flutter package project which contains a collection of cool and beautiful text animations.

pub.dev

좋은 예제들 많으니까 참고하시길

엣지에서는

엣지 브라우저- 플러터 웹 화면

 

크롬에서는

크롬- 플러터 웹 화면

이렇게 크게 출력된다.

 

어떻게 하지?

 

MediaQuery를 사용한다. 

textScaleFactor를 1 로 

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: (context, child) {
        return MediaQuery(
            data: MediaQuery.of(context).copyWith(textScaleFactor: 1),
            child: child!);
      },
      title: 'Flutter Demo',
      theme: ThemeData(
          primarySwatch: Colors.blue,
          textTheme: Theme.of(context).textTheme.apply(fontFamily: 'OpenSans')),
      home: const HomeView(),
    );
  }
}

 

firebase deploy 하고 확인하면 

짜잔! 크롬 브라우저에서도 정상적인 크기로 출력된다 .

 

다음에 MediaQuery가 뭐하는 용도인지 알아보자 

이게 될때가 있고 안될때가 있지만
다시 해보자

flutter web 으로 프로젝트를 만들었다고 가정합니다.


1. firebase console에서 프로젝트를 만듭니다.

프로젝트이름
애널리틱스 체크

해도 그만 안해도 그만이다.

애널리틱스 계정 선택

계정 선택하고 프로젝트를 만들면된다 .

2. 터미널에서 명령하기

 - 1단계: Firebase CLI 설치 

node.js 가 필요하다 .

npm install -g firebase-tools

npm 으로 firebase 를 전역변수로 설치합니다. 

firebase login

그리고 login 하는게 정신 건강에 좋습니다.

 

- 2단계: 프로젝트 초기화

 

firebase init

Are you ready to proceed? Yes

 

방금 만든 프로젝트를 사용하기 위해 

Use an existing project 

한다. 

What do you want to use as your public directory? build/web

웹 프로젝트 build 용 public 폴더의 위치는 어디에다 할 것인가? 

일반적으로 build 폴더의 web 폴더에 만드는것이 편한다

 

나중에 flutter build web 할때 위에 지정한 폴더에 web 프로젝트가 빌드된다. 

 

 Configure as a single-page app (rewrite all urls to /index.html)? 
Yes

프로젝트를 spa 로 구성할거냐 물어볼건데 그냥 spa로 해보자 

 

git hub 연결은 일단 안했다 .

$ flutter build web

$ firebase deploy

build web을 하는 순간 위에 지정한 폴더에 프로젝트가 빌드되고

firebase deploy 를 하면 배포된다. 

 

firebase 기본 화면이 뜰때도 있지만 그냥 조금 기다렸다가 되는 경우도 있다. 

 

참고자료: Building a Website In Flutter - Flutter Web Beginners Tutorial - YouTube

+ Recent posts