티스토리 뷰

JAVA

웹크롤링/ 이미지보드 프로젝트

장꾸꾸 2020. 11. 20. 16:12

다운로드
step11_Crawler.zip
0.32MB

 

 

 

이미지보드 프로젝트

 

* nullPoint 오류 날 때 코드 먼저 보기 전에 lib에 드라이버들 잘 들어가있는지 먼저 확인한다.

step11_imageBoard.txt
0.00MB

web

/imgboard

web

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="/board/imgboard/imageBoardWrite.html">글쓰기</a><br><br>
<a href="/board/imgBoardList.do?pg=1">게시판</a>
</body>
</html>

/imgboard

imageBoardList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title></head>
<link rel="stylesheet" href="/board/imgboard/styleImageBoard.css">
<script>
function imageBoardPaging(pg){
	location.href="/board/imgBoardList.do?pg="+pg;
}
function imgSelectCheck(){
	var array=document.getElementsByName("idx");
	var cnt=0;
	for(var i=0;i<array.length;i++){
		if(array[i].checked==true){
			cnt++;
		}
	}
	
	if(cnt==0){
		alert("삭제할 사진을 선택해 주세요");
	}else{
		document.deleteform.submit();
	}
}
</script>
</head>
<body>

<form name="deleteform" action="/board/imgBoardDelete.do"  method="post">
<table>
<tr>
	<td colspan="7" bgcolor="777777"></td>
</tr>

<tr align="center" height="35">
	<td width="100"><b>번 호</b></td>
	<td width="100"><b>이미지</b></td>
	<td width="150"><b>상품명</b></td>
	<td width="100"><b>단 가</b></td>
	<td width="50"><b>개 수</b></td>
	<td width="100"><b>합 계</b></td>
	<td width="150"><b>작성일</b></td>
</tr>

<tr>
	<td colspan="7" bgcolor="777777"></td>
</tr>
<c:forEach var="imageBoardDto" items="${list}">
<tr>
	<td>
		<input type="checkbox"  name="idx" value="${imageBoardDto.seq}">&nbsp;&nbsp;
		${imageBoardDto.seq}
	</td>
	<td class="imageA">
	<a href="">
	<img src="/board/storage/${imageBoardDto.imagePath}" 
	width="50" height="50" border="0"></a>
	</td>
	<td>
	<a href="#" class="imageA">${imageBoardDto.imageName}</a>
	</td>
	<td><fmt:formatNumber  groupingUsed="true"  value="${imageBoardDto.imagePrice}"/>원</td>
	<td><fmt:formatNumber  groupingUsed="true"  value="${imageBoardDto.imageQty}"/>개</td>
	<td><fmt:formatNumber  groupingUsed="true"  
	                       value="${imageBoardDto.imagePrice*imageBoardDto.imageQty}"/>원</td>
	<td>${imageBoardDto.logTime}</td>
</tr>

<tr>
	<td colspan="7" bgcolor="cccccc"></td>
</tr>
</c:forEach>

<tr>
	<td colspan="7" bgcolor="777777"></td>
</tr>
<tr>
	<td>
		<input type="hidden" name="pg" value="${pg}">   <!-- 삭제 후 현재 페이지를 보여주기 위해 -->
		<input type="button" value="선택삭제" onclick="imgSelectCheck()">
		<input type="button" value="글쓰기" 
		       onclick="location.href='/board/imgboard/imageBoardWrite.html'">
	</td>
	<td colspan="6" align="center">${imageBoardPaging.pagingHTML}</td>
</tr>
</table>
</form>
</body>
</html>

imageBoardDeleteOK.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script>
	alert("게시글이 삭제 되었습니다");
	location.href="/board/imgBoardList.do?pg=" + ${pg};
</script>
</body>
</html>

imageBoardWrite.html

