1.     
시작하기
-       
이번에는 게시판 리스트 보기에서 “글쓰기” 버튼을 눌렀을 때 게시물울 입력하는 화면과 함께 글을 
저장하는 부분을 개발하자.
-       
MultipartRequest를 사용한 파일 업로드를 위해 http://www.servlets.com/cos/ 에서 com.oreilly.servlet의 cos.jar 파일을 
다운받자.
2.     
새글 입력을 위한 DAO쪽 클래스를 작성하자.
BoardDAO.java 인터페이스, SpringBoardDAO.java 클래스에 
새 
글 
입력을 
위한 insertBoard() 메소드를 
추가하자.
소스 
코드 
중 
빨강색 
부분이 
새 
글 
입력을 
위해 
추가된 
부분
[BoardDAO.java]
package onj.board.dao;
import java.util.List;
import 
onj.board.model.BoardDTO;
import 
onj.board.model.CommentDTO;
import 
org.springframework.dao.DataAccessException;
public interface BoardDAO 
{
       //게시물 
리스트 
보기
       public 
List<BoardDTO> boardList() throws 
DataAccessException;
       //게시물 
본문 
미리보기
       public 
String preView(String seq) throws 
DataAccessException;
       //게시물 
본문 
읽기
       public 
BoardDTO readContent(String seq) throws 
DataAccessException;
       //읽은 
글의 
조회수 1증가
       public int 
updateReadCount(String seq) throws 
DataAccessException;
       //Comment저장
       public int 
insertComment(CommentDTO commentDTO) throws DataAccessException 
;
       //Comment조회
       public 
List<CommentDTO> commentList(String seq) throws 
DataAccessException;
       //게시글 
입력
       public int 
insertBoard(BoardDTO board) throws 
DataAccessException;
}
[SpringBoardDAO.java]
package onj.board.dao;
import 
java.sql.ResultSet;
import 
java.sql.SQLException;
import java.util.List;
import 
javax.sql.DataSource;
import 
onj.board.model.BoardDTO;
import 
onj.board.model.CommentDTO;
import 
org.springframework.dao.DataAccessException;
import 
org.springframework.jdbc.core.JdbcTemplate;
import 
org.springframework.jdbc.core.RowMapper;
public class SpringBoardDAO implements BoardDAO 
{
       private 
JdbcTemplate jdbcTemplate;
       public void 
setDataSource(DataSource dataSource) {
             
this.jdbcTemplate = new 
JdbcTemplate(dataSource);
       
}
       // /게시판 
전체 
리스트 
보기(list.html)
       public 
List<BoardDTO> boardList() throws DataAccessException 
{
             
List<BoardDTO> boardList = null;
             
String sql = "select * from board";
             
boardList = jdbcTemplate.query(sql, new RowMapper() 
{
                    
public Object mapRow(ResultSet rs, int rowNum) throws SQLException 
{
                           BoardDTO board = new 
BoardDTO();
                           
board.setSeq(rs.getInt("seq"));
                           
board.setName(rs.getString("name"));
                           
board.setPasswd(rs.getString("passwd"));
                           
board.setTitle(rs.getString("title"));
                           
board.setContent(rs.getString("content"));
                           
board.setFileName(rs.getString("filename"));
                           
board.setRegDate(rs.getString("regdate"));
                           
board.setReadCount(rs.getInt("readcount"));
                           
board.setReply(rs.getInt("reply"));
                           
board.setReply_step(rs.getInt("reply_step"));
                           
board.setReply_level(rs.getInt("reply_level"));
                           return 
board;
                    
}
             
});
             
return boardList;
       
}
       // 게시물 
본문내용 
미리보기(/preView)
       public 
String preView(String seq) throws DataAccessException 
{
             
String sql = "select * from board where seq = 
?";
             
String preContent = (String) 
jdbcTemplate.queryForObject(sql,
                           new Object[] { seq }, 
new RowMapper() {
                                 public Object 
mapRow(ResultSet rs, int rowNum)
                                              
throws SQLException {
                                        return 
rs.getString("content");
                                 
}
                           
});
             
return preContent;
       
}
       // 게시판 
상세보기, 게시글 
읽기
       public 
BoardDTO readContent(String seq) throws DataAccessException 
{
             
String sql = "select * from board where seq = 
?";
             
BoardDTO boardDTO = (BoardDTO) 
jdbcTemplate.queryForObject(sql,
                           new Object[] { seq }, 
new RowMapper() {
                                 public Object 
mapRow(ResultSet rs, int rowNum)
                    
                           throws 
SQLException {
                                        BoardDTO 
board = new BoardDTO();
                                        
board.setSeq(rs.getInt("seq"));
                                        
board.setName(rs.getString("name"));
                                        
board.setPasswd(rs.getString("passwd"));
                                        
board.setTitle(rs.getString("title"));
                                        
board.setContent(rs.getString("content"));
                                        
board.setFileName(rs.getString("filename"));
                                        
board.setRegDate(rs.getString("regdate"));
                                        
board.setReadCount(rs.getInt("readcount"));
                                        
board.setReply(rs.getInt("reply"));
                                        
board.setReply_step(rs.getInt("reply_step"));
                                        
board.setReply_level(rs.getInt("reply_level"));
                                        return 
board;
                                 
}
                           
});
             // 글 
조회수 1증가
             
this.updateReadCount(new 
Integer(boardDTO.getSeq()).toString());
             
return boardDTO;
       
}
       // 읽은 
글의 
조회수를 1증가
       public int 
updateReadCount(String seq) throws DataAccessException 
{
             
String sql = "update board set readcount = nvl(readcount,0) + 1 where seq 
= ?";
             
Object[] obj = { seq };
             
return jdbcTemplate.update(sql, obj);
       
}
       // 커맨트 
입력
       public int 
insertComment(CommentDTO commentDTO) throws DataAccessException 
{
             
String sql = "insert into comment_t(seq, name, comm) values (?, ?, 
?)";
             Object[] obj = { 
commentDTO.getSeq(), // 게시글순번
                           
commentDTO.getName(), // 작성자
                           
commentDTO.getComment() }; // 커맨트
             
return jdbcTemplate.update(sql, obj);
       
}
       // 커맨트 
