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 |
타임리프 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 |
'Spring > study' 카테고리의 다른 글
spring 28강 Spring Boot와 MongboDB 연동 실습(방명록) (0) | 2019.07.12 |
---|---|
spring 27강 Spring Boot와 MongboDB 연동 실습(한줄메모장) (0) | 2019.07.11 |
spring 24강 도로명 주소(daum api) (0) | 2019.07.08 |
spring 23강 게시판 만들기4( 게시물 수정 ) (0) | 2019.07.08 |
@RequestBody 어노테이션과 @ReponseBody 어노테이션의 사용 (0) | 2019.07.05 |