<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<title>이미지 업로드</title></head>
<script src="scriptImageBoard.js" charset="UTF-8"></script>
</head>
<body>
	<h3>이미지 업로드</h3>
	<hr/>
	<form name="imageBoardWriteForm" method="post" enctype="multipart/form-data" 
	action="/board/imgBoardWrite.do" >
	<table cellpadding="3">
		<tr>
			<td><b>상품코드</b></td>
			<td><input type="text" name="imageId"></td>
		</tr>
		<tr>
			<td><b>상품명</b></td>
			<td><input type="text" name="imageName"></td>
		</tr>
		<tr>
			<td><b>단가</b></td>
			<td><input type="text" name="imagePrice"></td>
		</tr>
		<tr>
			<td><b>개수</b></td>
			<td><input type="text" name="imageQty"></td>
		</tr>
		<tr>
			<td><b>내용</b></td>
			<td>
			<textarea rows="12" cols="40" name="imageContent"></textarea>
			</td>
		</tr>
		<tr>
			<td><b>이미지</b></td>
			<td><input type="file" name="imagepath" size="40"></td>
		</tr>
		<tr>
			<td colspan="2" align="center">
			<input type="button" value="이미지등록" onclick="checkImageBoardWrite()">
			<input type="reset" value="취소">
			<input type="button" value="목록" onclick="location.href='/board/imgBoardList.do?pg=1'">
			</td>
		</tr>
	</table>
	</form>
	<hr/>
</body>
</html>

imageBoardWriteOk.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script>
	alert("이미지 등록 완료");
	location.href="/board/imgBoardList.do?pg=1";
</script>
</body>
</html>

scriptImageBoard.js

function checkImageBoardWrite() {
	if(document.imageBoardWriteForm.imageId.value=="")
		alert("상품코드를 입력하세요");
	else if(document.imageBoardWriteForm.imageName.value=="")
		alert("상품명을 입력하세요");
	else if(document.imageBoardWriteForm.imagePrice.value=="")
		alert("상품 단가를 입력하세요");
	else if(document.imageBoardWriteForm.imageQty.value=="")
		alert("상품 개수를 입력하세요");
	else if(document.imageBoardWriteForm.imageContent.value=="")
		alert("내용을 입력하세요");
	else if(document.imageBoardWriteForm.imagepath.value=="")
		alert("상품 이미지를 첨부하세요");
	else
		document.imageBoardWriteForm.submit();
}

styleImageBoard.css 

@CHARSET "UTF-8";
th {font-size:12pt}
td {
	font-size:12pt;
	text-align: center;
}

a:LINK {color: black; text-decoration: none;}
a:ACTIVE {color: black; text-decoration: none;}
a:VISITED {color: black; text-decoration: none;}
a:HOVER {color: black; text-decoration: underline;}

/* 순서를 지켜라-우선순위가 맨밑에서부터이다  */
.imageA:LINK {color: black; text-decoration: none;}
.imageA:ACTIVE {color: black; text-decoration: none;}
.imageA:VISITED {color: black; text-decoration: none;}
.imageA:HOVER {color: green; text-decoration: underline;}

#pagingSpan{
	font-size:11pt;
	cursor:pointer;
}
#currentPagingSpan{
	font-size:11pt;
	color:red;
	cursor:pointer;
}

 

/storage

/table_schema

imgboard.sql

drop table imageboard purge;

create table  imageboard(
seq number primary key,                 --번호
imageid varchar(15) not null,           --상품코드 
imageName varchar2(20) not null,        --상품명
imagePrice  number,                     --단가
imageQty  number,                       --수량
imagecontent  varchar2(2000),           --내용  
imagepath  varchar2(80),                --이미지경로
logtime  date);                         --작성일

create sequence seq_imageboard increment by 1  start with 1 nocycle nocache;

select * from imageboard;
delete from IMAGEBOARD where seq=59;


INSERT INTO IMAGEBOARD VALUES(
SEQ_IMAGEBOARD.NEXTVAL,'a002','cat',1200,3,'고양이 구입','s16.jpg',SYSDATE);

INSERT INTO IMAGEBOARD VALUES(
SEQ_IMAGEBOARD.NEXTVAL,'a003','flower',2300,2,'해바라기 구입','s11.jpg',SYSDATE);

INSERT INTO IMAGEBOARD VALUES(
SEQ_IMAGEBOARD.NEXTVAL,'a004','기린',1000,1,'기린 구입','s16.jpg',SYSDATE);

INSERT INTO IMAGEBOARD VALUES(
SEQ_IMAGEBOARD.NEXTVAL,'a005','dog',2300,2,'강아지 구입','s16.jpg',SYSDATE);

INSERT INTO IMAGEBOARD VALUES(
SEQ_IMAGEBOARD.NEXTVAL,'a006','하트',3300,4,'하트 구입','s4.jpg',SYSDATE);

