Boot_exam

75953 ワード

  • BoardController.java
  • package com.example.controller;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    
    import com.example.entity.Board;
    import com.example.entity.BoardListProjection;
    import com.example.repository.BoardRepository;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.multipart.MultipartFile;
    
    @Controller
    @RequestMapping(value = "/board")
    public class BoardController {
    
        @Autowired
        BoardRepository bRepository;
    
        @Autowired
        ResourceLoader resLoader;
    
        // 수정 GET
        @GetMapping(value = "/update")
        public String updateGET(
                Model model,
                @RequestParam(name = "no") long no) {
            Board board = bRepository.findById(no).orElse(null);
            model.addAttribute("board", board);
            return "board/update";
        }
    
        // 수정 POST
        @PostMapping(value = "/updateaction")
        public String updateActionPOST(@ModelAttribute Board board) {
    
            System.out.println("=== board.getNo() === " + board.getNo());
            Board oldBoard = bRepository.findById(board.getNo()).orElse(null);
            board.setWriter(oldBoard.getWriter());
            board.setHit(oldBoard.getHit());
            board.setRegdate(oldBoard.getRegdate());
            if (board.getImagesize() <= 0) {
                board.setImage(oldBoard.getImage());
                board.setImagename(oldBoard.getImagename());
                board.setImagesize(oldBoard.getImagesize());
                board.setImagetype(oldBoard.getImagetype());
            }
    
            Board retBoard = bRepository.save(board);
            // System.out.println("=== retBoard === " + retBoard.toString());
            return "redirect:/board/selectone?no=" + board.getNo();
        }
    
        // 글쓰기 GET
        @GetMapping(value = "/insert")
        public String insertGET(Model model) {
    
            return "board/insert";
        }
    
        // 글쓰기 POST
        @PostMapping(value = "/insertaction")
        public String insertActionPOST(
                Model model,
                @ModelAttribute Board board,
                @RequestParam(name = "timage") MultipartFile image) {
    
            try {
                System.out.println("=== image === " + image.getOriginalFilename());
    
                // 4개항목 추가 (이미지)
                if (image.getSize() > 0) {
                    System.out.println("=== 이미지 첨부 됨 ===");
                    board.setImage(image.getBytes());
                    board.setImagename(image.getOriginalFilename());
                    board.setImagesize(image.getSize());
                    board.setImagetype(image.getContentType());
                } else {
                    System.out.println("=== 이미지 첨부 안됨 ===");
                }
    
                bRepository.save(board);
                model.addAttribute("msg", "게시물을 등록했습니다");
                model.addAttribute("url", "/board/selectlist");
                return "layout/alert";
                // return "redirect:/board/selectlist";
                // return "redirect:/board/insert";
            } catch (Exception e) {
                e.printStackTrace();
                return "redirect:/board/insert";
            }
        }
    
        // 전체목록 + 검색 + 페이지네이션
        @GetMapping(value = "/selectlist")
        public String selectListGET(
                Model model,
                @RequestParam(name = "page", defaultValue = "1") int page,
                @RequestParam(name = "txt", defaultValue = "") String txt) {
            try {
                PageRequest pageable = PageRequest.of((page - 1), 15);
                List<BoardListProjection> list = bRepository.findByTitleContainsOrderByRegdateDesc(txt, pageable);
    
                // 페이지네이션을 위한 개수 가져오기 해보기
                long total = bRepository.countByTitleContains(txt);
                long pages = (total - 1) / 15 + 1;
    
                model.addAttribute("pages", pages);
                model.addAttribute("list", list);
                return "board/selectlist";
            } catch (Exception e) {
                e.printStackTrace();
                return "redirect:/board/selectlist";
            }
        }
    
        // 게시글 한개 조회
        @GetMapping(value = "/selectone")
        public String selectoneGET(
                Model model,
                HttpServletRequest request,
                @RequestParam(name = "no") long no) {
            try {
                Board board = bRepository.findById(no).orElse(null);
                // 이미지 url 정보 추가 후 전송
                board.setImageUrl(request.getContextPath() + "/board/image?no=" + no);
                model.addAttribute("board", board);
    
                // 이전글
                BoardListProjection boardPrev = bRepository.findFirstByNoLessThanOrderByNoDesc(no);
                if (boardPrev != null) {
                    // long prev = boardPrev.getNo();
                    model.addAttribute("prev", boardPrev.getNo());
                } else {
                    model.addAttribute("prev", 0L);
                }
    
                // 다음글
                BoardListProjection boardNext = bRepository.findFirstByNoGreaterThanOrderByNoAsc(no);
                if (boardNext != null) {
                    model.addAttribute("next", boardNext.getNo());
                } else {
                    model.addAttribute("next", 0L);
                }
                // System.out.println("=== model === " + model);
                return "board/selectone";
    
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        // 이미지조회
        // http://127.0.0.1:9090/ROOT/board/image?no=7
        @GetMapping(value = "/image")
        public ResponseEntity<byte[]> imageGET(
                @RequestParam(name = "no") long no) throws IOException {
    
            Board board = bRepository.findById(no).orElse(null);
            // 이미지명, 크기, 타입, 데이터
    
            // System.out.println(board.getImagetype());
            // System.out.println(board.getImagesize());
            // System.out.println("board => "+board);
    
            if (board.getImagesize() > 0) { // 첨부한 파일이 존재
                // headers에 파일 타입 추가
                HttpHeaders headers = new HttpHeaders();
                if (board.getImagetype().equals("image/png")) {
                    headers.setContentType(MediaType.IMAGE_PNG);
                } else if (board.getImagetype().equals("image/jpeg")) {
                    headers.setContentType(MediaType.IMAGE_JPEG);
                } else if (board.getImagetype().equals("image/gif")) {
                    headers.setContentType(MediaType.IMAGE_GIF);
                }
    
                // 이미지 byte[], headers, Httpstatus.Ok
                ResponseEntity<byte[]> response = new ResponseEntity<>(board.getImage(), headers, HttpStatus.OK);
                return response;
            } else {
                InputStream is = resLoader.getResource("classpath:/static/img/default.jpg").getInputStream();
                // System.out.println("InputStream is => "+is);
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.IMAGE_JPEG);
                ResponseEntity<byte[]> response = new ResponseEntity<>(is.readAllBytes(), headers, HttpStatus.OK);
                return response;
            }
        }
    }
  • BoardRestController.java
  • package com.example.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import com.example.entity.Board;
    import com.example.repository.BoardRepository;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.MediaType;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping(value = "/restboard")
    public class BoardRestController {
    
        @Autowired
        BoardRepository bRepository;
    
        // 삭제
        @RequestMapping(value = "/delete", method = { RequestMethod.GET }, consumes = {
                MediaType.ALL_VALUE }, produces = {
                        MediaType.APPLICATION_JSON_VALUE })
        public Map<String, Object> boardDeleteGET(@RequestParam(name = "no") long no) {
            Map<String, Object> map = new HashMap<>();
            try {
                bRepository.deleteById(no);
                map.put("status", 200);
                // map.put("board", board);
    
            } catch (Exception e) {
                e.printStackTrace();
                map.put("status", 0);
            }
            return map;
        }
    
        // 조회수 1증가 rest로 만들고, 호출함
        // 127.0.0.1:9090/ROOT/restboard/updatehit?no=6
        @RequestMapping(value = "/updatehit", method = { RequestMethod.PUT }, consumes = {
                MediaType.ALL_VALUE }, produces = {
                        MediaType.APPLICATION_JSON_VALUE })
        public Map<String, Object> boardUpdateHit1PUT(@RequestParam(name = "no") long no) {
            Map<String, Object> map = new HashMap<>();
            try {
                Board board = bRepository.findById(no).orElse(null);
                board.setHit(board.getHit() + 2L);
                bRepository.save(board);
                map.put("status", 200);
                // map.put("board", board);
    
            } catch (Exception e) {
                map.put("status", 0);
            }
            return map;
        }
    }
  • HomeController.java
  • package com.example.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    
    @Controller
    public class HomeController {
    
        // 홈화면
        @GetMapping(value = { "/", "/home" })
        public String homeGET() {
    
            return "home";
        }
    }
  • Board.java(Entity)
  • package com.example.entity;
    
    import java.util.Date;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Lob;
    import javax.persistence.SequenceGenerator;
    import javax.persistence.Table;
    import javax.persistence.Transient;
    
    import org.hibernate.annotations.CreationTimestamp;
    import org.springframework.format.annotation.DateTimeFormat;
    
    import lombok.Data;
    
    @Entity
    @Data
    @Table(name = "BOARD")
    @SequenceGenerator(name = "SEQ_BOARD", sequenceName = "SEQ_BOARD_NO", allocationSize = 1, initialValue = 1)
    public class Board {
    
        // 글번호 no, 글제목 title, 내용 content, 작성자 writer, 이미지 image, 조회수 hit, 등록일 regdate
    
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_BOARD") // 시퀀스 적용
        private Long no;
    
        @Column(length = 200)
        private String title;
    
        @Lob
        private String content;
    
        @Column(length = 50)
        private String writer;
    
        @Lob
        private byte[] image;
    
        @Column(length = 200)
        private String imagename;
    
        private long imagesize;
    
        @Column(length = 50)
        private String imagetype;
    
        private long hit = 10L;
    
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
        @CreationTimestamp // CURRENT_DATE
        private Date regdate;
    
        // DB의 테이블에 생성되지 않고, 매핑도 안됨, 임시
        @Transient
        private String imageUrl;
    }
  • BoardListProjection.java
  • package com.example.entity;
    
    import java.util.Date;
    
    public interface BoardListProjection {
    
        // 글번호
        Long getNo();
    
        // 제목
        String getTitle();
    
        // 작성자
        String getWriter();
    
        // 조회수
        long getHit();
    
        // 등록일
        Date getRegdate();
    }
  • BoardRepository.java
  • package com.example.repository;
    
    import java.util.List;
    
    import com.example.entity.Board;
    import com.example.entity.BoardListProjection;
    
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public interface BoardRepository extends JpaRepository<Board, Long> {
    
        // 제목검색어 + 최신글부터 내림차순
        List<BoardListProjection> findByTitleContainsOrderByRegdateDesc(String title, Pageable pageable);
    
        // 페이지네이셔내을 위한 게시글 개수 (검색어 포함)
        long countByTitleContains(String title);
    
        // projection 목록 조회
        // Witer 검색
        List<BoardListProjection> findByWriterContainsOrderByNoDesc(String writer, Pageable pageable);
    
        // 페이지네이셔내을 위한 게시글 개수 (검색어 포함)
        long countByWriterContainsOrderByNoDesc(String writer);
    
        // 이전글
        // 작은것 중에서 가장 큰것
        BoardListProjection findFirstByNoLessThanOrderByNoDesc(long no);
    
        // 다음글
        // 현재 글 번호보다 큰것을 오름차순정렬해서 가장 처음 것
        // ex) 3번보다 큰것 => 4, 5, 7, 8, 9, 10(오름차순 정렬) => 4(제일 처음것)
        BoardListProjection findFirstByNoGreaterThanOrderByNoAsc(long no);
    }
  • board_insert.html
  • <!DOCTYPE html>
    <html lang="ko" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>게시판글쓰기</title>
    </head>
    
    <body>
        <div style="padding:20px">
        	<h3>글쓰기</h3>
        	<hr />
        	<form th:action="@{/board/insertaction}" method="post"
        		enctype="multipart/form-data">
        		제목 : <input type="text" name="title" /><br /> 
        		내용 : <textarea rows="6" name="content"></textarea><br />
        		작성자 : <input type="text" name="writer" /><br />
        		이미지 : <input type="file" name="timage" /><br />
        		<input type="submit" value="글쓰기" />
        	</form>
        </div>
    </body>
    </html>
  • board_selectlist.html
  • <!DOCTYPE html>
    <html lang="ko" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>게시판목록</title>
    </head>
    
    <body>
        <div style="padding:20px">
        	<h3>게시판목록</h3>
        	
        	<hr />
        	<form th:action="@{/board/selectlist}" method="get">
        		검색 : <input type="text" name="txt" /> 
        		<input type="submit" value="검색" />
        	</form>
        	
        	<hr />
        	<a th:href="@{/board/insert}">글쓰기</a>
        	<table border="1">
        		<tr>
        			<th>글번호</th>
        			<th>글제목</th>
        			<th>작성자</th>
        			<th>조회수</th>
        			<th>등록일</th>
        		</tr>	
        		
        		<tr th:each="tmp : ${list}">
            		<td th:text="${tmp.no}"></td>
            		<td>
            			<a href="#"
            				th:onclick="|javascript:updateHit('${tmp.no}')|"
            				th:text="${tmp.title}"></a>
            		</td>
            		<td th:text="${tmp.writer}"></td>
            		<td th:text="${tmp.hit}"></td>
            		<td th:text="${tmp.regdate}"></td>
            	</tr>
        	</table>
        	
        	 <th:block th:each="i : ${#numbers.sequence(1, pages)}">
            	<a th:href="@{/board/selectlist(page=${i}, txt=${param.txt})}"
            		th:text="${i}"></a>
            </th:block>
        </div>
        <script>
        	function updateHit(no){
    			const xhr = new XMLHttpRequest(); // ex) axios와 같은 것
                const url = '/ROOT/restboard/updatehit?no=' + no;
                xhr.open("PUT", url, true);
                xhr.responseType="json";
                xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8;');
    
                xhr.onload = function(e) {
                    console.log(e.target);
                    if(e.target.response.status === 200) {
                        // 2. 다음 페이지 이동
                        location.href="/ROOT/board/selectone?no=" + no;
                    }
                }
                xhr.send();
        	}
        </script>
    </body>
    </html>
  • board_selectone.html
  • <!DOCTYPE html>
    <html lang="ko" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>게시판내용</title>
    </head>
    
    <body>
        <div style="padding:20px">
        	<h3>게시판내용</h3>
        	<hr />
        	
        	<p th:text="${board.no}" />
        	<p th:text="${board.title}" />
    		
    		<img th:src="${board.imageurl}" style="width:50px" />		    	
        	<hr />
        	<a th:href="@{/board/update(no=${board.no})}">
        		<button>수정</button>
        	</a>
    
    		<button th:onclick="|javascript:handleDelete('${board.no}')|">삭제</button>
    		
        	<a th:if="${prev}" th:href="@{/board/selectone(no=${prev})}">
        		<button>이전글</button>
        	</a>
    		
    		<a th:if="${next}" th:href="@{/board/selectone(no=${next})}">
        		<button>다음글</button>
        	</a>
    
        	<a th:href="@{/board/selectlist}"><button>목록</button></a>
        	
        </div>
    </body>
    </html>
    <script>
    	function handleDelete(no) {
    		if(confirm('삭제할까요?')){
    			console.log("=== no === "+ no);
    			const xhr = new XMLHttpRequest();
    			console.log("=== xhr === " + xhr);
    			const url = "/ROOT/restboard/delete?no="+no;
    			console.log("=== url === " + url);
    			xhr.open("GET", url, true);
    			xhr.responseType = "json";
    			xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8;');
    			xhr.onload = function(e) {
    				console.log("=== e.target === " , e.target);
    				if(e.target.response.status === 200){
    					// 2. 해당 페이지로 이동
    					location.href = "/ROOT/board/selectlist";
    				}
    			}
    			// 호출해야 onload가 반응
    			xhr.send();
    			// GET으로 삭제처리 주소창을 바꿈
    			// location.href="/ROOT/board/delete?no="+no;
    		}
    	}
    </script>
  • board_update.html
  • <!DOCTYPE html>
    <html lang="ko" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>board/update</title>
    
    </head>
    <body>
        <div style="padding: 20px">
            <h3>글수정</h3>
            <hr />
    
            <!-- '@'의 역할 => context-path를 자동으로 잡아줌 -->
            <form th:action="@{/board/updateaction}" method="post" enctype="multipart/form-data" >
            	글번호 : <input type="text" name="no" th:value="${board.no}" hidden /><br />
               	글제목 : <input type="text" name="title" th:value="${board.title}" /><br />
               	글내용: <textarea rows="6" cols="" name="content" th:text="${board.content}"></textarea><br />
                
                <input type="submit" value="글수정" />
            </form>
        </div>
    </body>
    </html>
  • alert.html
  • <!DOCTYPE html>
    <html lang="ko" xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
            <script th:inline="javascript">
                /*<![CDATA[*/
                const msg = [[${msg}]];
                alert(msg); //알림표시
                window.location.replace( [[${#httpServletRequest.getContextPath()}]] + "" + [[${url}]] ); //이동하는 페이지
                /*]]*/ 
            </script>
        </head>
    </html>