Boot_exam

  • 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>

좋은 웹페이지 즐겨찾기