[Flutter] 스나이퍼팩토리 22일차

KWANWOO·2023년 2월 23일
1
post-thumbnail

스나이퍼팩토리 플러터 22일차

22일차에서는 클래스를 학습했고 이를 이용해 모델을 생성해 보았다.
(21일차는 월말 평가로 포스팅이 없습니다.)

학습한 내용

  • Class (클래스)
  • 멤버 변수, 멤버 함수
  • constructor (생성자)
  • Getter와 Setter

추가 내용 정리

Class (클래스)

클래스는 하나의 객체를 생성할 수 있는 것으로 생성자, 멤버 변수(인스턴스 변수), 멤버 함수(메소드)로 구성된다.

참고
클래스 외부에서 하나의 기능을 하는 함수는 Function이라고 하고, 클래스 내부에 있는 멤버 함수는 Method라고 한다.

Dart의 클래스는 아래와 같은 두 가지 특징을 가진다.

  • Dart의 클래스는 최상위 클래스로 Object 클래스를 상속한다
  • Dart는 Mixin-based inheritance를 지원한다.

mixin-based inheritance는 다중 상속을 지원한다는 의미이다.

  • 클래스 생성
var p1 = Point(2, 2);
var p1 = new Point(2, 2);

클래스 생성은 위의 예시 코드와 같이 수행하며 new 키워드는 생략 가능하다.

  • 클래스의 멤버 변수
class Point {
  double? x; // Declare instance variable x, initially null.
  double? y; // Declare y, initially null.
  double z = 0; // Declare z, initially 0.
}
  • 클래스의 멤버 함수
class Point {
  double? x; // Declare instance variable x, initially null.
  double? y; // Declare y, initially null.
  double z = 0; // Declare z, initially 0.
  
  //멤버 함수
  double getZ() {
  	return z;
  }
}

생성자 (constructor)

생성자는 클래스에서 객체를 생성할 때 실행된다.

참고
클래스의 호출 순서는 우선 자식 클래스에서 부모 클래스의 생성자를 호출하여 실행하고, 그 다음에 자식 클래스의 생성자가 실행된다.

이러한 생성자는 여러가지 사용방법이 있다.

  • 기본 생성자 (이름 없는 생성자)
class Person {
	Person(){}
}
  • 이름 있는 생성자
class Person {
	Person(){}	
	Person.init(){}
}

생성자에 이름을 지정해 특별한 경우에 사용할 수 있다. (이름 있는 생성자 선언 시 기본 생성자는 필수로 생성해야 한다.)

  • 초기화 리스트
class Person {
	String name;
	Person() : name = "realyoungk" {
    	print('My name is $name');
    }
}

콜론 : 표시 뒤에 작성하며 생성자의 구현부가 실행되기 전에 인스턴스 변수를 초기화 한다.

  • 리다이렉팅 생성자 (Redirection Constructor) 또는 대리 생성자 (Delegate Constructor)
class Person {
	String name;
    int age;
    
    Person(this.name, this.age){
    	print('$name, $age');
    }
 	Person.init(String name) : this(name, 20);
}

구현부를 다른 생성자에게 위임해 사용한다.

  • 상수 생성자 (Const Constructor)
class Person {
	
    final String name;
    final int age;
    
    const Person(this.name, this.age);
}

main(){
	
    Person p1 = const Person('kim', 20);
    Person p2 = const Person('kim', 20);
    Person p3 = Person('kim', 20);    
    Person p4 = Person('kim', 20);
    
    print(identical(p1, p2));	// true
    print(identical(p2, p3)); 	// false
    print(identical(p3, p4));	// false
    
}

상수 생성자는 생성자에 const를 사용해 클래스가 변하지 않는 객체를 생성한다. 이 클래스에서는 멤버 변수가 모두 final로 선언되어야 한다.

identical() 메소드는 매개변수로 들어온 객체가 같은 인스턴스인지 비교한다. const를 사용하면 같은 객체를 참조할 수 있기 때문에 메모리 효율을 높일 수 있다.

  • 팩토리 생성자 (Factory Constructor)
