[목차]

11. 게시판 만들기

① DB 연결

② List 출력

③ 게시물 등록

④ 검색

⑤ 상세 페이지 보기

⑥ 게시글 수정

⑦ 게시글 삭제

⑧ 댓글 기능 추가


[내용]

① DB 연결

 = sprweb16_board

 - db.properties

driver=org.mariadb.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/happydb
username=root
password=123

 => 계정 정보 설정파일.

 

 

 - Configuration.xml

<configuration>
<typeAliases>
	<typeAlias type="pack.model.BoardDto" alias="dto"/>
	<typeAlias type="pack.controller.BoardBean" alias="formBean"/>
</typeAliases>
 <mappers>
  <mapper resource="pack/mybatis/DataMapper.xml" />
 </mappers>
</configuration>

 => DataMapper.xml 연결.

 

 

- DataMapper.xml

<mapper namespace="dev">
 <!-- 전체 검색 -->
 <select id="selectList" resultType="dto">
  	select * from board order by gnum desc, onum asc
 </select>
 
 <!-- 카워드 검색 -->
 <select id="searchList" parameterType="formBean" resultType="dto">
  	select * from board 
  	where ${searchName} like concat('%',#{searchValue},'%') 
  	order by gnum desc, onum asc
 </select>
 
 <!-- 번호로 검색 -->
 <select id="selectOne" parameterType="string" resultType="dto">
  	select * from board where num=#{num}
 </select>
 
 <!-- 추가 -->
 <insert id="insertData" parameterType="formBean">
 	insert into board 
 	values(#{num},#{name},#{pass},#{mail},#{title},#{cont},
 	#{bip},#{bdate},0,#{gnum},0,0)
 </insert>
 
 <update id="updateData" parameterType="formBean">
 	update board 
 	set name=#{name},mail=#{mail},title=#{title},cont=#{cont}
 	where num=#{num}
 </update>
 
 <delete id="deleteData" parameterType="string">
 	delete from board where num=#{num}
 </delete>
 
 <!--  maxnum -->
 <select id="currentNum" resultType="integer">
 	select max(num) from board
 </select>
 
 <select id="totalCnt" resultType="integer">
 	select count(*) from board
 </select>
 
 <!-- readcnt -->
 <update id="updateReadcnt" parameterType="string">
 	update board set readcnt=readcnt + 1 
 	where num=#{num}
 </update>
 
 <!-- password -->
 <select id="selectPass" parameterType="string" resultType="string">
 	select pass from shopboard 
 	where num=#{num}
 </select>

 <!-- reply -->
 <update id="updateOnum" parameterType="formBean">
 	update board set onum=onum + 1
 	where onum >= #{onum} and gnum=#{gnum}
 </update>

 <insert id="insertReData" parameterType="formBean">
 	insert into board
 	values(#{num},#{name},#{pass},#{mail},#{title},#{cont},
 	#{bip},#{bdate},0,#{gnum},#{onum},#{nested})
 </insert>
</mapper>

 => SQL문 작성.

 

 

 - servlet-context.xml

<context:component-scan base-package="pack.controller" />
<context:component-scan base-package="pack.model" />

 => 어노테이션 사용을 위한 패키지 스캔.

 

 

 - root-context.xml

<!-- PropertyPlaceholderConfigurer 사용 : 외부 프로퍼티의 정보를 설정파일에서 사용가능.-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<!-- locations 속성으로 db.properties를 read하도록 설정 -->
	<!-- db.properties의 프로퍼티를 ${프로퍼티값}으로 사용할 수 있다. -->
	<property name="locations">
		<value>classpath:pack/mybatis/db.properties</value>
	</property>
</bean>
<!-- SimpleDriverDataSource 사용 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
	<!--  -->
	<property name="driverClass" value="${driver}"/>
	<property name="url" value="${url}"/>
	<property name="username" value="${username}"/>
	<property name="password" value="${password}"/>
</bean>
<!-- SqlSessionFactoryBean 사용 -->
<!-- 복수개일 경우 id를 셋팅 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- SqlMapConfig의 SqlSession에  Configuration.xml 연결-->
	<property name="dataSource" ref="dataSource"/>
	<property name="configLocation" value="classpath:pack/mybatis/Configuration.xml"></property>
</bean>

 => DB 연동을 위한 SqlSessionFactory 객체 생성.

 

 


② List 출력

 - index.jsp

<body>
	<h2>메인</h2>
	<ul>
		<li>쇼핑하기</li>
		<li>회원관리</li>
		<li>물류관리</li>
		<li><a href="list?page=1">게시판</a></li>
	</ul>
</body>

 => url (list), key=value(page=1) 전달(GET 방식).

 

 

- ListController

@Controller
public class ListController {
	@Autowired
	private BoardDaoInter boardInter;
	
	private int tot; // 전체 레코드 수
	private int plist=5; // 한 페이지 당 행의 수
	private int pageCount; // 전체 페이지 수
	
	public ArrayList<BoardDto> getListData(ArrayList<BoardDto> list, int page){
		ArrayList<BoardDto> result = new ArrayList<BoardDto>();
		int start = (page - 1)*plist; // 해당 페이지의 첫 게시물의 index
		int size = plist <= list.size() - start ? plist : list.size() - start;
		// 한 페이지에 표시할 데이터가 5개 이하일 경우엔 size가 그 수에 따르도록 조건처리.
        
		for(int i=0; i<size;i++) { // list에 한 페이지당 표시할 데이터만 담아 리턴한다.
			result.add(i, list.get(start + i));
		}
		
		return result;
	}
	
	public int getPageCount() { // get 페이지 수
		pageCount = tot/plist;
		if(tot%plist > 0 ) pageCount+=1; 
		
		return pageCount;
	}
	
	@RequestMapping("list")
	public Model process(Model model, @RequestParam("page") int page) {
		tot = boardInter.totalCnt();
		ArrayList<BoardDto> list = boardInter.getList();
		ArrayList<BoardDto> result = getListData(list, page);
		model.addAttribute("data",result);
		model.addAttribute("pageCount",getPageCount());
		model.addAttribute("page",page);
		//model.addAttribute("data",list); // 페이징 없이 작업할 경우
		return model;
	}
}

 => totalCnt() : 

 => getList() : 전체 List 검색하여 list 리턴.

 => getListData() : 전체 리스트에서 한 페이지 당 표시할 데이터만 리턴값으로 가진다.

 => plist : 한 페이지 당  표시할 레코드 수를 설정.

 => getPageCount() : 페이지의 개수를 리턴.

 

 

- BoardBean

public class BoardBean {
	private String name,pass,mail,title,cont,bip,bdate;
	private int num,readcnt,gnum,onum,nested;
	private String searchName, searchValue;
	
	//getter/setter
}

 => FormBean 작성.

 

 

- BoardDto

public class BoardDto {
	private String name,pass,mail,title,cont,bip,bdate;
	private int num,readcnt,gnum,onum,nested;
	
	//getter/setter
}

=> DTO 작성.

 

 

- BoardDaoInter

public interface BoardDaoInter {
	ArrayList<BoardDto> getList();
	ArrayList<BoardDto> getSearch(BoardBean bean);
	boolean insert(BoardBean bean);
	BoardDto getDetail(String num);
	boolean update(BoardBean bean);
	boolean delete(String num);
	
	int currentNum();
	int totalCnt();
	boolean updateReadcnt(String num);
	String selectPass(String num);
	
	boolean updateOnum(BoardBean bean);
	boolean insertReply(BoardBean bean);
}

=> interface 작성.

 

 

- BoardDaoImpl

@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
	
	@Autowired
	public BoardDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	
	@Override
	public ArrayList<BoardDto> getList() {
		return (ArrayList)getSqlSession().selectList("selectList");
	}
    
	@Override
	public int totalCnt() {
		return getSqlSession().selectOne("totalCnt");
	}
	...
	
}

 => getList() : board 테이블의 모든 정보 list 리턴.

 => totalCnt() : 레코드 수를 리턴.

 <!-- 전체 검색 -->
 <select id="selectList" resultType="dto">
  	select * from board order by gnum desc, onum asc
 </select>

<select id="totalCnt" resultType="integer">
 	select count(*) from board
 </select>

 

 

 - list.jsp

<body>
<jsp:include page="./top.jsp"></jsp:include>
<table  class="table">
  <tr>
  	<td>
  		[<a href="list?page=1">최근목록</a>]&nbsp;
  		[<a href="insert">새글작성</a>]
  	</td>
  </tr>
</table>
<table class="table table-striped">
  <thead>
  <tr style="">
  	<th>번호</th><th>제  목</th><th>작성자</th><th>작성일</th><th>조회</th>
  </tr>
  </thead>
  <c:forEach var="b" items="${data}">
  	<!-- 댓글 들여쓰기 준비-->
  	<c:set var="tab" value=""/>
  	<c:forEach var="n" begin="1" end="${b.nested}">
  		<c:set var="tab" value="${tab}&nbsp;&nbsp;"/>
  	</c:forEach>
  <tr>
  	<td>${b.num}</td>
  	<td>
  		${tab}<a href="detail?num=${b.num}&page=${page}">${b.title}</a>
  	</td>
  	<td>${b.name}</td>
  	<td>${b.bdate}</td>
  	<td>${b.readcnt}</td>
  </tr>	
  </c:forEach>
  <!-- paging -->
  <tr style="text-align: center;">
  	<td colspan="5">
  	<c:forEach var="psu" begin="1" end="${pageCount}">
  		<c:if test="${psu==page}">
  			<b>${psu}</b>
  		</c:if>
  		<c:if test="${psu!=page}">
  			<a href="list?page=${psu}">${psu}</a>
  		</c:if>
  	</c:forEach>
  	</td>
  	
  </tr>
  <!-- search -->
  <tr style="text-align: center;">
  	<td colspan="5">
  	<br><br>
  	<form action="search" method="post">
  		<select name="searchName">
  			<option value="title" selected="selected">글제목</option>
  			<option value="name">작성자</option> 
  		</select>
  		<input type="text" name="searchValue">
  		<input type="submit" value="검색">
  	</form>
  	</td>
  </tr>
</table>
</body>

 => 페이지 처리된 list를 출력한다.

 => 제목을 누를 경우 요청명 detail로 num과 page 값을 전달한다.

 => 검색 버튼을 누를 경우 searchName(key)에 title or name(value), searchValue(key)에 값이 요청명 search로 전달.

      (post방식) 

 

 => 게시물 등록 하이퍼링크 클릭시 insert 요청명 전달.(GET방식)

 

 


③ 게시물 등록

- InsertController

@Controller
public class InsertController {
	
	@Autowired
	private BoardDaoInter boardInter;
	
	@RequestMapping(value="insert", method = RequestMethod.GET)
	public String insert() {
		return "insform";
	}
	...
}

 => GET방식 요청명 insert 받을 경우 inform.jsp 실행.

 

 

 - insform.jsp

<head>
	<script type="text/javascript">
		window.onload=function(){
			let btnIns = document.getElementById("btnIns");
			btnIns.addEventListener("click",checkData, false);
		}
		function checkData(){
			if(frm.name.value === ""){
				alert("작성자를 입력하세요");
				frm.name.focus();
				return;
			}else if(frm.pass.value === ""){
				alert("비밀번호를 입력하세요");
				frm.name.focus();
				return;
			}
			frm.submit();
		}
	</script>
</head>
<body>
<jsp:include page="./top.jsp"></jsp:include>
<h4 style="text-align: center;">* 게시물 등록</h4>
<form action="insert" method="post" name="frm">
<table  class="table" style="width: 80%">
  <tr>
  	<td>이름</td>
  	<td><input type="text" name="name"></td>
  </tr>
  <tr>
  	<td>암호</td>
  	<td><input type="text" name="pass"></td>
  </tr>
  <tr>
  	<td>메일</td>
  	<td><input type="text" name="mail"></td>
  </tr>
  <tr>
  	<td>제목</td>
  	<td><input type="text" name="title"></td>
  </tr>
  <tr>
  	<td>내용</td>
  	<td>
  		<textarea rows="5" cols="50" name="cont"></textarea>
  	</td>
  </tr>
  <tr>
  	<td colspan="2" style="text-align: center;">
  		<input type="hidden" name="bip" value="<%=request.getRemoteAddr()%>">
  		<input type="button" value="등록" id="btnIns">
  		<!-- <input type="button" value="등록" onclick="checkData()"> -->
  		<input type="button" value="목록" onclick="location.href='list?page=1'">
  	</td>
  </tr>
</table>
</form>
</body>

 => 등록 버튼 누를 경우 name/pass칸이 비어있을 경우 pop-up 발생.

     요청명 insert 전달.(post 방식)

 => location.href  : 목록버튼 누를 경우 list 출력 창 1 page로 이동.

      focus() : 커서가 해당 창으로 이동.

      submit() : submit 동작 진행.

 

 

- InsertController

@Controller
public class InsertController {
	
	@Autowired
	private BoardDaoInter boardInter;
	...
	@RequestMapping(value="insert", method = RequestMethod.POST)
	public String submit(BoardBean bean) {
		bean.setBdate();
		int newNum = boardInter.currentNum() + 1; // 새로 작성된 글의 번호
		bean.setNum(newNum);
		bean.setGnum(newNum);
		
		boolean b= boardInter.insert(bean);
		if(b) {
			return "redirect:/list?page=1";
		}else {
			return "redirect:/error";
		}
	}
}

 => POST방식 요청명 insert 받을 경우 실행.

 => <form>태그 데이터 FormBean에 set.

 => setBdate() : Calendar객체를 이용하여 오늘 날짜를 set한다.

 => currentNum() : 현재 테이블의 num값중 가장 큰 값을 받아와 +1하여 현재 num가 group num 값을 set한다.

 => insert() : data 추가.

 => insert 성공 시 전체 list 페이지로 이동, 실패 error 페이지로 이동.

public void setBdate() {
	Calendar cal = Calendar.getInstance();
	int year = cal.get(Calendar.YEAR);
	int month = cal.get(Calendar.MONTH) + 1;
	int day = cal.get(Calendar.DATE);
	this.bdate = year + "-" + month + "-" + day;
}

 

 

- BoardDaoImpl

@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
	
	@Autowired
	public BoardDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	...	
	@Override
	public boolean insert(BoardBean bean) {
		int result = getSqlSession().insert("insertData",bean);
		if(result>0) {
			return true;
		}else {
			return false;
		}
	}
	...
	@Override
	public int currentNum() {
		//insert시 번호 자동 증가를 위해 현재 레코드 중 가장 큰 번호 얻기.
		if(getSqlSession().selectOne("currentNum") == null) {
			return 0;
		}else {
			return getSqlSession().selectOne("currentNum");
		}
	}
	...
}

 => insert() : insert에 성공 시 true, 실패 시 false 리턴.

 => currentNum() : 레코드가 없을 경우 현재 가장 큰 num값을 0으로 리턴.

...
<!-- 추가 -->
<insert id="insertData" parameterType="formBean">
	insert into board 
	values(#{num},#{name},#{pass},#{mail},#{title},#{cont},
	#{bip},#{bdate},0,#{gnum},0,0)
</insert>
...
<!--  maxnum -->
<select id="currentNum" resultType="integer">
	select max(num) from board
</select>
...

 => Data insert

 => board테이블의 num중 가장 큰 값 리턴.

 

 

 - ErrorController

@Controller
public class ErrorController {
	
	@RequestMapping("error")
	public String err() {
		return "error";
	}
}

 => error.jsp 실행

 

 

 - error.jsp

<body>
	<h2>에러 발생</h2>
	<a href="list?page=1">목록보기</a>
</body>

 => error 페이지 출력.

 

 


④ 검색

- SearchController

@Controller
public class SearchController {
	
	@Autowired
	private BoardDaoInter inter;
	
	@RequestMapping("search")
	public ModelAndView searchProcess(BoardBean bean) {
		ArrayList<BoardDto> list = inter.getSearch(bean); 
		ModelAndView view = new ModelAndView("list", "data", list);
		view.addObject("page","1");
		return view;
	}
}

=> list.jsp에서 검색 버튼을 누를 경우 searchName(key), searchValue(key)가 전달된다.

=> getSearch() : searchName(key), searchValue(key)에 맞는 값을 검색하여 리스트로 출력한다.

=> 리턴 값을 list.jsp에 출력.

 

 

- BoardDaoImpl

@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
	
	@Autowired
	public BoardDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	...	
	@Override
	public ArrayList<BoardDto> getSearch(BoardBean bean) {
		return (ArrayList)getSqlSession().selectList("searchList", bean);
	}
	...
}

 => getSearch() : 검색 후 ArrayList 리턴.

 

 

- DataMapper.xml

 ...
 <!-- 카워드 검색 -->
 <select id="searchList" parameterType="formBean" resultType="dto">
  	select * from board 
  	where ${searchName} like concat('%',#{searchValue},'%') 
  	order by gnum desc, onum asc
 </select>
 ...

 => searchName의 Column에서 searchValue의 키워드가 있을 경우 리스트를 리턴한다.

 

 


⑤ 상세 페이지 보기

- DetailController

@Controller
public class DetailController {
	@Autowired
	private BoardDaoInter inter;
	
	@RequestMapping("detail")
	public ModelAndView detailProcess(@RequestParam("num") String num,
			@RequestParam("page") String page) {
		
		// 조회수 증가 작업 선행
		boolean b = inter.updateReadcnt(num);
		
		// 상세보기 진행 후 jsp로 출력
		ModelAndView view = new ModelAndView("detail");
		view.addObject("data", inter.getDetail(num));
		view.addObject("page", page);
		return view;
		
	}
}

 => list.jsp에서 제목을 누를 경우 요청명 detail로 num과 page 값을 전달받는다.

 => @RequestParam : request.getParameter()와 동일.

 => updateReadcnt() : 조회수를 +1한다.

 => getDetail() : 해당 num의 한 레코드 값을 리턴하여 detail.jsp 실행.

 

 

- BoardDaoImpl

@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
	
	@Autowired
	public BoardDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	...
	@Override
	public BoardDto getDetail(String num) {
		// 글 내용보기, 글 수정 시 데이터 받아오기
		return getSqlSession().selectOne("selectOne",num);
	}
	...
	@Override
	public boolean updateReadcnt(String num) {
		//상세보기 전 조회수 증가
		int result = getSqlSession().update("updateReadcnt",num);
		if(result>0) {
			return true;
		}else {
			return false;
		}
	}
	...
}

 => getDetail() : 게시글의 내용을 받아온다.

 => updateReadcnt() : 업데이트 성공시 true, 실패 시 false 리턴

 

 

- DataMapper.xml

 ...
 <!-- 번호로 검색 -->
 <select id="selectOne" parameterType="string" resultType="dto">
  	select * from board where num=#{num}
 </select>
 ...
 <!-- readcnt -->
 <update id="updateReadcnt" parameterType="string">
 	update board set readcnt=readcnt + 1 
 	where num=#{num}
 </update>
 ...

 => num값과 동일한 레코드 리턴.

 => num과 동일한 레코드의 조회수 +1.

 

 

- detail.jsp

<script type="text/javascript">
	window.onload=function(){
	document.getElementById("btnDel").onclick = chkDeleData;
	}
	function chkDeleData(){
		
		let pass = prompt("비밀번호를 입력하세요");
		location.href="delete?num=${data.num}&page=${page}&pass="+pass;
	}
</script>
</head>
<body>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:if test="${msg != null}">
	<script type="text/javascript">
		(function msg(){
			alert("${msg}");
		})();
	</script>
</c:if>
<jsp:include page="./top.jsp"></jsp:include>
<div class="container">
<h4 style="text-align: center;">* 상세보기</h4>
<table class="table" style="width: 80%">
  <tr>
  	<td>비밀번호 : ${data.pass}</td>
  	<td style="text-align: right;">
  		<a href="reply?num=${data.num}&page=${page}">
  			<img src="./resources/images/reply.gif" />
  		</a>
  		<a href="update?num=${data.num}&page=${page}">
  			<img src="./resources/images/edit.gif" />
  		</a>
  		<button id="btnDel"><img src="./resources/images/del.gif" /></button>
  		<!-- 
  		<a href="delete?num=${data.num}&page=${page}">
  			<img src="./resources/images/del.gif" />
  		</a>
  		 -->
  		<a href="list?page=${page}">
  			<img src="./resources/images/list.gif" />
  		</a>
  	</td>
  </tr>
</table>

<table border="1" style="width: 80%">
  <tr>
  	<td>
  		작성자 : <a href="mailto:${data.mail}">${data.name}</a>
  		(ip : ${data.bip})
  	</td>
  	<td>작성일 : ${data.bdate}</td>
  	<td>조회수 : ${data.readcnt}</td>
  </tr>
  <tr>
  	<td colspan="3">제목 : ${data.title}</td>
  </tr>
  <tr>
  	<td colspan="3">
  		<textarea rows="5" readonly="readonly" style="width:99%">${data.cont}</textarea>
  	</td>
  </tr>
</table>
</div>
</body>

 => 댓글 기능 : 요청명 reply로 num과 page 값 전달.

 => 수정 기능 : 요청명 update로 num과 page 값 전달.(GET방식)

 => 삭제 기능 : 비밀번호 입력 prompt 출력 후 입력시 요청명 delete로 num, page, password 값 전달.

 => 목록 보기 기능 : 전체 list 출력 페이지로 이동.

 => msg가 있을 경우 pop-up창으로 발생.

 

 


⑥ 게시글 수정

- UpdateController

@Controller
public class UpdateController {
	
	@Autowired
	private BoardDaoInter inter;
	
	@RequestMapping(value="update", method=RequestMethod.GET)
	public ModelAndView edit(@RequestParam("num") String num,
			@RequestParam("page") String page) {
		
		// 모델에서 수정할 자료 읽기
		BoardDto dto = inter.getDetail(num);
		
		ModelAndView view = new ModelAndView("update");
		view.addObject("data", dto);
		view.addObject("page", page);
		return view;
	}
	...
}

=> 상세 페이지에서 수정 버튼 클릭시 실행.

=> getDetail() : 해당 데이터값 리턴.

=> update.jsp 실행.

 

 

- update.jsp

<script type="text/javascript">
	window.onload=function(){
	document.getElementById("btnUp").onclick = chkUpData;
	}
	function chkUpData(){
		if(upFrm.pass.value === ""){
			upFrm.pass.focus();
			alert("비밀번호를 입력하세요");
			return;
		}
		if(confirm("정말 수정할까요?")){
			upFrm.submit();
		}
	}
</script>
</head>
<body>
<jsp:include page="./top.jsp"></jsp:include>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:if test="${msg != null}">
	<script type="text/javascript">
		(function msg(){
			alert("${msg}");
		})();
		location.href="list?page=${page}";
	</script>
</c:if>
<div class="container">
<h4 style="text-align: center;">- 수정 -</h4>
<form action="update" method="post" name="upFrm">
<input type="hidden" name="num" value="${data.num}">
<input type="hidden" name="page" value="${page}">
<table class="table" style="width: 80%">
  <tr>
  	<td>이름</td>
  	<td><input type="text" name="name" value="${data.name}"></td>
  </tr>
  <tr>
  	<td>암호</td>	
  	<td><input type="text" name="pass"></td>
  </tr>
  <tr>
  	<td>메일</td>
  	<td><input type="text" name="mail" value="${data.mail}"></td>
  </tr>
  <tr>
  	<td>제목</td>
  	<td><input type="text" name="title" value="${data.title}"></td>
  </tr>
  <tr>
  	<td>내용</td>
  	<td>
  		<textarea rows="5" cols="50" name="cont">${data.cont}</textarea>
  	</td>
  </tr>
  <tr>
  	<td colspan="2" style="text-align: center;">
  		<input type="button" value="수정" id="btnUp">
  		<input type="button" value="목록" onclick="location.href='list?page=${page}'">
  	</td>
  </tr>
</table>
</form>
</div>
</body>

 => 수정버튼을 누를 경우 비밀번호가 빈칸일 경우 팝업 발생. 있을 경우 요청명 update 전달.(post방식)

 => msg가 있을 경우 pop-up내용 출력 후 list 페이지로 이동.

 

 

 

- UpdateController

@Controller
public class UpdateController {
	
	@Autowired
	private BoardDaoInter inter;
	...
	@RequestMapping(value="update", method=RequestMethod.POST)
	public ModelAndView editSubmit(BoardBean bean,
			@RequestParam("page") String page) {
		// 비밀번호 체크
		String pass = inter.selectPass(Integer.toString(bean.getNum()));
		
		ModelAndView view = new ModelAndView();
		if(bean.getPass().equalsIgnoreCase(pass)) { // 사용자 비밀번호와 db의 비밀번호 비교
			boolean b = inter.update(bean);
			if(b) {
				//view.setViewName("redirect:/list?page="+page);
				view.setViewName("redirect:/detail?num="+bean.getNum()+"&page="+page);
			}else {
				view.setViewName("redirect:/error");
			}
		}else {
			view.setViewName("update");
			view.addObject("msg", "비밀번호 불일치");
			view.addObject("page", page);
		}
		return view;
	}
}

=> 수정 페이지에서 수정 버튼 누를 경우 실행.

=> selectPass() : 해당 num의 password 리턴.

=> 입력 패스트워드와 DB의 저장된 비밀 번호가 일치 할 경우 update()메소드 실행.

                                                               불일치 시 error msg를 전달하여 update 페이지 실행.

=> update 성공 시 수정 진행한 detail 페이지로 이동. 실패 시 error.jsp 실행.

 

 

 - BoardDaoImpl

@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
	
	@Autowired
	public BoardDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	...
	@Override
	public boolean update(BoardBean bean) {
		try {
			int result = getSqlSession().update("updateData", bean);
			if(result > 0) {
				return true;
			}else {
				return false;
			}
		} catch (Exception e) {
			System.out.println("update err"+e);
			return false;
		}
	}
	...
	@Override
	public String selectPass(String num) { // 수정용 : 비밀 번호 비교
		return getSqlSession().selectOne("selectPass", num);
	}
	...
}

