REST API

byeol·2023년 8월 5일
0
post-thumbnail

그런 REST API로 괜찮은가라는
강연을 듣다가 너무 어려워서
테코톡 REST 을 보게 되었고 내용을 정리해 보았습니다.
또한
위 강연을 들으며
생긴 의문점들,
나는 지켰을까,
실제로 지켰을 때 어떤 변화가 있는지를 적어보았습니다.
(🤔, 🔦 이 달린 목차는 개인적으로 알아본 내용입니다. )

잘못된 점이 있다면 댓글로 남겨주세요 💡

만든 사람과 사람들의 사이에서 REST에 대한 생각이 다르다?!

REST API : REST 아키텍처 스타일을 따르는 API

REST : 분산 하이퍼미디어 시스템 예를 들어 웹을 위한 아키텍처 스타일

아키텍처 스타일 : 제약조건들의 집합

REST를 구성하는 스타일

  • client - server
  • stateless
  • uniform interface ( 이걸 잘 만족시키지 못한다)
  • layered system
  • code-on-demand(optional)

여기까지 듣다가 너무 어려워서 테코톡 REST으로 넘어가게
되었습니다.
이 내용 이후부터는 위 테코톡 내용을 중심으로 서술되어져 있습니다.

Uniform interface을 잘 지키지 못한다고 하였으니
이 부분에 대해서 테코톡에서 자세히 알아보도록 할게요.

Uniform interface

  1. 자원의 식별

  2. 표현을 통한 자원의 조작

  3. 자기 서술적 메세지

  4. HATEOS(hypermedia as the engine of application state)

1. 자원의 식별

자원

  • 이름을 지닐 수 있는 모든 정보
  • 개념적인 대상

자원은 객체

시간에 지남에 따라 변한다 -> 없었다가 변하고 파괴되어 사라진다 -> 따라서 이 변하는 객체에 대한 식별이 필요하다 -> 식별자가 필요하다

URI를 통해서 식별한다. user/1 -> DB에 있는 1번 해당

2. 표현을 통한 자원의 조작

표현이란

  • 특정 상태의 자원에 대한 표현

  • 자원은 다양한 방식으로 표현

사용자라는 자원 -> HTTP/1.0 200 OK ... or Hi I'm user One! => 문서, 파일, HTTP 메시지 엔티티


<출처 : https://www.youtube.com/watch?v=Nxi8Ur89Akw>

다시 반대로 자원의 기대되는 상태에 대한 표현을 보내면 이를 서버에서 자원을 저장하여 해당 자원을 표현하는 식별자를 응답으로 보낸다.


<출처 : https://www.youtube.com/watch?v=Nxi8Ur89Akw>

REpresentational State Transfer

표현된 (자원의) + 상태 + 전송

  • 자원의 현재 상태
  • 자원의 기대되는 상태

URI로 표현된 자원은 마치 객체와 같고 이는 시간에 따라 상태가 변화할 수 있다.

그리고 특정 시점에 자원이 지닌 상태를 표현하고 이를 서로 전송하는 것

3. 자기 서술적 메세지

클라이언트와 서버에서 오가는 메세지는 그 자체로 설명할 수 있어야 한다.

클라이언트와 서버 사이에는 수많은 중개자가 존재인 컴포넌트들이 존재합니다.

이 컴포넌트들이 어딘가에 있는 클라이언트와 서버는 메세지에 따라 전송하는데

따라서 이 메세지는 이 컴포넌들이 읽을 수 있도록 잘 설명된 자기 서술적 메세지여야 한다.

즉 메세지를 읽는 주체는 컴포넌트들이다.

이 메세지에 포함되어야할 내용

  • HOST 헤더 : 도메인명 기제 필요, 어디에서 보낸 요청이다!
    • IP 주소는 안될까? 하나의 IP 주소가 복수의 도메인명 존재 가능하기 때문에 ❌❌
  • 캐쉬
    • 캐쉬 관련 헤더를 통한 캐쉬 전략 지정
    • 진짜 캐쉬 자체의 의미 즉 컴포넌트가 캐쉬가 되는데
    • 그 전략에는 종류가 Cache-Control, Age, Etag, Vary가 있다.
    • 저는 이 부분이 어떤 내용인 궁금하여 더 찾게 되었고 뒤에 더 자세한 내용이 등장합니다.

4 HATEOAS

examle.com/

examle.com/email

examle.com/images

다 외워서 해당 페이지로 이동?

아니요 하이퍼미디어를 통한 앱 상태 변경하자

그래서 HATEOAS는 사용자가 하이퍼미디어를 통해서 앱 상태를 변경할 수 있는 인터페이스를 제공해야 한다는 의미를 가지고 있다.

근데 이건 프론트엔드 개발자의 일이 아닌가?

JSON으로 주는데 어떻게?

그렇지만 URI 등을 포함하여 보내도록 하자.

🔦 실제로 나는 **HATEOAS를 했을까? 🔺


    @PostMapping
    public ResponseEntity<VoucherServiceResponse> createVoucher(
            @RequestBody VoucherCreateRequest voucherCreateRequest) {

        VoucherServiceResponse voucher = voucherService.createVoucher(
                voucherControllerConverter.ofVoucherServiceCreateRequest(
                        voucherCreateRequest));

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

        return ResponseEntity.created(location)
                .body(voucher);
    }