class Person {
	Person.init();
    
    factory Person(String type){
    	switch(type){
        	
            case 'Student':
            	return Student();
            case 'Employee':
            	return Employee();
        }
    }
    
    String getType(){
    	return 'Person';
    }
}

class Student extends Person {
	
    Student() : super.init();
    
    
    String setType(){
    	return 'Student';
    }
}

class Employee extends Person {
	
    Employee() : super.init();
    
    
    String setType(){
    	return 'Employee';
    }
}

main() {
	
    Person student = Person('Student');
    Person employee = Person('employee');
    
    print('type = ${student.getType()}');
    print('type = ${employee.getType()}');
}

팩토리 생성자는 팩토리 패턴을 쓸 수 있도록 만들어졌다. 팩토리 패턴을 사용하면 해당 클래스의 인스턴스를 매번 생성하지 않아도 된다.

보통 자식 클래스의 인스턴스를 리턴 받는다.

매개변수 형태

함수에 매개변수를 넘겨줄 때 다양한 형태를 사용할 수 있다.

  • 위치 매개변수 (Positional parameters)
example(int a) {
...
}

void main() {
	example(10);
}
  • 선택 매개변수 (Optional parameters)
example({int a}) {
...
}

void main() {
	example(a: 10);
}

함수 호출 시 매개변수에 인자 값을 넘겨줄 때 { }를 사용하면 중괄호 안의 매개변수는 매개변수 명을 이용하여 인자 값을 넘겨줄 수 있다.

아래 코드처럼 { } 밖의 매개변수와 함께 사용할 수도 있다.

example(int a, {int b}) {
...
}

void main() {
	example(10, b: 20);
}
  • 위치적 선택 매개변수 (Positional optional parameters)
example(int a, [int b = 20, int c = 30, int d = 50]) {
...
}

void main() {
	example(10, 10, 10);
}

위치적 선택 매개변수는 초기 값을 지정해 놓고, 함수 호출 시 해당 매개 변수에 인자 값을 넘겨주지 않으면 초기 값을 사용한다.

[ ]를 사용하며, 선택 매개변수와 다르게 위치를 고려해야 한다.

VS code 단축키

VS code를 사용하면서 유용하게 사용하는 단축키를 까먹지 않게 작성하려고 한다.

  • Ctrl + Shift + L : 같은 이름 한 번에 선택 후 변경 가능
  • Ctrl + P : 파일 검색 후 이동
  • F12 또는 Ctrl + 클릭 : 함수 정의를 확인할 수 있도록 해당 파일 열기
  • Ctrl + Shift + P : Command Palette 열기

더 많은 유용한 vscode 단축키는 아래 링크를 참고
[VSCode] Visual Studio Code 단축키


22일차 과제

  1. User 클래스 만들기
  2. UserData 클래스의 생성자 작성
  3. 더미 데이터를 가지고 있는 클래스 생성자 작성
    4-1. Getter와 Setter
    4-2. Getter와 Setter를 사용하여 주어진 결과 만들기
    4-3. 멤버 함수를 사용하여 주어진 결과 만들기

1. User 클래스 만들기

아래의 카카오톡 유저에 들어갈 수 있는 내용을 보고 클래스를 제작하고자 한다.

이름
전화번호
이메일
닉네임
프로필이미지
배경이미지
친구목록
즐겨찾기 목록

요구 사항은 아래와 같다.

요구 사항

  • 클래스 명은 User로 한다.
  • 닉네임은 존재하지 않을 수도 있다.
  • 프로필 이미지가 존재하지 않을 수도 있다.
  • 배경 이미지가 존재하지 않을 수도 있다.

코드 작성

class User {
  //클래스 멤버 변수
  String name; //이름
  String phoneNum; //전화번호
  String email; //이메일
  String? nickName; //닉네임
  String? profileImage; //프로필 이미지
  String? backgroundImage; //배경 이미지
  List friends; //친구 목록
  List bookmark; //즐겨찾기 목록

