티스토리 뷰

728x90

스프링에서 오류를 마주했다 처음 한 두 번 마주한 오류는 어째 어째 해결했지만, 반복되는 일이 많았다.

뭐가 부족한지 고민을 한 결과 각 어노테이션 별 동작 유무 및 성공결과에 대한 정확한 개념 정리가 부족하다고 느껴 정리하게 되었다.

 

 

@RestController와 @Controller


먼저 @RestController와 @Controller를 비교하기 전에 Controller가 뭔지 가볍게 정리한다

 

뷰를 반환하는 Controller

전통적인 Spring MVC의 컨트롤러인 @Controller는 주로 View를 반환하기 위해 사용한다.

Spring MVC Container는 Client의 요청으로부터 View페이지를 반환한다.

 

Data를 반환하는 Controller

Controller의 역할은 View 페이지만 반환하는 것이 아닌 Data도 반환할 때가 있다. 

예를 들어 API요청이 온다면 view페이지를 뿌려주는 게 아니라 해당 DATA만 전달한다는 뜻이다.

 

이때 컨트롤러에서는 데이터를 반환하기 위해 @ResponseBody 어노테이션을 활용해주어야 한다. 

이를 통해 Controller도 Json 형태로 데이터를 반환할 수 있다.

 

 

 

@Controller와 @RestController 비교


위에서 Controller의 역할을 정의했다. Controller는 View페이지를 보여주거나 해당 데이터를 전달한다.

Data를 전달한다면 @RestController 혹은 @Controller + @ResponseBody 조합의 어노테이션을 활용해야 한다.

view페이지를 전달한다면 @Controller를 사용하면 된다.

 

[상황에 따라 아래처럼 선언하면 된다.]

@Controller
public class RequestHeaderController {

    @ResponseBody
    @GetMapping("/helloworld")
    public String helloWorld() {
        return "helloworld";
    }

 

@RestController
public class RequestHeaderController {
    @GetMapping("/helloworld")
    public String helloWorld() {
        return "helloworld";
    }

 

@RestController에 @ResponseBody어노테이션이 선언되어있어 

view페이지가 아닌 데이터를 전달한다고 할 때 @RestController를 선언해주면 된다. 

RestController와 Controller의 어노테이션을 집중해서 보자

 

 

2. @RequestParam과 @RequestBody


@RequestParam과 @RequestBody는 Controller로 넘어온 데이터를 받을 때 사용한다. 

아래  @RequestParam과 @RequestBody차이를 살펴보자  

@Controller
public class UserController {

	@PostMapping("/receive")
	public String user(@RequestParam String name) {
		System.out.println("통신 성공");
		System.out.println(">>> " + name);
		return "index";
	}
}

@Controller
public class UserController {

	@PostMapping("/receive")
	public String user(@RequestBody String name) {
		System.out.println("통신 성공");
		System.out.println(">>> " + name);
		return "index";
	}
}

[Controller로 데이터 전달]

String name = devJohn

int age = 99

 

데이터를 전송하여 컨트롤러에서 @RequestBody를 이용하여 데이터를 받아보았다.

 

[RequestBody]

@Controller
public class UserController {

	@PostMapping("/receive")
	public String user(@RequestBody String name) {
		System.out.println("통신 성공");
		System.out.println(">>> " + name);
		return "index";
	}
}

//통신 성공
//>>> name=devJohn&age=99

우리가 입력한 'devJohn'이라는 이름과 '99'이라는 나이가 잘 전달이 되었지만 단지 'name=devJohn&age=99'이라는String으로 전달되어 전달된 데이터를 사용하기에는 불편함이 있다.

RequestBody는 주로 Json 통신을 할 때  key:value 형식으로 사용한다.

이번엔 @RequestParam 으로 받아보자.

 

[@RequestParam]

@Controller
public class UserController {

