Spring Annotation & ViewResolver (tiles)

MisCaminos·2021년 3월 16일
0

Server & Web

목록 보기
14/23
post-thumbnail

스프링이 동작하면서 시작되는 일을 다시 정리하자면:

  • 스프링 프레임워크가 시작되면 먼저 스프링이 사용하는 메모리 영역이 생성된다.
    이를 컨텍스트(Context)라고 한다. (stack, heap, code, data 영역 모두 다 해당될 수 있다.

  • 스프링에선 ApplicationContext라는 이름의 객체가 만들어진다. 스프링은 자신이 객체를 생성하고 관리해야 하는 객체들에 대한 설정을 servlet-context.xml 파일에 작성한다.

  • servlet-context.xml파일에 설정되어 있는 <context:component-scan>에 지정되어 있는 패키지를 스캔하고 해당 패키지의 있는 클래스들 중에서 스프링이 사용하는 @Component가 선언된 클래스의 객체가 생성한다.

    Context= 운영할 수있는 전체적인 영역, 하나의 application이 작동할 수 있는 전체 환경
    (e.g., contextPath = application project의 root directory)
    만약 application이 두개라면, context가 2개

Spring Annotation

@Controller - 클래스 타입에만 적용되며, 웹 요청처리에 사용
@RequestMapping - 컨트롤러가 처리할 Get/Post방식 요청URL을 명시한다.(클래스,메소드 모두 사용 가능!) get인지 post인지 표기하지 않고 둘다 사용하는 방법은 RequestMapping을 사용하는것이다.

실습에서는 두가지 RequestMapping표기 방식을 사용했다:
(1) @RequestMapping("/bbs/list")
위의 방식의 경우 요청 경로의 value만 표기한다. 그 경로로 get,post 방식의 요청을 모두 handling 한다.
(2) @RequestMapping(value = "/", method = RequestMethod.GET)
이거는 옛날 방식인데. value에는 경로를, method에는 GET/POST 방식을 따로 표기한다.

RequestMapping은 Get/PostMapping대비 코딩은 복잡해지지만 메소드만이 아닌 클래스 level에 적용할 수도 있고, url경로 하나로 get/post 방식 요청을 다 할 수 있어서 장단점이 있어보인다. 개발자 스타일에 따라 RequestMapping을 지향 또는 지양한다고 한다.
@GetMapping - 컨트롤러가 처리할 Get방식 요청URL을 명시(메소드)
@PostMapping - 컨트롤러가 처리할 POST방식 요청URL을 명시(메소드)

Controller Method의 매개변수(parameter)에 사용되는 parameter type 및 annotation:

HttpServletRequest, HttpServletResponse,HttpSession - 서블릿 API
java.util.Locale - 현재 요청에 대한 locale
InputStream, Reader - 요청 contents에 직접 접근할때 사용
OutputStream, Writer - 응답 contents를 생성할 때 사용
@PathVariable - RESTful API를 구현할때에 RestController에서 URI template 변수에 접근할때 사용된다. parameter를 URL 형식으로 받을 수 있어서, mapping하려는 URL내에 "/{parameter}"를 포함시켜서 URL을 동적으로 제어할 수 있다.
example)

@GetMapping("/bbs/reply/{rnum}")
public ResponseEntity<ReplyDTO> get(@PathVariable("rnum") int rnum){
	log.info("get:"+rnum);
		
	return new ResponseEntity<ReplyDTO>(mapper.read(rnum), HttpStatus.OK);
}

@RequestParam - Http요청 parameter를 mapping할때 사용. Form 페이지(e.g., createForm.jsp, updateForm.jsp, etc)에서 넘어오는 parameter를 받을 수 있다. (client로 부터 parameter값을 전달받는것이다) 강제적으로 parameter를 받기위해 각각의 parameter에 이 annotation을 지정한다. (만약 해당 파라미터가 없다면 HTTP 400 - Bad Request 가 전달 된다.)
example)

@PostMapping("/member/login")
public String login(@RequestParam Map<String, String> map, 
			HttpSession session, HttpServletResponse response,
			HttpServletRequest request, Model model) {
...
} 

파라미터가 필수가 아니라면 required = false 로 지정하면 된다. 파라미터가 없으면 NULL이 들어간다. default 값을 지정 할 수도 있다.

public void view( @RequestParam(value = "id", required = false, defaultValue = "0" )  int id) {
...
}

@RequestParam을 생략하여 사용할수 있다. Map타입으로 데이터를 받을경우는 반드시 @RequestParam을 명시해야 데이터를 받아온다.