  //생성자
  User({
    required this.name, 
    required this.phoneNum, 
    required this.email, 
    this.nickName, 
    this.profileImage, 
    this.backgroundImage, 
    required this.friends, 
    required this.bookmark, 
  });
}

User 클래스를 생성하고 주어진 정보들을 담을 수 있도록 멤버 변수를 생성했다.

nickName profileImage backgroundImage는 존재하지 않을 수 있기 때문에 Nullable로 설정했다.

생성자는 선택 매개변수를 사용해 작성했다.

  • 테스트 코드
void main() {
  //User 객체 생성
  var user = User(
    name: 'Kim',
    phoneNum: '010-1234-5678',
    email: 'abc@gmail.com',
    friends: ['Teddy', 'Siri'],
    bookmark: ['Teddy'],
  );

  print(user.name); //이름 출력
  print(user.email); //이메일 출력
}

위와 같이 User 객체를 생성하고, 이름과 이메일을 출력해 보았다. 결과는 아래 콘솔 화면처럼 생성한 user의 이름과 이메일 정보가 출력되었다.

2. UserData 클래스의 생성자 작성

아래의 UserData 객체를 생성하는 코드를 보고 UserData 클래스를 작성하고자 한다.

  • UserData 객체 생성 코드
void main(List<String> arguments) {
	UserData userData = UserData(
		id: "id",
		birth: DateTime.now(),
		email:"sniperfactory@naver.com",
		lastLoginDate: DateTime.now(),
		name: "스나이퍼",
		phoneNumber: '01023456789'
	);
}

코드 작성

class UserData {
  //클래스 멤버 변수
  String id; //아이디
  DateTime birth; //생일
  String email; //이메일
  DateTime lastLoginDate; //마지막 로그인 날짜
  String name; //이름
  String phoneNumber; //전화번호

  //생성자
  UserData({
    required this.id,
    required this.birth,
    required this.email,
    required this.lastLoginDate,
    required this.name,
    required this.phoneNumber,
  });
}

UserData 클래스를 생성하고 예시 코드의 데이터들을 저장할 수 있도록 멤버 변수를 작성했다.

  • 테스트 코드
void main(List<String> arguments) {
  //UserData 객체 생성
  UserData userData = UserData(
      id: "id",
      birth: DateTime.now(),
      email: "sniperfactory@naver.com",
      lastLoginDate: DateTime.now(),
      name: "스나이퍼",
      phoneNumber: '01023456789');

  print(userData.id);
  print(userData.birth);
  print(userData.email);
  print(userData.lastLoginDate);
  print(userData.name);
  print(userData.phoneNumber);
}

위의 테스트 코드를 실행하면 아래의 콘솔처럼 userData의 각 정보가 출력된다.

3. 더미 데이터를 가지고 있는 클래스 생성자 작성

아래의 코드와 결과를 보고 과제 2번의 클래스에 더미 데이터를 품은 클래스 생성자를 작성하고자 한다.

코드 작성

class UserData {
  //클래스 멤버 변수
  String id; //아이디
  DateTime birth; //생일
  String email; //이메일
  DateTime lastLoginDate; //마지막 로그인 날짜
  String name; //이름
  String phoneNumber; //전화번호

  //생성자
  UserData({
    required this.id,
    required this.birth,
    required this.email,
    required this.lastLoginDate,
    required this.name,
    required this.phoneNumber,
  });

  //더미 데이터를 가진 생성자
  UserData.dummy()
      : id = 'DUMMY',
        name = '더미데이터',
        birth = DateTime.now(),
        phoneNumber = '010',
        email = 'dummyData@dummy.com',
        lastLoginDate = DateTime.now();
}

더미 데이터를 가진 이름 있는 생성자를 만들었다. 각 멤버 변수에는 의미가 없는 더미 데이터를 넣어주었다.

  • 테스트 코드
