본문 바로가기
Spring

[Spring] MVC (7) 실습 - 공지사항 수정하기

by moca7 2024. 10. 17.

 

 

<!-- 
            * 실습 (공지사항 수정하기)
            1) 공지사항 상세페이지에서 수정페이지로 이동 링크 만들기 (/notice/modifyForm.do?no=현재글번호  url 요청되도록)
            2) 해당 url 요청시 실행되는 Controller 작업 
               ㄴ 요청시 전달되는 파라미터(수정할글번호) 뽑기
               ㄴ 해당 번호로 공지사항 조회 서비스 요청
               ㄴ 조회된 공지사항 객체를 수정페이지에서 사용할수 있도록 담기
               ㄴ 수정페이지로 포워딩
            3) 해당 url 요청시 포워딩되는 수정페이지(/WEB-INF/views/notice/modifyForm.jsp) 제작하기 
                ㄴ 수정할수 있는 폼요소로 만들기 (제목 텍스트상자, 내용 텍스트상자 존재)
                   이때 조회된 기존의 공지사항 정보 노출시키기 
                ㄴ 수정 요청을 보낼 수 있는 submit 버튼 두기
                ㄴ 수정 요청시 /notice/update.do  url로 요청되도록
            4) 해당 url 요청시 실행되는 Controller 작업
                ㄴ 요청시 전달되는 파라미터들(번호(숨김상태로), 제목, 내용)을 NoticeDto 객체의 필드에 바로 매핑될 수 있도록 하기
                ㄴ 해당 NoticeDto 객체를 전달하면서 수정요청할 수 있는 service측 메소드 만들기 
                ㄴ dao측도 만들기 (이때 그냥 1로 반환되도록 하기 or 넘겨받은 글번호로 찾아서 반환)
                ㄴ 성공했을 경우 상세페이지로 다시 이동될수있도록 하기                
         -->

 

 

 

 

ㅁ 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" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>공지사항 상세 페이지</h3>

    <c:choose>
        <c:when test="${ empty notice }">
            조회된 공지사항이 없습니다.
        </c:when>
       
        <c:otherwise>
            번호 : ${ notice.no } <br>
            제목 : ${ notice.title } <br>
            내용 : ${ notice.content } <br><br>
           
            <a href="${contextPath}/notice/modifyForm.do?no=${notice.no}">수정페이지로 이동</a>
        </c:otherwise>
    </c:choose>
   

</body>
</html>
 

 

 

 

 

 

ㅁ NoticeController

 

 
package com.br.mvc.controller;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.br.mvc.dto.NoticeDto;
import com.br.mvc.service.NoticeService;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor // lombok
//@AllArgsConstructor lombok
@RequestMapping("/notice")
@Controller
public class NoticeController {

   
    // 직접 생성하면 결합도가 높아지는 문제가 있다. 매번 생성해서 메모리도 많이 사용한다.
    // private NoticeService noticeService = new NoticeServiceImpl();
    // @Autowired로 스프링에게서 주입받아 사용한다.
    // 그러려면 NoticeServiceImpl을 빈으로 등록해야 한다.
    // xml 파일에 가서 빈 등록을 해도 되지만 NoticeServiceImpl 클래스 위에 @Service 어노테이션을 작성하면 빈 스캐닝에 의해 빈 등록이 된다.
   

/*
    (1) 필드 주입 방식
    @Autowired
    private NoticeService noticeService;
*/
   
   

/*
    (2) 메소드 주입 방식
    private NoticeService noticeService;
   
    @Autowired
    public void setNoticeService(NoticeService noticeService) { // 자동실행
        this.noticeService = noticeService;
    }
*/
   
   
    //(3) 생성자 주입 방식 (생성자는 자동으로 실행되기 때문에 @Autowired 생략 가능)
    private int no;
    private final NoticeService noticeService;
   
   
/*
    public NoticeController(NoticeService noticeService) {   <- 롬복
        this.noticeService = noticeService;
    }
*/
   
   
   
