스프링 게시판 만들기1
1. DB 입력
우선 게시판을 만들기 앞서 게시판에 필요한 테이블을 만들어 준다. DB는 Mariadb를 이용하였다.
CREATE TABLE boardtest(
idx int AUTO_INCREMENT PRIMARY KEY,
writer VARCHAR(20) NOT NULL,
title VARCHAR(30) NOT NULL,
content TEXT NOT NULL,
reg_date DATE NOT NULL,
cnt int default 0
) DEFAULT CHARSET=utf8 ;
idx는 글 번호, writer은 작성자, title은 제목, contents는 내용, reg_date는 작성일, hit는 조회수를 위해 만들었고, default charset은 기본 언에셋을 utf-8로 지정하는 명령어다.
2. pom.xml
의존성을 위하여 pom.xml에 필요한 라이브러리들을 등록한다.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.spring</groupId>
<artifactId>test</artifactId>
<name>SpringStudy</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.8</java-version>
<org.springframework-version>4.3.3.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.6.0</version>
</dependency>
<!-- mariaDB -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
3. 필요한 파일 생성
이번 게시판 만들기 예제에서 필요한 파일들을 미리 생성해 놓는다.
src/main/java에 controller, dao, domain(vo), service, service.impl 패키지를 만들어 그 밑에 java파일들을 생성해 놓는다.
처음 프로젝트를 생성하면 만들어지는 HomeController는 삭제하였다.
그리고, resources에 mybatis-config.xml을 생성하고, mappers패키지를 생성한 다음 그 밑에 testMapper.xml을 생성한다.
4. POST 인코딩 설정
post로 값을 넘겨서 확인할 때 값이 깨져서 보여서 post 인코딩 설정을 하였다.
web.xml에 아래의 소스를 추가시켜주면 된다.
<!-- post incoding -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5. Mybatis MariaDB 연결 설정
프로젝트를 생성하면 기본적으로 생성되는 root-context.xml파일에 내용을 추가한다.
<!-- MariaDB JDBC DataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.mariadb.jdbc.Driver" />
<property name="url" value="jdbc:mariadb://아이피주소:포트/DB명" />
<property name="username" value="아이디" />
<property name="password" value="패스워드" />
</bean>
<!-- mybatis SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/mybatis-config.xml" />
<property name="mapperLocations" value="classpath:/mappers/**/*Mapper.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
ip주소와 포트, DB명과 id, password만 입력해 직접 입력해 주면 된다. (MariaDB 기본 포트 3306)
필자는 test 데이터 베이스를 사용하였다.
처음 bean은 mariadb연결을 위한 설정을 하는 곳이고,
두 번째 bean은 설정 파일과 sql문이 들어있는 xml파일을 등록시켜주는 부분이다.
세 번째 bean에서는 SqlSession을 사용하기 위해 등록해 주었다.
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 자동으로 카멜케이스 규칙으로 변환 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.spring.test.domain"/>
</typeAliases>
</configuration>
타이핑을 줄이기 위해 typeAliases를 사용하였고, 자동으로 카멜케이스 규칙으로 변환해주는것을 등록하였다.
카멜케이스로 변환해 준다는것은 예를 들어 regDate는 reg_date처럼 변환해 주는 것이다.
testMapper.xml
<?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="BoardMapper">
<select id="getBoardList" resultType="BoardVO">
SELECT idx,title,writer,reg_date,cnt
FROM boardtest
ORDER BY idx DESC
</select>
<select id="getContent" resultType="BoardVO">
<![CDATA[
SELECT *
FROM boardtest
WHERE idx = #{idx}
]]>
</select>
<select id="insertBoard">
<![CDATA[
INSERT INTO boardtest(title,writer,content,reg_date)
VALUES(#{title}, #{writer}, #{content}, now())
]]>
</select>
<update id="updateBoard">
<![CDATA[
UPDATE boardtest SET
title = #{title},
content = #{content}
WHERE idx = #{idx}
]]>
</update>
<delete id="deleteBoard">
<![CDATA[
DELETE FROM boardtest
WHERE idx = #{idx}
]]>
</delete>
</mapper>
CRUD를 위한 sql문을 미리 작성해 준다.
6. Controller 작성
일단 처음 접속했을 때 index.jsp로 가기 위하여 미리 만들어둔 MainController.java파일을 수정한다.
package com.spring.test.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MainController {
@RequestMapping("/")
public String getMain() {
return "index"; // View 이름 리턴
}
}
return을 index.jsp가 아닌 index만 입력한 이유는 servlet-context.xml 파일에 다음과 같은 내용이 있기 때문이다.
없다면 작성하여 주면된다.
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
prefix로 경로를 잡아주고
suffix로 return시 자동으로 .jsp를 붙여준다.
두 번째로 BoardController.java파일을 수정해 준다.
BoardController는 CRUD 글을 쓰고, 읽고, 수정하고, 삭제하기 위한 요청을 했을 때, 어떻게 처리할지 작성한다.
요청이 들어오면 Service로 보낸다.
package com.spring.test.controller;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;
import com.spring.test.domain.BoardVO;
import com.spring.test.service.BoardService;
@Controller
@SessionAttributes("board")
public class BoardController {
@Autowired
private BoardService boardService;
//글 목록 검색
@RequestMapping("/getBoardList.do")
public String getBoardList(BoardVO vo, Model model) {
List<BoardVO> boardList = boardService.getBoardList();
// Model 정보 저장
model.addAttribute("boardList",boardList);
return "boardList"; // View 이름 리턴
}
// 글 상세 조회
@RequestMapping("/getContent.do")
public String getBoard(BoardVO vo, Model model) {
model.addAttribute("board", boardService.getContent(vo)); // Model 정보 저장
return "content"; // View 이름 리턴
}
// 글 쓰기
@RequestMapping(value="/insertBoard.do")
public String insertBoard(BoardVO vo) throws IOException {
boardService.insertBoard(vo);
return "redirect:getBoardList.do";
}
// 글 쓰기 페이지 이동
@RequestMapping("/moveInsertBoard.do")
public String moveInsertBoard()throws Exception{
return "insertBoard";
}
// 글 수정
@RequestMapping("/updateBoard.do")
public String updateBoard(@ModelAttribute("board") BoardVO vo) {
boardService.updateBoard(vo);
return "redirect:getBoardList.do";
}
// 글 삭제
@RequestMapping("/deleteBoard.do")
public String deleteBoard(BoardVO vo) {
boardService.deleteBoard(vo);
return "redirect:getBoardList.do";
}
}
글 수정과 글 삭제에 redirect를 써준 이유는 servlet-context.xml 설정때문에 getBoardList.do로 가는것이 아닌 /WEB-INF/views/에 있는 getBoardList.do.jsp로 가기때문이다. redirect를 안써주게 되면 당연히 getBoardList.do.jsp파일이 없어 어 404에러가 난다.
위에 Model은 Jsp파일에서 EL/JSTL을 사용하기 위해서 등록해 주는 부분이다.
7. vo 작성
자바빈즈인 VO를 작성한다.
package com.spring.test.domain;
import java.util.Date;
public class BoardVO {
private int idx;
private String title;
private String writer;
private String content;
private Date regDate;
private int cnt;
public int getIdx() {
return idx;
}
public void setIdx(int idx) {
this.idx = idx;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
@Override
public String toString() {
return "BoardVO [idx=" + idx + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regDate="
+ regDate + ", cnt=" + cnt + "]";
}
}
8. BoardService 작성
boardService.java를 작성한다. boardService.java는 인터페이스로 작성하고 boardServiceImpl.java에서 상속받아 구체적인 내용을 작성한다.
service에서는 요청한 서비스에 대한 행위를 하도록 DAO를 호출한다.
boardService.java
package com.spring.test.service;
import java.util.List;
import com.spring.test.domain.BoardVO;
public interface BoardService {
// 글 목록 조회
List<BoardVO> getBoardList();
// 글 상세 조회
BoardVO getContent(BoardVO vo);
// 글 등록
void insertBoard(BoardVO vo);
// 글 수정
void updateBoard(BoardVO vo);
// 글 삭제
void deleteBoard(BoardVO vo);
}
boardServiceImpl.java
package com.spring.test.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.spring.test.dao.BoardDAO;
import com.spring.test.domain.BoardVO;
import com.spring.test.service.BoardService;
@Service
public class BoardServiceImpl implements BoardService{
@Autowired
private BoardDAO boardDAO;
@Override
public List<BoardVO> getBoardList() {
return boardDAO.getBoardList();
}
@Override
public BoardVO getContent(BoardVO vo) {
return boardDAO.getContent(vo);
}
@Override
public void insertBoard(BoardVO vo) {
boardDAO.insertBoard(vo);
}
@Override
public void updateBoard(BoardVO vo) {
boardDAO.updateBoard(vo);
}
@Override
public void deleteBoard(BoardVO vo) {
boardDAO.deleteBoard(vo);
}
}
10. DAO작성
DAO에서는 mybatis랑 연동하여 요청받은 행위를 한다.
package com.spring.test.dao;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.spring.test.domain.BoardVO;
@Repository
public class BoardDAO{
@Autowired
private SqlSessionTemplate mybatis;
public void insertBoard(BoardVO vo) {
System.out.println("===> Mybatis로 insertBoard() 기능 처리");
mybatis.insert("BoardMapper.insertBoard", vo);
}
public void updateBoard(BoardVO vo) {
System.out.println("===> Mybatis로 updateBoard() 기능 처리");
mybatis.update("BoardMapper.updateBoard", vo);
}
public void deleteBoard(BoardVO vo) {
System.out.println("===> Mybatis로 deleteBoard() 기능 처리");
mybatis.delete("BoardMapper.deleteBoard", vo);
}
public BoardVO getContent(BoardVO vo) {
System.out.println("===> Mybatis로 getContent() 기능 처리");
return (BoardVO) mybatis.selectOne("BoardMapper.getContent", vo);
}
public List<BoardVO> getBoardList() {
System.out.println("===> Mybatis로 getBoardList() 기능 처리");
return mybatis.selectList("BoardMapper.getBoardList");
}
}
11. JSP파일 작성
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>게시판 만들기 </h1>
<hr>
<br> <a href="getBoardList.do">글 목록 보기</a>
<hr>
</body>
</html>
boardList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>글 목록</title>
</head>
<body>
<table border="1" >
<tr>
<th bgcolor="" width="50">no</th>
<th bgcolor="" width="200">제목</th>
<th bgcolor="" width="150">작성자</th>
<th bgcolor="" width="150">작성일</th>
<th bgcolor="" width="100">조회수</th>
</tr>
<c:choose>
<c:when test="${!empty boardList}">
<c:forEach items="${boardList }" var="board">
<tr>
<td>${board.idx }</td>
<td align="left"><a href="getContent.do?idx=${board.idx }">
${board.title }</a></td>
<td>${board.writer }</td>
<td><fmt:formatDate value="${board.regDate }" pattern="yyyy-MM-dd"/></td>
<td>${board.cnt }</td>
</tr>
</c:forEach>
</c:when>
<c:otherwise>
<tr>
<td colspan="5">등록된 글이 없습니다.</td>
</tr>
</c:otherwise>
</c:choose>
</table>
<br>
<a href="moveInsertBoard.do">글 쓰기</a>
</body>
</html>
content.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 상세</title>
</head>
<body>
<h1>글 상세</h1>
<hr>
<form action="updateBoard.do" method="post">
<input name="seq" type="hidden" value="${board.idx}" />
<table border="1">
<tr>
<td bgcolor="orange" width="70">제목</td>
<td align="left"><input name="title" type="text"
value="${board.title }" /></td>
</tr>
<tr>
<td bgcolor="orange">작성자</td>
<td align="left">${board.writer }</td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left"><textarea name="content" cols="40" rows="10">${board.content }</textarea></td>
</tr>
<tr>
<td bgcolor="orange">등록일</td>
<td align="left"><fmt:formatDate value="${board.regDate }" pattern="yyyy-MM-dd"/></td>
</tr>
<tr>
<td bgcolor="orange">조회수</td>
<td align="left">${board.cnt }</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="글 수정" /></td>
</tr>
</table>
</form>
<hr>
<a href="moveInsertBoard.do">글 쓰기</a>
<a href="deleteBoard.do?idx=${board.idx }">글 삭제</a>
<a href="getBoardList.do">글 목록</a>
</body>
</html>
insertBoard.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>새글등록</title>
</head>
<body>
<h1>글 쓰기</h1>
<hr>
<form action="insertBoard.do" method="POST">
<table border="1">
<tr>
<td bgcolor="orange" width="70">제목</td>
<td align="left"><input type="text" name="title" /></td>
</tr>
<tr>
<td bgcolor="orange">작성자</td>
<td align="left"><input type="text" name="writer" size="10" /></td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left"><textarea name="content" cols="40" rows="10"></textarea></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="글 쓰기" /></td>
</tr>
</table>
</form>
<hr>
<a href="getBoardList.do">글 목록 가기</a>
</body>
</html>
이제 필요한 내용은 다 작성하였다.
스프링은 요청이 들어오면 다음과 같이 처리한다.
1. 클라이언트가 요청
2. 컨트롤러가 요청을 받아 서비스 호출
3. 서비스가 다오 호출
4. 다오가 sqlSessionTemplate객체를 이용해서 DB호출
6. 컨트롤러에서 jsp에 전달해줄 내용 model에 저장
7. jsp페이지 호출
8. 서블릿이 해당 jsp문서에 ${}처리된 것들 혹은 jstl명령어를 읽어서 html문서 작성.
9. 클라이언트에게 완성된 html문서 응답.