IntelliJ에서 SpringBoot 프로젝트 생성하기

서론

Spring은 J2EE나 JEE로 알려진 자바 엔터프라이즈 에디션을 경량화하기 위해 시작되었다. 스프링은 무거운 엔터프라이즈 자바 빈(EJB)로 컴포넌트를 개발하지 않았다. 그 대신 의존성 주입(DI)과 관점 지향 프로그래밍(AOP)을 활용해서 EJB의 기능을 평범한 자바 객체(POJO)로 구현할 수 있게 하여 간단하게 엔터프라이즈 자바 개발에 접근할 수 있도록 했다.
컴포넌트 코드 작성은 가벼워졌지만, Spring Framework 기반의 웹 프로젝트를 진행하게되면 최초 개발 구성(설정)하는 부분에 많은 시간이 소모되었다. 결국 애플리케이션 로직 작성이 아닌 프로젝트 구성 작업에 쓰는 시간이 많이 Gk소모되는 것이다.
SpringBoot는 Spring의 복잡한 설정을 최소화하여 빠르게 프로젝트 개발을 시작할 수 있게 해준다. 이 포스팅에서는 IntelliJ에서 SpringBoot로 웹 프로젝트를 시작하는 방법을 소개하며 SpringBoot의 특징을 소개한다.

IntelliJ에서 SpringBoot 프로젝트 생성

Spring Initializr는 SpringBoot 프로젝트 구조를 만드는 웹 애플리케이션이다. 기본적인 프로젝트 구조와 코드를 빌드하는 데 필요한 maven이나 gradle 빌드 명세를 만들어준다. 그러므로 Spring Initializr가 만든 프젝트에 애플리케이션 코드만 작성하면 된다.
Spring Initializr는 웹 기반 인터페이스, Spring Tool Suite(STS), IntelliJ IDE, SpringBoot CLI로 사용할 수 있다. 그 중 IntelliJ를 사용해 프로젝트를 생성해보자.

IntelliJ를 시작하여 Create New Project를 선택하고 새로운 프로젝트 다이얼로그를 연다. New Project 다이얼로그에서 Spring Initializr 프로젝트를 선택하고 자바 SDK를 설정한 후 Next 버튼을 누른다.

두 번째 화면에서는 프로젝트 이름, 빌드할 때 maven과 gradle 중 어느 것을 사용할지, 자바 버전 등 프로젝트의 기본적인 사항을 물어본다. 프로젝트 정보를 입력하고 Next 버튼을 누른다.

세 번째 화면에서는 프로젝트에서 필요한 종류의 의존성을 추가한다. Web, Thymeleaf, JPA, H2를 선택한 후 Next 버튼을 누른다.

다음으로 프로젝트가 저장되는 경로를 지정한다.

Gradle 설정을 지정한다.

코드 작성

도메인 정의 (Diary.java)

src/main/java/com.example.demo/Diary.java 파일을 작성한다.
일기를 나타내는 엔티티 정의한다. 간단하게 id, title, ocntent 필드를 갖고 있는 POJO 객체로 만든다. @Entity 어노테이션을 붙여 클래스를 JPA 엔티티로 지정했고, id 필드에는 @Id@GeneratedValue 어노테이션을 붙여 엔티티의 유일성을 식별하고 자동으로 값을 제공하는 필드로 지정했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.example.demo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Diary {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String user;
private String title;
private String content;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getUser() {
return user;
}

public void setUser(String user) {
this.user = user;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}

레파지토리 인터페이스 선언 (DiaryListRepository.java)

src/main/java/com.example.demo/DiaryListRepository.java 파일을 작성한다.
데이터베이스에 Diary 객체를 저장할 수 있는 레파지토리를 선언한다. 스프링 JPA를 사용하므로 스프링 데이터 JAP의 인터페이스를 상속하여 인터페이스를 만든다. JpaRepository 인터페이스는 타입 매개변수 두 개를 받는다. 첫 번째는 레파지토리가 사용할 도메인 타입, 두번 째는 클래스의 ID 프로퍼티 타입이다. 지정한 유저의 이름으로 도서 목록을 검색하는 findByUser() 메서드를 추가했다.
DiaryListRepository는 JpaRepository 인터페이스를 상속받아 18개의 메서드를 구현해야 한다. 그러나 스프링 데이터는 레파지토리를 인터페이스로 정의만 해도 잘 작동할 수 있게 런타임 시에 자동으로 구현해준다.

1
2
3
4
5
6
7
8
9
10
package com.example.demo;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

public interface DiaryListRepository extends JpaRepository<Diary, Long> {

List<Diary> findByUser(String user);
}

일기 목록 애플리케이션의 스프링 MVC 컨트롤러 (DiaryListController.java)

src/main/java/com.example.demo/DiaryListController.java 파일을 작성한다.
클래스에 @Controller 어노테이션을 추가하면, 자동 컴포넌트 검색으로 DiaryListController를 발견해 자동으로 스프링 애플리케이션 컨텍스트에 빈으로 등록한다. 요청을 처리하는 모든 메서드를 기본 URL 경로인 /로 매핑하기 위해 @RequestMapping 어노테이션을 붙였다.
usersDiarys() 메서드는 “diaryList”를 논리적 뷰 이름으로 반환한다. 그러므로 이 뷰도 만들어야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.example.demo;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

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

private static final String user="jongmin";

private DiaryListRepository diaryListRepository;

@Autowired
public DiaryListController(DiaryListRepository diaryListRepository) {
this.diaryListRepository = diaryListRepository;
}

@RequestMapping(method= RequestMethod.GET)
public String usersDiarys(Model model) {
List<Diary> diaryList = diaryListRepository.findByUser(user);
if (diaryList != null) {
model.addAttribute("diarys", diaryList);
}
return "diaryList";
}

@RequestMapping(method = RequestMethod.POST)
public String addToReadingList(Diary diary) {
diary.setUser(user);
diaryListRepository.save(diary);
return "redirect:/";
}
}

