[RestAPI] Java Persistence API 사용

wony·2024년 5월 30일

Spring

목록 보기
29/33

개요

  • JPA 개요
  • Enity 설정
  • H2 Console 사용을 위한 SecurityConfig 파일 수정
  • JPA Service를 위한 GET/POST/DELETE 메소드 추가
  • Post Entity 추가와 User Entity와의 관계 설정

1. Java Persistence API의 개요

1) JPA

  • Java Persistence API
  • 자바 ORM 기술에 대한 API 표준 명세
  • 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
  • EntityManager를 통해 CRUD 처리

2) Hibernate

  • JPA의 구현체, 인터페이스를 직접 구현한 라이브러리
  • 생산성, 유지보수, 비종속성

3) Spring Data JPA

  • Spring Module
  • JPA를 추상화한 Repository 인터페이스 제공

2. JPA 사용을 위한 Dependency 추가와 Entity 설정

1. Spring Security 설정 주석 처리

  • Spring Security 설정을 주석 처리
    • ( JPA ) 를 편리하게 사용하기 위해 잠시 주석처리
  • 주로 pom.xml 파일에서 해당 의존성을 주석 처리하거나 설정 파일에서 Spring Security 관련 구성을 주석 처리할 수 있습니다.

2. H2 Console 사용 설정 추가

  • H2 Console 을 사용하기 위해서는 pom.xml 파일과 application.yml 파일에 설정을 추가해야 합니다.

pom.xml:

<!-- H2 Database -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

application.yml:

spring:
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true

3. User 클래스 엔티티로 이동 및 매핑 설정

  • User 클래스를 엔티티로 지정하고, 데이터베이스 테이블과 매핑하기 위해 필요한 어노테이션을 추가합니다.

User.java:

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue
    private Integer id;

    // 추가 필드는 여기에 추가
}
  • 여기서 @Entity 어노테이션은 해당 클래스가 JPA 엔티티임을 표시합니다.
  • @Table(name = "users")은 테이블의 이름을 지정합니다.
  • @Id는 해당 필드가 기본 키(primary key)임을 나타내며,
  • @GeneratedValue는 기본 키 값이 자동으로 생성되도록 설정합니다.
  • 추가적인 필드가 있다면 User 클래스에 추가하여 해당 필드들도 데이터베이스의 컬럼과 매핑되도록 해야 합니다.

3. Sprign Data JPA를 이용한 초기 데이터 생성

1. 초기 데이터 저장을 위한 설정

  • data.sql 파일을 resources 폴더에 추가하여 초기 데이터를 삽입합니다.

resources/data.sql:

INSERT INTO users(id, join_date, name, password, ssn) VALUES (90001, NOW(), 'User1', 'test111', '701010-1111111');
INSERT INTO users(id, join_date, name, password, ssn) VALUES (90002, NOW(), 'User2', 'test222', '801111-2222222');
INSERT INTO users(id, join_date, name, password, ssn) VALUES (90003, NOW(), 'User3', 'test333', '901111-1222222');

INSERT INTO post(description, user_id) VALUES ('My first post', 90001);
INSERT INTO post(description, user_id) VALUES ('My second post', 90001);

2. Spring Security 설정

  • SecurityConfig 파일을 작성하여 H2 Console의 인증을 무시하도록 설정
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityCustomizer;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring()
                           .requestMatchers(new AntPathRequestMatcher("/h2-console/**"));
    }

    @Bean
    public SecurityCustomizer securityCustomizer(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .and()
            .httpBasic();
        http.csrf().disable();
        http.headers().frameOptions().disable();
        return http.build();
    }
}
  • 이렇게 하면 H2 Console을 사용할 때 인증 없이 접근할 수 있게 되며,
    초기 데이터가 data.sql을 통해 데이터베이스에 삽입됩니다.

4. JPA Service 구현을 위한 Controller, Repository 생성

1. UserRepository 인터페이스 생성

  • UserRepositoryJpaRepository를 상속받아 User 엔티티에 대한 기본 CRUD 메소드를 제공합니다.
  • @Repository 어노테이션을 사용하여 Spring의 구성 요소로 등록합니다.

UserRepository.java:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}

2. UserJPAController 생성

  • User 엔티티와 상호작용하는 RESTful 컨트롤러를 작성합니다.
  • 이 컨트롤러는 UserRepository를 주입받아 사용자 데이터를 관리하는 메소드를 정의합니다.