조회
       public 
List<CommentDTO> commentList(String seq) throws DataAccessException 
{
             
String sql = "select * from comment_t where seq = 
?";
             
List<CommentDTO> commentList = 
jdbcTemplate.query(sql,
                           new Object[] { seq }, 
new RowMapper() {
                                 public Object 
mapRow(ResultSet rs, int rowNum)
                                              
throws SQLException {
                                        
CommentDTO commentDTO = new CommentDTO();
                                        
commentDTO.setName(rs.getString("name"));
                                        
commentDTO.setComment(rs.getString("comm"));
                                        return 
commentDTO;
                                 
}
                           
});
             
return commentList;
       
}
       // 글쓰기
       public int 
insertBoard(BoardDTO board) throws DataAccessException 
{
             
String sql = "insert into board values(board_seq.nextval , ? , ? , ? , ? 
, ? , sysdate , 0 , board_seq.currval , 0 , 
0)";
             if 
(board.getFileName() == null) {
                    
Object[] obj = { board.getName(), 
board.getPasswd(),
                                 
board.getTitle(), board.getContent(), "" 
};
                    
return jdbcTemplate.update(sql, obj);
             } 
else {
                    
Object[] obj = { board.getName(), 
board.getPasswd(),
                                 
board.getTitle(), board.getContent(), board.getFileName() 
};
                    
return jdbcTemplate.update(sql, obj);
             
}
       
}
}
3.  service 쪽 
클래스를 
만들어 
보자. 
소스 
코드 
중 
빨강색 
부분이 
새 
글 
입력을 
위해 
추가된 
부분
[BoardService.java]
소스 
코드 
중 
빨강색 
부분이 
새 
글 
입력을 
위해 
추가된 
부분
package 
onj.board.service;
import java.util.List;
import 
onj.board.model.BoardDTO;
import 
onj.board.model.CommentDTO;
/*
 * 게시판에서 
구현할 
기능을 
인터페이스로 
정의
 */
