Luver Duck 2022. 9. 16. 01:47

- 하루동안 게시판을 구현하는 과제가 있었음 (수업 X)

- 해당 내용은 0919의 게시판 구현 부분임

 

게시판 구현

테이블 생성

게시판(board)
- 회원이 작성하는 글 정보
- 회원 정보가 외래키로 존재해야함
- 회원이 탈퇴하면 작성한 글의 작성자를 제거

구성요소
- 글 번호(board_no) - 유일한 식별키, 시퀀스로 부여
- 작성자(board_writer) - 회원의 아이디 정보, 외래키로 설정
- 제목(board_title) - 글의 제목 (한글 100자 기준으로 설정)
- 내용(board_content) - 글의 내용, 가능한 최대로 설정 (4000byte)
- 작성일(board_writetime) - 작성된 시점의 데이터베이스 시각
- 수정일(board_updatetime) - 수정된 시점의 데이터베이스 시각, 작성시에는 작성 시각으로 설정
- 조회수(board_read) - 글을 읽으면 자동으로 증가하도록 숫자 항목으로 구현
- 좋아요(board_like) - 회원이 글에 설정한 좋아요 갯수(추가 테이블 필요)
- 말머리(board_head) - 다음 항목(정보/유머/공지) 중 하나 혹은 미설정 가능

 

-- board 테이블 생성
create table board (
board_no number primary key,
board_writer references member(member_id) on delete set null,
board_title varchar2(300) not null,
board_content varchar2(4000) not null,
board_writetime date default sysdate not null,
board_updatetime date,
board_read number default 0 not null check(board_read >= 0),
board_like number default 0 not null check(board_like >= 0),
board_head varchar2(6) check(board_head in('정보', '유머', '공지'))
);

-- 시퀀스 생성
create sequence board_seq;

 

flow chart 구상

 

BoardDto 생성

- lombok을 이용하면 DTO 생성을 간결하게 할 수 있다

@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class BoardDto {

	// 필드
	private int boardNo;
	private String boardWriter;
	private String boardTitle;
	private String boardContent;
	private Date boardWritetime;
	private Date boardUpdatetime;
	private int boardRead;
	private int boardLike;
	private String boardHead;	
}

 

1. 게시글 작성 

- 게시글 작성은 form에서 전송한 DTO를 받아서 BOARD 테이블에 등록(INSERT)을 한다

 

BoardDao

public interface BoardDao {
	
	// 추상 메소드 - 게시글 작성 (다음 시퀀스 번호를 미리 생성 후 등록)
	void write(BoardDto boardDto)
}

 

BoardDaoImpl

- 게시글 작성 후 작성한 게시글로 redirect 해야 한다

- 작성한 게시글로 redirect 하기 위해서는 해당 게시글의 번호를 알아야 한다

- 다음 시퀀스 번호를 생성하는 SQL문을 실행하여 작성할 글 번호를 반환받은 후 이 값을 매개변수 배열에 대입한다,

@Repository
public class BoardDaoImpl implements BoardDao {

	// 의존성 주입
	@Autowired
	JdbcTemplate jdbcTemplate;

	// 추상 메소드 오버라이딩 - 게시글 작성 (다음 시퀀스 번호를 미리 생성 후 등록)
	@Override
	public int insert2(BoardDto boardDto) {
		// 다음 시퀀스 번호를 미리 생성
		String sql = "select board_seq.nextval from dual";
		int boardNo = jdbcTemplate.queryForObject(sql, int.class);
		
		sql = "insert into board("
					+ "board_no, board_title, board_content,"
					+ "board_writer, board_head"
				+ ") values(?, ?, ?, ?, ?)";
		Object[] param = {
			boardNo, boardDto.getBoardTitle(),
			boardDto.getBoardContent(), boardDto.getBoardWriter(),
			boardDto.getBoardHead()
		};
		jdbcTemplate.update(sql, param);
		
		return boardNo;
	}
}

 

