D+47::스프링부트a태그/커맨드객체/게시판_등록/수정/삭제/검색기능구현

Am.Vinch·2022년 8월 31일
0
post-thumbnail

20220831_Wed


커맨드 객체

: 컨트롤러에 매개변수에 속해있으면서 데이터를 받기위해있는 객체

  • 1.넘어오는 데이터를 받는 역할
  • 2.데이터를 다음 페이지에 넘기지 않아도(model.addattribute() 없어도) 자동으로 넘어감
  • 단, 데이터가 넘어갈 때의 이름은 클래스명에서 앞글자만 소문자로 바뀐 이름으로만 넘어간다(이와 다를시 오류발생)
    • ex1)
      : 아래 코드에서 (BoardVO boardVO) 가 커맨드 객체다
      //글등록
      @GetMapping("/regBoard")
      public String regBoard(BoardVO boardVO) {
      				boardService.insertBoard(boardVO);
      				return "board/reg_board_result";
      			}
    • ex2)
      : 우리가 작성한 변수명으로 넘어가는 것이아니다!!!
      아래 코드에서 (BoardVO board)라고 변수명(board)을 작성해도 board로 넘어가는 것이 아니라, 클래스형 자료형 BoardVO 에서 앞글자만 바뀐 boardVO로만 넘겨진다.
      //글등록
      @GetMapping("/regBoard")
      public String regBoard(BoardVO board) {
      				boardService.insertBoard(board);
      				return "board/reg_board_result";
      			}

스프링부트: a태그_데이터 넘길때

  • a태그로 데이터 넘길 때 문법
    <a th:href="@{이동할경로(name='java',age=20)}" th:text="${board.title}"></a>
    • 실제 board_list.html 에서 제목클릭시 상세보기 페이지 이동 시 적용하면..
      <a th:href="@{/board/detail(boardNum=${board.boardNum})}" th:text="${board.title}"></a>
  • 실제 a태그 사용한 board_list.html
    <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<table border="1">
		<thead>
		<tr>
			<td>게시글 번호</td>
			<td>제목</td>
			<td>작성자</td>
			<td>작성일</td>
			<!-- 리스트크기 데이터 갖고오는 방법 2가지-->
			<!-- <td th:text="${#lists.size(boardList)}"></td>
			<td th:text="${boardList.size()}"></td> -->
		</tr>
		</thead>

		<tbody>
		<th:block th:if="${#lists.size(boardList) == 0}">
			<tr>
				<td colspan="4">게시글이 없습니다.</td>
			</tr>
		</th:block>
		<th:block th:unless="${#lists.size(boardList) == 0}">
			<tr th:each="board : ${boardList}"><!-- 컨트롤러에서 넘긴 그대로:boardList -->
				<td th:text="${board.boardNum}"></td>
				
                <td>
					<a th:href="@{/board/detail(boardNum=${board.boardNum})}" th:text="${board.title}"></a>
				</td> 
                
				<td th:text="${board.writer}"></td>
				<td th:text="${board.createDate}"></td>
			</tr>		
		</th:block>
		</tbody>

	</table>
<div align="center">
<!-- location.href 이동시, /board/... 식으로(컨트롤러이동) 작성해야한다! -->
	<input class="btn" type="button" value="글쓰기" onclick ="location.href='/board/regBoard';">
</div>
</body>
</html>

게시글_상세보기/삭제/수정 기능 구현

실습내용(어제 계속..)

  • 게시글 상세보기 기능 구현(a태그)
  • 게시글 수정/삭제/뒤로가기 기능 구현

<생성파일 목록>

  • BoardController
  • BoardVO
  • html파일:
    • boardList.html
    • reg_board.html
    • reg_board_result.html
    • detail_board.html
    • update_board.html
    • update_board_result.html
    • delete_board.html
  • board-mapper
  • boardService
  • boardServiceImpl

  • BoardController
 @Controller
@RequestMapping("/board")// "/board"로 시작하면 무조건 컨트롤러 실행하겠다
public class Boardcontroller {
	//boardService 라는 이름으로 만들어진 개체를 로드해서 
	//"의존성 주입"을 실행한다
	//@Resource는 @autoWired와 비슷하지만 자료형이 같은 객체가 아니라 
	//name값이 같은 변수명을 가져와서 데이터를 넣어준다
	
	//1-1.의존성 주입
	@Resource(name = "boardService")//boardService라는 이름의 객체를 가져오겠습니다!
	private BoardService boardService;//스프링에서는 선언만!
	
	//게시글 목록 페이지 
	@GetMapping("/list")
	public String selectBoardList(Model model) {
		//1.게시글 목록 조회(한줄요약)
		model.addAttribute("boardList",boardService.selectBoardList());
		
		return "board/board_list";//어디로 이동할래?
	}
	
	//(양식)글등록하는 페이지로 이동: Form양식페이지로 안가는 이유가 있다!!
	///regBoard 컨트롤러로 넘어올 때 중복인데 어노테이션이 다르기때문 괜찮다!
	@GetMapping("/regBoard")//a태그,href일경우...
	public String regBoard() {
		return "board/reg_board";
	}
	
	//글 등록
	@PostMapping("/regBoard")//form태그로 넘어올땐!
	//커맨드 객체(컨트롤러에 매개변수에 속해있으면서 데이터를 받기위해있는 객체)
	//1.넘어오는 데이터를 받는 역할 
	//2.데이터를 다음 페이지에 넘기지 않아도 자동으로 넘어감
	public String regBoard(BoardVO boardVO) {
		boardService.insertBoard(boardVO);
		return "board/reg_board_result";
	}
	
	//상세보기
	//넘어오는 데이터의 이름과 매개변수의 이름을 일치시키면
	//매개변수에 자동으로 데이터가 넘어온다
	//매개변수에 @RequestParam 어노테이션사용하면
	//넘어오는 데이터에 관한 설정을 할 수 있다.
		//속성 1. required : true/false 값이 올 수 있고,
		//넘어오는 데이터의 필수여부를 지정한다. (true면 필수,false면 뭐든 노상관)
		//속성 2. defaultValue :넘어오는 데이터가 없을 때, 기본값을 지정
	    //속성 3. name :넘어오는 데이터의 이름과 매개변수의 이름이 불일치할 때 데이터를 바인딩 시켜준다. 
	@GetMapping("/detail")
	public String detailBoard(@RequestParam(required = false) int boardNum, Model model) {
		model.addAttribute("board",boardService.selectDetailBoard(boardNum));
		return "board/detail_board";
	}
	
	//글수정 양식페이지로 이동
	@GetMapping("/update")
    public String update(int boardNum,Model model) {
		//실제 글수정 전, 먼저 상세정보 조회 먼저 해야한다
		model.addAttribute("board",boardService.selectDetailBoard(boardNum));
        return "board/update_board";
    }
	
	//글 수정
	@PostMapping("/update")
	    public String update( BoardVO boardVO) {
		boardService.updateBoard(boardVO);
	    return "redirect:/board/detail?boardNum=" + boardVO.getBoardNum();
	    }

	 //글 삭제
    @GetMapping("/delete")
	    public String delete(int boardNum) {
	    	boardService.deleteBoard(boardNum); 
	        return "redirect:/board/list";
	    }
}
  • BoardVO(어노테이션 사용!)
package kh.study.board.vo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

//@Data
@Getter
@Setter
@ToString
public class BoardVO {
	//lombok 어노테이션(위에) 을 이용해 getter,setter를 편하게 만들수있다
	private int boardNum;
	private String title;
	private String content;
	private String writer;
	private String createDate;

}
  • boardList.html

    a 태그로 넘길 때
    <a th:href="@{이동할경로(name='java',age=20)}" th:text="${board.title}"></a>

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style type="text/css">
table{
	width: 800px;
	margin: 0 auto;
	border-collapse: collapse;
	border: 1px solid black;
}
table>thead>tr:first-child{
	margin-bottom: 10px;
}
tr,td{
	border-collapse: collapse;
	border: 1px solid black;
}
.btn{
	text-align: center;
	margin-top: 20px;
}
</style>
</head>
<body>
	<table border="1">
		<thead>
		<tr>
			<td>게시글 번호</td>
			<td>제목</td>
			<td>작성자</td>
			<td>작성일</td>
			<!-- 리스트크기 데이터 갖고오는 방법 2가지-->
			<!-- <td th:text="${#lists.size(boardList)}"></td>
			<td th:text="${boardList.size()}"></td> -->
		</tr>
		</thead>

		<tbody>
		<th:block th:if="${#lists.size(boardList) == 0}">
			<tr>
				<td colspan="4">게시글이 없습니다.</td>
			</tr>
		</th:block>
		<th:block th:unless="${#lists.size(boardList) == 0}">
			<tr th:each="board : ${boardList}"><!-- 컨트롤러에서 넘긴 그대로:boardList -->
				<td th:text="${board.boardNum}"></td>
				<td>
					<!-- a태그로 데이터 넘길 때 문법 <a th:href="@{이동할경로(name='java',age=20)}" th:text="${board.title}"></a> -->
					<a th:href="@{/board/detail(boardNum=${board.boardNum})}" th:text="${board.title}"></a>
				</td> 				
				<td th:text="${board.writer}"></td>
				<td th:text="${board.createDate}"></td>
			</tr>		
		</th:block>
		</tbody>

	</table>
<div align="center">
<!-- location.href 이동시, /board/... 식으로(컨트롤러이동) 작성해야한다! -->
	<input class="btn" type="button" value="글쓰기" onclick ="location.href='/board/regBoard';">
</div>
</body>
</html>
  • reg_board.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- jsp에서 foreach문 사용할때 lib태그 사용하는 것과 같다.앞으로 html 만들때는 사용해야한다. 원래 html만들때 next누르고 별도로 생성한 htnl_thymeleaf를 선택하면 자동생성된다-->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/board/regBoard" method="post"><!--form 하면 반드시 submit 필요 (onclick삭제)  -->
	<table>
		<tr>
			<td>제목</td>	
			<td><input name="title" type="text" ></td>	
		</tr>
		<tr>
			<td>작성자</td>	
			<td><input name="writer" type="text" ></td>	
		</tr>
		<tr>
			<td>내용</td>	
			<td><textarea rows="3" cols="50" name="content"></textarea></td>	
		</tr>
	</table>
<div align="center">
	<input type="submit" value="글등록">
	<input type="button" value="뒤로가기" onclick="location.href='/board/list';">
</div>
</form>
</body>
</html>
  • reg_board_result.html
   <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- jsp에서 foreach문 사용할때 lib태그 사용하는 것과 같다.앞으로 html 만들때는 사용해야한다. 원래 html만들때 next누르고 별도로 생성한 htnl_thymeleaf를 선택하면 자동생성된다-->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
	alert('글 등록되었습니다.');
	location.href='/board/list';
</script>
<!-- 컨트롤러에서 따로 데이터를 넘겨라는 코드가 없어도 자동으로 데이터가 받아짐 -->
<!--
데이터 자동으로 넘겨질 때 :
<span th:text="${boardVO.title}" ></span>
데이터 넘겨지지 않을 때:
<span th:text="${board.title}" ></span> 
-->
</body>
</html>

타임리프_페이지이동(버티컬바 ||)

  • detail_board.html

데이터가져가면서 페이지 이동 할 때

  • 스프링에서 타임리프로 데이터넘기면서 페이지이동 방법
    <input type="button" class="btn" value="삭제1" th:onclick="'location.href=\'' + @{/board/delete(boardNum=${board.boardNum})}+'\''" >
  • 버티컬바 사용시 더 편리하게 코드작성 가능
    <input type="button" class="btn" value="삭제2" th:onclick="|location.href='@{/board/delete(boardNum=${board.boardNum})}'|">

<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>게시글 상세보기 페이지</h2>
<table border="1">
	<tr>
		<td>글 번호</td>
		<td th:text="${board.boardNum}"></td>
	</tr>
	<tr>
		<td>글 제목</td>
		<td th:text="${board.title}"></td>
	</tr>
	<tr>
		<td>작성자</td>
		<td th:text="${board.writer}"></td>
	</tr>
	<tr>
		<td>작성일</td>
		<td th:text="${board.createDate}"></td>
	</tr>
	<tr>
		<td>글 내용</td>
		<td th:text="${board.content}"></td>
	</tr>
</table> 

 <div>
	<input type="button" class="btn" value="수정" th:onclick="|location.href='@{/board/update(boardNum=${board.boardNum})}'|"> 
	<input type="button" class="btn" value="삭제" th:onclick="|location.href='@{/board/delete(boardNum=${board.boardNum})}'|"> 
	<input type="button" class="btn" value="돌아가기" onclick ="location.href='/board/list';"> 
	
	<!-- 삭제버튼 설명 -->
	<!-- 삭제 1 : 이전 jsp 방식일때  -->
<!-- 	<input type="button" class="btn" value="삭제1" >-->	
	<!-- 삭제 2 : 숫자10 으로 바로넘길때 오류발생안남 -->
<!-- 	<input type="button" class="btn" value="삭제2" >
	위의 숫자 10으로 넘어가는 것처럼 ${board.baordNum}숫자로 넘겨지는게 아니라 문자그대로만 넘겨져 오류발생
	<input type="button" class="btn" value="삭제2" >
	삭제3 : 타임리프방식(스프링) \ 이스케이프문자 사용시 ''홀따옴표도 글자취급가능
	<input type="button" class="btn" value="삭제3" th:onclick="'location.href=\'' + @{/board/delete(boardNum=${board.boardNum})}+'\''" >
 	<!-- 스프링에서 타임리프로 데이터넘기면서 페이지이동 방법 -->
<!-- 	<input type="button" class="btn" value="삭제1" th:onclick="'location.href=\'' + @{/board/delete(boardNum=${board.boardNum})}+'\''" > 
	버티컬바 || 사용시
	<input type="button" class="btn" value="삭제2" th:onclick="|location.href='@{/board/delete(boardNum=${board.boardNum})}'|">	
 -->
</div> 
</body>
</html>
  • update_board.html
   <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>글 수정</h2>
<form action="/board/update" method="post">
<input type="hidden" name="boardNum" th:value="${board.boardNum}">
	<table>
		<tr>
			<td>글 번호</td>
			<td><span th:text="${board.boardNum}"></span></td>
		</tr>
		<tr>
			<td>작성일</td>
			<td><span th:text="${board.createDate}"></span></td>
		</tr>
		<tr>
			<td>작성자</td>
			<td><input type="text" name="writer" th:value=${board.writer}></td>
		</tr>
		<tr>
			<td>글 제목</td>
			<td><input type="text" name="title" th:value="${board.title}"></td>
		</tr>
		<tr>
			<td>글 내용</td>
			<td><textarea rows="5" cols="100" name="content" th:value="${board.content}"></textarea> </td>
		</tr>
</table>
<div>
	<input type="submit" class="btn" value="수정완료" >
</div>
</form>
</body>
</html>
  • update_board_result.html
   <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- jsp에서 foreach문 사용할때 lib태그 사용하는 것과 같다.앞으로 html 만들때는 사용해야한다. 원래 html만들때 next누르고 별도로 생성한 htnl_thymeleaf를 선택하면 자동생성된다-->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
	alert('수정 완료!');
	location.href='/board/list';
</script>
</body>
</html>
  • board-mapper
 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--(여기는 이클립스와 같다) 해당 파일에 모든 쿼리문을 작성 -->
<mapper namespace="boardMapper">
	<resultMap type="kh.study.board.vo.BoardVO" id="board">
		<id column="BOARD_NUM" property="boardNum"/>
		<result column="TITLE" property="title"/>
		<result column="WRITER" property="writer"/>
		<result column="CONTENT" property="content"/>
		<result column="CREATE_DATE" property="createDate"/>
	</resultMap>

<!-- 목록조회 -->
	<select id="selectBoardList" resultMap="board">
		SELECT BOARD_NUM 
		, TITLE
		, WRITER
		, CREATE_DATE
		FROM SPRING_BOARD
		ORDER BY BOARD_NUM DESC
	</select>
	
<!-- 게시글등록 -->	
	<insert id="insertBoard">
		INSERT INTO SPRING_BOARD (
			BOARD_NUM
			, TITLE
			, CONTENT
			, WRITER
		) VALUES(
			BOARD_NUM_SEQ.NEXTVAL
			, #{title}
			, #{content}
			, #{writer}
		) 
	</insert>
	
	<!-- 상세보기 -->
	<select id="selectDetailBoard" resultMap="board">
		SELECT BOARD_NUM
			, TITLE
			, WRITER
			, CONTENT
			, TO_CHAR(CREATE_DATE,'YYYY-MM-DD') AS CREATE_DATE
		FROM SPRING_BOARD
		WHERE BOARD_NUM = #{boardNum}
	</select>
	
	<!-- 글 수정 -->
	<update id="updateBoard">
		UPDATE SPRING_BOARD
		SET TITLE = #{title}
			, WRITER = #{writer}
			, CONTENT = #{content}
		WHERE BOARD_NUM = #{boardNum}
	</update>
	
	<!-- 글 삭제 -->
	<delete id="deleteBoard">
		DELETE FROM SPRING_BOARD
		WHERE BOARD_NUM = #{boardNum}
	</delete>
</mapper>
  • boardService
 package kh.study.board.service;

import java.util.List;

import kh.study.board.vo.BoardVO;

public interface BoardService {
	//게시글 목록조회
	List<BoardVO> selectBoardList();
	
	//글쓰기 등록
	void insertBoard(BoardVO boardVO);
	
	//상세보기
	BoardVO selectDetailBoard(int boardNum);
	//글 수정
	void updateBoard(BoardVO boardVO);
	//글 삭제
	void deleteBoard(int boardNum);
}
  • boardServiceImpl
 package kh.study.board.service;
import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import kh.study.board.vo.BoardVO;

@Service("boardService")//괄호안에 빈값이면 클래스명 소문자로 BoardServiceImpl 객체자동생성되기 때문에
public class BoardServiceImpl implements BoardService {
		   //쿼리 실행하는 객체
//		   SqlSessionFactory sqlSessionFactory = SqlSessionManager.getSqlSession();
//		   SqlSession sqlSession;//생성이 아닌 선언만
	
	//@Autowired어노테이션은 미리 생성한 객체를 의존성 주입 시킬 때 사용한다.
	//아래코드가 진행이 된다는 것은, 프로젝트 실행 시
	// 스프링이 내부적으로 sqlSessionTemplate 자료형으로 객체를 미리 만들어 놓기 때문에 사용가능하다.
	//@Autowired는 그래서 동일한 자료형으로 객체가 미리 2개 이상 생성되어있다면,
	//프로젝트 실행과 동시에 오류 발생
	//-> DB로 이동
	@Autowired//자동연결 (기준 자료형이 같은 객체만)
	SqlSessionTemplate sqlSession;

	
	//게시글 목록조회
	@Override
	public List<BoardVO> selectBoardList() {
		//원래 이클립스에서 하는 방식
//		List<BoardVO>  list = sqlSession.selectList("boardMapper.selectBoardList");
//		return list;
//		sqlSession.commit();(스프링은 자동 커밋)
		
		//->(스프링) 그래서 이제 부터 한 줄로 작성 가능해짐
		return sqlSession.selectList("boardMapper.selectBoardList");
	}
	
	//글쓰기 등록
	@Override
	public void insertBoard(BoardVO boardVO) {
		sqlSession.insert("boardMapper.insertBoard",boardVO);
	}
	//상세보기
	@Override
	public BoardVO selectDetailBoard(int boardNum) {
		return sqlSession.selectOne("boardMapper.selectDetailBoard",boardNum);
	}
	//글 수정
	@Override
	public void updateBoard(BoardVO boardVO) {
		sqlSession.update("boardMapper.updateBoard",boardVO);
	}
	//글 삭제
	@Override
	public void deleteBoard(int boardNum) {
		sqlSession.delete("boardMapper.deleteBoard",boardNum);
	}
}

검색기능

실습내용

  • 게시판 게시글 검색기능 구현
    <순서>
  • ① board_list.html 에서 테이블 위에 검색기능 추가
  • ② board-mapper에서 쿼리문 수정 (목록조회)
  • ③ baordService/ boardServiceImpl 각각 매개변수(String title) 수정
  • ④ baordController 목록조회 메소드수정

  • ① board_list.html 에서 테이블 위에 검색기능 추가
<!-- 검색기능 구현 -->
<form action="/board/list" method="post">
	<input type="text" name="title">
	<input type="submit" value="검색"><br>
</form>
  • ② board-mapper에서 쿼리문 수정 (목록조회)

쿼리문 작성시, 바로 test 값을 사용해도 되는 이유?

  • 빈값채울 때 빈값채우기 위해서 넘어오는 데이터 : _parameter
    : mybatis 에서 title 그래로 작성하면 getter인 title.getTitle()를 호출한다.
    getter는 빈값이 보이면 자동으로 데이터를 넘겨주는데 목록조회 쿼리문에서는 title 하나의 빈값만 존재하므로 _parameter 값을 사용한다
<select id="selectBoardList" resultMap="board">
		SELECT BOARD_NUM 
		, TITLE
		, WRITER
		, CREATE_DATE
		FROM SPRING_BOARD
		<if test="_parameter != null and !_parameter.equals('')">
		WHERE TITLE LIKE '%'||#{title}||'%'
		</if>
		ORDER BY BOARD_NUM DESC
	</select>
  • ③ baordService/ boardServiceImpl 각각 매개변수(String title) 수정

    //게시글 목록조회
    		List<BoardVO> selectBoardList(String title);
    
    //게시글 목록조회
    		@Override
    		public List<BoardVO> selectBoardList(String title) {
    			return sqlSession.selectList("boardMapper.selectBoardList",title);
    		}  
  • ④ baordController 목록조회 메소드수정

    Q. title 데이터 값을 넘겨받을 때, title 값이 목록조회 쿼리문에서 처럼 언제 null값이고 빈값인지 어떻게 구분하는가?
    : System.out.println("title="+ title); 으로 각각의 상황에 따라 콘솔창 확인하여 구분한다

    • 주소창에 기본경로 http://localhost:8081/board/list 페이지 바로 입력시
      -> title = null 값
    • 게시글 목록페이지에서 빈값으로 검색버튼 클릭시
      -> title = '' 빈값
 //게시글 목록 페이지 
	@RequestMapping("/list")//get,post 모두 받는 어노테이션
	public String selectBoardList(Model model,String title) {
		
		System.out.println("title="+ title);
		model.addAttribute("boardList",boardService.selectBoardList(title));
		return "board/board_list";//어디로 이동할래?
	}    
profile
Dev.Vinch

0개의 댓글