public interface BoardService 
{
       //게시판 
리스트 
보기
       public 
List<BoardDTO> boardList();      
       //게시물 
미리보기
       public 
String preView(String seq);
       //게시판 
본문 
내용보기, 게시글 
읽기
       public 
BoardDTO readContent(String seq);
       //커맨트 
입력
       public int 
insertComment(CommentDTO commentDTO);
       //커맨트 
조회
       public 
List<CommentDTO> commentList(String seq);
       //게시글 
입력
       public int 
insertBoard(BoardDTO board);
}
[SpringServiceImpl.java]
package 
onj.board.service;
import 
java.util.List;
import 
onj.board.dao.BoardDAO;
import 
onj.board.model.BoardDTO;
import 
onj.board.model.CommentDTO;
public class BoardServiceImpl implements BoardService 
{
    private 
BoardDAO boardDAO;
    public void 
setBoardDAO(BoardDAO boardDAO) {
        this.boardDAO = 
boardDAO;
    
}
    //게시물 
리스트 
보기
    public 
List<BoardDTO> boardList() {
        return 
boardDAO.boardList();
    
}
    //게시물 
본문 
내용 
미리보기
    public String 
preView(String seq) {
        return 
boardDAO.preView(seq);
    
}
    //게시글 
읽기
       public 
BoardDTO readContent(String seq) {
             
return boardDAO.readContent(seq);
       
}
       //커맨트 
입력
       public int 
insertComment(CommentDTO commentDTO) {
             
return 
boardDAO.insertComment(commentDTO);
       
}
       //커맨트 
조회
       public 
List<CommentDTO> commentList(String seq) 
{
             
return boardDAO.commentList(seq);
       }    
       //게시글 
입력
       public int 
insertBoard(BoardDTO board) {
             
return boardDAO.insertBoard(board);
       
}
}
4.     
이번에는 컨트롤러를 수정하자.
소스 
코드 
중 
빨강색 
부분이 
새 
글 
입력을 
위해 
추가된 
부분
[BoardMultiController.java]
package onj.board.controller;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import onj.board.model.BoardDTO;
import onj.board.model.CommentDTO;
import onj.board.service.BoardService;
import org.springframework.web.servlet.ModelAndView;
import 
org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;
/*
 * 
MultiActionController는 
비슷하거나 
관련있는 
로직을 
수행하는 
 * 다수의 
액션을 
가지고 
있을 
때 
사용하는 
컨트롤러
 * 연관된 
요청(Request)를 
묶을 
때 
용이함
 */
