AJAX를 통한 클라이언트와 서버 간 비동기 통신은 현대 웹 애플리케이션에서 필수적인 요소입니다. Spring MVC에서는 이러한 AJAX 요청을 효과적으로 처리할 수 있도록 @RequestBody와 @ResponseBody 어노테이션을 제공합니다.
@RequestBody 사용법
기본적으로 HTML 폼에서 전송되는 데이터는 application/x-www-form-urlencoded 형식으로 처리되며, 일반적인 요청 파라미터로 바인딩됩니다. 하지만 AJAX를 통해 JSON 형식의 데이터를 전달할 경우, 이는 요청 본문(request body)에 포함되며 application/json과 같은 미디어 타입으로 전송됩니다. 이런 경우 스프링은 자동 파라미터 바인딩이 불가능하므로 @RequestBody 어노테이션을 사용해 요청 몸체 전체를 직접 읽어야 합니다.
@ResponseBody의 역할
일반적인 @RequestMapping 메서드는 반환값을 뷰 이름으로 해석하여 JSP나 Thymeleaf 같은 템플릿으로 이동시킵니다. 반면 @ResponseBody를 선언하면 해당 반환값이 뷰가 아니라 HTTP 응답 본문(response body)에 직접 쓰여집니다. 특히 JSON 형태의 데이터를 반환할 때 매우 유용하며, 클라이언트에서 XMLHttpRequest 또는 fetch로 응답을 받을 수 있습니다.
실제 예제: JSON 기반 AJAX 요청 처리
다음은 클라이언트에서 JSON 데이터를 POST 방식으로 전송하고, 이를 Spring MVC 컨트롤러에서 처리하는 예제입니다.
JSP 페이지 (클라이언트 측)
<html>
<body>
<button onclick="sendAjaxRequest()">AJAX 요청 보내기</button>
<script>
function sendAjaxRequest() {
const xhr = new XMLHttpRequest();
const data = {
username: 'user123',
phone: '010-1234-5678',
password: 'pass987'
};
xhr.open('POST', '/SpringMVCDemo/ajax/process', true);
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
console.log('결과:', response.message);
console.log('상태:', response.status);
}
};
xhr.send(JSON.stringify(data));
}
</script>
</body>
</html>
서버 측 컨트롤러 (Java)
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/ajax")
public class AjaxProcessingController {
private final ObjectMapper objectMapper = new ObjectMapper();
@ResponseBody
@PostMapping(value = "/process", consumes = "application/json", produces = "application/json")
public String handleJsonRequest(@RequestBody String rawJson) throws Exception {
// JSON 문자열을 객체로 변환
UserData userData = objectMapper.readValue(rawJson, UserData.class);
System.out.println("수신된 사용자명: " + userData.getUsername());
System.out.println("전화번호: " + userData.getPhone());
// 응답 생성
ResponseData response = new ResponseData();
response.setStatus("success");
response.setMessage("데이터 처리 완료: " + userData.getUsername());
return objectMapper.writeValueAsString(response);
}
// 데이터 수신용 DTO 클래스
static class UserData {
private String username;
private String phone;
private String password;
// 기본 getter 및 setter
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
// 응답용 DTO 클래스
static class ResponseData {
private String status;
private String message;
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
}
위 예제에서 @RequestBody는 클라이언트가 전송한 JSON 문자열 전체를 문자열로 수신하며, Jackson 라이브러리를 사용해 이를 자바 객체로 변환합니다. 반대로 응답 시에는 @ResponseBody 덕분에 문자열로 변환된 JSON이 바로 클라이언트에게 전달됩니다.
또한, HttpServletResponse 객체를 직접 사용해 응답을 구성할 수도 있지만, @ResponseBody를 사용하는 것이 코드가 더 깔끔하고 유지보수가 쉬워집니다.