◆ 모델과 리포지터리의 역할
△ 모델(Model)
① 역할: 서버와 주고받는 데이터의 구조를 정의하는 설계도 역할을 한다.
② 핵심: JSON 데이터를 앱에서 사용할 Dart 객체로 변환하는 역할을 한다.
③ 예시: 서버에서 {"id": 1, "title": "첫 글"}을 보내면, 이 모델을 사용해 Post(id: 1, title: "첫 글") 객체로 만들어 사용한다.
△ 리포지터리 (Repository)
레포지토리, 레파지토리 등 다양한 표기법이 있는데 국립국어원 외래어표기법에 따르면 리포지터리라고 한다.
① 역할: 데이터 통신을 전담하는 관리자 역할을 한다.
② 핵심:
- GET, POST와 같은 HTTP 요청 로직을 모두 레포지토리 안에 캡슐화한다.
- UI 코드와 서버 통신 로직을 분리하여 재사용성을 높인다.
③ 예시: login() 메서드를 한 번 만들면, 앱의 어떤 화면에서든 재사용할 수 있다.
△ 분리 목적
① 이 두 가지를 분리하는 이유는 역할을 명확히 나누기 위함이다.
② 모델은 '데이터는 어떻게 생겼는가'에 답하고, 레포지토리는 '데이터는 어떻게 가져오는가'에 답한다.
예제)
1 모델링
백엔드와 통신하는 프론트엔드 앱의 데이터 계층을 구성하는 핵심 개념을 담는다.
// User 모델 클래스 설계
// API 문서 기준으로 설계해 볼 수 있다.
class User {
int? id; // API 에서 누락될 수 있다.
String? username;
String? imgUrl; // 프로필은 선택사항
String? accessToken;
User({
this.id,
this.username,
this.imgUrl,
this.accessToken,
});
// json to Dart 변환 생성자
// dart 문법. 이름이 있는 생성자
// dart 에서 json 문자열을 convert 패키지에서 Map 구조 변환
// User() 객체를 생성해주는 코드이다.
User.fromMap(Map<String, dynamic> data)
: id = data['id'],
username = data['username'],
imgUrl = data['imgUrl'],
accessToken = data['accessToken'];
// 디버깅용 문자열 표현
@override
String toString() {
return 'User{id: $id, username: $username, imgUrl: $imgUrl, accessToken: $accessToken}';
}
}
import 'package:flutter_blog/data/models/user.dart';
class Post {
// TODO - (댓글은 추후 추가)
int id; // 게시물 ID
String title;
String content;
DateTime createdAt; // 생성일시
DateTime updatedAt; // 수정일시
User user; // 작성자 (관계형 데이터)
int bookmarkCount; // 북마크 수
// 현재 사용자의 북마크 여부 (로그인 상태에 따라 달라짐)
bool? isBookmark;
Post({
required this.id,
required this.title,
required this.content,
required this.createdAt,
required this.updatedAt,
required this.user,
required this.bookmarkCount,
this.isBookmark,
});
// User.fromMap(..)
// 문자열을 DateTime 형변환 처리 해주어야 한다.
Post.fromMap(Map<String, dynamic> data)
: id = data['id'],
title = data['title'],
content = data['content'],
createdAt = DateTime.parse(data['createdAt']),
updatedAt = DateTime.parse(data['updatedAt']),
isBookmark = data['isBookmark'],
user = User.fromMap(data['user']),
bookmarkCount = data['bookmarkCount'];
}
2 리포지터리
import 'package:dio/dio.dart';
import 'package:flutter_blog/_core/utils/my_http.dart';
import 'package:logger/logger.dart';
import 'package:flutter/material.dart';
class UserRepository {
// 회원가입 요청 post 요청
Future<Map<String, dynamic>> join(
String username,
String email,
String password,
) async {
// 1.요청 데이터 구성 - map 구조로 설계
final requestBody = {
"username": username,
"email": email,
"password": password,
};
// 2.http post 요청
Response response = await dio.post(
"/join",
data: requestBody,
);
// 3.응답 데이터 처리
final responseBody = response.data; //바디데이터 담김
Logger().d(responseBody); //개발용 로깅
// 4.응답 데이터 리턴
return requestBody;
}
// 로그인 요청
Future<Map<String, dynamic>> login(
String username,
String password,
) async {
// 1.요청 데이터 구성 - map 구조로 설계
final requestBody = {
"username": username,
"password": password,
};
// 2.http post 요청
Response response = await dio.post(
"/login",
data: requestBody,
);
// 3.응답 데이터 처리
Map<String, dynamic> responseBody = response.data;
Logger().d(responseBody); //개발용 로깅
// 4.응답 데이터 리턴
return responseBody;
}
// 자동 로그인
Future<Map<String, dynamic>> autoLogin(String accessToken) async {
Response response = await dio.post(
"/auto/login",
options: Options(
headers: {"Authorization": accessToken},
),
);
Map<String, dynamic> responseBody = response.data;
Logger().e(responseBody);
return responseBody;
}
}//UserRepository
void main() {
//UserRepository().join('test11', 't@nate.com', '1234');
//UserRepository().login('test11', '1234');
UserRepository().autoLogin('토큰값');
}
import 'package:dio/dio.dart';
import 'package:flutter_blog/_core/utils/my_http.dart';
import 'package:logger/logger.dart';
import 'package:flutter/material.dart';
class PostRepository {
//작성요청
Future<Map<String, dynamic>> save(
String accessToken,
String title,
String content,
) async {
//요청데이터
final requestBody = {
"title": title,
"content": content,
};
//http post 요청
Response response = await dio.post(
"/api/post",
options: Options(
headers: {"Authorization": accessToken},
),
data: requestBody,
);
//응답데이터 처리
final responseBody = response.data;
Logger().d(responseBody);
//응답데이터 리턴
return requestBody;
}
//목록조회
Future<Map<String, dynamic>> list(
String accessToken,
) async {
//http get 요청
Response response = await dio.get(
"/api/post?page=0",
options: Options(
headers: {
"Authorization": accessToken,
},
),
);
//응답데이터 처리
final responseBody = response.data;
Logger().d(responseBody);
//응답데이터 리턴
return responseBody;
}
//상세조회
Future<Map<String, dynamic>> detail(
int id,
String accessToken,
) async {
//http get 요청
Response response = await dio.get(
"/api/post/$id",
options: Options(
headers: {
"Authorization": accessToken,
},
),
);
//응답데이터 처리
final responseBody = response.data;
Logger().d(responseBody);
//응답데이터 리턴
return responseBody;
}
//삭제
Future<Map<String, dynamic>> delete(
int id,
String accessToken,
) async {
//http delete 요청
Response response = await dio.delete(
"/api/post/$id",
options: Options(
headers: {
"Authorization": accessToken,
},
),
);
//응답데이터 처리
final responseBody = response.data;
Logger().d(responseBody);
//응답데이터 리턴
return responseBody;
}
//수정
Future<Map<String, dynamic>> update(
int id,
String accessToken,
String title,
String content,
) async {
//요청데이터
final requestBody = {
"title": title,
"content": content,
};
//http put 요청
Response response = await dio.put(
"/api/post/$id",
options: Options(
headers: {
"Authorization": accessToken,
},
),
data: requestBody,
);
//응답데이터 처리
final responseBody = response.data;
Logger().d(responseBody);
//응답데이터 리턴
return requestBody;
}
}
void main() {
// PostRepository().save(
// '토큰값',
// '제목',
// '내용',
// );
// PostRepository().list(
// '토큰값',
// );
// PostRepository().detail(
// 1,
// '토큰값',
// );
// PostRepository().delete(
// 24,
// '토큰값',
// );
PostRepository().update(
26,
'토큰값',
'제목1234',
'내용1234',
);
}'Flutter' 카테고리의 다른 글
| 로컬 커뮤니티 커머스) 피드백 (1) | 2025.08.25 |
|---|---|
| 플러터) 게시물작성 코드 구조 (4) | 2025.08.22 |
| 플러터에 MVVM 패턴을 적용해 관심사를 분리 (1) | 2025.08.18 |
| SharedPreferences (1) | 2025.08.13 |
| 구글 지도 api 써보기 (1) | 2025.08.13 |