혼자 적어보는 노트

프로그래머스 데브코스 TIL - Day 73 본문

스터디

프로그래머스 데브코스 TIL - Day 73

jinist 2022. 6. 30. 16:55

 

✅ 오늘의 학습

📌 React 심화(5)

 

- GraphQL

- Apollo Client

 


 

GraphQL

- RestAPI의 한계를 극복하기 위해 등장했다.

- RestAPI의 경우 특정 데이터를 받기 위해 필요없는 데이터 까지 불러올 때가 있다

- GraphQL의 쿼리 문법을 통해 필요한 데이터만 받아올 수 있고

  페이지에 필요한 데이터를 한번에 받아올 수 있다.

 

GraphQL의 규칙

1. POST 메서드로만 요청한다.

2. 백엔드에서 타입을 정의해야 한다.

3. 쿼리 변경은 mutaion을 사용한다.

 

GraphQL의 단점?

- 러닝커브가 RestAPI에 비해 높다.

- HTTP 캐싱 -> 하나의 URI로만 모든 데이터를 가져오다보니 캐싱이 애매하다 -> 라이브러리를 통해 해결 가능

- 필요한 필드를 모두 작성해야 한다.

- 파일 업로드에 대한 명쾌한 방법이 없다.

 

https://graphql-kr.github.io/learn/queries/

쿼리하는 방법은 위의 공식 문서에서 확인할 수 있었다.

 


 

Strapi에서 graphQL사용하기

Marketplace에서 GraphQL 플러그인을 설치하여 간단하게 사용할 수 있다.
cli를 통해서 설치를 하고 다시 npm run develop을 하면 Plugins에 적용된 것을 확인할 수 있다.

http://localhost:1337/graphql
위 경로를 통해 들어가면 쿼리를 작성할 수 있다.

* Docs를 보고 참고해서 값을 불러오자

 

Query

REST에서 GET과 같은 역할을 한다.

이렇게 작성해서 원하는 값만 불러올 수가 있다.

직접 결과를 받아오니 쿼리대로 결과가 나타나는 것을 볼 수 있다.

 

Mutation

데이터를 조작하는 부분은 mutation으로 작성한다.

로그인은 데이터를 조작하는 것이기 때문에 mutation에 있다. Dos에 친절히 설명되어있음.

create, update, delete도 mutation을 통해 요청할 수 있다.

 

HTTP 헤더

응답받은 jwt토큰을 사용해서 권한이 필요한 요청을 보낼 때

HTTP 헤더도 함께 보낼 수 있다

RestAPI와 동일하게 JSON형태로 작성한다.

 

 


 

Apollo

 

공식문서

GraphQL의 클라이언트 라이브러리 중 하나.

공식문서를 살펴보니 GraphQL을 실행하고 결과를 캐시하고

애플리케이션의 상태를 관리하는 라이브러리라고 한다.

 

설치

@apollo/client graphql

 

클라이언트 생성

const client = new ApolloClient({
  uri: 'https://flyby-gateway.herokuapp.com/',
  cache: new InMemoryCache(),
});

공식 문서에 나와있는대로  ApolloClient를 생성하고 Provider로 감싸는 것으로 세팅이 끝난다.

그리고 원하는 페이지에서 gql문법을 사용하여 쿼리를 보내고 받는다.

 

Gql

gql문법으로 만든 쿼리를 담아서 사용할 수 있다.

const UPDATE_TASK = gql`
  mutation UpdateTask($id: ID!, $complete: Boolean) {
    updateTask(id: $id, data: { complete: $complete }) {
      data {
        id
      }
    }
  }
`;

 

useQuery/useMutation

리액트에서는 useQuery/useMutation hook을 사용할 수 있다.

export const Task = ({ id, content, complete }) => {
  const [updateTask] = useMutation(UPDATE_TASK);

  return (
    <ListItem>
      <Content complete={complete}>{content}</Content>
      <ToggleButton onClick={() => updateTask({ variables: { id, complete: !complete } })}>
      toggle
      </ToggleButton>
    </ListItem>
  );
};

hook을 사용하여 gql으로 만든 쿼리를 담아서 쉽게 요청을 보낼 수 있다. contextAPI와 비슷한 형태.


캐시옵션

Apollo에서는 쿼리 결과를 서버에서 받았을 때 자동으로 캐시를 한다. -> 선택 가능

const { loading, error, data } = useQuery(GET_POSTS, {
  fetchPolicy: 'network-only',
});