public String add( @RequestParam Map<String, String> params ) {
...
} 

@RequestHeader - Http요청 header를 mapping할때 사용
@CookieValue - Http cookie mapping 용도
@RequestBody - Http 요청의 몸체 내용에 접근할때 사용. HttpMessage Converter를 이용해서 Http요청 데이터를 해당 타입으로 변환한다. JSON데이터를 서버에 보내는 경우에는 원하는 타입의 객체(문자열 등)로 변환하기 위해 사용한다. (JSON 데이터를 원하는 타입으로 바인딩 처리)
Map, Model, ModelMap - view에 전달할 model 데이터를 설정할때 사용. Model은 request보다는 더 확장된 객체로서 sendRedirect를 해도 Model 객체 안에 attribute가 없어지지 않는다.
Command object(커맨드 객체) - Http요청 parameter를 저장한 객체. 기본적으로 클래스 이름을 모델명으로 사용. @ModelAttribute annotation을 사용하여 모델명을 설정할 수 있음.
Errors, BindingResult - Http요청 parameter를 커맨드 객체에 저장한 결과. 커맨드 객체를 위한 parameter 바로 다음에 위치한다.
SessionStatus - form 처리를 완료했음을 처리하기 위해 사용한다. @SessionAttribute annotation을 명시한 session 속성을 제거하도록 event를 발생시킨다.

Controller method의 반환(return)값의 유형을 지정하는 parameter type 및 annotation:

ModelAndView - view정보 및 모델 정보를 담고있는 ModelAndView 객체
Model - Model에는 view에 전달할 객체 정보를 담을 수 있다. 이때 view 이름은 요청 url로부터 결정됨. Model은 sendRedirect를 해도 이동하는 view 페이지에서 model의 attributes를 사용할 수 있다. (RequestToViewNameTranslator를 통해 view 결정)
Map - view에 전달할 객체 정보를 담고있는 Map. (RequestToViewNameTranslator를 통해 view 결정)
String - view이름을 담은 문자열
View객체 - view 객체를 직접 반환한다. 그 view객체를 이용해서 view를 생성한다.
void - 메소드가 ServletResponse 또는 HttpServletResponse 타입의 parameter를 갖는 경우 메소드가 직접 응답을 처리하는것으로 가정한다. 만약 그렇지 않은 경우에는 요청 URL로부터 결정된 view를 보여준다. (RequestToViewNameTranslator를 통해 view 결정)
@ResponseBody annotation을 적용한 경우 - 메소드에서 @ResponseBody annotation이 적용된 경우, 리턴 객체를 Http 응답으로 전송한다. HttpMessageConverter를 이용해서 객체를 Http응답 스트림으로 변환한다. JSP와 같은 뷰가 전달되는 것이 아니라 데이터 자체를 전달하기위한 용도(Ajax 응답데이터)로 사용되는 것이다.

View page를 새롭게 그리지않고, data만 전달하는경우 (e.g., Ajax통신 방식에서 JSON 형태의 data를 전송할때) Controller method에 위에 언급한 매개변수 annotation인 @RequestBody(매개변수)와 @ResponseBody(리턴값 타입) annotation을 사용한다.

그외 annotations:

@ModelAttribute - parameter를 Object(DTO)형태로 받을때 사용
@SessionAttributes - 세션상에서 model의 정보를 유지하고 싶을 경우 사용

home.jsp 응답하기까지 순서

Web browser에서 요청을 들어오면 Controller들을 찾는다. (@Controller annotation이 표기된 .java 클래스들이다. 실습 project webtest의 경우에는 HomeController, BbsController, MemberController, 등이 있다.)

url 경로로 요청이 들어오는 경우는 Get방식으로 요청 url과 Controller의 메소드를 mapping한다.

Controller에서 (필요시, model(및 DB)와 연동된 작업 또는 다른 필요한 작업을 수행하고 그 결과를 가지고) 어떤 page로 이동할지를 지정한다. 대부분 Controller 메소드의 반환타입은 다음 이동 페이지를 지정하는 url 문자열이다.

HomeController.java:

@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
	logger.info("Welcome home! The client locale is {}.", locale);
		
	Date date = new Date();
	DateFormat dateFormat =
    	DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
	String formattedDate = dateFormat.format(date);
		
	//addAttibute가 하는 일은 setAttribute와 동일함
	model.addAttribute("serverTime", formattedDate );
		
	return "home";
}

HomeController의 경우, "/"를 GET 방식으로 mapping해서 home()이라는 메소드를 찾았고, 여기에서 원하는 data(여기서는 Locale Date)을 얻는 작업을 수행하고 model 객체안에 넣었다. 그리고 이 메소드의 반환타입은 문자열로 "/home"을 다음 view 페이지 이름으로 지정했다.