=> update() : 업데이트 성공 시 true, 실패시 false 리턴.

=> selectPass() : 해당 num의 password 리턴.

 

 

-DataMapper.xml

 ...
 <update id="updateData" parameterType="formBean">
 	update board 
 	set name=#{name},mail=#{mail},title=#{title},cont=#{cont}
 	where num=#{num}
 </update>
 ...
 <!-- password -->
 <select id="selectPass" parameterType="string" resultType="string">
 	select pass from board 
 	where num=#{num}
 </select>
 ...

=> update() : 해당 데이터로 수정.

=> selectPass() : 해당 num의 password 리턴.

 

 


⑦ 게시글 삭제

- DeleteController

@Controller
public class DeleteController {
	@Autowired
	private BoardDaoInter inter;
	
	@RequestMapping("delete")
	public ModelAndView del(@RequestParam("num") String num,
			@RequestParam("page") String page,
			@RequestParam("pass") String pass) {
		BoardDto dto = inter.getDetail(num);
		ModelAndView view = new ModelAndView();
		System.out.println(pass);
		if(dto.getPass().equalsIgnoreCase(pass)) { // 사용자 비밀번호와 db의 비밀번호 비교
			boolean b = inter.delete(num);
			if(b) {
				view.setViewName("redirect:/list?page="+page);
			}else {
				view.setViewName("redirect:/error");
			}
		}else {
			view.setViewName("detail");
			view.addObject("data", dto);
			view.addObject("msg", "비밀번호 불일치");
			view.addObject("page", page);
		}
		return view;
	}
}

 => 상세 페이지에서 delete 버튼 클릭시 실행.

 => 해당 패스워드와 일치 시 delete()메소드 실행.

                            불일치 시 detail 페이지로 이동하여 error msg 팝업 발생.

 => 데이터 삭제 성공 시 전체 페이지 보기로 이동. 실패 시 error.jsp로 이동.

 

 

- BoardDaoImpl

@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
	
	@Autowired
	public BoardDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	...
	@Override
	public BoardDto getDetail(String num) {
		// 글 내용보기, 글 수정 시 데이터 받아오기
		return getSqlSession().selectOne("selectOne",num);
	}
	...
	@Override
	public boolean delete(String num) {
		try {
			int result = getSqlSession().delete("deleteData", num);
			if(result > 0) {
				return true;
			}else {
				return false;
			}
		} catch (Exception e) {
			System.out.println("delete err"+e);
			return false;
		}
	}
	...
}

 => delete() : 삭제 성공시 true, 실패시 false 리턴.

 

 

 - DataMapper.xml

 <delete id="deleteData" parameterType="string">
 	delete from board where num=#{num}
 </delete>

 => 데이터 삭제.

 


⑧ 댓글 기능 추가

 - ReplyController

@Controller
public class ReplyController {
	
	@Autowired
	private BoardDaoInter inter;
	
	@RequestMapping(value="reply", method=RequestMethod.GET)
	public ModelAndView reply(@RequestParam("num") String num,
			@RequestParam("page") String page) {
		ModelAndView view = new ModelAndView("reply");
		view.addObject("data", inter.getDetail(num)); // 원글의 데이터
		return view;
	}
	...
}

 => 상세 페이지의 댓글 버튼 클릭시 실행.

 => 원본 글의 데이터를 저장 후 reply.jsp 실행.

 

 

 - reply.jsp

<head>
	<script type="text/javascript">
	window.onload=function(){
		document.getElementById("btnReply").onclick = chk;
	}
	function chk(){
		if(reFrm.name.value === ""){
			alert("작성자 이름을 입력하세요");
			reFrm.name.focus();
		}
		// 입력자료 오류 검사
		reFrm.submit();
	}
	</script>
</head>
<body>
<jsp:include page="./top.jsp"></jsp:include>
<h4 style="text-align: center;">* 댓글 쓰기</h4>
<form action="reply" method="post" name="reFrm">
<input type="hidden" name="num" value="${data.num}">
<input type="hidden" name="page" value="${page}">
<input type="hidden" name="gnum" value="${data.gnum}">
<input type="hidden" name="onum" value="${data.onum}">
<input type="hidden" name="nested" value="${data.nested}">
<input type="hidden" name="bip" value="<%=request.getRemoteAddr()%>">

<table class="table" style="width: 80%">
  <tr>
  	<td>이름</td>
  	<td><input type="text" name="name"></td>
  </tr>
  <tr>
  	<td>암호</td>
  	<td><input type="text" name="pass"></td>
  </tr>
  <tr>
  	<td>메일</td>
  	<td><input type="text" name="mail"></td>
  </tr>
  <tr>
  	<td>제목</td>
  	<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
  	<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  	<c:set var="title" value="${data.title}"/>
  	<td><input type="text" name="title" value="[Re]:${fn:substring(title,0,8)}"></td>
  </tr>
  <tr>
  	<td>내용</td>
  	<td>
  		<textarea rows="5" style="width:99%" name="cont"></textarea>
  	</td>
  </tr>
  <tr>
  	<td colspan="2" style="text-align: center;">
  		<input type="button" value="작성" id="btnReply">
  		<input type="button" value="목록" onclick="location.href='list?page=${page}'">
  	</td>
  </tr>
</table>
</form>
</body>

 => 댓글 제목이 8보다 클 경우 substring하여 출력.

 => 작성 버튼 클릭 시 작성자이 비어 있을 경우 팝업 발생. 성공 시 reply 요청명 전달(post방식)

 

 

- ReplyController

@Controller
public class ReplyController {
	
	@Autowired
	private BoardDaoInter inter;
	...
	@RequestMapping(value="reply", method=RequestMethod.POST)
	public String replySubmit(BoardBean bean,
			@RequestParam("page") String page) {
		// onum 갱신
		bean.setOnum(bean.getOnum() + 1);
		inter.updateOnum(bean); // 반환값 처리 필요.
		
		// 댓글 저장
		bean.setBdate(); // 작성일 set
		bean.setNum(inter.currentNum() + 1); // 새로운 글의 번호
		bean.setNested(bean.getNested() + 1); // 들여쓰기
		
		if(inter.insertReply(bean)) {
			return "redirect:list?page="+page; // 추가 후 글 목록 보기			
		}else {
			return "redirect:error";
		}
		
	}
}

 => updateOnum() : 원글 내 댓글 번호를 업데이트 한다.

 => insertReply() : 작성일, num+1, 들여쓰기+1하여 댓글을 추가 한다.

 

 

- BoardDaoImpl

@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
	
	@Autowired
	public BoardDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	...
	@Override
	public boolean updateOnum(BoardBean bean) {
		//댓글에서 onum 갱신
		int result = getSqlSession().update("updateOnum", bean);
		if(result>0) {
			return true;
		}else {
			return false;
		}
	}

	@Override
	public boolean insertReply(BoardBean bean) {
		try {
			int result = getSqlSession().insert("insertReData", bean);
			if(result > 0) {
				return true;
			}else {
				return false;
			}
		} catch (Exception e) {
			System.out.println("insertReply err"+e);
			return false;
		}
	}
}

 => updateOnum() : 업데이트 성공 시 true, 실패 시 false 리턴.

 => insertReply() : 추가 성공 시 true, 실패 시 false 리턴.

 

 

 ...
 <!-- reply -->
 <update id="updateOnum" parameterType="formBean">
 	update board set onum=onum + 1
 	where onum >= #{onum} and gnum=#{gnum}
 </update>

 <insert id="insertReData" parameterType="formBean">
 	insert into board
 	values(#{num},#{name},#{pass},#{mail},#{title},#{cont},
 	#{bip},#{bdate},0,#{gnum},#{onum},#{nested})
 </insert>
</mapper>

 => 모든 데이터 중 onum이 group num이 같고 onum이 클 경우 onum을 +1한다.

 => 댓글 정보를 추가한다.

[목차]

10. CRUD(Create, Read, Update, Delete)

 : 스프링 사용.

 : xml 의존방식 db 연결(root-context사용).

① DB 연동

② 모든 list 출력

③ 데이터 추가(Insert)

④ Data 수정(Update)

⑤ 데이터 삭제(Delete)


[내용]

 = sprweb15_crud

① DB 연동

 - Configuration.xml

<configuration>
 <typeAliases>
 	<typeAlias type="pack.model.MemDto" alias="dto"/>
 	<typeAlias type="pack.controller.MemBean" alias="formBean"/>
 </typeAliases>
 <!--  DB 연결을 root-context.xml에서 하도록 수정.
 <properties resource="pack/mybatis/db.properties" />
 <environments default="dev">
  <environment id="dev">
   <transactionManager type="JDBC" />
   <dataSource type="POOLED">
    <property name="driver" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
   </dataSource>
  </environment>
 </environments>
 -->
 <mappers>
  <mapper resource="pack/mybatis/DataMapper.xml" />
 </mappers>
</configuration>

 => DataMapper.xml 연결.

 

 

 - DataMapper.xml

<mapper namespace="dev">
 <select id="selectAll" resultType="dto">
  select num, name, addr from mem
 </select>
 
 <select id="selectPart" parameterType="String" resultType="dto">
 select num, name, addr  from mem
 where num=#{num}
 </select>
 
 <insert id="insertData" parameterType="formBean">
 	insert into mem values(#{num}, #{name}, #{addr})
 </insert>
 
 <update id="updateData" parameterType="formBean">
 	update mem set name=#{name}, addr=#{addr}
 	where num=#{num}
 </update>
 
 <delete id="deleteData" parameterType="String">
 	delete from mem where num=#{num}
 </delete>
 
</mapper>

 => sql문 mapping.

 

 

- db.properties

driver=org.mariadb.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/happydb
username=root
password=123

 => 계정 정보 파일.

 

 

 - root-context.xml

<!-- PropertyPlaceholderConfigurer 사용 : 외부 프로퍼티의 정보를 설정파일에서 사용가능.-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<!-- locations 속성으로 db.properties를 read하도록 설정 -->
	<!-- db.properties의 프로퍼티를 ${프로퍼티값}으로 사용할 수 있다. -->
	<property name="locations">
		<value>classpath:pack/mybatis/db.properties</value>
	</property>
</bean>

<!-- SimpleDriverDataSource 사용 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
	<!--  -->
	<property name="driverClass" value="${driver}"/>
	<property name="url" value="${url}"/>
	<property name="username" value="${username}"/>
	<property name="password" value="${password}"/>
</bean>
<!-- SqlSessionFactoryBean 사용 -->
<!-- 복수개일 경우 id를 셋팅 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- SqlMapConfig의 SqlSession에  Configuration.xml 연결-->
	<property name="dataSource" ref="dataSource"/>
	<property name="configLocation" value="classpath:pack/mybatis/Configuration.xml"></property>
</bean>

 => DB 연동. SqlFactory 객체 생성.

 

 

 - servlet-context.xml

<context:component-scan base-package="pack.controller" />
<context:component-scan base-package="pack.model" />

 => 어노테이션 스캔

 

 


② 모든 list 출력

 - index.jsp

<body>
	<h2>메인</h2>
	<ul>
		<li>인사관리</li>
		<li>영업관리</li>
	 	<li>물류관리</li>
	 	<li><a href="list">회원관리</a></li>
	 	<li>문의사항</li>
	</ul>
</body>

 => url "list" (Get 방식으로 송부)

 

 

- ListController

@Controller
public class ListController {
	@Autowired
	private MemDaoInter daoInter;
	
	@RequestMapping("list")
	public ModelAndView list() {
		/*
		List<MemDto> list = daoInter.getDataAll();
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("list");
		modelAndView.addObject("list", list);
		return modelAndView;
		*/
		return new ModelAndView("list","list",daoInter.getDataAll());
	}
}

 => @Controller : sevlet controller 동작.

 => url "list" 받을 경우 실행. list.jsp에 key("list")로 value(getDataAll() 리턴 값) 전달.

 

 

- MemBean

public class MemBean {
	private String num;
	private String name;
	private String addr;
	
	//getter/setter
}

 => FormBean작성. (insert/delete/update에 사용)

 

 

- MemDto

public class MemDto {
	private String num;
	private String name;
	private String addr;
	
	//getter/setter
}

 => DTO 작성(select에 사용)

 

 

- MemDaoInter

public interface MemDaoInter {
	List<MemDto> getDataAll();
	MemDto selectPart(String num);
	boolean insertData(MemBean bean);
	boolean updateData(MemBean bean);
	boolean deleteData(String num);
}

 => 다형성 프로그래밍을 위한 interface 사용.

 

 

- MemDaoImpl

@Repository
public class MemDaoImpl extends SqlSessionDaoSupport implements MemDaoInter{
	
	@Autowired
	public MemDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}

	@Override
	public List<MemDto> getDataAll() {
		return getSqlSession().selectList("selectAll");
	}
    ...
}

 => extends SqlSessionDaoSupport : sqlSession사용을 위해 상속진행.

 => 생성자 주입을 통한. sqlSessionFactory 객체 주입.

 => DataMapper.xml의 <select>태그의 id "selectAll" sql문 실행 결과 리턴

<select id="selectAll" resultType="dto">
 select num, name, addr from mem
</select>
...

 

 

 - list.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<body>
	<h2>*회원정보(@MVC - MyBatis : CRUD)</h2>
	<a href="insert">회원추가</a><br>
	
	<table border="1">
		<tr>
			<th>번호</th><th>이름</th><th>주소</th><th>변경</th>
		</tr>
		
		<c:forEach var="m" items="${list}">
			<tr>
				<td>${m.num}</td>
				<td>${m.name}</td>
				<td>${m.addr}</td>
				<td>
					<a href="update?num=${m.num}">수정</a>
					<a href="delete?num=${m.num}">삭제</a>
				</td>
			</tr>
		</c:forEach>
	</table>
</body>

 => 모든 list 출력 결과 실행.

 => 회원추가 하이퍼링크 클릭시 url "insert" 송부.(get방식)

 => 수정 하이퍼링크 클릭시 url "update" 송부.(get방식)(num 값 전달)

 => 삭제 하이퍼링크 클릭시 url "delete" 송부.(get방식)(num 값 전달)

 


③ 데이터 추가(Insert)

- InsertController

@Controller
public class InsertController {
	
	@Autowired
	private MemDaoInter daoInter;
	
	@RequestMapping(value = "insert", method = RequestMethod.GET)
	public String insert() {
		return "insform";
	}
	
	...
}

 => GET방식 url "insert" 받을 경우 insert() 메소드 실행.

 => insform.jsp 실행.

 

 

 - insform.jsp

<body>
	<h2>*회원입력</h2>
	<form action="insert" method="post">
		번호 : <input type="number" name="num"><br>
		이름 : <input type="text" name="name"><br>
		주소 : <input type="text" name="addr"><br>
		<input type="submit" value="추가">
	</form>
</body>

 => 회원정보 입력 <form> 생성.

 => submit시 url "insert" 송부(post방식).

 

 

- InsertController

@Controller
public class InsertController {
	
	@Autowired
	private MemDaoInter daoInter;
	...
	@RequestMapping(value="insert", method=RequestMethod.POST)
	public String submit(MemBean bean) {
		
		boolean b = daoInter.insertData(bean);
		if(b) {
			return "redirect:/list"; // list controller를 실행해야함으로 redirect로 보낸다. 
		}else {
			return "error";
		} 
	}
}

=> POST방식 url "insert" 받을 경우 submit() 메소드 실행.

=> <form>태그 value가 MemBean에 set된다.

=> bean값을 매개변수로 insertData()메소드 실행하여 insert 성공시 모든 list를 다시 출력하고,

     실패할 경우 error.jsp 실행.

 

 

 

- MemDaoImpl

@Repository
public class MemDaoImpl extends SqlSessionDaoSupport implements MemDaoInter{
	
	@Autowired
	public MemDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}

	...
	
	@Override
	public boolean insertData(MemBean bean) {
		try {
			getSqlSession().insert("insertData", bean);
			return true;
		} catch (Exception e) {
			System.out.println("insertData err"+e);
			return false;
		}
	}
	
	...
}

 => DataMapper.xml의 <insert>태그의 id "insertData" sql문 실행 결과 리턴

 