UserJPAController.java:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/jpa")
public class UserJPAController {

    private final UserRepository userRepository;

    @Autowired
    public UserJPAController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @GetMapping("/users")
    public List<User> retrieveAllUsers() {
        return userRepository.findAll();
    }
}

설명

  1. UserRepository.java:
    • UserRepository 인터페이스는 JpaRepository<User, Integer>를 상속받아 User 엔티티에 대한 기본 CRUD 메소드를 제공합니다.
    • @Repository 어노테이션을 사용하여 Spring의 구성 요소로 등록됩니다.
  1. UserJPAController.java:
    • @RestController@RequestMapping("/jpa") 어노테이션을 사용하여 RESTful 웹 서비스를 정의합니다.
    • UserJPAController 클래스는 UserRepository를 주입받아 사용자 데이터를 관리합니다.
    • @GetMapping("/users") 메소드는 모든 사용자 목록을 반환합니다.
  • 이 설정을 통해 Spring Data JPA 를 사용하여 User 엔티티에 대한 기본 CRUD 기능을 쉽게 구현할 수 있으며, RESTful API를 통해 사용자 데이터를 관리할 수 있습니다.

3. JpaRepository 설명

  • JpaRepositorySpring Data JPA에서 제공하는 인터페이스로,
    JPA를 사용하여 데이터베이스 작업을 수행할 수 있도록 다양한 메소드를 제공합니다.
  • 이 인터페이스는 PagingAndSortingRepositoryCrudRepository를 상속받으며, 기본적인 CRUD(Create, Read, Update, Delete) 및 페이징, 정렬 기능을 지원합니다.

1) 제네릭 타입

  • JpaRepository는 제네릭 타입을 사용하여 엔티티 클래스와 그 기본 키의 타입을 지정합니다.
  • 이 부분에서 JpaRepository<User, Integer>는 다음을 의미합니다:
  1. User: 엔티티 클래스의 타입을 지정합니다. 여기서는 User 엔티티를 관리합니다.
  2. Integer: 엔티티의 기본 키( primary key ) 타입을 지정합니다. 여기서는 User 엔티티의 기본 키가 Integer 타입임을 나타냅니다.

2) 주요 메소드

JpaRepository 인터페이스는 여러 메소드를 제공하며, 이러한 메소드를 통해 기본적인 데이터베이스 작업을 수행할 수 있습니다. 몇 가지 주요 메소드는 다음과 같습니다:

  • save(S entity): 엔티티를 저장하거나 업데이트합니다.
  • findById(ID id): 기본 키를 사용하여 엔티티를 조회합니다.
  • findAll(): 모든 엔티티를 조회합니다.
  • deleteById(ID id): 기본 키를 사용하여 엔티티를 삭제합니다.
  • delete(S entity): 엔티티를 삭제합니다.
  • existsById(ID id): 기본 키를 사용하여 엔티티가 존재하는지 확인합니다.
  • count(): 엔티티의 총 개수를 반환합니다.
  • 이 설정을 통해 UserRepository 인터페이스는 Spring Data JPA 의 기능을 사용하여 User 엔티티에 대한 데이터베이스 작업을 쉽게 수행할 수 있습니다.
  • UserJPAController는 이 리포지토리를 사용하여 RESTful API 를 구현합니다.

5. JPA를 이용한 개별 사용자 상세 조회 - HTTP Get method

1. 개별 사용자 상세 조회 API 코드

UserJPAController.java:

