graphql mutation, query -> 모두 쿼리문이라고 부른다.
import React from 'react';
import { gql, useMutation } from '@apollo/client';
const CREATE_BOARD = gql`
mutation {
createBoard(writer: "승현", title: "카카오", contents: "카카오초콜릿") {
_id , number, message
}
}
`;
$
은 변수를 나타낸다. variables
가 $
를 의미한다.
또한 shorthand-property
라고 객체에서 key와 value의 값이 같으면 value를 생략할 수 있다.
페이지 이동을 routing
이라고 하고 페이지를 이동시키는 도구를 router
라고 한다.
Router 기능
const router = useRouter();
router.push("이동할 페이지"); // http://localhost:3000은 자동으로 붙기 때문에 적지 않아도 된다.
yarn dev
명령 하는 위치는 명령어가 적용되어 있는 공간인 package.json파일에서 "scripts"가 실행 명령어를 가지고 있기 때문에 package.json 파일이 있는 파일 공간에서 실행해주어야 한다.
...
const [나의함수] = useMutation(CREATE_BOARD);
const onClickSubmit = async () => {
const result = await 나의함수({
variables: {
//variables $역할을 해준다.
writer: writer,
title: title,
contents: contents,
},
});
console.log(result);
alert(result.data.createBoard.message);
};
...
useMutation을 동적으로 사용하기 위해 useMutation을 저장한 함수에 함수를 실행할 때 변수를 할당받아 동적으로 사용했다.
const FETCH_BOARD = gql`
query {
fetchBoard(number: 1) {
writer
title
contents
}
}
`;
...
const { data } = useQuery(FETCH_BOARD);
...
위 코드는 하드코딩 했을 때 useQuery 사용
const FETCH_BOARD = gql`
query fetchBoard($number: Int){
// query fetchBoard(변수명: 변수의 타입){ =>playground에서 확인
fetchBoard(number: $number) { // fetchBoard(number: 변수명) {
writer
title
contents
}
}
`;
...
const { data } = useQuery(FETCH_BOARD, {
variables: { number: 1 },
});
...
return <>{data.fetchBoard.writer}</>
위 코드는 동적으로 할당할 때 useQuery 사용, 이때 쿼리문에서 데이터 타입을 기재해줘야 한다.
useMutation의 경우 함수를 정의하고 실행할 때에 사용하였다. useQuery의 경우도 실행할 때
에 동적으로 할당해야 한다. query는 페이지 이동되면 자동으로 실행되기 때문에 useQuery의 두 번째 매개변수로 넣어준다.
두 번째 매개변수에 variables를 할당한다.
이렇게 출력을 해보면 FETCH_BOARD
가 undefined가 되는데 그 이유는 API를 받아오는 useQuery가 비동기적으로 실행해서이다. API 요청과 동시에 화면에 뿌려주는 return 값이 실행되므로 fetchBoard에 에러가 발생한다.
일단 처음에 한번 실행이 되고 이때는 백엔드에 요청을 보냈으므로 값이 undefined가 나온다.
그렇다면 mutation에서와 같이 async/await 하면 되지 않나?
그렇게 동기적으로 실행하면 백엔드에서 응답할 때까지 화면에 보여지는 값이 없으므로 그렇게 하지 않는다. 더 빠르게 하기 위해 비동기적으로 실행을 하면서 조건부렌더링
을 사용하였다.
'앞에 것이 있으면 뒤에 것을 보여줘!' 라고 데이터가 있을 때 보여지도록 하는 일종의 조건문, 코드 양을 주는 장점이 있다.
return (
<>
<div>1번 게시물로 이동이 완료되었습니다!!</div>;
<div>작성자: {data && data.fetchBoard.writer}</div>
<div>제목: {data && data.fetchBoard.title}</div>
<div>내용: {data && data.fetchBoard.contents}</div>
</>
);
}
동기로 실행했을때보다 훨씬 효율적이다. 동기로 실행하게 되면 위에서 데이터를 받아올 때까지 기다렸다가 보여줘야하기 때문에 느리지만, 조건부렌더링을 사용하여 비동기적으로 실행하게 되면 일단 하드코딩된 부분이 바로 보여지고 데이터가 들어오면 보여지도록하였다.
<div>제목: {data ? data.fetchBoard.title : "로딩중"}</div>
<div>제목: {data && data.fetchBoard.title}</div>
<div>제목: {data?.fetchBoard.title}</div>
사람들은 1초만 빈화면이 떠도 느리다고 생각하고 2초가 넘어가면 화면을 끄는, 이탈율이 높아진다. 따라서 동적으로 받아오는 데이터는 나중에 보이더라도 비동기적으로 실행하여 보여줄 것은 보여주고 나중에 가져온 데이터를 화면에 렌더링 시키는 것이 UX으로 좋다.
직접 폴더를 따로따로 만드는 방식
그때 그때 동적으로 페이지를 생성하는 방식
[]
를 사용하게 되면 대괄호 안에 있는 폴더로 만들어진다.
밑의 그림을 보면 1이 aaa에 들어가게 되고 [aaa] 안에 있는 폴더인 index.js에 접속이 되고 접속된 aaa에서 useRouter
를 사용하여 변수를 꺼내 사용할 수 있고 변수는 우리가 사용한 변수 1인 들어간다. 또한 useRouter() 함수를 이용해 이동된 페이지에서 라우터 객체내에 있는 정보를 조회할 수 있는 방법이 있었다.
게시글 등록 -> 상세페이지 이동 프로세스
router.query를 활용하여 라우터 객체를 조회할 수 있다. 게시물ID 또는 게시물 번호를 가져올 수 있었습니다.
또한, 가져온 게시물ID와 게시물번호를 활용하여 데이터를 조회해보았다.
게시글 등록 상세페이지에서 useMutation의 함수를 요청하여 등록을 요청한다.
등록 요청에 대한 return 값으로 graphql에서는 원하는 값만 받을 수 있는데 게시글이 등록된 번호를 받아온다.
함수를 요청하면 백엔드에 요청이 들어가고 DB에 데이터가 쌓이고 그 결과로 해당 게시글의 번호를 받는다.
router.push("이동할 페이지"+게시글 번호)
를 사용하여 상세페이지로 이동한다.
+
를 이용하면 가독성이 좋지 않기 때문에 템플릿 리터럴
을 활용하여 가독성을 높인다.
useQuery()의 사용 방법은 useMutation()과 거의 동일하지만 몇가지 차이점이 있다.
변수명의 지정
useMutation()은 실행함수 이름을 적어줄 때 내 마음대로 변수명 선언이 가능했죠?
하지만 useQuery()를 사용할 때에는 중괄호 안에 data라는 변수명이 지정되어있기때문에 해당 변수명으로만 받아와야 했습니다.
정리하면, useMutation을 실행할 변수명은 내맘대로 지정이 가능하지만, useQuery()는 불가능 하다는 점 알고계시면 될 것 같습니다.
통신 방식
useMutation과 useQuery는 모두 비동기 방식은 맞다. 사용될 때 useMutation은 async/await를 사용하여 비동기를 동기적으로 처리하였지만 useQuery는 페이지가 그려질(렌더링) 때 자동으로 요청이 날아간다.
요청에 대한 응답을 받기 전에 먼저 html을 그려주고, data가 받아지면 내용을 채워 준다는 특징이 있다. 이로 인해 데이터가 들어오기 전에 데이터를 뿌려주는 명령이 실행되어 에러가 발생한다.
그러나 useQuery는 async/await로 동기적으로 처리하지 않고 더 빠르게 처리하기 위해
비동기적으로 처리하고 대신 조건부 렌더링
을 통해 보여질 것은 보여주고 데이터는 들어오면 뿌려주도록 하였다.
API 요청시 백엔드에서 데이터베이스에 등록을 하고 그리고 등록 결과가 날아오게 되어 본다. 그 중간에 백엔드나, 데이터베이스에서 에러를 줄 수 있다. 따라서 try-catch
를 통해 에러를 처리해야 한다.
try에 있는 내용을 시도하다가 실패하면, 아랫줄은 모두 무시하고 catch가 실행됨
소소한 꿀팁
렌더링 : 화면에 그려줘
조건부 렌더링 : 조건에 맞게 화면에 그려줘