Apollo 를 사용해보자 with React.js

BBAKJUN·2022년 3월 22일
0

React

목록 보기
3/7

우선 이글은 D2개발 블로그 내용에 의거하여 작성중입니다.

일단 이 글을 작성하기 위해 간단한 CRUD가 가능한 TODO APP
백엔드 개발프론트 개발을 마쳐놓은 상태입니다.

궁금하면 눌러줘요 제발!

Apollo는 무엇인가

진짜 뭘까 아폴로가?
내가 아는 아폴로는

이거였는데 말야

사실 GraphQL 은 들어봤었다. 노마드코더 니코쌤의 강의목록중에 GraphQL을 사용하는 강의들이 있었다.
(물론 들어보지못함)

흠 우버이츠 강의가 매우 탐이난다. 그런데 42만원... 썅 못 듣겠다 ㅋ

각설하고

아폴로는 Facebook 개발자들이 Restapi에 문제가있다고 느껴서??? 자체제작했다고한다....

뭐 나같은 초짜들은 감사하다고 생각하고 사용합니다..껄ㄹ껄

왜 Why? Apollo?

아폴로를 왜쓸까??

우선은.. 회사 기술스텍중 하나였다. 그러니까 얼렁 배워보고싶었다.

아폴로 공식문서에서 확인해보면 사용하는 이유를 아래와 같이 알려주는데

내가 제일 지린다, 오진다, 장난 아니다 라고 생각했던 부분은
구독 부분이었다..

예를 들어
트위터같은 대형 SNS에서 내친구가 친구의 디바이스로 본인의 게시글을 좋아요 눌렀다고 가장해보겠다.

이미 내 SNS 앱은 렌더링이 되었고 렌더링된 시점에서 데이터를 가져왔기에 렌더링 후 친구가 좋아요를 눌렀다고 본인의 디바이스의 앱의 상태는 변하지 않는다.
(물론 재렌더링 시키면됨)

물론 이를 가능하게는 구현이 가능할것이다. 졸라 멋없지만 말이다.

setInterval(() => {
	FetchingAPI....
},3000)

의미없이 3초마다 데이터를 갱신해오는 방법이긴하다.
물론 방금 내 머리속에서 나온방법이기도하다. 물론 다른 좋은 방법이 있었을것이고 반박시 님들 말이 맞다.

하지만 Apollo-Client 로 개발을 진행하게 된다면
SubScription(구독)을 하게되어 지속적으로 구독결과에 대한 업데이트 상태를 받아올수있게 되는것이다

기존방법과 다른점

Redux 방법

  1. 컴포넌트가 DOM위에 생성되기 전 Action Method는 fetch 요청에 필요한 정보(url, method)를 dispatch로 Redux에게 알려준다
  2. RestAPI 에 미리 선언해 놓은 엔드포인트를 통해 JSON 형태로 반환하여 받아와 데이터 준비가 완료되면 액션을 통해 Redux에게 알려준다.
  3. Reducer를 통해 받은 데이터를 Store에 있는 State를 변경해준다.
  4. 컴포넌트가 생성되어 렌더링될때 데이터를 전달한다.

Apollo-Client방법

Apollo-Client는 GraphQL 기반의 라이브러리다.

GraphQL은 서버 API를 통해 정보 전달을 주고 받기 위해 사용된느 쿼리 언어이다.
GraphQL은 보통 하나의 엔드포인트를 사용하며 요청시 사용하는 질의문에 따라 응답의 구조가 달라진다.

예를들어 서버에게 내 이름을 알려줄테니 내 정보를 알려달라하는 쿼리문을 짜보겠다

query User($name: String!) {
	getInfo(name: $name){
		age
        address
        phone_number
	}
}

정말 쉽지않나?

GraphQL의 작업 유형은
Query, Mutation, SubScription이 있다.


(네이버 D2 개발 블로그의 그림입니다.)

React에서의 사용방법

백엔드 파트는 설명하지 않겠습니다.
왜냐면 난 프론트엔드개발자니까... 까먹어도 됨 나중에 찾아보면 다 나옴

그러면 어떻게 사용하나 확인을 해보자

우선 코드를 보기전
본인은 Mobx + Apollo-client + MVVM 아키텍처를 사용했습니다.

왜 사용했냐 ??? 하면 이전글을 확인 ㄱ ㄱ

