이번 단원을 봤을 때 특별한 점은 없고, Mapping 활용, Request 파라미터 다루기(커맨드 객체 활용, Model 객체 활용, ModelAndView), JSP 문법을 주의 깊게 살펴보면 될 것 같다.
스프링에 대해 공부한다기 보다, JSP 웹 어플리케이션 실습, 코드 설명으로 다른 프로젝트를 해봤으면 이해하기 쉬운 내용같다.
전장에서 배운 방법은 @Controller
가 붙은 클래스에 @[Get|Post]Mapping
에 경로를 지정하는 방식이었다.
@Controller
public class FirstController {
@GetMapping("/test/what")
public String enter( Model model, @RequestParam(value = "name", required = false) String name ) {
model.addAttribute("greeting","name = " + name);
return "whatisthis";
}
}
이제 url을 지정하는 방식을 여러개 알아보겠다.
공통 내용을 가지는 기능들은 같은 url로 묶는 경우가 많다. 위처럼 단순히 경로 지정을 모두 하면 비효율 적이니 같은 부분은 따로 묶을 수 있다.
@Controller
@RequestMappling("/test")
public class FirstController {
@GetMapping("/what")
public String enter( Model model, @RequestParam(value = "name", required = false) String name ) {
...
}
@PostMapping
public String send( Model model, @RequestParam(value = "name", required = false) String name ) {
...
}
}
@[Get|Post]Mapping
의 대표인 RequestMapping으로도 지정 가능하다. Method를 지정할 수 있다. 디폴트는 GET method지만 코드를 보고 이해하기 위해 적어주는게 좋다.
@RequestMapping("/what", method=RequestMethod.GET)
public String enter( Model model, @RequestParam(value = "name", required = false) String name ) {
...
}
특별한 로직이 없는 경우 Controller를 만들기는 비효율 적이다. 아래는 Controller 없이 Mapping을 하기 위한 방법이다. 이를 Web MVC 설정을 했던 WebMvcConfigurer를 구현한 클래스에 넣어주면 된다.
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/mainURL").setViewName("mainJSP");
}
요청 후 되돌아가기, 비정상 접근에 대한 처리를 위해 리다이렉트 처리를 해야할 경우가 있다. 이 방법을 알아본다.
방법은 매우 간단하다. return으로 보내주는 문자열에 redirect url을 지정해주면 된다.
@Controller
@RequestMappling("/test")
public class FirstController {
@GetMapping("/tt")
public String enter( Model model, @RequestParam(value = "auth", required = false) String authToken ) {
model.addAttribute("profile","name = " + authToken);
return "redirect:/profile";
}
}
주의할 점이라면, 원래 return 하던 값은 JSP의 이름이라 단순 문자열이었지만, 리다이렉트는 url을 리턴하는 것이다. 상대 경로, 절대 경로가 적용된다.
가장 먼저 전용 객체에 담는 방법이다.
@GetMapping("/profile")
public String enter( HttpServletRequest req ) {
String authParam = req.getParameter("auth");
if(name == null)
return "main"
return "profile"
}
Node에서 하던 것처럼 Request 객체로 받아서 getParameter("name")를 통해 name에 해당하는 value를 불러온다.
먼저 간단히 사용해보고 넘어간 어노테이션 방식이다.
@GetMapping("/test")
public String enter( Model model, @RequestParam(value = "auth", required = false) String authToken ) {
model.addAttribute("profile","name = " + authToken);
return "profile";
}
Model에 관해서는 나중에 알아보기로 하고, 해당 어노테이션에 대해 보자.
먼저 @RequestParam
으로 지정해 String 객체 name에 받는다.
어노테이션은 3가지 속성을 지정한다.
파라미터가 많을 경우 getParameter()
나 @RequestParam
으로 지정하기는 코드가 더러워 보일 것이다. 이를 위해 스프링은 커맨드 객체란 것을 지원한다.
커맨드 객체란 별도의 클래스를 만들어 값을 연결하도록 하는데, 스프링은 키 값과 변수명을 동일하게 만들고 Getter/Setter 메소드를 작성하면 자동 파싱해준다.
// 커맨드 객체
public class CommandObj {
private String key1;
private String key2;
private String key3;
private String key4;
//Getter
//Setter
}
// 활용
@PostMapping("/test")
public String handleStep3(CommandObj comObj) {
try {
//객체로 데이터가 파싱되어
System.out.println(comObj.getKey1());
//DB를 다루는 서비스에 넘겨 조작.
daoService.putData(comObj);
return "test/tt";
} catch (DuplicateException ex) {
return "test/profile";
}
}
JSP에서 이 객체에 접근할 수 있는데, 간단히 객체의 이름의 첫글자를 소문자로하는 lowerCamelCase로 접근가능하며, @ModelAttribute
어노테이션을 통해 지정 가능하다. JSP의 문법은 밑에서 따로 설명하겠다.
public String handleStep3(@ModelAttribute("myObjName") CommandObj comObj)
컨트롤러는 뷰가 응답 화면을 구성하는데 필요한 데이터를 생성해서 전달해야 한다. 이를 위해 Model 객체를 사용한다. 전장에 연습으로 사용했던 방식이다.
@Controller
public class FirstController {
@GetMapping("/test/what")
public String enter( Model model, @RequestParam(value = "name", required = false) String name ) {
model.addAttribute("greeting","name = " + name);
return "whatisthis";
}
}
model.addAttribute("key", Object)
형식으로 설정한다.
두번째 인자에는 객체를 넣을 수 있다. JSP에서 객체가 List면 List형식으로 접근 가능하고, 객체 속 또 다른 객체가 있어도 outerObj.innerObj 형식으로 들어가서 접근 가능하다.
위의 방식들은 데이터 설정, 결과용 뷰 이름 리턴 두 개의 작업을 하는데, ModelAndView를 사용하면 한번에 처리 가능하다.
원래는 뷰의 이름을 리턴하기 위해 String을 리턴했지만, 지금은 ModelAndView를 리턴한다.
@GetMapping
public ModelAndView form() {
List<TestObj> test = createTestData();
ModelAndView mav = new ModelAndView();
mav.addObject("test", test);
//기본 view를 WEB-INF/views 로 했기 때문에 그 뒤 경로
mav.setViewName("test/testMaV");
}
로그인 폼 같은 경우 잘못 전송 했을 때, 입력했던 데이터가 삭제되어 다시 입력하는 번거로움이 없도록 한다. 이를 위해 동적으로 입력해주어야 하는데, 객체를 통해 입력하기 위해 GET POST 둘다 동일한 객체를 사용해야 한다. 커맨드 객체를 이용하면 이를 쉽게 할 수 있다.
@GetMapping("/test")
public String handleStep3(CommandObj comObj) {
return "test"
}
@PostMapping("/test")
public String handleStep3(CommandObj comObj) {
try {
//객체로 데이터가 파싱되어
System.out.println(comObj.getKey1());
//DB를 다루는 서비스에 넘겨 조작.
daoService.putData(comObj);
return "test/tt";
} catch (DuplicateException ex) {
return "test/profile";
}
}
객체의 속성 이름이 클래스 이름과 다르다면 @ModelAttribute
어노테이션으로 같은 이름을 지정해주면 된다.
JSP Expression Language에 대해 알아보겠다.
JSP는 <%= %>나 액션 태그<jsp:useBean> 식의 방법을 써서 빈의 데이터를 가져오는데, EL은 이보다 쉽게 데이터를 꺼내오는 표현식이다. JSP처럼 변수선언과 초기화 작업이 내부에서 자동적으로 지원되는 객체인 내장객체를 참조변수를 통해 바로 접근할 수 있게 한다.
객체 | 설명 | 호출 |
---|---|---|
pageContext | JSP의PageContext객체 | |
servletContext | ServletContext객체 | ${pageContext.servletContext.객체명} |
session | HttpSession객체 | ${pageContext.session.객체명} |
request | ServletRequest객체 | ${pageContext.request.객체명} |
response | ServletResponse객체 | |
param | 요청매개변수의값조회 | ${param.매개변수명} |
paramValues | 요청매개변수의값배열조회 | ${paramValues.매개변수명} |
header | HTTP헤더의값조회 | ${header.헤더명} |
headerValues | HTTP헤더의값배열조회 | ${headerValues.헤더명} |
cookie | 쿠키값조회 | ${cookie.쿠키명} |
initParam | 컨텍스트초기화매개변수의값조회 | ${initParam.매개변수명} |
pageScope | page보관소의값조회 | ${pageScope.객체명} |
requestScope | request보관소의값조회 | ${requestScope.객체명} |
sessionScope | session보관소의값조회 | ${sessionScope.객체명} |
applicationScope | application보관소의값조회 | ${applicationScope.객체명} |
앞에 배웠던 파라미터 접근 방법에 대한 JSP 코드이다.
먼저 커맨드 객체를 이용한 방법이다.
<!-- 일반적으로 객체를 불러와 사용 (호출 시 객체의 lowerCamelCase)-->
<input type="text" name="key1" id="key1" value="${commandObj.key1}">
<!-- 커맨드 객체에 List 컬렉션, 또다른 객체가 있을 경우 입력 받을 시-->
<!-- name = 프로퍼티이름[인덱스] 형식이면 리스트로 인식-->
<input type="text" name="list[0]" value="list0">
<input type="text" name="list[1]" value="list1">
<!-- name = 프로퍼티이름[인덱스] 형식이면 중첩 객체로 인식-->
<input type="text" name="obj.key1" value="objkey1">
<!-- 커맨드 객체에 List 컬렉션, 또다른 객체가 있을 경우 접근 시-->
<!-- List : 프로퍼티이름[인덱스] 형식이면 리스트로 인식-->
<p> ${commandObj.list[0]} </p>
<!-- Object : 프로퍼티이름. 형식이면 중첩 객체로 인식-->
<p> ${commandObj.obj.key1} </p>
Model 객체를 이용한 방법이다.
<!-- Model을 이용할 경우 model.addAttribute("name", Object); 에서 name 값을 사용한다. -->
<p> String : ${name} </p>
<p> Object : ${name.key} </p>
이를 사용하면 HTML 폼에 데이터를 바인딩하거나 에러메세지 처리등을 간편하게 할 수 있다.
뷰 페이지에 아래 태그를 추가해야한다.
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
객체를 불러와 값을 넣어줄 때 스프링 폼 태그를 이용하면 쉽게 가능하다.
객체의 프로퍼티 이름을 path로 매치하면 자동으로 파싱된다.
<!-- Spring Form 사용 prefix로 이름지정-->
<!-- modelAttribute로 커맨드 객체 지정하여 자동 입력 -->
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
...
<form:form action="/test/command" modelAttribute="commandObj">
<form:input path="key1" />
이 외의 여러개의 태그를 사용할 수 있다.
폼 형태 | 설명 | 부가 |
---|---|---|
<form:form> | form 태그 생성 | default (action : 현재 url, method : POST) |
<form:input> | input 태그 type="text" 생성 | |
<form:password> | input 태그 type="password" 생성 | |
<form:hidden> | input 태그 type="hidden" 생성 | |
<form:select> | select 태그 생성 | 지정한 콜렉션 객체를 주입받을 수 있음 |
<form:options> | option 태그 생성 | 지정한 콜렉션 객체를 주입받음 |
<form:option> | option 태그 생성 |
이 외에도 form관련 태그들을 많이 지원한다.
CSS 및 이벤트 관련 속성을 지원한다.
HTML의 기본 속성또한 사용 가능하다.
JSP Standard Tag Library의 약자로 위에 나온 JSP EL과 함께 사용하여 JSP 코드를 간결하게 한다. <%=if %>문을 <c:if>, <%=for%>문을 <c:forEach>로 대체하여 사용한다.
아래의 코어 라이브러리를 추가해주어야 한다.
<% @taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<!-- JSTL 사용 - 접근 이름 prefix로 c로 명명-->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:forEach var="command" items="${commandObj.list}" varStatus="status">
<p> ${status.index + 1}. ${command}<br/> </p>
</c:forEach>
태그 | 설명 |
---|---|
<c:set> | 변수명에 값을 할당 |
<c:out> | 값을 출력 |
<c:if> | 조건식에 해당하는 블럭과 사용될 scope설정 |
<c:choose> | 다른 언어의 switch와 비슷 |
<c:when> | switch문의 case에 해당 |
<c:otherwise> | switch문의 default에 해당 |
<c:forEach> | 다른언어의 loop문 items 속성에 배열을 할당할 수 있음 |
자주 사용하는 forEach 문법을 살펴본다.
<c:forEach var="i" begin="1" end="10" step="1">
<c:out value="${i}"/>
<br/>
</c:forEach>
<c:forEach var="command" items="${commandObj.list}" varStatus="status">
<p> ${status.index + 1}. ${command}<br/> </p>
</c:forEach>
if문은 매우 간단하다.
c:if test="${! q.choice }">
<input type="text" name="responses[${status.index}]">
</c:if>
varStatus
뭐하는지 궁금해서 따라가봤는데, 기본 설정들이 다 들어있는 것 같다.
대표적으로 아래 같은 설정이 있다private void init() { // defaults for internal bookkeeping index = 0; // internal index always starts at 0 count = 1; // internal count always starts at 1 status = null; // we clear status on release() item = null; // item will be retrieved for each round last = false; // last must be set explicitly beginSpecified = false; // not specified until it's specified :-) endSpecified = false; // (as above) stepSpecified = false; // (as above) // defaults for interface with page author begin = 0; // when not specified, 'begin' is 0 by spec. end = -1; // when not specified, 'end' is not used step = 1; // when not specified, 'step' is 1 itemId = null; // when not specified, no variable exported statusId = null; // when not specified, no variable exported |
이 블로그를 보았다.
javascript에서 model parameter를 가져오는 방법
var key = '<c:out value='${key}' />';
javascript에서 context path를 가져오는 방법
var G_CONTEXT_PATH = "${pageContext.request.contextPath}";
jsp에서 url의 get parameter 가져오는 방법
<c:if test="${param.loginFail eq 'true'}">
<div class="login-fail">아이디 혹은 비밀번호가 일치하지 않습니다.</div>
</c:if>
jsp에서 현재 년도 구하기
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<jsp:useBean id="now" class="java.util.Date" />
<fmt:formatDate var="year" value="${now}" pattern="yyyy" />
<p>Current year: ${year}</p>
<p>Previous year: ${year - 1}</p>