<insert id="insertData" parameterType="formBean">
 insert into mem values(#{num}, #{name}, #{addr})
</insert>

 

 

 - error.jsp

<body>
	<h2>에러가 발생하였습니다.</h2>
	<a href="list">회원자료</a> 보기
</body>

 => 에러 처리 jsp.

 


④ Data 수정(update)

- UpdateController

@Controller
public class UpdateController {
	@Autowired
	private MemDaoInter daoInter;
	
	@RequestMapping(value="update", method = RequestMethod.GET)
	public ModelAndView update(@RequestParam("num") String num) {
		MemDto dto = daoInter.selectPart(num);
		return new ModelAndView("upform","dto",dto);
	}
	...
}

=> GET방식 url "update" 받을 경우 update() 메소드 실행.

=> ?key=value값을 @RequestParam사용하여 num에 set.

=> selectPart()메소드 실행하여 검색 값 리턴하여 upform.jsp에 "dto"key로 값 전달.

 

 

 

- MemDaoImpl

@Repository
public class MemDaoImpl extends SqlSessionDaoSupport implements MemDaoInter{
	
	@Autowired
	public MemDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	...
	@Override
	public MemDto selectPart(String num) {
		return getSqlSession().selectOne("selectPart",num);
	}
	...
}

 => DataMapper.xml의 <select>태그의 id "selectPart" sql문 실행 결과 리턴

<select id="selectPart" parameterType="String" resultType="dto">
 select num, name, addr  from mem
 where num=#{num}
</select>

 

 

- upform.jsp

<body>
	<h2>* 회원 정보 수정</h2>
	<form action="update" method="post">
	번호 : ${dto.num}<br>
	<input type="hidden" name="num" value="${dto.num}">
	이름 : <input type="text" name="name" value="${dto.name}"><br>
	주소 : <input type="text" name="addr" value="${dto.addr}"><br>
	<input type="submit" value="수정">
	</form>
</body>

 => 검색한 Data를 초기값으로 set.

 => <form>태그 값 수정 후 submit시 url "update" post방식으로 전달.

 

 

- UpdateController

@Controller
public class UpdateController {
	@Autowired
	private MemDaoInter daoInter;
	...
	@RequestMapping(value="update", method = RequestMethod.POST)
	public String submit(MemBean bean) {
		boolean b = daoInter.updateData(bean);
		if(b) {
			return "redirect:/list";
		}else {
			return "error";
		}
	}
}

=> POST방식 url "update" 받을 경우 submit() 메소드 실행.

=> <form>태그 값 MemBean에 set.

=> bean을 매개변수로 updateData()메소드 실행하여 업데이트 성공시 list.jsp 출력. 실패 시 error.jsp실행.

 

 

 

- MemDaoImpl

@Repository
public class MemDaoImpl extends SqlSessionDaoSupport implements MemDaoInter{
	
	@Autowired
	public MemDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	...
	@Override
	public boolean updateData(MemBean bean) {
		try {
			getSqlSession().update("updateData", bean);
			return true;
		}catch (Exception e) {
			System.out.println("updateData err"+e);
			return false;
		}
	}
	...
}

 => DataMapper.xml의 <update>태그의 id "updateData" sql문 실행 결과 리턴

...
 <update id="updateData" parameterType="formBean">
 	update mem set name=#{name}, addr=#{addr}
 	where num=#{num}
 </update>
...

 

 


⑤ 데이터 삭제(Delete)

- DeleteController

@Controller
public class DeleteController {
	@Autowired
	MemDaoInter daoInter;
	
	@RequestMapping("delete")
	public String delete(@RequestParam("num") String num) {
		boolean b = daoInter.deleteData(num);
		if(b) {
			return "redirect:/list";
		}else {
			return "error";
		}
	}
}

=> GET방식 url "delete" 받을 경우 delete() 메소드 실행.

=> @RequestParam사용하여 num값 set.

=> num을 매개변수로 deleteData()메소드 실행하여 삭제 성공시 list.jsp 출력. 실패 시 error.jsp실행.

 

 

- MemDaoImpl

@Repository
public class MemDaoImpl extends SqlSessionDaoSupport implements MemDaoInter{
	
	@Autowired
	public MemDaoImpl(SqlSessionFactory factory) {
		setSqlSessionFactory(factory);
	}
	...
	@Override
	public boolean deleteData(String num) {
		try {
			int result = getSqlSession().delete("deleteData", num);
			return true;
		}catch (Exception e) {
			System.out.println("deleteData err"+e);
			return false;
		}
	}
}

 => DataMapper.xml의 <delete>태그의 id "deleteData" sql문 실행 결과 리턴

<delete id="deleteData" parameterType="String">
	delete from mem where num=#{num}
</delete>

 

[목차]

9. Controller 처리, DB연동

① 요청 파라미터

② get/post 방식이 다른 동일 요청 처리

③ DB 연동 - DataSource

④ DB연동 - JdbcDaoSuport

⑤ DB연동 - Mybatis(xml), (root-servlet.xml사용)

⑥ DB연동 - mybatis(annotation), (root-servlet.xml사용)


[내용]

① 요청 파라미터

 = sprweb10_para_url
 - index.jsp

<body>
	<h2>* 요청 파라미터 연습</h2>
	<h3>get</h3>
	<a href="kbs/login?type=admin">관리자</a><br>
	<a href="kbs/login?type=user">일반 사용자</a><br>
	<a href="kbs/login">파라미터 없음</a><br>
	<h3>post</h3>
	<form action="kbs/login? method="post">
		data : <input type="text" name="type" value="user">
		<input type="submit" value="전송">
	</form>
	<hr>
	<h2>* 요청 URL로 정보전달</h2>
	<form action="mbc/korea" method="get">
		data : <input type="text" name="name" value="tom">
		<input type="submit" value="전송">
	</form>
	<br>
	<form action="mbc/usa" method="get">
		data : <input type="text" name="name" value="james">
		<input type="submit" value="전송">
	</form>
	<h2>* 요청 URL로 정보전달2</h2>
	<form action="ent/bighit/singer/bts" method="get">
		신곡 : <input type="text" name="title" value="다이너마이트">
		<input type="submit" value="전송">
	</form>
	<form action="ent/yg/singer/blackpink" method="get">
		신곡 : <input type="text" name="title" value="아이스크림">
		<input type="submit" value="전송">
	</form>
</body>

 => 요청 url ? 뒤의 key = value 값을 이용하여 정보 전달. (get/post)

 => 요청 url 중 mbc/ 뒤의 값으로 정보 전달.

 => 요청 url 중 ent/ ? /singer/? 각 ent/ singer 뒤의 값으로 정보 전달.

 

 

 - servlet-context.xml

<context:component-scan base-package="aa.bb.controller" />

 => <context:componet-scan> : controller pakage의 어노테이션 스캔.

 

 

 - LoginController

@Controller // controller 동작
public class LoginController {
	
	@RequestMapping(value="kbs/login", params = "type=admin")
	public ModelAndView aa() {
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("show");
		modelAndView.addObject("msg","관리자");
		return modelAndView;
	}
	
	@RequestMapping(value="kbs/login", params = "type=user")
	public ModelAndView bb() {
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("show");
		modelAndView.addObject("msg","일반고객");
		return modelAndView;
	}
	
	@RequestMapping(value="kbs/login", params = "!type") // parameter가 없는경우
	public ModelAndView cc() {
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("show");
		modelAndView.addObject("msg","인자가 없음.");
		return modelAndView;
	}

 => @RequestMapping(value="url", params = "key=name") : value는 요청 url에, params는 key, name값에 매핑되는

      요청이 들어올 경우 해당 메소드가 실행되도록 한다.

 => params 값에 따라 각각 다른 메소드가 실행되어 setViewName()의 show.jsp 파일에 addObject()로 [msg] key에

      value를 전달한다.

 

 

	// 요청 url의 일부를 변수로 받아 처리
	@RequestMapping(value="mbc/{url}")// url변수에 요청 mbc/의 값을 대입한다.
	public ModelAndView dd(
			@PathVariable String url,
			@RequestParam("name") String name) {
		System.out.println("url : "+url+" name : "+name);
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("show");
		
		if(url.equals("korea")) {
			modelAndView.addObject("msg", url+" "+name+"님 만세");
		}else if(url.equals("usa")) {
			modelAndView.addObject("msg", url+" "+name+"님 ㅇㅇ ");
		}else {
			modelAndView.addObject("msg","기타");
		}
		return modelAndView;
	}
	
	@RequestMapping(value="ent/{co}/singer/{singer}")
	public ModelAndView ee(
			@PathVariable String co,
			@PathVariable String singer,
			@RequestParam("title") String title) {
		
		String datas = "소속사 : "+co+", 가수 : "+singer+", 타이틀 곡 : "+title; 
		
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("show");
		modelAndView.addObject("msg", datas);
		
		return modelAndView;
	}
}

=> @RequestMapping(value="url/{변수명}") : mbc/korea로 요청url이 들어올 경우 변수에 korea가 들어간다.

     @PathVariable 타입 변수명 : 요청 url을 변수로 받도록 하는 어노테이션.

     @RequestParam("<form>의 name 속성") 타입 변수 : 변수 String name에 <form>의 value값이 전달된다.

 

 

 - show.jsp

<body>
	결과는 ${msg}
</body>

 => EL 사용 결과값 출력.

 

 


② get/post 방식이 다른 동일 요청 처리

 = sprweb11_getpost
 - index.jsp

<body>
	<h2>메인</h2>
	회원처리 게시판 <a href="login">로그인(get)</a> 
	<pre>본문
	</pre>	
</body>

 => url요청을 login 전달. (Get방식)

 

 

 - LoginController

@Controller
public class LoginController {
	private String formName = "loginform";
	
	@Autowired
	private LoginForm loginForm;
			
	@RequestMapping(value="login", method=RequestMethod.GET)
	public String form() {
		return formName;
	}
	
	@RequestMapping(value="login", method=RequestMethod.POST)
	public String submit(LoginForm loginForm) {
		
		if(loginForm.getUserid().equalsIgnoreCase("aa")&
				loginForm.getPasswd().equalsIgnoreCase("11")) {
			
			return "redirect:/list";//로그인 성공하면 목록보기
		}else {
			return formName; // 로그인 실패하면 로그인 화면으로 이동
		}
	}
}

 => index.jsp에서 전달한 url "login"(get 방식)으로 form()메소드가 실행된다.

 => @RequestMapping(value="url명", method=RequestMethod.GET) : 해당 url중 GET방식으로 전달할 경우만 해당

       메소드가 실행된다.

=> @Controller의 클래스에 String을 리턴값으로 가지는 form() 메소드가 실행될 경우 리턴값과 동일한 jsp파일을 실행

     한다. (loginform.jsp 실행)

 

 

 - loginform.jsp

<body>
	<h2>자료입력</h2>
	<form action="login" method="post"/>
		id : <input type="text" name="userid" id="userid"/><br>
		pwd: <input type="text" name="passwd" id="passwd"/><br>
		<input type="submit" value="전송(전통적)"/><br>
	</form>
</body>

 => LoginController class의 form() 메소드 실행으로 실행된다.

 => index.jsp의 동일한 url 요청명인 "login"으로 Post방식으로 값을 전달한다. 

 

 

 - LoginController

@Controller
public class LoginController {
	private String formName = "loginform";
	
	@Autowired
	private LoginForm loginForm;
			
	@RequestMapping(value="login", method=RequestMethod.GET)
	public String form() {
		return formName;
	}
	
	@RequestMapping(value="login", method=RequestMethod.POST)
	public String submit(LoginForm loginForm) {
		
		if(loginForm.getUserid().equalsIgnoreCase("aa")&
				loginForm.getPasswd().equalsIgnoreCase("11")) {
			
			return "redirect:/list";//로그인 성공하면 목록보기
		}else {
			return formName; // 로그인 실패하면 로그인 화면으로 이동
		}
	}
}

 => @RequestMapping(value="url명", method=RequestMethod.POST) : url중 Post방식으로 송부시에 실행된다.

 => String 메소드명(FormBean 변수명) : <form>태그의 name 속성값과 동일한 변수를 가지는 FormBean을 매개변수로

      가질 경우 FormBean객체에 각 값들이 대입된다.

      str1.equalsIngnorseCase(str2) : str1과 str2를 대소문자 구분하지않고 비교한다.

 => Login에 성공할 경우 redirect로 클라이언트에서 요청을 실행한 것과 같이 실행하여야한다. (forward를 사용하지

      않음). Redirect로 list.jsp를 실행한다.

 

 

-  LoginForm

@Component
public class LoginForm {
	private String userid; // <form>태그의 name속성값과 동일하게 변수를 가진다.
	private String passwd;
	
	public String getUserid() {
		return userid;
	}
	public void setUserid(String userid) {
		this.userid = userid;
	}
	public String getPasswd() {
		return passwd;
	}
	public void setPasswd(String passwd) {
		this.passwd = passwd;
	}
}

 => <form>태그의 name속성값과 동일하게 변수를 가지도록 작성한다. FormBean은 client의 요청을 처리할 때 사용

      한다. DTO는 DB 자료를 처리할 때 사용. (용도에 따라 관례적으로 FormBean과 DTO를 사용한다)

 

 

 - ListController

@Controller
public class ListController {
	@RequestMapping("list")
	public ModelAndView aaa() {
		ModelAndView view = new ModelAndView("list");
		view.addObject("msg", "로그인 성공으로 하고 뭔가를 출력함");
		
		return view;
	}
}

 => 성공시 실행할 내용을 구현한다. "msg" key에 값 전달.

 

 

 - list.jsp

<body>
	결과 : ${msg}
</body>

 => EL사용하여 msg값 출력

 


③ DB 연동 - DataSource

 = sprweb12_db_legacy
 - main.jsp

<body>
	<h2>메인</h2>
	<a href="testdb">상품보기</a>
</body>

 => "testdb" url 전달.(get 방식)

     <%response.sendRedirect("testdb"); %>로 동일한 기능 수행하도록 구현가능.

 

 

 - servlet-context.xml

<context:component-scan base-package="aa.bb.controller" />
<context:component-scan base-package="aa.bb.model" />

 => 어노테이션 scan.

 

 

- ListController

@Controller // 클라이언트로 부터  요청 받을 경우 동작
public class ListController {
	
	@Autowired
	private DataDao dataDao;
	
	@RequestMapping("testdb") // url mapping
	public ModelAndView listProcess() {
		ArrayList<SangpumDto> list = dataDao.getDataAll();
		
		return new ModelAndView("list", "datas", list); // list.jsp에 연결.
	}
}

 => @Controller : 클라이언트로 부터  요청 받을 경우 동작.

      @Autowired : setter injection 기능 수행.

      @RequestMapping("url명") : url명과 동일한 url을 받을 경우 실행.

      dataDao의 getDataAll()를 실행한 list값을 "datas" key에 넣어 실행한다.

 

 

- SangpumDto

public class SangpumDto {
	private String code, sang, su, dan;
	//getter/setter
}

 => DB 데이터를 담는 DTO 정의.

 

 

- DataDao

@Repository // DB 연결.
public class DataDao {
	private Connection conn;
	private PreparedStatement ptmt;
	private ResultSet rs;
	
	public DataDao() {
	}
	
	@Autowired
	private DataSourceMaria dataSourceMaria; 
	
	public ArrayList<SangpumDto> getDataAll(){
		ArrayList<SangpumDto> list = new ArrayList<SangpumDto>();
		
		try {
			String sql = "select * from sangdata";
			conn = dataSourceMaria.getConnection();
			ptmt=conn.prepareStatement(sql);
			rs = ptmt.executeQuery();
			while(rs.next()) {
				SangpumDto dto = new SangpumDto();
				dto.setCode(rs.getString("code"));
				dto.setSang(rs.getString("sang"));
				dto.setSu(rs.getString("su"));
				dto.setDan(rs.getString("dan"));
				list.add(dto);
			}
		} catch (Exception e) {
			System.out.println("getDataAll err"+e);
		}finally {
			try {
				if(conn != null) conn.close();
				if(ptmt != null) ptmt.close();
				if(rs != null) rs.close();
			} catch (Exception e2) {
				System.out.println(e2);
			}
		}
		return list;
	}
}

 => @Repository : 객체 생성. DB 연결하는 클래스에 사용. 기능은 @Component와 동일.

      @Autowired : DriverManagerDataSource를 상속받은 클래스 객체를 setter injection한다.

      getConnection() : jdbc와 연결.

      prepareStatement(sql) : sql문을 get한다.

      executeQuery() : sql문을 실행한 결과를 리턴한다.

      rs.next() : sql 실행 결과를 한 행 단위로 읽는다.

      rs.getString("record명") : 읽고 있는 sql 실행결과 라인의 Record 값을 리턴한다.

 => sql문의 실행 결과를 dto에 담고 실행결과의 한 줄씩 list에 담는다.(list.add(dto))

 => sql문의 실행결과를 ArrayList로 반환한다.

 

 

- DataSourceMaria

@Repository() // DB연동
//@Repository("dataSource") 
public class DataSourceMaria extends DriverManagerDataSource{
	public DataSourceMaria() {
		setDriverClassName("org.mariadb.jdbc.Driver");
		setUrl("jdbc:mysql://127.0.0.1:3306/test");
		setUsername("root");
		setPassword("123");
	}
}

 => extends DriverManagerDataSource : 상속하여 DB와 연결하는 객체를 생성한다.

 => setDriverClassName() / setUrl() / setUsername() / setPassword() : JDBC연결을 위한 정보를 set한다.

 

 

- list.jsp

<%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"%>
<body>
	<h2>*상품자료(전통적)</h2>
	<table border="1">
		<tr>
			<th>코드</th><th>품명</th><th>수량</th><th>단가</th>
		</tr>
		
		<c:forEach var="s" items="${datas}">
			<tr>
				<td>${s.code}</td>
				<td>${s.sang}</td>
				<td>${s.su}</td>
				<td>${s.dan}</td>
			</tr>
		</c:forEach>
	</table>
</body>

 => <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> : EL 액션태그로 core 메소드를 액션태그를 사용

      할 수 있게 library를 연결한다.

 => <c:forEach var="변수명" items="${리스트명}"> : var에 items의 list 값을 index 0 부터 null까지 반복 실행한다.

       (for-each문)

 => EL사용하여 결과값 출력.

 

 


④ DB연동 - JdbcDaoSuport

= sprweb13_db_jdbc_dao_support
-  index.jsp

<body>
	<a href="testdb">상품보기(JbdcDaoSupport)</a>
</body>

 => DataSource와 동일

 

 

- servlet-context.xml

<context:component-scan base-package="aa.bb.controller" />
<context:component-scan base-package="aa.bb.model" />

 => DataSource와 동일

 

 

- ListController

@Controller // 클라이언트로 부터  요청 받을 경우 동작
public class ListController {
	
	@Autowired
	private DataDao dataDao;
	
	@RequestMapping("testdb") // url mapping
	public ModelAndView listProcess() {
		ArrayList<SangpumDto> list = dataDao.getDataAll();
		
		return new ModelAndView("list", "datas", list); // list.jsp에 연결.
	}
}

 => DataSource와 동일

 

 

- SangpumDto

public class SangpumDto {
	private String code, sang, su, dan;
	//getter/setter
}

 => DataSource와 동일

 

 

- DataSourceMaria

@Repository() // DB연동
//@Repository("dataSource") 
public class DataSourceMaria extends DriverManagerDataSource{
	public DataSourceMaria() {
		setDriverClassName("org.mariadb.jdbc.Driver");
		setUrl("jdbc:mysql://127.0.0.1:3306/test");
		setUsername("root");
		setPassword("123");
	}
}

 => DataSource와 동일

 

 

- DataDao

@Repository // DB 연결.
public class DataDao extends JdbcDaoSupport{
	//@Autowired
	//private DataSourceMaria dataSourceMaria; // DataDao에 주입됨.
	//DataSource를 JdbcDaoSupport가 가지고 있으로 생성자를 이용한다.
	
	public DataDao(DriverManagerDataSource dataSource) {
		setDataSource(dataSource);
	}
	
	public List<SangpumDto> getDataAll(){
		String sql="select * from sangdata";
		return getJdbcTemplate().query(sql, new ItemRowMapper());
	}
	
	class ItemRowMapper implements RowMapper{
		@Override
		public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
			SangpumDto dto = new SangpumDto();
			dto.setCode(rs.getString("code"));
			dto.setSang(rs.getString("sang"));
			dto.setSu(rs.getString("su"));
			dto.setDan(rs.getString("dan"));
			return dto;
		}
	}
}

 => extends JdbcDaoSupport : JdbcDaoSupport 상속 받아 기능을 수행한다.

 => @Autowird를 사용하면 DataDao객체에 dataSource가 setter injection됨으로 해당 기능이 정상동작하지않는다.

       정상동작을 위해선 상속을 받고있는 JdbcDaoSupport객체에 setter injection을 하여야한다. 생성자를 이용하여

       DataDao객체가 생성되면 setDataSource()가 실행되도록 하여 JdbcDaoSupport객체에 dataSource를 setter

       injection한다.

 => getJdbcTemplate().query(sql, new ItemRowMapper()) : sql문이 ItemRowMapper클래스가 실행하여 한줄 씩 실행된

      결과를  List 리턴.

 => implemets RowMapper : RowMapper를 상속하여 mapRow() 메소드를 오버라이딩하여 sql문을 실행한다.

 

 

 

