Skip to content

기술 스택

jgjgill edited this page Nov 9, 2023 · 6 revisions

TailwindCSS

tailwind에서 제공하는 유틸리티를 위주로 사용한다.

  • 피그마에서 모호한 부분이 발생하면 유틸리티에 우선순위를 둔다.
  • 작은 쪽과 큰 쪽이 같으면 작은 쪽으로 통일시킨다.

React Query

React Query 관련 코드는 _hooks/query 폴더에서 관리한다.

폴더 내에는 관리되는 데이터에 대한 파일들로 구성한다. 파일에는 다음과 같은 항목이 구성된다.

  • 타입
  • 키 객체
  • api 함수
  • query 함수
  • mutation 함수

타입

type TodoType = {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
};

키 객체

export const todoKeys = {
  all: ['todos'] as const,
};

api 함수

  • 선언했던 타입을 할당한다.
  • 주의 사항: fetch는 추후 axios로 변경될 예정
export const getTodos = async (): Promise<TodoType[]> => {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos', {
    method: 'GET',
    headers: { 'Content-Type': 'application/json;charset=UTF-8' },
  });

  const data = await response.json();

  if (!response.ok) {
    if (data.message) {
      throw new Error(data.message);
    }

    throw new Error('알 수 없는 에러');
  }

  return data;
};

const createTodo = async ({ userId, id, title, completed }: Create) => {
  return fetch('/todos/create', {
    ...관련 코드
  });
};

query 함수

  • 초기 데이터가 undefined가 나오는 것을 방지하기 위해 useSuspenseQuery를 사용한다.
  • 선언했던 키 객체를 사용해 키 값을 할당한다.
export const useGetTodos = () => {
  return useSuspenseQuery({ queryKey: [todoKeys.all], queryFn: getTodos });
};

mutation 함수

export const useCreateTodo = () => {
  return useMutation({ mutationFn: createTodo });
};

참고 사항

서버 컴포넌트에서 React Query 사용하는 방법

서버 컴포넌트에서 초기 데이터를 받아서 사용할 수 있다. 다음과 같은 코드 구성이 필요하다.

  • 키 객체와 api 함수를 불러와야 한다. (이와 관련해서 키 객체와 api 함수의 위치는 추후에 변경될 수 있다.)
export default async function Home() {
  const queryClient = new QueryClient();

  await queryClient.prefetchQuery({
    queryKey: [todoKeys.all],
    queryFn: getTodos,
  });

  return (
    ...
    <HydrationBoundary state={dehydrate(queryClient)}>
      <QueryTest />
    </HydrationBoundary>
   );
}

클라이언트 컴포넌트에서는 다음과 같이 사용할 수 있다. 초기 데이터가 성공적으로 받아진다.

const QueryTest = () => {
  const { data: todos } = useGetTodos();
  console.log(todos);

  return <div>query test</div>;
};

클라이언트 컴포넌트에서 사용할 때는 다음과 같은 형태로 사용하고자 한다.

  • useQuery관련 훅, useMutation 관련 훅 순서대로 배치한다.
  • useQuery의 경우 훅의 이름에서 명사만을 사용한다.
  • useMutation의 경우 훅의 이름에서 동사+명사만을 사용한다.
const { data: channels } = useGetChannels();
const { data: posts } = useGetPosts();
...

const { mutate: createChannel } = useCreateChannel();
const { mutate: createPost } = useCreatePost();

참고 문서

suspense 사용에 따른 주의 사항

React Query에서 타입적으로 undefined를 없애기 위해 useSuspenseQuery를 사용했다.

여기서 suspense를 통해 성공만을 가정할 수 있었지만 에러에 대한 처리도 필요하다.

이를 위해 Error Boundary를 사용할 필요가 있는데 NextJS에서 error.tsx 파일에서 기본적으로 내장되어 있다.

NextJS에서 제공하는 기본 error.tsx 예제 코드는 다음과 같다.

'use client' // Error components must be Client Components
 
import { useEffect } from 'react'
 
export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  useEffect(() => {
    // Log the error to an error reporting service
    console.error(error)
  }, [error])
 
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button
        onClick={
          // Attempt to recover by trying to re-render the segment
          () => reset()
        }
      >
        Try again
      </button>
    </div>
  )
}

MSW

폴더 구성

  • _mocks/handlers 경로에 테스트할 목록 관련 폴더를 구성한다.
  • 도메인 폴더에서 handler.ts, mock.ts 파일을 사용한다.
  • handler 파일에는 테스트할 api 코드, mock 파일에는 테스트에 사용되는 데이터를 추가한다.
  • handlers.ts 파일에서 사용되는 handler 배열을 추가한다.

_mocks/handlers/관심사/handler.ts

export const tempHandler = [
  http.get('/mocks/api', () => {
    console.log('Captured a "GET /mocks/api" request');

    return HttpResponse.json({ test: 'temp get mock data' });
  }),

  http.post('/mocks/api', async ({ request }) => {
    const data = await request.json();
    console.log('Captured a "POST /mocks/api" request, data: ', data);

    return HttpResponse.json({ test: 'temp post mock data' });
  }),
];

_mocks/handlers/관심사/mock.ts

export const ITEM = [사용되는 데이터 구성]

_mocks/handlers/handlers.ts

import { itemHandler } from './item/handler';

const handlers = [...tempHandler];

export default handlers;

사용 방법

query parameters

http.get('/mocks/api/items/stores', async ({ request }) => {
  await delay(1000);

  const url = new URL(request.url);

  const memberId = url.searchParams.get('member-id');
  const size = Number(url.searchParams.get('size'));
  const page = Number(url.searchParams.get('page'));

  if (!memberId || !size || !page) {
    return new HttpResponse(null, { status: 404 });
  }

  const results = ITEMS.slice(0, page * size);

  return HttpResponse.json(results);
}),
참고 문서

path params

http.get('/mocks/api/items/:itemId', async ({ params }) => {
  await delay(1000);

  const { itemId } = params;

  if (!itemId) {
    return new HttpResponse(null, { status: 404 });
  }

  const results = ITEMS[Number(itemId) - 1];

  return HttpResponse.json(results);
}),
참고 문서

request body

http.post('/mocks/api/items', async ({ request }) => {
  await delay(1000);

  const newPost = (await request.json()) as Omit<
    ItemType,
    'itemId' | 'storeId'
  >;

  const results = { itemId: ITEMS.length, storeId: 1, ...newPost };

  return HttpResponse.json(results);
}),
참고 문서

주의 사항

page.tsx에서 클라이언트 컴포넌트로 msw 사용시 에러가 발생

page.tsx에서 클라이언트 컴포넌트를 구성하여 msw를 사용할 시 에러가 발생할 수 있다. (msw 관련 문제인 것 같은데 아직 확실하지 않다.. 배포 api에서도 테스트가 필요한 상황..)

MSW에서는 useSuspenseQuery 사용시 에러 발생

msw를 사용하는 테스트 환경에서는 useSuspenseQuery를 사용하면 에러가 발생한다. 그래서 테스트 환경에서는 useQuery를 사용해야 합니다. (추후 배포 api에서 useSuspenseQuery 테스트 진행 예정)