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를 이용하면 쉽게 작성할 수 있다. (아래의 링크)

https://start.spring.io/

 

컨트롤러 작성

@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로 사용할 수 있는 라이브러리 검색하여 사용 가능

http://www.webjars.org/ 

 

예를 들어 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

+ Recent posts