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

KWANWOO·2023년 3월 8일
1
post-thumbnail

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

31일차에서는 여러 키워드들과 새로운 페이지 이동 방법에 대해 학습했다.

학습한 내용

  • const, final
  • static, getter, setter
  • GetX Pages - Get.toNamed

추가 내용 정리

const와 final

constfinal에 대해서는 [Flutter] 스나이퍼팩토리 6일차에서 정리한 적이 있지만 좀 더 자세히 정리해 보았다.

constfinal 모두 값을 한 번 지정하면 바꿀 수 없다는 공통적인 속성을 가지고 있다. 하지만 생성 시점이 다르다는 차이가 있다.

const로 선언된 변수 또는 위젯의 경우 생성을 앱이 시작될 때 메모리에 같이 등록하여 해당 시점에 새로 생성되는 것이 아닌 기존의 메모리에 올라가 있는 인스턴스를 계속 재사용 하게 된다.

즉, const를 통한 재사용 시 사용하지 않는 인스턴스가 메모리에 남게되는 메모리 낭비를 효율적으로 관리할 수 있다. 또한 const는 속도도 빠르다.

따라서 효율적인 코딩을 위해 const를 잘 작성하면 앱의 성능을 높일 수 있다.

const는 선언부에서 무조건 값을 부여해야 하지만 final은 선언 시 값을 부여하지 않아도 된다. final 선언 시 값을 부여하지 않은 경우 이후에 최초 1번만 값을 부여할 수 있다. 그렇기 때문에 선언부의 코드만으로 컴파일러가 해당 변수의 값을 알 수 없어서 const처럼 앱 실행 시 메모리에 넣어 관리를 할 수는 없다.

정리하면 const컴파일 타임에 데이터를 미리 정의하고, final런타임에 데이터를 정의한다.

static

static은 생성한 클래스 자체에 데이터를 넣어줄 때 사용한다. 따라서 해당 생성자로 여러 객체가 만들어져도 static은 전부 같은 값을 가진다.

자주 사용되는 예시로는 페이지의 라우트를 넣어 관리하는 경우이다. 아래 코드는 LoginPage에 라우트를 static으로 설정한 코드이다.

class LoginPage extends StatelessWidget{
	cost LoginPage({super.key});
    static const String route = '/login';
    
    
    Widget build(BuildContext context) {
    	return Scaffold(...);
    }
}

다른 예시로는 정적 데이터를 보관하고 관리할 때 사용하며 아래와 같이 앱의 페이지 라우트를 따로 클래스를 만들어 관리할 수 있다.

class AppRoutes {
	static const String login = LoginPage.route;
    static const String main = MainPage.route;
    static const String signup = SignupPage.route;
}

또한 API의 라우트들을 저장해 놓고 사용해도 코딩을 할 때 편리하게 사용 가능하다.

class BackendApiRoutes {
	static const String readPosts = '/api/v1/posts'
    static const String readMemos = '/api/v1/memos'
}

이외에도 앱에서 사용하는 색상이나, 텍스트 스타일을 미리 static으로 저장해 놓고 사용하면 코드를 훨씬 깔끔하게 작성할 수 있다.

Getter와 Setter

Getter와 Setter는 이번에 학습을 진행했지만 [Flutter] 스나이퍼팩토리 22일차의 과제 4-1에 정리를 했기 때문에 생략했다.

Getx Pages

Getx Pages로 앱 내에서 사용할 페이지를 미리 등록하고, URL 방식으로 이동할 수 있다.

먼저 기존의 Get.to를 사용하는 방식은 아래와 같은 코드로 페이지를 이동했다.

Get.to(() => MainPage());
Get.to(() => LoginPage());
Get.to(() => SignupPage());

URL 방식을 사용하면 아래와 같은 코드로 변경할 수 있다.

Get.toNamed('/main');
Get.toNamed('/login');
Get.toNamed('/signup');

하지만 URL 방식을 사용하려면 우선 GetMaterialApp에 미리 등록을 해야한다.

GetMaterialApp(
	...
    getPages: [
    	GetPage(name: '/main', page: () => MainPage()),
    	GetPage(name: '/login', page: () => LoginPage()),
        GetPage(name: '/signup', page: () => SignupPage()),
    ],
)

페이지 라우트를 모두 등록한 후에는 앞의 URL 코드 예시 처럼 Get.toNamed()를 사용하면 된다.

여기서 더 효과적으로 코드를 작성하고 싶다면 getPages를 따로 관리하는 방법이 있다. 이 때 코드 작성의 편의성을 높이기 위해 라우트들은 또 다른 파일로 작성해 관리할 수 있다.

