[SpringBoot API] Springboot REST API : 시작하기

Cover image

SpringBoot API 시작하기.

강의 자료

수업 자료

코드


REST API

API

  • Application Programming Interface

REST

  • REpresentational State Transfer
  • 인터넷 상의 시스템 간의 상호 운용성(interoperability)을 제공하는 방법중 하나
  • 시스템 제각각의 독립적인 진화를 보장하기 위한 방법
  • REST API: REST 아키텍처 스타일을 따르는 API

REST 아키텍처 스타일 (발표 영상 11분)

  • Client-Server
  • Stateless
  • Cache
  • Uniform Interface
  • Layered System
  • Code-On-Demand (optional)

Uniform Interface (발표 영상 11분 40초)

상세

Uniform Interface(일관된 인터페이스)란, Resource(URI)에 대한 요청을 통일되고, 한정적으로 수행하는 아키텍처 스타일을 의미합니다. 이것은 요청을 하는 Client가 플랫폼(Android, Ios, Jsp 등) 에 무관하며, 특정 언어나 기술에 종속받지 않는 특징을 의미합니다. 이러한 특징 덕분에 Rest API는 HTTP를 사용하는 모든 플랫폼에서 요청가능하며, Loosely Coupling(느슨한 결함) 형태를 갖게 되었습니다.

  • Identification of resources

    • 리소스가 URI로 식별되면 된다.
  • manipulation of resources through represenations

    • 리소스를 요청할 때 서버는 리소스를 표현하여 응답한다.
    • 대표적인 API 예로는 HTML, JSON, XML이 있다.
    • 콘텐츠 협상

      • 클라이언트가 클라이언트의 요구에 맞는 특정 표현을 요청할 수 있다.
      • 예를 들어 클라이언트는 리소스의 JSON 표현 또는 리소스의 XML 표현을 요청할 수 있다.
      • 서버는 그렇게 할 수 있는 경우 이 표현을 제공할 수 있다.
    • API에서 콘텐츠 협상을 사용하여 여러 클라이언트가 동일한 URL에서 다른 리소스 표현에 액세스할 수 있도록 할 수 있다.
  • self-descrive messages

    • 클라이언트가 resource를 가지고 어떤 일을 수행할때 필요한 모든 데이터가 응답되어야함.
    • 보통 안에 profile(docs) 링크를 명시하는식으로 구현.
  • hypermedia as the engine of appliaction state (HATEOAS)

    • 거의 모든 rest api에서 지키지못함.
    • 어플리케이션의 상태가 하이퍼링크를 통해서 항상 전이가 되어야함.
    • 스프링에서는 spring-boot-hateoas 패키지로 좀 편하게 구현 가능.
좋은 API 예시(github API)
{
  "timeline_url": "https://github.com/timeline",
  "user_url": "https://github.com/{user}",
  "current_user_public_url": "https://github.com/octocat",
  "current_user_url": "https://github.com/octocat.private?token=abc123",
  "current_user_actor_url": "https://github.com/octocat.private.actor?token=abc123",
  "current_user_organization_url": "",
  "current_user_organization_urls": [
    "https://github.com/organizations/github/octocat.private.atom?token=abc123"
  ],
  "security_advisories_url": "https://github.com/security-advisories",
  "_links": {
    "timeline": {
      "href": "https://github.com/timeline",
      "type": "application/atom+xml"
    },
    "user": {
      "href": "https://github.com/{user}",
      "type": "application/atom+xml"
    },
    "current_user_public": {
      "href": "https://github.com/octocat",
      "type": "application/atom+xml"
    },
    "current_user": {
      "href": "https://github.com/octocat.private?token=abc123",
      "type": "application/atom+xml"
    },
    "current_user_actor": {
      "href": "https://github.com/octocat.private.actor?token=abc123",
      "type": "application/atom+xml"
    },
    "current_user_organization": {
      "href": "",
      "type": ""
    },
    "current_user_organizations": [
      {
        "href": "https://github.com/organizations/github/octocat.private.atom?token=abc123",
        "type": "application/atom+xml"
      }
    ],
    "security_advisories": {
      "href": "https://github.com/security-advisories",
      "type": "application/atom+xml"
    }
  }
}

두 문제를 좀 더 자세히 살펴보자. (발표 영상 37분 50초)

  • Self-descriptive message

    • 메시지 스스로 메시지에 대한 설명이 가능해야 한다.
    • 서버가 변해서 메시지가 변해도 클라이언트는 그 메시지를 보고 해석이 가능하다.
    • 확장 가능한 커뮤니케이션
  • HATEOAS

    • 하이퍼미디어(링크)를 통해 애플리케이션 상태 변화가 가능해야 한다.
    • 링크 정보를 동적으로 바꿀 수 있다. (Versioning 할 필요 없이!)

Self-descriptive message 해결 방법

HATEOAS 해결 방법

  • 방법1: 데이터에 링크 제공

    • 링크를 어떻게 정의할 것인가? HAL
  • 방법2: 링크 헤더나 Location을 제공

“Event” REST API

참고 자료

이벤트 등록, 조회 및 수정 API

