REST API를 사용하여 응용 프로그램의 데이터를 검색하는 더 나은 방법이 있는지 궁금했던 적이 있습니까? 백엔드 개발에서 GraphQL은 더 유연하고 효율적인 데이터 검색 방법을 제공하는 강력한 대안으로 부상했습니다. Java에 익숙한 개발자들에게는 현대적인 백엔드에 GraphQL을 통합함으로써 다양한 사용 사례에 맞게 조정된 확장 가능하고 고성능의 API를 제공할 수 있습니다.
이 블로그에서는 GraphQL과 REST의 주요 차이점을 탐색하고, 데이터 검색에 GraphQL을 사용하는 고유한 이점을 강조하며, 실제 예제를 활용하여 Java에서 GraphQL API를 구현하는 방법을 안내합니다.
GraphQL이란?
GraphQL은 API용 쿼리 언어이자 해당 쿼리를 실행하는 런타임입니다. REST와는 달리 고정된 엔드포인트가 미리 정의된 데이터를 반환하는 것과 달리, GraphQL은 클라이언트가 정확히 필요로 하는 데이터를 요청할 수 있게 합니다. 이 세분성은 GraphQL을 복잡하거나 데이터 집약적인 응용 프로그램에 특히 효율적으로 만듭니다.
GraphQL 접근 방식의 장점:
- 세분화된 데이터 검색: 클라이언트는 부서와 같은 불필요한 필드를 검색하지 않고 이름 및 지정만을 쿼리할 수 있습니다.
- 중첩된 쿼리: 단일 쿼리에서 직원 정보와 함께 관리자 세부 정보를 검색합니다.
- 스키마 중심 개발: 스키마는 API 진화를 쉽게 만드는 계약 역할을 합니다.
REST API란?
대표 상태 전송 (REST)은 API를 구축하기 위한 아키텍처 스타일이다. GET, POST, PUT, DELETE와 같은 표준 HTTP 메서드를 사용하여 CRUD 작업을 수행한다. REST는 간결함과 널리 사용되는 특징으로 알려져 있다.
REST의 한계:
- 데이터의 과다 또는 부족한 가져오기.
- 변경 사항을 수용하기 위해 여러 엔드포인트와 버전 관리가 필요하다.
- 내장된 실시간 기능이 없다.
GraphQL 대 REST API: 차이점은 무엇인가?
GraphQL과 REST는 API를 구축하기 위한 두 가지 인기 있는 방법으로, 각각의 강점이 있다. REST는 오랫동안 표준이었지만, GraphQL은 데이터 검색 및 프론트엔드와 백엔드 팀 간의 협업에 더 많은 유연성과 효율성을 제공한다.
주요 차이점
- REST가 여러 엔드포인트를 사용하고 변경 사항을 수용하기 위해 버전 관리를 요구하는 반면, GraphQL은 데이터 검색을 단일 쿼리로 통합하고 클라이언트가 데이터 요구 사항을 지정하기 때문에 버전 관리 필요성을 줄인다.
- REST는 성공 또는 오류를 나타내기 위해 HTTP 상태 코드를 사용하는 반면, GraphQL은 항상 200 OK 상태를 반환하고 응답 본문에서 오류를 통신한다.
- GraphQL은 REST와 달리 내장된 실시간 업데이트를 지원하며, REST에는 내장된 실시간 지원이 없다.
- REST는 많은 도구들로 널리 확립되어 있지만, GraphQL의 환경은 빠르게 성장하여 더 쉬운 개발을 위한 강력한 도구인 GraphiQL과 같은 도구를 제공합니다.
- 마지막으로, REST는 캐싱을 위해 헤더를 사용하지만, GraphQL은 동적 쿼리로 인해 더 고급 기술이 필요하며 효율적인 캐싱을 위한 지속 쿼리와 같은 옵션을 제공합니다.
핵심 GraphQL 개념
1. 스키마 정의 언어 (SDL)
GraphQL은 API의 스키마를 정의하는 데 사용되는 자체 타입 시스템을 가지고 있습니다. 스키마를 작성하기 위한 구문을 스키마 정의 언어(Schema Definition Language, SDL)라고 합니다.
2. 쿼리 vs. 변이 vs. 구독
- 쿼리는 서버에서 데이터를 가져오는 데 사용됩니다. REST가 여러 고정된 엔드포인트를 사용하는 것과 달리, GraphQL은 단일 엔드포인트를 사용하고 클라이언트가 쿼리에서 필요한 데이터를 지정하여 유연성을 제공합니다.
- 변이는 데이터 생성, 업데이트 또는 삭제와 같이 서버의 데이터를 수정하는 데 사용됩니다. 클라이언트가 백엔드에 변경 사항을 전송할 수 있게 하며, 데이터를 작성해야 하는 애플리케이션에 필수적입니다.
- 구독은 클라이언트와 서버 간의 지속적인 연결을 유지하여 실시간 업데이트를 가능하게 합니다. 구독된 이벤트가 발생하면 서버는 클라이언트에 업데이트를 푸시하여 지속적인 데이터 스트림을 제공하며, 쿼리와 변이는 요청-응답 사이클을 따릅니다.
3. GraphQL 스키마
데이터 구조를 정의하여 쿼리하거나 변형할 수 있으며, 서버와 클라이언트 간의 계약 역할을 합니다. 클라이언트가 접근할 수 있는 타입, 필드 및 관계를 지정합니다. 스키마는 일반적으로 데이터 검색을 위한 Query, 데이터 수정을 위한 Mutation, 실시간 업데이트를 위한 Subscription과 같은 특별한 루트 타입을 포함합니다. 이러한 타입은 API의 기능과 클라이언트가 API와 상호작용하는 방식을 집합적으로 정의합니다.
4. 리졸버: GraphQL 쿼리를 데이터에 매핑
리졸버는 GraphQL 서버에서 데이터를 가져오는 논리를 처리하는 함수입니다. 스키마의 각 필드는 해당 필드의 데이터를 검색하거나 계산하는 방법을 결정하는 리졸버에 연결되어 있습니다. 쿼리가 실행되면 서버는 요청된 필드에 대해 적절한 리졸버를 호출합니다. 리졸버는 스칼라 또는 객체를 반환할 수 있으며, 객체가 반환되면 자식 필드에 대한 실행이 계속되고 스칼라가 반환되면 실행이 완료됩니다. null이 반환되면 실행이 중지됩니다. 리졸버는 GraphQL 쿼리를 실제 데이터 소스에 매핑하는 데 필수적입니다.
Java에서 GraphQL 사용의 이점
- 정확한 데이터 가져오기: 필요한 데이터만 쿼리하여 예측 가능하고 효율적인 결과를 보장합니다.
- 단일 요청으로 여러 리소스 가져오기: 관련 데이터를 한 쿼리로 가져와 여러 API 호출을 줄입니다.
- 타입 시스템: API를 타입과 필드로 조직하여 쿼리가 유효하고 오류가 명확하도록 보장합니다.
- 개발자 도구: GraphiQL과 같은 도구를 사용하여 생산성을 향상시키고, 더 나은 쿼리 빌딩 및 디버깅을 위해 타입 정의를 사용합니다.
- 버전 없는 진화: 기존 쿼리를 중단하지 않고 필드를 추가하거나 삭제하여 API를 유지 관리 가능하게 합니다.
- 유연한 데이터 통합: 다양한 저장 엔진과 언어와 호환되는 기존 데이터와 코드 위에 통합 API를 생성합니다.
Java에서 GraphQL API 설정하기
실제 예시: 사용자와 주문
대규모 조직을 위한 직원 디렉토리 API를 구축한다고 상상해 보세요. 목표는 클라이언트가 직원 이름, 직위, 부서 및 보고 계층과 같은 세부정보를 쿼리할 수 있도록 하는 것입니다.
1. 프로젝트 설정하기
Spring Tool Suite를 사용하거나 Spring Initialiser로 이동하여 새 Spring Boot 프로젝트를 생성합니다. 그런 다음 pom.xml
파일에 다음 종속성을 추가합니다:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 엔터티 생성하기
GraphQL을 통해 쿼리되거나 변형될 데이터를 나타내기 위해 Java 엔터티(예: User
및 Order
)를 생성합니다. 예를 들어:
public class User {
strategy = GenerationType.IDENTITY) (
private Long userId;
private String name;
private String email;
private String password;
// Getters and setters...
}
public class Order {
strategy = GenerationType.IDENTITY) (
private Long orderId;
private String orderDetails;
private String address;
private int price;
private User user;
// Getters and setters...
}
3. 리포지토리 생성하기
데이터베이스와 상호 작용하기 위한 리포지토리를 생성합니다:
public interface UserRepository extends JpaRepository<User, Long> {}
public interface OrderRepository extends JpaRepository<Order, Long> {}
4. 서비스 클래스 생성하기
비즈니스 로직을 처리하는 서비스 클래스를 만드세요:
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(User user) {
return userRepository.save(user);
}
public User getUser(Long userId) {
return userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public boolean deleteUser(Long userId) {
userRepository.deleteById(userId);
return true;
}
}
5. GraphQL 컨트롤러 생성
쿼리와 뮤테이션을 처리하는 GraphQL 컨트롤러를 정의하세요:
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
public List<User> getUsers() {
return userService.getAllUsers();
}
public User getUser( Long userId) {
return userService.getUser(userId);
}
public User createUser( String name, String email, String password) {
User user = new User();
user.setName(name);
user.setEmail(email);
user.setPassword(password);
return userService.createUser(user);
}
public boolean deleteUser( Long userId) {
return userService.deleteUser(userId);
}
}
6.GraphQL 스키마 정의
src/main/resources
디렉토리에 schema.graphqls
파일을 만드세요:
type User {
userId: ID!
name: String
email: String
password: String
}
type Query {
getUsers: [User]
getUser(userId: ID!): User
}
type Mutation {
createUser(name: String, email: String, password: String): User
deleteUser(userId: ID!): Boolean
}
7. application.properties에서 GraphQL 구성
원하는 경우, scr/main/resources/application.properties
에서 GraphQL 설정을 구성하세요:
spring.graphql.graphiql.enabled=true
8. 애플리케이션 실행
mvn spring-boot:run
또는 IDE에서 SpringBoot 애플리케이션을 실행하세요. 실행 중에 /graphiql
엔드포인트에 액세스할 수 있습니다.
9. GraphQL 쿼리로 테스트
GraphiQL 또는 Postman과 같은 도구를 사용하여 GraphQL API를 테스트하세요.
뮤테이션을 위한:
mutation {
createUser(
name:"swetha",
email:"[email protected]",
password:"23sde4dfg43"
){
name,
userId
}
}
출력:
{
"data": {
"createUser": {
"name": "swetha",
"userId": "3"
}
}
}
쿼리를 위한:
query{
getUsers{
name
}
}
출력:
{
"data": {
"getUsers": [
{
"name": "Medha"
},
{
"name": "Riya"
},
{
"name": "swetha"
}
]
}
}
고급 GraphQL 기능
1. Fragment를 사용하여 재사용성 향상
프래그먼트는 특정 유형에 대해 정의된 재사용 가능한 필드 세트입니다. GraphQL 코드의 구조와 재사용성을 향상시켜주는 기능입니다.
2. 인수를 사용하여 필드 매개변수화
GraphQL에서는 필드가 인수를 받아 쿼리를 보다 동적이고 유연하게 만들 수 있습니다. 이러한 인수를 통해 API에서 반환되는 데이터를 필터링하거나 사용자 정의할 수 있습니다.
3. GraphQL을 통한 페이지네이션 및 정렬
페이지네이션
페이지네이션은 API 설계에서 다루기 어려운 주제입니다. 전체적으로 보면, 이를 해결하는 두 가지 주요 접근 방식이 있습니다.
- 제한-오프셋: 검색할 항목의 인덱스를 제공하여 목록의 특정 청크를 요청합니다(실제로는 주로 시작 인덱스(오프셋)와 검색할 항목의 수(제한)를 제공합니다).
- 커서 기반: 이 페이지네이션 모델은 좀 더 고급입니다. 목록의 모든 요소는 고유한 ID(커서)와 연결되어 있습니다. 목록을 페이지네이션하는 클라이언트는 시작 요소의 커서와 검색할 항목의 수를 제공합니다.
정렬
GraphQL API 설계를 통해 특정 기준에 따라 정렬(주문됨)된 요소 목록을 반환할 수 있습니다.
GraphQL 사용의 도전 과제와 고려 사항
- 복잡성: GraphQL 스키마 및 쿼리를 관리하는 것은 간단한 데이터 모델이나 경험이 부족한 팀에게 도전적일 수 있습니다.
- 성능 문제: 깊게 중첩된 쿼리는 최적화되지 않으면 백엔드 리소스에 부담을 줄 수 있습니다.
- 캐싱 문제: 표준 REST 기반 캐싱 전략은 적용되지 않으며 맞춤형 솔루션이 필요합니다.
- 보안 문제: 과도한 데이터 요청 및 악의적인 쿼리는 쿼리 제한 및 기타 안전 장치를 필요로 합니다.
- 혼합 사용: 복잡한 데이터 요구에 가장 적합하며, 종종 더 간단한 작업을 위해 REST와 결합됩니다.
결론
GraphQL은 Java에서 현대 API를 구축하는 유연하고 효율적인 접근 방식을 제공하여 동적이고 데이터 집약적인 애플리케이션에 이상적인 선택입니다. 단일 엔드포인트 아키텍처와 강력한 타입 지정은 API 디자인을 단순화하면서 견고한 성능을 보장합니다. 간단한 직원 디렉토리를 만들든 복잡한 분석 플랫폼을 구축하든, GraphQL은 개발자가 쉽게 확장 가능한 솔루션을 제공할 수 있도록 지원합니다. Spring Boot 및 graphql-java
와 같은 도구를 사용하여 오늘 GraphQL 탐색을 시작하고 다음 프로젝트에서 그 잠재력을 최대한 활용해 보세요.
소스 코드
이 튜토리얼의 전체 소스 코드는 Github에서 확인할 수 있습니다.
Source:
https://dzone.com/articles/design-scalable-java-apis-with-graphql