boardController

@Controller
@RequestMapping("/board")
public class BoardController {

	// 의존성 주입
	@Autowired
	BoardDao boardDao;
	
	// 1. 게시글 작성 
	// 1) 게시글 작성 Mapping
	@GetMapping("/write")
	public String write() {
		return "board/write";
	}
	
	// 2) 게시글 작성 Mapping에 DTO 전달 및 DB 처리 
	@PostMapping("/write")
	public String write(HttpSession session, @ModelAttribute BoardDto boardDto, RedirectAttributes attr) {
		
		// 게시글 작성자(boardWriter)가 로그인중인 아이디가 되도록 session에서 회원 아이디를 반환
		String boardWriter = (String) session.getAttribute("loginId");
		
		// setter 메소드로 게시글 작성자(boardWriter)를 session에서 반환한 로그인 아이디로 설정
		boardDto.setBoardWriter(boardWriter);
		
		// 게시글 번호(boardNo)를 위한 다음 시퀀스 번호 반환
		int boardNo = boardDao.sequence();
		
		// setter 메소드로 게시글 번호(boardNo)가 다음 시퀀스 번호가 되도록 설정
		boardDto.setBoardNo(boardNo);
		
		// 설정이 끝난 boardDto로 게시글 작성 실행
		boardDao.write(boardDto);
		
		// 게시글 작성 후 해당 게시글의 상세 Mapping으로 강제 이동(redirect)
		attr.addAttribute("boardNo", boardNo);
		return "redirect:detail";
	}
}

 

write.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<jsp:include page="/WEB-INF/views/template/header.jsp">
	<jsp:param value="자유 게시판" name="title"/>
</jsp:include>

<h1>게시글 작성</h1>

<div align = "center">
	<form action="write" method="post">
	<table border="1" width="500">
		<tbody>
			<tr>
				<th>말머리</th>
				<td>
					<select name="boardHead">
						<option value="">선택</option>
						<option>정보</option>
						<option>유머</option>
						
						<c:if test="${mg == '관리자'}">
						<option>공지</option>
						</c:if>
					</select>
				</td>
			</tr>
			<tr>
				<th>제목</th>
				<td>
					<input type="text" name="boardTitle" required>
				</td>
			</tr>
			<tr>
				<th>내용</th>
				<td>
					<textarea name="boardContent" rows="10" cols="50" required></textarea>
				</td>
			</tr>
		</tbody>
		<tfoot>
			<tr>
				<td align="right" colspan="2">
					<a href="list">목록으로</a>
					<button type="submit">등록하기</button>
				</td>
			</tr>
		</tfoot>
	</table>
	</form>
</div>

<jsp:include page="/WEB-INF/views/template/footer.jsp"></jsp:include>

 

- session에 저장된 mg(회원등급)이 관리자일 때 공지라는 말머리를 사용할 수 있도록 설정한다

- <form></form>과 <table></table>을 함께 쓸 때 <form></form>가 <table></table>을 감싸도록 한다

- <textarea></textarea>는 <input>과는 달리 여러 줄 입력이 가능하도록 하는 태그

<textarea></textarea> name <form></form>으로 값 전송시 변수명
rows 가로 줄 수 (행의 수)
cols 세로 줄 수 (열의 수)

 

 

2. 게시글 목록

- 조회 유형(전체 조회 / 검색 조회)에 맞는 조회(SELECT) 명령을 실행한 후 그 결과를 model에 첨부하여 View에 보낸다

- 게시글 목록 페이지에는 전체 조회 / 검색 조회의 결과가 표시되도록 한다

 

BoardDao

public interface BoardDao {

	// 추상 메소드 - 게시글 조회
	// 1) 전체 조회
	List<BoardDto> selectList();
	
	// 2) 검색 조회
	List<BoardDto> selectList(String type, String keyword);
}

 

BoardDaoImpl