- list.jsp

<%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"%>
<body>
	<h2>*상품자료(전통적)</h2>
	<table border="1">
		<tr>
			<th>코드</th><th>품명</th><th>수량</th><th>단가</th>
		</tr>
		
		<c:forEach var="s" items="${datas}">
			<tr>
				<td>${s.code}</td>
				<td>${s.sang}</td>
				<td>${s.su}</td>
				<td>${s.dan}</td>
			</tr>
		</c:forEach>
	</table>
</body>

 => DataSource와 동일

 


⑤ DB연동 - Mybatis(xml) (root-servlet.xml사용)

 = sprweb14_db_mybatis

 - pom.xml

<!-- MariaDB driver -->
<dependency>
	<groupId>org.mariadb.jdbc</groupId>
	<artifactId>mariadb-java-client</artifactId>
	<version>2.6.2</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.0</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.0</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${org.springframework-version}</version>
</dependency>

 => MariaDB, Mybatis lib 추가한다.

 

 

- main.jsp

<body>
	<a href="list">상품보기(@MVC-MyBatis)</a>
</body>

=> url 요청명 list 전달.(get방식)

 

 

 - servlet-context.xml

<context:component-scan base-package="pack.controller" />
<context:component-scan base-package="pack.model" />

=> 어노테이션 사용을 위한 패키지 스캔.

 

 

- db.properties

driver=org.mariadb.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test
username=root
password=123

 => 계정정보를 가진 자바 속성 파일. key=name 형식. (암호화 파일로 구현하는 것을 지향)

 

 

 - Configuration.xml

<configuration>
 <typeAliases>
 	<typeAlias type="pack.model.SangpumDto" alias="dto"/>
 	<typeAlias type="pack.controller.SangpumBean" alias="formBean"/>
 </typeAliases>
 <!--  DB 연결을 root-context.xml에서 하도록 수정.
 <properties resource="pack/mybatis/db.properties" />
 <environments default="dev">
  <environment id="dev">
   <transactionManager type="JDBC" />
   <dataSource type="POOLED">
    <property name="driver" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
   </dataSource>
  </environment>
 </environments>
 -->
 <mappers>
  <mapper resource="pack/mybatis/DataMapper.xml" />
 </mappers>
</configuration>

 => 공유자원을 관리하는 root-context.xml에서 DB연결을 하도록, Sql문 mapping하는 DataMapper.xml관련 설정을

      제외하고 삭제.

 => DataMapper파일과 alias를 정의한다.

 

 

 - DataMapper.xml

<mapper namespace="dev">
	<select id="selectAll" resultType="dto">
		select * from sangdata
	</select>
	<select id="selectSearch" parameterType="formBean" resultType="dto">
		select code, sang, su, dan from sangdata
		where sang like concat('%',#{searchValue},'%')
	</select>
</mapper>

 => sql문과 연결되는 Mapper 파일을 작성한다.(<mapper>태그 내부에 sql문을 형식에 맞춰 작성)

 => <select> : select문 작성 시 사용되는 태그. (id속성 - 외부에서 호출할 이름 / resultType - sql문을 실행한 결과

                    Type / parameterType속성 - 외부로 부터 받을 매개변수의 타입, #{변수명}에 매개변수가 대입된다.)

=> like concat('%', 검색어, '%') : 검색어가 포함된 결과 반환. ( like %검색어%)

 

 

- SqlMapConfig : 미사용 (root-context.xml에서 DB연결)

 

 

 - root-context.xml

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="locations">
		<value>classpath:pack/mybatis/db.properties</value>
	</property>
</bean>

 => <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> : 외부 파일의 프로퍼티

       정보를 해당 설정파일에서 사용가능하도록 기능 제공.

 => locations 속성으로 db.properties를 read하도록 설정.
      db.properties의 프로퍼티를 ${프로퍼티값}으로 사용할 수 있다.

<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
	<property name="driverClass" value="${driver}"/>
	<property name="url" value="${url}"/>
	<property name="username" value="${username}"/>
	<property name="password" value="${password}"/>
</bean>

<bean class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"/>
	<property name="configLocation" value="classpath:pack/mybatis/Configuration.xml"></property>
</bean>

 => <bean class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> : db를 사용할 수 있도록 기능제공.

               drivaerClass, url, usernam, password를 setter 주입하여 db연결 객체 생성.

 => <bean class=org.mybatis.spring.SqlSessionFactoryBean"> : SqlSessionFatory 객체를 생성.

               Configuration.xml, dataSource를 setter 주입하여 factory 객체 생성.

               factory객체를 여러개 사용시에는 id속성을 사용한다.

 

 

- ListController

@Controller
//@ComponentScan("pack.model")
public class ListController {
	
	@Autowired // setter injection
	private SangpumInter sangpumInter; // 다형성 사용
	
	@RequestMapping("list")
	public Model process(Model model){
		model.addAttribute("data", sangpumInter.list());
		// Model객체는 addAttribe(key, value) 메소드를 사용하여 값을 전달.
		return model; // 받은 요청명 그대로 송부한다.
	}
}

 => @Controller : Controller 기능 수행 (servlet 동작)

 => "list" url 받을 경우 동작한다. list()메소드를 실행한 결과를 "data" key에 넣어 list.jsp에 리턴한다.

 

 

- SangpumDto

public class SangpumDto {
	private String code,sang, su, dan;
	//setter, getter
}

 => DB연동에 사용되는 데이터는 Dto로 작성.(select)

 

 

- SangpumBean

public class SangpumBean {
	// formBean : 수정, 삭제 등의 작업이 있다면 code, sang, su, dan도 처리
	// 검색용
	private String searchValue;

	public String getSearchValue() {
		return searchValue;
	}

	public void setSearchValue(String searchValue) {
		this.searchValue = searchValue;
	}
}

=> insert, delete의 작업을 진행할 경우 FormBean으로 작성.

=> select문에 매개변수로 들어가 조건을 추가할 searchValue 변수 정의.

 

 

- SangpumInter

public interface SangpumInter {
	List<SangpumDto> list() throws DataAccessException;
	List<SangpumDto> seach(SangpumBean bean) throws DataAccessException;
	//insert, update, delete ...
}

 => 테이블별로 interface로 관리한다.

 => db에서 select 전체 테이블 조회 기능을 갖출 list()메소드 선언.

 => db에서 select 특정 검색어가 들어간 데이터를 조회 기능을 갖출 search()메소드 선언.

 

 

- SangpumImpl

@Repository // DB연동
public class SangpumImpl extends SqlSessionDaoSupport implements SangpumInter{
	
	@Autowired // 타입에 의한 매핑
	// 복수개일 경우 @Quiyfied사용
	public SangpumImpl(SqlSessionFactory factory) {
		// root-context.xml에서 factory 객체 생성함.
		setSqlSessionFactory(factory); // SqlSessionDaoSupport에 factory 주입.
	}
	
	@Override
	public List<SangpumDto> list() throws DataAccessException {
		return getSqlSession().selectList("selectAll");
	}

	@Override
	public List<SangpumDto> seach(SangpumBean bean) throws DataAccessException {
		return getSqlSession().selectList("selectSearch", bean);
	}
}

 => @Repository : 객체생성(DB연동 기능을 갖출경우 관례적으로 @Repository사용)

 => extends SqlSessionDaoSupport : SqlSession을 제공하는 추상 클래스.(getSqlSession()메소드)

 => implements SangpumInter : 다형성을 통한 테이블별 관리를 하기 위해 interface사용

 => 생성자 setter 주입을 통해 SqlSessionFactory 객체를 sqlSessionDaoSupport에 set한다.(setSqlSessionFactory() 사용)

 => getSqlSession() : sqlSession객체의 메소드를 사용하여 sql문을 실행한 결과를 리턴한다.

=> selectList("매핑 id") : select문을 실행하여 복수의 data를 list로 리턴한다. DataMapper.xml의 id를 매개변수로

     가진다.

 

 

- SearchController

@Controller
public class SearchController {
	@Autowired
	private SangpumInter sangpumInter;
	
	@RequestMapping(value="search", method = RequestMethod.POST)
	public ModelAndView searchProcess(SangpumBean bean) {
		List<SangpumDto> list = sangpumInter.seach(bean);
		return new ModelAndView("list","data", list);
	}
}

 => "search" url 받을 경우 <form>태그의 name 속성들의 값을 formBean객체로 대입하여 받는다.

       formBean객체를 매개변수로 search()메소드를 실행하여 list결과를 "data" key에 리턴한다.

 

 

 - list.jsp

<body>
	<h2>* 상품정보(@MVC - MyBatis)</h2>
	<table border="1">
		<tr>
			<th>코드</th><th>품명</th><th>수량</th><th>단가</th>
		</tr>	
		<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
		<c:forEach var="s" items="${data}">
		<tr>
			<td>${s.code}</td>
			<td>${s.sang}</td>
			<td>${s.su}</td>
			<td>${s.dan}</td>
		</tr>
		</c:forEach>
		<tr>
			<td colspan="4">
				<form action="search" method="post">
					이름 : <input type="text" name="searchValue">
					<input type="submit" value="검색">
				</form>
			</td>
		</tr>
	</table>
</body>

 => sql 결과를 테이블 형태로 출력.

 

 


⑥ DB연동 - mybatis(annotation), (root-servlet.xml사용)

 = sprweb14_db_mybatis_annotation

- main.jsp

<body>
	<a href="list">상품보기(@MVC-MyBatis)</a>
</body>

=> xml방식과 동일

 

 

 - servlet-context.xml

<context:component-scan base-package="pack.controller" />
<context:component-scan base-package="pack.model" />

=> xml방식과 동일

 

 

 

- db.properties

driver=org.mariadb.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test
username=root
password=123

=> xml방식과 동일

 

 

 

 - Configuration.xml : 미사용(어노테이션 사용)

 

 

 - DataMapper.xml : 미사용(어노테이션 사용)

 

- SqlMapConfig : 미사용 (root-context.xml에서 DB연결)

 

 

 - root-context.xml

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="locations">
		<value>classpath:pack/mybatis/db.properties</value>
	</property>
</bean>

=> xml방식과 동일

 

 

- ListController

@Controller
//@ComponentScan("pack.model")
public class ListController {
	
	@Autowired // setter injection
	private SangpumInter sangpumInter; // 다형성 사용
	
	@RequestMapping("list")
	public Model process(Model model){
		model.addAttribute("data", sangpumInter.list());
		// Model객체는 addAttribe(key, value) 메소드를 사용하여 값을 전달.
		return model; // 받은 요청명 그대로 송부한다.
	}
}

=> xml방식과 동일

 

 

- SangpumDto

public class SangpumDto {
	private String code,sang, su, dan;
	//setter, getter
}

=> xml방식과 동일

 

 

 

- SangpumBean

public class SangpumBean {
	// formBean : 수정, 삭제 등의 작업이 있다면 code, sang, su, dan도 처리
	// 검색용
	private String searchValue;

	public String getSearchValue() {
		return searchValue;
	}

	public void setSearchValue(String searchValue) {
		this.searchValue = searchValue;
	}
}

=> xml방식과 동일

 

 

- SangpumInter

public interface SangpumInter {
	List<SangpumDto> list() throws DataAccessException;
	List<SangpumDto> seach(SangpumBean bean) throws DataAccessException;
	//insert, update, delete ...
}

=> xml방식과 동일

 

 

- SangpumAnnoInter

// MyBatis SQL mapping interface file
public interface SangpumAnnoInter {
	@Select("select * from sangdata")
	public List<SangpumDto> selectAllData();
	
	@Select(" select code, sang, su, dan from sangdata where sang like concat('%',#{searchValue},'%')")
	public List<SangpumDto> selectSearch(SangpumBean bean);
}

 => MyBatis SQL mapping interface파일 작성.

 => @Select("select sql문") : db에서 sql문 실행한 결과값을 리턴한다.

 

 

- SangpumImpl

@Repository // DB연동
public class SangpumImpl implements SangpumInter{
	
	@Autowired
	private SangpumAnnoInter sangpumAnnoInter;
	
	@Override
	public List<SangpumDto> list() throws DataAccessException {
		return sangpumAnnoInter.selectAllData();
	}

	@Override
	public List<SangpumDto> seach(SangpumBean bean) throws DataAccessException {
		return sangpumAnnoInter.selectSearch(bean);
	}
}

 => extends SqlSessionDaoSupport은 하지않는다.

 => @Autowired : sql mapping interface객체를 setter injection한다.

=> sql mapping interface객체의 메소드의 결과를 리턴받는다.

 

 

- SearchController

@Controller
public class SearchController {
	@Autowired
	private SangpumInter sangpumInter;
	
	@RequestMapping(value="search", method = RequestMethod.POST)
	public ModelAndView searchProcess(SangpumBean bean) {
		List<SangpumDto> list = sangpumInter.seach(bean);
		return new ModelAndView("list","data", list);
	}
}

=> xml방식과 동일

 

 

 - list.jsp

<body>
	<h2>* 상품정보(@MVC - MyBatis)</h2>
	<table border="1">
		<tr>
			<th>코드</th><th>품명</th><th>수량</th><th>단가</th>
		</tr>	
		<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
		<c:forEach var="s" items="${data}">
		<tr>
			<td>${s.code}</td>
			<td>${s.sang}</td>
			<td>${s.su}</td>
			<td>${s.dan}</td>
		</tr>
		</c:forEach>
		<tr>
			<td colspan="4">
				<form action="search" method="post">
					이름 : <input type="text" name="searchValue">
					<input type="submit" value="검색">
				</form>
			</td>
		</tr>
	</table>
</body>

=> xml방식과 동일

 

 


 

[목차]

8. Controller 처리

    ① Client요청을 Controller로 연결하기
    공유 객체 사용
    패턴을 사용한 경로 매핑
    @annotation을 이용한 Controller호출
    Spring MVC Project를 사용
    @RequestParam 사용(get)
        * Controller 값 처리(기존) : getParameter()
        * Controller 값 처리(어노테이션 사용)
        * get 방식은 한글이 안깨지나 post 방식사용시 한글이 깨진다. Encording을 하여 한글 깨짐을 방지한다.
    FormBean, @ModelAttribute 사용(post)
        * Controller 값 처리(기존) : getParameter()
        * Controller 값 처리(FormBean 사용)
        * Controller 값 처리(@ModelAttribute 사용)

 


[내용]

1) Client요청을 Controller로 연결하기

 

    : implements Controller와 @Controller 사용

 

=sprweb3

 - start.html

<body>
	<a href="hello.do">클릭 1-1(get)</a>
	<form action="hello.do" method="post">
		<input type="submit" value="클릭 1-2(post)"/>
	</form>
	<hr>
	<a href="world.do">클릭 2-1</a>
	<a href="good.do">클릭 2-2</a>
	<a href="hi/nice.do">클릭 2-3</a>
	<form action="korea.do" method="get">
		<input type="submit" value="클릭 2-4(get)"/>
	</form>
	<form action="usa.do" method="post">
		<input type="submit" value="클릭 2-4(post)"/>
	</form>
</body>

 => 클릭 1-1 (요청 : hello.do / get)

      클릭 1-2 (요청 : hello.do / post)

      클릭 2-1 (요청 : world.do / get)

      클릭 2-2 (요청 : good.do / get)

      클릭 2-3 (요청 : hi/nice.do / get)

      클릭 2-4 (요청 : korea.do / get)

      클릭 2-4 (요청 : usa.do / post)

 

 

 - web.xml

<!-- DispatcherServlet는 test-servlet.xml의 설정을 불러온다. -->
<servlet>
	<servlet-name>test</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- servlet-name이 아닌 다른  path로 연결할 경우 사용-->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			<!-- 공백 구분 -->
			/WEB-INF/abc/hello-servlet.xml
			/WEB-INF/def/world-servlet.xml
		</param-value>
	</init-param>
</servlet>
<servlet-mapping>
	<servlet-name>test</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

=> test-servlet.xml이 아닌 다른 patch로 연결할 경우, <init-param> 태그를 사용한다.

     hello-servlet, world-servlet으로 연결된다.

 

 

- hello-servlet.xml/ world-servlet.xml

 * Controller implemets(상속)을 사용할 경우

<!-- 2. Handler Mapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

<!-- 3. Controller -->
<bean name="/hello.do" class="pack.HelloController"></bean>

<!-- 5. ViewResover -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>

 => hello.do 요청이 올 경우 HelloController가 실행된다.

 

  * @Controller(어노테이션)을 사용할 경우

<!-- 어노테이션 사용시 삭제 -->
<!-- 2. Handler Mapping -->
<!-- 
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
 -->
<!-- 3. Controller -->
<!--  
<bean name="/hello.do" class="pack.HelloController"></bean>
-->
<!-- 어노테이션 사용시 추가 -->
<context:component-scan base-package="pack"/>

<!-- 5. ViewResover -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>

 => 어노테이션 사용시 <context:component-scan>를 사용한다. pack package의 어노테이션이 동작하도록 한다.

 

 

 - HelloController
 * Controller implemets(상속)을 사용할 경우

public class HelloController implements Controller{
	
	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
									throws Exception {
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("veiw1"); // veiw1.jsp에 연결 
		modelAndView.addObject("message", "hello"); // 모델에 다녀왔다 가정. // key-value 전달.
		
		return modelAndView;
	}
}

=> hello-servlet.xml/ world-servlet.xml에서 연결한 hello.do 요청 발생 시 실행된다.

=> handlerRequest() 메소드를 오버라이딩하여 request를 처리한다.

=> setVieName("파일명")으로 view1에 연결된다. .hello-servlet.xml 파일의 ViewResover 처리로 /views/view1.jsp파일이

     연결된다.

 => addObject(key, value)를 이용하여 값을 전달한다.

 

  * @Controller(어노테이션)을 사용할 경우

 

@Controller
@RequestMapping("hello.do") // 모든 메소드가 수행된다.
public class HelloController{
	@RequestMapping(method=RequestMethod.GET)
	//@RequestMapping("hello.do") // 해당 메소드만 실행된다.
	public ModelAndView aaa() {
		ModelAndView modelAndView =  new ModelAndView();
		modelAndView.setViewName("view1");
		modelAndView.addObject("message", "hello aaa");
		return modelAndView;
	}
	
	@RequestMapping(method=RequestMethod.POST)
	//@RequestMapping("hello.do")
	public ModelAndView bbb() {
		ModelAndView modelAndView =  new ModelAndView();
		modelAndView.setViewName("view1");
		modelAndView.addObject("message", "hello bbb");
		return modelAndView;
	}
}

 => hello-servlet.xml의 <context:component-scan>태그로 인해 @Controller가 동작한다.

      @Controller : 클라이언트 요청에 따라 해당 클래스가 Controller동작을 하게된다.

      @RequestMapping("요청명") : 요청명에 해당하는 요청이 들어올 경우 클래스가 실행된다.

      @RequestMapping(method=RequestMethod.GET) : Get방식의 요청이 들어올때만 실행하게 한다.

      @RequestMapping(method=RequestMethod.POST) : Post방식의 요청이 들어올때만 실행하게 한다.

 => ModelAndView객체를 리턴하도록 하여 값을 전달한다. 메소드명을 임의 지정 가능하다.

 

 

 -  WorldController
 * Controller implemets(상속)을 사용할 경우

public class WorldController implements Controller{
	
	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
								throws Exception {
		
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("view2"); // veiw1.jsp에 연결 
		modelAndView.addObject("message", "hi world"); // 모델에 다녀왔다 가정. // key-value 전달.
		
		return modelAndView;
	}
}

 => HelloController와 동일

 

  * @Controller(어노테이션)을 사용할 경우

 

@Controller
public class WorldController{
	@RequestMapping({"/world.do","/good*.*","/hi/nice.do"}) // 여러개의 요청을 받을 수 있음.
	//@RequestMapping("world.do")
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
					throws Exception {
		
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("view2"); // veiw1.jsp에 연결 
		modelAndView.addObject("message", "hi world"); // 모델에 다녀왔다 가정. key-value 전달.
		
		return modelAndView;
	}
	
	@RequestMapping(value="/korea.do", method=RequestMethod.GET) // GET만 받을 수 있다.
	//@RequestMapping({"/korea.do"})// Get과 Post를 모두 받을 수 있다.
	public ModelAndView handleRequest2(HttpServletRequest request, HttpServletResponse response)
					throws Exception {
		
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("view2"); // veiw1.jsp에 연결 
		modelAndView.addObject("message", "hi world korea"); // 모델에 다녀왔다 가정. key-value 전달.
		
		return modelAndView;
	}
    
	@RequestMapping(value="/usa.*", method=RequestMethod.POST) // POST만 받을 수 있다.
	//@RequestMapping(value="/usa.*", method=RequestMethod.GET) // GET만 받을 수 있다.
	//@RequestMapping({"/usa.*"})
	public ModelAndView handleRequest3(HttpServletRequest request, HttpServletResponse response)
					throws Exception {
		
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("view2"); // veiw1.jsp에 연결 
		modelAndView.addObject("message", "hi world usa"); // 모델에 다녀왔다 가정. key-value 전달.
		
		return modelAndView;
	}
}

 => @RequestMapping({요청명1, 요청명2,..}) : 배열을 사용하여 여러개의 요청명을 받을 수 있다.

      @RequestMapping(value="요청명", method=RequestMethod.GET) : 요청과 method 방식을 설정할 수 있다.

      요청명에 * 를 사용하여 복수의 요청을 받을 수 있다.

 

 

 - view1.jsp / view2.jsp

<body>
	hello 결과 : ${requestScope.message}
</body>

 => Controller에서 addObject()메소드로 전달한 value를 key를 사용하여 받을 수 있다. (EL 사용 - ${key})

 

 


2) 공유 객체 사용

 = spr4_share

 - index.html

<body>
	클래스 간 자원 공유<br>
	<a href="hello.do">요청 1</a><br>
	<a href="world.kor">요청2</a><br>
</body>

 

 

 - web.xml

<!-- WebApplicationContext보다 먼저 수행 : 공유 자원 설정시 필요 -->
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 => applicationContext.xml에 공유자원을 설정한다.

<!-- WebApplicationContext에 의해 자동 수행 -->
<servlet>
	<servlet-name>hello</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- <load-on-startup> init() 메소드 호출 순서 -->
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>hello</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

<servlet>
	<servlet-name>world</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>world</servlet-name>
	<url-pattern>*.kor</url-pattern>
	<!-- <url-pattern>/</url-pattern> 모든 요청사항 -->
</servlet-mapping>

 => *.do 요청과 hello-servlet.xml 연결

 => *.kor요청과 world-servlet.xml 연결

 

 

- applicationContext.xml

<!-- 공유 자원 객체 생성 -->
<bean id="sharedData" class="pack.SharedData">
	<property name = "shared" value="스프링 프로젝트 공유자원입니다."/>
</bean>

 => setter injection을 이용하여 공유자원 객체 shard에 value를 설정한다.

 

 

 - hello-servlet.xml, world-servlet.xml

<!-- 2. Handler Mapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

<!-- 3. Controller -->  
<bean name="/hello.do" class="pack.HelloController">
	<property name="data" ref="sharedData"/>
</bean>

<!-- 5. ViewResover -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>

 => hello.do과 HelloController 연결. HelloController에 공유객체를 setter injection한다.

 

 

 - SharedData

public class SharedData {
	private String shared;
	public void setShared(String shared) {
		this.shared = shared;
	}
	public String getShared() {
		return shared;
	}
}

 => getter, setter를 이용하여 공유객체를 사용한다.

 

 

 - HelloController

public class HelloController implements Controller{
	private SharedData data; // 공유객체 선언.
	// setter injection에 사용
	public void setData(SharedData data) {
		this.data = data;
	}
	
	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
					throws Exception {
		// 현재 클래스가 공유자원을 사용
		ModelAndView modelAndView = new ModelAndView("helloworld"); // ViewFile명
		modelAndView.addObject("msg", data.getShared() + "hello");
	
		return modelAndView;
	}
}

 => ModelAndView("ViewFile명") : ModelAndView 생성자에 매개변수로 ViewFile명을 전달하여 ViewFile에 연결.  (setViewName("ViewFile명")과 동일)
 => addObject()메소드 사용하여 공유 객체의 내용 전달.

 

 

 - helloworld.jsp

<body>
	결과 : ${msg}
</body>

 


3) 패턴을 사용한 경로 매핑

 

 =spr5_arrange

 - index.html

<body>
	@MVC 기본정리<p/>
	<a href="hello.do">인사하기 1</a>
</body>

 => hello.do 요청. (get)

 

 

 - web.xml

<!-- WebApplicationContext에 의해 자동 수행 -->
<servlet>
	<servlet-name>test</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- <load-on-startup> init() 메소드 호출 순서 -->
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>test</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

 => test-servlet.xml과 *.do를 url mapping한다.

 => <servlet-mapping>이 여러개 일 경우 <load-on-startup>로 init() 메소드 호출 순서를 지정할 수 있다.

 

 

 - test-servlet.xml

 * mapping 방법1 : 요청 url과 동일한 이름을 bean과 매핑(basic)

<!-- 2. Handler Mapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

<!-- 3. Controller -->
<bean name="/hello.do" class="pack.controller.HelloController">
	<property name="helloModel" ref="helloModel"></property>
</bean>

<bean id="helloModel" class="pack.model.HelloModel"></bean>

<!-- 5. ViewResover -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>

=> HelloController에 helloModel를 setter injection하여 객체 생성.

=>  hello.do 요청과 HelloController을 mapping

 

* mapping 방법2 : 경로 매핑(패턴 : *, ?)

<!-- 2. Handler Mapping-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<property name="alwaysUseFullPath" value="true"/>
	<property name="mappings">
		<props>
			<!-- <prop key="*.do">hi</prop> -->
			<!-- <prop key="h*.do">hi</prop> -->
			<!--<prop key="/**/*.do">hi</prop> -->
			<prop key="/**/?????.do">hi</prop>
		</props>
	</property>
</bean>

<!-- 3. Controller -->
<bean name="hi" class="pack.controller.HelloController">
	<property name="helloModel" ref="helloModel"></property>
</bean>

<bean id="helloModel" class="pack.model.HelloModel"></bean>

<!-- 5. ViewResover -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>

 =>SimSimpleUrlHandlerMapping사용하여 <prop>에 경로 매핑 가능하다.

 => ?는 글자 갯수. * 복수개의 문자를 나타냄.

 => HelloController mapping의 name을 <prop>의 값으로 받아 패턴을 연결한다.

 

 

 - HelloController

public class HelloController implements Controller{
	
	private HelloModel helloModel;
	
	//setter injection
	public void setHelloModel(HelloModel helloModel) {
		this.helloModel = helloModel;
	}
	
	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
				throws Exception {
		// 모델 만들고 모델과 통신하기
		String result = helloModel.getGreeting();
        
		ModelAndView modelAndView = new ModelAndView();
		//ModelAndView modelAndView = new ModelAndView("hello"); // ViewFile명
		
		modelAndView.setViewName("hello");
		modelAndView.addObject("result",result); // forword 방식(default)
		
		//modelAndView.setViewName("redirect:/views/hello.jsp?result='d'"); // reditect 방식
	
		return modelAndView;
	}
}

 => Controller를 상속받고 handleRequest 오버라이딩하여 요청사항을 처리한다.
 => 기본 방식을 forward로 하여 /views/hello.jsp에 addObject(key, value)로 값을 전달한다.
 => setViewName("redirect:/views/hello.jsp")으로 redirect 가능하다.

 

 - HelloModel

public class HelloModel {
	public String getGreeting() {
		int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
		if(hour >= 6 & hour <= 10) {
			return "좋은 아침입니다.";
		}else if(hour >= 12 & hour <= 15) {
			return "점심은 맛있게 드셨나요";
		}else if(hour >= 18 & hour <= 22) {
			return "좋은 저녁되세요";
		}else {
			return "안녕하세요";
		}
	}
}

 => 기능을 담당하는 model 클래스

 

 

 - hello.jsp

<body>
 인사말 : ${result}
</body>

 


4) @annotation을 이용한 Controller호출

 

 = sprwrb6_annotation

 - index.html

<body>
	@MVC 기본정리<p/>
	<a href="hello.do">인사하기 1 - annotation</a><br>
	<a href="abc/world.do">인사하기 2 - annotation</a><br>
	<a href="kbs.do">인사하기 3 - annotation</a><br>
	<a href="hello">인사하기 4 - annotation</a><br>
	<br>
	<a href="kakao.do">인사하기 5 - annotation</a><br>
	<a href="mbc">인사하기 6 - annotation</a><br>
	<a href="sbs">인사하기 7 - annotation</a><br>
</body>

 => 요청 url 설정.

 

 - web.xml

<servlet>
	<servlet-name>test</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>test</servlet-name>
    <!--  <url-pattern>*.do</url-pattern>  확장자가 do인 요청 -->
  	<!-- <url-pattern>/aa.jsp</url-pattern> 단일 파일일 경우 /를 붙인다.-->
	<url-pattern>/</url-pattern><!-- 모든요청 -->
</servlet-mapping>

 => <url-pattern>/</url-pattern> 모든요청.
 => test-servlet 실행.

 

 

 - test-servlet.xml

* web.xml의 <url-pattern>/</url-pattern>를 적용하려면 다음 코드를 test-servlet.xml에 추가한다.

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
	<property name="alwaysMustRevalidate" value="true"/>
</bean>

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
	<property name="alwaysUseFullPath" value="true"/>
</bean>

<mvc:annotation-driven/>
<mvc:default-servlet-handler/>

 => 모든 요청 경로가 연결된다.

 => <mvc:annotation-driven/> : @annotation기반의 Controller호출

 => <mvc:default-servlet-handler/> : 처리하지 못한 요청을 DefaultSerlvet에게 넘겨준다.

<!-- annotation인 경우 -->
<context:component-scan base-package="pack.controller"/>
<context:component-scan base-package="pack.model"/>

<!-- 5. ViewResover -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>

 => <context:component-scan> : 어노테이션 사용시 사용.

 


- HelloController

@Controller // controller implements
@RequestMapping({ "hello.do", "abc/world.do", "k*", "mbc", "sbs", "h*" }) // url mapping
public class HelloController {
	
	@Autowired // setter injection
	private HelloModel helloModel;
	
	@RequestMapping(method=RequestMethod.GET) // GET방식과 mapping 
	public ModelAndView abc() { // 모델 만들고 모델과 통신하기
		String result = helloModel.getGreeting();
		ModelAndView modelAndView = new ModelAndView();
		 
		modelAndView.setViewName("hello");
		modelAndView.addObject("result",result); // forword 방식(default)
		  
		//modelAndView.setViewName("redirect:/views/hello.jsp?result='d'"); // reditect 방식
		 
		return modelAndView; 
	}
}

 => @Controller : 어노테이션을 사용하여 Controller를 호출한다.

 => ModelAndView객체를 리턴값으로 값을 전달한다. result key에 result value를 전달한다.

 

@Controller // controller implements
@RequestMapping({ "hello.do", "abc/world.do", "k*", "mbc", "sbs", "h*" }) // url mapping
public class HelloController {
	
	@Autowired // setter injection
	private HelloModel helloModel;
    
	// Map을 이용할 경우 출력할 View의 ViewName을 명시하지않는다.
	// 요청명을 그대로 ViewName으로 가져간다.
	// 요청명 hello, view명 hello.jsp
	@RequestMapping(method = RequestMethod.GET) // GET방식과 mapping
	public Map<String, Object> abc() {
		String result = helloModel.getGreeting();
		
		HashMap<String, Object> map = new HashMap<String, Object>();
		
		map.put("result", result); // 요청명이 view 파일명이 된다.
		
		return map;
	}
}

 => Map을 이용할 경우 출력할 View의 ViewName을 명시하지않는다.
      요청명을 그대로 ViewName으로 가져간다. (spec에 약속된 사항)
      요청명 hello, view명 hello

=> HashMap를 이용하여 key, value를 전달.

@Controller // controller implements
@RequestMapping({ "hello.do", "abc/world.do", "k*", "mbc", "sbs", "h*" }) // url mapping
public class HelloController {
	
	@Autowired // setter injection
	private HelloModel helloModel;
    
	@RequestMapping(method = RequestMethod.GET) // GET방식과 mapping
	public Model abc(Model model) {
		String result = helloModel.getGreeting();
		
		model.addAttribute("result",result); // 요청명이 view 파일명이 된다.
		
		return model;
	}
}

 => Model 객체를 이용하여 addAttribute()메소드를 이용하여 값을 전달한다.

      요청명을 그대로 ViewName으로 가져간다. (spec에 약속된 사항)

@Controller // controller implements
@RequestMapping({ "hello.do", "abc/world.do", "k*", "mbc", "sbs", "h*" }) // url mapping
public class HelloController {
	@Autowired // setter injection
	private HelloModel helloModel;
	@RequestMapping(method = RequestMethod.GET) // GET방식과 mapping
	public String abc() {
		String result = helloModel.getGreeting();
		
		return "hello";
	}
}

 => String 리턴값을 가져간다.

      요청명을 그대로 ViewName으로 가져간다. (spec에 약속된 사항)

 

 

 - HelloModel

@Component
public class HelloModel {
	public String getGreeting() {
		int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
		if(hour >= 6 & hour <= 10) {
			return "좋은 아침입니다.";
		}else if(hour >= 12 & hour <= 15) {
			return "점심은 맛있게 드셨나요";
		}else if(hour >= 18 & hour <= 22) {
			return "좋은 저녁되세요";
		}else {
			return "안녕하세요";
		}
	}
}

 

 - hello.jsp

<body>
 - annotation<br>
 인사말 : ${result}
</body>

 

 


5) Spring MVC Project를 사용

 

[New] - [Spring Legacy Project] - project명 입력 - [Spring MVC Project] 선택 - [Next] - aa.bb.cc입력 - [finish]

 

 - pom.xml

<properties>
	<java-version>1.8</java-version>
	<org.springframework-version>5.0.0.RELEASE</org.springframework-version>
	<org.aspectj-version>1.6.10</org.aspectj-version>
	<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>

 => 변경

 

 


6) @RequestParam 사용(get)

 = sprweb8_parameter

 - login.jsp

<form action="login" method="get">
	id : <input type="text" name="id"/><br>
	password : <input type="text" name="pwd"/><br>
	<input type="submit" value="전송(get)">
</form>

 => login url로 name이 id, pwd인 값 전달.(get)

 

 

 - LoginController

* Controller 값 처리(기존) : getParameter()