    // =========== 포워딩할 응답페이지에 필요한 데이터를 담는 방법 ===========
   
    // (1) Model 객체 이용하기
    @GetMapping("/list.do")
    public String noticeList(Model model) {
        List<NoticeDto> list = noticeService.selectNoticeList(); // 응답페이지에 필요한 데이터
       
        model.addAttribute("list", list);
       
        // /WEB-INF/views/notice/list.jsp로 포워딩
        return "notice/list";
       
    }
   
   
    // (2) ModelAndView 객체 이용하기
    @GetMapping("/detail.do")
    public ModelAndView noticeDetail(int no, ModelAndView mv) { // key값과 매개변수명이 같으면 @RequestParam 생략가능
       
        // NoticeDto n = noticeService.selectNoticeByNo(no); 응답 페이지에 필요한 데이터
        // return "notice/detial"; 응답뷰
       
        mv.addObject("notice", noticeService.selectNoticeByNo(no)); // 응답 페이지에 필요한 데이터
        mv.setViewName("notice/detail"); // 응답뷰
       
        // mv.addObject("notice", noticeService.selectNoticeByNo(no)).setViewName("notice/detail");
        // addObject 메소드가 해당 ModelAndView 객체를 반환하기 때문에 한줄로 메소드 체이닝 가능하다.
       
        return mv;
       
    }
   
   
   
    @RequestMapping("/modifyForm.do")
    public String noticeModifyForm(int no, Model model) {
       
        NoticeDto notice = noticeService.selectNoticeByNo(no);
        model.addAttribute("notice", notice);
        return "notice/modifyForm";
       
    }
   
}
 

 

 

- noticeModifyForm 메소드를 만든다.

- 매개변수에 두면 스프링이 알아서 주입해준다.

 

 

 

 

 

 

ㅁ notice 폴더에 modifyForm.jsp를 만든다.

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h2>공지사항 수정 페이지</h2>

    <form action="${contextPath}/notice/update.do" method="post">
        제목 : <input type="text" value="${notice.title}"> <br>
        내용 : <textarea>${ notice.content }</textarea>
       
        <input type="submit" value="수정">
    </form>

</body>
</html>
 

 

 

 

 

 

 

 

 

 

 

ㅁ ModifyForm.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h2>공지사항 수정 페이지</h2>

    <form action="${contextPath}/notice/update.do" method="post">
        <input type="hidden" name="no" value="${notice.no}">
        제목 : <input type="text" name="title" value="${notice.title}"> <br>
        내용 : <textarea name="content">${ notice.content }</textarea>
       
        <input type="submit" value="수정">
    </form>

</body>
</html>
 

 

 

- input 요소 중 제목에 name="title", 내용에 name="content"를 준다.

- 특정 게시글만 업데이트되도록 hidden타입 input으로 글번호를 넘긴다.

 

 

 

 

 

ㅁ NoticeController 

 

 
package com.br.mvc.controller;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.br.mvc.dto.NoticeDto;
import com.br.mvc.service.NoticeService;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor // lombok
//@AllArgsConstructor lombok
@RequestMapping("/notice")
@Controller
public class NoticeController {

   
    // 직접 생성하면 결합도가 높아지는 문제가 있다. 매번 생성해서 메모리도 많이 사용한다.
    // private NoticeService noticeService = new NoticeServiceImpl();
    // @Autowired로 스프링에게서 주입받아 사용한다.
    // 그러려면 NoticeServiceImpl을 빈으로 등록해야 한다.
    // xml 파일에 가서 빈 등록을 해도 되지만 NoticeServiceImpl 클래스 위에 @Service 어노테이션을 작성하면 빈 스캐닝에 의해 빈 등록이 된다.
   

/*
    (1) 필드 주입 방식
    @Autowired
    private NoticeService noticeService;
*/
   
   

/*
    (2) 메소드 주입 방식
    private NoticeService noticeService;
   
    @Autowired
    public void setNoticeService(NoticeService noticeService) { // 자동실행
        this.noticeService = noticeService;
    }
*/
   
   
    //(3) 생성자 주입 방식 (생성자는 자동으로 실행되기 때문에 @Autowired 생략 가능)
    private int no;
    private final NoticeService noticeService;
   
   
/*
    public NoticeController(NoticeService noticeService) {   <- 롬복
        this.noticeService = noticeService;
    }
*/
   
   
   
    // =========== 포워딩할 응답페이지에 필요한 데이터를 담는 방법 ===========
   
    // (1) Model 객체 이용하기
    @GetMapping("/list.do")
    public String noticeList(Model model) {
        List<NoticeDto> list = noticeService.selectNoticeList(); // 응답페이지에 필요한 데이터
       
        model.addAttribute("list", list);
       
        // /WEB-INF/views/notice/list.jsp로 포워딩
        return "notice/list";
       
    }
   
   
    // (2) ModelAndView 객체 이용하기
    @GetMapping("/detail.do")
    public ModelAndView noticeDetail(int no, ModelAndView mv) { // key값과 매개변수명이 같으면 @RequestParam 생략가능
       
        // NoticeDto n = noticeService.selectNoticeByNo(no); 응답 페이지에 필요한 데이터
        // return "notice/detial"; 응답뷰
       
        mv.addObject("notice", noticeService.selectNoticeByNo(no)); // 응답 페이지에 필요한 데이터
        mv.setViewName("notice/detail"); // 응답뷰
       
        // mv.addObject("notice", noticeService.selectNoticeByNo(no)).setViewName("notice/detail");
        // addObject 메소드가 해당 ModelAndView 객체를 반환하기 때문에 한줄로 메소드 체이닝 가능하다.
       
        return mv;
       
    }
   
   
   
    @RequestMapping("/modifyForm.do")
    public String noticeModifyForm(int no, Model model) {
       
        NoticeDto notice = noticeService.selectNoticeByNo(no);
        model.addAttribute("notice", notice);
        return "notice/modifyForm";
       
    }
   
   
    @PostMapping("/update.do")
    public String noticeUpdate(NoticeDto n) { // 1) 요청시 전달값 처리 - 커맨드 객체
       
        // 2) 요청처리를 위한 서비스 호출
        int result = noticeService.updateNotice(n);
       
        // 3) 응답
        if(result > 0) {
           
            // return "notice/detail"; 이건 포워딩이다.
 
            return "redirect:/notice/detail.do?no=" + n.getNo();
            // 스프링에서 "redirect:" 뒤에 context path 안줘도 됨.
            // 여기에도 글번호 넘겨야 함.
 
 
        }else {
 
            // return "main"; 이렇게 하면 메인페이지가 뜨긴 하는데 url이 메인 페이지가 아니라 /update.do다.
 
            // MvcController 가보면 이미 메인페이지로 이동하는 메소드가 정의되어 있음.
            return "redirect:/";
 
        }
       
    }
   
   
}
 

 

- noticeUpdate() 메소드를 만든다.

- 매개변수로 NoticeDto n을 둬서 커맨드 객체 방식을 사용한다.

ModifyForm.jsp <- input 태그의 name 속성값(key값)과 NoticeDto의 필드명과 일치시킨다. 그러면 자동으로 담긴다.

 

 

- 1) 요청시 전달값 처리

- 2) 요청 처리를 위한 서비스 호출

- 3) 응답

 

 

 

 

 

 

- detail.jsp를 보면 notice 객체에서 값을 뽑는데 notice객체를 우리가 넘기지 않았다.

포워딩을 하고 싶다면 model 객체를 만들어서 notice라는 key값으로 notice 객체가 담겨있어야 한다.

- NoticeController의 noticeDetail 메소드를 보면 no만 넘기면 그 게시글을 조회해서 notice로 담아서 넘기는 메소드가 이미 정의되어 있다.

 

 

 

 

 

 

 

 

- response.sendRedirect(request.getContextPath() + "/notice/detail.do");

스프링을 사용하지 않을 때(Servlet이나 JSP를 사용하여 직접 리다이렉트를 구현할 경우)에는 context path를 명시해야 했다.

 

 

 

 

 

 

 

 

 

