
@RequestMapping과 파생 어노테이션
@RequestMapping(value="경로")
: 기본적으로 GET+POST 둘 다 처리 (Class, Method용)
두번째 인자로 GET으로만 받을건지, POST로만 받을건지 지정 가능 (생략가능)
=>@RequestMapping(value="경로", method=RequestGET)
=>@RequestMapping(value="경로", method=RequestPOST)
=>@RequestMapping(value="경로", method={RequestGET, RequestGet})@GetMapping("경로")
: GET 요청만 처리 (Method용)@PostMapping("경로")
: POST 요청만 처리 (Method용)
선생님 TIP
Controller는 들어왔는데, 그 안에서 view를 못찾는 경우와 Controller 자체를 찾지 못한 경우를 잘 구분할 줄 알아야함. (둘 다 404오류 발생 함)
=> Controller 자체를 못찾았을 때
=> Controller는 찾았으나 그 안에서 view페이지를 찾지 못했을 때
spring에서 주로 사용되는 어노테이션
@Controller@RestController@RequestMapping@RequestMapping@GetMapping / @PostMapping@ResponseBody@RequestParam : Request에 있는 파라미터 받아서 처리하기 위해 사용 ( => spring의 파라미터 자동 수집)@PathVariable : url 경로의 일부분을 변수로 처리하기 위해 사용@ModelAttribute : spring에서는 request.setAttribute이 방식이 아니라 model에 포함해서 파라미터를 전달하는 식으로 사용함. 그 때 사용되는 어노테이션 @SessionAttribute@Valid@RequestBody : Rest방식에서 문자열이나, JSON 데이터 그대로 전송할 때 사용Spring의 parameter 자동수집
Spring은 사용자의 request에서 넘어온 parameter를 자동으로 수집함(별다른 처리 없이도 알아서)
Controller 내 메서드(parameter에 대한 별다른 처리 없이 그냥 찍음)
@GetMapping("/view")
public void view(int idx, String name, int age) {
log.info("---------------------");
log.info("BbsController => view()");
log.info("idx : " + idx);
log.info("name : " + name);
log.info("age : " + age);
log.info("---------------------");
}
GET방식으로 Parameter를 냅다 보내봄

spring에서 알아서 자동수집해서 바로 사용 가능

💡 단, 이렇게 기본으로 사용할 때는 parameter값이 안들어올때 500 에러가 발생함.
이 때 사용할 수 있는게 @RequestParam어노테이션
@RequestParam은 자동수집한 값에 대해 name을 대입하고 기본값을 지정해줄 수 있음@GetMapping("/view") public void view(@RequestParam(name="idx", defaultValue = "0") int idx, @RequestParam(name="name", defaultValue = "")String name, @RequestParam(name="age", defaultValue = "0")int age) { log.info("---------------------"); log.info("BbsController => view()"); log.info("idx : " + idx); log.info("name : " + name); log.info("age : " + age); log.info("---------------------"); }이렇게 사용하면 매개변수로 입력한 Parameter 중 안들어오는것이 있더라도 입력한 기본값으로 받아 처리할 수 있음.
💡 기본 자료형이 아닌 Parameter 받기
Formatter 구현하기
spring에서는 Formatter 인터페이스를 제공하고 있다! (parse, print 메서드를 갖고있음)
이를 내가 원하는 방식으로 구현하면 됨package org.fullstack4.springmvc.controller.Formatter; import org.springframework.format.Formatter; import java.text.ParseException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Locale; public class LocalDateFormatter implements Formatter<LocalDate> { @Override public LocalDate parse(String text, Locale locale) throws ParseException { return LocalDate.parse(text.trim(), DateTimeFormatter.ofPattern("yyyy-MM-dd")); } @Override public String print(LocalDate object, Locale locale) { return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(object); } }spring이 자동으로 끌어쓸수있도록 Bean으로 등록
<!-- servlet-context.xml 의 일부! --> <!-- formatter 추가하는 부분 --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="formatters"> <set> <bean class="org.fullstack4.springmvc.controller.Formatter.LocalDateFormatter" /> </set> </property> </bean> <!-- 만든 formatter가 더 있다면 <set>안에 쭉 넣어주면 됨 --> <mvc:annotation-driven conversion-service="conversionService" /> <!-- 위에 열심히 만든거 어노테이션으로 사용하겠다는 의미 -->이렇게 등록해놓으면 spring이 알아서 Formatting이 필요할 때 알아서 잘 씀 (제대로 실습해보면 좋을듯)
💡 와 그러면...파라미터 100개 받아야하면 @ReauestParameter 100번해야하나요?
그럴땐 DTO객체를 만들어서 dto를 매개변수로 넣어주면 해당 dto의 필드명을 보고 spring이 알아서 수집하게 할 수 있다@PostMapping("/regist") public void registPost(BbsDTO dto, Model model) { log.info("---------------------"); log.info("BbsController => registPost()"); log.info("user_id : " + dto.getUser_id()); log.info("title : " + dto.getTitle()); log.info("display_date : " + dto.getDisplay_date()); log.info("---------------------"); }위 코드의 경우 DTO를 작성하고 아무런 작업도 안한 상태로 dto를 매개변수로 넣어준 상황입니다!
다만 이럴경우 단져주는 파라미터 명과 dto 필드가 일치해야 함.
spring에서의 PRG 패턴 적용
@PostMapping("/regist")
public String registPost(BbsDTO dto,
Model model,
RedirectAttributes redirectAttributes) {
log.info("---------------------");
log.info("BbsController => registPost()");
log.info("user_id : " + dto.getUser_id());
log.info("title : " + dto.getTitle());
log.info("content : " + dto.getContent());
log.info("display_date : " + dto.getDisplay_date());
log.info("dto.toString : " + dto.toString());
log.info("---------------------");
redirectAttributes.addAttribute("title", dto.getTitle()); //redirect 주소에 쿼리스트링으로 값을 넣어주는 방식
redirectAttributes.addFlashAttribute("title2", dto.getTitle()); //안보이게 값 보내주는 방식
return "redirect:/bbs/list";
}
redirect:경로 형식의 문자열을 그대로 리턴해주는 방식으로 사용할 수 있습니다. (아무런 작업없이도 spring이 알아서 처리 함)redirectAttributes.addAttribute(키, 값)으로 쿼리스트링 형식으로 같이 넘겨주는 방식redirectAttributes.addFlashAttribute(키,값)으로 url 노출 없이 값을 넘겨주는 방식

