심심해서 하는 블로그 :: [Spring Boot]1-2. 간단한 게시판 만들기 (Modal로 깔끔하게 만들자)

전체 소스 코드 : https://github.com/ssooni/ssoonidev_blog

Spring Boot : ver 1.5.9

JAVA : ver 1.8 

Build Tool : MAVEN


1. 목표

사용자가 View를 통해 데이터를 조회하여, 새로운 데이터를 입력하거나 기존 데이터를 삭제, 수정이 가능한 웹페이지를 작성하는 것이 이번 포스팅의 목표입니다. 


2. controller

Controller는 사용자가 요청하는 URL에 따라, 응답을 해주는 역할을 수행합니다. 그리고 GET, POST, PUT, DELETE 등의 Method로 사용자가 어떤 종류의 요청을 하는지를 정의해  줄 수 있습니다. 따라서 "/board" 같은 주소를 사용하더라도 GET 방식이냐 POST 방식인가에 따라서 사용자에서 다른 응답을 보여줄 수 있습니다. 아래는 이번 게시판 만들기 포스팅에서 사용할 URL의 설계 방안입니다.


/board GET : 홈 화면을 보여준다.

/board POST : 새로운 게시물을 데이터베이스에 저장한다.

/board PUT  : 기존의 게시물을 수정한다.

/board/{bno} DELETE  : {bno}번 게시물을 삭제한다.



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
@Controller
public class BoardController {
 
    private static Logger logger = LoggerFactory.getLogger(BoardController.class);
    
    @Autowired
    private BoardService boardService;
 
    @GetMapping("/board")
    public ModelAndView list() throws Exception{
        List<BoardDomain> boardList = boardService.findAll();
        ModelAndView nextPage = new ModelAndView("board/home");
        nextPage.addObject("boardList", boardList);
        return nextPage;
    }
    
    @PostMapping("/board")
    public void create(BoardDomain board) throws Exception{
        logger.info("POST /board : " + board.toString());
        boardService.insert(board);
    }
    
    @PutMapping("/board")
    public void modify(BoardDomain board) throws Exception{
        logger.info("PUT data : " + board.toString());
        boardService.update(board);
    }
    
