본문으로 바로가기

Spring 개발 - 게시판 만들기 #목록

category 공부/Spring 2018. 9. 17. 16:40

가장 기초적이고 기본적인 게시판을 만들어보자. 제일 먼저 단순히 불러오기만 하면 되는 게시판 목록을 만들어 보자!


1. DB 생성

1
2
3
4
5
6
7
8
9
10
CREATE TABLE `one_board` (
  `IDX` int(11NOT NULL AUTO_INCREMENT,
  `PRE_IDX` int(11DEFAULT NULL,
  `TITLE` varchar(100NOT NULL,
  `CONTENTS` varchar(4000NOT NULL,
  `HIT_CNT` int(11NOT NULL,
  `DEL_CHK` varchar(1NOT NULL DEFAULT 'N',
  `CREA_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `CREA_ID` varchar(30NOT NULL,  PRIMARY KEY (`IDX`)
ENGINE=InnoDB DEFAULT CHARSET=utf8
cs

MySQL을 사용하여 DB를 생성한 것이다. 



2. 파일 및 패키지 생성

Controller는 웹 클라이언트에서 들어온 요청을 해당 비즈니스 로직을 호출하고, 수행결과와 함께 응답을 해주는 Dispatcher 역할을 한다.

Service 영역은 두 개의 파일로 구성된다. Service 인터페이스와 이것을 실제로 구현한 ServiceImpl 클래스로 구현된다. 이렇게 사용하는 이유는 Spring의 IoC/D 기능을 이용한 Bean 관리 기능을 사용하기 위해서 이다. Service 인터페이스는 비즈니스 로직을 수행하기 위한 메서드를 정의하고, ServiceImp 클래스는 Service 인터페이스를 통해 정의된 메서드를 실제로 구현하는 클래스이다.

DAO는 실제로 데이터베이스에 접근하여 데이터를 가져오거나 입력하는 역할을 수행한다.


파일과 패키지들을 먼저 만들어 놓고 시작하자. 만들면서 하나하나 만들어도 된다!


2-1. BoardController.java

1
2
3
4
5
6
7
8
package tody.prj.controller;
 
import org.springframework.stereotype.Controller;
 
@Controller
public class BoardController {
 
}
cs

- 5행 : @Controller 어노테이션(Annotation)을 이용하여 Controller 객체임을 선언한다.


2-2. BoardService.java

1
2
3
4
5
package tody.prj.service;
 
public interface BoardService {
 
}
cs

** class가 아니라 interface이다.


2-3. BoardServiceImpl.java

1
2
3
4
5
package tody.prj.service;
 
public class BoardServiceImpl implements BoardService {
 
}
cs

** BoardService.java를 implements 받는다. Service 인터페이스에서 정의된 메서드들을 실제로 구현한다.


2-3. BoardDAO.java

1
2
3
4
5
6
7
8
9
10
package tody.prj.dao;
 
import org.springframework.stereotype.Repository;
 
import tody.common.dao.AbstractDAO;
 
@Repository("boardDAO")
public class BoardDAO extends AbstractDAO{
 
}
cs
** AbstractDAO는 직접 만든 클래스를 상속받은 것이다. AbstarctDAO를 상속받지 않아도 된다.
상속 받지 않고 싶으면 AbstractDAO에 구현된 기능들을 바로 여기 DAO에 구현하면 된다.
AbstarctDAO에 관한 내용은 https://to-dy.tistory.com/27?category=700248 여기를 참고하자!

2-4. board_SQL.xml
1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="board">
 
</mapper>
cs
호출할 쿼리가 저장되는 곳이다. 프로젝트에는 기본적으로 여러개의 <mapper>를 가지기 때문에 중복된 이름의 SQL이 존재 할 수 있다. 그래서 각 <mapper>마다 namespace 속성을 이용해서 <mapper>간의 유일성을 보장해야 한다.
- 03행 : board라는 이름의 namespace로 유일성을 보장한다.



3. BoardController.java ("/board/boardList")

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 tody.prj.controller;
 
import java.util.List;
import java.util.Map;
 
import javax.annotation.Resource;
 
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
 
import tody.common.common.CommandMap;
import tody.prj.service.BoardService;
 
@Controller
public class BoardController {
    
    Logger log = Logger.getLogger(this.getClass());
    
    @Resource(name="boardService")
    private BoardService boardService;
    
    @RequestMapping(value="/board/boardList")
    public ModelAndView openBoardList(CommandMap commandMap) throws Exception {
        
        ModelAndView mav = new ModelAndView("/board/boardList");
        
        List<Map<String,Object>> list = boardService.selectBoardList(commandMap);
        mav.addObject("list", list);
        
        return mav;
        
    }
 
}
cs

- 21~22행 : @Resource 어노테이션(Annotation)을 통해서 빈(bean)을 수동으로 등록하고, 그 빈의 이름을 "boardService"라 정한다. Controller에서 Service에 접근하기 위한 선언이다.

- 24행 : 요청 URL를 알려준다. ("/board/boardList") 

- 27행 : 우리가 화면에 보여줄 jsp 파일을 의미한다. 파일의 경로를 써주면 된다.

- 29행 : 게시판 목록을 저장하는 List를 선언한다. List 형식은 Map<String, Object>이다. Key와 Value의 형태로 각 게시글의 정보들이 저장된다. boardService.selectBoardList(commandMap)은 게시글 목록을 조회하는 비즈니스 로직을 호출한다. 이 메서드를 통해 얻어온 결과를 "list"라는 이름에 저장한다.

- 30행 : 서비스 로직의 결과(29행의 list에 담긴 정보)를 ModelAndView 객체에 담아서 jsp에 그 결과를 전송하여 사용하게 한다.


** selectBoardList에 빨간줄이 그어져있더라도 당황하지 말자. 아직 Service에 해당 메서드가 정의되지 않았기 때문이다.

boardService 인터페이스를 만들었다면, selectBoardList에 마우스를 가져다대서 BoardService에 selectBoardList() 메서드를 만들어주자.

(마우스를 가져다 대면, Create method 'selectBoardList(Map<String,Object>)' in type 'BoardService'가 뜰것이다.)



4. BoardService.java

1
2
3
4
5
6
7
8
9
10
11
12
package tody.prj.service;
 
import java.util.List;
import java.util.Map;
 
import tody.common.common.CommandMap;
 
public interface BoardService {
 
    List<Map<String, Object>> selectBoardList(CommandMap commandMap);
 
}
cs

- 10행 : throws Exception을 추가하였다. 



5. BoardServiceImple.java

이제 그러면 BoardServiceImpl.java에서 에러표시가 뜰 것이다. 당황하지 말자.

BoardServiceImpl 클래스는 BoardService 인테페이스를 상속받았기 때문에 BoardService 인터페이스에 있는 selectBoardList 메서드를 구현하라고 알려주는 것이다. 마찬가지로 BoardServiceImpl에 마우스를 가져다대서 selectBoardList 메서드를 구현하자.

(마우스를 가져다 대면, Add unimplemented methods가 뜰 것이다.)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package tody.prj.service;
 
import java.util.List;
import java.util.Map;
 
import org.springframework.stereotype.Service;
 
import tody.common.common.CommandMap;
 
@Service("boardService")
public class BoardServiceImpl implements BoardService {
 
    @Override
    public List<Map<String, Object>> selectBoardList(CommandMap commandMap) {
        // TODO Auto-generated method stub
        return null;
    }
 
}
cs

BoardService 인터페이스에서 정의된 메서드를 실제로 구현할 수 있도록 만들어진다. 


BoardSeviceImpl.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
package tody.prj.service;
 
import java.util.List;
import java.util.Map;
 
import javax.annotation.Resource;
 
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
 
import tody.common.common.CommandMap;
import tody.prj.dao.BoardDAO;
 
@Service("boardService")
public class BoardServiceImpl implements BoardService {
    
    Logger log = Logger.getLogger(this.getClass());
    
    @Resource(name="boardDAO")
    private BoardDAO boardDAO;
 
    @Override
    public List<Map<String, Object>> selectBoardList(CommandMap commandMap) {
        // TODO Auto-generated method stub
        return boardDAO.selectBoardList(commandMap);
    }
 
}
cs

- 19행 : @Resource 어노테이션으로 "boardDAO"이름으로 bean을 수동으로 등록한다. 마찬가지로 Service에서 데이터 접근을 위해 DAO의 객체를 선언한다.

- 25행 : boardDAO클래스의 selectBoardList메서드를 호출하여 그 결과값을 return(반환) 한다.


** 여기도 마찬가지로 selectBoardList에 빨간줄이 그일 것이다. BoardDAO에서 selectBoardList메서드가 구현되지 않았기 때문이다.

똑같이 마우스를 가져다 대서 BoardDAO에 selectBoardList 메서드를 만들어 주자,



6. BoardDAO.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package tody.prj.dao;
 
import java.util.List;
import java.util.Map;
 
import org.springframework.stereotype.Repository;
 
import tody.common.common.CommandMap;
import tody.common.dao.AbstractDAO;
 
@Repository("boardDAO")
public class BoardDAO extends AbstractDAO{
 
    @SuppressWarnings("unchecked")
    public List<Map<String, Object>> selectBoardList(CommandMap commandMap) {
        // TODO Auto-generated method stub
        return (List<Map<String,Object>>)selectList("board.selectBoardList", commandMap);
    }
 
}
cs

- 15행 : AbstractDAO를 상속받았기 때문에 selectList를 사용할 수 있다. selectList 메서드의 인자는 쿼리 이름, 쿼리가 실행되는데 필요한 변수로 2가지이다. 

여기서는 'board.selectBoardList' 가 쿼리 이름이고, commandMap이 쿼리 실행 시 필요한 변수들이다. 쿼리 이름은 무엇에 따라 결정 되는 지는 SQL 을 작성할 때 알 수 있다.

- 14행 : 컴파일 시 컴파일 경고를 사용하지 않도록 설정해 주는 것이다. unchecked는 미화인 오퍼레이션과 관련된 경고를 억제한다.



7. board_sql.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="board">
 
    <select id="selectBoardList" resultType="hashmap" parameterType="hashmap">
        <![CDATA[
            SELECT
                IDX,
                TITLE,
                HIT_CNT,
                CREA_DATE,
                CREA_ID
            FROM
                TB_BOARD
            WHERE
                DEL_CHK = 'N'
            ORDER BY IDX DESC
        ]]>
    </select>
 
</mapper>
cs

- 05행 : <select> 태그를 이용해서 쿼리문이 select문이라는 것을 명시한다. id를 selectBoardList라고 정의하였는데, BoardDAO에서 쿼리 이름을 나타낸다.

- resultType="hashmap" : 쿼리의 결과값이 HashMap 담겨서 반환된다는 것을 의미한다.

- parameterType="hashmap" : 쿼리가 실행될 때 필요한 변수가 HashMap 형태라는 것을 의미한다.

- 6~18행 : 글번호, 제목, 조회수, 작성일자, 작성자를 가져오기 위한 쿼리문이다.



8. BoardList.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
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
...
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
</head>
<body>
        ...
 
        <div class="container">
              <table class="table">
                  ...
              </thead>
              <tbody>
                  <c:choose>
                      <c:when test="${fn:length(list) > 0 }">
                          <c:forEach items="${list }" var="bList">
                              <tr>
                              <th scope="row">${bList.IDX }</th>
                              <td>${bList.TITLE }</td>
                              <td>${bList.CREA_ID }</td>
                              <td>${bList.CREA_DATE }</td>
                              <td>${bList.HIT_CNT }</td>
                            </tr>
                          </c:forEach>
                      </c:when>
                      <c:otherwise>
                          <tr>
                              <td colspan="5">조회된 결과가 없습니다.</td>
                          </tr>
                      </c:otherwise>
                  </c:choose>
                
              </tbody>
            </table>
            ...           
        </div>
 
</body>
</html>
cs

*** 중요한 부분만 잘랐다.

- 9~10행 : jstl문법을 사용하기위해 정의(?) 한 것이다.

- 22행 : Controller에서 결과를 저장한 list를 mv.addObject를 이용해서 "list"라는 이름으로 mv에 저장한 적이 있다. (mv.addObject("list",list)) 여기서 테이블에 사용할 아이템이 ${list }라고 되어있는데, 이것 바로 우리가 mv에 추가한 그 list 이다. 그 list에 담긴 데이터를 var에 있는 bList라는 변수에 저장한다. forEach문을 통해 반복수행한다.

- 24~28행 : bList 변수에 저장되 데이터를 가져온다. 여기선 쿼리를 실행시켰을 때 나온 데이터의 이름이다.(** 대소문자 구분)



9. 실행

9-1. 톰캣 실행

첫 화면 페이지가 나온다!


9-2. board/boardList

주소창에 board/boardList를 치자. 해당 주소는 Controller에 Mapping URL이다.

이렇게 게시판이 나오면 성공한 것이다! 난 각종 에러와의 싸움끝에 드디어 나왔다ㅠㅠ 흑

지금은 DB에 데이터가 없기때문에 조회된 결과가 없다고 나오는 게 당연하다. 그럼 이제 데이터를 넣고 게시판 목록에 가보자.


9-3. DB 데이터 입력

각자 알아서 데이터를 넣어주자


9-4. 실행

아까처럼 board/boardList 호출하자.

이렇게 입력한 데이터들이 나오면 성공한 것이다! 오예!