본문 바로가기

Backend/Spring

[Spring] Spring, Postman을 통해 Http통신 테스트

Http란?

Http(Hyper Text Transfer Protocol)이란 HTML(웹 문서를 만들기 위한 언어)를 만드는데 쓰이는 통신 프로토콜입니다.

 

통신 프로토콜이란 서로 다른 두 컴퓨터나 통신 장비 사이에 메시지를 주고받는 양식 혹은 규칙의 체계로 이해할 수 있습니다.

 

즉 Client(user)와 Server간에 주고받는 메시지들을 어떠한 방식으로 요청하고 응답할지에 대한 약속입니다.

 

Http 통신에는 기본적으로 다음 네가지 메소드를 많이 활용합니다.

 

  • Get
  • Post
  • Put
  • Delete

순서대로

 

Get method은 순수하게 데이터를 읽어오고 싶을 때 주로 사용됩니다. sql문의 SELECT문을 생각하시면 됩니다.

Post method는 데이터를 입력하고 싶을 때 주로 사용됩니다. sql문의 INSERT문을 생각하시면 됩니다.

Put method는 데이터를 수정하고싶을때(UPDATE), Delete는 데이터를 삭제하고 싶을 때(DELETE) 사용됩니다. 

 

*추가로 일부를 수정할 때 Patch 메소드를 활용하는 등 여러 메소드가 존재합니다.

 

그러면 각각 메소드들은 어떠한 정보를 읽고 넣고 수정하는지 서버에게 알려주기 위해 Client는 어떠한 방식으로 전송할까요?

 

 

설명의 이해를 돕기 위해 네트워크의 패킷 전송 방식에 대해 간략하게 설명드리겠습니다.

 

기본적으로 네트워크 통신은 Packet으로 통신됩니다. 내가 보내고 싶은 데이터가 1~32라면, 1~8, 9~16... 이렇게 패킷의 크기에 따라서 쪼개서 보내게 됩니다.

 

자 그러면 1번패킷부터 4번 패킷까지 순서대로 Client로부터 출발합니다.

 

하지만 우리의 네트워크망은 다음 그림과 같이 상당히 복잡합니다. 문제는 각 패킷마다 같은 루트로 가는 것이 아니라 Client에서는 1~4번 순서로 출발을 했어도 Server에는 어떠한 순서로 도착할지 모른다는 겁니다.

 

이미지 출처 : https://besuccess.com/opinion/the-anti-network/

그래서 Packet을 효율적으로 전송하고, 도착했을 때 순서대로 조립하기 위해 Header, Body 이렇게 두 구간으로 나눠주게 됩니다.

 

 

이렇게 되면,

Body에는 우리가 직접적으로 전달하고자 하는 Data, Header에는 조립에 필요한 정보 즉 Data에 대한 정보를 포함한 다양한 Body에 대한 정보가 담겨서 같이 전송되게 됩니다.

 

여기까지 이해가 되셨으면 다시 전의 문제로 돌아가서

Get Method에서는 Header에 원하는 정보를 담아서 Reqeust를 전달하게 됩니다.

 

나머지 Post Method, Put Method, Delete Method에서는 RequestBody에 insert, update, delete할 정보를 담아서 전달하게 됩니다. 

 

자 그러면 각각 메소드들이 어떻게 실행되는지 Postman을 활용하여 직접 확인해 볼게요!

 

package com.BlogWithSpringBoot;

import lombok.*;

@Data
@AllArgsConstructor @Builder
public class TestClass {
    private int id;
    private String email;
    private String password;
    public String PrettyPrint() {
        return "id: "+this.id+" email: "+this.email+" password: "+this.password;
    }
}

 

Test를 위해 간단한 Class를 하나 생성해 주었습니다. id, email, password의 field를 가집니다.

 

package com.BlogWithSpringBoot;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.awt.*;

@RestController
public class HelloController {

    @GetMapping("/get/hello")
    public String GetMappingTest (@RequestParam int id) {

        return "Get Mapping : " + id;
    }

    @GetMapping("/get/hello2")
    public String GetMappingTest2 (TestClass testClass) {

        return "Get Mapping : " + testClass.PrettyPrint();
    }

    @PostMapping("/post/hello")
    public String PostMappingTest (@RequestBody TestClass testClass) {

        return "Post Mapping : " + testClass.PrettyPrint();
    }

    @PutMapping("/put/hello")
    public String PutMappingTest (@RequestBody TestClass testClass) {

        return "Put Mapping : " + testClass.PrettyPrint();
    }