	@PostMapping("/receive")
	public String User(@RequestParam String name) {
		System.out.println("통신 성공");
		System.out.println(">>> " + name);
		return "result";
	}
}

//통신 성공
//>>> devJohn

@RequestBody로 데이터를 받을 때는 메서드의 변수명이 상관없었지만,

@RequestParam으로 데이터를 받을 때는 데이터를 저장하는 이름으로 변수명을 설정해주어야 한다.

devJohn이라는name의 데이터가 잘 전달되었고, name이라는 변수에 할당이 되어 사용하기에도 용이하다.

 

 

[@RequestParam의 default속성과 required속성]

ajax혹은 view에서 데이터를 전달할 때 Controller의 @RequestParam이 해당 값을 받는다고 선언이 되어 있지 않을 경우 오류가 발생할 수 있다. 이때 required=false 속성을 통해 해당 값이 없어도 오류가 나지 않도록 선언할 수 있다.

값을 매번 공통 초기값을 넣거나 기본값이 필요한 경우에는  속성 중에는defaultValue라는 기본값 속성이 있다 

@Controller
public class UserController {

	@PostMapping("/receive")
	public String User(@RequestParam(required = false) String name,@RequestParam(defaultValue = "99") int age) {
		return "result";
	}
}

이름은 입력받은 값을 받고, 나이는 입력하지 않을 경우 99를 age에 바인딩한다. 

[Map을 이용해서 @RequestParam 한 번에 받기 ]

@Controller
public class UserController {

	@PostMapping("/receive")
	public String User(@RequestParam Map<String, Object> paramMap ) {
    	log.info("username={}, age={}", paramMap.get("name"), paramMap.get("age"));
		return "result";
	}
}

자료형 Map을 통해 paramMap에 값을 담고 paramMap.get을 통해 해당 이름과 동일 값을 꺼내올 수 있다.

 

[@ModelAttribute]

위에서 @RequestParam을 이용해 요청 파라미터를 하나하나 받아줬다. 어노테이션을 이용해 최대한 편하게 받아줬지만, 요청 파라미터가 하나의 객체가 돼야 하는 경우 각각 파라미터 요청을 조회해서 객체에 값을 넣어서 생성해주는 작업을 해야 하기 때문에 번거롭다

번거로움을 해결하기 위해 나온 @ModelAttribute 어노테이션은 Hello 객체의 @Setter를 통해 Controller에서 넘어온 값을
Hello 객체에 저장한다.
 

컨트롤러로 넘어온 데이터를 세팅하기 위한 Hello 객체를 선언한다.

//편의상 @Data선언
@Data
public class Hello {
    private String name;
    private int age;
}

@ModelAttribute 애노테이션을 이용한 요청 파라미터 객체 바인딩

@Controller
public class UserController {

	@PostMapping("/receive")
	public String User(@ModelAttribute Hello hello) {
		hello.getName(), hello.getAge());
		return "result";
	}
}

결과를 보면 요청 시 Hello객체에 값이 담겨서 사용할 수 있는 걸 확인할 수 있다.

 

@ModelAttribute는 다음과 같이 동작된다고 이해하면된다.

1. Hello객체 생성을 미리 한 뒤 

2. @ModelAttribute 선언된 뒤 Hello hello객체를 찾는다.

3. hello객체에 선언된 setter를 호출해서 파라미터 값을 바인딩한다.

 

@RequestParam 한 번에 받기와 @ModelAttribute, @RequestBody 객체 바인딩 혼동 x

@RequestParam

목차 중 [Map을 이용해서 @RequestParam 한 번에 받기]는 @RequestParam Map <String, object> paramMap 선언으로

paramMap에 전달된 값을 get으로 꺼내오는 것이고, 

 

@ModelAttribute는 파라미터 값으로 DTO객체에 바인딩을 하는 방식이기 때문에 바인딩하려는 DTO객체에 Setter메서드가 반드시 있어야 하고,

 

@RequestBody는 요청 본문의 body에 json이나 xml값으로 요청을 하여 HttpMessageConveter를 반드시 거쳐 DTO객체에 맞는 타입으로 바꿔서 바인딩을 시켜준다. 

 

 

 

혹시 글이 잘못되었거나 부족한 부분이 있다면 댓글로 피드백 부탁드리겠습니다! 감사합니다.

 

728x90
댓글
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday