ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링부트+JPA 블로그 프로젝트 07 게시판
    스프링/스프링부트+JPA - 블로그 2021. 8. 31. 19:43

    유투버 '데어프로그래밍'님 강의 참조

     

     

    ▲ 글쓰기

    • 컨트롤러 세팅
    @Controller
    public class BoardController {
        
        @GetMapping("/board/saveForm")
    	public String saveForm() {
    		return "board/saveForm";
    	}
    • header.jsp에 주소 세팅
    <c:otherwise>
      <ul class="navbar-nav">
        <li class="nav-item"><a class="nav-link" href="/board/saveForm">글쓰기</a></li>
        <li class="nav-item"><a class="nav-link" href="/user/updateForm">회원정보</a></li>
        <li class="nav-item"><a class="nav-link" href="/logout">로그아웃</a></li>
      </ul>
    </c:otherwise>
    • board를 위한 jsp 폴더 및 saveForm 세팅

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    
    <%@ include file="../layout/header.jsp"%>
    <div class="container">
    	<form>
    		<div class="form-group">
    			<label for="title">Title:</label>
    			<input type="text" class="form-control" placeholder="Enter title" id="title">
    		</div>
    		<div class="form-group">
    			<label for="comment">Content:</label>
    			<textarea class="form-control summernote" rows="5" id="content"></textarea>
    		</div>
    	</form>
    	<button id="btn-save" class="btn btn-primary">글쓰기 완료</button>
    </div>
    <script>
    	$('.summernote').summernote({
    		placeholder : '내용',
    		tabsize : 2,
    		height : 300
    	});
    </script>
    <script src="/js/board.js"></script>
    <%@ include file="../layout/footer.jsp"%>
    • 디자인은 섬머노트! 헤더에 링크와 스크립트 삽입
    <link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-bs4.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-bs4.min.js"></script>

    굿!

     

    • 글쓰기 완료 버튼작동을 위한 board.js 와 ajax 세팅
    let index = {
    	init:function(){
    		$("#btn-save").on("click",()=>{ //function(){} 대신 ()=>{}를 써서 전역변수 this를 바인딩
    			this.save();
    		});
    	},
    	save:function(){ //this.save()의 save
    		//alert("user.js의 save 함수 호출됨");
    		let data = {
    			title:$("#title").val(),
    			content:$("#content").val()
    		};
    		//console.log(data);
    		$.ajax({
    			//ajax 기본 호출은 비동이
    			//오브젝트가 들어오는 곳
    			//통신 수행
    			type:"post",
    			url:"/api/board",
    			data:JSON.stringify(data),//HTTP BODY DATA
    			contentType:"application/json; charset=utf-8", //body데이터의 타입
    			dataType:"json" //서버에서 받을 데이터 형식, 즉 json으로 던지고 서버를위해 자동 파싱 = JSON->JS
    		}).done(function(resp){ //위의 데이터가 js로 바뀌고 파라미터로 사용 가능
    			//통신이 정상이면 done
    			alert("글쓰기 완료");
    			//alert(resp)
    			console.log(resp);
    			location.href="/";
    		}).fail(function(error){
    			//통신이 비정상이면 fail
    			alert(JSON.stringify(error));
    		});
    	},
    }
    index.init();
    • BoardApiController 세팅
    @RestController
    public class BoardApiController {
    	
    	@Autowired
    	private BoardService boardService;
    	
    	//글쓰기 진행
    	@PostMapping("/api/board")
    	public ResponseDto<Integer> save(@RequestBody Board board, 
    			@AuthenticationPrincipal PrincipalDetail principal) {
    		boardService.insert(board, principal.getUser());
    		return new ResponseDto<Integer>(HttpStatus.OK.value(),1);
    	}
    }
    • BoardRepository 세팅
    public interface BoardRepository extends JpaRepository<Board, Integer>{}
    • BoardService 세팅
    @Service
    public class BoardService {
    	
    	@Autowired
    	private BoardRepository boardRepository;
    
    	//글쓰기
    	@Transactional
    	public void insert(Board board, User user) {
    		board.setCount(0); // 데이터를 직접 넣고싶으면 .set
    		board.setUser(user);
    		boardRepository.save(board);
    	}
    }

     

    ☞ 글쓰기 세팅은 여기까지이며, 현재 시큐리티가 걸려있기때문에 글쓰기까지의 프로세스를 정리하자면 ↓

    1. 로그인 성공 후 글쓰기를 BoardCotroller saveForm() 요청
    2. save()의 saveForm.jsp 가 불러지고 거기서 title/content 아이디값을 들고 글쓰기 완료 버튼을 누름
    3. 두개의 값이 board.js로 가서 ajax를 탐 -> 먼저 '/api/board'를 타게 됨
    4. title/content를 들고 BoardApiController의 save()를 탐, 거기서 title/contnet + 포린키로 묶인 user값을 받음
    5. 글쓰기를 하게되면 Service의 insert()함수가 실행되고 조회수/user정보가 레파지토리로 넘겨짐 
    6. 레파지토리가 .save(board)를 진행 -> title/content/user정보를 DB에 전송 
    7. 정상적으로 되면 똑같이 return 1 이 되고 게시판 페이지로 이동

    이미지도 잘 들어간다

     

     

     

     

     

     

    ▲ 글 목록 띄우기

    • 일반 BoardController에서 서비스를 연결하여 데이터를 들고 메인페이지에 뿌려야 한다
    @Controller
    public class BoardController {
    	
    	@Autowired
    	private BoardService boardService;
    	
    	@GetMapping({"","/"})
    	public String index(Model model) { //데이터를 들고 오는 Model
    		model.addAttribute("boards", boardService.list());
    		return "index";
    	}
    • Service 세팅
    	//글목록 들고오기
    	public List<Board> list() {
    		return boardRepository.findAll();
    	}
    • index.jsp에 데이터 뿌리기 (EL표현식으로 model로 들고오는 boards의 데이터를 뿌리기)
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%@ include file="layout/header.jsp"%>
    <div class="container">
    	<c:forEach var="board" items="${boards}">
    		<div class="card m-2">
    			<div class="card-body">
    				<h4 class="card-title">${board.title}</h4>
    				<a href="#" class="btn btn-primary">Detail</a>
    			</div>
    		</div>
    	</c:forEach>
    </div>
    <%@ include file="layout/footer.jsp"%>
    • model.attribute로 세팅된 데이터는 request정보라고 생각하면되고 getter처럼 불러와진다. 

    굿!

     

    - 페이징으로 글 목록 뿌리기

    • BoardController에 메인페이지를 다루는 함수에 페이지어블 클래스 추가
    	@GetMapping({"","/"})
    	public String index(Model model,
    			@PageableDefault(size=3,sort="id",direction = Sort.Direction.DESC) Pageable pageable) { //데이터를 들고 오는 Model
    		model.addAttribute("boards", boardService.list(pageable));
    		return "index";
    	}
    • 서비스에도 마찬가지로 페이지어블로 바꿔야함
    	//글목록 들고오기
    	@Transactional(readOnly=true)
    	public Page<Board> list(Pageable pageable) {
    		return boardRepository.findAll(pageable);
    	}
    • index.jsp에서 boards.content로 받아야 오류없이 페이징이 된다. 
    <c:forEach var="board" items="${boards.content}">

    굿!

    • Bootstrap의 페이징 버튼을 index.jsp에 추가
    <ul class="pagination justify-content-center">
    <li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
    <li class="page-item"><a class="page-link" href="#">Next</a></li>

    • 이전/다음 버튼을 구분하는 링크 걸기
    <ul class="pagination justify-content-center">
    	<c:choose>
    		<c:when test="${boards.first}">
    			<li class="page-item disabled"><a class="page-link" href="?page=${boards.number-1}">Previous</a></li>
    		</c:when>
    		<c:otherwise>
    			<li class="page-item"><a class="page-link" href="?page=${boards.number-1}">Previous</a></li>
    		</c:otherwise>
    	</c:choose>
    	<c:choose>
    		<c:when test="${boards.last}">
    			<li class="page-item disabled"><a class="page-link" href="?page=${boards.number+1}">Next</a></li>
    		</c:when>
    		<c:otherwise>
    			<li class="page-item"><a class="page-link" href="?page=${boards.number+1}">Next</a></li>
    		</c:otherwise>
    	</c:choose>
    </ul>

    Pageable이 들고있는 데이터를 가지고 boards.으로 뿌릴 수 있다는게 중요 하다.

     

     

     

     

     

     

    ▲ 글 상세보기

    • index.jsp에서 url 설정
    <a href="/board/${board.id}" class="btn btn-primary">Detail</a>
    • BoardController
    	//상세보기
    	@GetMapping("/board/{id}")
    	public String findById(@PathVariable int id, Model model) {
    		model.addAttribute("board",boardService.detail(id));
    		return "board/detail";
    	}
    • BoardService
    	//글 상세보기
    	@Transactional(readOnly=true)
    	public Board detail(int id) {
    		return boardRepository.findById(id)
    				.orElseThrow(()->{
    					return new IllegalArgumentException("상세보기 실패!");
    				});
    	}
    • detail.jsp
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    
    <%@ include file="../layout/header.jsp"%>
    <div class="container">
    <button class="btn btn-secondary" onclick="history.back()">돌아가기</button>
    <c:if test="${board.user.id==principal.user.id}">
    <button id="btn-delete" class="btn btn-danger">삭제</button>
    <a href="/board/${board.id}/updateForm" class="btn btn-warning">수정</a>
    </c:if>
    <br>
    <br>
    <div class="form-group">
    글 번호: <span id="id"><i>${board.id} </i></span>
    글 작성자: <span><i>${board.user.username} </i></span>
    </div>
    <br>
    <div class="form-group">
    	<h3>${board.title}</h3>
    </div>
    <hr/>
    <div class="form-group">
    	<h3>${board.content}</h3>
    </div>
    </div>
    <script src="/js/board.js"></script>
    <%@ include file="../layout/footer.jsp"%>
    • 작성자의 경우 Board모델에 이미 User 전체 객체를 EAGER로 받아오기때문에 저렇게 받아진다!

    굿!

    • 상세보기도 잘되고 돌아가기도 잘되는걸 볼 수 있다.

     

     

    ▲글 삭제하기

    • board.js에서 삭제 기능 추가
    let index = {
    	init:function(){
        ..
        		$("#btn-delete").on("click",()=>{ //function(){} 대신 ()=>{}를 써서 전역변수 this를 바인딩
    			this.deletebyId();
    		});
    	},
        ..
        deletebyId:function(){ 
    		var id = $("#id").text();
    		$.ajax({
    			type:"delete",
    			url:"/api/board/"+id,
    		}).done(function(resp){
    			//통신이 정상이면 done
    			alert("삭제 완료");
    			location.href="/";
    		}).fail(function(error){
    			//통신이 비정상이면 fail
    			alert(JSON.stringify(error));
    		});
    	},
    }
    index.init();

    -> value값이 아닌 일반 text 아이디 값을 받아와야 찾아서 삭제된다. 

    • BoardApiController
    	//글 삭제 요청
    	@DeleteMapping("/api/board/{id}")
    	public ResponseDto<Integer> deleteById(@PathVariable int id){
    		boardService.deleteById(id);
    		return new ResponseDto<Integer>(HttpStatus.OK.value(),1);
    	}

    * 명심하자 보드 아이디를 받아와야 db에서 그 아이디를 찾아서 삭제 한다.

     

    • BoardService
    	@Transactional
    	//글 삭제하기
    	public void delete(int id) {
    		boardRepository.deleteById(id);
    	}

    굿!

    • 삭제또한 잘 처리 된다. 중간번호들이 삭제된걸 볼 수 있다

    ♣ 작성자와 동일한 사람만 삭제/수정되도록 세팅하자

    • header.jsp 시큐리티태그가 들어가있기때문에 적용이 된다. 아래처럼 코드 추가하자
    <c:if test="${board.user.id==principal.user.id}">
    <button id="btn-delete" class="btn btn-danger">삭제</button>
    <button id="btn-update" class="btn btn-warning">수정</button>
    </c:if>

    굿!

     

     

     

     

    ▲ 글 수정하기

    • BoardController
    	@GetMapping("/board/{id}/updateForm")
    	public String updateForm(@PathVariable int id, Model model) {
    		model.addAttribute("board", boardService.detail(id));
    		return "board/updateForm";
    	}
    • updateForm.jsp
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    
    <%@ include file="../layout/header.jsp"%>
    <div class="container">
    	<form>
    	<input type="hidden" id="id" value="${board.id}"/>
    		<div class="form-group">
    			<input value="${board.title}" type="text" class="form-control" placeholder="Enter title" id="title">
    		</div>
    		<div class="form-group">
    			<textarea class="form-control summernote" rows="5" id="content">${board.content}</textarea>
    		</div>
    	</form>
    	<button id="btn-update" class="btn btn-primary">글 수정 완료</button>
    </div>
    <script>
    	$('.summernote').summernote({
    		placeholder : '내용',
    		tabsize : 2,
    		height : 300
    	});
    </script>
    <script src="/js/board.js"></script>
    <%@ include file="../layout/footer.jsp"%>

    -> 아이디값이 있어야 어느 게시판을 불러올지 db에서 거르기때문에 hidden으로 처리하자

     

    • board.js
    let index = {
    	..
        $("#btn-update").on("click",()=>{ //function(){} 대신 ()=>{}를 써서 전역변수 this를 바인딩
    			this.update();
    		});
    	},
        ..
        update:function(){ 
    		let id = $("#id").val();
    		
    		let data = {
    			title:$("#title").val(),
    			content:$("#content").val()
    		};
    		$.ajax({
    			//ajax 기본 호출은 비동기
    			//오브젝트가 들어오는 곳
    			//통신 수행
    			type:"put",
    			url:"/api/board/"+id,
    			data:JSON.stringify(data),//HTTP BODY DATA
    			contentType:"application/json; charset=utf-8", //body데이터의 타입
    			dataType:"json" //서버에서 받을 데이터 형식, 즉 json으로 던지고 서버를위해 자동 파싱 = JSON->JS
    		}).done(function(resp){ //위의 데이터가 js로 바뀌고 파라미터로 사용 가능
    			//통신이 정상이면 done
    			alert("글 수정 완료");
    			location.href="/";
    		}).fail(function(error){
    			//통신이 비정상이면 fail
    			alert(JSON.stringify(error));
    		});
    	},
    }
    index.init();
    • BoardApiController
    	//글 수정 요청
    	@PutMapping("/api/board/{id}")
    	public ResponseDto<Integer> update(@PathVariable int id, @RequestBody Board board){
    		boardService.update(id, board);
    		return new ResponseDto<Integer>(HttpStatus.OK.value(),1);
    	}
    • BoardService
    	//글 수정하기
    	@Transactional
    	public void update(int id, Board requestBoard) {
    		Board board = boardRepository.findById(id)
    				.orElseThrow(()->{
    					return new IllegalArgumentException("글 찾기 실패!");
    				}); //영속화
    		board.setTitle(requestBoard.getTitle());
    		board.setContent(requestBoard.getContent());
    		//Transactional로 영속성 더티체킹!
    	}

    굿!

    * 자바스크립트가 반영이 빨리 안되면 캐시비우기로 한번 비우고 시작하자

     

     

Designed by Tistory.