본문 바로가기
Spring/study

spring 25강 Spring Boot와 Oracle 연동, Thymeleaf Template 적용

by avvin 2019. 7. 10.

spring 25강 Spring Boot와 Oracle 연동, Thymeleaf Template 적용



spring boot


2014년부터 개발되었으며 sprng legacy project에 비해서 설정이 매우 간소화됨


WAS(tomcat)가 포함되어 잇으므로 서버 설정이 간소화됨


아직 실무에서 많이 사용되지 않고 있지만 향후 spring legacy project를 대체하리라 예상됨



Spring Starter Project 생성




Dependencies 체크 항목


SQL : MySQL, JDBC, Mybatis 

Template Engines : Thymeleaf

Web : Web 


(버전이 달라져 체크 항목 이름이 약간 다르다)


pom.xml의 간소화



Spring02BootApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.example.spring03;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class Spring02BootApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(Spring02BootApplication.class, args);
    }
 
}
 



Spring Boot의 Main메서드라고 보면 된다.


시작 클래스 : 프로젝트 이름 + Application.java




Run as Spring Boot App 



서버로 시작하지 않아도 Tomcat Web Server 자동으로 실행돼있음



[ Console ]

***************************

APPLICATION FAILED TO START

***************************


Description:


Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.


Reason: Failed to determine a suitable driver class


: DB드라이버 설정이 안돼있어서 아직 구동이 안됨




웹 템플릿 엔진


spring boot application에서는 jsp 대신 다른 template을 사용하는 것을 권장(jsp도 템플릿의 일종)


이번 실습에서는 tamplate 중 타임리트 (Thyme leaf)를 활용하여 실습


스프링 MVC와의 통합 모듈을 제공하며, 애플리케이션에서 JSP로 만든 기능들을 완전히 대체할 수 있음


타임리프의 목표는 세련되고 잘 다듬어진 템플릿을 제공하는 것



src/main/resources - static 폴더 : 리소스 파일 ( js, image, css ... ) 같은 정적인 요소들을 저장하는 폴더

src/main/resources - templates : jsp가 아닌, 템플릿 위치


application.properties : 스프링 부트 설정 파일 (servlet-context.xml , root-context.xml, web.xml 없음)





jsp를 view로 사용하는 방법


1. src/main 하위에 디렉토리 추가

  : src/main/webapp/WEB-INF/view 로 Spring Legacy 프로젝트처럼 view 디렉토리 생성


2. application.propertoes 설정


3. pom.xml에서 thymeleaf 라이브러리 주석처리 //두가지를 같이 쓸 순 없음




실습예제


pom.xml


<dependencies> 위에 <repositories> 추가 (오라클 라이브러리 저장소)

(ojdbc6다운로드를 위한 소스코드는 저장소에 잘 안올라온다)


<!-- 추가된 부분 (ojdbc6 다운로드를 위한 저장소) -->

<repositories>

<repository>

<id>codeIds</id>

<url>https://code.Ids.org/nexus/content/groups/main-repo</url>

</repository>

</repositories>



artifactId


javax.inject

spring-boot-devtools : Spring Boot에는 없는 auto restart기능 추가

tomcat-embed-jasper

jstl

ojdbc6

spring-boot-starter-jdbc

mybatis

mybatis-spring

mybatis-spring-boot-starter

mysql-connector-java

spring-boot-starter-web


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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
    <dependencies>
        <!-- @Inject -->
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        <!-- 타임리프 템플릿 관련 라이브러리 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency
        <!-- spring boot auto restart(설정, 클래스가 바뀌면 auto restart) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- jsp 라이브러리 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- jstl 라이브러리 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <!-- 오라클 라이브러리 -->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.3</version>
        </dependency>
        <!-- 스프링 부트용 jdbc 라이브러리 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>




src/main/resources.application.properties



-Properties - Resource - Text file encoding - UTF-8



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
#:xe 아니고 /xe  xe는 express edition
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=spring
spring.datasource.password=123456
 
# http port
# change port number (default 8080)
server.port=80
//jsp주석 처리하고 타임리프만 사용  
#spring.mvc.view.prefix=/WEB-INF/views/
#spring.mvc.view.suffix=.jsp
#server.jsp-servlet.init-parameters.development=true
 
server.error.whitelabel.enabled=false
spring.thymeleaf.cache=false



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
#:xe 아니고 /xe  xe는 express edition
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=spring
spring.datasource.password=123456
 
# http port
# change port number (default 8080)
server.port=80
//jsp 사용하고 타임리프 주석처리
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
server.jsp-servlet.init-parameters.development=true
 
server.error.whitelabel.enabled=false
#spring.thymeleaf.cache=false



>>Thymeleaf와 jsp 중 하나만 선택해서 사용하고 사용하지 않는 것는 주석처리


>>jsp이용할 경우 Tymeleaf dependency도 주석처리


@SpringBootApplication : 스프링부트 시작페이지임을 명시한 빈 등록 어노테이션


@MapperScan




@Bean이 붙은 SqlSessionFactory를 리턴하는 매서드에 데이터 소스, 리소스 설정

보기)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Bean
 
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) Exception {
 
SqlSessionFactoryBean = new SqlSessionFactoryBean();
 
//데이터소스 설정(application.properties 참조)
bean.setDataSource(dataSource);
 
//xml mapper를 사용할 경우 아래 코드 추가
//import org.springframework.core.io.Resource;
//Resource[] res = new PathMatchingResourcePatternResolver()
//.getResources("classpath:mappers/*Mapper.xml");
//bean.setMapperLocations(res);
 
return bean,getObject();
 
}




SpringBootApplication.java

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
package com.example.spring03_boot;
 
import javax.sql.DataSource;
 
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
 
@SpringBootApplication
//sql 코드를 분리하지 않고 자바코드와 함께 쓸 예정
//(" ") : sql mapper의 위치 지정
@MapperScan("com.example.spring03_boot.model") //model패키지의 DAO를 스캔할 예정
public class Spring03BootApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(Spring03BootApplication.class, args);
    }
//javax.sql.DataSource     
// DataSource =>    SqlSessionFactory 
//   => SqlSessionTemplate => SqlSession    
    @Bean //자바코드로 bean을 등록
    //Legacy 프로젝트에서는 xml로 작성된 태그 내용을 읽어서 
    //자바코드로 바꿔 객체를 메모리에 올리는 작업이 이루어짐
    //Boot에서는 주로 @Bean어노테이션을 사용하여 자바코드로 설정
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        
        SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
    
        bean.setDataSource(dataSource); //데이터소스 설정
        
        return bean.getObject();
    }
    @Bean
    public SqlSessionTemplate sqlSession(SqlSessionFactory factory) {
        return new SqlSessionTemplate(factory);
    }
}


>>Mybatis ( SqlSession ) 사용을 위한 셋팅




HelloController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.spring03_boot.controller;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
 
@Controller
public class HelloController {
 
 
    @RequestMapping("/hello.do")
    public ModelAndView hello(ModelAndView mav) {
        
        //application.properties에서  view의 prefix, suffix 설정함
        ///WEB-INF/views/뷰이름.jsp
        mav.setViewName("hello"); //뷰의 이름
        
        mav.addObject("message","스프링 부트 애플리케이션");
        
        return mav;
    }
}



 hello.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${message}
</body>
</html>
cs



Spring Boot App 실행시 웹브라우저를 자동으로 띄워주지 않아 브라우저에 수동으로 주소를 입력해야한다.


http://localhost/hello.do 또는 http://localhost:80/hello.do

(프로젝트명이 포함되지 않는다.)









실습 예제 ) 방명록


템플릿(Thymeleaf) 사용 설정 > 에러 페이지 > 방명록 테이블 작성 > 방명록 DTO > DAO ( sql, main에서 @MapperScan ) 


> Service(Impl) > Controller 



1. 템플릿(Thymeleaf) 사용 설정


1)pom에서 Thymeleaf dependency 주석 풀기  


2) application.properties에서 jsp사용 막고 템플릿 사용 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
#:xe 아니고 /xe  xe는 express edition
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=spring
spring.datasource.password=123456
 
# http port
# change port number (default 8080)
server.port=80
 