@GetMapping("/users/{id}")
public ResponseEntity<EntityModel<User>> retrieveUser(@PathVariable int id) {
    Optional<User> user = userRepository.findById(id);

    if (!user.isPresent()) {
        throw new UserNotFoundException("id-" + id);
    }

    EntityModel<User> entityModel = EntityModel.of(user.get());
    WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers());
    entityModel.add(linkTo.withRel("all-users"));

    return ResponseEntity.ok(entityModel);
}
  1. @GetMapping("/users/{id}"):
    • 이 어노테이션은 HTTP GET 요청이 /jpa/users/{id} 경로로 올 때 retrieveUser 메소드를 호출하도록 합니다.
    • {id}는 경로 변수로, 조회할 사용자의 ID를 의미합니다.
  1. @PathVariable int id:
    • URL 경로에서 전달된 ID 값을 메소드 인자로 매핑합니다.
    • 예를 들어, /jpa/users/1 요청이 오면 id 값은 1이 됩니다.
  1. Optional<User> user = userRepository.findById(id):
    • userRepositoryfindById 메소드를 사용하여 주어진 ID의 사용자 데이터를 조회합니다.
    • 이 메소드는 조회된 사용자 데이터를 Optional 객체로 반환합니다.
  1. 존재 여부 확인:
    • if (!user.isPresent()): 조회된 사용자 데이터가 존재하지 않으면 UserNotFoundException 예외를 발생시킵니다.
    • UserNotFoundException("id-" + id): 사용자 ID를 포함한 예외 메시지를 생성합니다.
    • 이 예외는 HTTP 404 상태 코드와 함께 사용자에게 "사용자를 찾을 수 없음" 메시지를 반환합니다.
  1. HATEOAS 사용:
    • EntityModel<User> entityModel = EntityModel.of(user.get()): HATEOAS를 사용하여 사용자 데이터를 감싸는 EntityModel 객체를 생성합니다.
    • WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers()): 다른 API 엔드포인트(여기서는 모든 사용자 목록 조회 엔드포인트)에 대한 링크를 생성합니다.
    • entityModel.add(linkTo.withRel("all-users")): 생성된 링크를 EntityModel에 추가합니다. 이 링크는 클라이언트에게 모든 사용자 목록 조회 API를 쉽게 탐색할 수 있게 해줍니다.
  1. ResponseEntity 반환:
    • return ResponseEntity.ok(entityModel): HTTP 200 상태 코드와 함께 EntityModel을 응답으로 반환합니다.
    • 이는 클라이언트가 요청한 사용자 데이터를 포함하며, 추가로 모든 사용자 목록 조회 링크도 포함합니다.
  • 이 메소드는 Spring Data JPA의 findById 메소드를 사용하여 특정 사용자를 조회하고, HATEOAS를 통해 관련 링크를 추가하여 응답을 구성합니다. 예외 상황을 처리하여 사용자가 존재하지 않을 경우 적절한 메시지를 반환합니다.

6. JPA를 이용한 사용자 추가와 삭제 - HTTP POST/DELETE method

1. 사용자 삭제 API

UserJPAController.java:

@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable int id) {
    userRepository.deleteById(id);
}

설명

  • @DeleteMapping("/users/{id}"):
    • 이 어노테이션은 HTTP DELETE 요청이 /jpa/users/{id} 경로로 올 때 deleteUser 메소드를 호출하도록 합니다.
    • {id}는 경로 변수로, 삭제할 사용자의 ID를 의미합니다.
  • @PathVariable int id:
    • URL 경로에서 전달된 ID 값을 메소드 인자로 매핑합니다.
  • userRepository.deleteById(id):
    • userRepositorydeleteById 메소드를 사용하여 주어진 ID의 사용자 데이터를 삭제합니다.
    • 해당 사용자가 존재하지 않는 경우 Spring Data JPAEmptyResultDataAccessException을 던집니다.

2. 보안 설정 (SecurityConfig 클래스에 필터 체인 추가)

SecurityConfig.java:

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;

@EnableWebSecurity
public class SecurityConfig {

    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable);
        return http.build();
    }
}

설명

  • @EnableWebSecurity:
    • Spring Security 설정을 활성화합니다.
  • filterChain 메소드:
    • HttpSecurity 객체를 통해 보안 설정을 구성합니다.
    • http.csrf(AbstractHttpConfigurer::disable)를 사용하여 CSRF 보호를 비활성화합니다.
    • http.build()를 호출하여 SecurityFilterChain을 구성합니다.
    • 이 설정을 통해 DELETE, CREATE, UPDATE 같은 작업이 오류 없이 수행되도록 합니다.

3. 사용자 생성 API

UserJPAController.java:

@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
    User savedUser = userRepository.save(user);
    URI location = ServletUriComponentsBuilder
            .fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(savedUser.getId())
            .toUri();

    return ResponseEntity.created(location).build();
}