spring MVC의 예외처리
spring MVC에서는 여러가지 예외를 처리하는 가장 일반적인 방식으로, @controllerAdvice 어노테이션을 이요하며, @controllerAdvice가 선언된 클래스를 Bean으로 처리하여 spring에서 해당 예외가 발생시 불러와 처리합니다.
예시 : NumberFormatException
int타입에 stirng을 넣었을 때 numberFormatException이 발생
@GetMapping("/list2")
public void list2(String s1, int i2) {
log.info("--------------------");
log.info("BbsController => list2()");
log.info("s1 : " + s1);
log.info("i2 : " + i2);
log.info("--------------------");
}

=> @controllerAdvice 이용해서 예외처리
package org.fullstack4.springmvc.controller.exception;
import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@Log4j2
@ControllerAdvice
public class CommonException {
@ResponseBody
@ExceptionHandler(NumberFormatException.class)
public String exceptionNumber(NumberFormatException numberFormatException) {
log.info("-----------------------------");
log.info("CommonException >> exceptionNumber");
log.info(numberFormatException.getMessage());
log.info("-----------------------------");
return "Number format exception";
}
}
결과!


💡 와 그러면 예외마다 저렇게 해줘야하나요??
@ResponseBody @ExceptionHandler(Exception.class) public String exception(Exception exception) { return exception.getMessage(); }이렇게 냅다
Exception채로 한번에 예외처리를 할 수도 있음.
이 때도 일반적인 예외처리할 때 처럼 제일 예외 범위가 좁은것부터 넓은것 순으로 예외처리 클래스 내 예외메서드를 작성해야 옳습니다.
💡 에러 공통페이지 작성해보기
<!-- 에러 페이지 --> <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %> <%@ page trimDirectiveWhitespaces="true" %> <html> <head> <title>페이지를 찾을 수 없습니다.</title> </head> <body> <h1>페이지를 찾을 수 없습니다.</h1> </body> </html>@ExceptionHandler(NoHandlerFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public String notFound(){ return "custom404"; }<!-- web.xml 중 일부 --> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/servlet-context.xml</param-value> </init-param> <init-param> <param-name>throwExceptionIfNoHandlerFound</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
@RequestMapping과 파생 어노테이션을 이용해서 기존 Servlet만 사용 시에는 컨트롤러 하나 당 doGet, doPost로 나눠서 그 안에서 다 처리해주느라