똑같은 삽질은 2번 하지 말자
Vue Web API 디자인 패턴 (Repository Pattern) ver.2 본문
Repository Pattren 이란?
비지니스 로직과 API로 데이터를 취득하는 부분을 분리 시키기 위한 디자인 패턴이다.
그리고 서버와의 통신(api request)을 하기위해 개발할 때 고민해야 할 사항들을 해결해 줄 수 있다.
- api 재사용? 매번 URL을 다시 적어야 하나?
- 엔드포인트가 바뀌었을 때는 어떻게 됩니까?
- 다른 프로젝트에서 API 호출 처리를 재사용하고 싶은 경우는 어떻게 됩니까?
- 코드를 리팩터하거나 Vux의 actions로 이동하는 경우는 어떻게 됩니까?
- 여러 개의 리소스가 있는데, 네 개의 다른 엔드포인트를 정의해야 합니까?
- 테스트를 mock의 엔드포인트를 어떻게 다룰 수 있습니까?
기존에 썼던 repository pattern에서 조금 더 수정해서 더 좋은 pratice가 나올 수 있는 방법을 적어보고자 한다.
repositories/posts/index.ts
posts의 Repository를 작성한다($axios는 나중에 plugin에서 주입받을것이다.)
import { NuxtAxiosInstance } from '@nuxtjs/axios';
const resource = '/posts';
export const PostsRepository = ($axios: NuxtAxiosInstance) => ({
get() {
return $axios.get(`${resource}?lastId=1&limit=10`);
},
});
repositories/index.ts
위에서 작성한 repo를 plugins에서 쉽게 주입받기 위해서 Factory를 작성해준다.
import { PostsRepository } from './posts';
export interface Repositories {
posts: typeof PostsRepository;
}
const repositories: Repositories = {
posts: PostsRepository,
};
export const RepositoryFactory = {
get: (key: keyof Repositories) => repositories[key],
};
plugins/axios.ts
이번 주제에서 조금 벗어나지만 axios의 기본설정은 이 플러그인에서 작성하면 좋을꺼 같다.
import { Plugin } from '@nuxt/types';
const baseURL = 'http://localhost:5000';
const initAxios: Plugin = ({ $axios }) => {
$axios.setBaseURL(baseURL);
$axios.onRequest(config => {
console.log(config);
});
$axios.onResponse(config => {
console.log(config);
});
$axios.onError(e => {
console.log(e.response);
});
};
export default initAxios;
plugins/repositories.ts
커스텀 플러그인이기 때문에 type을 declare merge로 정의해주고
해당하는 도메인의 repositories가 불리는 시점에서 repo를 생성해서 반환해주는 플러그인이다.
import { Plugin } from '@nuxt/types';
import { RepositoryFactory, Repositories } from '@/repositories';
declare module '@nuxt/types' {
interface NuxtAppOptions {
// root or this 부분
$repositories: Repositories;
}
interface Context {
// context 부분
$repositories: Repositories;
}
}
const initRepositories: Plugin = ({ $axios }, inject) => {
const repositories = (name: keyof Repositories) => {
return RepositoryFactory.get(name)($axios);
};
inject('repositories', repositories);
};
export default initRepositories;
nuxt.config.js
export default {
...
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: ['~/plugins/repositories', '~/plugins/axios'],
// axios
modules: ['@nuxtjs/axios'],
...
};
usage
components/sample.vue
export default defineComponent({
setup() {
const { $repositories } = useContext()
onMounted(async () => {
const result = await $repositories('posts').get()
console.log(result)
})
},
})
store/action.js
// Test
export const actions = {
async get_selections({ commit }) {
const res = await this.$repositories('posts').get()
console.log('action', res)
},
}
소감
또또, store에서 쓸 때 타입이 문제가 될꺼 같다.
vuex-module-decorator를 쓰는 사람들에게는 이전의 방법이 조금더 쉽게 타입스크립트를 쓸 수 있지 않을까 라는 생각이 든다.
https://heewon26.tistory.com/343
참고한 곳
https://medium.com/js-dojo/consuming-apis-in-nuxt-using-the-repository-pattern-8a13ea57d520