public class BoardMultiController extends MultiActionController {
       private BoardService boardService;
       public void setBoardService(BoardService boardService) {
             this.boardService = boardService;
       }
       // 게시판 
리스트 
보기, 페이징 
기능은 
구현 
안함
       public ModelAndView list(HttpServletRequest req, HttpServletResponse 
res)
                    
throws Exception {
             ModelAndView mv = 
new ModelAndView("list", "list",
                           
boardService.boardList());
             return mv;
       }
       // 게시글 
읽기
       public ModelAndView read(HttpServletRequest req, HttpServletResponse 
res)
                    
throws Exception {
             String seq = 
req.getParameter("seq");
             ModelAndView mav = 
new ModelAndView("read", "read",
                           
boardService.readContent(seq));
             // 해당 
글의 
커맨트도 
함께 
내려 
보내자.
             
mav.addObject("comments", boardService.commentList(seq));
             return mav;
       }
       // 커맨트쓰기
       public ModelAndView comment(HttpServletRequest req, HttpServletResponse 
res) {
             String seq = 
req.getParameter("seq");
             CommentDTO 
commentDTO = new CommentDTO();
              commentDTO.setSeq(seq);
             
commentDTO.setName(req.getParameter("name"));
             
commentDTO.setComment(req.getParameter("comment"));
             boardService.insertComment(commentDTO);
             return new ModelAndView("redirect:/read.html?seq=" + seq);
       }
// 새글(게시글) 입력 
화면
       public ModelAndView 
write(HttpServletRequest req, HttpServletResponse 
res)
                    throws Exception 
{
       return new 
ModelAndView(“write”);"
}
       // 새글(게시글) DB 입력
       public ModelAndView 
writeok(HttpServletRequest req, HttpServletResponse 
res)
                    
throws Exception 
{
             
MultipartRequest multi = new MultipartRequest(req, 
"c:\\java\\project\\onjboard1\\upload",
                           5 * 1024 * 1024, 
"euc-kr", new 
DefaultFileRenamePolicy());
             
Enumeration formNames = 
multi.getFileNames();
             
String formName = (String) 
formNames.nextElement();
             
String fileName = 
multi.getFilesystemName(formName);
             
String name = 
multi.getParameter("name");
             
String passwd = 
multi.getParameter("passwd");
             
String title = 
multi.getParameter("title");
             
String content = 
multi.getParameter("content");
             
BoardDTO board = new 
BoardDTO(name, passwd, title, content, 
fileName);
             
boardService.insertBoard(board);
             return new 
ModelAndView("redirect:/list.html");
       
}
}
5.     
리스트 보기에서 글쓰기 버튼 클릭시 호출될 write.jsp를 작성하자.
[write.jsp]
<%@ page contentType="text/html; 
charset=euc-kr" language="java" 
errorPage="" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 
4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>게시물 
쓰기</title>
<meta http-equiv="Content-Type" content="text/html; charset=euc-kr">
<script language="javascript" type="text/javascript">
       function form_check(){
             if(document.form.name.value == ""){
                           
alert("이름을 
입력하세요.");
                           
document.form.name.focus();
                           
return false;
             }
             if(document.form.title.value == ""){
                           
alert("제목을 
입력하세요.");
                           
document.form.title.focus();
                           
return false;
             }
             if(document.form.content.value == ""){
                           
alert("내용을 
입력하세요.");
                           
document.form.content.focus();
                           
return false;
             }            
             if(document.form.passwd.value == ""){
                           
alert("비밀번호를  입력하세요.");
                           
document.form.passwd.focus();
                           
return false;
             }
       
document.form.submit();
       }
function addFileForm(){
  var tb1 = document.getElementById("file_table");
  if(9 >= tb1.rows.length) {
   var idx = getObj().parentElement.rowIndex + 1;
   var trow= tb1.insertRow(idx);
   var uploadOBJ="<input name='attatch' type='file' class='TEXT_FORM' 
id='f_id'><a onClick='javascript:addFileForm();'> 추가</a>  <a onClick='javascript:deleteRow();'>삭제</a> ";
   
trow.insertCell(0).innerHTML = uploadOBJ;
  } else {
   alert("문서파일은 10개 
이상 
접수할 
수  없습니다.");
   return;
  }
 }
 function getObj()
 {
     var obj = event.srcElement
     while (obj.tagName !='TD') //TD가 
나올때까지의 Object추출
     {
         obj = 
obj.parentElement
     }
     return obj
 }
 function deleteRow(){
  var tb1 = document.getElementById("file_table");
  var idx = getObj().parentElement.rowIndex;
  if(tb1.rows.length-1 !=0){
   var tRow = tb1.deleteRow(idx);
  }else{
    
document.getElementById('f_id').select();
       
document.selection.clear();
  }
 }
</script>
</head>
<body>
<H3>오엔제이 
프로그래밍 
실무교육센터 
스프링 
게시판 
글쓰기</H3>
<div style="width:600px;">
<div style="float:right;">
<!--  /writeok.html 
요청의 
경우 
컨트롤로의 write 메소드가 
실행되도록 
매핑되어있다 -->
<form name="form" method="post" action="/onjboard1/writeok.html" enctype="multipart/form-data">
<table width="580" 
height="277" 
border="1" 
align="center">
  <tr>
    <td width="100">* 이 
름</td>
    <td width="580">: <input name="name" type="text" size="50">
      </td>
  </tr>
  <tr>
    <td>* 제 
목</td>
    <td>:
    <input name="title" 
type="text" 
size="50"></td>
  </tr>
  <tr align="center">
    <td colspan="2"><textarea 
name="content" cols="80" rows="10"></textarea></td>
  </tr>
  <tr>
    <td>* 파 
일  :</td>
    <td><table id="file_table">
                           
<tr>
                                 
    <td>
                                 
                <input name="attatch" type="file" class="TEXT_FORM" id="f_id"><a OnClick="javascript:addFileForm();"> 추가</a>  <a OnClick="javascript:deleteRow();">삭제</a>
                                 
    </td>
                       </tr>
               </table>
    </td>
  </tr>
  <tr>
    <td>* 비밀번호</td>
    <td>:
      <input type="password" name="passwd"></td>
  </tr>
  <tr>
    <td> </td>
    <td><input type="button" 
name="Submit" 
value="쓰기" onclick="form_check();">
      <input type="button" 
name="Submit2" value="취소" onclick="history.back();"></td>
  </tr>
</table>
</div>
</div>
</form>
</body>
</html>
6.     
list.jsp  맨 아래 부분 글쓰기 버튼 링크 주소를 
변경하자.
[list.jsp]
<%@ page contentType="text/html; 
charset=euc-kr" language="java" 
errorPage="" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>오엔제이 
프로그래밍 
실무학원</title>
<meta http-equiv="Content-Type" content="text/html; charset=euc-kr">
<style type="text/css">
       #layer1{
             position:absolute;
             padding:5px;
             filter:alpha(opacity=50);
             width:250px; height:150px;
             background-color:white;
             border:2px #000000 dotted;
             visibility:hidden;
       }
</style>
<script language="javascript" type="text/javascript" src="/onjboard1/js/createXMLHttpRequest.js"></script>
<script type="text/javascript">
       var xmlHttp;
       var xmlDoc;
       var message;
       function contentprev(seq){
             var url = "preView?seq="+seq;  //미리보기 
서블릿 
호출
             xmlHttp = 
createXMLHttpRequest();
             
xmlHttp.onreadystatechange = handleStateChange;
             
xmlHttp.open("get" , url , true);
             
xmlHttp.send(null);
       }
       function handleStateChange(){
             if(xmlHttp.readyState == 4){
                           
if(xmlHttp.status == 200){
                                        xmlDoc = 
xmlHttp.responseText;
                                        
document.getElementById("layer1").innerHTML = xmlDoc;
                           
}
             }
       }
       function showlayer(id){    
             if(document.all)
                    
document.all[id].style.visibility="visible";
             else if(document.layers)
                    
document.layers[id].style.visibility="visible";             
       }
       function hidelayer(id){    
             if(document.all)
                    
document.all[id].style.visibility="hidden";
             else if(document.layers)
                    
document.layers[id].style.visibility="hidden";       
       }
    function movetip() {
        
layer1.style.pixelTop=event.y+document.body.scrollTop+10;
        
layer1.style.pixelLeft=event.x+document.body.scrollLeft+10;
    }
    
document.onmousemove=movetip;    
           
</script>
</head>
<body> 
<div id="layer1">
             게시물 
본문 
미리 
보기
</div>
<div style="width:500px;">
<div style="float:right;">
<H3>오엔제이 
프로그래밍 
실무교육센터 
스프링 
게시판</H3>
<h5>총 ${list.size()}건</h5>
<table width="600" 
border="1" 
align="left">
  <tr align="left">
    <td width="10%" 
align="center">번호</td>
    <td width="40%" 
align="center">제목</td>
    <td width="20%" 
align="center">이름</td>
    <td width="20%" 
align="center">날짜</td>
    <td width="10%" 
align="center">조회</td>
  </tr>
              <!-- list를 
가져와서 
board라 
명명 
후 
개 
수 
만큼 
반복 -->
             <c:forEach var="board" 
items="${list}">       
  <tr>
    <td align="center">
                    
<c:if test="${board.reply_step == 0}">
                                 
${board.seq}
                    
</c:if>
                    
<c:if test="${board.reply_step != 0}">
                    
</c:if>                    
    </td>
    <td>      <!-- 게시물은 
덧글에 
따른 
번호와 
덧글 
존재 
유무로 
정렬됨 -->
                    
<c:choose>                 
                           
<c:when test="${board.reply_step != 0}"><!-- 게시글이 
덧글일 
경우 -->
                                 
<c:forEach var="i" 
begin="1" 
end="${board.reply_level}" step="1"><!-- 레벨의 
수만큼 
글을 
뒤로 
민다 -->
                                 
</c:forEach>
                                        
<a href="read.html?seq=${board.seq}" onmouseover="contentprev('${board.seq}');showlayer('layer1');" onmouseout="hidelayer('layer1');">${board.title}</a>
                                        
<!-- 마우스를 
올리면 
게시물 
번호에 
따른 
showlayer(게시물 
미리보기 
창)가 
실행됨 -->
                           
</c:when>
                           
<c:when test="${board.reply_step == 0}">
                                        
<a href="read.html?seq=${board.seq}" onmouseover="contentprev('${board.seq}');showlayer('layer1');" onmouseout="hidelayer('layer1');">${board.title}</a>                     
                           
</c:when>
                    
</c:choose>
   </td>
    <td align="center">${board.name}</td>
    <td align="center">${board.regdate}</td>
    <td align="center">${board.readCount}</td>
  </tr>
             </c:forEach>
  <tr>
    <td align="center"><input 
type="button" 
value="글쓰기" onclick="location.href='write.html'"></td>
    <td> </td>
    <td> </td>
    <td> </td>
    <td> </td>
  </tr>
</table>
</div>
</div>
</body>
</html>
7.     
action-servlet.xml을 수정하자.
소스 
코드 
중 
빨강색 
부분이 
커멘트 
조회, 입력 
기능을 
위해 
추가된 
부분
<?xml version="1.0" 
encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD 
BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
       <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
             destroy-method="close">
             <property name="driverClassName">
                    
<value>oracle.jdbc.driver.OracleDriver</value>
             </property>
             <property name="url">
                    
<value>jdbc:oracle:thin:@127.0.0.1:1521:onj</value>
             </property>
             <property name="username">
                    
<value>scott</value>
             </property>
             <property name="password">
                    
<value>tiger</value>
             </property>
       </bean>
       <!-- 넘어오는 URL에 
따라 
컨트롤러에서 
실행될 
메소드 
매핑 -->
       <!-- PropertiesMethodNameResolver는 prop key로 
넘어오는 
url에 
대해 
실행할 
컨트롤러의 
메소드 
             정의 -->
       <bean id="userControllerMethodNameResolver"
              class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
             <property name="mappings">
                    
<props>
                           
<!-- list.html 요청이 
오면 
컨트롤러의 list 메소드 
실행 -->
                           
<prop key="/list.html">list</prop>
                           
<!-- read.html 요청이 
오면 
컨트롤러의 read 메소드 
실행 -->
                           
<prop key="/read.html">read</prop>
                           
<!-- comment.html 요청이 
오면 
컨트롤러의 comment 메소드 
실행 -->
                           
<prop key="/comment.html">comment</prop>
<!-- writeok.html 요청이 
오면 
실행 -->
                           
<prop 
key="/writeok.html">writeok</prop>
<!-- write.html 요청이 
오면 
메소드 
실행 -->
                           
<prop key="/write.html">write</prop>                               
                    
</props>
             </property>
       </bean>
       <!-- 뷰 
리졸버 -->
       <bean id="viewResolver"
             class="org.springframework.web.servlet.view.InternalResourceViewResolver">
             <property name="prefix">
                    
<value>/jsp/</value>
             </property>
             <property name="suffix">
                    
<value>.jsp</value>
             </property>
       </bean>
       <!-- 컨트롤러 
매핑 -->
       <bean name="/list.html 
/read.html /comment.html /write.html /writeok.html" class="onj.board.controller.BoardMultiController">
             <property name="methodNameResolver">
                    
<ref local="userControllerMethodNameResolver" />
             </property>
             <property name="boardService">
                    
<ref bean="boardService" />
             </property>
       </bean>
</beans>
실행결과 
및 현재까지 만들어진 이클립스 구조
(게시판 리스트보기 + 게시물 
본문내용 미리 보기 + 게시글 상세보기 + 
커멘트기능 + 글쓰기)
:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" 
/>

  [개강확정강좌]오라클자바커뮤니티에서 운영하는 개발자 전문교육 ,개인80%환급(www.onjprogramming.co.kr)
[주간]
[11/4]Spring3.X, MyBatis, Hibernate실무과정
[11/6]SQL초보에서실전전문가까지
[평일야간]
[11/1]C#,ASP.NET마스터
[11/5]iPhone 하이브리드 앱 개발 실무과정
[11/7]JAVA&WEB프레임워크실무과정
[11/8]Spring3.X, MyBatis, Hibernate실무과정
[주말]
[11/2]C#,ASP.NET마스터
[11/2]Spring3.X, MyBatis, Hibernate실무과정
[11/2]JAVA&WEB프레임워크실무과정
[11/9]안드로이드개발자과정
[주간]
[11/4]Spring3.X, MyBatis, Hibernate실무과정
[11/6]SQL초보에서실전전문가까지
[평일야간]
[11/1]C#,ASP.NET마스터
[11/5]iPhone 하이브리드 앱 개발 실무과정
[11/7]JAVA&WEB프레임워크실무과정
[11/8]Spring3.X, MyBatis, Hibernate실무과정
[주말]
[11/2]C#,ASP.NET마스터
[11/2]Spring3.X, MyBatis, Hibernate실무과정
[11/2]JAVA&WEB프레임워크실무과정
[11/9]안드로이드개발자과정
 

 
 
 
 
 
댓글 없음:
댓글 쓰기