본문 바로가기
도서/프로그래밍

첫 번째 REST API - 처음부터 제대로 배우는 스프링 부트 [02]

by 신발사야지 2024. 1. 23.

CHAPTER 2

2.1 Maven vs Gradle

2.1.1 Apache Maven

2003년에 출시

빌드 자동화를 위한 대중적이고 확실한 선택

선언형 방식은 다른 빌드 도구에 비해 간단

메이븐은 컨벤션에 따라 특정한 구조로 프로젝트를 생성

이 구조에 벗어나면 오히려 메이븐을 쓰지 않는 것이 더 나음

pom.xml

2.1.2 Gradle

2008년에 출시

도메인 특화 언어

그루비 또는 코틀린 사용

빌드 속도가 빠름

build.gradle

2.1.3 Maven vs Gradle

프로젝트가 엄청 크지 않은 이상 빌드 속도를 위해 Gradle 을 선택할 필요는 없음

메이븐은 프로젝트 구조가 유연하지 못하지만 그레이들의 유연성 때문에 오히려 설정하는데

어려움을 겪기도 함

이 책에서는 Maven 을 채택

2.2 Java vs Kotlin

2.2.1 JAVA

자바는 25년된 언어지만 계속해서 발전하는 언어

2017년 9월 이후로는 6개월마다 기능이 출시

2.2.2 Kotlin

떠오르는 샛별

  • 간결함
  • 안정성
  • 상호 운영성
  • 도구 친화성

자바처럼 하위 호환성을 많이 고려할 필요 없음

2.2.3 Java와 코틀린 중 선택

놀랍게도 둘 다 사용해도 상관 없음, 코틀린은 자바로 변경됨

2.3 스프링 부트 버전

특별한 상황이 아니면 항상 최신버전을 사용

2.4 스프링 이니셜라이저

웹 페이지 또는 CLI 로 사용 가능

CLI 로 사용하면 스크립트로 만들 수 있기 떄문에 CI/CD 에 장점이 있음

 

CHAPTER 3 첫 번째 REST API

3.1 API를 언제 어떻게 사용해야 할까요?

모놀리식 애플리케이션의 시대는 끝남

다음과 같은 경우가 아니면 마이크로서비스를 사용

  • 도메인과 도메인의 경계가 모호할 때
  • 제공된 기능이 긴밀하게 연결되었을 때, 유연성보다 성능이 중요할 때
  • 관련된 모든 애플리케이션의 요구사항이 알려져있고 일관적일 때
  • 기능이 변동성이 없을 때, 변화가 느리거나 변화 범위가 제한적일 때

마이크로 서비스에서는 기능을 작고 응집력 있는 청크로 분할해 디커플링 합니다.

더 빠른 배포 용이한 유지보수가 가능한 유연하고 튼튼한 시스템을 구축하게 됩니다.

어떤 마이크로 서비스에서도 통신이 핵심적인 기능

이 통신을 위해 REST API 가 사용 됨

로이필이 2000년 박사 학위를 논문에서 1994년의 HTTP 객체 모델을 기반으로 REST API 원칙을 세움

3.2 REST가 무엇이고 왜 중요할까요?

다른 서비스가 자신의 현재 상태를 저장하리라 기대하지 않는 것 (무상태)

생존 가능성과 회복 탄련성을 강화

3.4 GET 으로 시작하기

3.4.1 @RestController 개요

@Component의 스테레오타입/별칭입니다.

@Controller가 붙은 클래스는 Model 객체를 받습니다. Model 객체로 표현 계층에 모델 기반 데이터를 제공합니다. 또 ViewResolver와 함꼐 작동해 애플리케이션이 뷰 기술에 의해 렌더링된 특정 뷰를 표시하게 됩니다.

 

package com.example.subrrestdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@SpringBootApplication
public class SubrRestDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SubrRestDemoApplication.class, args);
    }

}

class Coffee {
    private final String id;
    private String name;

    public Coffee(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public Coffee(String name) {
        this(UUID.randomUUID().toString(), name);
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

@RestController
@RequestMapping("/coffees")
class RestApiDemoController {
    private List<Coffee> coffees = new ArrayList<>();

    public RestApiDemoController() {
        coffees.addAll(List.of(
                new Coffee("Cafe Cereza"),
                new Coffee("Cafe Ganador"),
                new Coffee("Cafe Lareno"),
                new Coffee("Cafe Tres Pontas")
        ));
    }

    @GetMapping("")
    Iterable<Coffee> getCoffees() {
        return coffees;
    }

    @GetMapping("/{id}")
    Optional<Coffee> getCoffeeById(@PathVariable String id) {
        for (Coffee c : coffees) {
            if (c.getId().equals(id)) {
                return Optional.of(c);
            }
        }

        return Optional.empty();
    }

    @PostMapping("")
    Coffee postCoffee(@RequestBody Coffee coffee) {
        coffees.add(coffee);
        return coffee;
    }

    @PutMapping("/{id}")
    ResponseEntity<Coffee> putCoffee(@PathVariable String id, @RequestBody Coffee coffee) {
        int coffeeIndex = -1;

        for (Coffee c : coffees) {
            if (c.getId().equals(id)) {
                coffeeIndex = coffees.indexOf(c);
                coffees.set(coffeeIndex, coffee);
            }
        }

        return coffeeIndex == -1 ?
                new ResponseEntity<>(postCoffee(coffee), HttpStatus.CREATED) :
                new ResponseEntity<>(coffee, HttpStatus.OK);
    }

    @DeleteMapping("/{id}")
    void deleteCoffee(@PathVariable String id) {
        coffees.removeIf(c -> c.getId().equals(id));
    }
}
###
GET <http://localhost:8080/coffees>

<> 2024-01-23T200348.200.json

###
GET <http://localhost:8080/coffees/{{id}>}

###
POST <http://localhost:8080/coffees>
Content-Type: application/json

{
  "id": "41821e16-d75d-4289-8fe2-91979f227993",
  "name": "mocha"
}

<> 2024-01-23T200411.200.json

###
PUT <http://localhost:8080/coffees/{{id}>}
Content-Type: application/json

{
  "id": "41821e16-d75d-4289-8fe2-91979f227993",
  "name": "modify"
}

<> 2024-01-23T200432.200.json

###
DELETE <http://localhost:8080/coffees/{{id}>}