@Repository
public class BoardDaoImpl implements BoardDao {

	// 의존성 주입
	@Autowired
	JdbcTemplate jdbcTemplate;

	// 게시글 조회를 위한 RowMapper
	private RowMapper<BoardDto> mapper = new RowMapper<>() {
		@Override
		public BoardDto mapRow(ResultSet rs, int rowNum) throws SQLException {
			return BoardDto.builder()
					.boardNo(rs.getInt("board_no"))
					.boardWriter(rs.getString("board_writer"))
					.boardTitle(rs.getString("board_title"))
					.boardContent(rs.getString("board_content"))
					.boardWritetime(rs.getDate("board_writetime"))
					.boardUpdatetime(rs.getDate("board_updatetime"))
					.boardRead(rs.getInt("board_read"))
					.boardLike(rs.getInt("board_like"))
					.boardHead(rs.getString("board_head"))
				.build();
		}
	};

	// 추상 메소드 오버라이딩 - 게시글 조회
	// 1) 전체 조회
	@Override
	public List<BoardDto> selectList() {
		String sql = "select * from board order by board_no desc";
		return jdbcTemplate.query(sql, mapper);
	}

	// 2) 검색 조회
	@Override
	public List<BoardDto> selectList(String type, String keyword) {
		String sql = "select * from board where instr(#1, ?) > 0 order by board_no desc";
		sql = sql.replace("#1", type);
		Object[] param = new Object[] {keyword};
		return jdbcTemplate.query(sql, mapper, param);
	}
}

 

BoardController

@Controller
@RequestMapping("/board")
public class BoardController {

	// 의존성 주입
	@Autowired
	BoardDao boardDao;
	
	// 2. 게시글 목록
	// 게시글 목록 Mapping
	@GetMapping("/list")
	public String selectList(Model model, 
				@RequestParam(required = false) String type, 
				@RequestParam(required = false) String keyword
				) {
		
		// 조회 유형 판정
		boolean isSearch = type != null && keyword != null;
		if(isSearch) {	// 검색 조회라면
			
			// 게시글 검색 조회 실행 후 그 결과를 List에 BoardDto의 형태로 반환
			List<BoardDto> list = boardDao.selectList(type, keyword);
			
			// 조회 결과를 model에 첨부
			model.addAttribute("list", list);
		}
		else {	// 검색 조회가 아니라면
		
			// 게시글 전체 조회 실행 후 그 결과를 List에 BoardDto의 형태로 반환
			List<BoardDto> list = boardDao.selectList();
			
			// 조회 결과를 model에 첨부
			model.addAttribute("list", list);
		}
		
		// 게시글 목록(list.jsp)로 연결
		return "board/list";
	}
}

 

** VO를 이용하여 여러 @RequestParam을 한 번에 입력받을 수 있다

BoardListSearchVO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class BoardListSearchVO {

	// 필드
	private String type;
	private String keyword;
	
	// 메소드 - 조회 유형이 검색 조회인지 여부 반환
	public boolean isSearch() {
		return type != null && keyword != null;
	}
}

 

BoardDao

public interface BoardDao {

	// 추상 메소드 - 게시글 조회
	// 3) BoardListSearchVO를 이용한 통합 조회
	List<BoardDto> selectList(BoardListSearchVO vo);
}

 

BoardDaoImpl

@Repository
public class BoardDaoImpl implements BoardDao {

	// 의존성 주입
	@Autowired
	JdbcTemplate jdbcTemplate;

	// 추상 메소드 오버라이딩 - 게시글 조회
	// 3) BoardListSearchVO를 이용한 통합 조회
	@Override
	public List<BoardDto> selectList(BoardListSearchVO vo) {
		String sql = "select * from board where instr(#1, ?) > 0 order by board_no desc";
		sql = sql.replace("#1", vo.getType());
		Object[] param = new Object[] {vo.getKeyword()};
		return jdbcTemplate.query(sql, mapper, param);
	}
}

 