#spring.mvc.view.prefix=/WEB-INF/views/
#spring.mvc.view.suffix=.jsp
#server.jsp-servlet.init-parameters.development=true
 
server.error.whitelabel.enabled=false
spring.thymeleaf.cache=false



spring.thymeleaf.cache=false


타임리프 html 파일이 수정되면 서버를 재부팅해야하는데, 

자바코드가 변경되는것도 아니고 페이지가 변경되는데 서버 재부팅을 하는 것은 번거로우므로 자동으로 재시작되게 하는 옵션


appliacation.properties

1
2
3
#스프링 부트 기본 에러 페이지가 아닌 직접 정의해서 쓰려면 false
server.error.whitelabel.enabled=false
 





false이므로 직접 정의해서 쓸 페이지 작성


src/main/templates

error.html

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<body>
<h3>에러가 발생했습니다.</h3>
</body>
</html>
cs






방명록.sql

--테이블 만들기

create table guestbook (


idx number not null primary key,

name varchar2(50) not null,

email varchar2(50) not null,

passwd varchar2(50) not null,

content varchar2(4000) not null,

post_date date default sysdate


);



--시퀀스

create sequence guestbook_seq



start with 1

increment by 1

nomaxvalue 

nocache; 

-- 시퀀스를 사용하면 빠른 발급을 위해 캐시 메모리에 시퀀스 번호를 10-20개 정도 올려놓는데

-- 서버가 중간에 꺼지면 번호를 건너뛰는 경우가 생긴다.

-- nochache 설정을 해두면 시퀀스 번호 발급이 조금 느리더라도 번호를 건너뛸 일은 없다.



insert into guestbook (idx, name, email, passwd, content) values

( guestbook_seq.nextval, 'kim', 'kim@nate.com', '1234', '방명록' );


select * from guestbook order by idx desc;




commit;




com.example.spring03_boot.model.dto


GuestbookDTO.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.spring03_boot.model.dto;
 
import java.util.Date;
 
public class GuestbookDTO {
    private int idx;
    private String name;
    private String email;
    private String content;
    private String passwd;
    private Date post_date; //java.util.Date
    //getter,setter, toString()
 
...GETTER / SETTER / TO_STRING 코드생략...    
}



guestbook 테이블과 동일하게 작성


GuestbookDAO

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
package com.example.spring03_boot.model.dao;
 
import java.util.List;
 
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
 
import com.example.spring03_boot.model.dto.GuestbookDTO;
 
// mybatis interface mapper (SQL 명령어가 포함된 코드)
 
public interface GuestbookDAO {
    @Select("select * from guestbook order by idx desc")
    public List<GuestbookDTO> list();
    
    @Insert("insert into guestbook "
+ "(idx,name,email,passwd,content)" 
+" values "
+"(guestbook_seq.nextval, #{name}, #{email}"
+ ", #{passwd},#{content})")
    public void insert(GuestbookDTO dto);
    
    @Select("select * from guestbook where idx=#{idx}")
    public GuestbookDTO view(int idx);
    
    @Update("update guestbook set "
            + " name=#{name}, email=#{email}, content=#{content}"
            + " where idx=#{idx}")
    public void update(GuestbookDTO dto);
 
    @Delete("delete from guestbook where idx=#{idx}")
    public void delete(int idx);
    
}



SpringBootApplication.java에서 설정한 @MapperScan("com.example.spring03.model")

model 패키지에 있는 @Select / @Insert / @Update / @Delete  태그를 모두 스캔



GuestbookService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.spring03_boot.service;
 
import java.util.List;
 
import com.example.spring03_boot.model.dto.GuestbookDTO;
 
public interface GuestbookService {
    public List<GuestbookDTO> list(); //목록
    public void insert(GuestbookDTO dto); //추가
    public GuestbookDTO view(int idx); //상세화면
    public void update(GuestbookDTO dto); //수정
    public void delete(int idx); //삭제
}



GuestbookSeriveImpl.java

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
package com.example.spring03_boot.service;
 
import java.util.List;
 
import javax.inject.Inject;
 
import org.springframework.stereotype.Service;
 
import com.example.spring03_boot.model.dao.GuestbookDAO;
import com.example.spring03_boot.model.dto.GuestbookDTO;
 