@Controller
public class LoginController {
	//@RequestMapping("login")
	@RequestMapping(value="login", method=RequestMethod.GET)
	public ModelAndView submit(HttpServletRequest request) {
		String id = request.getParameter("id");
		String pwd = request.getParameter("pwd");
		
		System.out.println("id:"+id);
		System.out.println("pwd:"+pwd);
		
		String data = "";
		if(id.equals("aa") && pwd.equals("11")) {
			data = "로그인 성공";
		}else {
			data = "로그인 실패";
		}
			
		ModelAndView view = new ModelAndView();
		view.setViewName("result");
		view.addObject("data", data);
		
		return view;
	}
}

 => getParameter() 메소드를 사용하여 값을 가져온다.

 => 로직 동작 후 seyViewName()로 result.jsp에 addObject()메소드로 data값을 전달한다.

 

* Controller 값 처리(어노테이션 사용)

@Controller
@RequestMapping("login") // type level mapping
public class LoginController {
	
	@RequestMapping(method=RequestMethod.GET) // method level mapping
	public ModelAndView submit(
			@RequestParam("id") String id,
			@RequestParam("pwd") String pwd) {
		
		String data = "";
		if(id.equals("aa") && pwd.equals("11")) {
			data = "로그인 성공";
		}else {
			data = "로그인 실패";
		}
			
		ModelAndView view = new ModelAndView();
		view.setViewName("result");
		view.addObject("data", data);
		
		return view;
	}
}

 => @RequestParam("name")을 사용하여 getParameter()의 기능을 수행한다.

 

 

 - result.jsp

<body>
	결과 : ${data}
</body>

 


* get 방식은 한글이 안깨지나 post 방식사용시 한글이 깨진다. Encording을 하여 한글 깨짐을 방지한다.

 

 - web.xml

<!-- 한글 깨짐 방지(post) -->
<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>

 => web.xml에 위의 코드를 copy&paste한다.

 


7) FormBean, @ModelAttribute 사용(post)

 

 

 - input.jsp

<h2>* 자료입력</h2>
<form action="sangpum" method="post">
	품명 : <input type="text" name="sang"><br>
	수량 : <input type="text" name="su"><br>
	단가 : <input type="text" name="dan"><br>
	<input type="submit" valus="전송(post)"/>
</form>

 

 - SangpumController

* Controller 값 처리(기존) : getParameter()

@Controller
public class SangpumController {
	@RequestMapping(value="sangpum", method=RequestMethod.POST)
	public ModelAndView submit(HttpServletRequest request) {
		String sang = request.getParameter("sang");
		String su = request.getParameter("su");
		String dan = request.getParameter("dan");
		System.out.println(sang+" "+" "+su+" "+dan);
		
		ModelAndView view = new ModelAndView();
		view.setViewName("result");
		view.addObject("data","good");
		
		return view;
	}
}

* Controller 값 처리(FormBean 사용)

@Controller
public class SangpumController {
	@RequestMapping(value="sangpum", method=RequestMethod.POST)
	public ModelAndView submit(SangpumBean bean) { // 지역 변수
		//클라이언트로 부터 들어오는 데이터 양이 많을 경우 사용(form bean)
		
		System.out.println(bean.getSang()+" "+" "+bean.getSu()+" "+bean.getDan());
		
		ModelAndView view = new ModelAndView();
		view.setViewName("result");
		view.addObject("data","good");
		
		return view;
	}
}

 => 매개변수로 FormBean을 사용하여 값을 전달한다. bean의 범위는 {}안으로 지역변수로 사용된다.

 

* Controller 값 처리(@ModelAttribute 사용)

@Controller
public class SangpumController {
	@Autowired // setter injection
	private SangpumModel sanpumModel;
	
	@RequestMapping(value="sangpum", method=RequestMethod.POST)
	//public ModelAndView submit(SangpumBean bean) { // 지역 변수
	public ModelAndView submit(@ModelAttribute("bean") SangpumBean bean) { // 전역변수
		//클라이언트로 부터 들어오는 데이터 양이 많을 경우 사용(form bean)
		
		System.out.println(bean.getSang()+" "+" "+bean.getSu()+" "+bean.getDan());
		
		ModelAndView view = new ModelAndView();
		view.setViewName("result");
		view.addObject("data",sanpumModel.computeSangpum(bean));
		
		return view;
	}
}

 => 매개변수로 @ModelAttribute을 사용시 bean의 범위는 전역변수로 사용되어 addObject() 메소드를 사용하지않아도

      다른 파일에서 접근이 가능하다.

 

 

 - SangpumBean

@Component
public class SangpumBean { // form bean
	private String sang; // 클라이언트로 부터 전달되어 오는 요청 Parameter값과 변수명을 동일하게 가져간다.
	private int su, dan;
	
	public String getSang() {
		return sang;
	}
	public void setSang(String sang) {
		this.sang = sang;
	}
	public int getSu() {
		return su;
	}
	public void setSu(int su) {
		this.su = su;
	}
	public int getDan() {
		return dan;
	}
	public void setDan(int dan) {
		this.dan = dan;
	}
}

 => Form Bean 정의. Form Bean의 멤버변수는 <form>태그 하위 태그 name의 값과 동일하게 가져간다.

 

 - SangpumModel

@Component // 객체생성
public class SangpumModel { // 비지니스 로직 처리를 하는 모델 영역의 클래스
	public String computeSangpum(SangpumBean bean) {
		int price = bean.getSu() * bean.getDan();
		String data = "품명" + bean.getSang() + " , 금액은 "+price;
		
		return data;
	}
}

 

 

- result.jsp

<body>
post 결과 : ${data}
<br>
${bean.sang}
</body>

 

[목차]

7. Spring MVC Pattern 
1) DispatcherServlet 
2) Servlet을 통한 forward(default) 
3) Annotation 사용

 


[내용]

7. Spring MVC Pattern

1) DispatcherServlet

①Dynamic Web Project 생성 - project 오른쪽 클릭 - Configure - Convert Maven Project - finish

 

② pom.xml에서 </build>아래 추가

<properties>
	<!-- Generic properties -->
	<java.version>14.0.2</java.version>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	<!-- Spring -->
	<spring-framework.version>5.0.0.RELEASE</spring-framework.version>
</properties>
<dependencies>
	<!-- Spring MVC -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-webmvc</artifactId>
		<version>${spring-framework.version}</version>
	</dependency>
	<!-- Other Web dependencies -->
	<dependency>
		<groupId>javax.servlet.jsp</groupId>
		<artifactId>javax.servlet.jsp-api</artifactId>
		<version>2.2.1</version>
	</dependency>
	<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>jstl</artifactId>
		<version>1.2</version>
	</dependency>
	<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>javax.servlet-api</artifactId>
		<version>3.1.0</version>
	</dependency>
	<!-- mariadb driver -->
	<dependency>
		<groupId>org.mariadb.jdbc</groupId>
		<artifactId>mariadb-java-client</artifactId>
		<version>2.6.2</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-jdbc</artifactId>
		<version>2.3.4.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-jdbc</artifactId>
		<version>5.0.0.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>commons-dbcp</groupId>
		<artifactId>commons-dbcp</artifactId>
		<version>1.4</version>
	</dependency>
	<!-- mybatis -->
	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.5.5</version>
	</dependency>
	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis-spring</artifactId>
		<version>2.0.5</version>
	</dependency>
</dependencies>

 

③ index.html

<a href="index.do">start</a>
<a href="good/nice.do">start2</a>

 => client request시 web.xml 실행

 

④ web.xml

<!-- DispatcherServlet : Spring의 중심이 되는 Servlet -->
<!-- DispatcherServlet는 test-servlet.xml의 설정을 불러온다. -->
<servlet>
	<servlet-name>test</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>test</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

 => DispatcherServlet가 test-servlet.xml 실행.

 

⑤ test-servlet.xml

Context - WEB_INF에 <servlet-name>의 네임-servlet의 Spring Bean Configuration을 생성.

<!-- 2. Handler Mapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

<!-- 3. Controller -->
<!-- BeanNameUrlHandlerMapping -->
<bean name="/index.do" class="org.springframework.web.servlet.mvc.ParameterizableViewController">
	<property name="viewName" value="list1"/>
	<!-- list1.jsp 실행 -->
</bean>

<bean name="/good/nice.do" class="org.springframework.web.servlet.mvc.ParameterizableViewController">
	<property name="viewName" value="list2"/>
	<!-- list1.jsp 실행 -->
</bean>

<!-- 5. ViewResover -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>

 => context/views/list1.jsp, context/views/list2.jsp 연결

 


2) Servlet을 통한 forward(default)

 - test-servlet.xml

<!-- 2. Handler Mapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

<!-- 3. Controller -->
<bean name="/start.do" class="pack.StartController"></bean>

<!-- 5. ViewResover -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>

 

 - StartController : Controller 상속

public class StartController implements Controller{

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// ModelAndView : DB 처리 결과를 기억, 출력을 위한 view 파일명 기억
		System.out.println("StartController handleRequest()수행");
		
		//return new ModelAndView("list"); // 생성자
		
		//모델과 통신하기 생략
		
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("list"); // setter. forward 방식으로 파일 요청 처리 (default)
		//request.setAttribute("key", value); // servlet에서 사용하는 방법.
		modelAndView.addObject("say", "messages");//모델과 통신 후 얻어진 결과를 기억했다 가정.(request)
		
		return modelAndView;
	}
}

 

 - list.jsp

기존
<% 
	String str = (String)request.getAttribute("say"); 
	out.println(str);%>

EL
${say} ${requestScope.say}

 


3) Annotation 사용

 - test-servlet.xml

<!-- 2. Handler Mapping -->
<!-- annotation 사용시 삭제 -->
<!-- <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> -->


<!-- 3. Controller -->
<!-- annotation 사용시 삭제 -->
<!-- <bean name="/start.do" class="pack.StartController"></bean> -->
<!-- annotation 사용시 추가 -->
<context:component-scan base-package="pack"/>

<!-- 5. ViewResover -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>

 

 - StartController

@Controller // Conponent와 동일. Cotroller에 사용.
public class StartController{
	@RequestMapping("start.do")
	public ModelAndView handleStart(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// ModelAndView : DB 처리 결과를 기억, 출력을 위한 view 파일명 기억
		System.out.println("StartController handleStart()수행 - annotaion");
		
		//return new ModelAndView("list"); // 생성자
		
		//모델과 통신하기 생략
		
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("list"); // setter. forward 방식으로 파일 요청 처리 (default)
		//request.setAttribute("key", value); // servlet에서 사용하는 방법.
		modelAndView.addObject("say", "messages - annotaion");//모델과 통신 후 얻어진 결과를 기억했다 가정.(request)
		
		return modelAndView;
	}
}

 

 

 

[목차]

6. Spring에서 DB 연동 

* Maria DB lib 등록 

① JAVA의 JDBC를 사용  

② DaoSupport 클래스 사용  - spring이 지원 

③ MyBatis 사용

 


 

[내용]

 - Spring api 문서

docs.spring.io/spring/docs/5.0.x/javadoc-api/

* Maria DB lib 등록

① search.maven.org/접속

② Search창에 mariadb-java-client 검색 
③ 버전선택 (2.6.2) 
④ Apache Maven 내용 pom.xml에 copy & paste

<!-- MariaDB driver -->
<dependency>
  <groupId>org.mariadb.jdbc</groupId>
  <artifactId>mariadb-java-client</artifactId>
  <version>2.6.2</version>
</dependency>

⑤ Search창에 spring-boot-starter-jdbc 검색 
⑥ 버전선택 (2.3.6.RELEASE) 
⑦ Apache Maven 내용 pom.xml에 copy & paste

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
  <version>2.3.6.RELEASE</version>
</dependency>

 


① JAVA의 JDBC를 사용 

 

 - SangpumDto

public class SangpumDto {
	
	private String code, sang, su, dan;
	//getter 추가
	//setter 추가
}


 - SangpumInter

public interface SangpumInter {
	ArrayList<SangpumDto> selectList();
}

 

 - SangpumImpl : DB 처리

@Repository // @Componet와 동일. db와 연동할 코드
public class SangpumImpl implements SangpumInter{
	
	private Connection conn;
	private PreparedStatement ptmt;
	private ResultSet rs;
	
	public SangpumImpl() {
		try {
			Class.forName("org.mariadb.jdbc.Driver");
		} catch (Exception e) {
			System.out.println("SangpumImpl err"+e);
		}
	}
	
	public ArrayList<SangpumDto> selectList() {
		ArrayList<SangpumDto> list = new ArrayList<SangpumDto>();
		String sql="select * from sangdata";
		try {
			conn =DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123");
			ptmt = conn.prepareStatement(sql);
			rs = ptmt.executeQuery();
			while(rs.next()) {
				SangpumDto dto = new SangpumDto();
				dto.setCode(rs.getString("code"));
				dto.setSang(rs.getString("sang"));
				dto.setSu(rs.getString("su"));
				dto.setDan(rs.getString("dan"));
				list.add(dto);
			}
		} catch (Exception e) {
			System.out.println("selectList err"+e);
		}finally {
			try {
				if(conn != null) conn.close();
				if(ptmt != null) ptmt.close();
				if(rs != null) rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return list;
	}

}

    @Repository : @Componet와 동일. db와 연동할 코드

 

 - BusinessInter

public interface BusinessInter {
	public void dataList();
}

 

 - BusinessImpl

@Service // business logic에 사용
public class BusinessImpl implements BusinessInter{
	@Autowired // setter 주입 사용
	private SangpumInter sangpumInter;
	
	/* 생성자 주입 사용가능
	 * @Autowired public BusinessImpl(SangpumInter sangpumInter) {
	 * this.sangpumInter=sangpumInter; }
	 */
	
	public void dataList() {
		ArrayList<SangpumDto> list = sangpumInter.selectList();
		for(SangpumDto s:list) {
			System.out.println(s.getCode()+" "+
		s.getSang()+" "+
		s.getSu()+" "+
		s.getDan());
		}
	}
}

 

 - main

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("dbinit.xml");
	BusinessInter businessInter = (BusinessInter)context.getBean("businessImpl");
	businessInter.dataList();
}

 

 - dbinit.xml

<!-- 어노테이션 사용하도록 설정 -->
<context:component-scan base-package="pack.business"/>
<context:component-scan base-package="pack.model"/>

 

 


② DaoSupport 클래스 사용

 - SangpumDto 
 - BusinessImpl 
 - main 
 동일

 

 - SangpumInter

public interface SangpumInter {
	List<SangpumDto> selectList() throws DataAccessException;
}

 

 - SangpumImpl

// Spring이 제공하는 DB 연결처리. JdbcDaoSupport 상속.
public class SangpumImpl extends JdbcDaoSupport implements SangpumInter {

//	생성자 주입으로 처리 가능.
//	public SangpumImpl(DataSource dataSource) {
//		super.setDataSource(dataSource);
//	}

	public List<SangpumDto> selectList(/* String code */) throws DataAccessException {
		// member : Datasource, JdbcTemplate
		RowMapper rowMapper = new SangRowMapper();
		
		return getJdbcTemplate().query("select * from sangdata", rowMapper);
		// record수 만큼 mapRow() 호출.
		//return getJdbcTemplate().query("select * from sangdata where code=code", rowMapper); 
		// 매개변수 사용시
	}
	
	// 내부 클래스
	class SangRowMapper implements RowMapper{ // RowMapper 상속
		public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
			// mapRow는 레코드 수 만큼 호출. rs.next()가 내부적으로 처리됨.
			System.out.println("rowNum" + rowNum);
			SangpumDto dto = new SangpumDto();
			dto.setCode(rs.getString("code"));
			dto.setSang(rs.getString("sang"));
			dto.setSu(rs.getString("su"));
			dto.setDan(rs.getString("dan"));
			return dto;
		}
	}
}

 

 - BusinessInter

public class BusinessImpl implements BusinessInter{
	private SangpumInter sangpumInter;
	
	public void setSangpumInter(SangpumInter sangpumInter) {
		this.sangpumInter = sangpumInter;
	}
	
	public void dataList() {
		ArrayList<SangpumDto> list = (ArrayList<SangpumDto>)sangpumInter.selectList();
		for(SangpumDto s:list) {
			System.out.println(s.getCode()+" "+
		s.getSang()+" "+
		s.getSu()+" "+
		s.getDan());
		}
	}
}

 

 - dbinit.xml

<!-- db연결 -->
<!-- xml파일이 아니 별도 class로 작성 가능. -->
<bean id="dataSorce" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName">
		<value>org.mariadb.jdbc.Driver</value>
	</property>
	<property name="url">
		<value>jdbc:mysql://localhost:3306/test</value>
	</property>
	<property name="username">
		<value>root</value>
	</property>
	<property name="password">
		<value>123</value>
	</property>
</bean>
<!-- db연결 클래스 객체 생성 -->
<bean id="sangpumImpl" class="pack.model.SangpumImpl">
<!--JdbcDaoSupport을 상속받았기 때문에 별도로 dataSource setter를 정의하지않아도 setter주입을 할 수 있다.-->
	<property name="dataSource" ref="dataSorce"/>
</bean>
<!-- 객체생성 -->
<bean id="businessImpl" class="pack.business.BusinessImpl">
	<property name="sangpumInter" ref="sangpumImpl"/>
</bean>

 

 - dbinit.xml

<!-- DriverManagerDataSource를 상속받은 db 연결 class 객체 생성. -->
<bean id="dataSource" class="pack.model.DataSourceMaria"></bean>

<!-- db연결 클래스 객체 생성 -->
<bean id="sangpumImpl" class="pack.model.SangpumImpl">
<!-- JdbcDaoSupport을 상속받았기 때문에 별도로 dataSource setter를 정의하지않아도 setter주입을 할 수 있다. -->
	<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 객체생성 -->
<bean id="businessImpl" class="pack.business.BusinessImpl">
	<property name="sangpumInter" ref="sangpumImpl"/>
</bean>

 


③ MyBatis 사용

 

 - db.properties

 

 - DataMapper.xml

<mapper namespace="dev">
 <select id="selectAll" resultType="dto">
  select * from sangdata
 </select>
</mapper>

 

 - Configuration.xml

<configuration>
 <properties resource="pack/mybatis/db.properties" />
 <typeAliases>
 	<typeAlias type="pack.model.SangpumDto" alias="dto"/>
 </typeAliases>
 <environments default="dev">
  <environment id="dev">
   <transactionManager type="JDBC" />
   <dataSource type="POOLED">
    <property name="driver" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
   </dataSource>
  </environment>
 </environments>
 <mappers>
  <mapper resource="pack/mybatis/DataMapper.xml" />
 </mappers>
</configuration>

 

 - SqlMapConfig

public class SqlMapConfig { // 메뉴얼 확인하여 작성.
	public static SqlSessionFactory sqlSession; // DB의 SQL 명령 실행을 위한 메소드를 가진 객체.
	
	static {
		String resource = "pack/mybatis/Configuration.xml";
		try {
			Reader reader = Resources.getResourceAsReader(resource);
			// Configuration.xml을 읽는다.
			sqlSession = new SqlSessionFactoryBuilder().build(reader);
			// sqlSession 객체를 생성한다.
			reader.close();
		} catch (Exception e) {
			System.out.println("SqlMapConfig err"+e);
		}
	}
	public static SqlSessionFactory getSqlSession() {
		return sqlSession;
	}
}

 

 - SangpumInter

public interface SangpumInter {
	List<SangpumDto> selectDataAll();
}

 

 - SangpumImpl

@Repository
public class SangpumImpl implements SangpumInter{
	
	private SqlSessionFactory factory = SqlMapConfig.getSqlSession();
	
	public List<SangpumDto> selectDataAll() {
		SqlSession sqlSession = factory.openSession();
		List<SangpumDto> list = null;
		
		try {
			list=sqlSession.selectList("selectAll");
		} catch (Exception e) {
			System.out.println("selectDataAll err"+e);
		}finally {
			if(sqlSession!=null) sqlSession.close();
		}
		return list;
	}
}

 

 - BusinessInter