delete from IMAGEBOARD where seq>=3  and seq<=7;


select seq,imageId, imagename,imageprice, imageqty,
       imagecontent, imagepath,
       to_char(logtime,'YYYY.MM.DD')as logtime
from (select rownum rn, aa.*
      from (select * from imageboard order by seq desc) aa)
where rn>=4 and rn<=6;      

/WEB-INF

commandPro.properties

/imgBoardWriteForm.do=com.bcu.action.ImageBoardWriteFormAction
/imgBoardWrite.do=com.bcu.action.ImageBoardWriteAction
/imgBoardList.do=com.bcu.action.ImageBoardListAction
/imgBoardDelete.do=com.bcu.action.ImageBoardDeleteAction

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>step11_imageBoard</display-name>
  <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>
  <servlet>
  	<servlet-name>ControlServlet</servlet-name>
  	<servlet-class>com.bcu.controller.ControlServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>ControlServlet</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

 

 

SRC

/com.bcu.action

CommandProcess.java

package com.bcu.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface CommandProcess {
	String requestPro(HttpServletRequest request, HttpServletResponse response) throws Throwable;
}

ImageBoardDeleteAction.java

package com.bcu.action;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.bcu.dao.ImageBoardDao;
import com.bcu.entity.ImageBoardEntity;


public class ImageBoardDeleteAction implements CommandProcess{

	@Override
	public String requestPro(HttpServletRequest request, HttpServletResponse response) throws Throwable {
		int page=Integer.parseInt(request.getParameter("pg"));
		String []str=request.getParameterValues("idx");  // [9, 8, 7]  
		
		if(str==null) {
			request.setAttribute("pg", page);
			request.setAttribute("result", str);
			return "imgboard/imageBoardDeleteOK.jsp";
		}
		
		List<Integer> list=new ArrayList<>();
		
		for(String temp : str)
		{
			list.add(Integer.parseInt(temp));
		}
		
		ImageBoardDao dao=ImageBoardDao.getInstance();		
		
		// storage 폴더의 파일 삭제------------------------------------
		List<ImageBoardEntity> imgPathList=dao.getImagePath(list);
		String realFolder=request.getServletContext().getRealPath("/storage");
		
		
		// 데이타베이스의 데이타삭제-------------------------------------
		int n=dao.imageBoardDelete(list);
		
		if(n > 0)
		{
			for(ImageBoardEntity entity : imgPathList){
				String path=realFolder + "/" + entity.getImagePath();
				if(fileIsLive(path)){
					fileDelete(path);
				}else{
					System.out.println("파일이 존재하지 않습니다");
				}
			}
			//request객체에 담기--------------------
			request.setAttribute("pg", page);
			//------------------------------------
			return "imgboard/imageBoardDeleteOK.jsp";
			
		}else{
			return "imgBoardList.do?pg=" + page;
		}	
	}
	
	//파일의 존재 여부를 확인하는 메서드---------------------------------
	public boolean fileIsLive(String isLiveFile){
		boolean existFile=false;
		
		try{
			File file=new File(isLiveFile);
			if(file.exists())
				existFile=true;
		}catch(Exception e){
			e.printStackTrace();
		}		
		return existFile;
	}
	//파일을 삭제하는 메서드--------------------------------------------
	public void fileDelete(String fileName){
		try{
			File file =new File(fileName);
			file.delete();
		}catch(Exception e){
			System.out.println("파일삭제가 실패 하였습니다");
		}
	}	
}

ImageBoardListAction.java

package com.bcu.action;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.bcu.dao.ImageBoardDao;
import com.bcu.dao.ImageBoardPaging;
import com.bcu.entity.ImageBoardEntity;

public class ImageBoardListAction implements CommandProcess{

	@Override
	public String requestPro(HttpServletRequest request, HttpServletResponse response) throws Throwable {
		int pg=Integer.parseInt(request.getParameter("pg"));
		
		//DB-Select
		ImageBoardDao dao=ImageBoardDao.getInstance();
		int endNum=pg*3;
		int startNum=endNum-2;
		
		Map<String,Object> map=new HashMap<>();
		map.put("startNum", startNum);
		map.put("endNum", endNum);
		List<ImageBoardEntity> list=dao.getImageBoardList(map);
		
		//페이징처리                                                       현재페이지,표시할 페이지수,출력할게시물의갯수
		ImageBoardPaging paging=new ImageBoardPaging(pg ,3,3);
		paging.makePagingHTML();                 // pg를 1 2 3으로 수정해 볼것
		
		//request객체에 등록
		request.setAttribute("list", list);
		request.setAttribute("pg", pg);
		request.setAttribute("imageBoardPaging", paging);
		
		return "imgboard/imageBoardList.jsp";
	}
}