void main(List<String> arguments) {
  UserData userData = UserData.dummy();

  print(userData.id);
  print(userData.name);
  print(userData.birth);
  print(userData.phoneNumber);
  print(userData.email);
  print(userData.lastLoginDate);
}

작성한 UserData.dummy() 생성자를 사용하여 객체를 생성하고 각 멤버 변수를 출력하면 아래 콘솔 화면처럼 정상적으로 작동된다.

4-1. Getter와 Setter

클래스의 멤버 변수를 private가 아닌 public으로 선언하면 어디서든지 접근이 가능하다. 이러한 방식을 사용하면 편리하긴 하지만 의도하지 않은 곳에서 접근하여 값을 변경하는 일이 발생할 수 있다.

이러한 경우가 발생하면 어디서 잘못된 값으로 변경되었는지 모두 확인을 해야한다. 그렇기 때문에 수정이 쉽지 않다.

따라서 이런 일을 방지하고자 클래스 내부 정보를 공개하지 않도록 정보 은닉을 해야한다. 이러한 정보 은닉은 캡슐화를 통해서 이뤄지는데 멤버 변수를 private로 선언하고, 해당 변수에 접근할 수 있는 메소드를 public으로 선언하면 멤버 변수에 직접적으로 접근하는 것을 막을 수 있다.

이 때 사용할 수 있는 메소드가 Getter와 Setter이다.

Getter는 멤버 변수의 값을 가져오는 역할을 하고, Setter는 값을 쓰는 역할을 한다.

Dart에서는 Getter 메소드에 get 키워드를 사용하고, Setter 메소드에 set 키워드를 사용한다.

Dart에서의 Getter와 Setter

  • Getter: get
  • Setter: set

아래는 Getter와 Setter의 기본 사용법 예시이다.

class Person {
	String _name;
    String get getName => _name;
    set setName(String name) => _name = name;
}

_nameprivate 멤버 변수로 직접 접근이 불가능 하지만 Getter를 통해 접근이 가능하다.

Dart에서 private 선언은 변수나 메소드이름 앞에 underscore(_)를 붙이면 된다.

get getName에서 getName이 접근할 때 사용할 이름이다.

Setter도 마찬가지로 public이라 접근이 가능하며 set setName에서 setName은 접근할 때 사용되는 이름이다.

하지만 위의 코드는 getName을 참조하여 어디서든지 값을 변경하면 Getter를 쓰지 않은 것과 같지 않은지 생각이 들 수 있지만, 다른 방식으로 사용하여 여러가지 제어를 할 수 있다.

아래 예시는 Getter와 Setter를 사용하여 name이 null인 경우에 제어를 하는 방식이다.

class Person {
	String _name;
    String get getName => (_name == null) ? 'Kim' : _name;
    set setName(String name) => (name == null) ? _name = 'Kim' : _name = name;
}

결론적으로 멤버 변수는 클래스에 해당 데이터를 저장하는 공간이고, 이를 제어할 수 있는 메소드가 Getter와 Setter이다.

4-2. Getter와 Setter를 사용하여 주어진 결과 만들기

아래의 주어진 코드를 Getter와 Setter를 사용하여 만들고자 한다.

코드 작성

class UserData {
  //클래스 멤버 변수
  String id; //아이디
  DateTime birth; //생일
  String email; //이메일
  DateTime lastLoginDate; //마지막 로그인 날짜
  String name; //이름
  String phoneNumber; //전화번호
  int _age; //나이

  int get getAge => _age; //나이 Getter

  set setAge(int age) => _age = age; //나이 Setter

  //생성자
  UserData({
    required this.id,
    required this.birth,
    required this.email,
    required this.lastLoginDate,
    required this.name,
    required this.phoneNumber,
  }) : _age = 20;

  //더미 데이터를 가진 생성자
  UserData.dummy()
      : id = 'DUMMY',
        name = '더미데이터',
        birth = DateTime.now(),
        phoneNumber = '010',
        email = 'dummyData@dummy.com',
        lastLoginDate = DateTime.now(),
        _age = 20;
}