    @DeleteMapping("/board/{bno}")
    public void delete(@PathVariable("bno"int bno) throws Exception{
        logger.info("DELETE bno : " + bno);
        boardService.delete(bno);
    }    
}
cs


URL 설계를 기반으로 작성한 Controller입니다. @Controller Annotation이 반드시 있어야 Spring Boot에서 웹 요청을 받아들이는 Controller로 이 클래스를 사용하고 beans로 등록합니다. 


URL Mapping

URL을 Mapping 할 때 @RequestMapping(value="/board", method=RequestMethod.GET)처럼 예전에는 조금 길게 작성하는 Annotation 코드를 @GetMapping처럼 짧게 작성할 수 있습니다. @PostMapping@PutMapping, @DeleteMapping도 마찬가지 역할을 수행합니다.


ModelAndView

10번째 줄 list() 함수는 ModelAndView 객체를 반환하는 함수입니다. ModelAndView 객체는 어떤 페이지로 이동하는 페이지 정보와 데이터를 실을 수 있는 객체입니다. 따라서 list() 함수가 수행한 뒤엔 board/list.jsp로 이동하고 이때 boardList라는 객체도 함께 실어서 이동합니다. 이 boardList는 당연히 list.jsp에서 접근 가능합니다.


@PathVariable

URL에 작성된 값을 참조하는 Annotation입니다. tistory 블로그의 URL을 보면 https://ssoonidev.tistory.com/{글번호}로 구성되어 있는데, 이 {글 번호}에 접근하기 위해 사용하는 Annotation이라고 생각하시면 됩니다.  


의존성 주입

데이터베이스에 접근하여 데이터를 처리하는 Service를 @Autowired Annotation을 이용하여 주입합니다. 흔히 Spring Framework에서 의존성 주입(DI)이라는 용어를 사용합니다. 이 개념은 포스팅 하나로 소개해도 무방할 정도로 강력한 개념이므로, 차후 포스팅으로 소개하도록 하고 지금은 간단히 "컨테이너가 미리 만들어진 객체를 가져다가 쓰겠다."라고 생각하면 좋겠습니다.

 

추가적으로 BoardMapper와 BoardService의 코드를 추가/변경하였습니다. 

GitHub의 코드를 꼭 참고하시길 바랍니다.


3. View를 만들자.

지난 포스팅에서 마지막으로 간단하게 뷰를 하나 만들어서 테스트를 수행하였습니다. 테스트 화면에서도 간단하게 Bootstrap을 적용하였는데요. 이번에는 Modal Class를 이용해서 새 글 작성과 수정, 삭제 기능을 하는 뷰를 작성해 보겠습니다. 


전체적인 화면구성

우선 전체적인 화면 구성을 봅시다. home.jsp 파일에 header.jsp 파일을 include 한 형태로 만들고자 합니다. header.jsp는 차후에 Bootstrap의 nav 클래스와 dropdown 클래스 등을 사용하여 검색 기능과 메뉴 이동 기능을 추가할 예정입니다. 시간이 여유롭다면 말이죠.



home.jsp는 게시물을 테이블 형태로 보여주는 화면입니다. 수정과 새 글쓰기 버튼을 누르면 위의 그림과 같은 Modal이 화면에 보이게 구현했습니다. 수정 버튼을 누르면 작성자 명과 작성한 콘텐츠가 텍스트 박스에 위의 오른쪽 그림같이 놓이게 됩니다. 


주요 코드만 간단하게 설명하겠습니다. 

전체 코드는 GitHub를 참고하시길 바랍니다.


home.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
<body>
    <div class="container">
        <jsp:include page="../include/header.jsp" />
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th class="col-md-1">bno</th>
                    <th class="col-md-7">contents</th>
                    <th class="col-md-2">userName</th>
                    <th class="col-md-2">수정 / 삭제</th>
                </tr>
            </thead>
            <tbody>
                <c:forEach var="board" items="${boardList}">
                    <tr id="tr${board.bno}">
                        <td>${board.bno}</td>
                        <td><a href="#">${board.contents}</a></td>
                        <td>${board.userName}</td>
                        <td>
                            <div class="btn-group">
                                <button name="modify" value="${board.bno}"
                                    class="btn btn-xs btn-warning">수정</button>
                                <button name="delete" value="${board.bno}"
                                    class="btn btn-xs btn-danger">삭제</button>
                            </div>
                        </td>
                    </tr>
                </c:forEach>
            </tbody>
        </table>
        <jsp:include page="../include/modal.jsp" />
        <button id="createBtn" type="button" class="btn btn-info btn-sm"
            data-toggle="modal">새 글 쓰기</button>
    </div>
</body>
cs


Home화면 jsp 코드입니다. 주소창에 http://localhost:8080/board라고 입력했을 때 보여주는 화면입니다. 


3번째 줄  : header.jsp를 home.jsp에 포함합니다. (경로에 주의바랍니다.)


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>


14번째 줄 ~ 28번째 줄 (c:foreach Line)

BoardController에서 list() 함수에서 전체 게시물 리스트를 담고 있는 "boardList"라는 List 객체를 전달해주는데,  이 객체를 ${boardList}로 받아 JSP에서 사용 가능합니다. <c:foreach>는 위의 JSP Core Tag Library를 추가하면 사용할 수 있고 for 문과 동일한 역할을 합니다. 즉, boardList 안에 있는 원소들을 board에 하나씩 할당하는 반복문을 수행합니다.  그리고 이 데이터를 테이블에 보여 주기 위해 <tr> <td>를 이용하여 행을 채워

나갑니다.


31번째 줄  : modal.jsp를 home.jsp에 포함합니다.


modal.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
<!-- Modal -->
<div class="modal fade" id="myModal" role="dialog">
    <div class="modal-dialog">
 