Apollo-Client Query


우선 쿼리문이다.

  • GET_DB_TODO_QUERY : 모든 TODO 리스트를 반환한다.
  • GET_COMPLETE_TODO_QUERY : 완료한 TODO 리스트를 반환한다.
  • GET_NOTCOMPLETE_TODO_QUERY : 완료하지 못한 TODO 리스트를 반환한다.

Apollo-Client Mutations

  • POST_DB_TODO_MUTATION : Todo 생성 액션
  • COMPLETE_DB_TODO_MUTATION : Todo 업데이트 액션
  • REMOVE_DB_TODO_MUTATION : Todo 삭제 액션

라고 생각하면 편할것같다.

TodoStore

import { observable, action, makeObservable } from 'mobx'
import { GET_DB_TODO_QUERY } from '../../utils/graphql/query/Todo'
import {
	POST_DB_TODO_MUTATION,
	REMOVE_DB_TODO_MUTATION,
	COMPLETE_DB_TODO_MUTATION,
} from '../../utils/graphql/mutaion/Todo'
export default class TodoModel {
	notCompleteTodo = []
	completeTodo = []

	constructor() {
		makeObservable(this, {
			notCompleteTodo: observable,
			completeTodo: observable,
			connectDataBase: action,
			getNotCompleteTodoList: action,
			getCompleteTodoList: action,
			setTodo: action,
			addTodoAction: action,
			completeTodoAction: action,
			removeTodoAction: action,
		})
	}

	connectDataBase = (client) => {
		const result = client.query({
			query: GET_DB_TODO_QUERY,
		})
		return result
	}

	getNotCompleteTodoList = async (client) => {
		try {
			const RESULT = await this.connectDataBase(client)
			this.setTodo(RESULT, false)
			return this.notCompleteTodo
		} catch (error) {
			console.log('notComplete Todo List Fetching Failed.. : ', error)
			throw error
		}
	}

	getCompleteTodoList = async (client) => {
		try {
			const RESULT = await this.connectDataBase(client)
			this.setTodo(RESULT, true)
			return this.completeTodo
		} catch (error) {
			console.log('notComplete Todo List Fetching Failed.. : ', error)
			throw error
		}
	}

	setTodo = (value, isComplete) => {
		const target = value.data.getDBTodoList
		if (isComplete) {
			this.notCompleteTodo.replace(target.filter((list) => list.isComplete === false))
		} else {
			this.completeTodo.replace(target.filter((list) => list.isComplete === true))
		}
	}

	addTodoAction = async (id, value, client) => {
		try {
			const result = await client.mutate({
				mutation: POST_DB_TODO_MUTATION,
				variables: {
					value: value,
					isComplete: false,
				},
			})
			const _id = result.data.addDBTodo._id
			this.notCompleteTodo.push({ _id, value, isComplete: false })
		} catch (err) {
			console.log(err)
			throw err
		}
	}
	completeTodoAction = (_id, client) => {
		try {
			client.mutate({
				mutation: COMPLETE_DB_TODO_MUTATION,
				variables: {
					_id,
				},
			})
			this.connectDataBase(client).then((res) =>
				res.data.getDBTodoList.filter((item) =>
					item._id === _id
						? this.completeTodo.push({
								_id: item._id,
								value: item.value,
								isComplete: true,
						  })
						: null,
				),
			)
			console.log(this.completeTodo)
			const result = this.notCompleteTodo.filter((item) => item._id !== _id)
			this.notCompleteTodo.replace(result)
		} catch (err) {
			console.log(err)
			throw err
		}
	}
	removeTodoAction = (_id, client) => {
		try {
			client.mutate({
				mutation: REMOVE_DB_TODO_MUTATION,
				variables: {
					_id,
				},
			})
			const result = this.notCompleteTodo.filter((item) => item._id !== _id)
			this.notCompleteTodo.replace(result)
		} catch (err) {
			console.log(err)
			throw err
		}
	}
}

코드가 너무 많아서 이정도까지만하고

getNotCompleteTodoListgetCompleteTodoList 메서드는 DOM 생성시에 자동으로 실행되어 Observable 배열에 데이터를 넣어주는 방식을 사용하였다.

profile
함께 일하고 싶은 환경을 만들어가는 프론트엔드 개발자 박준형입니다. 블로그 이전 [https://dev-bbak.site/]

0개의 댓글