아래보이는 root-context.xml에서 지정한듯이, tiles configurer를 사용하는 1순위 viewResolver 객체를 사용한다.

root-context.xml:

<!-- tiles2 설정 -->
<bean id="tilesconfigurer"
	class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
	<property name="definitions">
		<list>
			<value>/WEB-INF/spring/bbs.xml</value>
			<value>/WEB-INF/spring/member.xml</value>
		</list>
	</property>
</bean>
<bean id="viewResolver"
		class="org.springframework.web.servlet.view.UrlBasedViewResolver">
	<property name="viewClass">
		<value>org.springframework.web.servlet.view.tiles2.TilesView</value>
	</property>
	<property name="order" value="1"></property>
</bean>

Controller의 home() 메소드가 반환한 view page이름을 tiles 설정 파일에서 찾는다. 위 root-context.xml에는 bbs.xml, member.xml 두개 설정파일이 있다.

아래 bbs.xml을 보면, view page에서 사용할 template이 설정되어있다. header, footer등 view page의 template 구성을 main으로 설정하고, body만 다른 jsp 파일로 구성하여 view page를 구현하도록 설정한다. 각 요청을 처리한 후 반환된 경로 문자열은 definition name으로 지정하고 각 definition 요소가 extends="main"을 포함하게해서 모든 요청 처리 page가 header 또는 footer template을 포함하여 render 되도록 한다. 각 definition의 attribute로 "body"에 요청 처리 결과를 보여줄 view page .jsp 파일 경로를 지정한다.

bbs.xml:

<tiles-definitions> 
    <definition name="main" template="/WEB-INF/views/template/template.jsp"> 
        <put-attribute name="header" value="/WEB-INF/views/template/top.jsp"/>
        <!-- footer는 지정되어있지 않음 -->
    </definition>
    <definition name="/home" extends="main">
       <put-attribute name="title" value="기본페이지"></put-attribute>
       <put-attribute name="body" value="/WEB-INF/views/home.jsp" />
    </definition>
    ....
</tiles-definition>
    

definition name으로 "/home"은 body에 home.jsp를 담도록 설정되어있다. home.jsp에서 model 객체에 담아온 data를 browser화면에 render한다.

note: 매개변수로 전달되는 Model model객체는 request객체와 비슷하다. 단, Model은 request보다는 더 확장된 객체로서 sendRedirect를 해도 없어지지않는다.

DB 테이블마다 C,R,U,D 기능은 비슷하기때문에, 중복을 막기위해 Controller에서 요청 처리 후 연결하려는 view page .jsp는 경로를 폴더로 구분하여 생성한다.

example)
bbs의 create 기능 호출에 응답하기위한 설정 부분:

<definition name="/bbs/create" extends="main"> 
    <put-attribute name="title" value="bbs 등록" ></put-attribute> 
    <put-attribute name="body" value="/WEB-INF/views/bbs/createForm.jsp" /> 
</definition>  

member의 create 기능 호출에 응답하기위한 설정 부분:

<definition name="/member/create" extends="main">
    <put-attribute name="title" value="회원가입"></put-attribute>
    <put-attribute name="body" value="/WEB-INF/views/member/createForm.jsp" />
</definition>

만약 tiles mapping xml파일에 포함되지 않는 url로 요청이 들어오면, servlet-context.xml에서 설정했던 바와 같이 2순위 view resolving 방식으로 view page를 연결한다.

servlet-context.xml:

	<!-- view page를 찾는 ViewResolver -->
	<!-- Resolves views selected for rendering by @Controllers to .jsp resources 
    		in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
		<beans:property name="order" value="2" />
	</beans:bean>

여기 view resolver가 Controller에서 반환한 문자열에 prefix "/WEB-INF/views"와 suffix".jsp"를 더한 경로를 다음 view 페이지로 지정하는것이다.

참고: URI와 URL의 차이점?

URI: Uniform Resource Identifier
URL: Uniform Resource Locator

URI는 특정 resource를 identify하는 것이다.
(e.g., page, document, book, etc)
URL은 URI의 subset이기때문에 동일하게 resource를 identify하는 역할을 하지만, 특별히 그 resource를 어떻게 access하는지는 알려주는 identifier이다.
(e.g., HTTPs, FTP, etc.. - like https://velog.io)

References:

  1. Spring MVC 기반 Annotation from Lectureblue
  2. What's the Difference Between URI and URL?
profile
Learning to code and analyze data

0개의 댓글