Categories
웹개발/Spring
@Controller와 @RestController 차이 JSP와 JSON으로 정리
두 어노테이션이 헷갈리는 지점
Spring 컨트롤러를 작성하다 보면 @Controller와 @RestController 중 무엇을 붙여야 할지 망설이게 된다. 둘 다 요청을 받는 컨트롤러인데, 반환값을 다루는 방식이 다르다. @Controller @RestController 차이를 한 줄로 요약하면 이렇다. @Controller는 기본적으로 화면(뷰, 보통 JSP)을 돌려주고, @RestController는 데이터(주로 JSON)를 돌려준다.
그래서 증상도 갈린다. @RestController를 썼는데 JSP가 안 뜨고 "member/list" 같은 문자열이 화면에 그대로 찍히거나, @Controller에서 JSON을 기대했는데 뷰를 찾다가 404가 나는 식이다. 원인을 알려면 두 어노테이션이 반환값을 어떻게 해석하는지 보면 된다.
차이 비교표
| 구분 | @Controller | @RestController |
|---|---|---|
| 기본 반환 의미 | 뷰 이름(화면) | 응답 본문 데이터 |
| 반환값 "member/list" | 뷰 리졸버가 JSP를 찾음 | 문자열 그 자체를 응답 |
| @ResponseBody | 메서드마다 직접 붙여야 데이터 응답 | 클래스 전체에 자동 적용 |
| 주 용도 | JSP 등 화면 렌더링 | REST API, JSON 응답 |
| 객체 반환 시 | 모델/뷰로 해석 | JSON으로 변환 |
| 대표 상황 | 전통 웹(서버 렌더링) | Ajax, 모바일 API, 프론트 분리 |
@Controller: 화면(JSP)을 돌려준다
1. 문자열 반환은 뷰 이름으로 해석된다
@Controller에서 메서드가 문자열을 반환하면, 그 문자열은 뷰 이름으로 해석된다. 뷰 리졸버(ViewResolver)가 접두어·접미어를 붙여 실제 JSP 경로를 찾는다.
@Controller
public class MemberController {
@GetMapping("/member/list")
public String list(Model model) {
model.addAttribute("members", memberService.findAll());
// "member/list" → /WEB-INF/views/member/list.jsp 로 해석 (설정에 따라)
return "member/list";
}
}
여기서 return "member/list"는 "member/list.jsp 화면을 보여줘라"는 뜻이다. 뷰 리졸버 설정(prefix/suffix)에 따라 실제 경로가 결정된다. 모델에 담은 데이터는 JSP에서 꺼내 쓴다. 전통적인 서버 사이드 렌더링 방식이고, 공공 SI의 화면 개발에서 가장 많이 보는 형태다.
2. @Controller에서 JSON을 내보내려면
같은 @Controller에서도 특정 메서드만 데이터를 응답하게 할 수 있다. 메서드에 @ResponseBody를 붙이면 된다.
@Controller
public class MemberController {
@GetMapping("/api/members")
@ResponseBody // 이 메서드는 뷰가 아니라 데이터로 응답
public List<MemberVO> apiList() {
return memberService.findAll(); // JSON으로 변환되어 응답
}
}
@ResponseBody가 붙으면 반환값을 뷰 이름이 아니라 응답 본문으로 처리한다. 객체나 리스트면 메시지 컨버터가 JSON으로 바꿔 내보낸다. 즉 @Controller도 @ResponseBody만 붙이면 데이터 응답이 가능하다. 이 점이 다음 이야기로 이어진다.
@RestController: 데이터(JSON)를 돌려준다
1. @Controller + @ResponseBody의 합본
@RestController는 사실 @Controller에 @ResponseBody가 클래스 단위로 합쳐진 것이다. 그래서 클래스 안 모든 메서드가 기본적으로 데이터 응답이 된다. 메서드마다 @ResponseBody를 붙일 필요가 없다.
@RestController
public class MemberApiController {
@GetMapping("/api/members")
public List<MemberVO> list() {
// 별도 어노테이션 없이 JSON으로 응답
return memberService.findAll();
}
@GetMapping("/api/members/{id}")
public MemberVO detail(@PathVariable String id) {
return memberService.findById(id); // 객체 → JSON
}
}
객체를 반환하면 JSON으로 변환되고, 문자열을 반환하면 그 문자열 자체가 응답 본문이 된다. REST API, Ajax 응답, 모바일 백엔드처럼 화면이 아니라 데이터를 주고받는 경우에 적합하다.
2. @RestController로 화면을 돌려주려 하면
여기서 흔한 실수가 나온다. @RestController에서 return "member/list"를 하면 JSP를 찾지 않고 "member/list"라는 문자열을 그대로 응답한다. 화면에 경로 문자열만 덜렁 찍히는 이유가 이것이다. 화면을 렌더링하려면 @Controller를 써야 한다. 둘을 용도에 맞게 나누는 게 핵심이다.
그래서 언제 무엇을 쓰나
- JSP 같은 화면을 서버에서 렌더링해 보여 준다 → @Controller
- JSON 데이터를 주고받는 REST API, Ajax 응답 → @RestController
- 화면용 컨트롤러인데 일부 메서드만 데이터 응답이 필요하다 → @Controller + 해당 메서드에 @ResponseBody
- 프로젝트가 화면과 API를 명확히 분리한다 → 컨트롤러 클래스 자체를 화면용/API용으로 나눠 관리
공공 SI에서는 JSP 기반 화면이 여전히 많아 @Controller가 기본이고, Ajax로 부분 데이터를 받는 곳에 @ResponseBody나 별도 @RestController를 섞어 쓰는 구조를 자주 본다. 프론트엔드를 분리한 신규 시스템일수록 @RestController 비중이 높다.
JSON이 안 나가거나 화면이 안 뜰 때
증상별로 원인을 좁힐 수 있다. @RestController인데 한글 JSON이 깨진다면 메시지 컨버터의 인코딩이나 produces 설정을 본다. 객체가 JSON으로 안 바뀌고 오류가 난다면 JSON 변환 라이브러리(Jackson 등)가 의존성에 있는지 확인한다. @Controller인데 화면을 못 찾고 404가 난다면 뷰 리졸버의 prefix/suffix와 실제 JSP 경로가 맞는지 본다. 반대로 @RestController에서 화면 경로 문자열이 그대로 찍힌다면, 화면용인데 어노테이션을 잘못 골랐다는 신호다.
// JSON 응답에 콘텐츠 타입·인코딩을 명시하고 싶을 때
@GetMapping(value = "/api/members", produces = "application/json; charset=UTF-8")
@ResponseBody
public List<MemberVO> list() {
return memberService.findAll();
}
인코딩이나 메시지 컨버터 동작은 Spring 버전과 설정에 따라 다를 수 있으니, 환경에 맞춰 확인한다.
초급 개발자가 헷갈리는 부분
- @RestController를 화면용으로 쓰고 JSP가 안 뜬다고 고민하는 경우. 화면은 @Controller다.
- @RestController가 @Controller와 완전히 다른 것이라 여기는 경우. 사실은 @Controller + @ResponseBody다.
- @Controller 메서드에서 객체를 반환하면 자동으로 JSON이 될 거라 기대하는 경우. @ResponseBody가 없으면 뷰 이름으로 해석된다.
- 객체가 JSON으로 안 바뀌는데 원인을 컨트롤러에서만 찾는 경우. JSON 변환 라이브러리 의존성도 확인한다.
FAQ
Q1. @RestController와 @Controller는 뭐가 다른가요?
@Controller는 기본적으로 뷰(화면) 이름을 반환하고, @RestController는 데이터를 응답한다. @RestController는 @Controller에 @ResponseBody가 더해진 것이다.
Q2. @RestController를 썼는데 JSP가 안 뜹니다.
@RestController는 반환 문자열을 뷰가 아니라 응답 본문으로 처리한다. 화면을 렌더링하려면 @Controller를 써야 한다.
Q3. @Controller에서 JSON을 내보낼 수 있나요?
가능하다. 해당 메서드에 @ResponseBody를 붙이면 반환 객체가 JSON으로 변환되어 응답된다.
Q4. @ResponseBody는 정확히 무슨 역할인가요?
메서드 반환값을 뷰 이름이 아니라 HTTP 응답 본문으로 보내라는 표시다. 객체면 메시지 컨버터가 JSON 등으로 변환한다.
Q5. 객체를 반환했는데 JSON이 안 나옵니다.
@ResponseBody나 @RestController가 빠졌거나, JSON 변환 라이브러리(Jackson 등)가 의존성에 없을 수 있다. 둘 다 확인한다.
정리
@Controller @RestController 차이는 "화면을 돌려주느냐, 데이터를 돌려주느냐"로 갈린다. @Controller는 반환 문자열을 뷰 이름으로 해석해 JSP 같은 화면을 렌더링하고, @RestController는 반환값을 응답 본문으로 처리해 JSON 데이터를 내보낸다. 핵심은 @RestController가 곧 @Controller + @ResponseBody라는 점이다. 그래서 @Controller에서도 @ResponseBody만 붙이면 데이터 응답이 가능하다. 화면이 안 뜨거나 JSON이 안 나간다면 먼저 어노테이션 선택이 용도에 맞는지부터 확인하고, 뷰 리졸버 설정이나 JSON 변환 라이브러리를 점검하면 대부분 원인을 찾을 수 있다.
'웹개발 > Spring' 카테고리의 다른 글
| Spring *.do URL 패턴 정리, 공공 프로젝트는 왜 .do로 끝날까 (0) | 2026.07.02 |
|---|---|
| @RequestMapping @GetMapping 차이 (0) | 2026.07.01 |
| Spring IoC와 DI 개념 SI 신입을 위한 10분 정리 (0) | 2026.06.30 |
| Spring Framework vs Spring Boot 차이와 공공 SI에서의 선택 (0) | 2026.06.30 |
| Spring MVC resources 404 에러 원인과 해결 방법 정리 (0) | 2026.06.24 |