@Service //service bean으로 등록
public class GuestbookServiceImpl implements GuestbookService {
 
    @Inject
    GuestbookDAO guestbookDao;
    
    @Override
    public List<GuestbookDTO> list() {
        return guestbookDao.list(); 
    }
 
    @Override
    public void insert(GuestbookDTO dto) {
        guestbookDao.insert(dto); 
    }
 
    @Override
    public GuestbookDTO view(int idx) {
        return guestbookDao.view(idx); 
    }
 
    @Override
    public void update(GuestbookDTO dto) {
        guestbookDao.update(dto); 
    }
 
    @Override
    public void delete(int idx) {
        guestbookDao.delete(idx);
    }
 
}



따로 처리하는 일 없이 모든 데이터 처리를 dao로 떠넘김



GuestbookController.java 만들고


src/main.resources/templates / list.html


이 템플릿 html 파일을 서버에서 가공하여 최종 html이 만들어짐


xml문법을 따른다.(단독태그도 반드시 슬래시를 이용하여 닫아줘야한다. 안그러면 error )


최종 html이 아닌 Thymeleaf 템플릿이라는 표시

<html xmlns:th="http://www.thymeleaf.org">

//xml  namespace


list.html (Thymeleaf template) 표 부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div>방명록</div>
<!-- static resource : @{디렉토리/파일} -->
<a href="write.do">방명록 작성</a>
<table border="1">
    <tr>
        <th>번호</th>
        <th>이름</th>
        <th>내용</th>
        <th>날짜</th>
    </tr> <!--  개별변수:${집합변수} -->
    <tr th:each="row:${list}">
        <td><span th:text="${row.idx}"></span></td>
        <td><span th:text="${row.name}"></span></td>
        <td>
<a th:href="@{view.do(idx=${row.idx})}"><span th:text="${row.content}"></span>
</a>        
        </td>
        <td><span th:text=
"${#dates.format(row.post_date, 'yyyy-MM-dd HH:mm:ss')}"></span></td>
    </tr>
</table>



th:each <<  반복문




[ 리소스 파일 불러오기 ]


static/css/my.css

정적인 요소는 templates 폴더에 넣어봐야 적용되지 않는다.

1
2
3
4
5
6
@charset "UTF-8";
div {
    color: blue;
    font-size: 30px;
}
 
cs


html에서는

<link rel="stylesheet" type="text/css" href="/css/my.css"> 로 리소스 파일 참조

(정확히는 <link rel="stylesheet" type="text/css" th:href="@{/css/my.css}">  @:at )


templates/include/header.html

1
2
3
4
5
<head th:fragment="header">
    <script src="http://code.jquery.com/jquery-3.2.1.min.js"></script>
    <link rel="stylesheet" type="text/css" 
        th:href="@{/css/my.css}" />    
</head>



위 css를 header에서 참조하고 list에선 header를 참조한다.



list.html 상단부분

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<!-- 타임리프 템플릿으로 선언 -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
 
<!-- include="디렉토리/페이지::프레그먼트이름" remove="tag" 바깥쪽 태그 제거 -->
<meta th:include="include/header::header" th:remove="tag"></meta>    
 
<title>Insert title here</title>
 


header.html에서 frgment가 header인 태그 내용만 가져오는데


바깥쪽 태그 ( <head th:fragment="header"> 와 </head>는 제거해서 가져옴  )



[ 자바스크립트 파일 불러오기 ]


static/js/test.js

1
2
3
4

function test(){
    alert("자바스크립트 테스트")
}
cs


list.html 상단부분

1
2
3
4
5
6
7
<!-- include="디렉토리/페이지::프레그먼트이름" remove="tag" 바깥쪽 태그 제거 -->
<meta th:include="include/header::header" th:remove="tag"></meta>    
 
<title>Insert title here</title>
 
<!-- @{/디렉토리/파일} static resource를 참조함 -->
<script th:src="@{/js/test.js}"></script>
cs




[ 이미지 첨부 ]


static/images/Tulips.jpg


list.html

첨부할 부분에 img 태그 사용

1
<img th:src="@{/images/Tulips.jpg}" width="50px" height="50px" />
cs