    @DeleteMapping("/delete/hello")
    public String DeleteMappingTest (@RequestBody TestClass testClass) {

        return "Delete Mapping : " + testClass.PrettyPrint();
    }
}

 

그다음 테스트를 위한 RestController를 위 코드와 같이 사용하겠습니다. 

 

@RequestParam 어노테이션을 활용하면, Request Header에 Parameter를 넣어서 전달해줍니다.

@RequestBody 어노테이션을 활용하면, Request Body에 Data를 넣어서 전달해줍니다.

 

우선 GetMapping부터 확인해 봅니다.

// http://localhost:8080/get/hello?id=1

 

그림과 같이 Get Mapping : 1이 반환된 것을 확인할 수 있습니다.

 

이번에는 query string으로 id값이 아닌 한번 object에 속한 필드 값을 전달해보도록 하겠습니다.

 

// http://localhost:8080/get/hello2?id=1&email=aaa@tstory.com&password=111

 

이로써 알 수 있는 것은 @RequestParam 어노테이션이 없어도 Spring에서 각 인자에 맞는 Object를 매핑해주어 전달해주는 것을 알 수 있습니다.

 

자 이번에는 Post를 확인해 볼게요.

 

 

Body에 JSON형태로 다음의 인자 값들을 전달한 경우 정상적으로 잘 출력되는 것을 확인할 수 있습니다.

 

같은 방식으로 put과 delete 메소드도 확인해 주시면 됩니다.

 

* 추가로 Request 요청을 보내는 방식에 JSON형태로 보낼 수 있지만 경우에 따라서 x-www-form-urlencoded 방식으로 요청을 보낼 수밖에 없는 상황이 있을 수 있습니다.

 

가령

 

RestAPI를 개발하는데 Client 쪽에서 이미 모든 요청에 대해 x-www-form-urlencoded 방식으로 처리하고 있어서 JSON으로 모두 변경이 힘들 것 같습니다와 같은 주문이 들어올 수 있습니다.

 

만약 저 코드를 그대로 사용한 상태에서 위의 방식으로 Request를 보내게 되면...

 

415 error (MediaType 에러)가 발생하게 됩니다.

 

이 경우 

    @PostMapping("/post/hello")
    public String PostMappingTest (TestClass testClass) {

        return "Post Mapping : " + testClass.PrettyPrint();
    }

 

위와 같이 parameter에 @RequestBody 어노테이션을 지워주면 해당 문제가 해결됩니다.

 

그 이유는 x-www-form-unlencoded의 경우 form 형태로 data를 전송하게 되는데 @ResponseBody의 경우 data를 JSON으로 인지하고 데이터를 받기 때문에 정상적으로 작동하지 않는 것을 볼 수 있습니다.

 

@PostMapping("/post/hello")
public String PostMappingTest (@ModelAttribute TestClass testClass) {

    return "Post Mapping : " + testClass.PrettyPrint();
}

 

다음과 같이 Form형태의 Data를 객체의 Setter 메소드를 활용하여 매핑시켜주는 @ModelAttribute 어노테이션의 경우도 이 경우에는 작동을 합니다. 왜냐하면 @Data 어노테이션에 Setter 메소드가 포함되어있고, 모든 필드명을 일치시켰기 때문에 작동이 됩니다. 

 

** 어떤 어노테이션도 설정하지 않은 경우 @ModelAttribute가 암묵적으로 추가된다는 글을 보았는데, 조금 더 확인을 해보고 작성을 하겠습니다.

 

결론을 짓자면, 

기존의 JSON형태로도 Request를 받으면서, Form 형태로도 Request를 받고 싶은 상황이라면

 

    @PostMapping(value = "/post/hello", consumes = MediaType.APPLICATION_JSON_VALUE)
    public String PostMappingTestForJson (@RequestBody TestClass testClass) {

        return "Post Mapping : " + testClass.PrettyPrint();
    }

    @PostMapping(value = "/post/hello", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public String PostMappingTestForForm (TestClass testClass) {

        return "Post Mapping : " + testClass.PrettyPrint();
    }

 

다음과 같이 같은 url과 로직을 공유하되, MediaType이 다른 두 개의 함수를 사용하여 Client의 요청에 맞게 처리해주시면 됩니다. 이렇게 처리하면 코드도 더 명시적으로 읽기 쉽게 되고 뭔가 제 스타일에 맞는 것 같습니다.

 

*저의 글에 대한 피드백이나 지적은 언제나 환영합니다.