public interface BusinessInter {
	void dataList();
}

 

 - BusinessImpl

@Service
public class BusinessImpl implements BusinessInter{
	
	@Autowired
	private SangpumInter inter;
	
	public void dataList() {
		List<SangpumDto> list =inter.selectDataAll();
		
		for(SangpumDto s: list) {
			System.out.println(s.getCode()+" "+s.getSang()+" "+s.getSu()+" "+s.getDan());
		}
	}
}

 

 - main

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("dbinit.xml");
	BusinessInter businessInter = (BusinessInter)context.getBean("businessImpl");
	businessInter.dataList();
}

 

 - dbinit

<context:component-scan base-package="pack.business"/>
<context:component-scan base-package="pack.model"/>

 

 

 

[목차]

5. AOP (Aspect Oriented Program)

* 스프링 AOP에서 용어
① AOP 구현

 


 

[내용]

5. AOP (Aspect Oriented Program)

 

: 핵심 로직에서 임의의 동작을 진행할 경우 사용. 
: 별도의 클래스로 정의하여 메소드를 탈부착식으로 사용한다.

: ex) 시큐리티, 로그인, 트랜잭션

 

 1) 설정

 ⓛ 사이트 접속 - search.maven.org/

 ② search창에 aspectjweaver 검색

 ③ version 선택

 ④ Apache Maven 내용 copy & paste

 ⑤ search창에 cglib 검색

 ⑥ version 선택

 ⑦ Apache Maven 내용 copy & paste

 ⑧ pom.xml에 Code 추가

<!-- aop -->
<dependencies>
	<dependency>
		<groupId>aspectj</groupId>
		<artifactId>aspectjweaver</artifactId>
		<version>1.5.4</version>
	</dependency>
	<dependency>
		<groupId>cglib</groupId>
		<artifactId>cglib</artifactId>
		<version>3.3.0</version>
	</dependency>
</dependencies>

 ⑨ 경로에 생성됨. C:\Users\wonh\.m2\repository\

 

2) code

- HelloInter

public interface HelloInter {
	void hello();
	void hi();	
}

 - HelloInterImpl

public class HelloInterImpl implements HelloInter{
	
	public HelloInterImpl() {
		System.out.println("HelloInterImpl 생성자");
	}
	
	public void hello() {
		System.out.println("hello 처리");
	}

	public void hi() {
		System.out.println("hi 처리");
	}
}

 - WriteInter

public interface WriteInter {
	void write();
}

 - WriteInterImpl

public class WriteInterImpl implements WriteInter{ // 핵심 로직을 가진 클래스
	
	public WriteInterImpl() {
		System.out.println("WriteInterImpl 생성자");
	}

	public void write() {
		System.out.println("write 수행");
	}
}

 - MyAspect

public class MyAspect { // 관심사항을 가진 클래스
	public void myLogOn() {
		System.out.println("핵심 메소드 수행전 로그온 작업");
	}
	
	public void myLogOut() {
		System.out.println("핵심 메소드 수행후 로그아웃 작업");
	}
	
	public void mySecurity() {
		System.out.println("핵심 메소드 수행전 보안설정");
	}
}

 - AspectProcess

public class AspectProcess { // 여러개의 관심사항들을 모아 처리하는 클래스
	private MyAspect myAspect; // 관심사항 클래스. 여러개 가능
	
	public void setMyAspect(MyAspect myAspect) {
		this.myAspect = myAspect;
	}
	
	public Object logging(ProceedingJoinPoint joinPoint) throws Throwable{
		Object object = null;
		
		myAspect.myLogOn();
		object = joinPoint.proceed(); // 메타파일에서 설정한 핵심 메소드 수행.
		myAspect.myLogOut();
		
		return object;
	}
	
	public Object logging2(ProceedingJoinPoint joinPoint) throws Throwable{
		Object object = null;
		
		myAspect.mySecurity();
		object = joinPoint.proceed(); // 메타파일에서 설정한 핵심 메소드 수행.
		return object;
	}
}

 => logging() 메소드는 myLogOn() -> 핵심로직 -> myLogOut() 순 호출

 => logging2() 메소드는 mySecurity() -> 핵심로직 순 호출

 - main

public static void main(String[] args) {
	//String[] configs = new String[] {"aop1core.xml"}; // 핵심만 수행
	String[] configs = new String[] {"aop1core.xml", "aop1.xml"}; // 핵심 + Aop 수행.
	
	ApplicationContext context = new ClassPathXmlApplicationContext(configs);
	
	WriteInter writeInter = (WriteInter)context.getBean("writeInterImpl");
	writeInter.write();
	System.out.println();
	
	HelloInterImpl helloInter = (HelloInterImpl)context.getBean("helloInterImpl"); // 방법 1
	HelloInter helloInter = context.getBean("helloInterImpl", HelloInter.class); // 방법 2
	helloInter.hi();
	helloInter.hello();
}

 - aop1core.xml

<bean id="writeInterImpl" class="aop1.WriteInterImpl"/>
<bean id="helloInterImpl" class="aop1.HelloInterImpl"/>

 - aop1.xml

<!-- Aspect(Advice) -->
<bean id="aspectProcess" class="aop1.AspectProcess">
	<property name="myAspect" ref="myAspect"/>
</bean>
<bean id="myAspect" class="aop1.MyAspect"/>

<!-- Aop 설정 -->
<aop:config>
	<aop:pointcut expression="execution(public void wri*(..))" id="poi1"/>
	// wri로 시작하고 매개변수가 0개 이상인 메소드
	// <aop:pointcut expression="execution(public void write())" id="poi1"/>
	<aop:aspect ref="aspectProcess">
		<aop:around method="logging" pointcut-ref="poi1"/>
	</aop:aspect>
</aop:config>
<aop:config>
	<aop:pointcut expression="execution(public void hel*(..)) or execution(public void hi*(..))" id="poi2"/>
	// or || and &&
	<aop:aspect ref="aspectProcess">
		<aop:around method="logging2" pointcut-ref="poi2"/>
	</aop:aspect>
</aop:config>

 => 핵심 로직의 wri로 시작하고 메개변수가 0개 이상인 메소드가 호출될때 AspectProcess(Aspect를 처리하는) 클래스의 logging() 메소드가 실행된다.

 => 핵심 로직의 hel로 시작하고 메개변수가 0개 이상인 메소드 또는 hi로 시작하는 메소드가 호출될때 AspectProcess클래스의 logging2() 메소드가 실행된다.

 


 - ArticleInter

public interface ArticleInter {
	void select();
}

 

- ArticleDao

//@Component
//@Repository
@Repository("articleDao")

public class ArticleDao implements ArticleInter{

	public void select() {
		System.out.println("db의 상품 자료를 read");
	}
}

 - LogicInter

public interface LogicInter {
	void selectData_process();
}

 - LogicImpl

public class LogicImpl implements LogicInter{
	private ArticleInter articleInter; 
    
	public LogicImpl() {
	}
	
	public LogicImpl(ArticleInter articleInter) {
		this.articleInter = articleInter; 
	}
	
	public void selectData_process() {
		System.out.println("selectData_proces 수행");
		articleInter.select();
	}
}
@Service
public class LogicImpl implements LogicInter{

	@Autowired
	//@Qualifier("aticleDao")
	private ArticleInter articleInter; 
	
	public void selectData_process() {
		System.out.println("selectData_proces 수행");
		articleInter.select();
	}
}

 

 - ProfileAdvice

public class ProfileAdvice { // Advice, Aspect
	public Object abc(ProceedingJoinPoint joinPoint) throws Throwable{
		//핵심 메소드 이름 얻기
		String methodName = joinPoint.getSignature().toString();
		
		System.out.println(methodName+" 시작 전 작업");
		Object object = joinPoint.proceed(); // 핵심 메소드
		System.out.println(methodName+" 처리 후 작업");
		
		return object;
	}
}
@Aspect
@Component
public class ProfileAdvice { // Advice, Aspect
	@Around("execution(public void selectData_process())")
	public Object abc(ProceedingJoinPoint joinPoint) throws Throwable{
		//핵심 메소드 이름 얻기
		String methodName = joinPoint.getSignature().toString();
		
		System.out.println(methodName+" 시작 전 작업");
		Object object = joinPoint.proceed(); // 핵심 메소드
		System.out.println(methodName+" 처리 후 작업");
		
		return object;
	}
}

 

 - main

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("aop2.xml");
	LogicInter inter = (LogicInter)context.getBean("logicImpl");
	inter.selectData_process();
}

 - aop2.xml

<!-- 핵심 로직 처리용 클래스 -->
<bean id="logicImpl" class="aop2.LogicImpl">
	<constructor-arg>
		<ref bean="articleDao"/>
	</constructor-arg>
</bean>
<bean id="articleDao" class="aop2.ArticleDao"></bean>

<!-- Aop 설정 -->
<bean id="profileAdvice" class="aop2.ProfileAdvice"/>
<aop:config>
	<aop:aspect ref="profileAdvice">
		<aop:pointcut expression="execution(public void selectData_process())" id="poi"/>
		<aop:around method="abc" pointcut-ref="poi"/>
	</aop:aspect>
</aop:config>	

 => 핵심로직의 selectData_process()메소드 호출 시 profileAdvice클래스의 abc()메소드가 호출.

<!-- 객체 생성 : 어노테이션 -->
<context:component-scan base-package="aop3"/>
<!-- Aop 설정 -->
<aop:aspectj-autoproxy/>

 

 

 

* 스프링 AOP에서 용어


 - Joinpoint : ‘클래스의 인스턴스 생성 시점’, ‘메소드 호출 시점’ 및 ‘예외 발생 시점’ 과 같이 어플리케이션을 실행할 때 특정 작업이 시작되는 시점을 의미.

 - Advice : 조인포인트에 삽입되어 동작할 수 있는 코드를 말함.

 - Pointcut : 여러 개의 조인포인트를 하나로 결합한(묶은) 것을 말함.

 - Advisor : 어드바이스와 포인트컷을 하나로 묶어 취급한 것을 말함.

 - Weaving : 어드바이스를 핵심 로직 코드에 삽입하는 것을 말함.

 - Target : 핵심 로직을 구현하는 클레스.

 - Aspect : 여러 객체에 공통으로 적용되는 공통 관점 사항을 말함.

 

 

1) AOP 구현

 - LogicInter

public interface LogicInter {
	void startProcess();
}

 

 - LogicImpl

@Service // @Component와 동일하지만 가독성을 위해 Service기능을 사용하면 @Service를 사용.
public class LogicImpl implements LogicInter{
	
	public LogicImpl() {
	}
	
	public void startProcess() {
		System.out.println("핵심 로직 수행");
	}
}

 

 - aop4.xml

<!-- 어노테이션을 사용하여 객체 생성 -->
<context:component-scan base-package="aop4"/>
<!-- aop를 사용하도록 설정 -->
<aop:aspectj-autoproxy/>

    <context:component-scan base-package="패키지명"/>
    <aop:aspectj-autoproxy/>

 

 - main

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("aop4.xml");
	LogicInter inter = (LogicInter)context.getBean("logicImpl");
	// getBean의 매개변수 : @객체 생성할 클래스명을 첫글자 소문자로 변경하여 생성. 
	inter.startProcess();
}

 

 - RunAspect

// 아이디가 같으면 핵심로직 수행, 다르면 수행하지않도록 구현.
@Component
@Aspect // 독립적으로 동작할 관심 로직 클래스에 사용
public class RunAspect {
	
	@Around("execution(public void startProcess())")
	// 핵심로직의 메소드를 선택. 해당메소드 호출시 관심 메소드 호출.
	//@Around는 핵심로직 실행 전/후 모두 기능을 추가할 수 있다.
	public Object trace(ProceedingJoinPoint joinPoint) throws Throwable{
		
		System.out.println("AOP 시작 : 핵심 로직 처리전 id 검증");
		System.out.print("input id : ");
		Scanner scanner = new Scanner(System.in);
		String id = scanner.nextLine();
		if(! id.equalsIgnoreCase("aa")) { //equalsIgnoreCase() 대소문자 구분하지않고 비교.
			System.out.println("id 불일치하여 작업을 종료합니다.");
			return null;
		}
		
		Object obj = joinPoint.proceed(); // 핵심 로직 수행.
		
		return obj;
	}
}

    @Component

    @Aspect

    @Around("excution(핵심 로직의 메소드)")

    public Object 관심메소드명(ProceedingJoinPoint joinPoint) throws Throwable{ .. }

    Object obj = joinPoint.proceed();

    str1.equalsIgnoreCase(str2)

 

 

 

 

[목차]

2. 생성자 주입(Constructor Injection)

① meta.xml

② 생성자 호출 
③ MVC 패턴 및 다형성 활용한 프로그래밍

④ 생성자 주입(construnctor injection)
⑤생성자 주입방법 2

3. setter 주입(setter injection) 
4. new를 이용한 객체생성과 Spring 객체 생성의 차이 

 


[내용]

2. 생성자 주입(Constructor Injection)

public static void main(String[] args) {
	// new로 객체 생성시 매번 생성된다.
	
	MessageImpl m1 = new MessageImpl("김이상");
	m1.sayHi();
	
	MessageImpl m2 = new MessageImpl("김이상");
	m2.sayHi();
	
	System.out.println("주소 : "+m1);
	System.out.println("주소 : "+m2);
}

 => new를 이용한 객체 생성

 

① meta.xml

<beans>
	<bean id="moneyCalc" name="aa,bb,cc" class="pack.model.MoneyCalc"/>	
	<bean id="myProcess" class="pack.controller.MyProcess">
		<constructor-arg>
			<ref bean="moneyCalc"/>
		</constructor-arg>
	</bean>
</beans>

 => 매개변수가 있는 생성자일 경우 <contructor-arg>와 <ref> 태그를 사용하여 매개변수를 입력한다.

 => 매개변수도 생성자를 생성한다.

<beans>
	<bean id="moneyCalc" name="aa,bb,cc" class="pack.model.MoneyCalc"/>	
	<bean id="myProcess" class="pack.controller.MyProcess">
		<constructor-arg>
			<ref bean="aa"/>
		</constructor-arg>
	</bean>
</beans>

 => id나 name 둘중 무엇을 사용해도 상관없으며, <ref bean="name">에서 name을 호출가능.

 

 

② 생성자 호출

public class Main {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("money_init.xml");
		MyInter inter = (MyInter)context.getBean("myProcess");
		inter.inputMoney();
		inter.showResult();
	}
}

 => ApplicationContext 객체 생성시 xml에 연결된 모든 생성자가 호출되며, 각 객체가 생성된다.

 => context의 getBean()메소드로 객체를 get하여 메소드를 사용한다.

 => getBean() 메소드의 리턴값은 Object로 다형성을 사용하여 캐스팅하여 객체를 대입한다.

 => getBean() 리턴 객체도 다형성을 사용하여 interface 객체에 대입하여 사용하는 것을 지향한다.

 

 

③ MVC 패턴 및 다형성 활용한 프로그래밍

 

pakage Controller(Servlet) Model(Dto, Dao-sql)
Interface BusinessInterface ModelInterface
 
Class BusinessClass ModelClass

 => 다형성을 갖춘 MVC Pattern 작성을 지향하여 프로그래밍한다.

 => Controller의 Business Class에서 Model Interface를 호출하여 사용한다.

 => 각 모델은 다형성을 활용하여 테이블에 따른 Sql문을 호출하여 DB와 연결된다.

 => [src.main/java]에 작성한다.

 

- Business Interface

public interface MyInter {
	void inputMoney();
	void showResult();
}

 - Business Class

public class MyProcess implements MyInter{
	private MoneyInter inter;
	private int result[];
	
	public MyProcess(MoneyInter inter) {
		this.inter = inter;
	}
	
	public void inputMoney() {
		//키보드로 금액입력
		try {
			InputStreamReader reader = new InputStreamReader(System.in);
			BufferedReader br = new BufferedReader(reader);
			int myMoney=0;
			System.out.println("금액입력");
			String data = br.readLine();
			myMoney = Integer.parseInt(data);
			result = inter.calcMoney(myMoney);
		} catch (Exception e) {
			System.out.println("inputMoney err "+e);
		}
	}

	public void showResult() {
		//계산이 끝난 배열 자료 출력.
		System.out.println("만원 : "+result[0]+"개");
		System.out.println("천원 : "+result[1]+"개");
		System.out.println("백원 : "+result[2]+"개");
		System.out.println("십원 : "+result[3]+"개");
		System.out.println("일원 : "+result[4]+"개");
	}

}

 => 생성자에 모델의 interface 객체를 매개변수로 받아 사용.

 

- Model Interface

public interface MoneyInter {
	int[] calcMoney(int money);
}

 

- Model Class

public class MoneyCalc implements MoneyInter{
	public MoneyCalc() {
	}
	public int[] calcMoney(int money) {
		//단위별 금액 건수 계산용
		int result[] = new int[5];
		int temp=0;
		int len = result.length;
		for(int i=0; i<len;i++) {
			if(i == 0) {
				result[i] = money/(int)Math.pow(10, (len-1-i));
				temp=money%(int)Math.pow(10, (len-1-i));
				continue;
			}
			result[i] = temp/(int)Math.pow(10, (len-1-i));
			temp=temp%(int)Math.pow(10, (len-1-i));
		}
		return result;
	}
}

 

③ 생성자 주입(construnctor injection)

 

 - ShowData(Dao)

public class ShowData {
	public String toAlias() {
		return "홍길동";
	}
	
	public String toHobby() {
		return "운동";
	}
}

 - MyProcess(Controller)

public class MyProcess { // 포함으로 약결합 진행
	private int age;
	private String name;
	private ShowData showData; // Class Type
	
	public MyProcess(int age, String name, ShowData showData) {
		this.age = age;
		this.name = name;
		this.showData = showData;
	}
	
	public void printData() {
		System.out.println(name+age+showData.toAlias()+showData.toHobby());
	}
}

 - xml

<beans>
	<bean id="showData" class="pack.model.ShowData"/>	
	<bean id="myProcess" class="pack.controller.MyProcess">
		<constructor-arg>
			<value>13</value>
		</constructor-arg>
		<constructor-arg>
			<value>홍길동</value>
		</constructor-arg>
		<constructor-arg>
			<ref bean="showData"/>
		</constructor-arg>
	</bean>
</beans>

- main

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("init.xml");
	MyProcess myProcess = (MyProcess)context.getBean("myProcess");
	myProcess.printData();
}

④ 생성자 주입방법 2

 - MessageInter

public interface MessageInter {
	void sayHi();
}

 - MessageImpl

public class MessageImpl implements MessageInter{
	private String name1;
	private String name2;
	private int year;
	private MyClass myclass;
	private String spec;
	
	// 생성자 주입
	public MessageImpl(String name1, String name2, int year, MyClass myclass) {
		this.name1=name1;
		this.name2=name2;
		this.year=year;
		this.myclass = myclass;
	}
	
	//setter 주입
	public void setSpec(String spec) {
		this.spec = spec;
	}
	
	// 출력담당 메소드
	public void sayHi() {
		String msg="이름 : "+name1+" / "+name2+"\n";
		msg+="직업 : "+(year+21)+"년 " + myclass.myData();
		msg+="\n보유기술 : "+spec;
		System.out.println(msg);
	}
}

 - arrange.xml

 ① 생성자 주입방법 1

<bean id="mbean" class="pack.MessageImpl" scope="singleton"> 
	<constructor-arg value="홍길동">
		<ref bean="name1"/>
	</constructor-arg>