저는 제가 Location을 제공했다는 사실만으로 HATEOAS를 지킨 줄 알았는데요. 맞았습니다. 사실 틀린 줄 알았는데 그런 REST API로 괜찮은가 라는 강연에서 맞다고 하셔서 생각해보니 그 자원의 상태 전이가 일어나 맞다는 생각이 들었습니다.

그게 아니었습니다.

스프링에서는 HATEOAS를 지키도록 하는 라이브러리를 제공하고 있는데 아래 URI를 통해 들어가서 자세히 보시길 바랍니다.

Spring HATEOAS (https://spring.io/projects/spring-hateoas)

실무에서 HATEOAS 사용할까? 🤔

저는 현업에 계신 개발자분께 물어봤을 때 사용하는 걸 본 적이 없다고 하셨고

아래는 실제로 저와 같은 의문을 가지고 질문하셨고 그에 대한 인프런 지식공유자의 답변입니다.

그래서 이 모두 지켜야할까???

그건 아니다

상황에 따라 최적이 아닐 수 있다.

다음으로 저는 캐시 전략에 대한 궁금증이 생겼고 이에 대해서 더 자세히 알아보게 되었습니다.

🔦 캐시와 REST(부제 : ETags for REST with Spring )

캐시 능력과 이전 자원을 재사용하는 것은 성능 최적화에 중요하다

이유

크기가 큰 응답들을 클라이언트와 서버 사이에 많은 왕복 작업이 드는데

자원이 필요할 때나 브라우저가 작업을 할 때 계속 불러오는 것은 데이터 비용이 많이 발생한다.

그래서 브라우저는 HTTP 캐시를 구현하고 있다.

개발자가 해야할 일은 브라우저에게 응답 캐시를 언제 얼마나 보유할지 가이드를 내려줘야 한다.

위 상황을 보면

  • 1024 Byte
  • 120초 동안 유지
  • 유효토큰 x234dff

유효토근과 Cache-Control의 의미

유효토근 Etag

위 상황을 더 자세히 말해보겠습니다.

120초가 지나서 브라우저가 이 자원에 대해서 요청을 하겠죠?

브라우저는 로컬 캐시를 확인합니다.

불행히 자신이 원하는 자원은 없습니다.

그래서 서버에 요청을 보냅니다.

분명 지금과 같은 자원인데도 브라우저는 알 길이 없으므로 또 보내게 됩니다.

이를 해결해주는 것이 Etag헤더 입니다.

Etag 헤더는 파일에 대한 지문이라고 볼 수 있습니다.

서버는 리소스에 대한 지문을 만들고 이를 브라우저에게 전달합니다.

120초가 지나 다음 요청이 왔을 때 이 지문을 확인하고 같다면

다운로드 하여 다시 보내지 않고 "304 Not Modified"라는 값을 리턴하게 됩니다.

그러면 브라우저는 내가 요청한 자원이 지금과 같다고 생각하고 똑같은 것을 보여줍니다.

아니 그래서 우리는 어떻게 Etag를 만들까요 🤔

스프링 프레임 워크가 제공하는 ShallowEtagHeaderFilter가 있습니다.

이를 적용하기 위해서 빈으로 등록하여 사용하면 된다고 합니다.

@Configuration
public class EtagConfiguration {
  @Bean
  public ShallowEtagHeaderFilter getShallowEtagHeaderFilter() {
    return new ShallowEtagHeaderFilter();
  }
}

또한 특정 URI에만 적용할 수도 있습니다.
실제로 저는 데브코스에 했던 바우처 미션에 적용해보았습니다.

@Configuration
public class EtagConfiguration {

    @Bean
    public FilterRegistrationBean<ShallowEtagHeaderFilter> getShallowEtagHeaderFilter () {
        FilterRegistrationBean<ShallowEtagHeaderFilter> filterFilterRegistrationBean
                = new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
        filterFilterRegistrationBean.addUrlPatterns("/view/*");
        filterFilterRegistrationBean.setName("etagFilter");
        return filterFilterRegistrationBean;
    }
}

실제로 이를 적용해보았을 때 어떤 변화가 생길까요 🤔

Etag를 적용하기 전과 후를 살펴봅시다.

응답 결과를 보니 Etag가 있고

같은 페이지를 새로고침했을 때 바뀐 내용이 없어 304를 반환하고 있습니다!

그리고 정말로 절약이 가능한지 postman으로 확인해보았습니다.

처음 요청에는 1.93KB

두 번째 요청에는 157B만 다운로드 된 것을 확인할 수 있습니다!

그러나 생각해봐야 할것은

출처: https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/

위 그림과 같이 필터의 로직 처리가 해당 요청의 시작과 응답 마지막에 위치하고 있어서 항상 모든 데이터에 대한 Hash 함수 처리를 진행한다는 것!

따라서 API로 제공하는 데이터 크기가 클 수록 Hash 함수로 값을 계산하는 시간이 증가하여 HTTP 응답 시간이 길어진다고 합니다.

출처

Etag를 이용하여 더 나은 Restful API 만들기

테코톡 정의 REST

https://www.baeldung.com/etags-for-rest-with-spring

profile
꾸준하게 Ready, Set, Go!

1개의 댓글

comment-user-thumbnail
2023년 8월 5일

좋은 글 감사합니다. 자주 올게요 :)

답글 달기