일기 목록을 보여주는 Thymeleaf 탬플릿 (diaryList.html)

src/main/resources/template/diaryList.html 파일을 작성한다.
유저의 일기 목록 부분과 일기를 일기 목록에 추가할 때 사용하는 입력 폼을 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.springframework.org/schema/data/jaxb">
<head>
<meta charset="UTF-8" />
<title>일기 리스트</title>
</head>
<body>
<h2>일기 목록</h2>
<div th:unless="${#lists.isEmpty(diarys)}">
<dl th:each="diary : ${diarys}">
<dt>
<span th:if="${diary.title}" th:text="${diary.title}">Title</span>
</dt>
<dd>
<span th:if="${diary.content}" th:text="${diary.content}">Content</span>
</dd>
</dl>
</div>

<hr />

<h3>일기 작성</h3>
<form method="POST" th:action="@{/}">
<label for="title">Title:</label>
<input type="text" name="title" size="50" /><br />
<label for="content">Content:</label>
<input type="text" name="content" size="100" /><br />
<input type="submit" value="추가" />
</form>
</body>
</html>

실행 결과

SpringBoot 특징 살펴보기

SpringBoot를 이용해 간단한 애플리케이션을 만들어 보았다. 이 애플리케이션을 바탕으로 SpringBoot의 특징을 알아보자.

스타터 의존성

처음 프로젝트를 생성하며 Spring Initializr에서 필요한 Dependencies들(Web, Thymeleaf, JPA, H2)을 쉽게 추가했었다. 만약 이런 스타터 의존성이 없었다면, 애플리케이션을 개발하기도 전에 build.gradle 또는 pom.xml에서 필요한 Dependencies를 직접 추가해야했을 것이다. (또햔, 여러 의존성들 사이에 잘 호환이 되는지도 확인해야 한다.)

프로젝트의 build.gradle 코드를 잠시 살펴보자.

1
2
3
4
5
6
7
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
runtime('com.h2database:h2')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

Spring Initializr에서 체크했던 의존성들이 gradle에 추가되어 있는것을 볼 수 있다. 또한 각 라이브러리의 버전이 명시되어 있지 않은데, 이는 SpringBoot 버전에 따라 스타터 의존성 버전이 결정되기 때문이다. 즉, 사용자는 스타터 의존성만 지정하면 어떤 라이브러리와 어떤 버전을 사용해야 하는지 걱정없이 구성에서 자유로워질 수 있는 것이다.

자동 구성

SpringBoot Auto-configuration스프링 구성을 적용해야 할지 말지를 결정하는 요인들을 판단하는 런타임 과정이다. 애플리케이션이 시작될 때마다 스프링 부트는 보안, 통합, 데이터 저장, 웹 개발 영역 등을 커버하기 위해 자도성에서 대략 200가지 정도 결정을 내린다. 이 자동 구성 덕분에 필요한 상황이 아니면 명시적으로 구성을 작성하지 않아도 된다.

참고