저번까지 다운로드, 업로드를 해보았습니다.

이번엔 비동기방식인 Ajax를 간단하게 알아보도록 하겠습니다.

List에서 다음 페이지를 볼려고할 때 페이지가 전환되는 것을 알 수 있었습니다.

그렇다면 페이지 전환없이 바로 다음 페이지를 보여줄 수 있게하면 어떨까요?

이것이 웹프로그램이 아닌 일반 어플리케이션을 보는 것과 같은 사용자 경험인 

RIA(Rich Internet Application)의 시작입니다.

이러한 기술들이 가능하기 위해 사용되는 것이 바로 AJAX(Asynchronous Javascript And XML)입니다.

AJAX는 Javscript를 이용한 비동기 통신, 클라이언트(브라우저)와 서버(웹서버)간에 XML 데이터를 주고 받는 기술입니다.

먼저 기존의 방식과 차이점을 그림으로 설명해보겠습니다.


그림 출처 : http://hieroglyph.tistory.com/13


기존 MVC패턴의 동작방식

요청페이지에서 요청을 날리면 웹서버에서 그에 맞는 액션을 한다음 그에 맞는 jsp페이지를 반환하면

사용자는 페이지가 전환되면서 응답페이지를 보게 됩니다.



AJAX를 사용한다면

그림과 같이 요청페이지에서 자바스크립트를 이용해 웹서버에 비동기요청을 보냅니다.

웹서버에서는 이 요청에 대한 액션을 실행한뒤 데이터를 반환하는데,

이때 자바스크립트에서 해당 응답을 받아서 돌아온 데이터들을 XML, JSON, TEXT등의 형식으로 받아서

요청 페이지의 DOM을 조작해 데이터를 보여주는 것으로 페이지 전환없이 요청에 대한 응답이 끝나게 됩니다.


간단히 말해 AJAX는 페이지 전환이 없는 웹 동작방식인겁니다.

AJAX를 이용해 실습해볼건 List에서 다음 페이지로 페이지 전환이 되는게 아니라 아래에 이어서 쭉쭉 나오게 하는것으로 실습해보겠습니다.

그러기위해서 Jquery를 사용할건데, 해드태그가 끝나기전 다음 구문을 입력해주세요. (List.jsp에)

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>


그리고 다음페이지를 누르면 잘 요청되는지 확인을 위해 다음 구문을 넣어줍시다.

<fmt:parseNumber value="${page/10+1 }" type="number"  integerOnly="True" /> 페이지


<c:if test="${fn:length( articleList ) < 10}"> 

<a href="#">다음페이지</a>

</c:if>

<c:if test="${fn:length( articleList ) >= 10}"> 

<a href="#" onclick="loadNextPage('${page+10}')">다음페이지</a>

</c:if>

<script>

function loadNextPage(page){

var param = "page="+page;

$('#append_article').load("list.do", param, function(data){

alert(data);

});

}

</script>


<a href="write.jsp">글쓰기</a>

href = "#"은 현재페이지이며, 클릭을 하면 loadNextPage라는 함수를 실행하게 됩니다.

loadNextPage에서는 페이지를 파라미터로 받고 jquery의 selector를 이용해 위에 만든 div태그를 선택한뒤 .load함수를 

실행하게 됩니다. 세번째 인자값으로 응답 함수에 요청이 완료되어 돌아온 값을 alert으로 출력을 합니다.

한번 실행결과를 봅시다.

아래에 무언가가 하나더 생기고 alert으로 요청 값이 넘어가서 반환되는 jsp페이지가 그대로 출력되는것을 알 수 있습니다.

아 load 함수는 jquery에서 내부적으로 ajax요청이 구현되어 있는 함수입니다.

그런데 우리는 위의 결과처럼 전체 jsp페이지를 원하는게 아니고 안에 내용만 추가되는 것을 원합니다.

그래야 부하도 덜 걸리고 좀 더 깔끔해 보이겠죠.

이를 위해 ajax요청에 대비한 Action을 만들어야겠습니다.

url은 ajaxList.do

클래스 명은 AjaxListAction

AjaxListAction을 사용할려면 Command.propertis에 등록해야겠죠? 입력해줍시다.

그리고 AjaxListAction을 만들고, ListAction과 유사하기때문에 복사 붙여넣기뒤 조금 수정을 하였습니다.

그리고 데이터를 뿌려줄 ajaxList.jsp를 만들었구요. 그럼 ajaList.jsp파일을 생성해줍니다.

생성하는데 기존에 있는 body나 head부분을 다 지워주고 위의 내용만 넣을 수 있도록 합시다.


List.jsp에서 는 함수를 추가해주고요. 

<input type="hidden" name="page" id="page value="${page}"/>

는 페이지가 최초로 호출되어있을 때 딱히 보여주는게 없으므로 hidden으로 둡니다.

ajax 요청이 끝나면 페이지의 값이 변화 해야하는데

요청시에는 jquery를 통해 page input태그에 접근해 해당 input태그로부터 값을 불러와 요청을 합니다.

페이지가 로드되고 게시글들이 테이블에 append된 이후에 이때 page의 값을 마찬가지로

jquery를 통해 page input태그에 접근하여 값을 갱신합니다.

이렇게 다음 게시글을 계속 불러올때도 page에서 10이 더해져 리스트를 출력하게 됩니다.


이번에 사용한 함수는 jquery에서 ajx를 쉽게 사용할 수 있도록 구현된 ajax함수입니다.

사용법은 load함수와 유사합니다.

간단하게 ajax가 어떻게 구동되는지 알아보았습니다.

http://cusmaker.tistory.com/114

위의 블로그에서 대부분의 내용을 차용하였으며, 많은 공부가 되었습니다.
































저번 시간에는 파일 업로드까지 했습니다. 그럼 이번엔 다운로드를 해야겠죠?

다운로드도 게시판에서 빼놓을 수 없는 기능인데요.

물론 이 기능에서도 보안적인 취약점이 있습니다. 파일 다운로드 취약점으로

권한 없는 악의적인 사용자가 서버의 파일을 다운로드할 수 있는 취약점이죠.

이번 게시글에서도 보안은 신경쓰지않고 어떻게 다운로드가 되는지 흐름을 알아보기 위함입니다.

 

다운로드를 받기위해선 어떻게 해야할까요?

가장 간단한 방법은 해당 파일의 경로를 a 태그의 href에 파일이름과 명시하면 되는데

이 방법은 서버의 파일경로가 노출되므로 보안상 매우 취약합니다.

페이지 이동하는 것도 이때까지 다운로드해봤을 때 아닌거 같구요.

 

그래서 꼼수를 쓰기로했습니다.

iframe 태그를 이용하여 페이지의 redirect없이 다운로드할 수 있도록 말이죠.

iframe은 페이지속에 또다른 페이지를 표시하기 위한 태그입니다.

 

function onDownload(idx){
 var o = document.getElementById("ifrm_filedown");
 o.src = "download.do?idx="+idx;
}
</script>

</head>

<body>

<iframe id="ifrm_filedown"  style="position:absolute; z-index:1;visibility : hidden;"></iframe> 

......

  <tr>
   <th colspan="2"> 첨부파일 </th>
   <td colspan="8">
    <a href="#" onclick="onDownload('${article.idx}')">${article.filename}</a>
   </td>
  </tr>

꼼수로 iframe을 이용하여 다운로드가 될 수 있도록 하였습니다.

위에 보시면 download.do?idx=를 통해 해당 게시글의 idx를 통해 파일 이름을 가지고 올겁니다.

download.do? 보니까 무엇을 고쳐야할지 이제 감이 오시죠?

Command.properties에 추가합시다

/download.do=com.board.action.DownloadAction

그렇다면 DownloadAction도 만들어주어야죠.

간략하게 설명하자면

파일 이름을 받아서, 실제 파일이 들어있는 경로에 설정한 upload폴더와 파일이름을 붙여서

해당 스트링에서 파일을 가져오고, 파일이 존재하지 않으면 에러 처리를 합니다.

BoardDao는 content와 똑같은 기능을하기때문에 getArticle을 재사용했으며

파일 사이즈를 조사하고 content타입과 헤더를 세팅하여 파일을 출력합니다.

그리고 파일다운로드를 별도로 리턴할 페이지가 없기때문에 null로 반환합니다.

 

이제 업로드, 다운로드도 끝났습니다.

그런데 게시글을 지운다면 파일도 같이 지워져야하는데 아직 그 기능은 만들지 않았습니다.

만들어 봅시다. 게시글을 지울 때 동작하기 때문에 DeleteAction을 고치러 갑니다.

별로 길지도 않고 어렵지 않습니다.

딱 보시면 보이는 소스..

 

이로써 Model2까지 다 해보았습니다.

앞으로 Ajax를 이용하여 게시글 목록을 계속 보는것만 남았는데 그 이후(Spring)는 에러를 잡지못해서.. 추후에 해보도록 하겠습니다.

 

직접 게시판을 만들어보고 글쓰기, 삭제, 수정, 파일업로드, 다운로드 등등을 직접 만들다 보니 많은 도움이 됩니다.

웹해킹에서 주로발생하는 Sql 인젝션 파라미터변조 파일 업로드, 다운로드 취약점 등등 어떻게 발생하는지에 대해

간략적이나마 감이 오기 시작합니다.

나중에 시간이 되면 이 글에서 보안적 취약점을 조치하는 방법까지 기술하도록 해보겠습니다.

수고하셨습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

저번시간에 counting까지 해보았습니다.

이번 시간에는 파일 업로드를 해봅시다.

파일 업로드는 게시판에서 빠질 수가 없죠!

그리고 파일 업로드 취약점도 있구요. 일단 보안은 생각안하고 구현만 해봅시다.


파일 업로드에는 뭐가 필요할까요?

먼저 View에서 파일을 올려야할 폼을 추가해야하고

조회할땐 다운로드 할 수 있게해야죠?


컨트롤러에서는 업로드 요청이 들어오면 파일이 담긴 폼데이터를 받는 특수한 형태의 객체를 사용해야합니다.

MultipartRequest라는 녀석인데요. 이를 이용해서 기존의 reqeust객체를 대신해 파일과 파라미터를 받아서 처리해야합니다.


모델에서는 파일을 저장한 공간을 고려해야합니다.

파일을 byte 스트림을 이용해 데이터베이스에 저장할 순 있지만 보통은 그렇게 하지않고, 외부에 저장합니다.

그래도 파일의 위치를 저장해야하니까 기존의 테이블에 파일 이름을 담을 컬럼을 추가하고,

쿼리를 수정해주어야겠죠.

먼저 글쓰기 폼을 수정해야죠.

write.jsp에서 값을 바꾸어줍시다.


<body>


<h1> Write This </h1>

<form action="insert.do" method="post" enctype="multipart/form-data" onsubmit="return formCheck();">

제목 : <input type="text" name="title" /> <br/>

작성자 : <input type="text" name="writer" /> <br/>

내용 : <textarea rows="10" cols="20" name="content"/></textarea> <br/>

파일 : <input type="file" name="filename"><br/>


<input type="submit"/>

</form>


추가된 부분을 표시해두었습니다.

enctype multipart가 추가되었고 파일을 올리는 곳이 생겼습니다.

실행시키면 파일을 올리는 부분이 나올겁니다.


 


그다음으로 폼태그에 action에 등록한 insert.do에 해당하는 InsertAction.Java를 수정하면됩니다.

아까전에 말했듯이 MultipartRequest라는 객체가 필요한데 이것을 사용하기 위해 다음 라이브러리를 사용합니다.

WebContent > WEB-INF > lib

cos.jar

그리고 올라갈 파일들이 있을 폴더가 있어야하므로 Webcontent에 upload 폴더를 만들어 줍시다.

그리고 InsertAction.JAVA를 고쳐주어야합니다.

request.*를 multi로 바꾸어야합니다.

  
  
    MultipartRequest multi = null; 

    int sizeLimit = 10 * 1024 * 1024 ; // 10메가

    String savePath = request.getRealPath("/upload");    // 파일이 업로드될 실제 tomcat 폴더의 WebContent 기준

    try{

    multi=new MultipartRequest(request, savePath, sizeLimit, "euc-kr", new DefaultFileRenamePolicy());

     }catch (Exception e) {

     e.printStackTrace();

     }

    String filename = multi.getFilesystemName("filename");


  String title = multi.getParameter("title");
  String writer = multi.getParameter("writer");
  String content = multi.getParameter("content");
  int count = 0;
  String regip = request.getRemoteAddr();
  

multi로 다 바꾸어주고 파일이 올라갈 것도 같이 만들어 줍니다.

MultipartReqeust랑 DefaultFileRenamePolicy()에 빨간줄이 생길텐데 import하시면 됩니다.

DefaultFileRenamePolicy()는 같은 이름의 파일이 올라오면 filename1, filename2로 자동적으로 숫자를 붙여줍니다.

그리고 실행을 해보고 파일을 올려봅시다. 하지만 아무리 찾아도 올린 파일을 찾을 수가 없어요.

이게 아파치로 실행되면서 다른쪽에 저장되기 때문인데요.

~사용자환경\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\

으로 가시면 파일과 이때까지 작성한 소스를 확인할 수 있습니다.

업로드가 된것을 확인하면 이제 content에서 보여줘야하기때문에 content.jsp를 고쳐줍시다.

 

  <tr>

   <th colspan="2">첨부파일</th>   

   <td colspan="8">${article.filename}</td>

  </tr>

하지만 아직 끝난게 아닙니다.

데이터베이스에 파일 이름을 넣을 칼럼을 추가해주어야하고

board Beans에 변수를 추가하고 게터와 세터를 추가하고 쿼리를 추가하는 작업이 필요합니다.

이건 이때까지 포스팅해온 내용에 다 있기때문에 따로 언급하진 않겠습니다.

잘할 수 있으리라 생각합니다.

 

이번 시간에는 counting을 넣어볼건데요.

프로그래밍에는 정해진 방법이 없다고 하니, 여건에 맞게 개발하면되겠습니다.

보통 카운터가 올라갈 때는 게시글을 클릭했을 때 올라가겠죠?

근데 그냥 클릭했을때만 올라가게한다면 글쓴이가 자기 카운터올릴려고 계속 들어갈 수 있겠죠?

이 게시판을 만들면서 개인정보가 있을 만한 로그인이나 회원가입을 만들지 않았습니다.

그렇다면 간단하게 개인을 확인할 수 있는 방법이 무엇이 있을까요?

IP를 사용하면 됩니다. IP를 가져오는 메소드가 있는데, request.getRemoteAddr() 메소드입니다.

IP를 사용하려면 컬럼등 추가해야할게 많습니다. 다 추가하러 가봅시다

먼저 DB에 칼럼값을 넣어줘야합니다.

기본값은 127.0.0.1로 컬럼을 추가하였습니다. (SQL Developer를 통해서)


DB를 고쳤으니 DB에서 값을 가져오는 형태인 beans.board도 고쳐야합니다.

변수를 선언해주고 게터와 세터도 제너레이터로 만들어줍시다.


ip를 받을 변수도 선언했으니 이제 글을 쓸 때 ip를 저장해야합니다.

insertAction.Java는 따로 만드는 것을 안했습니다만, 개인적으로 만들었을거라 생각합니다.

reqeust.getREmoteAddr()로 ip를 가져옵니다.

IP를 가져왔으니 이제 SQL 쿼리구문에도 IP를 저장할 수 있게 고쳐야겠습니다.

board.xml로 이동하여 regip를 추가해줍시다.


마지막으로 view를 고쳐주면 되겠습니다. ip를 추가한김에 게시글에 ip가 보이도록 설정하겠습니다.

Content.jsp에서 ip가 보이도록 고쳤습니다.

이제 Count가 증가할 수 있도록 해야합니다.

그런데 처음설명할 때 글을 클릭하면 카운트가 1증가할 수 있게해야합니다. 근데 ContentAction.java에서 처리할건지

다른 Action을 추가할건지 고민해봐야 합니다.

이번엔 CountAction을 추가하고 Redirect로 Content.Action으로 갈 수 있도록 해봅시다.

CountAction이 할 일은 게시글을 쓴 ip와 접속한 ip를 비교하여 같으면 카운트가 증가하지 않고 ContentAction으로

다를 경우 카운트를 증가하고 contentAction으로 Redirect하면 되겠습니다.


먼저 List.jsp에서 content.do가 아닌 count.do로 바꿉니다. 그래야 countAction으로 들을테니까요.

CountAction을 사용할려면 Command.properties에서도 추가해주어야합니다. 까먹으면 안되요.
/count.do=com.board.action.CountAction

CountAciton의 기능을 구현할 차례입니다.

게시글의 idx를 통해 ip를 가져오고 사용자의 ip를 가져와서 서로 대조한 후 같으면 증가를 안하고 다르면 증가합니다.

그리고 증가한 count의 값을 데이터베이스에 저장시키면 되는거죠.

그런데 우린 count가 증가한 값을 데이터베이스에 저장하는 Sql구문과 메소드가 없습니다. 만들러가야죠.

com.board.dao.BoardDao.java에 기능추가하러 갑시다.


기능 추가하였으니 이제 SQL 쿼리문도 추가하러 갑니다

Board.xml


완벽합니다~

하지만 최신 아파치를 쓰면 0:0:0:0:0:0:0:0이 찍히는 경우가 있는데 이 경우는 IPv6여서 나옵니다.

아파치 설정을 바꾸면 127.0.0.1로 나오니 구글에서 검색하시면 되겠습니다.

(0:0:0:0:0:0:0:0 ip로 몇개 게시글을 쓰고 127.0.0.1로 바꾸면 count가 증가하는 테스트를 쉽게 할 수 있습니다)

누락되었던 기능도 하나하나 만들어가네요.

나름 그냥저냥 게시판같은 느낌이 드네요.

거의 다 끝나갑니다. 드디어! 

이번에는 i-batis를 이용하여 Paging 기능을 구현해봅시다.

페이징 기능을 간단하게 정의해보자면

모든 게시글에 대해 정해진 게시글 개수만큼 페이지 번호를 매겨 뿌린다!

가 되겠습니다.

자바에서 가져온 리스트를 잘라서 보여주는 형식으로 구현해볼건데요.

저번 글에서 사용한 queryForList를 사용할겁니다. 여기에 변수가 2개 들어가는데요.

변수를 4개까지 쓸 수 있도록 오버로딩되어있습니다.

앞 두개 변수는 똑같고 세번째 변수는 페이지번호 네번째 변수는 데이터 rows 갯수입니다.

BoardDao.java에서 ("getArticleList", null, 0, 10)

을 넣고 실행해봅시다.

원래는 있는 글들 전부다 들고왔는데, 변수 2개만 추가 해보았는데 이번엔 10개만 들고 왔습니다.

세번째 변수가 페이지 번호라고 했는데요

0을 넣으면 반환값에서 0개를 제외한 다음 10개를 가져옵니다.

다음페이지를 보려면 현재 페이지 번호 + 10을 넣으면 다음 페이지를 볼 수 있습니다. (페이지당 10게시글 기준)

하지만 이 방식은 게시글 모든 List를 가져와서 짤라서 보여주는 것이므로 게시글이 엄청 많아지면 느려지게 됩니다.

그래서 DB에 날리는 질의에 파라미터를 주고 조건에 해당하는 게시글만 가져올 수 있도록 개발하는게 좋다고 합니다.

세번째 변수를 0으로 해놨는데 이 값이 유동적으로 변할 수 있게 page로 바꿉시다. 

그리고 이 메소드를 쓰는 ListAction도 바꿔주어야 겠죠?

이제 view(.jsp)에서 페이지 번호를 구현하고 동작할 수 있도록 합니다.

이번에는 이전페이지, 다음페이지만 추가해보도록하겠습니다.


list.jsp로 갑시다.

표현하기 위해 JSTL을 씁시다. Model1때 설명한 표현언어 생각나시죠?

<c:if page="${}"></c:if> 구문을 사용할겁니다.

그리고 리스트의 사이즈를 조사하기 위해 

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

소수점 처리를 위해

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

를 최상단에 넣어줍시다.


현재페이지는

   <fmt:parseNumber value="${page/10+1 }" type="number"  integerOnly="True" />


이전페이지는

<c:if test="${page > 0}"> 
<a href="list.do?page=${page-10}">이전페이지</a> 
</c:if>
<c:if test="${page == 0}"> 
<a href="#">이전페이지</a> 
</c:if>


다음페이지는

<c:if test="${fn:length( articleList ) < 10}"> 
<a href="#">다음페이지</a>
</c:if>
<c:if test="${fn:length( articleList ) == 10}"> 
<a href="list.do?page=${page+10}">다음페이지</a>
</c:if>



실행시켜보시면 아래와 같이 나오겠습니다.

위에 파라미터 값 page=10이 넘어가는거 보이시죠? 어떻게 페이징이 되는지 아실거라 생각됩니다.




































저번시간까지 MVC를 완성해보았는데요.

이번엔 외부라이브러리를 써서 Model을 좀 바꿔 보도록하겠습니다.

저번에 구성한 Model은 i-batis를 이용하여 재구성해보는 시간을 가지겠습니다.

i-batis는 아파치 소프트웨어 재단에서 개발하였으며 iBATIS(아이바티스)는 SQL에 기반한 데이터베이스와 자바닷넷(.NET), 루비(Ruby) 등을 연결시켜 주는 역할을 하는 영속성 프레임워크(Persistence Framework)이다. 이러한 연결은 프로그램의 소스코드에서 SQL 문장을 분리하여 별도의 XML 파일로 저장하고 이 둘을 서로 연결시켜주는 방식으로 작동한다.
ORM(Object Relational Mapping)

지금은 구글로 넘어가서 My-batis라고 불립니다. 

JDBC와 i-batis의 차이점 비교해봅시다.

기존의 JDBC에서는 실제로 사용되는 DB드라이버 로드부터 커넥션 객체를 생성하고 실제 DB에 접근하여 

해당 결과 값을 받아옵니다.

현재 구현된 소스에서는 하나하나의 커넥션을 관리해주는 커넥션 풀을 사용하지 않고,

모든 사용자에게 커넥션을 허용하였기 때문에 사용자가 몰릴 경우 DB에 과도한 무리를 주어 성능 상의 문제를 야기할 수 있습니다. 

i-Batis는 이러한 커넥션 생성부터 관리까지의 일련의 db연결과정을 정형화시켜놓은 라이브러리입니다.

XML파일에 맵핑 정보와 쿼리를 적어두고 데이터베이스의 테이블과 자바 객체를 맵핑하여

코딩의 단순화와 jdbc를 사용할 때 불편했던 작업들을 대신 수행해주므로써

코드의 분량을 기존대비 60%정도만으로도 프로그램의 작성이 가능해 집니다.


i-batis의 사용을 위해 라이브러리를 추가합시다.

ibatis-dao-2.jar

ibatis-sqlmap-2.jar

ibatis-common-2.jar


저번과 똑같이 WebContent > WEB-INF > lib폴더에 넣어두면 됩니다.

이제 이 라이브러리를 사용하기 위해 src 폴더에 

new > package > com.board.db.sqlconfig 

를 생성해줍시다. 

위의 패키지안에 SqlMapConfig.xml파일을 생성합시다.

그리고 아래의 내용을 채워넣습니다.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://www.ibatis.com/dtd/sql-map-config-2.dtd">

<sqlMapConfig>

    <properties resource="com/board/db/sqlconfig/SqlMapConfig.properties" />

    <!-- 접속정보를 저장한 properties파일을 지정합니다. -->

    <settings

        cacheModelsEnabled="true"

        enhancementEnabled="true"

        lazyLoadingEnabled="true"

        maxRequests="20"

        maxSessions="10"

        maxTransactions="10"

        useStatementNamespaces="false"

    />

    <transactionManager type="JDBC">

        <dataSource type="DBCP">

            <property name="JDBC.Driver" value="${driver}"/>

            <property name="JDBC.ConnectionURL" value="${url}"/>

            <property name="JDBC.Username" value="${username}"/>

            <property name="JDBC.Password" value="${password}"/>

            <property name="Pool.MaximumActiveConnections" value="10"/>

            <property name="Pool.MaximumIdleConnections" value="10"/>

            <property name="Pool.MaximumWait" value="60000"/>

            <property name="Pool.ValidationQuery" value="select 1 from dual"/>

            <property name="Pool.LogAbandoned" value="false"/>

            <property name="Pool.RemoveAbandoned" value="false"/>

            <property name="Pool.RemoveAbandonedTimeout" value="60000"/>

        </dataSource>

    </transactionManager> 

    <sqlMap resource="com/board/db/sqlmaps/Board.xml"/>

</sqlMapConfig>


간단히 설명해보자면 접속정보를 저장해둔 SqlMapConfig.properties에서 파일을 지정하고

<setting>에서 접속에 관련된 내용을 세팅합니다. 

그리고 맨아래에 sqlMap resource에 경로가 있는데 이 .xml파일이 DB에 접속할 SQL구문이 들어가게 됩니다.

Board.xml에 있는 내용은 추후에 채워넣기로 하고 지금까지 설정한 파일을 Java에서 사용하기 위해

IBatisDBConnector.java를 만듭시다.

com.board.db.sqlconfig에서 IBatisDBConnector.java파일을 만들어줍시다.

들어갈 내용은 아래와 같습니다.

package com.board.db.sqlconfig;

 

import java.io.Reader;

import com.ibatis.common.resources.Resources;

import com.ibatis.sqlmap.client.SqlMapClient;

import com.ibatis.sqlmap.client.SqlMapClientBuilder;

 

 

 

public final class IBatisDBConnector {

    private static SqlMapClient mySQLMap;

    static

    {

        try

        {   // 접속설정파일로드

            String resource = "com/board/db/sqlconfig/SqlMapConfig.xml";   

            Reader reader = Resources.getResourceAsReader(resource);

            mySQLMap = SqlMapClientBuilder.buildSqlMapClient(reader);

 

        }

        catch(Exception e)

        {

            e.printStackTrace();

        }      

    }

    public static SqlMapClient getSqlMapInstance()

    {   // 인스턴스 반환 메서드

        return mySQLMap;

    }

}

접속 설정파일 경로를 지정하고 읽을 준비를하고 인스턴스를 반환하는 메소드를 구현하였습니다.

아 그전에 DB연결을 위한 driver와 url, username, password파일이 필요한대요.

com.board.db.sqlconfig에서 SqlMapConfig.properties를 만들어 줍시다.

driver=core.log.jdbc.driver.OracleDriver

url=jdbc:oracle:thin:@localhost:1521:XE

username=root

password=root

위의 구문을 넣어주시는데 공백이나 필요없는 문자가 들어가면안됩니다.


보시면 IBatisDBConnector에서 연결을 위해 설정 값 파일인 SqlMapConfig.xml를 불러옵니다.

SqlMapConfig.xml에 properties resource에는 SqlMapConfig.properties라는 파일이 있는데, DB의 연결을 위해 driver,url,username,password가 적힌 파일을 읽어옵니다.

그리고 연결을 위한 settings값이 들어가고 property 설정이 들어갑니다. 마지막에 sqlMap resource에서는 DB에 들어갈 SQL Query문이 들어가게됩니다. 


이제 DB에 들어갈 SQL Query문이 있는 Board.xml을 채워봅시다. 

<typeAlias alias="str" type="java.lang.String" />

<typeAlias alias="int" type="java.lang.Integer" />

<typeAlias alias="board" type="com.board.beans.board" />


typeAlias는 긴 클래스명에 별칭을 주어 별칭만으로 접근 가능할 수 있도록 해줍니다.

기본타입의 String과 int 이외에도 사용자 지정 클래스(우리가 선언한 bean.board)도 별칭 지정이 가능합니다.


이제 Sql Query문이 들어갈 태그를 작성해보겠습니다.

쿼리 태그에서 대표적으로 쓰는 4가지 태그가 있습니다.

<select id="" parameterClass="" resultClass=""> </select>

<insert id=""  parameterClass ="" > </insert>

<delete id=""  parameterClass ="" > </delete>

<update id=""  parameterClass ="" > </update>

id는 Dao에서 해당 쿼리를 식별하기위한 변수(식별자)로 쓰입니다.

parameterClass는 쿼리를 실행할 때 파라미터가 필요할 경우, 이 속석값에 클래스 패스를 적어줍니다.

만약 int 값이라면 java.lang.Integer를 넣어주시면되는데 우리는 typeAlias에 선언을 했기때문에 int를 써도 무방합니다.

Board.xml 완성은 다음과 같습니다.


i-batis를 이용해서 DB 연결을 위한 세팅과 SQL 쿼리문까지 다 만들었습니다. 그렇다면 이제 이 것을 사용해야겠죠.

CommonDao.java로 가서 클래스 내의 내용을 다 지워줍시다. 어차피 여기서 하는 일은 i-batis로 다 넘겼기때문에

필요가 없지요.

i-Batis에서 제공하는 sqlMapClient객체를 생성하고 IBatisDBConnector로부터 인스턴스를 가져오는 SetDB메소드를 구현합니다.

IBatisDBConnector를 사용하기위해 import하고 myDB를 사용하기위해 sqlMapClient도 import합니다.


CommonDao.java까지 바꾸었습니다. 그럼 CommonDao를 쓰는 BoardDao.java도 바꾸어주어야합니다.

마찬가지로 BoardDao에 있던 내용들을 싹다 지워줍니다.

그리고 다음내용으로 바꿔줍니다.

package com.board.dao;


import com.board.beans.board;

import java.util.ArrayList;

import java.sql.SQLException;


public class BoardDao extends CommonDao{


public static BoardDao getInstance() {    // 기존에 구현했던 getInstance메서드

    BoardDao _instance = new BoardDao();

    _instance.SetDB();                // 이 코드 한줄이 추가되었습니다.

    return _instance;

 

}

@SuppressWarnings("unchecked")

public ArrayList<board> getArticleList() throws SQLException{

return (ArrayList<board>)GetDB().queryForList("getArticleList", null);

}

}


CommonDao를 extends하여  SetDB()으로 CommonDao에서 GetDB()를 통해 SqlMap 객체를 가져옵니다.

i-Batis를 통해 queryForList()라는 메소드로 IBatisDBConnector를 이용하여 Board.xml에 있는 getArticleList라는 식별자를

가진 SQL 쿼리문을 실행하게됩니다. 그 값을  ArrayList<board>형태로 값을 반환합니다.

두번째 변수인 null은 쿼리에 쓸 파라미터입니다. Object형태로 어떤 객체든 넘길 수 있다고 합니다.

여기서 queryForList()이외에 queryForObject등 다른 메소드가 있는데,

SELECT문에서 여러 개의 행을 가져오면 QueryForList() 메소드를 써야하고

하나의 행을 가져오면 QueryForOjbect() 메소드를 사용하면됩니다.

포스팅은 하지 않을거지만 개인적으로 수정,삭제를 추가하실때

update쿼리일 경우에 update

delete쿼리일 때 delete

insert쿼리일 때 insert메소드를 사용하여 쓰시면 됩니다.

이거말고도 많은 쿼리가 있다는데, 찾아보시면 될 듯싶습니다.

그리고!

commons-dbcp-1.2.2.jar

commons-pool-1.3.jar

log4sql.jar

이 파일들을 WebContent > WEB-INF > lib에 넣읍시다.

그리고 구동해서 잘나오면 됩니다.

아래에 SQL 쿼리문이 어떤게 실행되는지 확인할 수 있습니다.

** SqlMapConfig.properties에 driver값이 다를 수 있는데 log4sql.jar를 넣으면서 교체해주어야합니다.
*** 그냥 쭉 따라하시면 잘 될거에요(아마)




















































저번 포스팅까지는 MVC에서 C(컨트롤러)를 만들어 보았는데요.

이번에는 ListAction에 포함되어있는 DB 커넥트부분을 따로 때어내서 Model을 만들어 봅시다.

Model은 데이터베이스 접속에 관련된 부분을 담당하는 파트로 데이터베이스의 트랜젝션 관리, 커넥션관리, 쿼리제어 등

Action에서 데이터베이스에 관련된 모든 부분을 담당하게 됩니다.

http://cusmaker.tistory.com/88 (갓갓 블로그!!)


간단하게 그림을 설명해보겠습니다.

Controller에서 ListAction을 맵핑시켜 실행하게되고 ListAction은 DB의 정보가 필요합니다. 그래서 ListAction은

BoardDao를 사용하여 CommonDao를 이용하여 DB에 연결하고 값을 가져와서 View로 반환합니다.(.jsp로)

ListAction.java를 보시면 뭔가 딱 DB 커넥션에 연관있어보이는 소스코드가 바로 보일겁니다.

이런식으로 구성하게 되면 하나의 Action ( 하나의 요청)이 실행될 때 마다 DB에 접속하게 됩니다.

그렇다면 다수의 사용자가 실행하게되면 하나하나의 Action에 DB에 접속하고 끊고 접속하고 끊게 됩니다.

그러면 당연히 DB에 무리를 주게되고 많이 느려지거나 뻥! 터지겠죠.

따라서 커넥션의 효율적인 관리가 필요하게 됩니다. ( 커넥션 풀을 사용하는 이유)


만약 하나의 Action에서 두개 이상의 쿼리 실행이 생길 경우에 (글 입력, 로그입력 등등)

첫번째 쿼리가 실행에 실패했을 경우 다음에 실행하는 쿼리도 같이 실패해야 

DB의 무결성을 유지할 수 있는데 이 무결성을 유지하는 것을 트랜젝션관리라고 합니다.

이러한 코드들을 하나의 Action에 전부 넣게되면 코드의 길이가 길어지고 가독성이 떨어지게 됩니다.

이런 코드들을 분리하여 관리하기 위해 패키지하나를 생성합시다.

com.board.dao

dao란 데이터 접근 객체 (Data Access Object)의 약자로 비즈니스 로직을 수행하는 자바클래스를 Action이라고 명명하는 것과 마찬가지로

Model 객체를 명명하는 이름입니다.

패키지를 만들고 BoardDao.java를 만듭시다.


아까전에 ListAction에서 보았듯이 DB 커넥션때 공통되는 부분이 있습니다.

공통되는 부분을 CommonDao로 묶고 나머지가 CommonDao를 상속받아서 사용하면 효율적이지 않을까요?

코드의 효율성과 성능을 고려하여 코드 구조를 설계하는 것이죠.

그렇다면 CommonDao를 만들고 공통되는 부분을 넣도록 해봅시다.

경로를 똑같게 com.board.dao > CommonDao를 만듭시다. 

(복붙하고 DB접속이 안되는 불상사가 없길 바랍니다. ID, PW등 확인)


CommonDao를 만들었고 이제 하위 Dao인 BoardDao를 사용할 차례입니다.



CommonDao도 만들었고 BoardDao도 만들었습니다.

공통되게 DB커넥션에 사용하는 부분은 CommonDao에 넣어 BoardDao에서 CommonDao를 상속받게합니다.

BoardDao를 보시면

처음에 getInstance()가 있는데 DB의 접속이 일어날 때마다 Dao객체를 생성하고 연결하는 것은 낭비라고 볼 수 있습니다.

이에 자바에서는 싱글턴(singleton)패턴이라고 하는 디자인 패턴을 사용한다고 합니다.

최초에 객체를 생성하고 이후 접속 요청이 오면 새로운 객체를 생성하지 않고 인스턴스를 반환합니다. 

이 역시 완벽하게 구현된 싱글턴 패턴이 아니라고 하니 이해만하고 넘어가고 추후에 한번 더 공부해봅시다.


다시 BoardDao로 돌아와서

getArticleList()라는 함수에서는 모든 게시글을 가져오는 역할을 하며, CommonDao에 있는 openConnection()을 이용해

DB에 연결하게 되고 sql구문을 실행하게 됩니다. 


마지막으로는 CommonDao에 있는 closeConnection()을 통해 접속을 끊게 되구요. 그리고 실행되어 반환된 값을

ResultSet 변수에 담은뒤 board형태로 선언된 ArrayList(articleList)에 넣게되겠습니다. 

이제 ListAction에서 Model부분인 DB커넥션을 구현하였습니다. 그럼 ListAction에서는 더이상 DB 연결이 필요 없고

BoardDao를 불러와서 쓰면 되겠습니다. 

ListAction에서 DB커넥션 부분을 지우고 BoardDao를 호출하여 사용하도록 해보겠습니다.


Model 부분을 떼어내고 나니 ListAction이 매우 간단해졌습니다. 

ListAction이 할 일은 DB에 있는 게시판 리스트들을 가져와서 뿌려주는 일만 하면 되므로

반환받을 변수를 선언하고 Dao객체로부터 해당 메소드를 사용하기만 하면 됩니다.

잘 실행되시겠죠? ( 잘 실행되시길.. )


흐름도를 보자면

List.do 요청 -> web.xml에서 url파악후 ControllerAction 전달 -> ControllerAction에서 Url 분석 후 ListAction으로 전달 ->

ListAction에서 Model로 게시판 전체목록 쿼리 실행 -> 필요한 값들을 view로 반환 (.jsp)


List뿐만 아니라 다른 기능들도 구현하고 Model로 분리해보시길 바랍니다.

이번 포스팅으로 MVC에서 M(Model)이 끝났습니다. 그러면 MVC를 다해본거네요? (짝짝짝)

List를 통해 MVC를 만들면서 느낄 수 있는 부분이 기능을 하나 추가하게 되면 손 볼 파일들이 많아지는 것입니다.

머릿속에서 흐름을 기억해두시면 추후 기능을 추가할 때 빠지는 부분이 없을거라 생각됩니다.

기존 블로그와 같이 다음 게시글부터는 못만든 기능(count, paging)과 외부 라이브러리를 사용해보도록 하겠습니다.

꼭 직접 손으로 일일이 코딩해보시는 걸 추천드립니다.(복붙만하면 정말 금방해요.. 필자는 손으로 치다보니 오타도 나고, 에러잡고하니 생각보다 오래 걸렸어요.. )
































아... 글쓰다가 날려먹었네요...으..아...어..아.........(멘붕)

세상에 거의다 써가고 있었는데.. 

처음부터 다시ㅠㅠ


저번시간에는 View의 코드 분리를 하였고 이제부터 MVC패턴의 C인 컨트롤러를 구성해보겠습니다.

컨트롤러에서는 Java파일 즉, 클래스 파일로 넘어가기때문에 html, javascript코드는 볼 수 없습니다.

MVC가 어떻게 거쳐 동작하는지 간단하게 알아봅시다.

http://cusmaker.tistory.com/86


사용자 Browser에서 요청이 날라옵니다. 요청은 write.jsp처럼 페이지를 요청하는 건데 MVC에서는 요청이 바로 jsp로

바로 넘어가는게 아니라 Action(클래스명)을 거치게 됩니다. 이 자바 파일에서 비즈니스로직(로그인, 데이터가져오기, 세션, 유효성검증 등등)을 수행하고 View(.jsp)에서 필요한 데이터만 페이지로 넘기게 되겠습니다.

그렇기때문에 .jsp로 요청하는게 아닌 .do or .action등 다른 방식으로 URL을 요청하게됩니다. 인터넷 서핑하다보면

.jsp가 아닌 .do나 다른 확장자로 요청하는 것을 간간히 볼 수 있습니다. 이렇게 날아온 요청은 

해당 Action에 맵핑시키기위해 해당 맵핑정보가 있는 Properties파일(보통 텍스트파일)을 읽고 해당 클래스에 접근할 수 있게

해준다음 실행하게 되겠습니다. 

해당 Action에서 데이터베이스에 접속이 필요하다고 하면 이때 Model이 사용되게 됩니다.

Model은 DB의 접속(트랜잭션관리, 커넥션 풀 관리 등)과 각종 쿼리들을 처리할 수 있게 메소드 빈 형식으로 구성됩니다.

데이터베이스에 접근한 뒤 필요한 데이터를 반환하거나 수정, 삭제하고 다시 Action으로 돌아와 View에서 필요한 데이터를

jsp페이지로 보내면서 반환합니다 (dispatcher합니다)

로그인을 예로 들어보겠습니다.

사용자가 browser에서 id와 pw값을 가진 login.do를 요청 -> Controller에서 login.do의 기능을 하는 Action을 매칭 ->

해당 Action에서 사용자가 보낸 값인 id와 pw를 가지고 Model에 쿼리를 실행 -> 해당 Action에서 로그인 처리 ->

값에 따른 로그인 성공, 실패 페이지(JSP)로 반환

전체적인 흐름은 위와 같습니다.


자 그러면 먼저 사용자가 *.do로 요청할 때 처리할 수 있도록 기능을 만들어야겠죠?

먼저 index.jsp를 list.do로 바꾸어줍시다. 파일은 list.jsp로 고쳐주시고 링크같은 경우는 list.do로 바꾸어 줍시다.

<script>location.href="list.do";</script>


사용자가 *.do로 접속했을 때 제일 먼저 처리할 수 있도록 해야합니다. 그러기 위해선 WebContent->lib->web.xml파일을

고쳐주어야합니다.

없는 경우도 있는데 그럴때 우클릭 -> Other -> xmlfile로 만들어주시면 됩니다.

  <servlet>

   <servlet-name>ControllerAction</servlet-name>

   <servlet-class>com.board.controller.ControllerAction</servlet-class>

</servlet>

 

<servlet-mapping>

   <servlet-name>ControllerAction</servlet-name>

   <url-pattern>*.do</url-pattern>

</servlet-mapping>


위의 구문을 web.xml에 추가하시면됩니다. </welcom-file-list>아래 </web-app>위에


간단하게 설명해보자면 

<servlet>태그안에 name은 컨트롤러 이름을 설정하는것이고 class는 컨트롤러가 있는 위치를 지정해줍니다.

<servlet-mapping>태그안에 name은 맵핑할 컨트롤러 이름을 가져오는 것이고, url-pattern은 어떤 url로 들어오면

컨트롤러로 보낼것인지 설정하는 겁니다. 결과적으로 *.do로 오는 요청은 ControllerAction을 거치게 되는 것이죠.

자 그러면 우리는 web.xml에 ControllerAction을 선언했습니다만, 소스에는 저 ControllerAction이 없습니다.

그럼 만들어주어야죠. 


이제 ControllerAction에 내용을 넣어봅시다.

package com.board.control;
 
import java.io.*; 
import java.util.*; 
import javax.servlet.*; 
import javax.servlet.http.*;
 
public class ControllerAction extends HttpServlet {
 
    private Map commandMap = new HashMap(); // 명령어와 명령어 처리 클래스를 쌍으로 저장
 
    public void init(ServletConfig config) throws ServletException {
 
        // Common properties 
        loadProperties("com/board/properties/Command");
 
    }
 
    // properties 설정 
    private void loadProperties(String path) {
 
        ResourceBundle rbHome = ResourceBundle.getBundle(path);// 누구를 실행할지를 rb에
                                                                // 저장.
 
        Enumeration<String> actionEnumHome = rbHome.getKeys();
 
        while (actionEnumHome.hasMoreElements())
 
        {
 
            String command = actionEnumHome.nextElement();
           
 
            String className = rbHome.getString(command);
 
            try {
 
                Class commandClass = Class.forName(className); // 해당 문자열을 클래스로
                                                                // 만든다
 
                Object commandInstance = commandClass.newInstance(); // 해당 클래스의
                                                                        // 객체를
                                                                        // 생성
 
                commandMap.put(command, commandInstance); // Map 객체인 commandMap에
                                                            // 객체 저장
 
            } catch (ClassNotFoundException e) {
 
                continue; // error
 
                // throw new ServletException(e);
 
            } catch (InstantiationException e) {
 
                e.printStackTrace();
 
            } catch (IllegalAccessException e) {
 
                e.printStackTrace();
 
            }
 
        }
 
    }
 
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        requestPro(request, response); // get방식과 post방식을 모두 requestPro로 처리
 
    }
 
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
 
        requestPro(request, response);
 
    }
 
    // 사용자의 요청을 분석해서 해당 작업을 처리
 
    private void requestPro(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
 
        String view = null;
 
        CommandAction com = null;
 
        try {
 
            String command = request.getRequestURI();
 
            if (command.indexOf(request.getContextPath()) == 0) {
 
                command = command.substring(request.getContextPath().length());
 
            }
 
            com = (CommandAction) commandMap.get(command);
 
            if (com == null) {
 
                System.out.println("not found : " + command);
 
                return;
 
            }
 
            view = com.requestPro(request, response);
 
            if (view == null) {
 
                return;
 
            }
 
        } catch (Throwable e) {
 
            throw new ServletException(e);
 
        }
 
        if (view == null)
 
            return;
 
        RequestDispatcher dispatcher = request.getRequestDispatcher(view);
 
        dispatcher.forward(request, response);
 
    }
 
}

위의 소스코드로 입력하시면 빨간 줄이 많이 뜨는 것을 확인할 수 있습니다. 

같은 경로에서 CommandAction이라는 클래스를 하나더 만들어 줍시다. 그리고 다음 소스코드를 입력합시다.


package com.board.control;


import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public interface CommandAction {

public String requestPro(HttpServletRequest request, HttpServletResponse response) 

throws Throwable;

}

CommandAction은 명령어에서 찾은 클래스를 클래스화 시키는 것입니다. 

※ 추가적인 공부가 필요할 듯싶네요..


다시 ControllerAction으로 돌아와서 소스코드를 보시면 

init에서 com/board/properties/Command에서 맵핑정보를 가진 Properties파일을 로드해옵니다.

그렇다면 com/board/properties/Command에 Properties파일을 만들어주어야겠죠. 마저 설명하고 만들러 가봅시다.

로드해와서 loadProperties에서 맵핑을하면서 패키지 정보를 바탕으로 클래스화 시킨뒤 리소스 번들이라는 객체에

저장하게 됩니다. 

이제 Properties를 만들러갑시다. 

src에서 com.board.proerties라는 패키지를 만들어주고 new -> file -> Command.properties로 파일을 생성합니다.

우측에는 Command.properties 파일의 내용인데요. /list.do로 들어오는 요청은 com.board.action.ListAction으로 처리하겠다는 겁니다.

다음에 뭐 만들어야 될지 감이 오겠죠?

com.board.action이라는 패키지를 만들고 ListAction이라는 클래스를 만들어 줍시다.

만약 다른 Action이 추가될때마다 여기 경로에 추가해줘야 매칭이 되겠습니다.


다시 ControllerAction으로 돌아와서 

post요청과 get요청이 들어오면 requestPro라는 함수를 호출하여 처리합니다. 

requestPro함수에서는 사용자의 요청 URL을 분석하여 리소스 번들에 저장된 해당 액션 객체를 실행합니다.

해당 액션 객체의 실행이 끝나면 액션에서 리턴되는 뷰(파일경로, 이름)로 리턴합니다. 


ControllerAction은 끝났고 아까전에 만들어두었던 ListAction을 마저 완성하러 갑시다.

내용을 넣기전에 먼저 Command Action 인터페이스를 적용시켜 봅시다

Line 3 
public  class ListAction {

}

public class ListAction implements CommandAction{

}

으로 바꿔줍시다. 

그러면 라인앞에 전구표시가 뜨는데 Add unimplemented methods를 클릭하면 소스가 생깁니다.

이제 내용을 채워야하는데 이미 ListAction에서 동작할 소스는 만들어져 있습니다.

어딨냐면 list.jsp에 스크립트릿으로 존재합니다. 

list.jsp에 있는 스크립트릿을 복사하여 ListAction에 붙여넣기 합시다.

스크립트릿 처리는 ListAction에서 하기때문에 list.jsp에서는 더이상 스크립트릿이 필요하지 않습니다.

당연히 지워주어야합니다.

그리고 ListAction으로 온 소스에 out.print ~의 앞에 System.을 붙여줍시다. ( 콘솔창에 메시지가 떠요)

컨트롤러의 구성과 ListAction의 작업이 끝났습니다.

직접 손으로 다 쳐보시길 바랍니다.(오타를 잡으면서.......)

정상적으로 실행이 된다면 아래와 같이 나올겁니다.

URL을 보면 list.do로 요청이 가는 것을 볼 수 있습니다. list.jsp하면 아무것도 안떠요.

list.do로 흐름도를 보자면

사용자가 list.do 요청 -> web.xml에서 url을 파악하여 ControllerAction으로 전달 -> ControllerAction에서 Command.properties를 통해 맵핑, 요청 분석 -> ListAction으로 맵핑이 되고, 실행 -> view에 필요한 데이터를 .jsp로 리턴


이 포스팅에서는 ListAction만 구현되어 있습니다. Write, Delete, Modify등등 직접 구현해보시길 바랍니다.

여기까지가 MVC에서 C(컨트롤러)를 만들었습니다. 하지만 아직 ListAction에 데이터베이스 접속하는 로직이 포함되어있습니다.

데이터베이스 접속하는 로직은 Model으로 분리를 해주어야 MVC가 완성이 되겠습니다. 


이제 MVC 패턴 게시판을 만들어 봅시다.

코드는 1.6에서 올린 파일을 그대로 사용하겠습니다~

필요하신분은 이전 게시글가서 가져오세요.(기능상 똑같으나 미세하게 다름 DB 계정이나 비밀번호등..)

