본문 바로가기

개발노트/JAVA&JSP

[IntelliJ + Gradle + SpringBoot] 그래들로 스프링부트 웹 프로젝트를 빌드해보자

배경

그래들에 대해 얘기만 많이 들어봤지 써본적은 없어서 이번 기회에 공부를 해보기로 했다.

기왕이면 그래들을 공부하면서 추가적으로 스프링 부트에 대해서도 익혀두면 여러모로 좋을 것 같아서 같이 진행하기로 함.

다른 컴퓨터에서도 작업을 할 수 있도록 github 에 올려놓았다. 혹시 힌트가 필요하여 이 글을 보시는 분들 중 소스가 필요하면 깃헙 주소를 확인해주세요.

(https://github.com/starrybleu/springBoot-gradle-demo)




개발 환경

OS : OSX 10.12.5 Sierra

IDE : IntelliJ 2017.3

Gradle : v3.5 (with Groovy v2.4.10 & Ant v1.9.6)

JVM : 1.8.0._101

Spring Boot : v1.5.4

Spring : v4.3.9



진행 순서

1. 프로젝트 생성

File | New | Project | Spring Initializr

Project SDK 설정 및 Default URL 선택 후 다음


Group : 프로젝트의 Artifact 그룹(프로젝트 저장소와 관련이 있다고 한다) - 아직 정확한 의미는 모르겠음...

Artifact : 아마 프로젝트의 artifact를 의미하는 듯

Type : 빌드 타입인 것 같음

Packaging : 배포 패키징 형식(?) war 또는 jar

Java Version : 자바 버전

Language : 프로젝트에 사용할 언어(자바도 있고 그루비,코틀린 등도 있고...)

Version : 이 프로젝트의 버전(자기가 관리하는 대로 적으면 됨. 간단하게 v1.0.0 으로 적는게 편하겠지?)

Name : 프로젝트 이름

Description : 이 프로젝트에 대한 설명

Package : (정확하게 뭘 의미하는지 모름) 패키지 (패키지라 그래서 자동으로 소스 패키지를 미리 만들어주는 건가 했는데 그건 아니었음)



의존성을 미리 설정하는 것인데 일단 Web을 추가하고, Template Engines 는 MVC패턴에서 View 로 사용될 포맷이라 보면 되는 듯하다.

내가 해볼 땐 일단 Thymeleaf 로 추가만 해보았다.

실제로는 View로 JSP를 사용하였음.



프로젝트 이름과 소스파일들이 위치할 경로.

인텔리J에서의 'Project'는 이클립스에서의 'Workspace'와 비슷한 개념이다.

(아까 프로젝트 Name을 설정한 것 같은데 왜 또 여기서 설정을 하는 걸까... 그래서 앞에서 설정한 Group, Name 등은 도큐먼트를 찾아보고 다시 정리해야겠다.)



그래들을로 부터 모듈을 가져오는 설정인데,

Default로 설정된 것에서 Use auto-import와 Create directories for empty content roots automatically 에 체크해주었다.

(정확하게 각 항목들이 어떤 것을 의미하는 지 모르겠지만, 일단 자동으로 뭘 해준다고 하니 체크해보았음. 이 부분도 찾아봐야함)


그러면 아래와 같이 그래들 프로젝트로 생성이 된다.

프로젝트 스트럭쳐는


spring-gradle-demo

|+ .gradle

|+ .idea

|+ src

|+ build.gradle

|+ spring-gradle-demo.iml


의 모습으로 갖춰져 있다.


2. 그래들로 빌드

그래들로 작업을 하기 위해서는 그래들에서 지원하는 태스크(Task)를 활용해야 한다.


인텔리J에서 그래들로 빌드하기 위해서는 Gradle Tool Window 를 활용하는 것이 좋은데 아래처럼 불러올 수 있다.

(만약 프로젝트를 생성하고 이게 보이지 않으면 인텔리J를 종료했다가 켜면 보이게 된다.)



친절한 IntelliJ가 maven 저장소중 인덱싱이 되지 않은 저장소가 있다고 알려주는데, 이것 때문에 조금 고생했다.

트러블슈팅은 맨아래에 따로 기록해 뒀음.



오른쪽에 뜨는 gradle tool window 에서 build를 더블클릭하면 빌드가 수행된다.

자주 사용하는 Task가 clean, build, bootRun, run 정도 인듯?

더 고급으로 가면 모듈화된 태스크를 하나씩 필요한 것만 실행할 수도 있겠지만 일단 초보인 나는 이 4가지만 가지고 빌드를 했다.


그래들에서 스프링부트 프로젝트 빌드를 할 때 Unable to find main class 라는 에러가 발생할 수 있는데, 스프링부트에서는 부팅(booting)을 실행하는 main() 메소드를 실행할 클래스가 필요하다는 것을 알아본 뒤, 소스 최상위 패키지에 메인 메소드를 가지는 Application.java 라는 클래스를 만들어주었다. (소스 참고)




(초록색의 run 아이콘 오른쪽의 삼각자같이 생긴 아이콘을 누르면 콘솔 output을 gui 모드, cli 모드를 전환하여 확인할 수 있다.)



3. 소스

[build.gradle]

아래는 최종적으로 그래들 빌드 스크립트를 작성하는 build.gradle 파일이다.

타임리프 말고 JSP를 뷰로 사용할 것이기 때문에 타임리프 의존성은 주석처리 하였고,

Application 클래스에서 뷰 리졸버를 명시하지 않고, src/main/resources/application.yml 파일을 활용할 것이기 때문에 processResources 블럭을 아래와 같이 작성해줌.

buildscript {
ext {
springBootVersion = '1.5.4.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'application'


version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
mavenCentral()
}


dependencies {
//compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.apache.tomcat.embed:tomcat-embed-jasper')
compile('javax.servlet:jstl')
compile('org.yaml:snakeyaml')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

processResources {
filesMatching("**/application.yml") {
expand(project.properties)
}
}

JSP를 뷰로 사용하기 위해서는 dependencies 에 이 코드가 필요하다. (JSTL을 사용하지 않는다면 'javax.servlet.:jstl' 이건 없어도 되겠지.

   compile('org.apache.tomcat.embed:tomcat-embed-jasper')
compile('javax.servlet:jstl')


[src/main/resources/application.yml]

찾아보니 주로 application.properties 로 설정을 하는 사람이 많은데 계층 구조를 한 눈에 볼 수 있고, 가독성이 좋은 YAML로도 설정을 할 수 있었다.

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


[src/main/java/com/bean/controller/BoardController.java]

게시판을 만드려고 했던 의도가 있어서 컨트롤러 클래스의 이름은 BoardController 이다.

이 때, @Controller 와 Spring 4.0 부터 지원한 @RestController 는 차이점이 있으니 사용에 유의!

@RestController = @Controller + @ResponseBody 라고  알려져 있다.

하지만 기존의 스프링MVC에서는 JSP 페이지를 뷰로 반환을 하려면 String으로 JSP파일 이름을 반환해야 했는데,

@ResponseBody가 붙어버린 메소드에서는 반환 문자열 그 자체를 뷰로 반환하는 것이기 때문에, 특히 Model 객체에 Attribute 를 추가하여 반환해줘도 뷰에서는 그것을 확인할 수가 없는 경우가 있다.

package com.bean.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/")
public class BoardController {

@RequestMapping("/hello")
public String hello(Model model) {
System.out.println("hello() 실행은 됨");
model.addAttribute("message", "Hello, World!!!");
return "hello";
}

}

[src/main/java/com/Application.java]

뷰 리졸버를 여기서 바로 지정하여 사용할 수도 있다.(주석쳐진 부분)


<각 어노테이션의 의미>

@Configuration : 이 클래스가 Spring의 설정파일임을 어플리케이션 컨텍스트에게 알려줌("이 클래스는 스프링 설정을 위한 클래스이다!")


@EnableAutoConfiguration : 스프링부트가 클래스패스, 다른 Bean 설정, 의존성주입 등의 설정을 자동으로 구성하도록 함(클래스패스에 있는 빈(bean)들을 자동으로 설정해줌.


@ComponentScan : 이 클래스가 속해있는 패키지의 하위 패키지들을 스캔하여 Bean을 찾고 등록하도록 하는 것임. (spring-context.xml 에서 <context:component-scan base-package="com"> 과 같은 역할)


위의 3가지 어노테이션의 기능을 포함한 하나의 어노테이션이 있다. : @SpringBootApplication


package com; /**
* Created by mac on 2017. 6. 14..
*/
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
System.out.println("Spring Boot Started.");
}

// @Bean
// public InternalResourceViewResolver setupViewResolver() {
//
// InternalResourceViewResolver resolver = new InternalResourceViewResolver();
//
// resolver.setPrefix("/WEB-INF/view/");
// resolver.setSuffix(".jsp");
// return resolver;
// }
}



[src/main/webapp/WEB-INF/view/hello.jsp]

테스트를 위한 JSP작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
테스트 중 <br/>
${message}
</body>
</html>


4. 트러블 슈팅(Trouble shooting)

** Gradle Tool Window 가 안 보일 땐, 일단 작업 내용을 저장하고 인텔리J를 껐다가 켜면 된다.

프로젝트를 새로 생성하면 프로젝트에 그래들 플러그인 적용이 안 되서 그런 듯.


** maven repositories 인덱싱 과정 중 멈춰있어서 그래들 빌드 오류가 발생

참고 : https://stackoverflow.com/questions/15991561/intelli-j-idea-takes-forever-to-update-indices

-> 캐시 날리고 재시작후 Update 누르고 기다리면 됨 : 약 20분정도 걸린 것 같음...


** return String 만 출력되고, Model.addAttribute() 로 넣은 값이 출력되지 않는 문제

https://stackoverflow.com/questions/31410528/spring-keeps-returning-string-instead-of-jsp-html-content

-> @RestController 와 @Controller의 차이!!


** application.yml 로 뷰 리졸버 설정할 때

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

mvc: 가 꼭 있어야 함.(빼먹어서 404오류가 계속 났던 것임, 다른 블로그의 소스 참고했었는데 거기선 어떻게 된거지?)



정리

왜 개발자들이 스프링부트, 스프링부트를 강조하는지 알겠다.
xml등의 설정 없이 순수 자바만으로 개발하는 것도 좋고, 내장된 WAS를 활용할 수도 있고 개발 프로젝트 초기 환경설정에 들이는 시간을 매우 줄일 수 있는 장ㅈ머이 있는 것 같다.
그리고 그래들로 빌드를 하는 것은 재밌었다. 저 기본적인 빌드스크립트에 추가적으로 작성해야할 코드가 엄청 많을 줄 알았는데 그건 아니었다.
그래들로 빌드를 할 때도 콘솔로 에러를 확인할 수 있다.
곧 war로 패키징하여 스프링부트 내장 웹 어플리케이션 서버가 아닌 외부 웹 어플리케이션 서버에 배포를 해야겠다.


반응형