origoni's Blog from Millky

origoni의 스프링 블로그 입니다.

[블로그개발_04] 스프링 부트(Spring Boot)의 세가지 뷰 (JSP, Velocity, Thymeleaf)

블로그개발 시리즈 - 다른글 : http://millky.com/@origoni/folder/30/post/list

라이브 데모 : http://blog.millky.com/post/list

자바 웹 개발 시작하기 : http://www.slideshare.net/origoni/presentations




오늘은 SpringBoot화면 표시에 대한 부분을 보도록 하겠다.

스프링 부트에서는 여러가지 뷰가 사용 가능하다.

  • FreeMarker
  • Groovy
  • Thymeleaf
  • Velocity
  • JSP

...

요즘은 자바 웹 서비스의 경우 REST API 서버로만 사용하고.
View쪽을 자바스크립트로 따로 개발하는 경우가 많다.
사실 아이폰, 안드로이드 앱을 만들때도 사용할 수 있고. API로 개발하는것이 여러모로 좋기는 하다.

그래도. 간단하게 블로그 만드는데~~ ^^; (소 잡는 칼로 닭을...)


이번 글에서는 3가지를 해보겠다. (사실 아래 3가지만 써봤다;; 프라마커도 많이 쓴다는데.. 난 왜 한번도 못써봤지?;;)

  • JSP : 그래도 표준이니까.
  • Velocity : 벨로시티. 이름처럼 빠르다(고 한다). 우리나라 현업에서 많이 사용되는것으로 알고 있다.(나도 회서에서~)
  • Thymeleaf : 타임리프? 스프링 부트 초반(2014년 초)에는... 요 탬플릿 엔진만? 지원했다 -_-; 그래서 작년에 프로젝트 할때 시범적으로 써보았다.. 결과물은 적절하게 나왔으나.. 개발하는 동안 왠지 고민스러울 때가 많았다;;;


자 위의 3가지 뷰를 간단하게 시작해보자~

코드는 여기에 : https://github.com/origoni/Spring-Blog/releases/tag/v0.0.4


그럼 먼저 뷰를 표시할 컨트롤러를 준비 해보자.

package com.millky.blog.presentation.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String index(Model model) {
model.addAttribute("name", "SpringBlog from Millky");
return "hello";
}
}

매우 간단하다.
/hello 라는 주소로 리퀘스트가 들어오면 name라는 속성에 문자열을 hello 라는 탬플릿으로 넘겨준다.

이 컨트롤러를 3가지 뷰에 공통으로(? 컨트롤러는 이거 하나만 뷰를 바꿔가면서!) 사용할 것이다.


컨터롤러는 준비되었고. 설정들을 바꾸어 가면 3가지 뷰를 확인해보자.




그럼 표준인 JSP부터 보자.
아직도 JSP 와 자바 웹 개발을 혼동하시는 분들이 있는것 같다.
여기에 대해 이야기 하려면 길어지니(구글링 하면 좋은 글이 많다)... 바로 스프링부트에서 사용하는법 살펴보자.

어쩌면 스프링 부트에서 가장 사용하기 힘든 View가 바로 JSP이다. (다른것보다 설정할 것이 많고 제약사항도 있다.)


필요한 라이브러리를 확인해보자.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>

JSP와 JSTL을 사용하기 위해서는 pom.xml에 위와 같이 설정되어 있으면 된다.


그리고 JSP파일을 만들어 보자.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Hello Millky</title>
</head>
<body>
<c:out value="<xmp>" escapeXml="true"></c:out>
<h2>Hello! ${name}</h2>
<div>JSP version</div>
</body>
</html>

아주 간단한 JSP 이다.
위치는 /src/main/webapp/WEB-INF/jsp/hello.jsp 이다..

bootRun(서비스 시작)하기 전에..  JSP만 해야 할 일이 하나 더 있다;;

스프링 부트 설정 파일에 뷰 경로를 적어줘야 한다.
다른 탬플릿 앤진용은.. 다 기본값이 있는데. JSP는 없다;


spring:
    view:
        prefix: /WEB-INF/jsp/
        suffix: .jsp

application.yml 파일에 위와 같이 설정 해 주자.




위의 그림과 같으면 되겠다.


이제 bootRun을 하고 http://localhost:8080/hello 에 접속해보자.



잘 나온다 ^^




이번에는 Velocity를 해보자.


우선 pom.xml에 [아까 설정한 3가지를 주석처리하고... (jsp 파일에서 에러나는것은 잠시 무시하자 ㅠㅠ)]

Velocity 를 사용하기 위해서는.. 요것 하나만 추가하면 된다.


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-velocity</artifactId>
</dependency>

위와 같이 pom.xml이 설정 되었으면 vm 파일을 만들어보자. 위치는 아래와 같다.

spring.velocity.resource-loader-path=classpath:/templates/

스프링부트에 기본적으로 정의가 되어 있어서 따로 설정할 필요는 없다.




위의 그림과 같은 위치이다.

아래는 vm파일이다. JSP보다 간단하다. (오늘 설명할 3가지중 벨로시티가 가장 간단하다.)


