Part3 - CH4. 스프링 부트 시작하기(1) Spring Boot, GET/POST API
SpringBoot
- Spring Boot는 프로덕션 수준의 스프링 기반 어플리케이션을 쉽고 빠르게 만들 수 있다.
- Spring Boot는 Spring구성이 거의 필요하지 않다.
- Spring Boot는 java -jar로 실행하는 Java 어플리케이션을 만들 수 있다.
주요 목표
- Spring 개발에 대해 빠르고 광범위하게 적용할 수 있는 환경
- 기본값 설정이 있지만 바꿀 수 있다.
- 대규모 프로젝트에 공통적인 비 기능 제공(보안, 모니터링 등)
-
XML 구성 요구사항이 전혀 없다.
- Build Tool : Maven, Gradle
- Servlet Container : Tomcat, Jetty, Undertow, Netty, …
Hello Spring Boot REST Api
REST Api를 처리하는 컨트롤러를 만드려면 다음과 같이 한다.
@RestController //스프링부트에게 이 클래스는 REST를 처리하는 곳이라는 것을 알려준다.
@RequestMapping("/api") //어떤 URI를 매핑할지 알려준다.
public class ApiController {
@GetMapping("/hello") //GET방식으로 hello라는 url로 요청이 들어올시 처리하는 곳.
public String hello(){
return "hello spring boot";
}
}
GET API
위의 Hello Spring Boot에서 사용한 @GetMapping 어노테이션으로 들어가보면 다음과 같다.
/**
* Annotation for mapping HTTP {@code GET} requests onto specific handler
* methods.
*
* <p>Specifically, {@code @GetMapping} is a <em>composed annotation</em> that
* acts as a shortcut for {@code @RequestMapping(method = RequestMethod.GET)}.
*
* @author Sam Brannen
* @since 4.3
* @see PostMapping
* @see PutMapping
* @see DeleteMapping
* @see PatchMapping
* @see RequestMapping
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class)
String name() default "";
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] path() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] params() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] headers() default {};
/**
* Alias for {@link RequestMapping#consumes}.
* @since 4.3.5
*/
@AliasFor(annotation = RequestMapping.class)
String[] consumes() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] produces() default {};
}
위에서 보이듯이, 여러가지 방법으로 GET요청방식을 지정할 수 있는데, 아무런 지정없이 문자열을 넣어주면 value로 설정되는것이 기본값이다.
보통 다음과 같이 GetMapping을 사용한다.
@RestController
@RequestMapping("/api/get")
public class GetApiController {
@GetMapping(path = "/hello") // http://localhost:8080/api/get/hello
public String hello(){
return "get hello";
}
//에전방식, 위와 동일한 동작
// @RequestMapping("/hi") //모든 메소드가 동작한다. (GET, POST, DELTE, PUT ...)
@RequestMapping(path="/hi", method = RequestMethod.GET) // /api/get/hi
public String hi(){
return "get hi";
}
}
Path Variable
요청하는 URL 패스를 변수로 사용하고 싶으면 다음과 같이한다.
// http://localhost:8080/api/get/path-variable/{name}
@GetMapping("/path-variable/{name}") //주소에는 대문자 대신 -로 구분하는것이 좋다.
public String pathVariable(@PathVariable String name){ //pathVariable을 받도록한다.
System.out.println("PathVariable : " + name);
return name;
}
위는 PathVariable로 받으려고 하는 중괄호 안의 변수명과 파라미터로 받는 변수명이 일치하지만, 일치하지 않을 경우는 다음과 명시해준다.
public String pathVariable(@PathVariable(name="id") String pathName){ //pathVariable을 받도록한다.
Query Parameter
요청하는 URL뒤에 ~~~?variable1=somethig1&variable2=somthing2
와 같이 &key=value
형식
으로 어떤 정보를 전달하는데 이것이 쿼리 파라미터이다. 이것을 처리하려면 다음과 같이 한다.
//목표 : http://localhost:8080/api/get/query-param?user=dboo&email=~~.com&age=30
@GetMapping(path = "query-param")
public String queryParam(@RequestParam Map<String, String> queryParam){ //RequestParam으로 쿼리파라미터를 받는다.
StringBuilder sb = new StringBuilder();
queryParam.entrySet().forEach(
entry ->{
System.out.println(entry.getKey());
System.out.println(entry.getValue());
System.out.println("\n");
sb.append(entry.getKey() + " = " + entry.getValue() + "\n");
});
return sb.toString();
}
그런데, 이 경우 어떤 키값으로 파라미터가 들어올 것인지 전혀 예측하거나 제한할 수 없기 때문에 이를 지정하 는 방법을 알아보자.
@GetMapping(path="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;
}
그런데, 이 경우에 쿼리파라미터의 갯수가 늘어나면 늘어날수록 지정해야하는 파라미터도 많아지기 때문에 여기에 DTO를 연계할 수 있도록 스프링이 제공을 한다. 현업에서 가장 많이 사용하는 방식이기도 하다.
받을 쿼리파라미터를 DTO로 만들어보자.
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 +
'}';
}
}
위 DTO를 활용해서 쿼리 파라미터를 받으려면 다음과 같이 한다.
queryParam02와 같이 어노테이션은 붙이지 않는다. 스프링부트가 알아서 매칭시켜준다.
@GetMapping(path="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();
}
POST API
- 보통 쿼리 파라미터를 사용하지 않고, 데이터 바디에 정보를 실어서 전달한다.
- 데이터 형테는 XML이나 JSON으로 전달하고 보통 JSON을 사용한다.
짤막하게 JSON데이터 형식에 대해 알아보자.
JSON
“key” : “value” 형식으로 나열하며, 키-값쌍들은 쉼표(,)로 구분한다.
사용할 수 있는 변수는 다음과 같다.
- string : value
- number : value
- boolean : true, false
- object : {}
- array : []
snake case : “phone_number”
camel case : “phoneNumber”
{
"string" : "value",
"number" : 10,
"boolean" : false,
"object" : {
"var1" : "variable1",
"var2" : 2,
},
"array" : [
{
"array1" : "1"
},
{
"array2" : "2"
}, ...
]
}
POST Controller
기본적인 PostMapping은 다음과 같이한다.
@PostMapping("/post")
public void post(@RequestBody Map<String, Object> requestData){
requestData.forEach((key, value)->{
System.out.println("key : " + key);
System.out.println("value : " + value);
});
}
GET방식과는 다르게 @RequestBody를 붙여준다.
이번에도 DTO를 만들어서 데이터바디를 객체로 받아보자.
public class PostRequestDto {
private String account;
private String email;
private String address;
private String password;
//getter, setter, toString 생략
}
그러면 PostController에서
@PostMapping("/post")
public void post(@RequestBody PostRequestDto postRequestDto){
System.out.println(postRequestDto.toString());
}
와 같이 줄일 수 있다. GET에서와 다른 점은 DTO를 쓰더라도 @RequestBody를 생략하지 않는다.
그런데, DTO에서 받아야 하는 변수명이 snake-case로 오게 될 시, 자바에서의 camel-case와 어떻게 매핑 할것인지 문제가 있다.
public class PostRequestDto {
private String account;
private String email;
private String address;
private String password;
private String phoneNumber;
//getter, setter, toString 생략
}
camel-case형식인 변수명 phoneNumber가 추가되었다.
기본적으로 JSON으로 넘어온 변수명들이 ObjectMapper를 통해서 매핑이 되는데, snake-case인 변수명을 따로 매핑정보를 줘야 한다.
- @JsonProperty 어노테이션을 이용한 변수명매핑
public class PostRequestDto {
private String account;
private String email;
private String address;
private String password;
@JsonProperty("phone_number")
private String phoneNumber;
//getter, setter, toString 생략
}
위 방법은 굳이 snake-case가 아니더라도, JSON 변수명을 매핑할때 사용할 수 있다.
이 방법 말고도 @JsonNaming 이라는 어노테이션으로도 해결할 수 있는데 이는 나중에 다시 알아보자.