다음에 데이터를 받아올 때 캐시된 내용을 받아오게될 경우 캐시를 사용하는데 ( default가 catch-first)
무조건 네트워크를 통해 받은 것으로 업데이트를 하고 싶다면 fetchPolicy: network-only (캐시를 저장하기는 함)


Refetch

사용자의 이벤트마다 쿼리를 다시 가져오는 기능을 사용할 수 있다.

const { loading, error, data, refetch } = useQuery(GET_POSTS, {
    variables: { postId },
  });
  
/*JSX*/
 <button onClick={() => refetch({ postId: 2 })}>Refetch</button>

refetch()만 사용할 경우 이전 실행에서 사용한 것과 같은 변수를 사용한다.

 

import { NetworkStatus } from '@apollo/client';

function DogPhoto({ breed }) {
  const { loading, error, data, refetch, networkStatus } = useQuery(
    GET_DOG_PHOTO,
    {
      variables: { breed },
      notifyOnNetworkStatusChange: true,
    }
  );

  if (networkStatus === NetworkStatus.refetch) return 'Refetching!';
  if (loading) return null;
  if (error) return `Error! ${error}`;

refatch를 하고 loading이 아니라 다른 state를 보여주고 싶을 때 networkStatus를 사용할 수 있다.
notifyOnNetworkStatusChange를 true로 변경하면 networkStatus를 통해 refatch시 원하는 값을 받을 수 있다.

useLazyQuery

useQuery와는 다르게 사용자 이벤트를 통해서 나중에 데이터를 받을 수 있다
첫 번째인자로 패치할 수 있는 함수를 제공

export const TaskList = () => {
  const [getTasks, { data, loading, error }] = useLazyQuery(GET_TASKS);

  if (loading) return "Loading,,";
  if (error) return "Error!";

  console.log(data);
  return (
    <UnorderedList>
      {data?.tasks.data.map((task) => {
        return (
          <Task key={task.id} id={task.id} content={task.attributes.content} complete={task.attributes.complete} />
        );
      })}
      <button onClick={() => getTasks()}>getTasks</button>
    </UnorderedList>
  );
};

 

 

Providing options

기본 변수를 지정해놓을 수 있다.

const [addTodo, { data, loading, error }] = useMutation(ADD_TODO, {
  variables: {
    type: "placeholder",
    someOtherVariable: 1234,
  },
});

 

useApolloClient

쿼리 요청 후에 바로 화면에 반영을 시키기 위해 useApolloClient를 이용할 수 있다.

useApolloClient를 사용하면 Client 객체를 받아올 수 있다.

export const NewTaskForm = () => {
  const client = useApolloClient();
  const [createTask] = useMutation(CREATE_TASK);
  const [task, setTask] = useState("");

  const handleSubmit = useCallback(
    (e) => {
      e.preventDefault();
      if (task.length > 0) {
        createTask({ variables: { content: task } }); // 생성 후
        setTask("");
        client.refetchQueries({ include: ["GetTasks"] }); // 리스트 다시 요청
      }
    },
    [createTask, task, client]
  );

  return (
    <Form onSubmit={handleSubmit}>
      <Input type="text" value={task} placeholder="Add a Task" onChange={(e) => setTask(e.target.value)} />
      <SubmitButton>Add</SubmitButton>
    </Form>
  );
};

 


- strapi 관리자에 접속하려면 이전에 만든 my-project의 서버를 구동시켜야 한다 (npm run develop)
- 당연한 이야기겠지만 strapi에서 Collection 생성할 때 지정한 데이터와

  다른 형태의 데이터를 전달할 경우 에러가 발생한다.
- 정상적인 요청을 보냈음에도 불구하고 빈값이 내려온다면 Content-Type Builder에서 draft 설정이 되어있는지 확인하기
- 네트워크 요청 탭에서 해당 요청의 이름을 더블 클릭해서 query를 테스트를 해 볼 수 있다.
- id값은 string인줄 알았는데 따로 ID타입으로 전달해주어야 한다.


✍ 느낀 점

graphQL을 맛보기?로 접하기 좋았던 것 같다.

기초적인 부분만 다루었지만 strapi로 편하게 백엔드를 구성해 놓아서 그런지

Apollo와 함께 사용하는 것이 쉽게 느껴졌다. 이전에 관심있던 부분이여서 재밌기도 했다.

아무래도 이전 프로젝트에서는 주어진 API만 사용해야 했어서 부분에 갈증이 조금 있었던 듯 하다..

다만 현재 타입스크립트와 리액트에 집중해야 해서 당장 좀 더 깊게 다루지 못할 듯 하여 아쉽다.

Comments