Chapter 4 스프링 부트 웹 개발
1. 스프링 부트에 대한 이해
스프링을 사용할 떄는 다양한 모듈을 설정해야 하는 데, 이러한 설정은 처음 시작하는 입장에서 부담스러움
-> 스프링 부트 등장
1) 스프링 부트의 프로젝트 레이아웃
빌드 결과 파일 모맷에 따른 폴더 규격 구분
- 결과 파일 포맷은 크게 WAR와 JAR 2가지로 구분할 수 있다.
-> 프로젝트 파일을 압축한 것
-> 각 압축 파일은 내부 폴더 규격이 다름
- WAR : WEB-INF 폴더를 기준으로 자바 클래스 폴더,, src/main/webapp 폴더에 웹 정적 자원이 위치함
스프링 부트에서 웹 자원을 실행하기 위한 규약
- 정적 HTML 파일
: src/main/resources/static
: src/main/public
- 웹 페이지 대표 아이콘
: src/main/resources/favicon.ico
- 템플릿
: src/main/resources/templetes
html - Thymeleaf
tpl - Groovy
ftl - Freemarker
vm - velocity
webJAR를 이용한 외부 프론트앤드 라이브러리 관리
- booststrap, jQuery, AngularJs 등 많은 라이브러리를 사용함
- webJAR를 이용하면 JAR 형태로 메이븐이나 그래들로 관리 가능
2) 스프링 부트 실행하기
책에서는 spring-boot-cli를 이용하여 스프링 부트를 실행하는 방법을 설명하고 있다.
일반적으로는 intellij나 eclipse를 이용하여 스프링 부트를 실행하기 때문에 넘어간다.
또한, build.gradle 역시 Spring Initializr를 이용하면 쉽게 작성할 수 있다. (아래의 링크)
컨트롤러 작성
@RestController
public class HomeController {
@RequestMapping("/")
public String hello() {
return "hello";
}
}
RestController 어노테이션을 사용하면 컨트롤러 응답 자체가 body가 되므로 별도로 뷰 페이지를 작성하지 않고 설정이 제대로 되었는지 확인할 수 있다.
메인함수
@SpringBootApplication
public class UIMain {
public static void main(String ar[]){
SpringApplication.run(UIMain.class, ar);
}
}
스프링부트는 JAR 형태로 동작하기 때문에 main 메서드를 이용하여 실행할 수 있다.
main 메서드가 실행되면 내장된 톰캣에 의해 웹페이지가 실행된다.
@SpringBootApplication 은 @ConponentScan, @Configuration, @EnableAutoConfiguration 어노테이션의 기능을 포함하는 어노테이션이다.
- @ConponentScan : 컨트롤러 클래스들을 읽을 수 있도록 스캘 패키지를 지정함
- @Configuration : 설정임을 나타냄
- @EnableAutoConfiguration : 미리 정의된 Configuration 클래스를 읽을 수 있도록한다.
gradlew bootRun을 cmd에 입력하면, 내장된 톰캣이 함께 실행되고, WebApplicationContext가 초기화된다.
스프링부트가 로드하는 빈을 확인하려면 SpringApplication.run 메서드의 반환값을 ApplicationContext에 대입하여 확인할 수 있다.
@SpringBootApplication
public class UIMain {
public static void main(String ar[]){
ApplicationContext ctx = SpringApplication.run(SpringLoadBeans.class,ar);
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for(String beanName : beanNames)
System.out.println(beanName);
}
}
2. 정적 자원 관리
1) 정적 자원 기본 설정
정적 자원 : html, css, image, javascript 등 컴파일이 필요없는 파일
웹 리소스 폴더
- /META-INF/resources, /static, /public 이 3개의 폴더를 웹 리소스 폴더라고 부른다.
- 템플릿 엔진 의존성을 클래스패스에 추가하면 스프링 부트에서는 자동으로 src/main/resources/templates 경로를 기본 경로로 인식함
이미지 URL 접근하기
WebMvcAutoConfiguration 클래스를 보면 string 배열으로 웹 리소스 폴더 경롣르이 지정되어 있음
/src/main/resources/static 경로에 images 폴덜르 만들고 이미지를 추가한다.
localhost:8080/images/이미지 파일명으로 접근하면 추가한 이미지 파일을 확인할 수 있다.
2) 웹 리소스 폴더 설정
static이나 public 폴더 이외에 다른 폴더를 지정하거나 별도의 리졸버를 추가하거나 캐시 설정을 변경하는 등의 작업
-> WebMvcConfigureAdapter 클래스의 addResourceHandlers 메서드를 오버라이드
별도의 리졸버를 추가하기 위해 WebConfig 파일을 작성한다.
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
boolean devMode = this.env.acceptsProfiles("dev");
boolean useResourceCache = !devMode;
Integer cachePeriod = devMode ? 0 : null;
registry.addResourceHandler("/assets/**")
.addResourceLocations("classpath:/assets/", "/assets/");
}
}
이때 addResourceHandlers를 오버라이드 하기 위해 WebMvcConfigureAdapter 클래스를 상속받았다.
.addResourceHandler : 호출 경로로 사용될 url 값 입력
.addResourceLocations : 실제로 파일이 위치할 폴더 경로
리소스 주소에 MD5 값 설정
- 브라우저의 캐시는 요청한 request 파일명이 같으면 동작함
- 브라우저의 캐시 제어와 상관 없이 애플리케이션에서 웹 리소스에 대한 캐시를 관리하고 싶으 경우
-> 스프링에서 제공하는 콘텐츠 버전 정책 사용
-> URL 주소에 해시값잉 추가, 캐시 주기도 별도로 설정 가능
- 마찬가지로 addResourceHandlers를 통해 설정할 수 있다.
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**")
.addResourceLocations("classpath:/assets/", "/assets/")
.setCachePeriod(3600) //60 * 60 * 24 * 365 1year
.resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
setCachePeriod : 캐시 만료일 설정
resourceChain : true로 설정하면 VersionResourceResolver와 같은 추가 설정 가능
VersionReourceResourceResolver에 addContentVersionStrategy : 리소스 파일 로드시 해시 적용
웹페이지에 버전 리소스 리졸버 적용
versionResourceResolver를 통해 웹 리소스를 호출하려면 ResourceUrlProvider 를 이용하는 것이 좋다.
@ControllerAdvice
public class ResourceAdvice {
@Autowired
private ResourceUrlProvider resourceUrlProvider;
@ModelAttribute("versionResourceResolver")
public ResourceUrlProvider versionResourceResolver() {
return this.resourceUrlProvider;
}
}
ModelAttribute 어노테이션을 통해 versionResourceResolver를 사용한다.
3. 템플릿 엔진
서버에서 페이지를 만드는 경우 템플릿 엔진을 사용하는 것이 좋다.
템플릿 엔진 : 서식(html)과 데이터(데이터베이스에 저장된 데이터)를 결합한 결과물을 만들어 주는 도구
1) 설정
build.gradle에 다음 코드를 추가
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
th.html에서 데이터를 출력할 수 있는 controller를 작성한다.
@Controller
public class UIController {
@Autowired
InMemoryProductService inMemoryProductService;
@RequestMapping(value = "/th")
public String templatePage(Model model){
model.addAttribute("message", "boot template");
return "th";
}
}
Model 객체에서 message의 값인 boot template을 출력할 것이다.
2) 속성
변수 표현식 : ${변수명} -> 변수 값 출력
객체의 속성을 출력하는 경우 : th:object = ${객체}로 객체이름을 명시, *{객체의 속성}을 통해 객체의 속성을 사용 가능
<div th:object="${product}">
<p>name: <span th:text="*{name}"></span></p>
<p>color: <span th:text="*{color}"></span></p>
<p>price: <span th:text="*{price}"></span></p>
</div>
조건문 : if, unless, case 사용가능
<div th:if="${product.price} > 3000" th:text="비싸다"/>
<div th:if="${product.price} > 1500" th:text="적당하다"/>
<div th:unless="${product.price} >3000" th:text="비싸다"/>
반복문 : each 태그를 이용하여 표현 가능
<tr th:each="prod : ${products}">
<td th:text="${prod.color}"></td>
<td th:text="${prod.productName}"></td>
<td th:text="${prod.price}"></td>
</tr>
4. WebJars를 이용한 프론트라이브러리 관리
자바스크립트 라이브러리를 서버 라이브러리처럼 통합해서 사용가능
1) WebJars 적용
다음의 웹페이지에서 webJar로 사용할 수 있는 라이브러리 검색하여 사용 가능
예를 들어 jquery와 부트스트랩을 추가하여 이용하는 경우
build.gradle에 다음의 의존성을 추가한다.
compile 'org.webjars:jquery:3.1.0'
compile 'org.webjars:bootstrap:3.3.1'
이제 html 페이지에서 다음의 코드로 사용할 수 있다.
<script type="text/javascript" src="/webjars/jquery/3.1.0/jquery.min.js"></script>
<link rel="stylesheet" href="/webjars/bootstrap/3.3.1/css/bootstrap.min.css"/>
<script src="/webjars/bootstrap/3.3.1/js/bootstrap.min.js"></script>
localhost:8080을 입력했을 때 home.html이 호출되도록 WebConfig 클래스에서 addViewControllers 메서드를 오버라이드한다.
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
addViewControllers 메서드 : 페이지에 데이터를 전달하지 않고 페이지와 URL 연결만 필요한 경우 사용함
.addViewController : 매핑될 url 지정
.setViewName : 페이지 이름을 지정
버전 정보를 축약해서 나타내는 경우
bulid.gradle에 다음을 추가
compile group: 'org.webjars', name: 'webjars-locator', version: '0.32'
html에서 다음과 같이 사용가능
<script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
2) 인터셉터 활용
3장에서 컨트롤러의 실행 시간을 계산하는 ExecuteTimeInterceptor를 다음과 같이 구현 가능
public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
String reqUri = request.getRequestURI();
System.out.println("reqUri: " + reqUri);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
long executeTime = endTime - startTime;
request.setAttribute("executeTime", executeTime);
System.out.println("[" + handler + "] executeTime: " + executeTime + "ms");
}
}
이렇게 만든 인터셉터를 WebConfig에 등록해야한다.
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(executeTimeInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/");
}
@Bean
public ExecuteTimeInterceptor executeTimeInterceptor() {
return new ExecuteTimeInterceptor();
}
}
@Bean을 통해 ExecuteTimeInterceptor의 인스턴스를 스프링 컨테이너에 등록한다.
addInterceptor : executeTimeInterceptor를 추가하고 호출될 경로를 설정.
addPathPatterns : 호출될 경로 설정
excludePathPatterns : 파라미터를 "/"으로 설정 -> 인덱스 페이지가 호출될 경우 인터셉터가 호출되지 않음
'Spring > 스프링 부트로 배우는 자바 웹 개발' 카테고리의 다른 글
Chapter 1~3 (0) | 2021.11.06 |
---|