0. 구현하기 전
uri 축소하기
- REST방식의 uri는
method + uri
로 구성되어 있다.
- POST방식은 테이블 기반으로 살펴보았을 때, 일반적으로 정보를 등록하겠다는 의미가 강하다.
- 기존의 URI가
/board/selectOne.do?no=?
일때 Spring 방식의 URI는 /board?no=5
또는 /board/5
- GET - 조회
- POST - 수정
- DELETE - 삭제
REST는 왜쓸까?
- URI로 내 정보를 전달하기 위해 사용.
- 분산서버에서 많이 씀
구현 순서
- SpringContainer 실행
- Spring-mvc.xml에 등록된 bean객체 생성
- SqlSessionFactory, SqlSessionTemplate…
- Autowired로 자동주입
그외 Tip
- 게시판 하나 조회하는데 Spring을 사용하지 않음
- Spring은 규모가 있는 프로젝트를 위해 사용하는 프레임워크
- 계속 유지보수가 필요한 프로젝트에 필요 ( 변화에 민감하지 않음 ) - 상속, 추상클래스, 인터페이스
- 인터페이스먼저만들어놓고 DAO 생성하는게 맞다.
1. 환경설정
pom.xml
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
config/spring/spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="kr.ac.kopo" />
<mvc:annotation-driven />
<mvc:default-servlet-handler />
<mvc:view-resolvers>
<mvc:jsp prefix="/WEB-INF/jsp/" suffix=".jsp" />
</mvc:view-resolvers>
</beans>
webapps/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>spring-mvc</display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:config/spring/spring-mvc.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<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>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
2. Spring DB
- spring은 미리 컨테이너가 필요한 객체를 만들어놓고 필요한 객체를 주입하는 방식을 사용한다.
- SQL Session팩토리 객체 -> SQL Session Templates객체 생성
2-1. 환경설정
- DriverManager를 이용하여 DataSource 설정하는 방법
- 커넥션 풀을 이용한 data source 설정하는 방법 :
DBCP API를 이용한 설정
- IO를 사용하면 DB 열고 닫는데 시간 오래걸림
- 인터페이스를 이용하는 방법
- DAO 에서 SQL Session을 얻기 위해 XML 문서에 SqlSessionTemplate를 선언
- 필요 라이브러리
- commons-dbcp
- ojdbc8
- spring-jdbc
- MyBatis
- MyBatis spring
01) commons-dbcp, ojdbc8 추가
<!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc8 -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>21.1.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
02) DataSource설정
- config/spring/spring-mvc.xml
<!-- DataSource설정 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:xe"/>
<property name="username" value="hr"/>
<property name="password" value="hr"/>
</bean>
02-1) properties파일로 db driver 정보 넘기고 싶을 때.
- config/prop 폴더 생성 후
db.properties
파일 생성후 DB연결 정보 입력
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:포트번호:sid
username=hr
password=hr
- config/spring/spring-mvc.xml
<!-- properties파일 연결 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:config/prop/db.properties" />
<property name="fileEncoding" value="UTF-8"></property>
</bean>
<!-- DataSource설정 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
잘 연결되었나 확인하기
- 내가 만든 메소드가 잘 실행되는지 확인해보고 싶을 때 수행하는 것이 단위테스트 (JUnit)
- 필요 라이브러리를 pom.xml 에 붙여넣는다.
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.9</version>
<scope>test</scope>
</dependency>
- src/test/java에 class 만들기
- kr.ac.kopo ->
MybatisTest.java
생성
package kr.ac.kopo;
import static org.junit.Assert.assertNotNull;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:config/spring/spring-mvc.xml"})
public class MybatisTest {
@Autowired
private DataSource dataSource;
@Test // Test실행시 사용하는 메소드
public void DataSourceTest() throws Exception{
System.out.println("dataSource : " + dataSource);
//또는
assertNotNull(dataSource);//올바르게 주입되었는지 확인.
}
}
- Junit 실행하면 dataSource가 나옴.
03) MyBatis, MyBatis Spring 추가
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
04) Sql Session Factory 연결
src/main/resources
에 mybatis
폴더생성 후 sqlMapConfig.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>
</configuration>
config/spring/spring-mvc.xml
에 sqlSessionFactory
, sqlSessionTemplate
추가
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:config/mybatis/sqlMapConfig.xml" />
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory" />
</bean>
05) Mapper파일 만들기
- config/sqlmap/oracle경로에
sqlmap-board.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="board.BoardDAO">
</mapper>
- config/spring/
spring-mvc.xml
파일 sqlSessionFactory
부분 property 추가 (Mapper 추가)
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:config/mybatis/sqlMapConfig.xml" />
<property name="mapperLocations" value="classpath:config/sqlmap/oracle/*.xml" />
</bean>
2-2. 실습하기
01) 전체게시글 조회
sqlmap-board.xml
- mapper태그 안에 추가
- 편의를 위해 resultMap 사전 설정
<resultMap type="kr.ac.kopo.board.vo.BoardVO" id="boardMap">
<result column="view_cnt" property="viewCnt"/>
<result column="reg_date" property="regDate"/>
</resultMap>
<select id="selectAll" resultMap="boardMap">
select no, title, writer, to_char(reg_date,'yyyy-mm-dd') as reg_date
from t_board
order by no desc
</select>
DAO Interface 생성
package kr.ac.kopo.board.dao;
import java.util.List;
import kr.ac.kopo.board.vo.BoardVO;
public interface BoardDAO {
/**
* 전체 게시글 조회
* @return DB에서 조회된 BoardVO List
*/
List<BoardVO> searchAll();
}
DAO Class 생성
package kr.ac.kopo.board.dao;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import kr.ac.kopo.board.vo.BoardVO;
@Repository
public class BoardDAOImpl implements BoardDAO{
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
public List<BoardVO> searchAll() {
List<BoardVO> list = sqlSessionTemplate.selectList("board.BoardDAO.selectAll");
return list;
}
}
- 단위 테스트 (BoardTest.java)
- componet scan에 의해 객체가 만들어졌기 때문에 Test에서 boardDAO로 자동주입할 수 있다.
package kr.ac.kopo;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import kr.ac.kopo.board.dao.BoardDAO;
import kr.ac.kopo.board.vo.BoardVO;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:config/spring/spring-mvc.xml"})
public class BoardTest {
@Autowired
private BoardDAO boardDAO;
//private SqlSessionTemplate sqlSessionTemplate;
@Test
public void 전체게시글조회Test() throws Exception{
List<BoardVO> list = boardDAO.searchAll();
//List<BoardVO> list = sqlSessionTemplate.selectList("board.BoardDAO.selectAll");
for(BoardVO board:list) {
System.out.println(board);
}
}
}
Service Interface 생성
package kr.ac.kopo.board.service;
import java.util.List;
import kr.ac.kopo.board.vo.BoardVO;
public interface BoardService {
List<BoardVO> selectAllBoard();
}
Service 생성
package kr.ac.kopo.board.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.ac.kopo.board.dao.BoardDAO;
import kr.ac.kopo.board.vo.BoardVO;
@Service
public class BoardServiceImpl implements BoardService{
@Autowired
private BoardDAO boardDAO;
/**
* DAO에서 sqlSessionTemplate로 반환한 전체게시글 리스트 반환
*/
public List<BoardVO> selectAllBoard() {
List<BoardVO> boardList = boardDAO.searchAll();
return boardList;
}
}
Controller생성
package kr.ac.kopo.board.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class BoardController {
@RequestMapping("/board")
public ModelAndView selectAll() {
List<BoardVO> boardList = service.selectAllBoard();
ModelAndView mav = new ModelAndView("board/list");
mav.addObject("list", boardList); // 공유영역에 업로드
return mav;
}
}
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>
<h2>안녕!</h2>
<a href="${ pageContext.request.contextPath }/board">게시글</a>
</body>
</html>
list.jsp
<div align="center">
<hr>
<h2>게시판 목록</h2>
<hr>
<br>
<table id="list" border="1" style="width:100%">
<tr>
<th width="7%">번호</th>
<th>제목</th>
<th width="16%">작성자</th>
<th width="20%">등록일</th>
</tr>
<c:forEach items="${ requestScope.list }" var="l" varStatus="loop">
<tr <c:if test="${ loop.index mod 2 ne 0 }"> class="odd"</c:if>>
<td>${ l.no }</td>
<td>
<a href="${ pageContext.request.contextPath }/board/detail?no=${l.no}">
<%-- href="javascript:doAction()" a태그에서 자바스크립트 문법 실행하도록. --%>
<%-- <a href="javascript:doAction(${ l.no })"> --%>
<c:out value="${ l.title }" />
</a></td>
<td>${ l.writer }</td>
<td>${ l.regDate }</td>
</tr>
</c:forEach>
</table>
<br>
<c:if test="${ not empty sessionScope.loginfo }">
<button id="addBtn">새글등록</button>
</c:if>
</div>
02) 상세게시글 조회
DAO Interface
/**
* 글 번호로 게시글 조회
* @return DB에서 No로 조회된 BoardVO
*/
BoardVO searchOne(int no);
DAO
public BoardVO searchOne(int no) {
BoardVO board= sqlSessionTemplate.selectOne("board.BoardDAO.selectByNo", no);
return board;
}
Service Interface
BoardVO selectOneBoard(int no);
Service
public BoardVO selectOneBoard(int no) {
BoardVO board = boardDAO.searchOne(no);
return board;
}
Contorller 메소드 추가1 ( ?no= 방법 )
/**
* 상세게시글 조회 ?no=
* @param request
*/
@RequestMapping("/board/detail")
public void selectByNo(HttpServletRequest request) {
int no = Integer.parseInt(request.getParameter("no"));
}
//또는
/**
* 상세게시글 조회 ?no=
* @param request
*/
@RequestMapping("/board/detail")
public void selectByNo(@RequestParam("no") int no) {
// HttpServletRequest request
//int no = Integer.parseInt(request.getParameter("no"));
System.out.println(no);
}
- list-> detail연결부분 herf 링크
<a href="${ pageContext.request.contextPath }/board/detail?no=${l.no}">
Contorller 메소드 추가2 ( REST방식 )
- GET방식일 때
/board/11
형식 (11번 게시물 조회)
@RequestMapping("/board/{no}")
public ModelAndView selectByNo2(@PathVariable("no") int no) { // Path에서 날아온 변수를 사용 (RequestMapping{no}에 설정한 변수이름과 같게한다.)
BoardVO board = service.selectOneBoard(no);
//BoardVO board = sessionTemplate.selectOne("board.BoardDAO.selectByNo", 11);
ModelAndView mav = new ModelAndView("board/detail");
mav.addObject("board", board);
return mav;
}
- list-> detail연결부분 herf 링크
<a href="${ pageContext.request.contextPath }/board/${l.no}">
- 목록으로 되돌아가기 링크 변경
- location.href=”${ pageContext.request.contextPath }/board