먼저 util 폴더를 만들고 아래와 같이 AppRoutes를 작성한다.

class AppRoutes {
	static const main = MainPage.route;
	static const login = LoginPage.route;
    static const signup = SignupPage.route;
}

다음으로 getPages를 작성할 AppPages 클래스를 아래와 같이 작성한다.

class AppPages {
	static final pages = [
		GetPage(name: AppRoutes.main, page: () => const MainPage());
    	GetPage(name: AppRoutes.login, page: () => const LoginPage());
    	GetPage(name: AppRoutes.signup, page: () => const SignupPage());
    ];
}

이렇게 작성한 pagesGetMaterialAPPgetPages에 연결시키면 된다.

GetMaterialApp(
	...
    getPages: AppPages.pages
    ...
)

31일차 과제

  1. private
  2. super
  3. Get.parameter와 Get.arguments

1. private

private은 멤버 변수나 멤버 함수에 사용할 수 있으며, 해당 변수나 함수를 클래스 내부에서만 참조하여 사용할 수 있게 한다.

하지만 클래스 안에서만 private가 사용 가능한 다른 언어와 달리 Dart에서는 클래스가 달라도 같은 파일에서는 접근이 가능하다.

Flutter에서 private를 선언하는 방법은 [Flutter] 스나이퍼팩토리 22일차의 Getter와 Setter에서 간단히 설명한 적이 있는데 변수명이나 함수명 앞에 언더스코어 _를 붙이면 된다.

아래의 예시 코드에서 User클래스의 _name 변수와 _printName()함수는 User클래스 내부에서만 접근 가능하다. (단, 같은 파일에서는 클래스가 달라도 접근 가능!)

class User {
  final String _name;
  
  User(this._name);
  
  _printName() {
    print(_name);
  }
}

이러한 private 변수를 사용하는 이유는 캡슐화자료 보호 때문이다.

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

이러한 경우에는 어디서 잘못된 값으로 변경되었는지 모두 확인해야 한다. 따라서 수정이 쉽지 않다.

private를 사용해 정보 은닉을 하면 이러한 일을 방지할 수 있다. 그렇기 때문에 혼자 진행하는 프로젝트가 아닌 다수가 함께 참여하는 프로젝트의 경우에는 private의 사용이 매우 중요하다.

2. super

Flutter에서 여러 클래스들을 생성해 보면서 super라는 키워드를 많이 접했을 것이다. super 키워드를 예시 코드를 통해 정리해보고자 한다.

우선 아래와 같은 Person 클래스를 생성했다.

class Person {
	final String name;
    final int age;
    
    Person(
    	this.name,
        this.age,
    );
}

Person 클래스를 상속받는 Student 클래스를 아래와 같이 작성했다.

class Student extends Person {
	int grade;
    
    Student(
    	int grade,
      	String name,
      	int age,
    ):this.grade = grade,
    super(
    	name = name,
        age = age,
    );
    
    printInfo() {
    	print('이름: ${super.name}');
        print('나이: ${this.age}');
        print('학년: ${this.grade}');
    }
}

두 가지 클래스를 만들고, 아래와 같이 main()함수를 작성하여 실행하면 이름과 나이, 학년이 정상적으로 출력된다.

void main() {
  var student = Student(1, 'Kim', 17);
  student.printInfo();
  
  //결과
  //이름: Kim
  //나이: 17
  //학년: 1
}

이를 통해 super 키워드를 사용하면 상속받은 자식 클래스에서 부모 클래스에 접근할 수 있다는 것을 알 수 있다.

또한 this 키워드는 자기 자신의 클래스를 지칭한다. 하지만 위의 예시 코드에서 Student 클래스에 age 변수는 존재하지 않는데 this.age로 접근해도 정상적인 값이 출력된다.

그 이유는 자식 클래스가 부모 클래스의 모든 것을 상속 받기 때문에 this 키워드를 사용해도 부모의 age 변수에 접근할 수 있었던 것이다.

만약 age 변수를 Student 클래스에 추가한다면 자식 클래스에서 해당 변수를 재정의 하게 된다. 이 경우에는 this를 사용하면 재정의된 자기 자신의 변수에 접근하고, super를 사용했을 때 부모의 변수에 접근할 수 있을 것이다.

3. Get.parameter와 Get.arguments

GetPage의 URL 방식 네비게이션을 활용한 페이지 접근은 원하는 데이터를 포함하여 다음 페이지로 이동할 수 있다.

이러한 방식에는 두 가지가 존재하는데 Get.parametersGet.arguments이다.

Get.parameters

Get.paramters는 전달받은 paramters를 사용할 수 있게 해준다.