설명

  • @PostMapping("/users"):
    • 이 어노테이션은 HTTP POST 요청이 /jpa/users 경로로 올 때 createUser 메소드를 호출하도록 합니다.
  • @Valid @RequestBody User user:
    • @RequestBody 어노테이션은 요청 본문을 User 객체로 변환합니다.
    • @Valid 어노테이션은 User 객체의 유효성을 검사합니다.
  • User savedUser = userRepository.save(user):
    • userRepositorysave 메소드를 사용하여 새로운 사용자 데이터를 데이터베이스에 저장합니다.
    • 저장된 사용자 데이터를 반환합니다.
  • URI 생성:
    • ServletUriComponentsBuilder를 사용하여 새로 생성된 사용자의 URI를 만듭니다.
    • .path("/{id}"): 경로에 새로 생성된 사용자의 ID를 포함합니다.
    • .buildAndExpand(savedUser.getId()).toUri(): 새로 생성된 사용자의 ID를 사용하여 URI를 완성합니다.
  • ResponseEntity.created(location).build():
    • HTTP 201 상태 코드와 함께 Location 헤더에 새로 생성된 리소스의 URI를 포함한 응답을 반환합니다.

이 설정을 통해 Spring Data JPA와 Spring Security를 사용하여 사용자 추가와 삭제 기능을 구현하고, 보안 설정을 통해 이러한 작업이 오류 없이 수행되도록 합니다.

7. 게시물 관리를 위한 Post Entity 추가와 초기 데이터 생성

1. Post 엔티티 정의

먼저, 게시물을 관리하기 위한 Post 엔티티 클래스를 정의합니다.

Post.java:

import javax.persistence.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String description;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnore
    private User user;
}

설명

  • @Entity: 이 클래스가 JPA 엔티티임을 나타냅니다.
  • @Data: Lombok 을 사용하여 getter, setter, toString, equals, hashCode 메소드를 자동으로 생성합니다.
  • @NoArgsConstructor@AllArgsConstructor: Lombok을 사용하여 기본 생성자와 모든 필드를 인자로 받는 생성자를 자동으로 생성합니다.
  • @Id@GeneratedValue(strategy = GenerationType.IDENTITY): 기본 키인 id 필드를 자동으로 생성되도록 합니다.
  • @ManyToOne(fetch = FetchType.LAZY): Post 엔티티가 User 엔티티와 N:1 관계임을 나타냅니다. LAZY 로딩을 사용하여 User 객체를 지연 로딩합니다.
  • @JsonIgnore: 순환 참조 문제를 방지하기 위해 user 필드를 JSON 직렬화에서 제외합니다.

2. User 엔티티에 One-to-Many 관계 추가

User.java:

import javax.persistence.*;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.List;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Size(min = 2, message = "Name은 2글자 이상 입력해 주세요.")
    private String name;

    @Past
    private Date joinDate;

    private String password;
    private String ssn;

    @OneToMany(mappedBy = "user")
    private List<Post> posts;

    public User() {
    }

    public User(Integer id, String name, Date joinDate, String password, String ssn) {
        this.id = id;
        this.name = name;
        this.joinDate = joinDate;
        this.password = password;
        this.ssn = ssn;
    }

    // getters and setters
}
  • @OneToMany(mappedBy = "user"): Post 엔티티와 1:N 관계를 설정합니다. mappedBy 속성은 Post 엔티티의 user 필드에 의해 매핑됨을 나타냅니다.

3. 관계 설정 및 클래스 설명

User-Post 관계 설정:

  • 사용자(User)와 게시물(Post)은 1:N 관계를 가집니다. 하나의 사용자는 여러 개의 게시물을 가질 수 있습니다.
  • User 엔티티에서는 List<Post> posts 필드를 통해 관련 게시물들을 참조합니다.
  • Post 엔티티에서는 User user 필드를 통해 게시물의 작성자를 참조합니다.

이와 같이 UserPost 엔티티를 설정하고 초기 데이터를 추가하면 사용자 관리와 게시물 작성을 포함한 기능을 구현할 수 있습니다.

8. 게시물 조회를 위한 Post Entity와 User Entity와의 관계 설정

  • 위 코드는 사용자의 ID를 받아 해당 사용자가 작성한 모든 게시물을 조회하는 API를 추가하는 것입니다.

1. retrieveAllPostsByUser 메소드 설명

UserJPAController.java:

