[Spring] Spring MVC - 2

Fortice·2021년 2월 4일
2

Spring

목록 보기
9/13
post-thumbnail

스프링 프로그래밍 입문 5 - 11장

이번 단원을 봤을 때 특별한 점은 없고, Mapping 활용, Request 파라미터 다루기(커맨드 객체 활용, Model 객체 활용, ModelAndView), JSP 문법을 주의 깊게 살펴보면 될 것 같다.

스프링에 대해 공부한다기 보다, JSP 웹 어플리케이션 실습, 코드 설명으로 다른 프로젝트를 해봤으면 이해하기 쉬운 내용같다.

Mapping 활용

전장에서 배운 방법은 @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을 지정하는 방식을 여러개 알아보겠다.

1. Base 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 ) {
        ...
    }
}

2. RequestMapping

@[Get|Post]Mapping의 대표인 RequestMapping으로도 지정 가능하다. Method를 지정할 수 있다. 디폴트는 GET method지만 코드를 보고 이해하기 위해 적어주는게 좋다.

@RequestMapping("/what", method=RequestMethod.GET)
    public String enter( Model model, @RequestParam(value = "name", required = false) String name ) {
        ...
    }

3. Controller가 없는 Mapping

특별한 로직이 없는 경우 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을 리턴하는 것이다. 상대 경로, 절대 경로가 적용된다.

  • 상대 경로 ( redirect:profile )
    • 이 경우 현재 요청 경로(spring/test/tt)를 기반으로 상대 경로 설정 : /spring/test/profile
  • 절대 경로 ( redirect:/profile )
    • /spring/profile

Request 파라미터 다루기

1. HttpServletRequest

가장 먼저 전용 객체에 담는 방법이다.

@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를 불러온다.

2. @RequestParam

먼저 간단히 사용해보고 넘어간 어노테이션 방식이다.

@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가지 속성을 지정한다.

  • value : HTTP 요청 파라미터의 이름.
  • required : 필수 여부. true일 경우 파라미터의 값이 없으면 익셉션 발생(default = true)
  • defaultValue : 해당 요청 파라미터의 값이 없을 경우 사용할 문자열

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)

4. Model 객체

컨트롤러는 뷰가 응답 화면을 구성하는데 필요한 데이터를 생성해서 전달해야 한다. 이를 위해 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 형식으로 들어가서 접근 가능하다.

5. ModelAndView

위의 방식들은 데이터 설정, 결과용 뷰 이름 리턴 두 개의 작업을 하는데, 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");
}

6. GET/POST 동일 객체 사용

로그인 폼 같은 경우 잘못 전송 했을 때, 입력했던 데이터가 삭제되어 다시 입력하는 번거로움이 없도록 한다. 이를 위해 동적으로 입력해주어야 하는데, 객체를 통해 입력하기 위해 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-EL 문법

JSP Expression Language에 대해 알아보겠다.
JSP는 <%= %>나 액션 태그<jsp:useBean> 식의 방법을 써서 빈의 데이터를 가져오는데, EL은 이보다 쉽게 데이터를 꺼내오는 표현식이다. JSP처럼 변수선언과 초기화 작업이 내부에서 자동적으로 지원되는 객체인 내장객체를 참조변수를 통해 바로 접근할 수 있게 한다.

기본 문법

  • ${ }
    • JSP가 실행될 때 즉시 반영된다.
    • 객체 프로퍼티 값을 꺼낼 때 사용한다.
  • #{ }
    • 시스템에서 필요하다고 판단될 때 그 값을 사용한다.
    • 사용자 입력값을 객체의 프로퍼티에 담는 용도로 주로 사용한다.

내장 객체

객체설명호출
pageContextJSP의PageContext객체
servletContextServletContext객체${pageContext.servletContext.객체명}
sessionHttpSession객체${pageContext.session.객체명}
requestServletRequest객체${pageContext.request.객체명}
responseServletResponse객체
param요청매개변수의값조회${param.매개변수명}
paramValues요청매개변수의값배열조회${paramValues.매개변수명}
headerHTTP헤더의값조회${header.헤더명}
headerValuesHTTP헤더의값배열조회${headerValues.헤더명}
cookie쿠키값조회${cookie.쿠키명}
initParam컨텍스트초기화매개변수의값조회${initParam.매개변수명}
pageScopepage보관소의값조회${pageScope.객체명}
requestScoperequest보관소의값조회${requestScope.객체명}
sessionScopesession보관소의값조회${sessionScope.객체명}
applicationScopeapplication보관소의값조회${applicationScope.객체명}

설명

  • EL 사용예제
    • request.getParameter(“name”); -> ${param.name}
  • JSP 내부객체 접근 순서
    • page -> request -> session -> application
    • 이름이 중복되면 Scope를 지정하여 접근 가능.
  • EL 객체 접근 예제
    • ${member.name}
      • 도트연산자를 통해 member 객체에서 name이라는 속성의 값을 가진 key의 value를 가져올 수 있다.
    • ${member[“name”]}
      • 문자열로 표현 할 수도 있으면 []를 이용하여 사용한다.
      • 문자열과 일치하는 키의 값을 가져온다.
  • 삼항연산, 사칙연산, 논리연산등의 기본적인 자바 연산자를 사용 가능

예제 코드

앞에 배웠던 파라미터 접근 방법에 대한 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>

Spring Form Tag

이를 사용하면 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 태그 속성

CSS 및 이벤트 관련 속성을 지원한다.

  • cssClass ; HTML의 class 속성 값
  • cssErrorclass : 폼 검증 에러가 발생했을 때 사용할 HTML의 class 속성 값
  • cssStyle : HTML의 CSS 속성 값

HTML의 기본 속성또한 사용 가능하다.

  • id, title, dir
  • disabled, tabindex
  • onfocus, onblur, onchange
  • onclick, ondblclick
  • onkeydown, onkeypress, onkeyup
  • onmousedown, onmousemove, onmouseup
  • onmouseout, onmouseover

JSTL

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 문법을 살펴본다.

  • var : 변수 생성
  • begin : 변수 시작 값
  • end : 변수
  • step : 변수의 값을 증가시킨다?
    • for문의 i++, i+=2 같은 기능이다.
  • items : 객체를 지정하여 객체 내의 프로퍼티를 하나하나 변수에 넘겨준다.
  • varStatus : 루프를 도는데 도움이 되는 기능이 들어있다.
    • 출력 시 아래 객체가 나온다.
    • javax.servlet.jsp.jstl.core.LoopTagSupport$1Status@a8cc1cc
<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>
profile
서버 공부합니다.

0개의 댓글