Skip to content

fetch API 사용하기

정재희 edited this page Nov 1, 2023 · 2 revisions

목적

Fetch api를 커스텀하고 서버사이드에서의 사용과 클라이언트의 사용이 분리됨에 따라 적절한 가이드를 제공하고자 함.

설명

Fecth API를 클래스로 래핑하여 사용

NextJS에서 axios 등의 서드파티 라이브러리를 사용하면 캐싱 정책 등을 따로 적용해야 하는 수고가 있다.

때문에 NextJS 공식 문서에서도 이를 권장하지 않고, 기존 fetch를 확장한 형태를 사용하기 때문에 fetch를 사용하는 것이 좋다.

class FetchAPI {
  private baseURL: string
  private headers: { [key: string]: string }

  private static instance: FetchAPI

  private constructor() {
    this.baseURL = ''
    this.headers = {
      'Content-Type': 'application/json',
    }
  }

  public static getInstance(): FetchAPI {
    if (!FetchAPI.instance) {
      FetchAPI.instance = new FetchAPI()
    }
    return FetchAPI.instance
  }

  public setBaseURL(url: string): void {
    this.baseURL = url
  }

  public setDefaultHeader(key: string, value: string): void {
    this.headers[key] = value
  }

  public async get(
    endpoint: string,
    nextInit: RequestInit = {},
    customHeaders: { [key: string]: string } = {},
  ): Promise<any> {
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      method: 'GET',
      headers: { ...this.headers, ...customHeaders },
      ...nextInit,
    })
    return response
  }

  public async post(
    endpoint: string,
    body: any,
    nextInit: RequestInit = {},
    customHeaders: { [key: string]: string } = {},
  ): Promise<any> {
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      method: 'POST',
      headers: { ...this.headers, ...customHeaders },
      body: JSON.stringify(body),
      ...nextInit,
    })
    return response
  }

  public async put(
    endpoint: string,
    body: any,
    nextInit: RequestInit = {},
    customHeaders: { [key: string]: string } = {},
  ): Promise<any> {
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      method: 'PUT',
      headers: { ...this.headers, ...customHeaders },
      body: JSON.stringify(body),
      ...nextInit,
    })
    return response
  }

  public async delete(
    endpoint: string,
    nextInit: RequestInit = {},
    customHeaders: { [key: string]: string } = {},
  ): Promise<any> {
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      method: 'DELETE',
      headers: { ...this.headers, ...customHeaders },
      ...nextInit,
    })
    return response
  }
}

export default FetchAPI

context 제공

해당 클래스를 전역에서 관리하기 위해 Context를 전역에 wrapper로 감싸 제공한다. singleton 클래스의 인스턴스를 불러오는 것으로 충분하기 때문에 삭제되었습니다.

관련 PR

apiClient 사용하기

해당 모듈에서 불러와 사용하면 된다.

// apiClient.ts
import { Environment } from '@/config/environment'
import FetchAPI from '@/lib/fetchAPI'

const apiClient = FetchAPI.getInstance()
apiClient.setDefaultHeader('Content-Type', 'application/json')
apiClient.setBaseURL(Environment.apiAddress() ?? '')

export default apiClient

함수 정의

각 요청에 대한 함수를 사전에 정의한다. 필요시 직접 fetch를 사용해도 무관.

각 내용을 src/services/ 이하에 만들어 사용한다.

// 예시 test.ts
import ApiEndPoint from '@/config/apiEndPoint'
import apiClient from '../apiClient'

const getTest = async () => {
  const response = await apiClient.get(
    ApiEndPoint.test(),
    {next: {revalidate:10}},
    { Authorization: 'Bearer ' },
  )
  return response
}

export { getTest }

컴포넌트에서 사용 예

  • 클라이언트 사이드
'use client'

import React, { useEffect } from 'react'
import { getTest } from '@/services/test/test'

export default function TestBlock() {
  useEffect(() => {
    async function fetchData() {
      const data = await getTest()
      console.log(await data.json())
    }
    fetchData()
  }, [])

  return <div>{'index '}</div>
}
  • 서버 사이드
import React from 'react'
import { getTest } from '@/services/test/test'

async function getTestValue() {
  const res = await getTest()
  const data = await res.json()
  return data
}

export default async function TestBlock() {
  const data = await getTestValue()
  console.log(data.message)

  return <div>{'index '}</div>
}