2020. 12. 17. 11:16ㆍ교육과정/KOSMO
키워드 : Spring Legacy Project / 애자일 개발과 스크럼
****
0. Spring Legacy Project 와 Dynamic Web Project 의 디렉토리 구조 차이
1. 스프링MVC 실습
(1) 새 프로젝트 gWebBoard 생성 후 실습 파일 붙여넣기
① pom.xml 에서 Java 1.8버전으로, Spring 5.0.8 버전으로 변경
② junit 4.12 버전으로 변경
(2) 프로젝트명 우클릭 - Properties
① Project facets 에서 버전 변경 후 Apply
② Server 에서 Tomcat 선택 후 Apply
(3) web.xml 에서 서블릿 url-pattern을 * . do 로 변경
(4) 불필요한 com.javassem.basic 패키지 삭제
(5) servlet-context.xml 에서 2라인을 보면 xmlns에서 mvc만 이름 생략 가능함을 확인할 수 있다.
이전 프로젝트에서는 xmlns에서 이름 생략 가능한 태그는 beans 였다.
이름 생략 가능한 태그는 단 하나 뿐이므로 보다 많이 사용할 것 같은 태그로 지정하면 된다.
<context> 태그에서는 스캔할 패키지를 "com.javassem"으로 변경해준다.
<!-- 해당 패키지를 스캔하여 지정된 @를 찾아서 실행 -->
<context:component-scan base-package="com.javassem" />
(6) <resources> 태그에서 location 속성값에 해당하는 경로에만 클라이언트가 접근할 수 있다.
이 경로에는 이후 css, js, jquery, image, files 가 오게 된다.
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<!-- 클라이언트가 접근할 수 있는 부분 : css, js, jquery, image, files -->
<resources mapping="/resources/**" location="/resources/" />
(7) mybatis-config.xml 에서 com.javassem.domain 패키지의 BoardVO 클래스에
별칭으로 board를 주었음을 확인할 수 있다.
<!-- Alias 설정 -->
<typeAliases>
<typeAlias alias="board" type="com.javassem.domain.BoardVO" />
</typeAliases>
(8) rootContext.xml 에서 localhost 변경
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
<property name="url" value="jdbc:oracle:thin:@192.168.0.17:1521:orcl"></property>
<property name="username" value="scott"></property>
<property name="password" value="tiger"></property>
</bean>
(9) index.jsp 에서 작성된 getBoardList.do 요청을 받기 위해
com.javassem.controller 패키지의 BoardController.java 가 컨트롤러 역할을 하고
해당 요청을 받도록 애노테이션을 붙인다.
@Controller
public class BoardController {
@RequestMapping("getBoardList.do")
public void getBoardList() {
}
}
(10) getBoardList( ) 메소드에서 boardService클래스의 getBoardList( ) 메소드를 수행하도록 작성한다.
이 메소드는 BoardVO 타입을 인자로 받는다.
@Autowired
private BoardService boardService;
@RequestMapping("getBoardList.do")
public void getBoardList(BoardVO vo) {
// BoardVO vo 현재는 필요 업음
boardService.getBoardList(vo);
}
(11) getBoardList( ) 메소드 수행 결과를 List로 받은 뒤 HashMap에 담고 리턴해준다.
@RequestMapping("getBoardList.do")
public Map getBoardList(BoardVO vo) {
List<BoardVO> list = boardService.getBoardList(vo);
Map map = new HashMap();
map.put("boardList", list);
return map;
}
(12) getBoardList.jsp 에서 아래 태그는 컨트롤러에서 넘겨준 데이터를 가리킨다.
<c:forEach items="${boardList }" var="board">
<tr>
<td>${board.seq }</td>
<td align="left"><a href="getBoard.do?seq=${board.seq }">
${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>
(13) 서비스 역할을 할 BoardServiceImpl.java 에 boardService 라는 이름으로 빈을 생성하여
컨트롤러에서 찾아올 수 있도록 애노테이션을 붙인다.
@Service("boardService")
public class BoardServiceImpl implements BoardService {
(14) 클래스 내의 BoardDAOImpl 에 대해서도 스프링 컨테이너에서 객체를 찾아서 연결해주도록 애노테이션을 붙인다.
@Service("boardService")
public class BoardServiceImpl implements BoardService {
@Autowired
private BoardDAOImpl boardDAO;
(15) 서비스에서 수행하는 getBoardList( ) 메소드는 BoardDAOImpl 의 getBoardList( ) 메소드를 호출한다.
public List<BoardVO> getBoardList(BoardVO vo) {
return boardDAO.getBoardList(vo);
}
(16) BoardDAOImpl.java 에서 Repository 역할을 하도록 애노테이션을 붙인다.
@Repository("boardDAO")
public class BoardDAOImpl implements BoardDAO{
(17) 클래스 안의 SqlSessionTemplate 이 DB와 연결해주는 커넥션 역할을 한다.
해당 클래스의 객체를 스프링 컨테이너에서 연결해주도록 애노테이션을 붙인다.
@Autowired
private SqlSessionTemplate mybatis;
( 실행결과 )
(18) 글 제목을 클릭했을 때 게시글을 확인하기 위해서는
getBoard.do 요청이 수행되어야 하며 파라미터로 글 번호를 가져간다.
<td align="left"><a href="getBoard.do?seq=${board.seq }">${board.title}</a></td>
(19) BoardController 에서 getBoard.do 요청을 받는 메소드를 작성하고
글번호 데이터를 파라미터로부터 받을 수 있도록 BoardVO를 인자로 지정한다.
@RequestMapping("getBoard.do")
public void getBoard(BoardVO vo) {
}
(20) BoardService로부터 vo를 받아와서 변수 result에 담는다.
데이터를 뷰로 넘기기 위해 ModelAndView 방식을 채택했으므로
setViewName( ) 함수로 다음 페이지를 getBoard.jsp로 지정하고,
addObject( ) 함수로 변수에 담긴 데이터를 넘겨주는 스크립트를 작성한다.
mv를 리턴해야 하므로, 리턴형을 void에서 ModelAndView로 수정한다.
@RequestMapping("getBoard.do")
public MpdelAndView getBoard(BoardVO vo) {
BoardVO result = boardService.getBoard(vo);
ModelAndView mv = new ModelAndView();
mv.setViewName("getBoard");
mv.addObject("", result);
return mv;
}
(21) getBoard.jsp 를 확인하면 board라는 이름으로 데이터를 전달받고 있음을 확인할 수 있으므로,
mv에 추가되는 오브젝트명을 board로 작성해준다.
<input name="seq" type="hidden" value="${board.seq}" />
@RequestMapping("getBoard.do")
public void getBoard(BoardVO vo) {
BoardVO result = boardService.getBoard(vo);
ModelAndView mv = new ModelAndView();
mv.setViewName("getBoard");
mv.addObject("board", result);
}
( 실행결과 )
(22) 새 글 작성을 클릭했을 때 글 작성 페이지로 이동하기 위해 insertBoard.do 요청을 수행하도록
<a href="insertBoard.do">글등록</a>
(23) 컨트롤러에서
@RequestMapping("insertBoard.do")
public void insertBoard() {
}
(24) insertBoard.jsp 에서 새 글 등록 버튼을 클릭했을 때 글이 등록되려면
saveBoard.do 요청이 수행되어야 함을 확인할 수 있다.
<form action="saveBoard.do" method="post">
(25) <form>의 요소들에 name 속성이 없으면 데이터가 전송되지 않으므로 VO와 동일한 이름을 부여해준다.
<input type="text" name='title' />
<input type="text" size="10" name='writer' />
<textarea cols="40" rows="10" name='content' >
(26) 컨트롤러에서 saveBoard.do 요청을 수행할 수 있도록 save( ) 메소드를 작성하는데
이 때, name과 BoardVO의 변수명을 동일하게 부여했으므로,
인자에 BoardVO를 기술하면 정보를 알아서 가져온다.
받아온 데이터 vo로 DB에 입력하려면
BoardService 의 insertBoard( ) 메소드를 수행하는 스크립트를 작성한다.
@RequestMapping("saveBoard.do")
public void save(BoardVO vo) {
boardService.insertBoard(vo);
}
( 실행결과 )
(27) 글 저장은 되었으나 목록 보기 페이지로 자동 전환이 되지 않기 때문에
글 저장시 리다이렉트가 함께 수행되도록 save( ) 메소드를 수정한다.
@RequestMapping("saveBoard.do")
public String save(BoardVO vo) {
boardService.insertBoard(vo);
return "redirect:/getBoardList.do";
}
( 실행결과 )
: 글 작성 후 정상적으로 저장되고, 목록으로 페이지 자동 전환됨
(28) 글 상세 페이지 getBoard.jsp 에서 글 수정 버튼을 클릭했을 때 수행될 요청을 확인한다.
<form action="updateBoard.do" method="post">
(29) 컨트롤러에서 요청을 수행할 함수를 작성한다.
@RequestMapping("updateBoard.do")
public void update() {
}
(30) 글 수정 페이지에 기존 글 내용을 가져와서 화면에 표시하도록 메소드의 인자로 받아와야 한다.
작성한 내용은 updateBoard( ) 메소드로 DB까지 전달되고,
완료 후 글 상세 페이지로 자동 전환되도록 리다이렉트 시켜주되, 글 번호를 가져가도록 작성한다.
@RequestMapping("updateBoard.do")
public String update(BoardVO vo) {
boardService.updateBoard(vo);
return "redirect:getBoard.do?seq=" + vo.getSeq() + "";
}
( 실행결과 )
: 작성한 글 변경 후 "글 수정" 버튼 클릭시 DB에 반영된다.
수정된 글을 바로 보기 때문에 사용자 입장에서는 페이지 전환이 눈에 보이진 않는다.
(31) 삭제 버튼을 클릭했을 때 해당 글이 DB에서도 지워지고 목록보기로 페이지 전환되도록
컨트롤러에서 스크립트를 작성한다.
@RequestMapping("deleteBoard.do")
public String delete(BoardVO vo) {
boardService.deleteBoard(vo);
return "redirect:getBoardList.do";
}
( 실행결과 )
(32) 요청을 수행할 때마다 내용이 비어있는 함수를 매번 작성해야 하는 번거로움을 줄이려면,
다음과 같은 함수를 한 번 작성하고 나면 비슷한 코드 작성을 하지 않아도 알아서
변수명에 해당되는 페이지로 뷰 페이지를 지정한다.
// @RequestMapping("insertBoard.do")
// public void insert() {
//
// }
@RequestMapping("{step}.do")
public String insert(@PathVariable String step) {
return step;
}
< 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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javassem</groupId>
<artifactId>basic</artifactId>
<name>gWebMybatis</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<!-- ### 버전 변경 ### -->
<java-version>1.8</java-version>
<org.springframework-version>5.0.8.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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- 마이바티스 추가 -->
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- SPRING JDBC 필요 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- 트랜잭션 필요 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</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>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</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>
<web.xml 전체 소스 코드 >
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- 한글 인코딩 -->
<filter>
<filter-name>characterEncoding</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>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
< servlet-context.xml 전체 소스 코드 >
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<!-- 클라이언트가 접근할 수 있는 부분 : css, js, jquery, image, files -->
<resources mapping="/resources/**" location="/resources/" />
<!-- 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>
<!-- 해당 패키지를 스캔하여 지정된 @를 찾아서 실행 -->
<context:component-scan base-package="com.javassem" />
</beans:beans>
< 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>
<!-- Alias 설정 -->
<typeAliases>
<typeAlias alias="board" type="com.javassem.domain.BoardVO" />
</typeAliases>
</configuration>
< root-context.xml 전체 소스 코드 >
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
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-4.1.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
<property name="url" value="jdbc:oracle:thin:@192.168.0.17:1521:orcl"></property>
<property name="username" value="scott"></property>
<property name="password" value="tiger"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml"></property>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
</bean>
</beans>
< BoardMapper.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="BoardDAO">
<insert id="insertBoard" parameterType="board">
<![CDATA[
INSERT INTO BOARD(SEQ, TITLE, WRITER, CONTENT, REGDATE, CNT)
VALUES(board_seq.nextval,
#{title}, #{writer}, #{content},
sysdate, 0)
]]>
</insert>
<update id="updateBoard" parameterType="board">
<![CDATA[
UPDATE BOARD SET
TITLE = #{title},
CONTENT = #{content}
WHERE SEQ = #{seq}
]]>
</update>
<delete id="deleteBoard" parameterType="board">
<![CDATA[
DELETE BOARD
WHERE SEQ = #{seq}
]]>
</delete>
<select id="getBoard" parameterType="board" resultType="board">
<![CDATA[
SELECT *
FROM BOARD
WHERE SEQ = #{seq}
]]>
</select>
<select id="getBoardList" resultType="board">
<![CDATA[
SELECT *
FROM BOARD
ORDER BY SEQ DESC
]]>
</select>
</mapper>
< BoardController.java 전체 소스 코드 >
package com.javassem.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.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import com.javassem.domain.BoardVO;
import com.javassem.service.BoardService;
@Controller
public class BoardController {
@Autowired
private BoardService boardService;
@RequestMapping("getBoardList.do")
public Map getBoardList(BoardVO vo) {
// BoardVO vo 현재는 필요 업음
List<BoardVO> list = boardService.getBoardList(vo);
Map map = new HashMap();
map.put("boardList", list);
return map;
}
@RequestMapping("getBoard.do")
/* public ModelAndView getBoard(BoardVO vo) {
BoardVO result = boardService.getBoard(vo);
ModelAndView mv = new ModelAndView();
mv.setViewName("getBoard");
mv.addObject("board", result);
return mv;
} */
public void getBoard(BoardVO vo, Model m) {
m.addAttribute("board", boardService.getBoard(vo));
}
// @RequestMapping("insertBoard.do")
// public void insert() {
// }
@RequestMapping("{step}.do")
public String insert(@PathVariable String step) {
return step;
}
@RequestMapping("saveBoard.do")
public String save(BoardVO vo) {
boardService.insertBoard(vo);
return "redirect:/getBoardList.do";
}
@RequestMapping("updateBoard.do")
public String update(BoardVO vo) {
boardService.updateBoard(vo);
return "redirect:getBoard.do?seq=" + vo.getSeq();
}
@RequestMapping("deleteBoard.do")
public String delete(BoardVO vo) {
boardService.deleteBoard(vo);
return "redirect:getBoardList.do";
}
}
< BoardServiceImpl.java 전체 소스 코드 >
package com.javassem.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.javassem.dao.BoardDAOImpl;
import com.javassem.domain.BoardVO;
@Service("boardService")
public class BoardServiceImpl implements BoardService {
@Autowired
private BoardDAOImpl boardDAO;
public void insertBoard(BoardVO vo) {
boardDAO.insertBoard(vo);
}
public void updateBoard(BoardVO vo) {
boardDAO.updateBoard(vo);
}
public void deleteBoard(BoardVO vo) {
boardDAO.deleteBoard(vo);
}
public BoardVO getBoard(BoardVO vo) {
return boardDAO.getBoard(vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
return boardDAO.getBoardList(vo);
}
}
< BoardDAOImpl.java 전체 소스 코드 >
package com.javassem.dao;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.javassem.domain.BoardVO;
@Repository("boardDAO")
public class BoardDAOImpl implements BoardDAO{
@Autowired
private SqlSessionTemplate mybatis;
public void insertBoard(BoardVO vo) {
System.out.println("===> Mybatis insertBoard() 호출");
mybatis.insert("BoardDAO.insertBoard", vo);
}
public void updateBoard(BoardVO vo) {
System.out.println("===> Mybatis updateBoard() 호출");
mybatis.update("BoardDAO.updateBoard", vo);
}
public void deleteBoard(BoardVO vo) {
System.out.println("===> Mybatis deleteBoard() 호출");
mybatis.delete("BoardDAO.deleteBoard", vo);
}
public BoardVO getBoard(BoardVO vo) {
System.out.println("===> Mybatis getBoard() 호출");
return mybatis.selectOne("BoardDAO.getBoard", vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("===> Mybatis getBoardList() 호출");
return mybatis.selectList("BoardDAO.getBoardList", vo);
}
}
<BoardVO.java 전체 소스 코드 >
package com.javassem.domain;
import java.util.Date;
//VO(Value Object)
public class BoardVO {
private int seq;
private String title;
private String writer;
private String content;
private Date regDate;
private int cnt;
public int getSeq() {
return seq;
}
public void setSeq(int seq) {
this.seq = seq;
}
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;
}
}
< index.jsp 전체 소스 코드 >
<%@page contentType="text/html; charset=EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Main Page</title>
</head>
<body>
<h1>게시판 프로그램</h1>
<hr>
<br> <a href="getBoardList.do">글 목록 바로가기</a>
<hr>
<br>
</body>
</html>
< getBoardList.jsp 전체 소스 코드 >
<%@ page contentType="text/html; charset=EUC-KR"%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>글 목록</title>
</head>
<body>
<h1>게시글 목록</h1>
<table border="1">
<tr>
<th bgcolor="orange" width="100">번호</th>
<th bgcolor="orange" width="200">제목</th>
<th bgcolor="orange" width="150">작성자</th>
<th bgcolor="orange" width="150">등록일</th>
<th bgcolor="orange" width="100">조회수</th>
</tr>
<!-- boardList : 컨트롤러에서 넘겨준 데이터 -->
<c:forEach items="${boardList }" var="board">
<tr>
<td>${board.seq }</td>
<td align="left"><a href="getBoard.do?seq=${board.seq }">
${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>
</table>
<br> <a href="insertBoard.do">새글 등록</a>
</body>
</html>
< getBoard.jsp 전체 소스 코드 >
<%@page contentType="text/html; charset=UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>글 상세</title>
</head>
<body>
<h1>글 상세</h1>
<hr>
<form action="updateBoard.do" method="post">
<input name="seq" type="hidden" value="${board.seq}" />
<table border="1" cellpadding="0" cellspacing="0">
<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">${board.regDate }</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="insertBoard.do">글등록</a>
<a href="deleteBoard.do?seq=${board.seq }">글삭제</a>
<a href="getBoardList.do">글목록</a>
</body>
</html>
< insertBoard.jsp 전체 소스 코드 >
<%@page contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>새글등록</title>
</head>
<body>
<h1>글 등록</h1>
<hr>
<form action="saveBoard.do" method="post"> <!-- enctype="multipart/form-data" -->
<table border="1" cellpadding="0" cellspacing="0">
<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" size="10" name='writer' /></td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left"><textarea cols="40" rows="10" name='content' ></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>
2. Spring Legacy Project 실습 (2)
(1) 실습파일 gWebBoard1.zip 다운로드한다.
(2) hWebBoard라는 이름의 Spring Legacy Project 생성시 com.javaclass.basic 이라 입력했다면,
자동으로 생성된 com.javaclass.basic 패키지를 삭제한다.
(JavaEE 모드에서는 src/main/java 안의 폴더 및 views 안의 home.jsp를 삭제한다.)
(3) root-context.xml 에서 DB 연결 정보를 수정한다.
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
<property name="url" value="jdbc:oracle:thin:@192.168.0.17:1521:orcl"></property>
<property name="username" value="scott"></property>
<property name="password" value="tiger"></property>
</bean>
(4) 데이터베이스.txt 참고하여 DB를 생성한다.
create table upLoadtemp(
b_id number(5) primary key, -- 글번호
b_name varchar2(20), -- 글쓴이
b_email varchar2(50), -- 글쓴이 메일 주소
b_title varchar2(80), -- 글 제목
b_content varchar2(3000), -- 글 내용
b_pwd varchar2(12), -- 비밀번호
b_date date, -- 글쓴날짜
b_count number(5) default 0, -- 조회횟수
b_ip varchar2(15), -- 글쓴이 아이피주소
b_fname varchar2(100), -- 파일이름
b_fsize number -- 파일용량
);
create sequence uploadtemp_bid_seq ;
(5) insertBoard.jsp 에서 <form>에 name이 없으므로 VO를 참고하여 작성한다.
단, file의 name은 file로 작성해야 한다.
<form action="saveBoard.do">
....
<input type="text" name='b_title'/>
<input type="text" size="10" name='b_name'/>
<textarea cols="40" rows="10" name='b_content'>
<input type="text" name='b_email'/>
<input type="text" name='b_pwd'/>
<input type="hidden" name='b_ip' value="<%=request.getRemoteAddr()%>">
<input type="file" name='file' maxlength="60" size="40">
(6) 파일업로드.txt 참고하여 pom.xml에 파일업로드를 위한 <dependency> 태그를 작성한다.
<!-- 파일업로드 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3</version>
</dependency>
(7) 파일업로드.txt 참고하여 servlet-context.xml 에 <beans> 태그 작성한다.
<!-- 파일업로드 하려면 MultipartResolver를 등록해야 함 -->
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
(8) 파일업로드.txt 참고하여 insertBoard.jsp 에 <form>태그에서 전송방식을 post로 작성해야 데이터가 전송될 수 있다.
<form action="saveBoard.do" method='post' enctype="multipart/form-data">
(9) 파일업로드.txt 참고하여 BoardVO.java를 수정 (File 경로 수정)
MultipartFile file; // write.jsp에 파일첨부시 name="file"과 동일한 변수명
public MultipartFile getFile() {
return file;
}
public void setFile(MultipartFile file) {
this.file = file;
// 업로드 파일 접근
if(! file.isEmpty()){
this.b_fname = file.getOriginalFilename();
this.b_fsize = file.getSize();
//***********************************************
// 해당 경로로 변경 (실제 경로임)
File f = new File("C:\\java\\webwork\\Spring\\Junior\\hWebBoard\\src\\main\\webapp\\resources\\upload\\"+b_fname);
try {
file.transferTo(f);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(10) webapp/resources 폴더 아래에 업로드한 파일이 저장될 upload 폴더 생성
(11) BoardMapper.xml 에서 insertBoard가 호출되었을 때 수행할 SQL문을 작성한다.
<insert id="insertBoard" parameterType="board">
INSERT INTO upLoadTemp (
b_id, b_name, b_email, b_title, b_content,
b_pwd, b_date, b_count, b_ip, b_fname, b_fsize
) VALUES (
uploadtemp_bid_seq.NEXTVAL, #{b_name}, #{b_email},
#{b_title}, #{b_content}, #{b_pwd}, sysdate, 0,
#{b_ip}, #{b_fname}, #{b_fsize}
)
</insert>
(12) 글 작성시 유효성 검사는 jQuery를 사용한다.
(13) 파일을 등록하지 않더라도 글 작성이 가능하려면 파일 등록에 관한 SQL 부분이 조건문에 들어가야 한다.
<insert id="insertBoard" parameterType="board">
INSERT INTO upLoadTemp (
b_id, b_name, b_email, b_title, b_content,
b_pwd, b_date, b_count, b_ip
<if test="b_fname !=null">
,
b_fname, b_fsize
</if>
) VALUES (
uploadtemp_bid_seq.NEXTVAL, #{b_name}, #{b_email},
#{b_title}, #{b_content}, #{b_pwd}, sysdate, 0,
#{b_ip}
<if test="b_fname !=null">
,
#{b_fname}, #{b_fsize}
</if>
)
</insert>
(14) upload 파일에 저장된 이미지 파일을 글 상세보기 페이지에서 확인할 수 있다.
< root-context.xml 전체 소스 코드 >
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
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-4.1.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
<property name="url" value="jdbc:oracle:thin:@192.168.0.17:1521:orcl"></property>
<property name="username" value="scott"></property>
<property name="password" value="tiger"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml"></property>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
</bean>
<!-- <context:component-scan base-package="org.javassem"></context:component-scan> -->
</beans>
< insertBoard.jsp 전체 소스 코드 >
<%@page contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>새글등록</title>
</head>
<body>
<h1>글 등록</h1>
<hr>
<!-- 1. 폼태그에 속성 추가 -->
<form action="saveBoard.do" method='post' enctype="multipart/form-data">
<table border="1" cellpadding="0" cellspacing="0">
<!-- 2. 각 항목에 name 맞추기 -->
<tr>
<td bgcolor="orange" width="70">제목</td>
<td align="left"><input type="text" name='b_title'/></td>
</tr>
<tr>
<td bgcolor="orange">작성자</td>
<td align="left"><input type="text" size="10" name='b_name'/></td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left"><textarea cols="40" rows="10" name='b_content'></textarea></td>
</tr>
<!-- 추가항목 시작 -->
<tr>
<td bgcolor="orange">이메일</td>
<td align="left"><input type="text" name='b_email'/></td>
</tr>
<tr>
<td bgcolor="orange">비밀번호</td>
<td align="left"><input type="text" name='b_pwd'/>
<input type="hidden" name='b_ip' value="<%=request.getRemoteAddr()%>"></td>
</tr>
<tr>
<td bgcolor="orange" width="70">파일추가</td><td align="left">
<input type="file" name='file' maxlength="60" size="40"></td>
</tr>
<!-- 추가항목 끝 -->
<tr>
<td colspan="2" align="center"><input type="submit" value=" 새글 등록 " /></td>
</tr>
</table>
</form>
<hr>
<a href="getBoardList.do">글 목록 가기</a>
</body>
</html>
< BoardMapper.mxl 전체 소스 코드 >
<?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="BoardDAO">
<insert id="insertBoard" parameterType="board">
INSERT INTO upLoadTemp (
b_id, b_name, b_email, b_title, b_content,
b_pwd, b_date, b_count, b_ip
<if test="b_fname !=null">
,
b_fname, b_fsize
</if>
) VALUES (
uploadtemp_bid_seq.NEXTVAL, #{b_name}, #{b_email},
#{b_title}, #{b_content}, #{b_pwd}, sysdate, 0,
#{b_ip}
<if test="b_fname !=null">
,
#{b_fname}, #{b_fsize}
</if>
)
</insert>
<select id="getBoardList" resultType="board">
<![CDATA[
SELECT * FROM uploadtemp ORDER BY b_id DESC
]]>
</select>
<update id="updateBoard" parameterType="board">
<![CDATA[
UPDATE uploadtemp SET
b_title = #{b_title},
b_content = #{b_content}
WHERE b_id = #{b_id}
]]>
</update>
<delete id="deleteBoard" parameterType="board">
<![CDATA[
DELETE uploadtemp
WHERE b_id = #{b_id}
]]>
</delete>
<select id="getBoard" parameterType="board" resultType="board">
<![CDATA[
SELECT *
FROM uploadtemp
WHERE b_id = #{b_id}
]]>
</select>
</mapper>
< BoardVO.java 전체 소스 코드 >
package com.javassem.domain;
import java.io.File;
import java.io.IOException;
import org.springframework.web.multipart.MultipartFile;
public class BoardVO {
private int b_id;
private String b_name;
private String b_email;
private String b_title;
private String b_content;
private String b_pwd;
private String b_date;
private int b_count;
private String b_ip;
private String b_fname;
private long b_fsize;
//*************************************************
MultipartFile file; // write.jsp에 파일첨부시 name="file"과 동일한 변수명
public MultipartFile getFile() {
return file;
}
public void setFile(MultipartFile file) {
this.file = file;
// 업로드 파일 접근
if(! file.isEmpty()){
this.b_fname = file.getOriginalFilename();
this.b_fsize = file.getSize();
//***********************************************
// 해당 경로로 변경 (실제 경로임)
File f = new File("C:\\java\\webwork\\Spring\\Junior\\hWebBoard\\src\\main\\webapp\\resources\\upload\\"+b_fname);
try {
file.transferTo(f);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public int getB_id() {
return b_id;
}
public void setB_id(int b_id) {
this.b_id = b_id;
}
public String getB_name() {
return b_name;
}
public void setB_name(String b_name) {
this.b_name = b_name;
}
public String getB_email() {
return b_email;
}
public void setB_email(String b_email) {
this.b_email = b_email;
}
public String getB_title() {
return b_title;
}
public void setB_title(String b_title) {
this.b_title = b_title;
}
public String getB_content() {
return b_content;
}
public void setB_content(String b_content) {
this.b_content = b_content;
}
public String getB_pwd() {
return b_pwd;
}
public void setB_pwd(String b_pwd) {
this.b_pwd = b_pwd;
}
public String getB_date() {
return b_date;
}
public void setB_date(String b_date) {
this.b_date = b_date;
}
public int getB_count() {
return b_count;
}
public void setB_count(int b_count) {
this.b_count = b_count;
}
public String getB_ip() {
return b_ip;
}
public void setB_ip(String b_ip) {
this.b_ip = b_ip;
}
public String getB_fname() {
return b_fname;
}
public void setB_fname(String b_fname) {
this.b_fname = b_fname;
}
public long getB_fsize() {
return b_fsize;
}
public void setB_fsize(long b_fsize) {
this.b_fsize = b_fsize;
}
}
< 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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javassem</groupId>
<artifactId>basic</artifactId>
<name>gWebMybatis</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<!-- ### 버전 변경 ### -->
<java-version>1.8</java-version>
<org.springframework-version>5.0.8.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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- 마이바티스 추가 -->
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- SPRING JDBC 필요 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- 트랜잭션 필요 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- ################################################# -->
<!-- 파일업로드 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3</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>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</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>
< servlet-context.xml 전체 소스 코드 >
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
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">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- 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>
<context:component-scan base-package="com.javassem" />
<!-- ##################################################### -->
<!-- 파일업로드 하려면 MultipartResolver를 등록해야 함 -->
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
</beans:beans>
<BoardControll.java 전체 소스 코드 >
package com.javassem.controller;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import com.javassem.domain.BoardVO;
import com.javassem.service.BoardService;
@Controller
public class BoardController {
@Autowired
private BoardService boardService;
//
@RequestMapping("/{step}.do")
public String viewPage(@PathVariable String step) {
return step;
}
// 글 목록 검색
@RequestMapping("/getBoardList.do")
public void getBoardList(BoardVO vo, Model model) {
model.addAttribute("boardList", boardService.getBoardList(vo));
// ViewResolver를 지정하지 않으면 아래처럼 페이지명 지정
// return "views/getBoardList.jsp"; // View 이름 리턴
}
// 글 등록
@RequestMapping(value = "/saveBoard.do")
public String insertBoard(BoardVO vo) throws IOException {
boardService.insertBoard(vo);
return "redirect:/getBoardList.do";
}
// 글 수정
@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";
}
// 글 상세 조회
@RequestMapping("/getBoard.do")
public void getBoard(BoardVO vo, Model model) {
model.addAttribute("board", boardService.getBoard(vo)); // Model 정보 저장
}
}
< BoardServiceImpl.java 전체 소스 코드 >
package com.javassem.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.javassem.dao.BoardDAOImpl;
import com.javassem.domain.BoardVO;
@Service("boardService")
public class BoardServiceImpl implements BoardService {
@Autowired
private BoardDAOImpl boardDAO;
public void insertBoard(BoardVO vo) {
boardDAO.insertBoard(vo);
}
public void updateBoard(BoardVO vo) {
boardDAO.updateBoard(vo);
}
public void deleteBoard(BoardVO vo) {
boardDAO.deleteBoard(vo);
}
public BoardVO getBoard(BoardVO vo) {
return boardDAO.getBoard(vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
return boardDAO.getBoardList(vo);
}
}
< BoardDAOImplljava 전체 소스 코드 >
package com.javassem.dao;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.javassem.domain.BoardVO;
@Repository("boardDAO")
public class BoardDAOImpl implements BoardDAO{
@Autowired
private SqlSessionTemplate mybatis;
public void insertBoard(BoardVO vo) {
System.out.println("===> Mybatis insertBoard() 호출");
mybatis.insert("BoardDAO.insertBoard", vo);
}
public void updateBoard(BoardVO vo) {
System.out.println("===> Mybatis updateBoard() 호출");
mybatis.update("BoardDAO.updateBoard", vo);
}
public void deleteBoard(BoardVO vo) {
System.out.println("===> Mybatis deleteBoard() 호출");
mybatis.delete("BoardDAO.deleteBoard", vo);
}
public BoardVO getBoard(BoardVO vo) {
System.out.println("===> Mybatis getBoard() 호출");
return (BoardVO) mybatis.selectOne("BoardDAO.getBoard", vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("===> Mybatis getBoardList() 호출");
return mybatis.selectList("BoardDAO.getBoardList", vo);
}
}
< index.jsp 전체 소스 코드 >
<%@page contentType="text/html; charset=EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Main Page</title>
</head>
<body>
<h1>게시판 프로그램</h1>
<hr>
<br> <a href="getBoardList.do">글 목록 바로가기</a>
<hr>
<br>
</body>
</html>
< getBoard.jsp 전체 소스 코드 >
<%@page contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>글 상세</title>
</head>
<body>
<h1>글 상세</h1>
<hr>
<form action="updateBoard.do" method="post">
<input name="b_id" type="hidden" value="${board.b_id}" />
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td bgcolor="orange" width="70">제목</td>
<td align="left"><input name="b_title" type="text"
value="${board.b_title }" /></td>
</tr>
<tr>
<td bgcolor="orange">작성자</td>
<td align="left">${board.b_name }</td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left"><textarea name="b_content" cols="40" rows="10">${board.b_content }</textarea></td>
</tr>
<tr>
<td bgcolor="orange">등록일</td>
<td align="left">${board.b_date }</td>
</tr>
<tr>
<td bgcolor="orange">조회수</td>
<td align="left">${board.b_count }</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="글 수정" /></td>
</tr>
</table>
<img src="resources/upload/icon_01.png"/>
<img src="/hWebBoard/resources/upload/icon_02.png"/>
</form>
<hr>
<a href="insertBoard.do">글등록</a>
<a href="deleteBoard.do?b_id=${board.b_id }">글삭제</a>
<a href="getBoardList.do">글목록</a>
</body>
</html>
3. 애자일 개발과 스크럼
(1) 기본방식과의 비교
- 애자일과 폭포수 방식은 대립구조는 아니다.
- 애자일은 사고방식과 변화를 수용하고자 하는 움직임을 따르고자 한다.
- 애자일의 궁극적인 목적은 기술 조식, 비지니즈 조직 그리고 고객이
상호 협력하여 해결책을 같이 고심하자는 것이다.
Waterfall | Agile |
각 공정 단계 완료 후에는 이전 단계로 되돌아 갈 수 없다. | 우선 순위가 높은 기능부터 개발 |
단계간 커뮤니케이션은 주로 문서로 수행 | 문서보다는 동작하는 소프트웨어 중심으로 대화 |
개발을 시작하여 완료될 쯤엔 이미 시장환경이 변화를 따라갈 수 없게 된다. | 전체를 한 번에 만들지 않고 일부라도 동작하는 부분을 완성하여 고객에게 보여주고 피드백을 반영하여 발전시켜 나간다. |
요구사항 분석 - 무엇을 만들지 이해 설계 - 어떻게 만들지 검토 구현 - 실제 소프트웨어로 작성 테스트 - 만들어진 소프트웨어를 확인 |
(2) 폭포수 방법론
※ 소프트웨어 개발 현장에서 자주 일어나는 현상
1. 프로젝트 초기에 전체 설계를 하다 보니 전체 프로젝트에 대한 예측이 정홖하지 않을 수 있다. - 개발 기간 부족으로 일정이 지연되기도 한다. - 처음에 요구사항이 완성 시점에서 구식이 되기도 한다. - 개발 전체의 진척 사항을 정확히 판단하기 어렵다. |
2. 프로젝트의 마지막 단계에서 동작 기능이 완성되어 고객과 사용자는 마지막까지 실제 어떻게 동작되는지 알기 어렵다. - 개발 막바지 단계에서 기능 수정이나 변경사항이 요구되는 경우가 발생한다. |
3. 각각 조직은 서로 문서로 주고 받다 보니 문서 이해에 대한 오해가 발생하기도 한다. - 팀이 커질수록 생산성이 떨어진다. |
(3) 애자일 프로젝트
※ 프로젝트가 실패하는 이유
: 각자가 생각하는 프로젝트 성공의 의미가 다르게 해석되는 경우가 많다.
: 처음부터 의견이 일치되지 않은 상태에서 프로젝트가 시작된다.
: 현명한 선택을 하기 위해서는 다른 팀원들과 소통해야 한다.
: 고객과의 지속적인 피드백으로 소통해야 한다.
(4) 용어정리
- 스크럼 : 애자일 프로젝트를 관리하기 위한 프로젝트 관리 툴
- XP : 애자일 프로젝트에 필요한 핵심 소프트웨어 엔지니어링 실천법
스크럼 | XP |
스프린트 | 이터레이션 |
제품 백로그 | 마스터 스토리 리스트 |
제품 책임자 | 고객 |
- 스프린트 (iteration)
: 짧은 반복 주기를 계속하는 개발 프로세스
: 1~4주 간격의 개발 기간
: 애자일의 Iteration (반복)
: 스프린트 계획 → 일일스크럼 → 스프린트 리뷰 → 회고
- 제품 백로그 (마스터 스토리 리스트)
: 개발해야 할 제품의 기능 목록
: 고객이 이해하는 언어로 작성
: 후에는 '사용자 스토리' 형식으로 작성되는 경우가 많음
- 스프린트 백로그
: 제품 백로그에서 스프린트 중 개발한 기능 목록
: 이 목록은 한 번의 스프린트에서만 사용
: 스프린트 계획을 수립할 때 제품 소유자가 결정한 순위와 개발팀이 결정한 공수 정보를 통합하여 논의한 후 만든다.
- 일일스크럼
: 스탠드업 미팅 (매일 15분 정도)
: 팀원들은 '어제 한 일', '오늘 할 일', '장애가 되고 있는 일'을 말하고 스크럼 마스터는 그 장애를 제거한다.
: 일일스크럼의 상황은 제품 소유자가 공유한다.
- 스프린트 (1~4주)
: 큰 문제들은 작은 문제들로 세분화하라
: 가장 중요한 것을 먼저 집중하고, 다른 것들은 잊어버려라
: 소프트웨어가 제대로 작동하는지 확인하고 또 확인하라
: 고객의 피드백을 구하라
: 필요하다면 계획을 바꿔라
(5) Inception Deck (인셉션 덱)
: 애자일의 도구 중 하나이다.
※ Inception ?
: 사전적 의미는 어떤 활동이나 단체를 시작, 설립하는 단계를 가리킴
: 프로젝트 초기 단계에 고객과 개발팀이 서로 알아가는 과정을 갖는 일정한 기간으르 말한다.
(Agile samurai 저서의 Jonathan Rasmusson 저자의 정의)
※ [ Why? ] 왜 우리가 이 프로젝트를 하려는지 이해하기 위해 다음과 같은 질문을 한다.
(1) 우리는 왜 모였는가? (반드시 고객과 함께)
(2) 이 프로젝트의 엘리베이터 피치는 무엇인가?
(3) 우리 제품을 광고한다면 어떻게 표현할까?
(4) 우리가 하지 말아야 하는 것은 무엇이 있을까?
(5) 프로젝트 관계자 중 우리가 알아두어야 할 사람은 누구인가?
(1) 우리는 왜 모였는가? |
프로젝트의 주인은 고객이다. 고객의 의도가 무엇인지 파악한다. 예를 들어 건설회사의 광산에 출입허가관리 프롲게트를 한다면, 직접 현장을 체험하고 실제 프로그램을 사용할 직원(고객)을 만나 소통한다. 항공사 사장의 기내식단으로 의견을 좁히던 중 사장님의 취지는 비행기 요금인하라는 것을 알게 된다면, 메뉴를 추가하는 방식이 문제가 아니라 진짜 프로젝트의 목적을 파악한다. |
(2) 이 프로젝트의 엘리베이터 피치는 무엇인가? |
투자자를 엘레베이터에서 만났다면 30초 안에 우리 회사의 아이디어를 어떻게 멋지게 소개하여 사업 구현까지 이루어지게 하는지를 가리킨다. 즉, 짧은 시간 안에 핵심을 피력하는 것이다. [고객이 필요로 하는 사항]을 [목표로 하는 고객]에게 [제품의 카테고리]인 [제품의 이름]은 [제품이 주는 혜택이나 구매해야만 하는 이유]이다. [경쟁사 제품의 기능과 다르게] 우리 제품은 [우리 제품이 가지는 특별한 점]이다. |
(3) 우리 제품을 광고한다면 어떻게 표현할까? |
만일 소프트웨어를 슈퍼마켓에서 판다면 소비자에게 어필할 광고를 만들어보자. - 기능보다 혜택이 무엇인지 - 기억에 남거나 느낌이 좋은 슬로건으로 - 포장이나 이미지를 구상한다면 |
(4) 우리가 하지 말아야 하는 것은 무엇이 있을까? |
해야 할 리스트 옆에, 다음 릴리즈에 해도 되는 목록은 적지 말아야 한다. |