Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Graphql과 REST API, 그리고 BFF #43

Open
yuiseo opened this issue Feb 10, 2025 · 1 comment
Open

Graphql과 REST API, 그리고 BFF #43

yuiseo opened this issue Feb 10, 2025 · 1 comment
Assignees
Labels
백엔드 질문 리스트의 직무 구분을 위한 라벨 프론트엔드 질문 리스트의 직무 구분을 위한 라벨

Comments

@yuiseo
Copy link
Member

yuiseo commented Feb 10, 2025

📝 Graphql과 REST API, 그리고 BFF

📚 주제:
GraphQL, REST API, 그리고 Backend for Frontend(BFF)의 개념과 차이점을 비교하고, 각각의 장단점과 활용 사례를 조사한다.


🎯 스터디 목표
GraphQL과 REST API의 기본 개념을 이해하고, BFF의 필요성과 역할을 분석하여 실무에서 적절한 아키텍처를 선택할 수 있도록 한다.


📖 핵심 내용:

Grpahql


GraphQL은 쿼리(Query) 기반 API로, 클라이언트가 필요한 데이터만 선택적으로 요청 할 수 있는 방식

  • 단일 엔드포인트(/grpahql)를 사용하여 필요한 필드만 선택 하고, 한 번의 요청으로 여러 데이터를 가져올 수 있다.

장점

  • 오버페칭 방지 → 필요한 데이터만 가져올 수 있음
  • 언더페칭 방지 → 한 번의 요청으로 연관 데이터까지 가져올 수 있음
  • 유연한 API 확장 가능 → 필드 추가 시 기존 API 변경 없이 사용 가능

단점

  • 초기 설정이 복잡 → 스키마 정의 및 백엔드 설정 필요
  • 캐싱이 어렵다 → 모든 요청이 POST로 처리될 경우 CDN 캐싱 활용이 어려움
  • N+1 문제 발생 가능 → 리졸버 최적화 필요 (DataLoader 사용)

grpahql의 리졸버(resolver)?


👉 graphql에서 쿼리를 해석하고 데이터를 반환하는 함수

즉, 클라이언트가 요청한 데이터를 백엔드에서 찾아서 반환하는 역할

근데 왜 리졸버가 더 많아져?

  • graphql은 각 필드마다 리졸버를 따로 만들어야 하기 때문!

  • 예시

    사용자 정보와 게시글을 함께 요청하는 GraphQL 쿼리

    query {
      user(id: 1) {
        name
        posts {
          title
          content
        }
      }
    }
    

    백엔드 리졸버 (각 필드별로 따로 존재)

    const resolvers = {
      Query: {
        user: (_, { id }) => getUserById(id) // 사용자 정보 가져오는 리졸버
      },
      User: {
        posts: (parent) => getPostsByUserId(parent.id) // 사용자의 게시글 가져오는 리졸버
      }
    };
    

    📌 user 리졸버와 posts 리졸버가 따로 있음!

    📌 ➡ 데이터가 많아질수록 리졸버가 계속 증가하게 됨.

    📌 ➡ 유지 보수가 어려워짐!!

    📌 ➡ 데이터 타입마다 리졸버가 계속 추가됨

    📌➡ 코드가 많아지고, 유지보수가 어려워짐

    📌➡ 각 리졸버가 따로 동작하다 보면 N+1 문제 발생 가능

Graphql의 N+1 문제


데이터를 요청할 때 비효율적인 쿼리 실행으로 인해 발생하는 성능 문제인데, 리졸버를 사용하여 데이터를 가져올 때 발생

예시 사용자 목록과 각 사용자의 게시글 리스트를 가져오는 graphql 쿼리를 작성

query {
  users {
    id
    name
    posts {
      id
      title
    }
  }
}

💡 실행 흐름 (문제 발생 과정)

  • users 데이터를 가져오기 위해 1개의 쿼리 실행

    SELECT * FROM users;  -- (1개의 쿼리 실행)
    
  • posts 데이터를 가져오기 위해 N개의 쿼리 실행

    SELECT * FROM posts WHERE user_id = 1;  -- (N번 실행)
    SELECT * FROM posts WHERE user_id = 2;
    SELECT * FROM posts WHERE user_id = 3;
    ...
    SELECT * FROM posts WHERE user_id = N;
    

👩🏻‍💻

users 데이터를 먼저 가져온 후, 각 사용자별 posts를 개별적으로 가져오면 총 N+1개의 쿼리가 발생(1개의 users 조회 + N개의 posts 조회)

사용자가 많아질수록 (N이 커질수록) 데이터베이스 쿼리 요청이 기하급수적으로 증가하게 되고, 이는 성능 저하를 초래하게 된다.

해결 방법

  • DataLoader(Facebook이 만든 라이브러리)를 활용 - 가장 많이 사용
    • 원리는 쿼리 갯수를 1개로 줄여 성능을 향상 시키는 것
      • data loader 사용시 batching(일괄 처리)와 caching(캐싱)을 적용하여 쿼리 갯수를 줄이는 것

REST API


REST API는 리소스 기반의 HTTP 요청 방식으로, 각 리소스(데이터)에 대해 고정된 엔드포인트(URL)을 사용해 요청하는 방식

장점

  • 구조가 단순하고 익숙함 (RESTful 원칙 따름)
  • 캐싱 활용 가능 (브라우저 및 CDN 지원)
  • HTTP 상태 코드 활용 가능 (200, 404, 500 등)