BoardController

- @RequestParam인 String type, String keyword를 묶어 VO로 만들 수 있다

- @ModelAttributes의 name을 vo로 하여 View에서 해당 name을 변수명으로 사용할 수 있다

 

@Controller
@RequestMapping("/board")
public class BoardController {

	// 의존성 주입
	@Autowired
	BoardDao boardDao;
	
	// 게시글 목록
	// 게시글 목록 Mapping
	@GetMapping("/list")
	public String selectList(Model model, @ModelAttribute(name = "vo") BoardListSearchVO vo) {
		
		// 조회 유형 판정 - BoardListSearchVO의 isSearch() 메소드
		if(vo.isSearch()) {	// 검색 조회일 경우
			// model에 검색 조회 selectList(BoardListSearchVO vo)의 결과를 첨부
			model.addAttribute("list", boardDao.selectList(vo));
		}
		else {	// 그렇지 않다면
			// model에 전체 조회 selectList()의 결과를 첨부
			model.addAttribute("list", boardDao.selectList());
		}
		
		// 게시글 목록(list.jsp)로 연결
		return "board/list";
	}
}

 

list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix = "fmt" uri = "http://java.sun.com/jsp/jstl/fmt" %>

<%-- 오늘 날짜를 구해서 today라는 변수로 만든다 --%>
<jsp:useBean id = "now" class = "java.util.Date"></jsp:useBean>
<c:set var = "today">
	<fmt:formatDate value = "${now}" pattern = "yyyy-MM-dd"/>
</c:set>

<jsp:include page = "/WEB-INF/views/template/header.jsp">
	<jsp:param value = "게시글 목록" name = "title"/>
</jsp:include>

<div align = "center">
<h1>게시글 목록</h1>

<table border = "1" width = "900">
<thead>
	<%-- 로그인 상태에서만 글쓰기 항목이 보이도록 설정 --%>
	<c:if test = "${loginId != null}">
	<tr>
		<td align = "right" colspan = "5">
			<a href = "write">글쓰기</a>
		</td>
	</tr>
	</c:if>
	
	<tr>
		<th>번호</th>
		<th width = "45%">제목</th>
		<th>작성자</th>
		<th>작성일</th>
		<th>조회수</th>
	</tr>
</thead>

<tbody align = "center">
	<c:forEach var = "boardDto" items = "${list}">
	<tr>
		<td>${boardDto.boardNo}</td>
		<td align = "left">
		
			<%-- 말머리가 있을 경우에만 출력 --%>
			<c:if test = "${boardDto.boardHead != null}">
				[${boardDto.boardHead}]
			</c:if>
			
			<%-- 제목을 누르면 해당 게시글의 상세 페이지로 이동하도록 --%>
			<a href = "detail?boardNo=${boardDto.boardNo}">
				${boardDto.boardTitle}
			</a>
			
		</td>
		<td>${boardDto.boardWriter}</td>
		<td>
			<%-- 작성 날짜를 current라는 변수로 만든다 --%>
			<c:set var = "current">
				<fmt:formatDate value = "${boardDto.boardWritetime}" pattern = "yyyy-MM-dd"/>
			</c:set>
			
			<c:choose>
				<%-- today(오늘 날짜)와 current(작성 날짜)가 같다면 시간과 분만 표시--%>
				<c:when test = "${today == current}">	
					<fmt:formatDate value = "${boardDto.boardWritetime}" pattern = "HH:mm"/>
				</c:when>
				<%-- 그렇지 않다면 년도-월-일로 표시 --%>
				<c:otherwise>
					<fmt:formatDate value = "${boardDto.boardWritetime}" pattern = "yyyy-MM-dd"/>
				</c:otherwise>
			</c:choose>
		</td>
		<td>${boardDto.boardRead}</td>
	</tr>
	</c:forEach>
</tbody>

