Spring boot 프로젝트 (데이터베이스 연동, Template 적용)

Back-End/Spring 2019. 7. 7. 22:07
728x90
반응형

-방명록 만들기-


방명록 관련 sql


데이터베이스에서 쿼리 작성

1
2
3
4
5
6
7
8
create table guestbook (    //gustbook 테이블 생성
idx number not null primary key,    //글번호, null값 x, 기본키로 설정
name varchar2(50not null,        //이름, null값 x
email varchar2(50not null,    //이메일, null값 x
passwd varchar2(50not null,    //비밀번호, null값 x
content varchar2(4000not 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


GuestbookDTO.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
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


728x90
반응형
: