Spring boot 프로젝트 (데이터베이스 연동, Template 적용)
Back-End/Spring 2019. 7. 7. 22:07-방명록 만들기-
방명록 관련 sql
데이터베이스에서 쿼리 작성
1 2 3 4 5 6 7 8 | create table guestbook ( //gustbook 테이블 생성 idx number not null primary key, //글번호, null값 x, 기본키로 설정 name varchar2(50) not null, //이름, null값 x email varchar2(50) not null, //이메일, null값 x passwd varchar2(50) not null, //비밀번호, null값 x content varchar2(4000) not null, //내용, null값 x post_date date default sysdate //날짜, 기본값으로 설정 (현재날짜로) ); | cs |
시퀀스 만들기
1 2 3 4 5 6 | create sequence guestbook_seq //시퀀스를 생성 start with 1 //시작은 1부터 시작해서 increment by 1 //1씩 증가함 nomaxvalue //무한대로 증가 nocache; //cache를 사용하지 않는 옵션으로 생성 //(그러니까 캐쉬에 저장된 페이지를 불러오는게 아니라 항상 최신에 페이지를 불러오는것) //캐쉬에 저장하면 발급속도는 빠른대신에 서버가 멈추면 캐쉬안에 저장된 값도 날라가기 때문에 //속도가 좀 느리더라도 캐쉬를 사용하지 않는 방법을 선택했다. | cs |
만든 방명록 테이블에 자료 삽입
1 2 | insert into guestbook (idx,name,email,passwd,content) values (guestbook_seq.nextval,'kim','kim@nate.com','1234','방명록'); | cs |
위의 쿼리를 다 작성한 후에 작업완료
1 | commit; | cs |
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 | 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() public int getIdx() { return idx; } public void setIdx(int idx) { this.idx = idx; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getPasswd() { return passwd; } public void setPasswd(String passwd) { this.passwd = passwd; } public Date getPost_date() { return post_date; } public void setPost_date(Date post_date) { this.post_date = post_date; } @Override public String toString() { return "GuestbookDTO [idx=" + idx + ", name=" + name + ", email=" + email + ", content=" + content + ", passwd=" + passwd + ", post_date=" + post_date + "]"; } } | cs |
GuestbookDAO.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 44 45 46 | 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.MapperScan; import com.example.spring03_boot.model.dto.GuestbookDTO; // mybatis interface mapper (SQL 명령어가 포함된 코드) //인터페이스에는 원래 미완성된 코드 (추상메소드)만 넣을 수 있으나 여기에서는 //sql쿼리 어노테이션을 붙여서 추상메소드를 실행하면 위에 있는 sql쿼리가 실행이 된다. //아까 애플리케이션.java파일에서 MapperScan 어노테이션을 사용해서 //("com.example.spring03_boot.model")경로에 있는 mapper을 사용할 수 있도록 설정해놓았기 때문에 // 여기 있는 sql쿼리를 바로 사용할 수 있는 것이다. //그리고 여기에서 다 구현을 하고 있기 때문에 따로 DAOImpl (인터페이스 구현 클래스)를 만들필요가 없다. public interface GuestbookDAO { @Select("select * from guestbook order by idx desc") // 게시글번호의 내림차순으로 방명록의 모든 요소를 검색 public List<GuestbookDTO> list(); // sql문이 여러줄 있을 경우에는 따움표나 쉼표나 띄어쓰기를 주의해서 사용한다. @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); } | cs |
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); //게시글 삭제 } | cs |
서비스를 구현할 구현 클래스 (GuestbookServiceImpl.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 //dao를 사용하기 위해 의존성 주입 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); } } | cs |
컨트롤러를 생성 (GuestbookController.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 | package com.example.spring03_boot.controller; import java.util.List; import javax.inject.Inject; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import com.example.spring03_boot.model.dto.GuestbookDTO; import com.example.spring03_boot.service.GuestbookService; @Controller // Controller bean으로 등록 public class GuestbookController { @Inject // 서비스 빈 inject (서비스 객체를 호출하기 위해서 의존성을 주입함) GuestbookService guestbookService; @RequestMapping("list.do") public ModelAndView list(ModelAndView mav) { mav.setViewName("list"); //뷰의 이름 List<GuestbookDTO> list=guestbookService.list(); mav.addObject("list", list); //뷰에 전달할 데이터 return mav; //뷰로 이동 (화면 출력함) } } | cs |
아까는 jsp에서 출력을 했지만 이번에는 템플릿 (list.html을 만들어서) 을 사용할 예정
- 타임리프 활성화 -
지금부터 실습하는 예제는 타임리프 템플릿을 사용함 (jsp파일 대신 출력할 view)
1. application.properties 에서 jsp 뷰 설정을 주석 처리함 (둘 중에 하나만 쓸 수 있기 때문!)
2. pom.xml에서 thymeleaf 라이브러리의 주석을 해제함
3. templates 폴더 하위에 list.html 파일을 작성
주의할점 : 닫는 태그가 있을 경우에는 상관없지만 단독 태그일 경우에는 반드시 끝에 "/" 를 붙여야 한다.
ex) <meta charset> 등등
xml 문법을 따르기 때문에 이렇게 해주어야 한다.
list.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 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 | <!DOCTYPE html> <!-- 타임리프 템플릿으로 선언--> <!-- 이 파일은 html이 아니라 템플릿이다. --> <!-- xml형식이라 생각하면 된다 xmlns는 xml의 네임스페이스라는 뜻 --> <!-- 그리고 타임리프 템플릿이라 지정해주는 코드이다. --> <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> <!-- @{/디렉토리/파일} static resource를 참조함 --> <script th:src="@{/js/test.js}"></script> <script> $(function(){ //test(); }); </script> </head> <body> <!-- <img th:src="@{/images/Tulips.jpg}" width="50px" height="50px" /> --> <div>방명록</div> <!-- static resource : @{디렉토리/파일} --> <a href="write.do">방명록 작성</a> <table border="1"> <tr> <th>번호</th> <th>이름</th> <th>내용</th> <th>날짜</th> </tr> <!-- 개별변수:${집합변수} --> <!-- dates.format(날짜데이터, 출력형식) --> <!-- href="view.do?idx=3" --> <!-- th:href="@{}" @{정적인 텍스트(변수=값)} 이라는 뜻이다.--> <tr th:each="row:${list}"> <!-- each는 반복문이라는 뜻이다. 즉 tr구문이 반복된다는 뜻--> <!-- row 옆에 콜론 ":"이 있는데 변수라는 의미이다. --> <!-- 즉 list를 row변수안에 넣는다는 의미 밑에 코드는 row안에 있는 값들을 하나씩 출력하는 것 --> <!-- 컨트롤러에서 변수명을 list라고 넘겼기 때문에 이것을 받기 위해 사용한 구문이다. --> <td><span th:text="${row.idx}"></span></td> <td><span th:text="${row.name}"></span></td> <td> <!-- 변수를 사용할땐 ${}를 쓰고, 리소스는 (클릭하면 view.do라는 리소스로 넘어간다) @{}를 사용한다. --> <a th:href="@{view.do(idx=${row.idx})}"><span th:text="${row.content}"></span> </a> </td> <td> <!-- #dates는 내장함수이고, 날짜를 yyyy-MM-dd HH:mm:ss이런 형식으로 만들겠다는 구문이다.--> <span th:text= "${#dates.format(row.post_date, 'yyyy-MM-dd HH:mm:ss')}"></span></td> </tr> </table> </body> </html> | cs |
아까 application.properties파일에서 기본 에러페이지를 사용하지 않고, 내가 만든 에러페이지를 사용한다고 했었기 때문에
에러페이지를 하나 만들어주어야 한다.
application.properties중 일부
1 | server.error.whitelabel.enabled=false //기본 에러페이지를 사용하지 않고, 내가 만든 에러페이지를 사용한다는 코드 | cs |
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 |
지금 실행하면 jsp로 만들었을때는 메시지가 나올 페이지를 만들었지만, 타임리프 템플릿에서는 메시지가 나올 페이지를 아직 만들지
않았기 때문에 에러가 발생한다. 그래서 메시지를 출력할 타임리프 템플릿을 만들어주어야 한다.
메시지를 출력할 hello.html 파일 작성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!-- templates/hello.html --> <!DOCTYPE html> <!-- xml namespace 지정 --> <html xmlns:th="http://www.thymeleaf.org"> <head> <!-- 단독태그는 안되기 때문에 슬래시 "/"를 붙인다. --> <meta charset="UTF-8" /> <title>Insert title here</title> </head> <body> <!-- th: 타임리프 태그의 요소 --> <!-- th:text="타임리프 변수" 변수값을 텍스트로 출력함 --> <!-- 단독으로 message만 사용하면 값이 나오지 않기 때문에 --> <!-- 반드시 태그 안쪽에 변수를 사용해서 출력하여야 한다. --> <h2>타임리프 템플릿 사용 버전</h2> <span th:text="${message}"></span> </body> </html> | cs |
========================= 정적인 요소들 사용하는 방법 (css, js) =========================
static (리소스는 이 폴더에 넣어야됨) 폴더 하위 css폴더와 js폴더를 각각 만들고 css, js파일 생성
my.css
1 2 3 4 5 6 | @charset "UTF-8"; div { color: blue; font-size: 30px; } | cs |
test.js
1 2 3 4 | // static/js/test.js function test(){ alert("자바스크립트 테스트") } | cs |
타임리프 템플릿에 include를 시키고 싶으면 include폴더를 만들고 하위에 header.html 파일을 생성하고 링크시킨다.
header.html 파일 생성
1 2 3 4 5 | <!-- 타임리프의 코드 조각 --> <head th:fragment="header"> //다른 파일에 include되는 부분 <script src="http://code.jquery.com/jquery-3.2.1.min.js"></script> <link rel="stylesheet" type="text/css" th:href="@{/css/my.css}" /> //리소스를 추가할때는 @{}를 사용한다. 안에 있는 것은 css파일의 경로 //js파일과 css파일을 include 하겠다는 의미 </head> | cs |
이미지를 넣고 싶을때는 static 디렉토리 하위에 파일을 넣고, 템플릿에서 링크를 걸면 된다.
list.html에 이미지 링크 태그를 추가
1 2 | <img th:src="@{/images/Tulips.jpg}" width="50px" height="50px" /> //마찬가지로 jpg도 리소스이기 때문에 @{}형식 안에 출력할 이미지를 넣어주면 된다. | cs |
'Back-End > Spring' 카테고리의 다른 글
Spring Boot와 MongoDB 연동해서 회원관리 예제 (회원가입, 로그인, 로그아웃) (1) | 2019.07.08 |
---|---|
Spring Boot와 MongoDB 연동 (0) | 2019.07.08 |
타임리프 (Thymeleaf) 란? (0) | 2019.07.07 |
Template System 이란? (0) | 2019.07.07 |
Spring boot 프로젝트 (생성, 실행) (0) | 2019.07.06 |