        <!-- Modal content-->
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal">&times;</button>
                <h4 id="modal-title" class="modal-title"></h4>
            </div>
            <div class="modal-body">
                <table class="table">
                    <tr>
                        <td>사용자명</td>
                        <td><input class="form-control" id="userName" type="text"></td>
                    </tr>
                    <tr>
                        <td>내용</td>
                        <td><textarea class="form-control" id="contents" rows="10"></textarea></td>
                    </tr>                    
                </table>
            </div>
            <div class="modal-footer">
                <button id="modalSubmit" type="button" class="btn btn-success">Submit</button>
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>
cs


Bootstrap Modal 입니다. 하나의 Modal을 이용해서 작성용 Modal과 수정용 Modal을 구현해서 Modal Title (9번째 줄)에 들어가는 내용이 없습니다. 여기에 들어갈 내용은 모두 modal.js에서 해결합니다.


modal.js 

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
74
var action = '';
var url = '';
var type = '';
var bno = 0;
 
$(document).ready(function(){
 
    // 새 글 쓰기 버튼 클릭
    $("#createBtn").click(function(){
        action='create';
        type = 'POST'
        $("#modal-title").text("새 글 작성");
        $("#myModal").modal();
    });
    
    // 수정하기 버튼 클릭
    $("button[name='modify']").click(function(){
        action='modify';
        type = 'PUT';
        bno = this.value;
 
        // content 담기
        var row = $(this).parent().parent().parent();
        var tr = row.children();
        
        var userName = tr.eq(2).text();
        var contents = tr.eq(1).text();
 
        $("#modal-title").text("수정하기");
 
        $("#userName").val(userName);
        $("#contents").val(contents);
        
        $("#myModal").modal();
    });
    
    // 삭제하기 버튼 클릭
    $("button[name='delete']").click(function(){
        bno = this.value;
        $.ajax({
            url : '/board/' + bno,
            type : 'DELETE',
        });
        location.reload();
    })
    
    // Modal의 Submit 버튼 클릭
    $("#modalSubmit").click(function(){
        
        if(action == 'create'){
            bno = 0;
            url = '/board';
        }else if(action == 'modify'){
            url = '/board';
        }
 
        var data = {
            "bno" : bno,
            "userName" : $("#userName").val(),
            "contents" : $("#contents").val()
        };
        
        $.ajax({
            url : url,
            type : type,
            data : data,
success: function(data){ $("#myModal").modal('toggle'); }
complete: function(data){ location.reload(); }
        })
    });
    
 
});
 

 cs


각각의 버튼의 클릭 이벤트를 처리하는 함수들로 구성되어 있습니다. Modal을 보여주는 부분은 $("#myModal").modal() 부분입니다. jQuery에서 # selector는 element의 ID를 찾아 추적합니다. 앞선 modal.jsp에서 modal의 id를 myModal 임을 확인 할 수 있습니다.


또한 눈에 띄는 것은 Ajax 구문입니다.  데이터가 입력 / 수정하면 기존 방식에서는 화면 전체가 reload 됩니다. 변화하는 부분은 테이블 하나인데 전체 페이지가 변경되는 쓸모없는 자원이 낭비합니다. 이 낭비를 줄이기 위해 나온 것이 Ajax입니다 Ajax는 서버에 요청을 자바스크립트에서 하기 때문에 화면 전체가 아닌 일부분만 갱신이 가능합니다.


물론 지금 구현한 함수는 완벽한 Ajax 구문이 아닙니다. 왜냐하면 화면을 전체 Reload 하기 때문에 굳이 Ajax가 아니더라도 해결할 수 있는 부분입니다. 맛보기로 이런 게 있구나 보여주는 것이죠. 이것이 완벽한 Ajax를 구현한 것은 아니라는 점 생각해주시길 바랍니다.


-- 내용 추가 18.08.16 --

borker님의 실행 결과 리플래시가 안되는 문제가 있다해서 확인하였고, MySQL의 insert 요청을 보낸 동시에

화면이 reload해서 새로고침이 안됩니다. success로 Modal 을 닫고, complete로 reload하는 함수를 추가하여 

반영하였습니다. 피드백 해주셔서 감사합니다.


이제 어느 정도 게시판 다운 모습을 하기 시작합니다. 

추가적인 기능을 넣어보는 것은 조금씩 조금씩 애정을 갖고 만들어 나가보겠습니다.

혹시 먼저 만들어 보았으면 하는 기능이 있다면 댓글로 남겨주세요


다음 포스팅은 Mongo DB를 이용해서 REST API를 만드는 것을 해보도록 하겠습니다.

전체 소스 코드는 GitHub를 참조해주시길 바랍니다.



,