ImageBoardWriteAction.java

package com.bcu.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.bcu.dao.ImageBoardDao;
import com.bcu.entity.ImageBoardEntity;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;

public class ImageBoardWriteAction implements CommandProcess{

	@Override
	public String requestPro(HttpServletRequest request, HttpServletResponse response) throws Throwable {
		
		//실제 저장 경로
		String realFolder=request.getServletContext().getRealPath("/storage");
		//System.out.println("저장 폴더:" + realFolder);
		
		//파일업로드
		MultipartRequest multi=new MultipartRequest(
                request, realFolder, 5*1024*1024, "UTF-8",new DefaultFileRenamePolicy());
		
		//데이터 얻어오기
		String imageId=multi.getParameter("imageId");
		String imageName=multi.getParameter("imageName");
		int imagePrice=Integer.parseInt(multi.getParameter("imagePrice"));
		int imageQty=Integer.parseInt(multi.getParameter("imageQty"));
		String imageContent=multi.getParameter("imageContent");
		String imagePath=multi.getFilesystemName("imagepath");
		
		//데이터 저장
		ImageBoardEntity entity=new ImageBoardEntity();
		entity.setImageId(imageId);
		entity.setImageName(imageName);
		entity.setImagePrice(imagePrice);
		entity.setImageQty(imageQty);
		entity.setImageContent(imageContent);
		entity.setImagePath(imagePath);
		
		//DB-insert
		ImageBoardDao dao=ImageBoardDao.getInstance();
		int n=dao.imageBoardInsert(entity);
		
		if(n>0)
			return "imgboard/imageBoardWriteOk.html";
		else
			return "imgboard/imageBoardWrite.html";
	}
}

ImageBoardWriteFormAction.java

package com.bcu.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ImageBoardWriteFormAction implements CommandProcess{

	@Override
	public String requestPro(HttpServletRequest request, HttpServletResponse response) throws Throwable {
		
		return null;
	}

}

 

/com.bcu.controller

ControlServlet.java

package com.bcu.controller;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.bcu.action.CommandProcess;

@SuppressWarnings("serial")
public class ControlServlet extends HttpServlet{
	@SuppressWarnings("rawtypes")
	Map map = new HashMap();
		