MVC는 소스의 분업화, 코드분리입니다. 

*.jsp파일들을보면은 형태가 다른 2가지로 구성이 되어 있는 것을 확인할 수 있습니다.

<% %> 구문과 아닌 구문들이 있지요. 이것을 구별하는 것이 시작입니다.

MVC에서는 스크립트릿을 src폴더안에 자바코드로 들어가게 됩니다.

그러면 .jsp에는 HTML코드와 자바스크립트 코드만 남게 됩니다.

스크립트릿은 src폴더안에 들어가게 되지요!

그럼 src폴더에 있는 스크립트릿이 어떻게 .jsp에 뿌려줄것인가? 가 문제겠죠. 뿌려주는건 JSP가 해야하니..그렇다면

JSP에서는 EL(Expression Language)라고하는 표현언어를 통해서 뿌려주게 됩니다.


jstl-1.2.jar


이 친구를 다운받아서 WebContent ->WEB-INF -> lib 폴더에 붙여넣으면됩니다. 

이제 이 라이브러리를 사용하기 위해선 

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

를 선언해줍시다.

사용법은 <%= %> 대신에 ${변수이름} 입니다.

for문 대신에 <c:forEach items="" var=""> <c:forEach>

item에는 for문으로 돌릴 리스트나 맵이 들어가며 var에는 iterator될 값들이 들어갑니다.

if문 대신에 <c:if test=""></c:if>

쌍따옴표 안에는 조건문


contets.jsp를 예제로 사용할건데

jstl을 쓰려면 각 소스에 흩어져있는 스크립트릿을 모아주어야 합니다.


**request 객체에 대해서 알아보고 갑시다.

request객체의 역할은 인코딩문제와 파라미터를 받는 역할뿐만아니라 MVC에서 중요한 역할인 dispatcher와 forward를 합니다. 

request.setAttribute()라는 메소드는 getParameter()와 반대로 페이지에서 해당 변수를 사용할 수 있게 하는 겁니다.


자 그럼 content.jsp파일을 고쳐봅시다. 한곳으로 모아주어야겠죠?


수정된 소스코드를 보면은 스크립트릿과 자바스크립트, HTML이 구분된것을 알 수 있습니다.

자 그럼 여기서 스크립트릿은 src폴더안에 java소스로 갈것이고, 이 java소스를 .jsp파일과 연결해야겠죠?

이 때 연결해주는 역할을 하는게 컨트롤러입니다.

컨트롤러의 역할은 해당 URL을 파악하고 해당하는 java액션(src폴더안에 java소스)파일을 찾아 매칭하고

해당 액션이 실행되면 request.setAttribute()에 의해 넘어온 값들을 jsp페이지로 포워딩시켜주는 역할입니다.

포워딩 역할을 해주기전에 먼저 index.jsp파일(블로그 설명에서는 list.jsp로 바꾼 파일)의 코드분리가 필요한데요.

content.jsp는 하나만 뿌려주면되는데 목록에서는 여러 개의 값을 가져와야합니다. 여러 개의값을 가져와야하니 어디다가

request.setAttribute()를 써야할지 애매하고.. 이때 쓰는 것이 Entity Beans라고 하는 디자인 패턴을 사용합니다.

Entity Beans를 쉽게 설명하면 DB에 가져온 데이터들을 담는 그릇이라고 생각하면 쉽습니다.(http://kimseunghyun76.tistory.com/327)

Entity Beans의 정의는 다음과 같습니다.

- DB에 저장된 데이터를 객체로 표현하기 위한 EJB Component
- Entity Bean의 내용은 DB의 내용과 일치
- Entity Bean하나의 객체는 DB테이블의 하나의 row와 mapping됨.
- DB의 하나의 행(recode,row,entity)은 여러 개의 열(column, attribute)을 가지고 있는데, 각각의 attribute는 Bean 객체의 멤버 필드에 값이 저장됨.

이제 이 Entity Bean를 만들어 볼건데요. src폴더에 패키지를 하나 생성합시다.


생성을 했다면 com.board.beans라는 패키지안에 Java파일을 만들어줍니다.  New->Class로 만들면 되요.



많이 보던 변수들이죠? 이제 이것들을 한데 묶어 사용할겁니다. 사용하기전에 제대로 사용하기 위해선 겟터와 셋터를 만들어주어야 하는데

일일이 치면은 엄청 귀찮겠죠? 이런 일을 대비해서 이클립스에서는 코드제너레이터 기능이 있습니다!

완전 편하게 게터와 셋터를 다 만들어 줍니다! (다른 곳에서도 써먹으세요)

Source -> Generate Getters and Setters -> 모두 선택 -> OK하면 게터와 셋터가 다 만들어집니다!

이제 사용할 수 있는 Entity Beans가 생겼으니까 이 빈을 사용하기 위해 코드 상단에 import를 시켜주어야겠죠?

이 Beans가 쓰일 곳은 index.jsp(list.jsp) 이니까 당연히 이 파일에 import시켜주어야 합니다.

<%@ page import="com.board.beans.board"%>

그리고 이 객체를 ArrayList에 담기위해 ArrayList도 import 합니다.

<%@ page import="java.util.ArrayList" %>

그리고 표현언어인 jstl도 import해주어야죠

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

자 이제 bean와 Arraylist쓸 준비도 되었겠다. 코드 분리를 해봅시다. content.jsp와 비슷하게 먼저 스크립트릿을 분리합니다.

코드를 분리한다음 스크립트릿에서 Bean와 ArrayList를 이용하여 값을 받아옵니다. 값을 받아오고나서 

뿌려줄 수 있도록 set한다음 html,javascript에서 jstl을 이용해 ${변수}로 출력하면되겠죠?

자 그럼 index.jsp(list.jsp)까지 수정되었고 잘 동작하는지 확인해봅시다. 잘 동작하기를 바라겠습니다.. 


저번 게시글까지는 Model1에 해당하는 내용이었습니다.


이번 게시글부터는 Model2방식을 사용할텐데요

Model2방식은 java의 디자인 패턴중 하나로 우리가 만들었던 소스들을 Model, View , Controller로 나누어 개발합니다.


Model은 데이터베이스와 접속을 관리(트랜젝션관리)하고 SQL을 제어하는 데이터베이스와 관련된 부분을 담당하며

View는 Model에서 가져온 데이터들을 화면에 뿌려주는 역할을 하고

Controller는 URL처리 및 파라미터, 기능제어 역할을 하게 됩니다.

이 3가지가 묶여서 MVC(Model2방식) 이라고 합니다.


이 패턴은 웹어플리케이션 개발뿐만아니라 여러 개발환경에서 쓰이고 있습니다.

MVC패턴 방식의 개발방법은 많은 이점이 있습니다.

1. 유지보수가 편해짐

2. 분업이 용이

3. 테스트가 쉬워짐

그리고 각 파트별로 지원되는 라이브러리들이 많이 존재하여 보다 풍부한 사용자경험(UX)과 인터페이스를 가진

Rich Internet Application(RIA)의 개발이 가능해집니다.


MVC에 대한 설명이 더 자세히 되어 있는 부분이 있어 대체 설명합니다.

출처 : http://egloos.zum.com/tory45/v/4928629


+ Recent posts