* 레포지토리 패턴은 대부분 백엔드 내용이 많은데 프론트에도 쓰일 수 있나 해서 찾아본 글입니다!
듣기로는 백엔드에도 repository-service-controller 3계층으로 나누고 repository 계층에서 data source(DB)에 접근하는것으로 알고 있다.
이 글은 https://medium.com/backenders-club/consuming-apis-using-the-repository-pattern-in-vue-js-e64671b27b09를 개인 공부용으로 요약한 내용이고 틀린 오역이 있을수도 있습니다.
레포지토리 패턴이란?
데이터 출처에 관계없이 동일한 인터페이스로 접속할 수 있는 디자인 패턴
보통 코드를 짜면 네트워크 요청을 할때 fetch/axios와 강한 결합도를 가지고 변경에 유연하지 않은데
중간에 인터페이스를 둬
1. data acess code 재사용
2. (도메인별로 repositiory를 나눴을때) domain logic 사용이 쉬워짐
3. logic의 결합도 감소
4. data access logic 과 business logic이 분리되어 테스트가 더 쉬워짐
5. DI의 개념이 추가됨 -> 테스트가 더 쉬워짐
5개의 장점을 가질 수 있다.
4, 5번은 클린코드에서 말한
아직 존재하지 않는 코드를 사용하기
경계와 관련해 또 다른 유형은 아는 코드와 모르는 코드를 분리하는 경계다.
ex) 저쪽 팀이 아직 API를 설계하지 않았으므로 구체적인 방법은 모를때 구현을 미루고 이쪽 코드를 진행하기 위해 자체적으로 인터페이스를 정의한다. - 경계 part
경계를 구현하다보니 부수적으로 얻어지는 효과인거 같다.
테스트시 or 백엔드에서 아직 구현이 안되었을시엔 프론트에서 mocking api를 사용하다 백엔드에서 구현이 완료되었을경우 손쉽게 갈아치울 수 있을걸로 보인다.
// 03-28 추가
서비스워커에서 api 요청을 가로채서 프론트단에서 dummy data를 구현 안해도 되는 msw 라는걸 알게 되었다.
https://tech.kakao.com/2021/09/29/mocking-fe/
예제 코드
axiosclient.js
import axios from "axios";
const baseDomain = "https://jsonplaceholder.typicode.com";
const baseURL = `${baseDomain}`; // Incase of /api/v1;
// ALL DEFUALT CONFIGURATION HERE
export default axios.create({
baseURL,
headers: {
// "Authorization": "Bearer xxxxx"
}
});
우선 기본 axios를 정의한다.
repository.js
import Client from './Clients/AxiosClient';
const resource = '/posts';
export default class Repository {
constructor({resource}){
this.resource = resource;
}
get() {
return Client.get(`${this.resource}`);
},
getPost(id) {
return Client.get(`${this.resource}/${id}`);
},
create(payload) {
return Client.post(`${this.resource}`, payload);
},
update(payload, id) {
return Client.put(`${this.resource}/${id}`, payload);
},
delete(id) {
return Client.delete(`${this.resource}/${id}`)
},
// MANY OTHER ENDPOINT RELATED STUFFS
};
기본 repository에 CRUD를 구현하고 필요시 상속 or 확장으로 메소드를 추가한다.
예를 들어 /user, /item, /car 등 여러 경로(도메인?)마다 기본 CRUD가 필요하다면 Repository를 상속만으로 각각 UserRepository, ItemRepositoy, CarRepository 를 사용해 기본 CRUD를 빠르게 구현할 수 있고 추가 메소드를 유연하게 붙여줄 수 있다.
repositoryFactory.js
import PostRepository from './PostRepository'; // 만들어서 export?
import UserRepository from './UserRepository';
const repositories = {
'posts': PostRepository,
'users': UserRepository
}
export default {
get: name => repositories[name]
};
레포지토리 생성은 팩토리 메소드 패턴을 이용한다.
각 도메인별로 이름을 붙이면 될 듯 하다.
Posts.js
<template>
<div class="row">
<Post v-for="(post, i) in posts" :key="i" :posts="post" />
<div class="col-lg-8 col-md-10 mx-auto">
<div class="clearfix">
<a class="btn btn-primary float-right" href="#">Older Posts →</a>
</div>
</div>
</div>
</template>
<script>
import Repository from "../repositories/RepositoryFactory";
const PostRepository = Repository.get("posts");
import Post from "./Post";
export default {
name: "Posts",
components: {
Post
},
data() {
return {
posts: []
};
},
created() {
this.getPosts();
},
methods: {
getPosts: async function() {
const { data } = await PostRepository.get();
this.posts = data;
}
}
};
</script>
Vue.js 예제인데 코드 흐름을 이해하는건 어렵진 않다.
사용하는 입장(Posts.js)에서는 레포지토리의 세부 사항을 알 필요가 없으므로 data access logic과 business logic이 분리되었다.
reference
https://blog.hanlee.io/2019/do-front-ends-dream-of-back-ends
https://blog.logrocket.com/implementing-repository-pattern-flutter/
'소프트웨어공학 > 디자인패턴' 카테고리의 다른 글
React Compound Component 패턴 (0) | 2022.06.15 |
---|---|
메모 (0) | 2022.03.25 |
[행위] 책임 연쇄 패턴 (0) | 2022.03.13 |
[행위] 전략 패턴 & 커맨드 패턴 (0) | 2022.03.07 |
[행동] 중재자 패턴 (1) | 2022.03.01 |