<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Hello Millky</title>
</head>
<body>
<h2>Hello! ${name}</h2>
<div>Velocity version</div>
</body>
</html>


역시 bootRun을 하고 http://localhost:8080/hello 에 접속해보자.




잘나온다 ㅋ




마지막으로 타임리프.

Thymeleaf 도 요것 하나만 있으면 일단 bootRun으로 동작 가능하다.


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>


벨로시티 부분도 잠시 주석처리 해두자.





아래는 Thymeleaf 파일이다. 확장자가 .html이다.

그리고 내용을 봐도. html에 어긋남이 없다. 가장 좋은 기능중 하나이다. 개발중인 파일을 웹브라우저에서 그냥 열어도 확인이 가능하다.


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
<title>Hello Millky</title>
</head>
<body>
<!--/* <xmp> */-->
<h2>Hello! <span th:text="${name}">${name}</span></h2>
<div>Thymeleaf version</div>
</body>
</html>


역시 bootRun을 하고 http://localhost:8080/hello 에 접속해보자.




잘나온다 ㅋ




지금까지 bootRun으로 돌리는 법을 알아봤다.

외부 톰켓에 돌리기 위해서는 추가적인 작업이 조금 필요하다.


pom.xml에 패키징 부분이 war로 정의되어 있어야 내부 이클립스에서 인식이 된다.


<packaging>war</packaging>


그리고 SpringBootServletInitializer 를 상속받은 class를 하나 만든다.


package com.millky.blog.application.configuration;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import com.millky.blog.Booter;
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Booter.class);
}
}



그리고 마지막으로 pom.xml에 아래 부분을 추가 해준다.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

(단 벨로시티는 요부분이 없어도 외부 톰캣에서 돌아간다.)


이렇게 하면 톰켓으로 프로젝트를 올릴수 있다. (이부분은 다음에~)




지금까지 스프링 부트를 이용하여 뷰를 표하시는 몇가지 방법을 알아 보았다.

이 문서에서는 어떠한 뷰에 대해서 배우지 않았다.(문법 및 기타...)
단지 스프링 부트를 이용하면 아렇게 간단하게 뷰 설정이 가능하다는 것이다.


사실 3가지나 설명하느라 이글이 복잡해 보이지만.
하나만 결정해서 사용한다면. 정말 간단하게 시작이 가능하다.

특히 Velocity 의 경우 
pom.xml 에 spring-boot-starter-velocity 만 정의되어 있으면 바로 사용 가능하다.(아. 단 툴 박스 사용시. 다른 이슈가 있간하다;;)


결론은 스프링 부트에서 지원하는 뷰를 사용하면 쉽게 웹 UI부분 개발이 가능하다는 것이다.


그리고 중요한 순간이 왔다.
앞으로 스프링 불로그를 어떤 뷰를 이용하여 만들어 갈 것인가를 졀정하는 순간 말이다 ^^;;

일단 내 생각은 JSP(+JSTL)를 이용할 생각이다.

쉽기는 벨로시티가 가장 쉬울 것 같은데. STS에 기본적으로 에디터가 없어 플러그인을 깔아야 한다.
사실 플러그인까는것은 일도 아니지만.. 또하나 JSP를 선택한 이유는..

그래도 표준이라는 점이다.


그럼 이 글은 여기서 정리하고.. 다음에는 JSP로 기본적인 블로그 뷰를 만들어 보겠다.


다른글 : http://millky.com/@origoni/folder/30/post/list

코드는 : https://github.com/origoni/Spring-Blog


아래 이미지 출처 : http://www.slideshare.net/BhagwatKumar/spring-bootlatest



qktmxh 2016-08-25 11:26:31

안녕하세요... 블로그 올린 글 따라하면서 JSP부터 막히기 시작했는데요...
에러 코드는 아래와 같습니다
2016-08-25 11:23:00.550 ERROR 5816 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Circular view path [hello]: would dispatch back to the current handler URL [/hello] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)] with root cause

javax.servlet.ServletException: Circular view path [hello]: would dispatch back to the current handler URL [/hello] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
at org.springframework.web.servlet.view.InternalResourceView.prepareForRendering(InternalResourceView.java:205) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:145) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1257) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.4.jar:8.5.4]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:522) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1110) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:785) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1425) [tomcat-embed-core-8.5.4.jar:8.5.4]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.4.jar:8.5.4]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_73]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_73]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.4.jar:8.5.4]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_73]


검색해보니 /hello 와 리턴값이 같아서라는데... jsp를 hellojsp로 교체하고 리턴값을 hellojsp로 하면 에러코드는 안나오는데 웹페이지에
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Thu Aug 25 11:27:24 KST 2016
There was an unexpected error (type=Not Found, status=404).
No message available

이렇게 나오는데요... 혹시 원인을 좀 알 수 있을까요...
개발 툴은 인텔리J14 버전 사용하고 있습니다~

valenti1424 2018-04-02 15:06:00

저도 같은 에러로 좀 찾아 봤는데, 최신 버전에서는
jsp 설정을 application.properties 파일에서
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
와 같이 설정해야 한다고 합니다. 위와 같이 하니 해결 되었습니다.
https://stackoverflow.com/questions/29782915/spring-boot-jsp-404
back to top