스프링 부트 일기 (Spring Boot Journal)
개발자가 매일 자신이 한 일을 짤막하게 기록하는 간단한 일기 응용 프로그램
[프로젝트]
STS에서 스프링 부트 일기 앱을 만들어보자.
1. Spring Starter Project
File > New > Other
Spring Boot > Spring Starter Project
아래와 같이 Name, Type, Packaging 등을 입력하고 Next
2. Spring Starter Project 마법사 - 스프링 부트 의존체
일기 앱에서 쓸 기술을 선택하는 과정이다.
아래와 같이 4개의 의존체를 담아주자.
- 데이터 - JPA
- 템플릿 엔진 - 타임리프(Thymeleaf)
- 웹 - Web
- 데이터베이스 - H2
3. 스프링 부트 프로젝트 폴더 구조
잠시 기다리면 아래와 같이 폴더 구조가 생성된다.
4. 폼(pom.xml) 파일
pom.xml 파일을 열어보자.
2단계에서 담아준 의존체들이 담겨져 있는 것을 확인할 수 있다.
- spring-boot-starter-data-jpa : 데이터 퍼시스턴스를 처리할 데이터 기술
- spring-boot-starter-thymeleaf : HTML 페이지를 화면에 그릴 템플릿 엔진
- spring-boot-starter-web : 웹 기술
- h2 : 인메모리 DB 엔진
spring-boot-starter-test : 단위와 통합 테스트를 도와줄 테스트 프레임워크
5. Journal 클래스 작성
com.apress.spring.domain.Journal 클래스 생성하기
이 클래스는 일기 레코드에 해당하는 클래스이다.
com.apress.spring > New > Class
Package와 Name을 아래와 같이 수정하고 Finish
Journal.java 코드
package com.apress.spring.domain;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Transient;
@Entity
public class Journal {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id; // 일기 작성자 id
private String title; // 일기 제목
private Date created; // 일기 작성일
private String summary; // 일기 내용
@Transient
private SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
// 일반 생성자
public Journal(String title, String summary, String date) throws ParseException {
this.title = title;
this.summary = summary;
this.created = format.parse(date);
}
// 무인자 생성자
Journal() {}
// 변수마다 get/set 메서드
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
// 작성일 created를 format변수에 저장해둔 형식으로 반환하는 메서드
public String getCreatedAsShort() {
return format.format(created);
}
// toString 메서드 재정의
public String toString() {
StringBuilder value = new StringBuilder("JournalEntry(");
value.append("Id: ");
value.append(id);
value.append(", 제목: ");
value.append(title);
value.append(", 요약: ");
value.append(summary);
value.append(", 일자: ");
value.append(getCreatedAsShort());
value.append(")");
return value.toString();
}
}
애너테이션
Journal 클래스는 DB에 저장 가능한 JPA 엔티티이므로 클래스 위에 @Entity 애너테이션을 표시하고, 변수들 위에 @Id, @GeneratedValue 애너테이션을 표시한다. 다만 변수 중에서 format은 날짜 형식을 가리키는 프로퍼티여서 DB에 저장하지 않아야 한다. @Transient 애너테이션을 붙이면 JPA 엔진은 값을 저장하지 않고 무시한다.
생성자
- DB 레코드를 추가할 때 쓸 일반 생성자
- JPA 엔진에서 필요한 무인자(no-argument) 생성자
6. JournalRepository 인터페이스 작성
com.apress.spring.repository.JournalRepository 인터페이스 생성하기
이 인터페이스는 일기 레코드를 DB에 저장하는 코드를 가진다.
JPA 기술을 쓰기로 했기때문에 JpaRepository를 상속한 인터페이스가 필요하다.
com.apress.spring > New > Interface
Package와 Name을 아래와 같이 수정하고 Finish
JournalRepository.java 코드
package com.apress.spring.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.apress.spring.domain.Journal;
public interface JournalRepository extends JpaRepository<Journal, Long> {
}
JpaRepository
JpaRepository는 표지 인터페이스로, 스프링 데이터 레포지터리 엔진이 자동 인지한다.
기본 CRUD (Create 생성, Read 조회, Update 수정, Delete 삭제)뿐만 아니라 커스텀 메서드 구현에 필요한 프락시 클래스를 적용하기 위해 쓰인다.
JpaRepository는 데이터 정렬, 페이징 같은 액션을 추가할 때 간편한 구현 로직을 지니고 있다.
7. JournalController 클래스 작성
com.apress.spring.web.JournalController 클래스 생성하기
이 클래스는 일기 레코드를 전부 조회하는 웹 컨트롤러이다.
com.apress.spring > New > Class
Package와 Name을 아래와 같이 수정하고 Finish
JournalController.java 코드
package com.apress.spring.web;
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 com.apress.spring.repository.JournalRepository;
@Controller
public class JournalController {
@Autowired
JournalRepository repo;
@RequestMapping("/")
public String index(Model model) {
model.addAttribute("journal", repo.findAll());
return "index";
}
}
애너테이션
스프링 MVC 엔진이 웹 컨트롤러로 취급할 수 있게 클래스 위에 @Controller를 붙인다.
@Autowired는 JournalRepository 타입의 repo 변수를 자동 연결해서 index 메서드가 JournalRepository의 메서드를 갖다 쓸 수 있게 한다. JournalRepository는 JpaRepository를 상속했기 때문에 JpaRepository의 메서드인 findAll( )을 repo 변수로 가져올 수 있다.
index 메서드 위에는 @RequestMapping 애너테이션을 붙여서 기본 경로(/) 요청을 담당하는 핸들러로 임명한다.
스프링은 Model 타입의 파라미터를 생성하고 여기에 repo.findAll( ) 메서드의 호출 결과를 journal이라는 이름의 속성으로 추가한다. JournalRepository는 JpaRepository를 상속한 인터페이스라서 자신의 부모가 물려준 여러 가지 기본 메서드를 가지고 있는데, findAll 역시 DB에 저장된 모든 레코드를 반환하는 JpaRepository 메서드이다.
Controller → View
마지막 return문에서 index라는 페이지를 가리키는 인덱스명을 반환하면 스프링 MVC 엔진은 템플릿 폴더에서 index.html 파일을 찾는다.
8. index.html 파일 작성
src/main/resources/templates 폴더에 index.html 파일 생성
이 파일은 타임리프 엔진이 렌더링하는 파일이다.
templates > New > Other
Web > HTML File
Finish
index.html 코드
<!DOCTYPE html>
<html lang="en-US" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"></meta>
<meta http-equiv="Content-Type" content="text/html"></meta>
<title>Spring Boot Journal</title>
</head>
<body>
<h1>스프링 부트 일기</h1>
<span th:each="entry : ${journal}"><br>
<span th:text="${entry.title}">제목</span><br>
<span th:text="${entry.createdAsShort}">에 작성.</span><br>
<span th:text="${entry.summary}">요약</span><br>
<br>
</span>
</body>
</html>
html 태그에 XML 이름공간(namespace)인 xmlns가 명시되어 있다.
th:each 엘리먼트의 entry 변수는 각 일기 레코드를 나타낸다.
일기 레코드를 컬렉션 형태로 가져와서 레코드 개수만큼 컬렉션을 순회하면서 각 태그를 렌더링한다.
개별 레코드 속성은 th:text 엘리먼트로 접근한다.
9. CSS, 자바스크립트, 이미지 등을 static 폴더에 추가한다.
생략
10. SpringBootJournalApplication 클래스 작성 (메인 애플리케이션)
com.apress.spring.SpringBootJournalApplication
이 클래스는 Spring Starter Project 마법사가 프로젝트 생성 시 만들어준 클래스이다.
SpringBootJournalApplication.java 코드
package com.apress.spring;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.apress.spring.domain.Journal;
import com.apress.spring.repository.JournalRepository;
@SpringBootApplication
public class SpringBootJournalApplication {
@Bean
InitializingBean saveData(JournalRepository repo) {
return () -> {
repo.save(new Journal("스프링 부트 입문", "오늘부터 스프링 부트 공부하기 시작했다", "08/10/2021"));
repo.save(new Journal("간단한 스프링 부트 프로젝트", "스프링 부트 프로젝트를 처음 만들어보았다", "08/11/2021"));
repo.save(new Journal("스프링 부트 해부", "스프링 부트를 자세히 살펴보았다", "09/10/2021"));
repo.save(new Journal("스프링 부트 클라우드", "클라우드 파운드리를 응용한 스프링 부트를 공부했다", "10/10/2021"));
};
}
public static void main(String[] args) {
SpringApplication.run(SpringBootJournalApplication.class, args);
}
}
saveData 메서드가 반환하는 InitializingBean은 스프링 엔진이 인스턴스 생성 후 초기화할 때 항상 호출하는 특수 클래스이다. saveData 메서드는 앱이 시동 준비를 마치기 전에 실행된다.
11. 실행
SpringBootJournalApplication > Run As > Spring Boot App
실행 완료
브라우저를 열고 http://localhost:8080에 접속하면 다음과 같은 화면이 나온다.
이클립스 터미널 창에서 Ctrl + C 를 누르면 앱이 종료된다.
스프링 부트 일기 웹 애플리케이션을 API 형태로 서비스하고 싶다면?
http://localhost:8080/journal URL로 접속해서 JSON 형식으로 테이터로 받을 수 있게 해보자.
/journal 요청을 받아 JSON 데이터로 응답하는 메서드를 JournalController 클래스에 추가
package com.apress.spring.web;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.apress.spring.domain.Journal;
import com.apress.spring.repository.JournalRepository;
@Controller
public class JournalController {
@Autowired
JournalRepository repo;
@RequestMapping(value="/journal", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
public @ResponseBody List<Journal> getJournal() {
return repo.findAll();
}
@RequestMapping("/")
public String index(Model model) {
model.addAttribute("journal", repo.findAll());
return "index";
}
}
데이터 변환은 스프링 부트가 아니라 스프링 MVC 모듈이 수행한다. 메서드에 @ResponseBody를 붙이면 스프링 MVC는 '응답 메시지를 알맞은 HTTP 메시지 변환기를 써서 JSON 데이터로 변환해야겠구나'라고 알아차린다.
따라서 getJournal 이라는 메서드에 JSON 데이터로 자동 응답하도록 @ResponseBody 애너테이션을 붙였다.
Spring Boot App을 다시 실행하고 http://localhost:8080/journal 에 접속
'BOOK REVIEW > 실전 스프링 부트 워크북' 카테고리의 다른 글
[실전 스프링 부트 워크북] CH5. 스프링과 스프링 부트 (0) | 2021.08.15 |
---|---|
[실전 스프링 부트 워크북] 스프링부트 애플리케이션 구성 알아보기 / 스프링 부트 프로젝트 따라하기 (0) | 2021.08.14 |
[실전 스프링 부트 워크북] CH3 스프링 부트 자동 구성과 주요 기능 (0) | 2021.08.13 |
[실전 스프링 부트 워크북] CH2 스프링 부트 앱 첫 개발 (0) | 2021.08.10 |
[실전 스프링 부트 워크북] CH1 스프링 부트 들어가기 (0) | 2021.08.10 |