</bean>
<bean id="mbean" class="pack.MessageImpl" scope="singleton"> 
// <bean> scope의 default는 singleton.
// scope를  prototype 설정 시 getBean()호출시 매번 객체를 생성한다.
// request, session은 web에서 사용.

	<constructor-arg index="1">
		<value>홍길동</value>
	</constructor-arg>
	
	<constructor-arg index="0">
		<value>도둑놈</value>
	</constructor-arg>
	
	<constructor-arg index="2" value="2000"/> // String -> int로 자동 변경된다.
	<constructor-arg index="2" type="int" value="2000"/> // 매개변수가 int일 경우 
	<constructor-arg index="2" type="int" value="이천"/> // error
	<constructor-arg index="3" type="pack2.MyClass" ref="myClass"/>
</bean>
<bean id="myClass" class="pack2.MyClass">
	<property name="data" value="자바"/> // setter injection
</bean>

② 생성자 주입방법 2

 : arrange.xml의 Namespace Tab에서 c, p check

<bean id="mbean" class="pack.MessageImpl"
			c:name1="고길동"
			c:name2="깡패"
			c:year="2000"
			c:myclass-ref="myClass"
			p:spec="sqld">
</bean>

<bean id="myClass" class="pack2.MyClass">
	<property name="data" value="자바"/><!-- setter injection -->
</bean>

 => contruct, setter injection 혼용가능

 

- main

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("arrange.xml");
	ApplicationContext context = new ClassPathXmlApplicationContext("classpath:pack/arrange.xml");
	// src/main/java/pack에 있을경우 
	
	//다형성
	MessageInter inter = (MessageInter)context.getBean("mbean"); // 캐스팅을 interface로 한다.
	// MessageInter inter = (MessageImpl)context.getBean("mbean"); // 지양
	inter.sayHi();
	System.out.println("주소 : "+inter);
}

 


3. setter 주입(setter injection)

 

 - MyProcess

public class MyProcess {
	private int age;
	private String name;
	private ShowData showData;

	// setter를 이용해서 private 멤버에 값을 입력
	public void setAge(int age) {
		this.age = age;
	}
	public void setName(String name) {
		this.name = name;
	}
	public void setShowData(ShowData showData) {
		this.showData = showData;
	}
	
	public void printData() {
		System.out.println(name+age+showData.toAlias()+showData.toHobby());
	}
}

 - init.xml

① 방법 1

<bean id="myProcess" class="pack.MyProcess">
	<property name="age" value="22"/> //MyProcess의 setAge()를 호출한다. 
	<property name="name" value="김이상"/> // setName() 호출
	<property name="showData" ref="showData"/> //객체의 setter 호출시 ref를 사용한다.  
</bean>
 
<bean id="showData" class="pack.ShowData"/> // 다른 <bean>태그에서도 호출 가능

② 방법 2 : 하위 element로 사용

<bean id="myProcess" class="pack.MyProcess">
	<property name="age">
		<value>23</value>
	</property>
	<property name="name">
		<value>김이상</value>
	</property>
	<property name="showData">
		<bean class="pack.ShowData"/> // 해당 <bean>태그에서만 사용가능
	</property>
</bean>

 - main

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("init.xml");
	MyProcess myProcess = (MyProcess)context.getBean("myProcess");
	myProcess.printData();
}

 


 

4. new를 이용한 객체생성과 Spring 객체 생성의 차이

 

 - new

public static void main(String[] args) {
	// new로 객체 생성시 매번 생성된다.
	MessageImpl m1 = new MessageImpl("김이상");
	m1.sayHi();

	MessageImpl m2 = new MessageImpl("김이상");
	m2.sayHi();

	System.out.println("주소 : "+m1); // 주소 : pack.MessageImpl@74e28667
	System.out.println("주소 : "+m2); // 주소 : pack.MessageImpl@1cf6d1be
}

 - xml

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("arrange.xml");
	
	// getBean사용시 singletone으로 객체 생성
	MessageImpl impl1 = (MessageImpl)context.getBean("mbean");
	impl1.sayHi();
	
	MessageImpl impl2 = (MessageImpl)context.getBean("mbean");
	impl2.sayHi();
	
	System.out.println("주소 : "+impl1);
	System.out.println("주소 : "+impl2);
}

 

. 어노테이션 : 객체생성시 xml에 의존적 -> @annotation으로 대체.

① @Require 어노테이션 : setter를 사용하도록 제약을 건다.

 - Abc

 public class Abc {
	private int number;
	
	@Required // setter를 사용하도록 제약을 건다.
	public void setNumber(int number) {
		this.number = number;
	}
	
	public void printData() {
		System.out.println("number : "+number);
	}
}

 - anno1.xml

<beans>
	<context:annotation-config/>
	<bean id="abc" class="anno1.Abc">
		<property name="number" value="100"></property>
	</bean>
</beans>

 - main

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("anno1.xml");
	Abc abc = (Abc)context.getBean("abc");
	abc.printData();
}

② @Component : 객체 생성 

 - SenderInter

public interface SenderInter{
	void show();
}

 - Sender

@Component // 객체 생성 
public class Sender implements SenderInter{
	public void show() {
		System.out.println("show메소드 수행");
	}
}

- Sender2

//@Component = @Component("Sender2")// 객체 생성
@Component("별명")
public class Sender2 implements SenderInter{
	public void show() {
		System.out.println("Sender2의 show메소드 수행");
	}
}

③ @Service : @Component와 동일하나 Business 로직에서 사용한다.

④ @Autowired : setter가 내부적으로 정의되며, type에 의한 mapping 진행.

⑤ @Qualifier("아이디") : type에 대하여 mapping시 동일한 타입(같은 부모를 상속받는 자식) 객체가 복수인 경우 

     Qualifier의 아이디에 명시한 아이디로 mapping한다.

 - SenderProcess

//@Component
//@Service
@Service("SenderProcess") //Component와 동일
public class SenderProcess { // 서비스를 하는 비지니스 로직
	private String name = "tom";
    
	@Autowired  // xml의 type에 대한 mapping.
	//@Qualifier("sender2") // 동일한 타입의 객체(상속받는 자식 객체)가 복수인 경우 id로 mapping
	@Qualifier("별명")
	private SenderInter senderInter;
	
	//public void setSender(Sender sender) { // @Autowired사용시 삭제가능
	//	this.sender = sender;
	//}
    
	public void displayData() {
		System.out.println("displayData의 name : " + name);
		senderInter.show();
	}
}

 - xml

<context:annotation-config/> // @어노테이션을 사용가능하게 한다.
<bean id="senderProcess" class="anno2.SenderProcess"/>
<bean id="sender" class="anno2.Sender"/>
<bean id="sender2" class="anno2.Sender2"/>
 // SenderInter를 상속하는 class가 복수가 되면 type으로 mapping하기 때문에 식별자가 필요하다.
<bean class="anno2.Sender"/>
// type에 대한 mapping으로 id가 없어도 mapping된다.
<context:annotation-config/> // 생략 가능
<context:component-scan base-package="anno2"/> // pakage의 @Component, @Service가 작성된 class의 Bean을 생성.

 - main

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("anno2.xml");
	SenderProcess senderProcess = (SenderProcess)context.getBean("senderProcess");
	senderProcess.displayData();
}

 

[목차]

1. Spring 환경구축
* 이클립스에 연동
* 라이브러리 로딩방법
① maven을 사용하지 않고 spring lib 파일을 직접 다운로드
② Maven, Gradle


 

[내용]

1. Spring 환경구축

* 이클립스에 연동

 

[eclipse] - [help] - [eclipse marketplace] - [find : spring] - [Spring Tools 3 Add-On for Spring Tools 4 3.9.15 RELEASE] download


* 라이브러리 로딩방법

 

① maven을 사용하지 않고 spring lib 파일을 직접 다운로드

 framwork download

maven.springframework.org/release/org/springframework/spring/5.0.5.RELEASE/

 

Index of release/org/springframework/spring/5.0.5.RELEASE

 

maven.springframework.org

 => [spring-framework-5.0.5.RELEASE-dist.zip] Download

 

 라이브러리 등록

[File] - [New] - [New Spring Legacy Project] - [Simple java] check - [next] - [Library] tab - [Add jar] - [spring-framework-5.0.5.RELEASE\libs] 선택

 

③ bean xml 파일에 bean 등록

프로젝트의 [src] 오른쪽 클릭 - [New] - [Spring Bean Configuration file] - xml파일명 입력 - [finish]

<beans>
	<bean id="mes" class="pack.Message1" />
</beans>

④ getBean

ApplicationContext context = new ClassPathXmlApplicationContext("init.xml");
MessageInter inter2 = (MessageInter)context.getBean("mes");
inter2.sayHello("양길동");
// Spring이 객체를 Singletone으로 생성하여 줌.

 

② Maven, Gradle - 주로 사용

 - 외부에서 로딩
 - 프로젝트, 라이브러리 관리

 

① maven 프로젝트 생성

[New] - [Maven Project] - [create a simple project] check - [Group id], [Atifact id] 입력 - [finish]

 

pom.xml파일 수정

<project>
	<properties>
		<!-- Generic properties -->
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<!-- Spring -->
		<spring-framework.version>5.0.0.RELEASE</spring-framework.version>
		<!-- => 원하는 버전정보 입력 -->
		<!-- Mybatis -->
		<mybatis.version>3.2.3</mybatis.version>
	</properties>
	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.annotation</groupId>
			<artifactId>javax.annotation-api</artifactId>
			<version>1.3.2</version>
		</dependency>
		<!-- Spring jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<!-- oracle jdbc driver -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.1.0.7.0</version>
		</dependency>
		<!-- mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>${mybatis.version}</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.0.2</version>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.2.2</version>
		</dependency>
	</dependencies>
</project>

 => 다음 경로에 라이브러리가 사이트에서 받아지고 이후 로컬의 라이브러리를 사용한다.

C:\Users\wonh\.m2\repository\org\springframework\spring-core\5.0.0.RELEASE\

 

③ meta.xml 파일 생성

프로젝트의 [src/main/resource] 오른쪽 클릭 - [New] - [Spring Bean Configuration file] - xml파일명 입력 [next] - [beans],[http://www.springframwork.org/schema/beans/spring-beans.xsd] check - [finish] 

<beans>
	<bean id="mes" class="pack.Message1" />
</beans>

12. MVC pattern

Client
(html)
----> Controller
(servlet,
interface, class)
----> Model
(DTO, DAO-sql)
----> DB
----> View
(FormBean, jsp)
   

 

1) Client : html의 <form>,<a>태그로 값을 전달한다.

<form action="path" method="get">
	<input type="text" name="name1" value="value1">
	<input type="submit">
</form>


<a href="sang.do?command=sang">상품</a><br> // Parameter로 값 전달
<a href="sang.do">상품</a> // 요청명으로 값 전달
<a href="buser.do">부서</a>

 

2) Controller

① servlet

@WebServlet("/hobby.do") : 단일
@WebServlet({"/hobby.do","/nicd.do"}) : 배열(복수)
@WebServlet({"*.do","*.kor"}) : 확장자(복수)

 => ( )안 설정한 url pattern을 사용하여 servlet 호출할 수 있다.

 

private HobbyModel model;
public void init() throws ServletException {
	model = new HobbyModel();
}

 => servlet의 service에 구현시 요청마다 new 객체 생성시 메모리를 많이 소모 함으로 init()에 작성한다.

 

protected void service(HttpServletRequest request, HttpServletResponse response)
						throws ServletException, IOException {

 => get/post방식에 상관없이 동작하도록 구현.

 

	try {
		name=request.getParameter("name");
	} catch (Exception e) {
		name=null;
	}  

 => 요청처리 방법 1 : parameter사용. request.getParameter()로 html의 value를 전달받는다.

 

	String str1 = request.getRequestURI();
	// 요청명 : /mvc_ex3_db/sang.do
	int idx = str1.lastIndexOf('/');
	// 11
	String str2 = str1.substring(idx+1); // idx+!부터 끝까지 자름
	// sang.do
	StringTokenizer str3 = new StringTokenizer(str2,"."); // str2를 .을 기준으로 토큰화
	// sang ,  do
	str4 = str3.nextToken();
	// sang
	String command = str4;

 => 요청처리 방법 2 : 요청명을 사용

      "/프로젝트명/uripattern.확장자"로 전달받는다.

 

	CommandInter inter = null;
	String viewName = "views/";
	try {
		if(command.equals("sang")) {
			inter = SangpumImpl.instance();
		}else if(command.equals("buser")) {
			inter = BuserImpl.instance();
		}else if(command.equals("jikwon")) {
			inter = JikwonImpl.instance();			
		}else {
			viewName += "error.html";
			response.sendRedirect(viewName);
            return;
		}
		viewName += inter.showData(request, response);
	} catch (Exception e) {
		System.out.println("service : "+e);
	}

 => interface를 상속받은 클래스의 singletone 객체를 받아온다.

 => 명령에 따른 다른 table의 객체를 받아오도록 오버라이딩을 진행한 메소드를 실행한다.

 

	//HobbyModel model = new HobbyModel();
	ArrayList<String> list = model.getHobby(hobby);
	request.setAttribute("datas", list);

 => 다음의 모델은 interface를 상속받은 클래스에 구현한다.(다형성을 사용)

 

	//response.sendRedirect(viewName+"?result="+result); // 컬렉션 류의 객체는 전달X
		
	request.setAttribute("result", result); // 객체 전달 가능.
	RequestDispatcher rd = request.getRequestDispatcher(viewName);
	rd.forward(request, response);
}

 => request setAttribute(), getAttribute()으로 값 전달.

 => sendRedirect의 경우 객체를 전달할 수 없어 forward로 요청 재지정하여 값 전달.

 

 

 * web.xml를 이용한 url pattern 등록 (경로 : Context/WEB_INF/web.xml)

<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>

 => <welcom-file>내 파일명을 입력하면, 도메인만 입력시에 해당 파일이 실행된다.

 => 위쪽 부터 우선순위를 가진다.

 

<servlet>
	<servlet-name>hobby</servlet-name>
	<servlet-class>pack.controller.HobbyController</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>hobby</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

=> url pattern 등록

 

 

② Interface

public interface CommandInter {
	String showData(HttpServletRequest request, HttpServletResponse response);
}

 

 

③ class

public class SangpumImpl implements CommandInter{
	//Singleton
	static SangpumImpl impl = new SangpumImpl();
	public static SangpumImpl instance() {
		return impl;
	}

	@Override
	public String showData(HttpServletRequest request, HttpServletResponse response) {
		SangpumModel model = new SangpumModel();//원래는 싱글으로 작성된 클래스를 읽어야함.
		ArrayList<SangpumDto> list = model.getDataAll();
		request.setAttribute("datas", list);
		return "sangpum.jsp";
	}

}

 => 모델에 있는 클래스와 통신을 하는 컨트롤러 영역내의 클래스를 선언한다.

 

* Singletone : 한클래스당 하나의 객체만 사용하도록 하는 인스턴스.

class 클래스명{
	private static 클래스명 singleton명 = new 클래스명();
	static 클래스명 getInstance(){
		return singleton;
	}
}

 

 

3) Model

① DTO

public class SangpumDto {
	private String code;
	private String sang;
	private String su;
	private int dan;
	public String getCode() {
		return code;
	}
	public void setCode(String code) {
		this.code = code;
	}
    ...
}

 => 필드, getter, setter 선언

 

 

② DAO (or Model)

 * JDBC Connection pooling

public class SangpumModel {
	private Connection conn;
	private PreparedStatement ptmt;
	private ResultSet rs;
	private DataSource ds;
	
	
	public SangpumModel() {
		try {
			Context context = new InitialContext();
			ds= (DataSource)context.lookup("java:comp/env/jdbc_maria");
		} catch (Exception e) {
			System.out.println("SangpumModel err"+e);
		}
	}
	
	public ArrayList<SangpumDto> getDataAll(){
		ArrayList<SangpumDto> list = new ArrayList<>();
		try {
			conn=ds.getConnection();
			String sql = "select * from sangdata";
			ptmt = conn.prepareStatement(sql);
			rs= ptmt.executeQuery();
			while(rs.next()) {
				SangpumDto dto = new SangpumDto();
				dto.setCode(rs.getString("code"));
				dto.setSang(rs.getString("sang"));
				dto.setSu(rs.getString("su"));
				dto.setDan(rs.getInt("dan"));
				list.add(dto);
			}
		} catch (Exception e) {
			System.out.println("getDataAll err"+e);
		}finally {
			try {
				if(conn!=null) conn.close();
				if(ptmt!=null) ptmt.close();
				if(rs!=null) rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return list;
	}
}

 

 * Mybatis (xml)

  - db.properties : 계정정보

  - Configuration.xml : <alias>, <mapper> 수정

  - DataMapper.xm : xml 형식 sql문 작성

  - SqlMapConfig : SqlSessionFactory 객체 생성

  - DAO(or model)

public class SangpumModel {
	private SqlSessionFactory factory = SqlMapConfig.getSqlSession();
	
	public ArrayList<SangpumDto> getDataAll(){
		ArrayList<SangpumDto> list = null;
		SqlSession sqlSession = factory.openSession();
		list = (ArrayList)sqlSession.selectList("selectDataAll");
		sqlSession.close();
		return list;
	}
}

 

* Mybatis (@어노테이션)

  - db.properties : 계정정보

  - Configuration.xml : <alias>, <mapper> 삭제

  - DataMapper.xm : 삭제

  - SqlMapperInter : 테이블수에 맞게 작성. sql문 작성

public interface SqlMapperInter_Jikwon {
	@Select("select jikwon_no, jikwon_name, buser_num, jikwon_jik from jikwon where buser_num = #{buser_no}")
	public List<JikwonDto> selectPartJikwon(String buser_no);
}

 

  - SqlMapConfig : SqlMapperInter의 class 배열에 연결.

Class[] mappers = {SqlMapperInter_Buser.class, SqlMapperInter_Jikwon.class, SqlMapperInter_Gogek.class};
for(int i=0; i<mappers.length;i++) {
	sqlSession.getConfiguration().addMapper(mappers[i]);
}

 

  - DAO(or model) : SqlSessionFactory객체사용하여 sql 메소드 호출.

public class JikwonDao {
	private SqlSessionFactory factory = SqlMapConfig.getSqlSession();
	
	public ArrayList<JikwonDto> selectPartJikwon(String buser_no){
		ArrayList<JikwonDto> list = new ArrayList<>(); 
		SqlSession sqlSession = factory.openSession();
		
		try {
			SqlMapperInter_Jikwon inter = sqlSession.getMapper(SqlMapperInter_Jikwon.class);
			list = (ArrayList)inter.selectPartJikwon(buser_no);
		} catch (Exception e) {
			System.out.println("selectPartJikwon err"+e);
		}finally {
			if(sqlSession != null) sqlSession.close();
		}
		return list;
	}
	
}

 

 

4) View

① jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>Insert title here</title>
</head>
<body>
	*상품자료(MVC pattern)
	<table border="1">
		<tr>
			<td>코드</td><td>품명</td><td>수량</td><td>단가</td>
		</tr>
		<c:forEach var = "s" items="${datas}">
			<tr>
				<td>${s.code}</td>
				<td>${s.sang}</td>
				<td>${s.su}</td>
				<td>${s.dan}</td>
			</tr>
		</c:forEach>
	</table>
	
</body>
</html>

 

'BACK END > Servlet' 카테고리의 다른 글

[Servlet] Servlet 정리9 - Mybatis2  (0) 2021.01.06
[Servlet] Servlet 정리8 - Mybatis  (0) 2021.01.06
[Servlet] Servlet 정리7 - JDBC  (0) 2021.01.06
[Servlet] Servlet 정리6 - EL, JSTL  (0) 2021.01.04
[Servlet] Servlet 정리5 - Ajax  (0) 2021.01.03

+ Recent posts

1···456789