게시판 만들기 (게시물 수정, 파일 첨부, 첨부파일 삭제, 게시물 삭제)
Back-End/Spring 2019. 7. 4. 18:19첨부파일이 저장되는 테이블을 작성
테이블이 중복될수 있으므로 테이블과 테이블에 관련된 제약조건을 삭제
1 | drop table attach cascade constraints; | cs |
1 2 3 4 5 6 | create table attach ( fullName varchar2(150) not null, //첨부파일 이름, null값 방지, 파일이름은 uuid를 이용해서 중복되지 않도록 할 예정 bno number not null, //게시글 번호, null값 방지 regdate date default sysdate, //등록날짜, 현재시간으로 설정 primary key(fullName) //기본키를 첨부파일이름으로 설정 ); | cs |
attach테이블의 bno 속성(글번호)이 board테이블에 bno 속성 (글번호)를 참조하도록 외래키로 설정함. (제약조건 설정)
(게시글 번호가 꼬이면 안되기 때문, 게시글이 삭제되면 같이 삭제될수 있게끔...)
1 2 | alter table attach add constraint fk_board_attach //fk_board_attach는 제약조건의 이름 foreign key (bno) references board(bno); | cs |
중복될 수 있으므로 먼저 시퀀스를 삭제
1 | drop sequence seq_board; | cs |
board 테이블의 bno 컬럼을 위한 시퀀스를 생성
1 2 3 | create sequence seq_board start with 1 //시작값을 1로하고 increment by 1; //1씩 증가하도록 설정함 | cs |
===============================================================================================
view.jsp 페이지에서 common.js를 include 해야한다.
1 | <script src="${path}/include/js/common.js"></script> | cs |
view.jsp 중 일부
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 32 | $(".fileDrop").on("dragenter dragover",function(e){ //기본 효과 막음, 기본효과가 있으면 이미지 파일이 실행되어 버림 e.preventDefault(); }); $(".fileDrop").on("drop",function(e){ e.preventDefault(); //첫번째 첨부파일 //드롭한 파일을 폼 데이터에 추가함 var files=e.originalEvent.dataTransfer.files; var file=files[0]; //폼 데이터에 첨부파일 추가 (0번 인덱스이므로 첫번째 파일만 붙였다.) var formData=new FormData(); formData.append("file",file); $.ajax({ //비동기 방식으로 호출 url: "${path}/upload/uploadAjax", // uploadAjax를 호출함 data: formData, // formData를 보내고 dataType: "text", // text 타입으로 보낸다. processData: false, // 요청으로 보낸 데이터를 query string 형태로 변환할지 여부 contentType: false, // 서버로 보내지는 데이터의 기본값 type: "post", //호출처리가 완료된 다음 (성공한 후)에 실행되는 구문 //컨트롤러에서 업로드 경로와 상태코드 (200)을 리턴해서 이쪽으로 보낸다. success: function(data){ //콜백 함수 //console.log(data); //data : 업로드한 파일 정보와 Http 상태 코드 var fileInfo=getFileInfo(data); //첨부파일의 정보 //console.log(fileInfo); var html="<a href='"+fileInfo.getLink+"'>"+ fileInfo.fileName+"</a><br>"; html += "<input type='hidden' class='file' value='" +fileInfo.fullName+"'>"; //hidden 태그를 추가 $("#uploadedList").append(html); //div에 추가 } }); }); | cs |
AjaxUploadController.java
1 2 3 4 5 6 7 8 9 10 | // 업로드한 파일은 MultipartFile 변수에 저장됨 @ResponseBody // json 형식으로 리턴 @RequestMapping(value = "/upload/uploadAjax", method = RequestMethod.POST, produces = "text/plain;charset=utf-8") public ResponseEntity<String> uploadAjax(MultipartFile file) throws Exception { //파일의 정보를 로그에 출력 logger.info("originaName:" +file.getOriginalfilename()); logger.info("size:" +file.getSize()); logger.info("contentType:" +file.getContentType()); //new ResponseEntity (데이터, 상태코드) //new ResponseEntity (업로드된 파일이름, 상태코드) return new ResponseEntity<String>( UploadFileUtils.uploadFile(uploadPath, file.getOriginalFilename(), file.getBytes()), HttpStatus.OK); } | cs |
view.jsp 중 일부
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | //목록 버튼 $("#btnList").click(function(){ location.href="${path}/board/list.do"; }); //수정 버튼 $("#btnUpdate").click(function(){ //첨부파일 이름들을 폼에 추가 var str=""; //id가 uploadList인것들 (div)에 클래스에 file인것 각각 (each) $("#uploadedList .file").each(function(i){ str+="<input type='hidden' name='files["+i+"]' value='" +$(this).val()+"'>"; //파일이름에다 인덱스 번호를 하나씩 붙인다. }); //폼에 hidden 태그들을 추가함 $("#form1").append(str); document.form1.action="${path}/board/update.do"; //위에서 만든 폼을 가지고 update.do로 보낸다. document.form1.submit(); }); | cs |
지금까지 보낸 내용들이 dto안에 다 쌓인다.
BoardServiceImpl.java
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | package com.example.spring02.service.board; import java.util.List; import javax.inject.Inject; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.example.spring02.model.board.dao.BoardDAO; import com.example.spring02.model.board.dto.BoardDTO; @Service // service bean public class BoardServiceImpl implements BoardService { @Inject //dao를 호출하기 때문에 의존성을 주입 BoardDAO boardDao; //첨부파일 레코드 삭제 @Override public void deleteFile(String fullName) { boardDao.deleteFile(fullName); } @Override public List<String> getAttach(int bno) { return boardDao.getAttach(bno); } // 1.글쓰기 - 게시물 번호 생성 // 2.첨부파일 등록-게시물 번호 사용 @Transactional @Override public void create(BoardDTO dto) throws Exception { //board 테이블에 레코드 추가 boardDao.create(dto); //attach 테이블에 레코드 추가 String[] files=dto.getFiles(); //첨부파일 이름 배열 if(files==null) return; //첨부파일이 없으면 skip for(String name : files) { boardDao.addAttach(name); //attach 테이블에 insert } } @Override public BoardDTO read(int bno) throws Exception { return boardDao.read(bno); } @Transactional //트랜잭션 처리 method //코드 수정과 첨부파일을 첨부하는 기능이 동시에 같이 들어가야하기 때문에 //일관성을 유지하기 위해서 트랜잭션 처리를 실시한다. //만약 두개중에 하나라도 되지않으면 작업을 취소시키기고, 롤백을 한다. @Override public void update(BoardDTO dto) throws Exception { boardDao.update(dto); //board 테이블 수정 //attach 테이블 수정 String[] files=dto.getFiles(); if(files==null) return; for(String name : files) { System.out.println("첨부파일 이름:"+name); boardDao.updateAttach(name, dto.getBno()); } } @Transactional @Override public void delete(int bno) throws Exception { //reply 레코드 삭제 //attach 레코드 삭제 //첨부파일 삭제 //board 레코드 삭제 boardDao.delete(bno); } @Override public List<BoardDTO> listAll( //매개변수는 시작 레코드번호, 끝번호, 옵션과 키워드가 들어간다 String search_option, String keyword,int start, int end) throws Exception { return boardDao.listAll(search_option,keyword,start,end); } //조회수 증가 처리 @Override public void increaseViewcnt(int bno, HttpSession session) throws Exception { long update_time=0; if(session.getAttribute("update_time_"+bno)!=null) { //최근에 조회수를 올린 시간 update_time= (long)session.getAttribute("update_time_"+bno); } long current_time=System.currentTimeMillis(); //일정 시간이 경과한 후 조회수 증가 처리 if(current_time - update_time > 5*1000) { //조회수 증가 처리 boardDao.increateViewcnt(bno); //조회수를 올린 시간 저장 session.setAttribute("update_time_"+bno, current_time); } } @Override public int countArticle( String search_option, String keyword) throws Exception { return boardDao.countArticle(search_option,keyword); } | cs |
BoardDAOImpl.java
1 2 3 4 5 6 7 8 9 | //첨부파일 정보 수정 @Override public void updateAttach(String fullName, int bno) { Map<String,Object> map=new HashMap<>(); //값을 여러개 담을때는 haspmap를 사용한다. map.put("fullName", fullName); //첨부파일 이름 map.put("bno", bno); //게시물 번호 sqlSession.insert("board.updateAttach", map); //updateAttach mapper을 호출 } | cs |
boardMapper.xml
1 2 3 4 5 6 7 | <!-- 다른 mapper와 중복되지 않도록 네임스페이스 기재 --> <mapper namespace="board"> <!-- 새로운 첨부파일 추가 --> <insert id="updateAttach"> //attach테이블에 입력받은 파일이름과 게시글 번호를 삽입한다 insert into attach (fullName, bno) values ( #{fullName}, #{bno} ) </insert> | cs |
BoardDAOImpl.java
1 2 3 4 5 | //레코드 수정 @Override public void update(BoardDTO dto) throws Exception { sqlSession.update("board.update", dto); } | cs |
boardMapper.xml
1 2 3 4 5 6 | <mapper namespace="board"> <!-- 게시물 내용 수정 --> <update id="update"> //게시글의 내용 수정, 게시글 번호가 맞으면 제목과 내용을 입력한 대로 수정해서 갱신함 update board set title=#{title}, content=#{content} where bno=#{bno} </update> | cs |
view.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | listAttach(); //첨부파일 리스트를 출력하는 함수 function listAttach(){ $.ajax({ type: "post", url: "${path}/board/getAttach/${dto.bno}", success: function(list){ // list : json //console.log(list); $(list).each(function(){ var fileInfo=getFileInfo(this); //console.log(fileInfo); var html="<div><a href='"+fileInfo.getLink+"'>" +fileInfo.fileName+"</a> "; <c:if test="${sessionScope.userid == dto.writer}"> html+="<a href='#' class='file_del' data-src='" +this+"'>[삭제]</a></div>"; </c:if> $("#uploadedList").append(html); }); } }); } | cs |
BoardController.java 중 일부
1 2 3 4 5 6 7 8 9 | //첨부파일 목록을 리턴 //ArrayList를 json 배열로 변환하여 리턴 //view에서 넘긴 bon를 경로값 (url에 포함된 변수)로 받아서 맵핑한다. @RequestMapping("getAttach/{bno}") @ResponseBody //view가 아닌 데이터 자체를 리턴할 때 사용하는 어노테이션 //(즉, 화면이 바뀌는게 아니라 데이터가 넘어간다는 의미.. 리턴한 값) public List<String> getAttach(@PathVariable("bno") int bno){ return boardService.getAttach(bno); } | cs |
BoardServiceImpl.java 중 일부
1 2 3 4 | @Override public List<String> getAttach(int bno) { return boardDao.getAttach(bno); } | cs |
BoardDAOImpl.java 중 일부
1 2 3 4 5 | //첨부파일 리스트 @Override public List<String> getAttach(int bno) { return sqlSession.selectList("board.getAttach", bno); } | cs |
boardMapper.xml 중 일부
1 2 3 4 5 6 7 8 | <!-- 다른 mapper와 중복되지 않도록 네임스페이스 기재 --> <mapper namespace="board"> <!-- 첨부파일 목록 --> <select id="getAttach" resultType="String"> //결과타입은 필드가 1개이므로 String이다. select fullName //attach 테이블에서 첨부파일이름을 검색 (단, 글번호가 내가 선택한 글번호여야됨) //그리고 날짜의 내림차순으로 정렬시킨다. from attach where bno=#{bno} order by regdate desc </select> | cs |
여기서 검색한 결과값이 컨트롤러로 넘어간다.
============================첨부 파일 삭제=============================================
view.jsp
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 32 33 34 35 36 37 | //첨부파일 리스트를 출력하는 함수 function listAttach(){ $.ajax({ type: "post", url: "${path}/board/getAttach/${dto.bno}", //getAttach(파일얻을때) 게시글 번호를 보냄 //컨트롤러로 이동 //위의 구문이 처리가 정상적으로 처리되었을때 실행되는 구문 (success) //list는 배열 BoardController.java
BoardServiceImpl.java
BoardDAOImpl.java
boardMapper.xml
success: function(list){ // list : json //console.log(list); $(list).each(function(){ //list 변수 각각에 대하여 //현재 파일의 파일 정보 var fileInfo=getFileInfo(this); //console.log(fileInfo); //파일정보를 보는 하이퍼링크를 만듦 (div에 하이퍼링크를 만듦) var html="<div><a href='"+fileInfo.getLink+"'>" +fileInfo.fileName+"</a> "; //------------파일 삭제------------- //세션에 담긴 userid와 dto에 담긴 작성자가 같으면 //담아놓은 파일을 삭제하기 위해서 링크를 건다. <c:if test="${sessionScope.userid == dto.writer}"> html+="<a href='#' class='file_del' data-src='" +this+"'>[삭제]</a></div>"; </c:if> //추가 $("#uploadedList").append(html); }); } }); } | cs |
view.jsp (위쪽의 #uploadedList)와 연결되는 코드)
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 | //첨부파일 삭제 //id가 uploadedList인 태그의 class가 file_del인 태그 클릭 $("#uploadedList").on("click",".file_del",function(e){ var that=$(this); //클릭한 태그 //data: {fileName: $(this).attr("data-src") }, $.ajax({ type: "post", url: "${path}/upload/deleteFile", AjaxUploadController.java
data: "fileName="+ $(this).attr("data-src"), dataType: "text", //컨트롤러에서 삭제가 성공하면 결과값이 result로 넘어오고 //result의 값이 deleted와 같으면 화면에서 div 태그를 제거한다. success: function(result){ if(result=="deleted"){ //컨트롤러에서 넘어온 리턴값이 deleted면 첨부파일이 정상적으로 삭제되었다는 뜻이므로 밑의 구문을 생성 //화면에서 태그 제거 that.parent("div").remove(); //that(this를 의미)의 클릭한 태그의 parent의 div를 삭제 //div태그를 삭제해야 파일에 관한 링크와 정보들이 삭제된다. } } }); }); | cs |
첨부파일이 올라간 상태에서 글을 쓰고 확인을 누르면 mapper에서 id가 addAttach인 쿼리를 호출
파일업로드를 하게되면 트랜잭션 처리를 해야 된다.
왜냐하면 게시글이 올라가고, 파일도 등록되어야 하는데 (board 테이블에도 insert되어야 하고, attach테이블에도 insert되어야 한다)
그렇게 되어야 문제가 발생되지 않기 때문에, 둘 중에 하나만 된다면 롤백시켜야 한다.
===게시글을 작성시, 파일이 첨부되어야 하기 때문에 게시물 쓰기 코드에 트랜잭션을 처리하는 코드를 추가=====================
write.jsp 중 일부
1 2 3 | <h2>글쓰기</h2> <form id="form1" name="form1" method="post" action="${path}/board/insert.do"> BoardController.java
BoardService.java
boardMapper.xml
| cs |
view.jsp에 삭제버튼을 추가하는 코드 추가
1 2 3 4 5 6 | $("#btnDelete").click(function(){ if(confirm("삭제하시겠습니까?")){ document.form1.action = "${path}/board/delete.do"; document.form1.submit(); BoardController.java
BoardServiceImpl.java
BoardDAOImpl.java
boardMapper.xml
} }); | cs |
'Back-End > Spring' 카테고리의 다른 글
Spring boot와 Spring legacy의 차이점 (0) | 2019.07.05 |
---|---|
도로명 주소 (daum api) 사용 (0) | 2019.07.05 |
AJAX 방식 (0) | 2019.07.04 |
게시판 만들기 (댓글 쓰기 / 댓글 목록/ 댓글 갯수) (2) | 2019.07.04 |
게시판 만들기 (상세화면) (0) | 2019.07.04 |