ㅁ NoticeService

 
package com.br.mvc.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.br.mvc.dto.NoticeDto;

public interface NoticeService {

   
    // 목록조회
    List<NoticeDto> selectNoticeList();
   
    // 상세조회
    NoticeDto selectNoticeByNo(int noticeNo);
   
    // 수정
    int updateNotice(NoticeDto n);
   
}
 

 

- updateNotice 추상메소드를 만든다.

 

 

 

 

ㅁ NoticeServiceImpl

 

 
package com.br.mvc.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.br.mvc.dao.NoticeDao;
import com.br.mvc.dto.NoticeDto;

import lombok.RequiredArgsConstructor;

@Service // Service 역할의 클래스에 부여하는 Component 어노테이션 (빈 스캐닝에 의해 자동으로 빈 등록됨)
@RequiredArgsConstructor
public class NoticeServiceImpl implements NoticeService {

   
    private final NoticeDao noticeDao;
   
   
    @Override
    public List<NoticeDto> selectNoticeList() {
        return noticeDao.selectNoticeList();
    }

    @Override
    public NoticeDto selectNoticeByNo(int noticeNo) {
        return noticeDao.selectNoticeByNo(noticeNo);
    }

    @Override
    public int updateNotice(NoticeDto n) {
        return noticeDao.updateNotice(n);
    }

}
 

 

- updateNotice 추상메소드를 구현한다.

 

 

 

 

 

ㅁ NoticeDao

 

 
package com.br.mvc.dao;

import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Repository;

import com.br.mvc.dto.NoticeDto;

@Repository  //  dao 역할의 클래스에 부여하는 Componenet 어노테이션(빈 스캐닝에 의해 빈 등록됨)
public class NoticeDao {

   
    // db에서 조회되었다고 가정하고 샘플데이터 만들기
    // db에 담겨있는 공지사항 데이터 재사용
    // list를 생성하고 add로 담지 않고, 한줄로 끝냄.
    private List<NoticeDto> dbList = Arrays.asList(
                                        new NoticeDto(1, "제목1", "내용1")
                                      , new NoticeDto(2, "제목2", "내용2")  
                                      , new NoticeDto(3, "제목3", "내용3")  );

   
    public List<NoticeDto> selectNoticeList(){
        return dbList;
    }
   
   
    public NoticeDto selectNoticeByNo(/*SqlSession sqlSession,*/int noticeNo) {
       
        // return 쿼리결과;
       
       
        // 그냥 3개의 NoticeDto 객체 중에서 하나를 찝어도 되고,
        for(NoticeDto n : dbList) {
            if(n.getNo() == noticeNo) {
                return n;
            }
        }
       
       
        return null; // db 연동해도 조회결과 없으면 null 반환이다. for문이 끝나기 전까지 조회결과를 못찾으면 null 반환.
       
    }
   
   
   
    public int updateNotice(NoticeDto n) {
       
        // 원래라면 쿼리 실행. 그러나 지금 db 연동이 되어있지 않으므로 가상의 dbList를 사용한다.
       
        for(NoticeDto notice : dbList) {
            if(notice.getNo() == n.getNo()) {
                notice.setTitle(n.getTitle());
                notice.setContent(n.getContent());
                return 1; // 업데이트 성공
            }
        }
       
        return 0;
    }
   
   
}
 

 

- updateNotice 메소드를 만든다.

 

 

 

 

 

===========================================================================================

 

 

ㅁ 수정 성공시 alert 띄우기

 

 
package com.br.mvc.controller;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.br.mvc.dto.NoticeDto;
import com.br.mvc.service.NoticeService;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor // lombok
//@AllArgsConstructor lombok
@RequestMapping("/notice")
@Controller
public class NoticeController {

   
    // 직접 생성하면 결합도가 높아지는 문제가 있다. 매번 생성해서 메모리도 많이 사용한다.
    // private NoticeService noticeService = new NoticeServiceImpl();
    // @Autowired로 스프링에게서 주입받아 사용한다.
    // 그러려면 NoticeServiceImpl을 빈으로 등록해야 한다.
    // xml 파일에 가서 빈 등록을 해도 되지만 NoticeServiceImpl 클래스 위에 @Service 어노테이션을 작성하면 빈 스캐닝에 의해 빈 등록이 된다.
   

/*
    (1) 필드 주입 방식
    @Autowired
    private NoticeService noticeService;
*/
   
   

/*
    (2) 메소드 주입 방식
    private NoticeService noticeService;
   
    @Autowired
    public void setNoticeService(NoticeService noticeService) { // 자동실행
        this.noticeService = noticeService;
    }
*/
   
   
    //(3) 생성자 주입 방식 (생성자는 자동으로 실행되기 때문에 @Autowired 생략 가능)
    private int no;
    private final NoticeService noticeService;
   
   
/*
    public NoticeController(NoticeService noticeService) {   <- 롬복
        this.noticeService = noticeService;
    }
*/
   
   
   
    // =========== 포워딩할 응답페이지에 필요한 데이터를 담는 방법 ===========
   
    // (1) Model 객체 이용하기
    @GetMapping("/list.do")
    public String noticeList(Model model) {
        List<NoticeDto> list = noticeService.selectNoticeList(); // 응답페이지에 필요한 데이터
       
        model.addAttribute("list", list);
       
        // /WEB-INF/views/notice/list.jsp로 포워딩
        return "notice/list";
       
    }
   
   
    // (2) ModelAndView 객체 이용하기
    @GetMapping("/detail.do")
    public ModelAndView noticeDetail(int no, ModelAndView mv) { // key값과 매개변수명이 같으면 @RequestParam 생략가능
       
        // NoticeDto n = noticeService.selectNoticeByNo(no); 응답 페이지에 필요한 데이터
        // return "notice/detial"; 응답뷰
       
        mv.addObject("notice", noticeService.selectNoticeByNo(no)); // 응답 페이지에 필요한 데이터
        mv.setViewName("notice/detail"); // 응답뷰
       
        // mv.addObject("notice", noticeService.selectNoticeByNo(no)).setViewName("notice/detail");
        // addObject 메소드가 해당 ModelAndView 객체를 반환하기 때문에 한줄로 메소드 체이닝 가능하다.
       
        return mv;
       
    }
   
   
   
    @RequestMapping("/modifyForm.do")
    public String noticeModifyForm(int no, Model model) {
       
        NoticeDto notice = noticeService.selectNoticeByNo(no);
        model.addAttribute("notice", notice);
        return "notice/modifyForm";
       
    }
   
   
    @PostMapping("/update.do")
    public String noticeUpdate(NoticeDto n, Model model) { // 1) 요청시 전달값 처리 - 커맨드 객체
       
        // 2) 요청처리를 위한 서비스 호출
        int result = noticeService.updateNotice(n);
       
        // 3) 응답
        if(result > 0) {
           
            // return "notice/detail"; 이건 포워딩이다.

            model.addAttribute("alertMsg", "성공적으로 수정되었습니다.");
           
            return "redirect:/notice/detail.do?no=" + n.getNo(); // context path 안줘도 됨. 여기에도 글번호 넘겨야 함.
        }else {
            // return "main"; 이렇게 하면 메인페이지가 뜨긴 하는데 url이 메인 페이지가 아니라 /update.do다.
            // MvcController 가보면 이미 메인페이지로 이동하는 메소드가 정의되어 있음.
            return "redirect:/";
        }
       
    }
   
   
}
 

 

 

- noticeUpdate 메소드에 수정 성공시 alert를 띄우기 위해서 model.addAttribute("alertMsg", "성공적으로 수정되었습니다.");를 추가했다.

 

 

 

 

 

ㅁ 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" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

 
    <c:if test="${ not empty alertMsg }">
        <script>
            alert("${alertMsg}");
        </script>
    </c:if>


    <h3>공지사항 상세 페이지</h3>

    <c:choose>
        <c:when test="${ empty notice }">
            조회된 공지사항이 없습니다.
        </c:when>
       
        <c:otherwise>
            번호 : ${ notice.no } <br>
            제목 : ${ notice.title } <br>
            내용 : ${ notice.content } <br><br>
           
            <a href="${contextPath}/notice/modifyForm.do?no=${notice.no}">수정페이지로 이동</a>
        </c:otherwise>
    </c:choose>
   

</body>
</html>
 

 

 

- detail.jsp에 오는 경우가 2가지다. 그냥 오는경우, 수정 후 오는 경우. 수정 후 오는 경우에만 alertMsg가 있다.

 

 

 

 

 

ㅁ 그런데 alert가 뜨지 않는다.

- 처음 상세페이지가 뜰 때는 alert가 안 뜨는 것이 맞다.

- 그런데 수정하고 상세페이지로 redirect할 때도 alert가 안 뜬다.

 

 

- 그리고 url에 파라미터로 되어있다.

- 포워딩이 아니라 redirect인데 model 객체에 담으면 파라미터로 된다.

 

- 포워딩 방식이었으면 됐을 건데 포워딩은 적절치 않아서 redirct를 했다.

noticeDetail 메소드를 호출하기 때문에 거기서 포워딩 할 때는 model에 notice 객체 밖에 담겨있지 않다.

- 전에는 redirect할 때 session에 담았었다.

session은 일회용이 아니라 꼐속 담겨있기 때문에 우리가 지우는 것까지 했었다.

 

 

 

 

 

ㅁ redirect시 포워딩 되는 페이지에 필요한 데이터를 담는 방법

- model 객체는 따지고보면 request scope다. 그래서 바로 포워딩되는 jsp에서만 사용 가능하다.

- redirect로 다른 controller가 실행되는 순간 현재 만들어진 Model 객체는 소멸된다.

 

- 특정 url로 요청해서 controller가 실행되고, 거기서 바로 포워딩(주로 조회페이지로 이동)하는 경우에는 controller에서 생성하는 Model 객체에 데이터를 담으면 포워딩된 jsp까지 Model 객체가 유지된다.

 

- 그런데 지금 케이스는 수정 url을 요청해서 controller(/update.do)가 실행되고, 여기서 성공하면 redirect로 다른 controller(/detail.do)를 재요청했다. 두번째 컨트롤러에서 포워딩을해서 /notice/detail.jsp가 보여진다.

- 처음 컨트롤러에서 만든 Model 객체는 2번째 컨트롤러로 리다이렉트할 때 사라지고, 두번째 컨트롤러에서 만든 모델 객체만 포워딩된 jsp에도 유지된다.

 

 

 

- RedirectAttributes 객체를 생성해서 데이터를 담으면 건너 건너 포워딩된 페이지(jsp)까지 RedirectAttributes 객체가 유지된다.

- 즉 현재 controller에서 url 재요청을 통해 다른 controller에 의해 포워딩되는 jsp에서까지 데이터를 쭉 사용하고 싶다면 Model 객체가 아니라 RedirectAttributes 객체를 사용하면 된다.

이때 addAttribute 메소드도 있는데 그걸로 담으면 Model에 담은 것과 동일하다. 

addFlashAttribute 메소드로 담으면 된다.

 

- RedirectAttributes는 Model의 역할도 하고 멀티다.

- 스프링에서 제공하는 객체다.

 

 

 

 

 

 

ㅁ NoticeController

 

 
package com.br.mvc.controller;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.br.mvc.dto.NoticeDto;
import com.br.mvc.service.NoticeService;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor // lombok
//@AllArgsConstructor lombok
@RequestMapping("/notice")
@Controller
public class NoticeController {

   
    // 직접 생성하면 결합도가 높아지는 문제가 있다. 매번 생성해서 메모리도 많이 사용한다.
    // private NoticeService noticeService = new NoticeServiceImpl();
    // @Autowired로 스프링에게서 주입받아 사용한다.
    // 그러려면 NoticeServiceImpl을 빈으로 등록해야 한다.
    // xml 파일에 가서 빈 등록을 해도 되지만 NoticeServiceImpl 클래스 위에 @Service 어노테이션을 작성하면 빈 스캐닝에 의해 빈 등록이 된다.
   

/*
    (1) 필드 주입 방식
    @Autowired
    private NoticeService noticeService;
*/
   
   

/*
    (2) 메소드 주입 방식
    private NoticeService noticeService;
   
    @Autowired
    public void setNoticeService(NoticeService noticeService) { // 자동실행
        this.noticeService = noticeService;
    }
*/
   
   
    //(3) 생성자 주입 방식 (생성자는 자동으로 실행되기 때문에 @Autowired 생략 가능)
    private int no;
    private final NoticeService noticeService;
   
   
/*
    public NoticeController(NoticeService noticeService) {   <- 롬복
        this.noticeService = noticeService;
    }
*/
   
   
   
    // =========== 포워딩할 응답페이지에 필요한 데이터를 담는 방법 ===========
   
    // (1) Model 객체 이용하기
    @GetMapping("/list.do")
    public String noticeList(Model model) {
        List<NoticeDto> list = noticeService.selectNoticeList(); // 응답페이지에 필요한 데이터
       
        model.addAttribute("list", list);
       
        // /WEB-INF/views/notice/list.jsp로 포워딩
        return "notice/list";
       
    }
   
   
    // (2) ModelAndView 객체 이용하기
    @GetMapping("/detail.do")
    public ModelAndView noticeDetail(int no, ModelAndView mv) { // key값과 매개변수명이 같으면 @RequestParam 생략가능
       
        // NoticeDto n = noticeService.selectNoticeByNo(no); 응답 페이지에 필요한 데이터
        // return "notice/detial"; 응답뷰
       
        mv.addObject("notice", noticeService.selectNoticeByNo(no)); // 응답 페이지에 필요한 데이터
        mv.setViewName("notice/detail"); // 응답뷰
       
        // mv.addObject("notice", noticeService.selectNoticeByNo(no)).setViewName("notice/detail");
        // addObject 메소드가 해당 ModelAndView 객체를 반환하기 때문에 한줄로 메소드 체이닝 가능하다.
       
        return mv;
       
    }
   
   
   
    @RequestMapping("/modifyForm.do")
    public String noticeModifyForm(int no, Model model) {
       
        NoticeDto notice = noticeService.selectNoticeByNo(no);
        model.addAttribute("notice", notice);
        return "notice/modifyForm";
       
    }
   
   
    @PostMapping("/update.do")
    public String noticeUpdate(NoticeDto n, /*Model model*/ RedirectAttributes ra) { // 1) 요청시 전달값 처리 - 커맨드 객체
       
        // 2) 요청처리를 위한 서비스 호출
        int result = noticeService.updateNotice(n);
       
        // 3) 응답
        if(result > 0) {
           
            // return "notice/detail"; 이건 포워딩이다.

            //model.addAttribute("alertMsg", "성공적으로 수정되었습니다.");
            ra.addFlashAttribute("alertMsg", "성공적으로 수정되었습니다.");
           
            return "redirect:/notice/detail.do?no=" + n.getNo(); // context path 안줘도 됨. 여기에도 글번호 넘겨야 함.
        }else {
            // return "main"; 이렇게 하면 메인페이지가 뜨긴 하는데 url이 메인 페이지가 아니라 /update.do다.
            // MvcController 가보면 이미 메인페이지로 이동하는 메소드가 정의되어 있음.
            return "redirect:/";
        }
       
    }
   
   
 
}
 

 

 

- 우리가 직접 생성할 필요 없이 매개변수에 써두기만 하면 된다.

- session에 담아도 되는데 session은 계속 유지가 돼서 내가 비워줘야 했다.

이건 한번 하고 끝남.

 

- detail.jsp는 수정하지 않았다.

- 이렇게 작성하면 업데이트 성공 후 alert 메세지가 뜬다.