REGDATE 값이 두 파라미터로 전달받은 from 과 to 사이의 Member 목록을 구한다.
private RowMapper<Member> memRowMapper =
new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member(rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
};
시간을 나타내기 위해 LocalDateTime 타입으로 변환(Casting)해야 한다. @DateTimeFormat 애노테이션을 통해 자동으로 형변환 할 수 있다.
import org.springframework.format.annotation.DateTimeFormat;
public class ListCommand {
@DateTimeFormat(pattern = "yyyyMMddHH")
private LocalDateTime from;
@DateTimeFormat(pattern = "yyyyMMddHH")
private LocalDateTime to;
@DateTimeFormat 애노테이션을 통해 LocalDateTime 타입으로 변환한다. "2018030115" 문자열을 "2018년 3월 1일 15시" 값을 갖는 LocalDateTime 객체로 변환할 수 있다.
다음은 ListCommand 클래스를 커맨드 객체로 사용한 컨트롤러이다.
package controller;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import spring.Member;
import spring.MemberDao;
@Controller
public class MemberListController {
private MemberDao memberDao;
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
@RequestMapping("/members")
public String list(
@ModelAttribute("cmd") ListCommand listCommand,
Errors errors, Model model) {
if (listCommand.getFrom() != null && listCommand.getTo() != null) {
List<Member> members = memberDao.selectByRegdate(
listCommand.getFrom(), listCommand.getTo());
model.addAttribute("members", members);
}
return "member/memberList";
}
}
태그 파일을 사용하여 LocalDateTime 값을 지정한 형식으로 출력한다.
<%@ tag body-content="empty" pageEncoding="utf-8" %>
<%@ tag import="java.time.format.DateTimeFormatter" %>
<%@ tag trimDirectiveWhitespaces="true" %>
<%@ attribute name="value" required="true"
type="java.time.temporal.TemporalAccessor" %>
<%@ attribute name="pattern" type="java.lang.String" %>
<%
if (pattern == null) pattern = "yyyy-MM-dd";
%>
<%= DateTimeFormatter.ofPattern(pattern).format(value) %>
view는 다음과 같다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="tf" tagdir="/WEB-INF/tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원조회</title>
</head>
<body>
<form:form modelAttribute="cmd">
<p>
<label>from: <form:input path="from" /></label>
~
<label>to:<form:input path="to" /></label>
<input type="submit" value="조회">
</p>
</form:form>
<c:if test="${! empty members}">
<table>
<tr>
<th>아이디</th><th>이메일</th>
<th>이름</th><th>가입일</th>
</tr>
<c:forEach var="mem" items="${members}">
<tr>
<td>${mem.id}</td>
<td><a href="<c:url value="/members/${mem.id}"/>">
${mem.email}</a></td>
<td>${mem.name}</td>
<td><tf:formatDateTime value="${mem.registerDateTime }"
pattern="yyyy-MM-dd" /></td>
</tr>
</c:forEach>
</table>
</c:if>
</body>
</html>
해당 화면에서 from 이나 to 에서 지적된 형식에 맞지 않는 값을 입력하면 400에러가 발생한다. 400에러 대신 에러메시지를 출력하고 싶다면 Errors 타입 파라미터를 요청 매핑 어노테이션 적용 메서드에 추가한다.
package controller;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import spring.Member;
import spring.MemberDao;
@Controller
public class MemberListController {
private MemberDao memberDao;
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
@RequestMapping("/members")
public String list(
@ModelAttribute("cmd") ListCommand listCommand,
Errors errors, Model model) {
if (errors.hasErrors()) {
return "member/memberList";
}
if (listCommand.getFrom() != null && listCommand.getTo() != null) {
List<Member> members = memberDao.selectByRegdate(
listCommand.getFrom(), listCommand.getTo());
model.addAttribute("members", members);
}
return "member/memberList";
}
}
label.properties에 typeMismatch의 메시지를 추가한다.
- typeMismatch.java.time.LocalDateTime = 잘못된 형식
view 도 다음과 같이 수정한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="tf" tagdir="/WEB-INF/tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원조회</title>
</head>
<body>
<form:form modelAttribute="cmd">
<p>
<label>from: <form:input path="from" /></label>
<form:errors path="from" />
~
<label>to:<form:input path="to" /></label>
<form:errors path="to" />
<input type="submit" value="조회">
</p>
</form:form>
<c:if test="${! empty members}">
<table>
<tr>
<th>아이디</th><th>이메일</th>
<th>이름</th><th>가입일</th>
</tr>
<c:forEach var="mem" items="${members}">
<tr>
<td>${mem.id}</td>
<td><a href="<c:url value="/members/${mem.id}"/>">
${mem.email}</a></td>
<td>${mem.name}</td>
<td><tf:formatDateTime value="${mem.registerDateTime }"
pattern="yyyy-MM-dd" /></td>
</tr>
</c:forEach>
</table>
</c:if>
</body>
</html>
변환 처리에 대한 이해
@DateTimeFormat 애노테이션을 통해 LocalDateTime 타입으로 변환해준다. 이때 해당 데이터 타입으로 변환시켜 주는 것이 WebDataBinder이다. WebDataBinder는 역할을 Conversion Service에게 위임한다. @EnableWebMvc 애노테이션 사용시 DefaultFormattingConversionService를 ConversionService로 사용한다. 커맨드로 사용할 클래스에 @DateTimeFormat 애노테이션만 붙이면 지정한 형식의 문자열을 시간 타입 값으로 받을 수 있다. 이렇게 받은 값을 jsp의 form을 표현한다.
@PathVariable을 이용한 경로 변수 처리
로그인하여 접속하는 개인정보를 가진 처리에서는 정보보안의 문제로 잘 쓰지 않는다.
@GetMapping("/members/{id}")
public String detail(@PathVariable("id") Long memId, Model model) {
Member member = memberDao.selectById(memId);
model.addAttribute("member",member);
return "member/memberDetail";
}
'{경로변수}' 와 같이 중괄호로 둘러 쌓인 부분을 경로변수라고 부른다.경로값을 @PathVariable("id") 애노테이션이 적용된 memId 파라미터에 전달한다. memId 파라미터 타입을 WebDataBinder를 통해 String 타입 값을 Long으로 변환한다.
같은 컨트롤러에 @ExceptionHandler 애노테이션을 적용한 메서드가 존재하면 그 메서드가 익셉션을 처리한다.
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.TypeMismatchException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import spring.Member;
import spring.MemberDao;
import spring.MemberNotFoundException;
@Controller
public class MemberDetailController {
public MemberDao memberDao;
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
@GetMapping("/members/{id}")
public String detail(@PathVariable("id") Long memId, Model model) {
Member member = memberDao.selectById(memId);
if(member == null) {
//@ExceptionHandler의 파라미터 값에 따라 처리가 달라진다.
throw new MemberNotFoundException();
}
model.addAttribute("member",member);
return "member/memberDetail";
}
@ExceptionHandler(TypeMismatchException.class)
public String handleTypeMismatchException() {
return "member/invalidId";
}
@ExceptionHandler(MemberNotFoundException.class)
public String MemberNotFoundException() {
return "member/noMember";
}
}
@ControllerAdvice를 이용한 공통 익셉션 처리
컨트롤러 클래스에 @ExceptionHandler 애노테이션을 적용하면 해당 컨트롤러에서 발생한 익셉션만 처리한다. 여러 컨트롤러에서 동일하게 처리할 익셉션 발생시 @ControllerAdvice 애노테이션을 이용해서 중복을 없앤다.
@ControllerAdvice("spring")//spring이 범위가 된다.
public class CommonExceptionHandler{
@ExceptionHandler(RuntimeException.class)
public String handleRuntimeException(){
return "error/commonException";
}
}
@ExceptionHandler 적용 매서드의 우선순위는 다음과 같다.
속성 | 타입 | 설명 |
value basePackages |
String[] | 공통 설정을 적용할 컨트롤러가 속하는 패키지 |
annotations | Class<? extends Annotation>[] | 특정 애노테이션이 적용된 컨트롤러 대상 |
assignableTypes | Class<?>[] | 특정 타입 또는 그 하위 타입인 컨트롤러 대상 |
@ExceptionHandler를 붙인 메서드는 다음 파라미터를 가질 수 있다.
- HttpServletRequest, HttpServletResponse, HttpSession
- Model
- 익셉션
리턴타입은 다음과 같다.
- ModelAndView (자동으로 설정됨)
- String(뷰이름)
- (@ResponseBody 애노테이션을 붙인 경우) 임의 객체
- ResponseEntity
'Java > JavaSpring' 카테고리의 다른 글
Chap 13. MVC 3: 세션, 인터셉터, 쿠키 (0) | 2022.07.14 |
---|---|
Chap 12. MVC 2 : 메시지, 커맨드 객체 검증 (0) | 2022.07.13 |
JUNIT (0) | 2022.07.11 |
Chap 11. MVC 1 : 요청 매핑, 커맨드 객체, 리다이렉트, 폼 태그, 모델 (0) | 2022.07.07 |
Chap 10. 스프링 MVC 프레임워크 동작 방식 (0) | 2022.07.06 |