@GetMapping("/users/{id}/posts")
public List<Post> retrieveAllPostsByUser(@PathVariable int id) {
    Optional<User> user = userRepository.findById(id);
    if (!user.isPresent()) {
        throw new UserNotFoundException("id-" + id);
    }

    return user.get().getPosts();
}
  • @GetMapping("/users/{id}/posts"):
    • 이 어노테이션은 HTTP GET 요청이 /jpa/users/{id}/posts 경로로 올 때 retrieveAllPostsByUser 메소드를 호출하도록 합니다.
    • {id}는 경로 변수로, 조회할 사용자의 ID를 의미합니다.
  • @PathVariable int id:
    • URL 경로에서 전달된 ID 값을 메소드 인자로 매핑합니다.
  • Optional<User> user = userRepository.findById(id):
    • userRepositoryfindById 메소드를 사용하여 주어진 ID의 사용자 데이터를 조회합니다.
    • 이 메소드는 조회된 사용자 데이터를 Optional 객체로 반환합니다.
  • 사용자 확인:
    • 사용자가 존재하지 않는 경우 UserNotFoundException을 발생시킵니다.
  • 게시물 조회:
    • 사용자가 존재할 경우 해당 사용자가 작성한 모든 게시물을 반환합니다.
  • 이렇게 구현된 API를 통해 특정 사용자가 작성한 모든 게시물을 조회할 수 있습니다.

9. JPA를 이용한 새 게시물 추가 - HTTP POST Method

  • 위 코드에서 PostRepository 인터페이스는 JpaRepository 인터페이스를 확장합니다.

1. PostRepository 인터페이스 설명

import kr.co.joneconsulting.myrestfulservice.bean.Post;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PostRepository extends JpaRepository<Post, Integer> {
}
  • JpaRepository<Post, Integer>:
    • JpaRepository는 Spring Data JPA에서 제공하는 인터페이스로, CRUD(Create, Read, Update, Delete) 작업을 수행할 수 있도록 도와줍니다.
    • Post 엔티티와 연관된 JpaRepository를 정의하며, 첫 번째 제네릭 타입은 엔티티 클래스를, 두 번째 제네릭 타입은 기본 키의 데이터 타입을 나타냅니다.
    • 이 인터페이스를 통해 Spring Data JPA가 자동으로 필요한 메소드들을 제공합니다. 이는 데이터베이스 조작에 필요한 다양한 메소드들이 포함됩니다.

따라서 위 인터페이스를 사용하여 게시물(Post) 엔티티의 CRUD 작업을 간편하게 수행할 수 있습니다. 이는 createPost 메소드에서 postRepository.save(post)를 통해 구현된 것처럼, 새로운 게시물을 데이터베이스에 저장할 때 사용됩니다.

    User user = userOptional.get();

    post.setUser(user);

    postRepository.save(post);

    URI location = ServletUriComponentsBuilder
            .fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(post.getId())
            .toUri();

    return ResponseEntity.created(location).build();
}
  • 이거 만들기
  • 위 코드는 새로운 게시물을 추가하는 API를 구현하는 것입니다.

2. createPost 메소드 설명

UserJPAController.java:

@PostMapping("/users/{id}/posts")
public ResponseEntity<Post> createPost(@PathVariable int id, @RequestBody Post post) {
    Optional<User> userOptional = userRepository.findById(id);
    if (!userOptional.isPresent()) {
        throw new UserNotFoundException("id-" + id);
    }

    User user = userOptional.get();

    post.setUser(user);

    postRepository.save(post);

    URI location = ServletUriComponentsBuilder
            .fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(post.getId())
            .toUri();

    return ResponseEntity.created(location).build();
}
  • @PostMapping("/users/{id}/posts"):
    • 이 어노테이션은 HTTP POST 요청이 /jpa/users/{id}/posts 경로로 올 때 createPost 메소드를 호출하도록 합니다.
    • {id}는 경로 변수로, 새로운 게시물을 추가할 사용자의 ID를 의미합니다.
  • @PathVariable int id:
    • URL 경로에서 전달된 ID 값을 메소드 인자로 매핑합니다.
  • 사용자 조회:
    • userRepositoryfindById 메소드를 사용하여 주어진 ID의 사용자 데이터를 조회합니다.
    • 조회된 사용자 데이터를 Optional 객체로 반환합니다.
  • 사용자 확인:
    • 사용자가 존재하지 않는 경우 UserNotFoundException을 발생시킵니다.
  • 게시물 생성:
    • 사용자가 존재할 경우 해당 게시물의 작성자로 사용자를 설정합니다.
    • postRepositorysave 메소드를 사용하여 새로운 게시물을 데이터베이스에 저장합니다.
  • 응답 생성:
    • 새로운 게시물의 URI를 생성하여 응답 헤더의 Location에 포함시킵니다.
    • HTTP 상태 코드 201(생성됨)과 함께 응답을 반환합니다.
  • 이렇게 구현된 API를 통해 특정 사용자에게 새로운 게시물을 추가할 수 있습니다.

