📚 공부한 책 : 코드로배우는 스프링 부트 웹프로젝트
❤️ github 주소 : https://github.com/qkralswl689/LearnFromCode/tree/main/guestbook2022
작성된 게시물의 번호를 클릭하면 상세내용을 조회할 수 있다
import com.example.guestbook2022.dto.GuestbookDTO;
public interface GuestbookService {
GuestbookDTO read(Long gno);
}
findById()를 통해 엔티티 객체를 가져왔다면 entityToDto()를 이용해 엔티티 객체를 DTO로 변환해 반환한다
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.entity.Guestbook;
import com.example.guestbook2022.repository.GuestbookRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor // 의존성 자동 주입
public class GuestbookServiceImpl implements GuestbookService{
private final GuestbookRepository repository;
@Override
public GuestbookDTO read(Long gno) {
Optional<Guestbook> result = repository.findById(gno);
// findById()를 통해 엔티티 객체를 가져왔다면 entityToDto()를 이용해 엔티티 객체를 DTO로 변환해 반환한다
return result.isPresent() ? entityToDto(result.get()) : null;
}
}
GET방식으로 gno값을 받아 Model에 GuestbookDTO 객체를 담아 전달한다, 추후 다시 목록 페이지로 돌아가는 데이터를 같이 저장하기 위해 PageRequestDTO를 파라미터로 같이 사용한다
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.dto.PageRequestDTO;
import com.example.guestbook2022.service.GuestbookService;
import lombok.RequiredArgsConstructor;
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.RequestMapping;
@Controller
@RequiredArgsConstructor // 자동주입
@RequestMapping("/guestbook")
public class GuestbookController {
private final GuestbookService service;
@GetMapping({"/read","/modify"})
// 다시 목록 페이지로 돌아가는 데이터를 같이 저장하기 위해 PageRequestDTO를 파라미터로 같이 사용한다
public void read(Long gno,@ModelAttribute("requestDTO") PageRequestDTO requestDTO,Model model){
// GET방식으로 gno값을 받아 Model에 GuestbookDTO 객체를 담아 전달한다
GuestbookDTO dto = service.read(gno);
model.addAttribute("dto",dto);
}
}
- readonly 속성을 적용해 수정이 불가하도록 한다
- dto라는 이름으로 전달된 DTO를 이용해 글의 내용 출력
- @ModelAttribute로 처리된 requestDTO로 페이지와 관련된 부분 처리
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">
<th:block th:fragment="content">
<h1 class="mt-4">GuestBook Read Page</h1>
<div class="form-group">
<label >Gno</label>
<!--readonly 속성을 적용해 수정이 불가하도록 한다-->
<!--dto라는 이름으로 전달된 DTO를 이용해 글의 내용 출력-->
<input type="text" class="form-control" name="gno" th:value="${=dto.gno}" readonly >
</div>
<div class="form-group">
<label >Title</label>>
<input type="text" class="form-control" name="title" th:value="${dto.title}" readonly >
</div>
<div class="form-group">
<label >Content</label>
<textarea class="form-control" rows="5" name="content" readonly>[[${dto.content}]]</textarea>
</div>
<div class="form-group">
<label >Writer</label>
<input type="text" class="form-control" name="writer" th:value="${dto.writer}" readonly>
</div>
<div class="form-group">
<label >RegDate</label>
<input type="text" class="form-control" name="regDate" th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
<div class="form-group">
<label >ModDate</label>
<input type="text" class="form-control" name="modDate" th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
<!--@ModelAttribute로 처리된 requestDTO로 페이지와 관련된 부분 처리-->
<a th:href="@{/guestbook/modify(gno = ${dto.gno}, page=${requestDTO.page})}">
<button type="button" class="btn btn-primary">Modify</button>
</a>
<a th:href="@{/guestbook/list(page=${requestDTO.page})}">
<button type="button" class="btn btn-info">List</button>
</a>
</th:block>
</th:block>
- 수정시 : POST 방식으로 이루어져야 한다
- 고려사항
- 수정시 수정해야하는 내용(제목,내용,글번호)이 전달되어야한다
- 수정후 목록페이지로 이동하거나 조회페이지로 이동해야한다 (기존의 페이지 번호를 유지하는것이 좋다)☆ 2-1.서비스 구현
import com.example.guestbook2022.dto.GuestbookDTO;
public interface GuestbookService {
void remove(Long gno);
void modify(GuestbookDTO dto);
}
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.entity.Guestbook;
import com.example.guestbook2022.repository.GuestbookRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor // 의존성 자동 주입
public class GuestbookServiceImpl implements GuestbookService{
private final GuestbookRepository repository;
@Override
public void remove(Long gno) {
repository.deleteById(gno);
}
@Override
public void modify(GuestbookDTO dto) {
Optional<Guestbook> result = repository.findById(dto.getGno());
if(result.isPresent()){
Guestbook entity = result.get();
entity.changeTitle(dto.getTitle());
entity.changeContent(dto.getContent());
repository.save(entity);
}
}
}
수정 : 수정해야 하는 글의 정보를 가지는 GuestbookDTO, 기존 페이지 정보 유지를 위한 PageRequestDTO 가 필요하다
삭제 : POST방식으로 gno 값을 전달하고 삭제후 다시 목록의 첫 페이지로 이동한다
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.dto.PageRequestDTO;
import com.example.guestbook2022.service.GuestbookService;
import lombok.RequiredArgsConstructor;
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.servlet.mvc.support.RedirectAttributes;
@Controller
@RequiredArgsConstructor // 자동주입
@RequestMapping("/guestbook")
public class GuestbookController {
private final GuestbookService service;
@GetMapping({"/read","/modify"})
public void read(Long gno,@ModelAttribute("requestDTO") PageRequestDTO requestDTO,Model model){
GuestbookDTO dto = service.read(gno);
model.addAttribute("dto",dto);
}
@PostMapping("/remove")
public String remove(Long gno, RedirectAttributes redirectAttributes){
// POST방식으로 gno 값을 전달하고 삭제후
service.remove(gno);
redirectAttributes.addFlashAttribute("msg",gno);
// 다시 목록의 첫 페이지로 이동한다
return "redirect:/guestbook/list";
}
@PostMapping("modify")
public String modify(GuestbookDTO dto,@ModelAttribute("requestDTO") PageRequestDTO requestDTO,RedirectAttributes redirectAttributes){
service.modify(dto);
redirectAttributes.addAttribute("page",requestDTO.getPage());
redirectAttributes.addAttribute("gno",dto.getGno());
return "redirect:/guestbook/read";
}
}
- 수정/삭제 작업을 POST 방식으로 처리하므로 form 태그로 수정하는 애용을 감싸도록 처리한다
- 수정시 기존 페이지번호를 유지하기위해 페이지 번호를 전달한다
- Remove 버튼을 클릭하면 form 태그의 action 속성과 method 속성을 조정한다
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">
<th:block th:fragment="content">
<h1 class="mt-4">GuestBook Modify Page</h1>
<!--수정/삭제 작업을 POST 방식으로 처리하므로 form 태그로 수정하는 애용을 감싸도록 처리한다-->
<form action="/guestbook/modify" method="post">
<!--페이지 번호 : 수정시 기존 페이지번호를 유지하기위해 페이지 번호를 전달한다 -->
<input type="hidden" name="page" th:value="${requestDTO.page}">
<div class="form-group">
<label >Gno</label>
<input type="text" class="form-control" name="gno" th:value="${dto.gno}" readonly >
</div>
<div class="form-group">
<label >Title</label>>
<input type="text" class="form-control" name="title" th:value="${dto.title}" >
</div>
<div class="form-group">
<label >Content</label>
<textarea class="form-control" rows="5" name="content">[[${dto.content}]]</textarea>
</div>
<div class="form-group">
<label >Writer</label>
<input type="text" class="form-control" name="writer" th:value="${dto.writer}" readonly>
</div>
<div class="form-group">
<label >RegDate</label>
<input type="text" class="form-control" th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
<div class="form-group">
<label >ModDate</label>
<input type="text" class="form-control" th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
</form>
<button type="button" class="btn btn-primary modifyBtn">Modify</button>
<button type="button" class="btn btn-info listBtn">List</button>
<button type="button" class="btn btn-danger removeBtn">Remove</button>
<script th:inline="javascript">
var actionForm = $("form"); //form 태그 객체
<!-- Remove 버튼을 클릭하면 form 태그의 action 속성과 method 속성을 조정한다-->
$(".removeBtn").click(function(){
actionForm
.attr("action", "/guestbook/remove")
.attr("method","post");
actionForm.submit();
});
$(".modifyBtn").click(function() {
if(!confirm("수정하시겠습니까?")){
return ;
}
actionForm
.attr("action", "/guestbook/modify")
.attr("method","post")
.submit();
});
$(".listBtn").click(function() {
var pageInfo = $("input[name='page']");
actionForm.empty(); //form 태그의 모든 내용을 지우고
actionForm.append(pageInfo); // 목록 페이지 이동에 필요한 내용을 다시 추가
actionForm
.attr("action", "/guestbook/list")
.attr("method","get");
actionForm.submit();
})
</script>
</th:block>
</th:block>
검색 처리는 서버 사이드처리, 화면쪽의 처리로 나눌 수 있다
- 서버 사이드 처리
- PageRequestDTO에 검색 타입과 키워드 추가
- 서비스에서 Querydsl 이용해 검색 처리- 검색 항목 정의
- 제목(t),내용(c),작성자(w)
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@Builder
@AllArgsConstructor
@Data
public class PageRequestDTO {
private int page;
private int size;
// 검색 처리를 위해 추가
private String type;
private String keyword;
public PageRequestDTO(){
this.page = 1;
this.size = 10;
}
public Pageable getPageable(Sort sort){
return PageRequest.of(page -1, size,sort);
}
}
- getList() 메소드에도 적용
- PageRequestDTO 를 파라미터로 받아 검색조건이 있는 경우 conditionBuilder 변수를 생성해 각 검색조건을 or로 연결해 처리한다
- 검색 조건이 없다면 gno > 0 으로만 생성된다
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.dto.PageRequestDTO;
import com.example.guestbook2022.dto.PageResultDTO;
import com.example.guestbook2022.entity.Guestbook;
import com.example.guestbook2022.entity.QGuestbook;
import com.example.guestbook2022.repository.GuestbookRepository;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor // 의존성 자동 주입
public class GuestbookServiceImpl implements GuestbookService{
private final GuestbookRepository repository;
// 외 코드 생략
@Override
public PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestDTO) {
Pageable pageable = requestDTO.getPageable(Sort.by("gno").descending());
BooleanBuilder booleanBuilder = getSearch(requestDTO); // 검색 조건 처리
Page<Guestbook> result = repository.findAll(booleanBuilder,pageable); // Querydsl 사용
Function<Guestbook,GuestbookDTO> fn = (entity -> entityToDto(entity));
return new PageResultDTO<>(result,fn);
}
private BooleanBuilder getSearch(PageRequestDTO requestDTO){ // Querydsl 처리
String type = requestDTO.getType();
BooleanBuilder booleanBuilder = new BooleanBuilder();
QGuestbook qGuestbook = QGuestbook.guestbook;
String keyword = requestDTO.getKeyword();
// 검색 조건이 없다면 gno > 0 으로만 생성된다
BooleanExpression expression = qGuestbook.gno.gt(0L); // gno > 0 조건만 생성
booleanBuilder.and(expression);
if(type == null || type.trim().length() == 0 ){ // 검색 조건이 없는경우
return booleanBuilder;
}
// 검색 조건 작성
// PageRequestDTO 를 파라미터로 받아 검색조건이 있는 경우 conditionBuilder 변수를 생성해 각 검색조건을 or로 연결해 처리한다
BooleanBuilder conditionBuilder = new BooleanBuilder();
if(type.contains("t")){
conditionBuilder.or(qGuestbook.title.contains(keyword));
}
if(type.contains("c")){
conditionBuilder.or(qGuestbook.content.contains(keyword));
}
if(type.contains("w")){
conditionBuilder.or(qGuestbook.writer.contains(keyword));
}
// 모든 조건 통합
booleanBuilder.and(conditionBuilder);
return booleanBuilder;
}
}
- PageRequestDTO를 이용해 검색 타입에 맞게 자동으로 선택될 수 있도록 구성
- Search 버튼을 클릭하면 무조건 1페이지를 지정하도록 한다
- Clear 버튼을 클릭하면 모든 검색 조건 없이 새로 목록 페이지를 보여준다
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">
<th:block th:fragment="content">
<h1>gestbook test
<span>
<a th:href="@{/guestbook/register}">
<button type="button" class="btn btn-outline-primary">REGISTER
</button>
</a>
</span>
</h1>
<!--추가-->
<form action="/guestbook/list" method="get" id="searchForm">
<div class="input-group">
<!--Search 버튼을 클릭하면 무조건 1페이지를 지정하도록 한다-->
<input type="hidden" name="page" value = "1">
<div class="input-group-prepend">
<select class="custom-select" name="type">
<!--PageRequestDTO를 이용해 검색 타입에 맞게 자동으로 선택될 수 있도록 구성-->
<option th:selected="${pageRequestDTO.type == null}">-------</option>
<option value="t" th:selected="${pageRequestDTO.type =='t'}" >제목</option>
<option value="c" th:selected="${pageRequestDTO.type =='c'}" >내용</option>
<option value="w" th:selected="${pageRequestDTO.type =='w'}" >작성자</option>
<option value="tc" th:selected="${pageRequestDTO.type =='tc'}" >제목 + 내용</option>
<option value="tcw" th:selected="${pageRequestDTO.type =='tcw'}" >제목 + 내용 + 작성자</option>
</select>
</div>
<input class="form-control" name="keyword" th:value="${pageRequestDTO.keyword}">
<div class="input-group-append" id="button-addon4">
<button class="btn btn-outline-secondary btn-search" type="button">Search</button>
<button class="btn btn-outline-secondary btn-clear" type="button">Clear</button>
</div>
</div>
</form>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Gno</th>
<th scope="col">Title</th>
<th scope="col">Writer</th>
<th scope="col">Regdate</th>
</tr>
</thead>
<tbody>
<!--수정 : type 과 keyword 항목 추가-->
<tr th:each="dto : ${result.dtoList}" >
<th scope="row">
<a th:href="@{/guestbook/read(gno = ${dto.gno},
page= ${result.page},
type=${pageRequestDTO.type} ,
keyword = ${pageRequestDTO.keyword})}">
[[${dto.gno}]]
</a>
</th>
<td>[[${dto.title}]]</td>
<td>[[${dto.writer}]]</td>
<td>[[${#temporals.format(dto.regDate, 'yyyy/MM/dd')}]]</td>
</tr>
</tbody>
</table>
<!--수정 : type 과 keyword 항목 추가-->
<ul class="pagination h-100 justify-content-center align-items-center">
<li class="page-item " th:if="${result.prev}">
<a class="page-link" th:href="@{/guestbook/list(page= ${result.start -1},
type=${pageRequestDTO.type} ,
keyword = ${pageRequestDTO.keyword} ) }" tabindex="-1">Previous</a>
</li>
<li th:class=" 'page-item ' + ${result.page == page?'active':''} " th:each="page: ${result.pageList}">
<a class="page-link" th:href="@{/guestbook/list(page = ${page} ,
type=${pageRequestDTO.type} ,
keyword = ${pageRequestDTO.keyword} )}">
[[${page}]]
</a>
</li>
<li class="page-item" th:if="${result.next}">
<a class="page-link" th:href="@{/guestbook/list(page= ${result.end + 1} ,
type=${pageRequestDTO.type} ,
keyword = ${pageRequestDTO.keyword} )}">Next</a>
</li>
</ul>
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
<script th:inline="javascript">
var msg = [[${msg}]];
console.log(msg);
if(msg){
$(".modal").modal();
}
<!--수정-->
var searchForm = $("#searchForm");
$('.btn-search').click(function(e){
searchForm.submit();
});
// Clear 버튼을 클릭하면 모든 검색 조건 없이 새로 목록 페이지를 보여준다
$('.btn-clear').click(function(e){
searchForm.empty().submit();
});
</script>
</th:block>
</th:block>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">
<th:block th:fragment="content">
<h1 class="mt-4">GuestBook Read Page</h1>
<div class="form-group">
<label>Gno</label>
<input type="text" class="form-control" name="gno" th:value="${dto.gno}" readonly>
</div>
<div class="form-group">
<label>Title</label>>
<input type="text" class="form-control" name="title" th:value="${dto.title}" readonly>
</div>
<div class="form-group">
<label>Content</label>
<textarea class="form-control" rows="5" name="content" readonly>[[${dto.content}]]</textarea>
</div>
<div class="form-group">
<label>Writer</label>
<input type="text" class="form-control" name="writer" th:value="${dto.writer}" readonly>
</div>
<div class="form-group">
<label>RegDate</label>
<input type="text" class="form-control" name="regDate"
th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
<div class="form-group">
<label>ModDate</label>
<input type="text" class="form-control" name="modDate"
th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
<!-- 수정 : type 과 keyword 항목 추가-->
<a th:href="@{/guestbook/modify(gno = ${dto.gno}, page=${requestDTO.page}, type=${requestDTO.type}, keyword =${requestDTO.keyword})}">
<button type="button" class="btn btn-primary">Modify</button>
</a>
<a th:href="@{/guestbook/list(page=${requestDTO.page} , type=${requestDTO.type}, keyword =${requestDTO.keyword})}">
<button type="button" class="btn btn-info">List</button>
</a>
</th:block>
</th:block>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">
<th:block th:fragment="content">
<h1 class="mt-4">GuestBook Modify Page</h1>
<form action="/guestbook/modify" method="post">
<!--페이지 번호 -->
<input type="hidden" name="page" th:value="${requestDTO.page}">
<!--수정 : type 과 keyword 항목 추가-->
<input type="hidden" name="type" th:value="${requestDTO.type}" >
<input type="hidden" name="keyword" th:value="${requestDTO.keyword}" >
<div class="form-group">
<label >Gno</label>
<input type="text" class="form-control" name="gno" th:value="${dto.gno}" readonly >
</div>
<div class="form-group">
<label >Title</label>
<input type="text" class="form-control" name="title" th:value="${dto.title}" >
</div>
<div class="form-group">
<label >Content</label>
<textarea class="form-control" rows="5" name="content">[[${dto.content}]]</textarea>
</div>
<div class="form-group">
<label >Writer</label>
<input type="text" class="form-control" name="writer" th:value="${dto.writer}" readonly>
</div>
<div class="form-group">
<label >RegDate</label>
<input type="text" class="form-control" th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
<div class="form-group">
<label >ModDate</label>
<input type="text" class="form-control" th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
</div>
</form>
<button type="button" class="btn btn-primary modifyBtn">Modify</button>
<button type="button" class="btn btn-info listBtn">List</button>
<button type="button" class="btn btn-danger removeBtn">Remove</button>
<script th:inline="javascript">
var actionForm = $("form"); //form 태그 객체
$(".removeBtn").click(function(){
actionForm
.attr("action", "/guestbook/remove")
.attr("method","post");
actionForm.submit();
});
$(".modifyBtn").click(function() {
if(!confirm("수정하시겠습니까?")){
return ;
}
actionForm
.attr("action", "/guestbook/modify")
.attr("method","post")
.submit();
});
$(".listBtn").click(function() {
//var pageInfo = $("input[name='page']");
var page = $("input[name='page']");
var type = $("input[name='type']");
var keyword = $("input[name='keyword']");
actionForm.empty(); //form 태그의 모든 내용을 지우고
<!--수정 : type 과 keyword 항목 추가-->
actionForm.append(page);
actionForm.append(type);
actionForm.append(keyword);
actionForm
.attr("action", "/guestbook/list")
.attr("method","get");
actionForm.submit();
})
</script>
</th:block>
</th:block>
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.dto.PageRequestDTO;
import com.example.guestbook2022.service.GuestbookService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.servlet.mvc.support.RedirectAttributes;
@Controller
@RequiredArgsConstructor // 자동주입
@RequestMapping("/guestbook")
public class GuestbookController {
private final GuestbookService service;
@PostMapping("modify")
public String modify(GuestbookDTO dto,@ModelAttribute("requestDTO") PageRequestDTO requestDTO,RedirectAttributes redirectAttributes){
service.modify(dto);
redirectAttributes.addAttribute("page",requestDTO.getPage());
redirectAttributes.addAttribute("gno",dto.getGno());
// 검색조건 추가
redirectAttributes.addAttribute("type",requestDTO.getType());
redirectAttributes.addAttribute("keyword",requestDTO.getKeyword());
return "redirect:/guestbook/read";
}
}