<c:if test = "${loginId != null}">
<tfoot>
	<tr>
		<td align = "right" colspan = "5">
			<a href = "write">글쓰기</a>
		</td>
	</tr>
</tfoot>
</c:if>

</table>

<%-- 페이지 네비게이터 --%>
<h3> &laquo; &lt; 1 2 3 4 5 6 7 8 9 10 &gt; &raquo; </h3>

<%--검색창 --%>
<form action = "list" method = "get">
<select name = "type" required>
	<option value = "board_title" <c:if test = "${vo.type == 'board_title'}">selected</c:if>>제목</option>
	<option value = "board_content" <c:if test = "${vo.type == 'board_content'}">selected</c:if>>작성자</option>
	<option value = "board_writer" <c:if test = "${vo.type == 'board_writer'}">selected</c:if>>작성자</option>
</select>

<input type = "search" name = "keyword" placeholder = "검색어" required value = "${vo.keyword}">

<button type = "submit">검색</button>
</form>
</div>

<jsp:include page = "/WEB-INF/views/template/footer.jsp"></jsp:include>

 

3. 게시글 상세

- 게시글 상세 Mapping으로 이동하기 위해서는 게시글 번호(boardNo)가 필요하다

- Controller에서 게시글 번호(boardNo)를 요청 파라미터로 받아야 단일 조회를 실행할 수 있다

- 게시글 목록에서 게시글 상세로 이동할 때 해당하는 열의 게시글 번호(boardNo)와 그 값을 포함하는 하이퍼링크를 통해

  게시글 상세 Mapping에 요청 파라미터를 전달할 수 있다

- 게시글 번호(boardNo)를 받아 단일 조회(DETAIL) 명령을 수행한 후 그 결과를 model에 첨부하여 View에 보낸다

- 게시글 상세 페이지에는 단일 조회의 결과가 표시되도록 한다

- 게시글 상세 페이지에 들어올 때 해당 게시글의 게시글 조회수(boardRead)가 1씩 증가하도록 한다

- 만약 이전에 들어와본 게시글이라면 해당 게시글의 조회수가 증가하지 않도록 한다

 

BoardDao

public interface BoardDao {

	// 추상 메소드 - 게시글 상세
	// 1) 게시글 상세 - 조회수가 올라가지 않는 단일 조회
	BoardDto selectOne(int boardNo);
	
	// 2) 게시글 상세 - 조회수가 올라가는 단일 조회
	BoardDto read(int boardNo);
	
	// 3) 조회수 증가
	boolean updateReadCount(int boardNo);
}

 

BoardDaoImpl

@Repository
public class BoardDaoImpl implements BoardDao {

	// 의존성 주입
	@Autowired
	JdbcTemplate jdbcTemplate;
	
	// 게시글 상세를 위한 ResultSetExtractor
	private ResultSetExtractor<BoardDto> extractor = new ResultSetExtractor<>() {
		@Override
		public BoardDto extractData(ResultSet rs) throws SQLException, DataAccessException {
			if(rs.next()) {
				return BoardDto.builder()
						.boardNo(rs.getInt("board_no"))
						.boardWriter(rs.getString("board_writer"))
						.boardTitle(rs.getString("board_title"))
						.boardContent(rs.getString("board_content"))
						.boardWritetime(rs.getDate("board_writetime"))
						.boardUpdatetime(rs.getDate("board_updatetime"))
						.boardRead(rs.getInt("board_read"))
						.boardLike(rs.getInt("board_like"))
						.boardHead(rs.getString("board_head"))
					.build();
			}
			else {
				return null;
			}
		}	
	};
	
	// 추상 메소드 오버라이딩 - 게시글 상세
	// 게시글 상세 - 조회수가 올라가지 않는 단일 조회
	@Override
	public BoardDto selectOne(int boardNo) {
		String sql = "select * from board where board_no = ?";
		Object[] param = new Object[] {boardNo};
		return jdbcTemplate.query(sql, extractor, param);
	}
}

 

