GET과 POST의 차이
의미 | CRUD | * 멱등성 | 안정성 | * Path Variable |
* Query Parameter |
Databody | |
GET | 리소스 취득 | R | O | O | O | O | X |
POST | 리소스 생성, 추가 | C | X | X | O | △ | O |
* 멱등성 : 연산을 여러번 적용하더라도 결과가 달라지지 않음
* Query Parameter : 데이터를 처리하기위해 넘겨주는 여러가지 매개변수이며, 항상 ?가 붙음
[예시 - 아이디가 123인 user의 데이터를 가져온다]
/users?id=123
* Path Variable : 데이터를 처리하기위해 URL / URI 경로에 변수를 넣어줌
[예시 - 아이디가 123인 사용자를 가져온다]
/users/123
위 표를 해설하자면 아래와 같다.
GET
- 요청 데이터를 읽어들이므로 R(Read)에 해당
- 요청 결과는 정해져있으므로, 멱등성은 O
- 요청 결과는 정해져있으므로, 안정성은 O
- Path Variable을 설정하여 데이터를 요청할 수 있음
- Query Parameter을 설정하여 데이터를 요청할 수 있음
- Query Parameter을 설정하여 데이터를 충분히 요청할 수 있으므로, Databody는 사용 X
-> 엄밀히 말하자면 조회 조건이 길어질 경우 body를 사용할 수 있으나, 이는 시스템 요구사항에 따라 달라지며 일반적으로는 사용하지 않는 것을 권장한다. (출처 : https://brunch.co.kr/@kd4/158)
POST
- 데이터를 생성 및 추가하므로 C(Create)에 해당
- 데이터를 새로 생성하므로 멱등성은 X
- 데이터를 새로 생성하므로 안정성은 X
- Path Variable을 설정하여 데이터를 생성할 수 있음
- Query Parameter을 설정하여 데이터를 생성할 수 있으나, 일반적으로 Databody를 많이 활용함
-> 일부 웹 서버에는 URI에 길이 제한이 있으므로, 매우 많은 인자가 존재할 경우 Query Parameter는 부적절하기 때문이다.
설명하기에 앞서
아래에서 언급될 파일들의 경로는 다음과 같다.
설명을 돕기위해 Get과 Post Api Controller을 따로 분류했으나, 현업에서는 하나하나 분류하지 않는다.
Query Paramter를 활용한 GET
1. 어떤 데이터가 쿼리 파라미터로 들어올지 모르는 경우
// controller 코드 내부
public String queryParm(@RequestParam Map<String, String> queryParam){
StringBuilder sb = new StringBuilder();
queryParam.forEach((key, value) -> {
System.out.println(key);
System.out.println(value);
System.out.println("\n");
sb.append(key + " = " + value);
});
return sb.toString();
}
URI = http://localhost:8080/api/get/query-param?user=dm&email=dm@gmail.com&age=26
[Console 결과]
[Postman 결과]
* Postman = API Platform
[단점]
지정된 데이터만을 넘겨받아야할 경우(ex : user와 email만 필요),
불필요한 데이터까지 넘겨받아 데이터를 주고 받는데 문제가 발생할 수 있다.
2. 매개변수가 소수이며 명확한 경우
// controller 코드 내부
@GetMapping("query-param02")
public String queryParam02(
@RequestParam String name,
@RequestParam String email,
@RequestParam int age
){
System.out.println(name);
System.out.println(email);
System.out.println(age);
return name+" "+email+" "+age;
}
URI = http://localhost:8080/api/get/query-param02?name=dm&email=dm@gmail.com&age=26
[Console 결과]
[Postman 결과]
[단점]
매개변수가 추가될때마다 일일이 코드를 수정해야한다.
또한 값이 잘못 들어갈 경우, 400에러가 발생한다.
3. 현업에서 가장 많이 쓰이는 방법
// controller 코드 내부
@GetMapping("query-param03")
public String queryParam03(UserRequest userRequest){
System.out.println(userRequest.getName());
System.out.println(userRequest.getEmail());
System.out.println(userRequest.getAge());
return userRequest.toString();
}
// DTO 코드 내부의 UserRequest
package com.example.hello.dto;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
public class UserRequest {
private String name;
private String email;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "UserRequest{" +
"name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}
URI = http://localhost:8080/api/get/query-param03?name=dm&email=dm@gmail.com&age=26
[Console 결과]
[Postman 결과]
객체를 만들어서 쿼리 파라미터가 바로 매핑되게 하는 이 방법이 현업에서 많이 쓰인다.
이러한 방식은 DTO를 활용한 것이며, 디테일한 정보는 해당 링크를 참조하면 된다.
값이 잘못들어가거나 안들어가도 기본 Null로 들어가서 400에러가 발생하는 일을 방지해준다.
Databody를 활용한 POST
POST또한 아래와 같은 방법으로 데이터를 생성할 수 있다.
@PostMapping("/post")
public void post(@RequestBody Map<String, Object> requestData){
requestData.forEach((key, value) -> {
System.out.println("key : " + key);
System.out.println("value : " + value);
});
}
하지만 이미 해당코드에 대한 단점을 위에서 언급했으므로, 현업에서 가장 많이 쓰는 방식을 언급하겠다.
// controller 코드 내부
@PostMapping("/post2")
public void post2(@RequestBody PostRequestDto requestData){
System.out.println(requestData);
}
// DTO 코드 내부의 PostRequestDto
package com.example.hello.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
public class PostRequestDto {
private String account;
private String email;
private String address;
private String password;
@JsonProperty("phone_number") // 일일이 JsonPropert를 달아줘야하는 단점이 있지만, 카멜케이스 -> 해당 케이스로 변환해서 인식하는 장점이 있다
private String phoneNumber;
@JsonProperty("OTP") // 이외에 대문사 -> 소문자로 변환하는 등, 상황에따라 적절하게 변환하여 인식한다
private String OTP;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getOTP() {
return OTP;
}
public void setOTP(String OTP) {
this.OTP = OTP;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "PostRequestDto{" +
"account='" + account + '\'' +
", email='" + email + '\'' +
", address='" + address + '\'' +
", password='" + password + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
", OTP='" + OTP + '\'' +
'}';
}
}
[Console 결과]
현업에서 쓰는 방법에서 만약 잘못된 변수가 들어갔다면?
예를 들어 자바 코드 내부에서는 phoneNumber로 변수를 선언했다고 가정하자. 이는 Camel Casing으로 구현되었다.
하지만 POST 과정에서 Databody에는 phone_number로 key를 선언했다고 가정하자. 이는 Snake Casing으로 구현되었다.
이럴 경우 데이터를 읽어들이지 못하는 과정을 방지하기 위해, 다음과 같이 JsonProperty 어노테이션을 사용하면 된다.
public class PostRequestDto {
private String account;
private String email;
private String address;
private String password;
@JsonProperty("phone_number") // 일일이 JsonPropert를 달아줘야하는 단점이 있지만, 카멜케이스 -> 해당 케이스로 변환해서 인식하는 장점이 있다
private String phoneNumber;
@JsonProperty("OTP") // 이외에 대문사 -> 소문자로 변환하는 등, 상황에따라 적절하게 변환하여 인식한다
private String OTP;
변수를 선언할때마다 @JsonProperty 어노테이션을 사용해야하는 불편함은 있지만, 만약의 상황을 방지할 수 있으므로 현업에서 자주 쓰인다. 해당 어노테이션은 케이싱 변환 외에도 대문자와 소문자의 변환도 자동으로 이루어준다.
'👩🏻💻 Programming > SpringBoot' 카테고리의 다른 글
Bean vs Component (0) | 2021.08.10 |
---|---|
AOP (Aspect Oriented Programming) (0) | 2021.08.10 |
IoC(제어의 역전)와 DI(의존성 주입) (0) | 2021.08.08 |
SpringBoot의 Object Mapper (0) | 2021.08.05 |
POJO(Plain Old Java Object)와 POJO Framework (0) | 2021.07.30 |