단점

  • 오버페칭(Over-fetching) → 불필요한 데이터까지 포함될 수 있음
  • 언더페칭(Under-fetching) → 원하는 데이터를 위해 여러 번 요청해야 할 수도 있음
  • API 버전 관리 필요 → 새로운 필드 추가 시 /v1/, /v2/ 같은 엔드포인트 추가 필요

오버 페칭

👉 필요한 데이터보다 더 많은 데이터를 받아오는 문제

즉, 클라이언트가 원하지 않는 정보까지 같이 전달되는 상황

예시 사용자의 이름(name)만 필요한 상황

REST 방식 (오버페칭 문제 발생)


GET /users/1
{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com",
  "age": 30,
  "address": {
    "street": "123 Main St",
    "city": "Seoul"
  },
  "phone": "010-1234-5678"
}
  • 클라이언트는 이름(name)만 필요한데,
  • 이메일, 나이, 주소, 전화번호 등 불필요한 정보까지 받아옴
  • 👉 불필요한 데이터 전송으로 네트워크 비용 증가 & 성능 저하

GraphQL 방식 (오버페칭 해결)


query {
  user(id: 1) {
    name
  }
}
{
  "data": {
    "user": {
      "name": "Alice"
    }
  }
}

오직 필요한 name 필드만 가져오기 때문에 오버페칭 방지 가능! 🔥

언더페칭

👉 필요한 데이터를 한 번의 요청으로 가져오지 못하고, 여러 번 요청해야 하는 문제

예시 클라이언트에서 특정 사용자(user)와 해당 사용자의 게시글(posts)을 한 번에 가져오고 싶다면?

REST 방식


  1. GET /users/1 → 사용자 정보 요청
  2. GET /users/1/posts → 해당 사용자의 게시글 요청

이렇게 2번의 API 호출이 필요

만약 사용자와 댓글 정보까지 같이 가져오려면 3개의 요청이 필요!

언더페칭은 API가 분리 되어 있어 원하는 데이터를 한 번에 가져오지 못하는 경우 발생

GraphQL 방식


query {
  user(id: 1) {
    name
    posts {
      title
      comments {
        text
      }
    }
  }
}

한 번의 요청으로 모든 데이터를 가져올 수 있음.

BFF(backend for frontend)


  • 프론트엔드 애플리케이션의 특성에 맞게 백엔드 API를 제공하는 아키텍처 패턴
  • BFF는 특정 프론트엔드 클라이언트(웹, 모바일, 데스크톱 등)에 최적화된 API를 제공
  • BFF는 프론트엔드와 백엔드 사이의 '맞춤형 API 레이어' 역할

📌 BFF와 GraphQL 비교

  BFF GraphQL
개발 방식 프론트엔드별 맞춤형 API 서버 개발 클라이언트가 원하는 데이터만 요청 가능
데이터 가공 BFF가 데이터 조합 및 가공 GraphQL 서버가 데이터 조합
오버페칭 해결 맞춤 API 제공 원하는 필드만 요청 가능
언더페칭 해결 여러 API를 조합해서 제공 한 번의 요청으로 필요한 데이터 제공

📌 BFF vs GraphQL 차이점

  • GraphQL은 단일 API로 해결, 하지만 BFF는 맞춤형 API 서버를 추가로 운영
  • GraphQL을 사용하면 BFF 없이도 프론트엔드가 필요한 데이터만 요청 가능

💡 참고 자료:
https://graphql.org/learn/
https://tech.kakao.com/posts/364
https://blog.toktokhan.dev/rest-api-vs-graphql-7348f54a220b
https://blog.postman.com/graphql-vs-rest/
https://fe-developers.kakaoent.com/2022/220310-kakaopage-bff/
https://docs.github.com/ko/rest/about-the-rest-api/comparing-githubs-rest-api-and-graphql-api?apiVersion=2022-11-28
https://kosaf04pyh.tistory.com/341

@yuiseo yuiseo added 백엔드 질문 리스트의 직무 구분을 위한 라벨 프론트엔드 질문 리스트의 직무 구분을 위한 라벨 labels Feb 10, 2025
@yuiseo yuiseo self-assigned this Feb 10, 2025
@yuEseo
Copy link

yuEseo commented Feb 16, 2025

n+1 해결 방법
DataLoader(Facebook이 만든 라이브러리)를 활용 - 가장 많이 사용
원리는 쿼리 갯수를 1개로 줄여 성능을 향상 시키는 것
data loader 사용시 batching(일괄 처리)와 caching(캐싱)을 적용하여 쿼리 갯수를 줄이는 것

해당 내용을 좀 더 설명하자면, data loader의 경우 같은 종류의 데이터 조회를 Batching하여 한 번에 처리하는 기법을 사용합니다.
GPT의 도움을 받아 예시를 확인해보니 다음과 같았습니다.

const commentLoader = new DataLoader(async (postIds) => {
  const comments = await db.comments.findMany({
    where: { post_id: { in: postIds } },
  });

  return postIds.map(id => comments.filter(c => c.post_id === id));
});

이렇게 하면 comments를 가져올 때 개별적으로 N번 쿼리를 실행하는 대신, 한 번의 IN 쿼리로 모든 데이터를 가져올 수 있다고 합니다.

SELECT * FROM comments WHERE post_id IN (1, 2, 3, ..., N);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
백엔드 질문 리스트의 직무 구분을 위한 라벨 프론트엔드 질문 리스트의 직무 구분을 위한 라벨
Projects
None yet
Development

No branches or pull requests

2 participants