BoardController

@Controller
@RequestMapping("/board")
public class BoardController {

	// 의존성 주입
	@Autowired
	BoardDao boardDao;

	// 3. 게시글 상세
	// 게시글 상세 Mapping
	@GetMapping("/detail")
	public String detail(Model model, @RequestParam int boardNo) {
		
		// 게시글 상세의 결과를 model에 첨부
		model.addAttribute("boardDto", boardDao.selectOne(boardNo));
		
		// 게시글 상세 페이지(detail.jsp)로 연결
		return "board/detail";
	}
}

 

detail.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix = "fmt" uri = "http://java.sun.com/jsp/jstl/fmt" %>

<jsp:include page = "/WEB-INF/views/template/header.jsp">
	<jsp:param value = "게시글 상세" name = "title"/>
</jsp:include>

<div align = "center">
<h1>게시글 상세</h1>

<table border = "1" width = "900">
<tbody>
	<tr>
		<th width = "25%">번호</th>
		<td>${boardDto.boardNo}</td>
	</tr>
	<tr>
		<th>말머리</th>
		<td>${boardDto.boardHead}</td>
	</tr>
	<tr>
		<th>제목</th>
		<td>${boardDto.boardTitle}</td>
	</tr>
	<tr>
		<th>작성자</th>
		<td>${boardDto.boardWriter}</td>
	</tr>
	<tr height = "200" valign = "top">
		<th>내용</th>
		<td>
			<%-- pre 태그는 엔터, 띄어쓰기, 탭키 등을 있는 그대로 표시하는 영역 --%>
			<pre>${boardDto.boardContent}</pre>
		</td>
	</tr>
	<tr>
		<th>조회수</th>
		<td>${boardDto.boardRead}</td>
	</tr>
	<tr>
		<th>작성일</th>
		<td>
			<fmt:formatDate value = "${boardDto.boardWritetime}" pattern = "y년 M월 d일 E요일 a h시 m분 s초"/>
		</td>
	</tr>
	<%-- 한 번이라도 수정한 적이 있다면(수정 시간이 null이 아니라면) 수정 시간을 표시 --%>
	<c:if test = "${boardDto.boardUpdatetime != null}">
		<tr>
			<th>수정일</th>
			<td>
				<fmt:formatDate value = "${boardDto.boardUpdatetime}" pattern = "y년 M월 d일 E요일 a h시 m분 s초"/>
			</td>
		</tr>
	</c:if>
</tbody>
<tfoot>
	<tr>
		<td colspan = "2" align = "right">
			<%-- 로그인 상태에서만 게시글 작성이 가능하도록 설정 --%>
			<c:if test = "${loginId != null}">
				<a href = "write">글쓰기</a>
			</c:if>
			
			<%-- 로그인한 회원이 게시글 작성자인지에 대한 변수(boolean) 생성 --%>
			<c:set var = "owner" value = "${loginId == boardDto.boardWriter}"></c:set>
			
			<%-- 로그인한 회원의 등급이 관리자인지에 대한 변수(boolean) 생성 --%>
			<c:set var = "admin" value = "${mg == '관리자'}"></c:set>
			
			<%-- 만약 로그인한 회원이 게시글 작성자라면 게시글을 수정할 수 있도록 설정--%>
			<c:if test = "${owner}">	
				<a href = "edit?boardNo=${boardDto.boardNo}">수정</a>
			</c:if>
			
			<%-- 만약 로그인한 회원이 게시글 작성자이거나 등급이 관리자라면 게시글을 삭제할 수 있도록 설정--%>
			<c:if test = "${owner || admin}">	
				<a href = "delete?boardNo=${boardDto.boardNo}">삭제</a>
			</c:if>
			
			<a href = "list">목록으로</a>
		</td>
	</tr>
</tfoot>
</table>
</div>

<jsp:include page = "/WEB-INF/views/template/footer.jsp"></jsp:include>

 

4. 게시글 수정

- 게시글 수정 페이지로 이동하기 위해서는 해당 게시글 번호(boardNo)가 필요하다

- Controller에서 게시글 번호(boardNo)를 요청 파라미터로 받아 단일 조회의 결과를 model에 첨부하여 View에 보낸다

- 게시글 목록에서 게시글 상세로 이동할 때 해당하는 열의 게시글 번호(boardNo)와 그 값을 포함하는 하이퍼링크를 통해

  게시글 상세 Mapping에 요청 파라미터를 전달할 수 있다

- 게시글 수정 페이지에서 단일 조회의 결과를 <form>에 표시하여 수정 전 값이 표시되도록 한다

- 게시글 수정 페이지에서 수정된 값이 입력되면 ModelAttribute로 받아서 수정(UPDATE) 명령을 실행한다

- 게시글 수정이 끝나면 해당 게시글 번호(boardNo)의 게시글 상세 페이지로 redirect 시킨다

 

 

BoardDao

public interface BoardDao {

	// 추상 메소드 - 게시글 수정
	boolean update(BoardDto boardDto);
}

 

BoardDaoImpl

@Repository
public class BoardDaoImpl implements BoardDao {

	// 의존성 주입
	@Autowired
	JdbcTemplate jdbcTemplate;
	
	// 추상 메소드 오버라이딩 - 게시글 수정
	@Override
	public boolean update(BoardDto boardDto) {
		String sql = "update board set "
						+ "board_title = ?, "
						+ "board_content = ?, "
						+ "board_head = ?, "
						+ "board_updatetime = sysdate "
					+ "where board_no = ?";
		Object[] param = new Object[] {
						boardDto.getBoardTitle(), 
						boardDto.getBoardContent(), 
						boardDto.getBoardHead(), 
						boardDto.getBoardNo()
						};
		return jdbcTemplate.update(sql, param) > 0;
	}
}

 

BoardController

@Controller
@RequestMapping("/board")
public class BoardController {

	// 의존성 주입
	@Autowired
	BoardDao boardDao;

	// 4. 게시글 수정
	// 1) 게시글 수정 Mapping
	@GetMapping("/edit")
	public String edit(Model model, @RequestParam int boardNo) {
		
		// 하이퍼링크를 통해 전달받은 boardNo를 이용하여 단일 조회 실행 후 그 결과를 boardDto에 저장
		BoardDto boardDto = boardDao.selectOne(boardNo);
		
		// 단일 조회의 결과가 없다면(null) 설정한 예외 발생
		if(boardDto == null) {	
			throw new TargetNotFoundException();
		}
		
		// 단일 조회의 결과(boardDto)를 model에 첨부
		model.addAttribute("boardDto", boardDto);
		
		// 게시글 수정 페이지(edit.jsp)로 연결
		return "board/edit";
	}

	// 2) 게시글 수정 Mapping에 DTO 전달 및 DB 처리 
	@PostMapping("/edit")
	public String edit(@ModelAttribute BoardDto boardDto, RedirectAttributes attr) {
		
		// 게시글 수정 성공 여부
		boolean result = boardDao.update(boardDto);
		
		// 게시글 수정에 성공했다면 해당 게시글의 상세 Mapping으로 강제 이동(redirect)
		if(result) {
			attr.addAttribute("boardNo", boardDto.getBoardNo());
			return "redirect:detail";
		}
		
		// 게시글 수정에 실패했다면 설정한 예외 발생
		else {
			throw new TargetNotFoundException();
		}
	}
}

 

edit.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    

<jsp:include page = "/WEB-INF/views/template/header.jsp">
	<jsp:param name = "title" value = "게시글 수정"/>
</jsp:include>

<div align = "center">
<form action = "edit" method = "post">
<%-- 수정 완료 후 해당 상세 페이지로 이동하기 위해 boardNo는 수정이 불가능하도록 하며 그 값을 다시 되돌려 받는다 --%>
<input type = "hidden" name = "boardNo" value = "${boardDto.getBoardNo()}">

<table border = "1" width = "500">
	<tbody>
		<tr>
			<th>말머리</th>
			<td>
				<select name = "boardHead">
				<option value = "">선택</option>
				<%-- option의 selected 속성은 페이지 로드시 미리 선택되어지는 옵션 --%>
				<%-- 수정하기 전 boardHead가 '정보'였다면 수정 페이지에서 '정보'가 자동으로 선택되어있다 --%>
				<option <c:if test = "${boardDto.boardHead == '정보'}">selected</c:if>>정보</option>
				<option <c:if test = "${boardDto.boardHead == '유머'}">selected</c:if>>유머</option>
				
				<%-- 회원 등급이 관리자일 때만 '공지'라는 말머리를 사용할 수 있도록 한다 --%>
				<c:if test = "${mg == '관리자'}">
				<option <c:if test = "${boardDto.boardHead == '공지'}">selected</c:if>>공지</option>
				</c:if>
				</select>
			</td>
		</tr>
		<tr>
			<th>제목</th>
			<td>
				<input type = "text" name = "boardTitle" required value = "${boardDto.boardTitle}">
			</td>
		</tr>
		<tr>
			<th>내용</th>
			<td>
				<textarea name = "boardContent" rows = "10" cols = "50" required>${boardDto.boardContent}</textarea>
			</td>
		</tr>
	</tbody>
	<tfoot>
		<tr>
			<td align = "right" colspan = "2">
				<a href = "list">목록으로</a>
				<button type = "submit">수정하기</button>
			</td>
		</tr>
	</tfoot>
</table>
</form>
</div>

<jsp:include page = "/WEB-INF/views/template/footer.jsp"></jsp:include>

 

5. 게시글 삭제

- 게시글 삭제 페이지로 이동하기 위해서는 해당 게시글 번호(boardNo)가 필요하다

- Controller에서 게시글 번호(boardNo)를 요청 파라미터로 받아 단일 조회의 결과를 model에 첨부하여 View에 보낸다

- 게시글 목록에서 게시글 상세로 이동할 때 해당하는 열의 게시글 번호(boardNo)와 그 값을 포함하는 하이퍼링크를 통해

  게시글 삭제 Mapping에 요청 파라미터를 전달할 수 있다

- 게시글 삭제가 끝나면 게시글 목록 Mapping으로 redirect 시킨다

 

BoardDao

public interface BoardDao {

	// 추상 메소드 - 게시글 삭제
	boolean delete(int boardNo);
}

 

BoardDaoImpl

@Repository
public class BoardDaoImpl implements BoardDao {

	// 의존성 주입
	@Autowired
	JdbcTemplate jdbcTemplate;

	// 추상 메소드 오버라이딩 - 게시글 삭제
	@Override
	public boolean delete(int boardNo) {
		String sql = "delete board where board_no = ?";
		Object[] param = new Object[] {boardNo};
		return jdbcTemplate.update(sql, param) > 0;
	}
}

 

BoardDaoController

@Controller
@RequestMapping("/board")
public class BoardController {

	// 의존성 주입
	@Autowired
	BoardDao boardDao;

	// 5. 게시글 삭제
	// - 게시글 삭제 Mapping
	@GetMapping("/delete")
	public String delete(@RequestParam int boardNo) {
		
		// 게시글 삭제 성공 여부
		boolean result = boardDao.delete(boardNo);
		
		// 게시글 삭제에 성공했다면 해당 게시글 목록 Mapping으로 강제 이동(redirect)
		if(result) {
			return "reidrect:list";
		}
		
		// 게시글 수정에 실패했다면 설정한 예외 발생
		else {
			throw new TargetNotFoundException();
		}
	}
}