GET /api/events

이벤트 목록 조회 REST API (로그인 안 한 상태)

  • 응답에 보여줘야 할 데이터

    • 이벤트 목록
    • 링크

      • self
      • profile: 이벤트 목록 조회 API 문서로 링크
      • get-an-event: 이벤트 하나 조회하는 API 링크
      • next: 다음 페이지 (optional)
      • prev: 이전 페이지 (optional)
    • 문서?

      • 스프링 REST Docs로 만들 예정

이벤트 목록 조회 REST API (로그인 한 상태)

  • 응답에 보여줘야 할 데이터

    • 이벤트 목록
    • 링크

      • self
      • profile: 이벤트 목록 조회 API 문서로 링크
      • get-an-event: 이벤트 하나 조회하는 API 링크
      • create-new-event: 이벤트를 생성할 수 있는 API 링크
      • next: 다음 페이지 (optional)
      • prev: 이전 페이지 (optional)
    • 로그인 한 상태???? (stateless라며..)

      • 아니, 사실은 Bearer 헤더에 유효한 AccessToken이 들어있는 경우!

POST /api/events

  • 이벤트 생성

GET /api/events/{id}

  • 이벤트 하나 조회

PUT /api/events/{id}

  • 이벤트 수정

Events API 사용 예제

  1. (토큰 없이) 이벤트 목록 조회

    • create 안 보임
  2. access token 발급 받기 (A 사용자 로그인)
  3. (유효한 A 토큰 가지고) 이벤트 목록 조회

    • create event 보임
  4. (유효한 A 토큰 가지고) 이벤트 만들기
  5. (토큰 없이) 이벤트 조회

    • update 링크 안 보임
  6. (유효한 A 토큰 가지고) 이벤트 조회

    • update 링크 보임
  7. access token 발급 받기 (B 사용자 로그인)
  8. (유효한 B 토큰 가지고) 이벤트 조회

    • update 안 보임

REST API 테스트 클라이언트 애플리케이션

  • 크롬 플러그인

    • Restlet
  • 애플리케이션

    • Postman

스프링 부트 프로젝트 만들기

추가할 의존성

  • Web
  • JPA
  • HATEOAS
  • REST Docs
  • H2
  • PostgreSQL
  • Lombok

자바 버전 11로 시작

  • 자바는 여전히 무료다.

스프링 부트 핵심 원리

  • 의존성 설정 (pom.xml)
  • 자동 설정 (@EnableAutoConfiguration)
  • 내장 웹 서버 (의존성과 자동 설정의 일부)
  • 독립적으로 실행 가능한 JAR (pom.xml의 플러그인)

Event 생성 API 구현: 비즈니스 로직

Event 생성 API

  • 다음의 입력 값을 받는다.

    • name
    • description
    • beginEnrollmentDateTime
    • closeEnrollmentDateTime
    • beginEventDateTime
    • endEventDateTime
    • location (optional) 이게 없으면 온라인 모임
    • basePrice (optional)
    • maxPrice (optional)
    • limitOfEnrollment

basePrice와 maxPrice 경우의 수와 각각의 로직 | basePrice | maxPrice | | | --------- | -------- | ------------------------------------------------------------ | | 0 | 100 | 선착순 등록 | | 0 | 0 | 무로 | | 100 | 0 | 무제한 경매 (높은 금액 낸 사람이 등록) | | 100 | 200 | 제한가 선착순 등록

처음부터 200을 낸 사람은 선 등록

100을 내고 등록할 수 있으나 더 많이 낸 사람에 의해 밀려날 수 있음. |

  • 결과값

    • id
    • name
    • ...
    • eventStatus: DRAFT, PUBLISHED, ENROLLMENT_STARTED, ...
    • offline
    • free
    • _links

      • profile (for the self-descriptive message)
      • self
      • publish
      • ...

Event 생성 API 구현: Event 도메인 구현

public class Event {

    private String name;
    private String description;
    private LocalDateTime beginEnrollmentDateTime;
    private LocalDateTime closeEnrollmentDateTime;
    private LocalDateTime beginEventDateTime;
    private LocalDateTime endEventDateTime;
    private String location; // (optional) 이게 없으면 온라인 모임
    private int basePrice; // (optional)
    private int maxPrice; // (optional)
    private int limitOfEnrollment;

}

추가 필드

    private Integer id;
    private boolean offline;
    private boolean free;
    private EventStatus eventStatus = EventStatus.DRAFT;

EventStatus ENUM 추가

public enum EventStatus {

    DRAFT, PUBLISHED, BEGAN_ENROLLMEND, CLOSED_ENROLLMENT, STARTED, ENDED

}

롬복 애노테이션 추가

@Getter @Setter @EqualsAndHashCode(of = "id")
@Builder @NoArgsConstructor @AllArgsConstructor
public class Event {
  • 왜 @EqualsAndHasCode에서 of를 사용하는가
  • 왜 @Builder를 사용할 때 @AllArgsConstructor가 필요한가
  • @Data를 쓰지 않는 이유
  • 애노테이션 줄일 수 없나