3번 과제에서 작성한 UserData 클래스에 private_age 변수를 추가하고, Getter인 getAge를 작성했다.

Setter는 setAge로 매개변수로 받은 값을 _age에 저장한다.

생성자에서는 _age의 값을 20으로 초기화 했다.

  • 테스트 코드
void main(List<String> arguments) {
  UserData userData = UserData(
      id: 'sniperFactory',
      birth: DateTime.parse('1990-01-01'),
      email: 'sniperfactory@naver.com',
      lastLoginDate: DateTime.now(),
      name: '스나이퍼',
      phoneNumber: '01023456789');

  print('나이는 : ${userData.getAge}살 입니다.');
}

getAge_age 멤버 변수에 접근하여 값을 읽어오는 Getter로 객체를 생성했을 때 20이 저장되어 아래와 같은 결과가 출력된다.

4-3. 멤버 함수 사용하여 주어진 결과 만들기

아래의 주어진 코드를 멤버 함수를 사용하여 만들고자 한다.

코드 작성

class UserData {
  //클래스 멤버 변수
  String id; //아이디
  DateTime birth; //생일
  String email; //이메일
  DateTime lastLoginDate; //마지막 로그인 날짜
  String name; //이름
  String phoneNumber; //전화번호
  int _age; //나이

  int get getAge => _age; //나이 Getter

  set setAge(int age) => _age = age; //나이 Setter

  // 유저 이름 변경
  updateUserName(String name) {
    this.name = name;
  }

  //생성자
  UserData({
    required this.id,
    required this.birth,
    required this.email,
    required this.lastLoginDate,
    required this.name,
    required this.phoneNumber,
  }) : _age = 20;

  //더미 데이터를 가진 생성자
  UserData.dummy()
      : id = 'DUMMY',
        name = '더미데이터',
        birth = DateTime.now(),
        phoneNumber = '010',
        email = 'dummyData@dummy.com',
        lastLoginDate = DateTime.now(),
        _age = 20;
}

작성했던 UserData 클래스에 유저 이름을 변경하는 updateUserName 멤버 함수를 만들었다.

  • 테스트 코드
void main(List<String> arguments) {
  UserData userData = UserData(
      id: 'sniperFactory',
      birth: DateTime.now(),
      email: 'sniperfactory@naver.com',
      lastLoginDate: DateTime.now(),
      name: '스나이퍼',
      phoneNumber: '01023456789');

  print('업데이트 전: ${userData.name}');
  userData.updateUserName('팩토리');
  print('업데이트 후: ${userData.name}');
}

updateUserName() 메소드를 사용하여 데이터를 업데이트 했다.

업데이트 전과 업데이트 후의 이름을 출력했을 때 아래 콘솔 화면처럼 스나이퍼팩토리가 각각 출력되었다.


하루 쉬고 돌아옴

어제는 월말평가를 진행해서 블로그 포스팅이 없었다. (그래서 포스팅이 20일차에서 바로 22일차로 넘어옴ㅋㅋㅋㅋ) 어제는 월말평가로 뉴스 앱을 만들었는데 이 코드도 시간이 되면 나중에 포스팅을 따로 할 예정이다. 오늘도 정리할 내용이 엄청 많았는데 너무 길어져서 일단 여기서 마무리했다. (공부해야 되는게 저번에 이해 못한 메소드 채널이랑, 오늘 퀴즈에 나온 Mixin이랑...factory는 정리했으니까 팩토리 패턴에 대해서도 해야되고, 막 찾아보다 보니까 abstract랑 interface도 나오고...) 공부해야되는 내용이 산더미다..ㅠㅠㅠㅠ 😭😭 이 내용은 나중에 이 포스팅에 추가될 수도 있고, 아니면 다음 포스팅에서 하나씩 정리될 수도 있고 아직은 모름 ㅎㅎ

📄 Reference

profile
관우로그

0개의 댓글

관련 채용 정보