사용 방식은 Get.toNamed()page의 URL의 뒤에 전달할 데이터를 추가하면 된다. 아래의 예시는 1234를 전달하는 코드이다.

Get.toNamed('/page/1234');

전달된 값을 받으려먼 Get.parameters를 사용하면 된다.

Get.parameters['param'];

또한 쿼리를 사용해 데이터를 전달할 수도 있다.

Get.toNamed('/page/value?id=123&pw=456');

쿼리로 전달 받은 값도 Get.parameters를 사용해 받을 수 있다.

Get.parameters['param']; // value 값
Get.parameters['id']; // id 값, 123
Get.parameters['pw']; // pw 값, 456

Get.arguments

arguments를 페이지 이동과 함께 전달하고 싶다면 Get.toNamed()arguments를 사용하면 된다. (Get.to()에서도 사용 가능)

Get.toNamed('/page', arguments: value);

value에 원하는 값을 넣으면 되며, Get.arguments를 통해 값을 사용할 수 있다.

Get.arguments;

만약 뒤로 페이지를 돌아갈 때 값을 전달하고 싶다면 Get.backresult를 설정하면 된다.

Get.back(result: value);

DetailPage에 데이터 전달 코드 작성

Get.toNamed를 활용하여 데이터를 넘기는 두 가지 방법을 구현해 보고자 한다.

아래는 데이터를 전달받을 DetailPage의 예시 코드이다.

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class DetailPage extends StatelessWidget {
  const DetailPage({super.key});
  static const String route = '/detail';

  
  Widget build(BuildContext context) {
    var params = Get.parameters;
    var arguments = Get.arguments;
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$params'),
            Text('$arguments'),
          ],
        ),
      ),
    );
  }
}

만들 페이지의 결과물은 아래와 같다.

코드 작성

  • main.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:new_app/page/main_page.dart';
import 'package:new_app/util/app_pages.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return GetMaterialApp(
      getPages: AppPages.pages,//페이지 라우팅 설정
      home: const MainPage(), //MainPage 호출
    );
  }
}

main.dart 에서는 getPages로 페이지 라우팅을 설정하는데 앱의 라우트는 AppRoutes로 앱의 페이지 이동은 AppPages 클래스로 따로 작성해 보았다.

  • lib/util/app_routes.dart
import 'package:new_app/page/detail_page.dart';
import 'package:new_app/page/main_page.dart';

class AppRoutes {
  static const main = MainPage.route;
  static const detail = DetailPage.route;
}

AppRoutes는 앱의 페이지들의 라우트를 가지고 있다.

  • lib/util/app_pages.dart
import 'package:get/get.dart';
import 'package:new_app/page/detail_page.dart';
import 'package:new_app/page/main_page.dart';
import 'package:new_app/util/app_routes.dart';

class AppPages {
  static final pages = [
    GetPage(name: AppRoutes.main, page: () => const MainPage()),
    GetPage(name: AppRoutes.detail, page: () => const DetailPage()),
  ];
}

AppPages는 저장한 라우트를 사용해 페이지를 라우팅한 pages를 가지고 있다.

  • lib/page/main_page.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:new_app/util/app_routes.dart';

class MainPage extends StatelessWidget {
  const MainPage({super.key});
  static const String route = '/main';

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: TextButton(
          onPressed: () => Get.toNamed(
            '${AppRoutes.detail}/value?a=3', //parameters를 전달
            arguments: 'Hello world!', //arguments를 전달
          ),
          child: const Text('Detail Page로 이동'),
        ),
      ),
    );
  }
}

DetailPaageGet.parametersGet.arguments로 모두 데이터를 받아 출력하기 때문에 Get.toNamedpageparameter를 보낼 수 있도록 /value?a=3을 더해주었고, argumentsHello world!를 보냈다.

  • lib/page/detail_page.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class DetailPage extends StatelessWidget {
  const DetailPage({super.key});
  static const String route = '/detail';

  
  Widget build(BuildContext context) {
    var params = Get.parameters;
    var arguments = Get.arguments;
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$params'),
            Text('$arguments'),
          ],
        ),
      ),
    );
  }
}

DetailPage는 주어진 예시 코드를 그대로 사용했다. 해당 페이지는 전달받은 parametersarguments를 출력한다.

  • 결과

오늘도 GetX

어제에 이어서 오늘도 GetX를 학습했다. 새로운 방식으로 이동하는 것을 배웠고, 추가로 몇 가지 키워드들도 알아보았다. 오늘따라 후기에 쓸 말이 생각이 안나네...ㅋㅋㅋㅋ 오늘은 짧게 마무리 하겠습니다!!

📄Reference

profile
관우로그

0개의 댓글

관련 채용 정보