본문 바로가기

Flutter

riverpod 과 MVVM 활용 - 데이터 접근 계층인 PostRepository 만들어 보자(4)

Repository라는 이름을 붙이는 이유는 데이터 접근 계층을 나타내기 위해서이다다.

 

주요 목적은?

Repository는 API, 데이터베이스, 로컬 파일, 캐시(데이터 소스) 등에서 데이터를 동일한 인터페이스를 통해 접근할 수 있도록 해준다. 즉, 특정 데이터 소스에 종속되지 않고 일관된 방식으로 데이터를 사용할 수 있게 설계할 수 있다.

 

의존성 역전 원칙(Dependency Inversion Principle)을 실현 - DIP

상위 레벨의 비즈니스 로직(서비스 계층)이 하위 레벨의 데이터 접근 세부사항(API, 데이터베이스 등)에 직접적으로 의존하지 않고 Repository 인터페이스에 의존하게 된다.

 

단일 책임 원칙(SRP)

Repository는 데이터를 가져오거나 저장하는 하나의 책임만 가집니다. 명확한 역할과 책임을 가짐.

 

 

repository/post_repository.dart
import '../models/post.dart';

abstract class PostRepository {

  // 모든 게시글 목록을 가져온다.
  Future<List<Post>> fetchPosts();
  // 새로운 게시글을 생성한다.
  Future<Post> createPost(Post post);
  // 기본 게시글을 수정한다.
  Future<Post> updatePost(Post post);
  // 특정 게시글을 삭제한다. 게시글 PK
  Future<void> deletePost(int id);
  // 특정 게시글 하나를 요청한다. 게시글 PK
  Future<Post> fetchPostById(int id);
 // ...
}

 

 

 

repository/post_repository_impl.dart
import 'package:class_riverpod_mvvm/models/post.dart';
import 'package:class_riverpod_mvvm/repository/post_repository.dart';
import 'package:dio/dio.dart';

class PostRepositoryImpl implements PostRepository {
  final Dio _dio;

  // dio 을 가져올 때 우리가 사용할 수 있는 방법은
  // 1. 생상자 주입을 통한 - 의존성을 수동으로 관리
  // 2. 리버팟을 활용한 관리 - x
  PostRepositoryImpl(this._dio);

  @override
  Future<Post> createPost(Post post) async {
    // 통신 관련 코드는 무조건 예외 처리 코드 습관화
    try {
      final response = await _dio.post(
        '/posts',
        data: post.toJson(),
      );
      if (response.statusCode == 201 || response.statusCode == 200) {
        return Post.fromJson(response.data);
      } else {
        throw Exception('게시글 생성 실패');
      }
    } catch (e) {
      throw Exception('게시글 생성 중 오류 발생 : $e');
    }
  }

  @override
  Future<void> deletePost(int id) async {
    try {
      final response = await _dio.delete('/posts/${id}');
      if (response.statusCode != 200) {
        throw Exception('게시글 삭제 실패');
      }
    } catch (e) {
      throw Exception('게시글 삭제 중 오류 발생 : $e');
    }
  }

  @override
  Future<Post> fetchPostById(int id) async {
    try {
      final response = await _dio.get('/posts/$id');
      if(response.statusCode == 200) {
        return Post.fromJson(response.data);
      } else {
        throw Exception('게시글 불러 오기 실패');
      }
    } catch(e) {
      throw Exception('게시글 불러 오는 중 오류 발생 : $e');
    }
  }

  @override
  Future<List<Post>> fetchPosts() async {
    try {
      final response = await _dio.get('/posts');
      if(response.statusCode == 200) {
        // [ '{userId : 홍길동, id: 1 ...}' , '' , '' , '' ]
        List<dynamic> data = response.data;
        // [ 'Post(,,,)' , 'Post(,,,)' , 'Post(,,,)' , 'Post(,,,)' ]
        return data.map((json) => Post.fromJson(json)).toList();
      } else {
        throw Exception('게시글 불러 오기 실패');
      }
    } catch(e) {
      throw Exception('게시글 불러 오는 중 오류 발생 : $e');
    }
  }

  @override
  Future<Post> updatePost(Post post) async {
    try {
      final response = await _dio.put(
        '/posts/${post.id}',
        data: post.toJson(),
      );
      if (response.statusCode == 201 || response.statusCode == 200) {
        return Post.fromJson(response.data);
      } else {
        throw Exception('게시글 수정 실패');
      }
    } catch (e) {
      throw Exception('게시글 수정 중 오류 발생 : $e');
    }
  }
}
728x90