10. RESTFUL API 설계 가이드

  • REST API 의 성숙도 모델은 Leonard Richardson 이 제안한 모델로, RESTful API 가 얼마나 RESTful 한지를 4단계 로 평가합니다.
  • 각 단계는 REST 원칙의 적용 정도를 나타내며, 단계가 올라갈수록 더 RESTful 합니다.
  • 이 모델은 Level 0 에서 Level 3 까지로 나누어집니다.

Level 0: The Swamp of POX

  • 설명: 이 단계는 RESTful하지 않은 API를 나타냅니다. 시스템은 하나의 HTTP 엔드포인트만 사용하여 모든 기능을 수행합니다.
  • 특징:
    • 단일 URI 엔드포인트가 모든 요청을 처리합니다.
    • 모든 요청이 동일한 HTTP 메서드(보통 POST)로 이루어집니다.
    • API 요청은 보통 XML이나 JSON 형태의 페이로드를 포함합니다.
  • 예시: 모든 요청을 /api 엔드포인트로 보내고, 요청의 동작을 페이로드로 구분하는 방식입니다.
POST /api
Content-Type: application/json

{
"action": "getUser",
"userId": 1
}

Level 1: Resources

  • 설명: 이 단계에서는 리소스를 개념화하여 각각의 리소스를 고유한 URI로 표현합니다.
  • 특징:
    • 개별 리소스는 각각의 URI로 접근됩니다.
    • 여전히 HTTP 메서드는 단일 메서드(주로 POST)로 제한될 수 있습니다.
  • 예시: 각 리소스를 개별 URI로 구분하지만, 요청은 여전히 POST로만 처리됩니다.

    POST /users
    Content-Type: application/json
    
    {
      "action": "getUser",
      "userId": 1
    }

Level 2: HTTP Verbs

  • 설명: 이 단계에서는 HTTP 메서드를 사용하여 다양한 동작을 표현합니다. 즉, CRUD(생성, 읽기, 업데이트, 삭제) 작업을 HTTP 메서드로 구분합니다.
  • 특징:
    • 리소스는 URI로 식별됩니다.
    • HTTP 메서드(GET, POST, PUT, DELETE)를 사용하여 다양한 작업을 수행합니다.
    • 요청과 응답에 HTTP 상태 코드를 사용합니다.
  • 예시: 각 리소스는 HTTP 메서드를 사용하여 조작됩니다.
GET /users/1     // 사용자 정보 조회
POST /users      // 새로운 사용자 생성
PUT /users/1     // 사용자 정보 수정
DELETE /users/1  // 사용자 삭제

Level 3: Hypermedia Controls (HATEOAS - Hypermedia As The Engine Of Application State)

  • 설명: 이 단계에서는 클라이언트가 서버의 리소스를 탐색하고 상호작용할 수 있도록 하이퍼미디어 링크를 제공합니다. 이는 클라이언트가 서버의 상태를 이해하고, 리소스의 URI를 하드코딩하지 않아도 되게 합니다.

  • 특징:

    • 응답에는 관련 리소스에 대한 링크가 포함되어 있습니다.
    • 클라이언트는 이 링크를 따라가며 애플리케이션의 상태를 전이시킬 수 있습니다.
    • RESTful 원칙을 완전히 준수합니다.
  • 예시: 각 리소스는 관련된 리소스에 대한 링크를 포함합니다.

    GET /users/1
    Content-Type: application/json
    
    {
      "id": 1,
      "name": "John Doe",
      "links": {
        "self": "/users/1",
        "friends": "/users/1/friends",
        "posts": "/users/1/posts"
      }
    }
    
    GET /users/1/friends
    Content-Type: application/json
    
    {
      "friends": [
        {
          "id": 2,
          "name": "Jane Doe",
          "links": {
            "self": "/users/2",
            "friends": "/users/2/friends",
            "posts": "/users/2/posts"
          }
        }
      ]
    }
  • Level 3는 가장 RESTful한 형태로, 클라이언트가 서버의 상태를 링크를 통해 탐색하고 상호작용할 수 있게 합니다.
profile
안녕하세요. wony입니다.

0개의 댓글