본문 바로가기

Flutter

통신을 위한 모델과 리포지터리 설계

◆ 모델과 리포지터리의 역할

△ 모델(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