factory는 공장이라는 이름 때문에 인스턴스를 새로 생성하는 공장이라고 착각하기 쉽다. 사실은 warehouse, 창고의 의미와 더 가깝다.
factory는 서브클래스의 인스턴스를 반환할 때도 사용할 수 있다고 했다.
매번 인스턴스를 새로 생성하여 비용이 많이 드는 (generative) constructor와 달리 factory에는 몇 가지 특징이 있다.
- 기존에 이미 생성된 인스턴스가 있다면 return 하여 재사용한다.
- 하나의 클래스에서 하나의 인스턴스만 생성한다(싱글톤 패턴).
- 서브 클래스 인스턴스를 리턴할 때 사용할 수 있다.
- Factory constructors 에서는 this 에 접근할 수 없다.
DTO를 편하게 쓰기 위해 factory 사용한다.
(주석으로 설명)
//post_dto.dart
//테이블 용
class PostDTOTable {
int userId; //유저 번호
int id; //글번호
String title; //제목
//{}로 감싸면 required가 적혀짐.
PostDTOTable({required this.userId, required this.id, required this.title});
//팩토리
//생성자를 통해서 객체를 만들려고 하는데
//처리가 번거로울 경우
//어떠한 타입을 받아서 클래스 내부에서 객체를 생성해서
//리턴 해주기 위함
factory PostDTOTable.fromJson(dynamic json) => PostDTOTable(
userId: json["userId"],
id: json["id"],
title: json["title"]
);
}
(일부 수정된 부분만 들고옴)
class ListPage extends HookWidget {
const ListPage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
//더미데이터
//PostDTOTable postDTOTable = PostDTOTable(userId: 0, id: 0, title: "테스트 제목");
//실제 데이터
//통신은 실패할 수 있다 = nullable
final listState = useState<List<PostDTOTable>?>(null);
//?를 넣어줘야 null이 될 수 있음(java랑 다름)
// final jsonState = useState<String?>(null);
//useEffect(작동함수, 관찰할 상태 리스트);
//빌드가 완료되면 작동한다
//관찰하는 상태가 변경되면 작동한다
//관찰하는 상태가 없으면 빌드 시 1번 작동한다
useEffect((){
String url = "https://jsonplaceholder.typicode.com/posts";
http.get(Uri.parse(url)).then((response) {
//추가
//정사적으로 받아왔는지 체크
//200은 정상 응답
if(response.statusCode == 200) {
//string을 json형식으로 파싱
dynamic decodedBody = jsonDecode(
response.body); //아무거나 받을 때 dynamic으로 함.
//json을 Map List로 캐스팅
List jsonList = decodedBody as List;
//list가 됐긴 때문에 아래에 list map을 할 수 있음
//List를 map 함수로 풀어서 요소를 PostDTOTable로 변경
//state에 입력
listState.value = jsonList.map((data) {
// return PostDTOTable(
// userId: data["userId"], id: data["id"], title: data["title"]);
return PostDTOTable.fromJson(data);
}).toList();
}
//jsonState.value = response.body;
});
},[]);
return Scaffold(
body: SafeArea(
child: ListView(
children: listState.value?.map((e) => ListItem(postDTOTable: e)).toList() ?? [],
// Text(jsonState.value ?? "값이 없습니다"),//null이면 값이 없다고
),
),
);
}
}
return PostDTOTable();을 주석처리하고, return PostDTOTabel.fromJson(data);를 적어줌.
DTO에서 json을 불러와 data를 반환하는 것이다.
실행결과는 아래와 같다.
통신(코드해석)
->문자열이 날아옴 ->문자열을 파싱 (내가 필요한 타입으로 변경) ->jsonDecode ->dynamic (dynamic 모든 타입을 받을 수 있음) (dynamic 모든 타입으로 변할 수 있음) (개발자 알아서 파싱해서 쓰라고) ->리스트로 캐스팅(형변환) ->DTO객체 생성 json데이터의 값을 넣어줌 ->DTO를 편하게 쓰기 위해 factory 사용
(주석에 설명을 해 놨음)
//post_dto.dart
//테이블 용
class PostDTOTable {
int userId; //유저 번호
int id; //글번호
String title; //제목
//{}로 감싸면 required가 적혀짐.
PostDTOTable({required this.userId, required this.id, required this.title});
//팩토리
//생성자를 통해서 객체를 만들려고 하는데
//처리가 번거로울 경우
//어떠한 타입을 받아서 클래스 내부에서 객체를 생성해서
//리턴 해주기 위함
//생성자를 이용한 팩토리 함수
factory PostDTOTable.fromJson(dynamic json) => PostDTOTable(
userId: json["userId"],
id: json["id"],
title: json["title"]
);
//팩토리 아님
//팩토리는 타입이 정해져있는데 일반 함수는 타입을 아무거나 해도 됨.
//instance랑 상관없기 때문에 static으로 해줌
//팩토리를 이용한 함수
static List<PostDTOTable> fromJsonList(List jsonList){
return jsonList.map((json) => PostDTOTable.fromJson(json)).toList();
}
}
(일부 수정된 부분만 들고옴)
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_http_myself/post_dto.dart';
import 'package:http/http.dart' as http;
class ListPage extends HookWidget {//HookWidget을 바꿈
const ListPage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
final listState = useState<List<PostDTOTable>?>(null);
//실패하면 null이 나오게
useEffect((){
String url = "https://jsonplaceholder.typicode.com/users";
//id,name,email 예시의 json링크를 넣음
http.get(Uri.parse(url)).then((response){
if(response.statusCode == 200){//200은 정상응답
//string을 json형식으로 파싱
// dynamic decodeBody = jsonDecode(response.body);
// //json을 Map List로 캐스팅
// List jsonList = decodeBody as List;
// // list가 됐긴 때문에 아래에 list map을 할 수 있음
// // List를 map 함수로 풀어서 요소를 PostDTOTable로 변경
// // state에 입력
// listState.value = jsonList.map((data){
// // return PostDTOTable(id: data["id"], name: data["name"], email: data["email"]);
// return PostDTOTable.fromjson(data);
// }).toList();
listState.value = PostDTOTable.fromJsonList(jsonDecode(response.body));
}
});
},[]);
return Scaffold(
body: SafeArea(
child: ListView(
children: listState.value?.map((e) => ListItem(postDTOTable: e)).toList() ?? [],
//children 안에 적어줘야 나옴.
),
),
);
}
}
팩토리를 이용한 함수를 사용했기때문에 한줄로 불러올 수 있다.
main.dart에서 실행하면 아래와 같은 결과 화면이 나온다.
객체의 인스턴스가 오직 하나만 생성되는 것, 즉 여러개의 생성자를 호출 하더라도 실제로 생성되는 인스턴스는 하나이다.
1. Dart Flutter에서 비동기식을 사용할 때 Future, async, await를 사용한다.
2. 함수 이름 앞 Future은 반환을 나타낸다. 가독성을 위해 적는것을 추천하지만, 생략해도 무방하다.
3. await를 사용하기 위해서는 반드시 async가 적혀있어야한다.
4. await를 사용하면 비동기 함수가 끝날때까지 기다리며, await를 사용하지않으면 기다리지않는다.
5. 비동기함수가 끝났음을 알리고싶다면 Callback함수를 이용하여 알릴 수 있다.
비동기식 Future, await, async 참고(1)
비동기식 Future, await, async 참고(2)
(설명은 주석으로 되어있다)
//post_repository.dart
import 'dart:convert';
import 'package:flutter_http_1/post_dto.dart';
import 'package:http/http.dart' as http;
class PostRepository{
//싱글톤 - 해당 타입의 객체가 프로그램에서 단 1개
//스태틱 변수 선언
//static은 변수 또는 메서드가 인스턴스에 귀속되는 것이 아닌 클래스에 귀속되는 것
static PostRepository? _instance;
//퍼블릭 생성자 제거
//dart에서 private은 맨 앞에 언더바를 붙인다.
PostRepository._();//private이면서 이름이 언더바 임
//싱글톤 객체 getter
static PostRepository get instance => _instance ??= PostRepository._();
//_instance는 무조건 null이라서 PostRepsitory가 출력됨
//통신은 실패할 수 있다 == nullable
Future<List<PostDTOTable>?> getDTOList() async {
String url = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(Uri.parse(url));
//http.get(Uri.parse(url)).then((response) {
if(response.statusCode == 200) {
return PostDTOTable.fromJsonList(jsonDecode(response.body));
}else {
return null;
}
}
}
void main(){
PostRepository.instance;
PostRepository.instance;
PostRepository.instance;
}
변수 ?? 대체값
->변수가 null일 경우 대체값을 리턴한다.ex) String str = [null일수도 있는 String]??"값이 없습니다"; String str = 값이 없습니다";
변수 ??= 대입값
->변수가 null일 경우 변수에 대입값을 넣고 리턴한다.String str = [null일수도 있는 String]??="값이 없습니다"; String str = ["값이 없습니다"라는 값이 들어간 null일수도 있는 String];
코드에서 실행 부분인 void main()옆에 초록색 삼각형이 두개 겹쳐있는 것을 클릭해서
Run 부분을 클릭해서 실행한다.
변수 ??로 해서 실행할 경우 1 2 3이 출력되고, 변수 ??=로 해서 실행하게 되면 1이 출력이 된다.
싱글톤 패턴 참고(2)를 보면 이해가 된다.