	public void init(){}
	public void init(ServletConfig config){
		String  property=config.getServletContext().getRealPath("/WEB-INF/commandPro.properties");
//		System.out.println("property="+property);
		
		FileInputStream fin=null;
		Properties properties = new Properties();
		
		try {
			fin = new FileInputStream(property);
			properties.load(fin);
//			System.out.println("properties="+properties);
			
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				fin.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		Iterator it = properties.keySet().iterator();
		while(it.hasNext()){
			String key = (String)it.next();
//			System.out.println("key="+key);
			
			String className = properties.getProperty(key);
//			System.out.println("className="+className);
			
			try {
				Class classType = Class.forName(className);
				Object ob = classType.newInstance();
				
				map.put(key, ob);//맵에 저장
				
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}//while
	}//init()
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{
		execute(request,response);
	}
	
	public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{
		execute(request,response);
	}

	public void execute(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{
//		System.out.println("\n-------------------");
		
		//한글처리
		if(request.getMethod().equals("POST")){
			request.setCharacterEncoding("UTF-8");
		}
		
		//요청
		String category = request.getServletPath();
//		System.out.println("category = " + category);
		
		CommandProcess command = (CommandProcess) map.get(category);
//		System.out.println("command = " + command);
		
		String view=null;
		try {
			view = command.requestPro(request, response);
		} catch (Throwable e) {
			e.printStackTrace();
		}
		
		//응답
		RequestDispatcher dispatcher = request.getRequestDispatcher(view);//상대번지
		dispatcher.forward(request, response);//제어권넘기기
	}	
	
}

/com.bcu..dao

ImageBoardDao.java

package com.bcu.dao;

import java.io.IOException;
import java.io.Reader;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.bcu.entity.ImageBoardEntity;

public class ImageBoardDao {
	private static ImageBoardDao instance;
	private static SqlSessionFactory factory;
	
	//Mybatis연결객체 생성--------------------------------------
	static {
		try {
			String resource="mybatis/mybatis-config.xml";
			Reader reader=Resources.getResourceAsReader(resource);
			factory=new SqlSessionFactoryBuilder().build(reader);
		}catch(IOException e) {
			e.printStackTrace();
		}
	}
	//dao 객체를 한번만 생성해서 사용(singleton방식)-------------------- 
	public static ImageBoardDao getInstance() {
		if(instance==null) {
			synchronized (ImageBoardDao.class) {  //동기화 처리
				instance=new ImageBoardDao();
			}
		}
		return instance;
	}
	//---------------------------------------------------------
	public int imageBoardInsert(ImageBoardEntity entity) {
		SqlSession session=factory.openSession();
		int n=0;
		
		try{
			n=session.insert("mybatis.ImageBoardMapper.imageBoardInsert", entity);
			if(n > 0)
				session.commit();
		}catch(Exception e){
			session.rollback();
		}finally{
			session.close();
		}
		return n;
	}
	//---------------------------------------------------------
	public int getTotalArticle() {   //총 게시물의 갯수
		SqlSession session=factory.openSession();
		int n=session.selectOne("mybatis.ImageBoardMapper.getTotalArticle");
		session.close();
		return n;
	}
	//--------------------------------------------------------
	public List<ImageBoardEntity> getImageBoardList(Map<String, Object> map) {
		SqlSession session=factory.openSession();
		List<ImageBoardEntity> list=session.selectList("mybatis.ImageBoardMapper.getImageBoardList", map);
		session.close();
		return list;
	}
	//--------------------------------------------------------
	public List<ImageBoardEntity> getImagePath(List<Integer> list) {
		SqlSession session=factory.openSession();
		List<ImageBoardEntity> imgPathList
		          =session.selectList("mybatis.ImageBoardMapper.getImagePath", list);
		session.close();
		return imgPathList;
	}
	//--------------------------------------------------------
	public int imageBoardDelete(List<Integer> list) {
		int n=0;
		SqlSession session=factory.openSession();
		
		try {
			n=session.delete("mybatis.ImageBoardMapper.imageBoardDelete",list);
			if(n > 0)
				session.commit();
		}catch(Exception e) {
			session.rollback();
		}finally{
			session.close();
		}
		return n;
	}

}

/com.bcu.entity

ImageBoardEntity.java

package com.bcu.entity;

//getter & setter
public class ImageBoardEntity {
	private int seq;
	private String imageId;
	private String imageName;
	private int imagePrice;
	private int imageQty;
	private String imageContent;
	private String imagePath;
	private String logTime;
	
	public int getSeq() {
		return seq;
	}
	public void setSeq(int seq) {
		this.seq = seq;
	}
	public String getImageId() {
		return imageId;
	}
	public void setImageId(String imageId) {
		this.imageId = imageId;
	}
	public String getImageName() {
		return imageName;
	}
	public void setImageName(String imageName) {
		this.imageName = imageName;
	}
	public int getImagePrice() {
		return imagePrice;
	}
	public void setImagePrice(int imagePrice) {
		this.imagePrice = imagePrice;
	}
	public int getImageQty() {
		return imageQty;
	}
	public void setImageQty(int imageQty) {
		this.imageQty = imageQty;
	}
	public String getImageContent() {
		return imageContent;
	}
	public void setImageContent(String imageContent) {
		this.imageContent = imageContent;
	}
	public String getImagePath() {
		return imagePath;
	}
	public void setImagePath(String imagePath) {
		this.imagePath = imagePath;
	}
	public String getLogTime() {
		return logTime;
	}
	public void setLogTime(String logTime) {
		this.logTime = logTime;
	}
}

/mybatis

config.properties
0.00MB
ImageBoardMapper.xml
0.00MB
mybatis-config.xml
0.00MB

 

'JAVA' 카테고리의 다른 글

13_이어서  (0) 2020.11.24
step13_boardProject  (0) 2020.11.23
DB파일올리기2_JSP  (0) 2020.11.20
DB에 파일올리기_JSP  (0) 2020.11.20
myBatis update ver/  (0) 2020.11.19
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함