Spring Boot와 MongoDB 연동 실습 (한줄메모장)

Back-End/Spring 2019. 7. 9. 17:27

한줄메모장 만들기


MongoDB는 테이블을 사전에 만들 필요가없고, 코딩에 의해 테이블이 즉각적으로 생겨나고 자료가 삽입된다.


비정형 데이터를 넣는것에 적합한 DB이다.



 

  한줄메모장 구조



  -글목록-


  1. home.jsp에 include된 menu.jsp 페이지에 있는 "메모장" 버튼을 누르면 MemoController.java에 있는 memo.do메소드로 맵핑된다.


  2. 맵핑된 메소드에서 memo.jsp 페이지로 이동시킨다.


  3. memo.jsp 페이지가 호출될때 가장먼저 memo_list() 함수가 호출되고, ajax방식이고, 컨트롤러에 memo_list.do와 맵핑되는데 데이터만

     넘어가고 화면은 바뀌지 않는다.


  4. memo_list.do와 맵핑된 컨트롤러의 메소드에서는 서비스에서 메모목록을 가져와 hashmap에 저장하고, memo_list.jsp 페이지로

     이동시키고, 아까 map변수에 담은 list도 같이 넘겨준다.


  5. memo_list.jsp 페이지에서는 넘겨받은 map에 있는 게시글번호, 작성자 이름, 작성글내용, 작성날짜를 출력한다.

  


  -글쓰기-


  1. memo.jsp 페이지에서 "이름" 과 "메모" 를 작성한 후에 "확인" 버튼을 누르면 해당되는 id값 (btnWrite)가 넘어가서 id값에 맞는 

     자바스크립트 함수가 실행되고, 이 함수는  memo_insert()함수를 호출한다.


  2. memo_insert() 함수에서 작성자의 이름과 메모를 각각 변수에 담고, ajax방식으로 페이지를 안바꾸고 데이터만 post방식으로

     컨트롤러에 memo_insert.do로 맵핑시킨다.


  3. memo_insert.do에 맵핑된 메소드에서는 서비스를 호출해서 dto값을 담고 memo.do로 다시 맵핑시킨다.


  4. memo.do로 맵핑된 메소드에서는 다시 memo.jsp 페이지로 이동시킨다.


  5. 아까 memo_inset() 함수가 정상적으로 실행되서 자료가 전달되었으므로, success문 하위에 있는 memo_list()함수가 실행된다.


  6. memo_list()함수가 실행되면 ajax방식으로 컨트롤러에 memo_list.do와 맵핑되고, 거기에서 list를 map로 받아온다.


  7. memo_list()함수에서 자료 전달이 성공적으로 됬으므로 success문을 실행해서 컨트롤러에서 받아온 map값을 result 변수에 저장하고,

     id가 #memoList인 태그에 컨트롤러로부터 넘겨받은 map를 출력시키면 메모글이 추가된다.



  -글삭제-


  1. memo_view 페이지에서 "삭제" 버튼을 누르면 id값인 btnDelete가 위쪽 자바스크립트 함수와 맵핑되어서 자바스크립트 함수에서

     컨트롤러에 있는 memo_delete.do로 맵핑되면서 자료를 넘겨준다.


  2. memo_delete.do로 맵핑된 메소드에서 서비스에 _id(키값)을 매개변수로 넘겨서 레코드를 삭제하고 memo.do로 리턴시킨다.


  3. memo.do로 맵핑된 메소드에선 memo.jsp 페이지로 이동시킨다. 


  4. memo.jsp 가 호출되었으므로 memo_list() 함수가 호출되어서 ajax방식으로 컨트롤러에 있는 memo_list.do로 맵핑된다.


  5. memo_list.do에선 아까 갱신(삭제한) 리스트를 다시 map에 담아서 memo_list.jsp 페이지로 리턴한다. (map)와 함께


  6. memo_list.jsp 에선 map에 있는 자료를 차례로 출력하면 아까 삭제한 글을 제외하고 리스트로 출력되게 된다.



  -글수정-


  1. memo_view 페이지에서 자료를 수정하고 "수정" 버튼을 누르면 id값인 btnUpdate가 위쪽 자바스크립트 함수와 

     맵핑되어서 자바스크립트 함수에서컨트롤러에 있는 memo_update.do로 맵핑된다.


  2. memo_update.do와 맵핑된 메소드에선 서비스를 호출해서 memoUpdate메소드에 dto에 담긴값들을 매개변수로 담고, 

     memo.do로 리턴한다.


  3. memo.do와 맵핑된 메소드에선 다시 memo.jsp 페이지로 이동시킨다.


  4. memo.jsp 페이지가 실행되었으므로 memo_list()함수가 실행된다.


  5. memo_list()함수에서는 ajax방식으로 memo_list.do와 맵핑되서 화면은 바뀌지 않고 데이터만 컨트롤러로 넘긴다.


  6. memo_list.do와 맵핑된 컨트롤러에 있는 메소드에서는 서비스를 호출해 list를 hashmap에 저장하고, 

     memo_list.jsp 페이지로 자료를 넘기고, 페이지를 이동시킨다.  


  7. memo.jsp 페이지에서는 ajax방식으로 자료를 보내는데에 성공했으므로 success구문이 실행되서 

     컨트롤러가 받아온 값을 result변수에 저장하고, id가 #memoList인 태그에 넘어온 값을 출력시킨다.

     (그러니까 memo.jsp페이지에서 메모리스트(memo_list)를 보여줌


  



MemoDTO.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
package com.example.spring04.model.memo.dto;
 
import java.util.Date;
 
public class MemoDTO {
    
    private String _id; //id는 pk이므로 _id로 하여야한다.
    private String writer;    //작성자
    private String memo;    //메모내용
    private Date post_date;    //날짜
    
    //getter/setter,toString()
    
    public String get_id() {
        return _id;
    }
    public void set_id(String _id) {
        this._id = _id;
    }
    public String getWriter() {
        return writer;
    }
    public void setWriter(String writer) {
        this.writer = writer;
    }
    public String getMemo() {
        return memo;
    }
    public void setMemo(String memo) {
        this.memo = memo;
    }
    public Date getPost_date() {
        return post_date;
    }
    public void setPost_date(Date post_date) {
        this.post_date = post_date;
    }
    public MemoDTO(String _id, String writer, String memo, Date post_date) {
        super();
        this._id = _id;
        this.writer = writer;
        this.memo = memo;
        this.post_date = post_date;
    }
    
}
 
cs



MemoDAO.java 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.example.spring04.model.memo.dao;
 
import java.util.List;
 
import com.example.spring04.model.memo.dto.MemoDTO;
 
public interface MemoDAO {
    List<MemoDTO> getMemoList();    //목록보기
    void memoInsert(MemoDTO dto);    //글쓰기
    MemoDTO memoDetail(String _id);    //상세화면
    void memoUpdate(MemoDTO dto);    //글수정
    void memoDelete(String _id);    //글삭제
}
 
cs



MemoDAOImpl.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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package com.example.spring04.model.memo.dao;
 
import java.util.Date;
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
 
import com.example.spring04.model.memo.dto.MemoDTO;
 
@Repository //DAO 사용 어노테이션 선언
public class MemoDAOImpl implements MemoDAO {
 
    @Autowired    //MongoDB사용을 하기위해 의존성을 주입한다.
    MongoTemplate mongoTemplate;
    String COLLECTION_NAME="memo"//컬렉션(테이블) 이름을 "memo"라고 한다,

    
    @Override
    public List<MemoDTO> getMemoList() {
        //목록을 뽑을때 사용하는 클래스 (query)
        Query query=new Query();
        query.with(new Sort(Sort.Direction.DESC, "post_date")); //날짜기준 내림차순 정렬
        return (List<MemoDTO>)mongoTemplate.find(//memo테이블에서 날짜기준으로 내림차순 정렬시킨 값을 DTO타입으로 저장해 리스트를 만들어 리턴하라는 의미이다.
                query, MemoDTO.class, COLLECTION_NAME);
    }
 
    @Override
    public void memoInsert(MemoDTO dto) {//저장할때 날짜를 따로 입력해서 저장하지 않기 때문에  
        dto.setPost_date(new Date()); 
                                                    
        //java.util.Date
        // insert( 추가할객체, 컬렉션이름 )
        mongoTemplate.insert(dto, COLLECTION_NAME); //코드로 날짜를 집어넣고 insert를 하면 된다.
    }
 
    @Override
    public MemoDTO memoDetail(String _id) {
        //id에 해당하는 값 (레코드) 1개를 찾을 경우에는 findById(_id, 클래스, 테이블이름) 사용
        return mongoTemplate.findById(
                _id, MemoDTO.class, COLLECTION_NAME);
        //id에 해당되는 자료를 COLLECTION_NAME테이블에서 가져와서 DTO에 저장
    }
 
    @Override
    public void memoUpdate(MemoDTO dto) {
        // update 테이블 set 필드=값, 필드=값 where 필드=값
        // where 조건
        Query query=new Query(
                new Criteria("_id").is(dto.get_id()));
        //테이블에 있는 id와 내가적은 id가 같을경우에 글을 수정할 수 있다.
        //수정할 내용
        Update update=new Update();
        update.set("writer", dto.getWriter());
        update.set("memo", dto.getMemo());
        // upsert : update or insert
        //없으면 추가하고, 있으면 수정한다는 의미이다.
        mongoTemplate.upsert(
                query, update, MemoDTO.class, COLLECTION_NAME);
    }
 
    @Override
    public void memoDelete(String _id) {
        Query query=new Query(new Criteria("_id").is(_id)); //저장되있는 아이디가 내가 입력한 아이디와 같을경우에
        mongoTemplate.remove(query, COLLECTION_NAME);        //테이블에 있는 쿼리를 삭제함
    }
 
}
 

cs



MemoService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.spring04.service.memo;
 
import java.util.List;
 
import com.example.spring04.model.memo.dto.MemoDTO;
 
public interface MemoService {
    
    List<MemoDTO> getMemoList();    //메모 목록
    void memoInsert(MemoDTO dto);    //메모 저장
    MemoDTO memoDetail(String _id);    //메모 상세 내용 보기
    void memoUpdate(MemoDTO dto);    //메모 수정
    void memoDelete(String _id);    //메모 삭제
}
 
cs



MemoServiceImpl.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
package com.example.spring04.service.memo;
 
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.example.spring04.model.memo.dao.MemoDAO;
import com.example.spring04.model.memo.dto.MemoDTO;
 
@Service    //서비스 어노테이션
public class MemoServiceImpl implements MemoService {
 
    @Autowired //스프링프레임워크 의존성 주입, dao를 호출하기위해 의존성을 주입
    MemoDAO memoDao;
 
    
    
    @Override
    public List<MemoDTO> getMemoList() {    //메모 목록 리턴    
        return memoDao.getMemoList();
    }
 
    @Override
    public void memoInsert(MemoDTO dto) {    
        memoDao.memoInsert(dto);
    }
 
    @Override
    public MemoDTO memoDetail(String _id) {
        return memoDao.memoDetail(_id); 
    }
 
    @Override
    public void memoUpdate(MemoDTO dto) {
        memoDao.memoUpdate(dto); 
    }
 
    @Override
    public void memoDelete(String _id) {
        memoDao.memoDelete(_id); 
    }
 
}
 
cs



menu.jsp에서 메모장 링크를 눌렀을때, memo.do로 맵핑되서 메모 목록을 얻어서 화면에 출력시킬 예정



menu.jsp

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
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" 
uri="http://java.sun.com/jsp/jstl/core" %>
 
<!-- HOME, 메모장, 방명록페이지로 가는 링크를 추가 -->
<a href="${path}/">Home</a> | 
<a href="${path}/memo.do">메모장</a> | 
<a href="${path}/guestbook.do">방명록</a> | 
<div style="text-align:right;">
    
    <c:choose>
    <!-- 세션에 저장된 유저의 아이디가 NULL이면 로그인페이지로 이동 -->
    <c:when test="${sessionScope.userid == null }">
    
    <!-- 로그인 페이지로 이동하는 링크 -->
    <a href="${path}/member/login.do">로그인</a>
    </c:when>
    
    <c:otherwise>
        <!-- 세션에 저장된 유저의 아이디가 NULL이 아니라면 로그인 중이라는 메시지를 출력한다. -->
        ${sessionScope.name}님이 로그인중입니다.
        
        <!-- 로그인중이므로 로그아웃 할 수 있는 링크를 추가한다. -->
        <a href="${path}/member/logout.do">로그아웃</a>
        
    </c:otherwise>
</c:choose>    
</div>  
<hr>
cs



MemoController.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
59
package com.example.spring04.controller;
 
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import org.springframework.beans.factory.annotation.Autowired;
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.spring04.model.memo.dto.MemoDTO;
import com.example.spring04.service.memo.MemoService;
 
@Controller //컨트롤러 빈 선언
public class MemoController {
 
    @Autowired // @Inject, 서비스를 호출하기위해 의존성을 주입
    MemoService memoService;
    
    @RequestMapping("/memo.do")
    public String memo() {
        return "memo/memo"//memo디렉토리에 memo jsp파일로 가서 목록을 다시 얻어온다.
    }
    
    
    @RequestMapping("/memo_list.do"//게시물 리스트
    public ModelAndView memo_list() {
        Map<String,Object> map=new HashMap<>();
        List<MemoDTO> list=memoService.getMemoList(); //서비스에서 메모목록을 가져온다.
        map.put("items", list); //값이 여러개기때문에 hashmap로 담아서 ModelAndView로 넘기고, 거기에서 memo_list jsp페이지에 "memo"라는 변수명으로 보내 출력시킨다.
        return new ModelAndView("memo/memo_list""map", map);
    }
    
    @RequestMapping("/memo_insert.do"//게시물 삽입
    public String memo_insert(@ModelAttribute MemoDTO dto) {
        memoService.memoInsert(dto);
        return "redirect:/memo.do";
    }
    
    @RequestMapping("/memo_view.do"
    public ModelAndView memo_view(String _id) {
        MemoDTO dto=memoService.memoDetail(_id);
        return new ModelAndView("memo/memo_view""dto", dto);
    }
    
    @RequestMapping("/memo_update.do")
    public String memo_update(@ModelAttribute MemoDTO dto) {
        memoService.memoUpdate(dto);
        return "redirect:/memo.do";
    }
    
    @RequestMapping("/memo_delete.do")
    public String memo_delete(String _id) {
        memoService.memoDelete(_id);
        return "redirect:/memo.do";
    }
}
cs



View 파일들


memo.jsp

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
<%@ 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>
<%@ include file="../include/header.jsp" %>
<script>
//이 페이지가 호출되었을때 memo_list()가 맨처음 실행
$(function(){
    memo_list();
    
    //메모를 쓰고 난다음 확인버튼을 누르면 밑에있는 코드에 id값 :btnWrite가 이 태그로 맵핑되고, memo_insert()함수가 실행된다.
    $("#btnWrite").click(function(){
        memo_insert();
    });
});
 
function memo_insert(){
    var writer=$("#writer").val(); // 메모를 작성한 작성자의 이름이 writer변수에 담긴다.
    var memo=$("#memo").val();        // 작성한 메모가 memo변수에 저장된다.
    
    $.ajax({                                //ajax방식은 페이지가 바뀌지 않고 데이터만 주고 받을 수 있는 방식
        type: "post",                        //post방식으로 넘김
        data: {"writer":writer, "memo":memo},//각각 값들을 저장함
        url: "${path}/memo_insert.do",    //memo_insert.do (컨트롤러로) 맵핑됨
        
        success: function(){ //위의 구문이 성공했을시에 실행되는 함수
            memo_list(); //목록 갱신
            $("#writer").val(""); //writer에 value값에 ""(공백) 값을 넣는다.
            $("#memo").val("");        //memo에 value값에 ""(공백) 값을 넣는다.
        }
    });
}
//맨 처음 실행되는 함수 ajax방식으로 실행되고, memo_list.do와 맵핑 (다시 데이터만 컨트롤러로 넘어감, 화면은 안바뀜)
function memo_list(){
    
    //ajax방식은 화면이 안바뀌고 백그라운드에서만 실행되는것.
    $.ajax({
        url: "${path}/memo_list.do",
        success: function(result){ //컨트롤러가 받아온 값을 result변수에 저장하고, id가 #memoList인 태그에 넘어온값을 출력시킬 예정
            $("#memoList").html(result);
        }
    });
}
function memo_view(num){
    // memo_view.do?_id=5
    location.href="${path}/memo_view.do?_id="+num;
}
</script>
</head>
<body>
<%@ include file="../include/menu.jsp" %>
<h2>한줄메모장</h2>
 
이름 : <input id="writer">
메모 : <input id="memo" size="50">
<input type="button" value="확인" id="btnWrite">
<div id="memoList"></div>
</body>
</html>
cs



memo_list.jsp

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
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" 
uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" 
uri="http://java.sun.com/jsp/jstl/fmt" %>
 
<table border="1" width="700px">
    <tr>
        <th>No</th>
        <th>이름</th>
        <th>메모</th>
        <th>날짜</th>
    </tr>
<c:forEach var="row" items="${map.items}" varStatus="status"<!-- varStatus는 변수명을 붙인것 -->
                <!-- 컨트롤러에서 보낸 map의 items안에 있는 값들을 하나씩 출력 -->
    <tr>
        <td>${status.count}</td
        <!-- 원래 {row._id}가 키값인데 이렇게 쓰면 숫자 뒤에 소수점이 붙어서 나오고, status를 쓰면 중간에 값이 빠질일이 없이 제대로 출력된다.-->
        <!-- index 0부터, count 1부터이기 때문에 count를 사용해서 소수점을 제외하고 출력시킨다.-->
        
        <td>${row.writer}</td>
        <!--작성자이름을 호출한다.-->
        
        <td>
            <a href="#" onclick="memo_view('${row._id}')">${row.memo}</a>
            <!-- _id가 키값이기 때문에 id를 넘겨서  메모내용으로 넘어간다.-->
        </td>
        <td>
        <!-- 날짜가 나오는 형식을 지정하기 위해 formatDate태그와 패턴을 사용한다. -->
        <fmt:formatDate value="${row.post_date}" 
                pattern="yyyy-MM-dd HH:mm:ss" /></td>
    </tr>
</c:forEach>    
</table>
cs



memo_view.jsp

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
<%@ 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>
<%@ include file="../include/header.jsp" %>
<script>
$(function(){
    //수정버튼을 클릭하면 실행되는 함수, 밑에 코드에서 맵핑되어서
    //컨트롤러의 memo_update.do url로 맵핑시킨다.
    $("#btnUpdate").click(function(){ 
        document.form1.action="${path}/memo_update.do";
        document.form1.submit();
    });
    
    //삭제버튼을 클릭하면 실행되는 함수, 밑에 코드에서 맵핑되어서
    //컨트롤러에 memo_delete.do url로 맵핑시킨다.
    $("#btnDelete").click(function(){
        if(confirm("삭제하시겠습니까?")){
            document.form1.action="${path}/memo_delete.do";
            document.form1.submit();
        }
    });
});
</script>
</head>
<body>
<%@ include file="../include/menu.jsp" %>
<h2>메모장</h2>
 
<form name="form1" method="post"<!--키값이 id이기 때문에 _id를 dto에서 받아서 히든타입으로 자료를 넘겨준다. -->
<input type="hidden" name="_id" value="${dto._id}">
 
이름 <input name="writer" value="${dto.writer}"><br<!-- dto에 저장된 작성자이름 출력 -->
메모 <input name="memo" value="${dto.memo}" size="50"><br><!-- dto에 저장된 메모 출력 -->
 
<input type="button" value="수정" id="btnUpdate"<!-- 수정버튼을 누르면 id값이 위쪽에 있는 자바스크립트 함수에  맵핑되서 자바스크립트문에서 컨트롤러로 이동시킴-->
 
<input type="button" value="삭제" id="btnDelete"<!-- 삭제버튼을 누르면 id값이 위쪽에 있는 자바스크립트 함수에  맵핑되서 자바스크립트문에서 컨트롤러로 이동시킴-->
 
<input type="button" value="목록" id="btnList" onclick="location.href='${path}/memo.do'">
<!-- 목록 버튼을 누르면 id값인 btnList가 컨트롤러로 맵핑됨 (memo.do URL로) -->
 
</form>
</body>
</html>
cs



mongo DB에서 서버를 실행하고, 프로젝트를 run해서 메모장에 값들을 집어넣어본후에 mongoDB에서 자료를 


검색해보면 _id값이 고유번호인 uuid로 나오는 것을 확인해볼 수 있다.


(왜냐하면 글번호는 작성자가 집어넣는게 아니기 때문이고, _id값을 따로 정해주지 


않았기때문에 서버에서 임의적으로 정해서출력된 값이기 때문이다.)





: