본문 바로가기
Spring

[Spring] MVC (6) 응답페이지 이동시 응답데이터 담기

by moca7 2024. 10. 17.

 

 

 

(1) Model 객체 이용하기

- requestScope 영역에 데이터를 맵형식(key-value)로 담을 수 있는 객체

- 단 setAttribute가 아닌 addAttribute 메소드를 사용한다.

- 포워딩 처리가 되면 알아서 ~

 

 

(2) ModelAndView 객체 이용하기

- Model과 View가 합쳐져 있는 객체.

- Model은 데이터를 담는 객체, View는 응답 뷰에 대한 정보를 담는 객체

 

- ModelAndView 객체에 데이터와 응답 뷰에 대한 정보를 담고 해당 객체를 반환한다

 

 

 

 

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

 

 

 

(1) Model 객체 이용하기

 

 

 

 

ㅁ main.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>

<script src="${ contextPath }/resources/js/sample.js"></script>

<script src="${ contextPath }/assets/js/jquery-3.7.1.min.js"></script>

</head>
<body>

    <!-- / 또는 /main.do라는 url mapping으로 요청시 해당 /WEB-INF/views/main.jsp가 보여지도록 한다. -->
    <h1>메인페이지입니다</h1>

    <h3>1. 정적 자원 확인</h3>
    <img src="${ contextPath }/resources/images/1.jpeg" width="100" onclick="test();">
    <img src="${ contextPath }/assets/images/2.jpg" width="100" id="img">
   
    <script>
        $(function(){
            $('#img').on("click", () => {
                alert("어서오세요");
            })
        })
    </script>
   
    <hr>


    <h3>2. 응답페이지 보여지게 하는 연습 (포워딩, redirect)</h3>

    <!-- <a href="${contextPath}/list.bk">도서목록페이지로 이동</a> -->
    <a href="${contextPath}/book/list.do">도서목록페이지로 이동</a> <br>
    <a href="${contextPath}/book/enrollForm.do">도서등록페이지로 이동</a>



    <hr>


    <h3>3. 요청시 전달되는 파라미터 처리하는 연습 (request의 parameter)</h3>

    <!-- <a href="${contextPath}/detail.mem?no=1">회원상세조회</a> -->
    <a href="${contextPath}/member/detail.do?no=1">회원상세조회</a> <br><br><br>

    <!-- (1) HttpServletRequest 방법 -->
    <form action="${ contextPath }/member/enroll1.do" method="post">
        이름 : <input type="text" name="name"> <br>
        나이 : <input type="text" name="age"> <br>
        주소 : <input type="text" name="address"> <br>
        <button>등록</button>
    </form>

    <br><br>


    <!-- (2) @RequestParam 방법 -->
    <form action="${ contextPath }/member/enroll2.do" method="post">
        이름 : <input type="text" name="name"> <br>
        나이 : <input type="text" name="age"> <br>
        주소 : <input type="text" name="address"> <br>
        <button>등록</button>
    </form>

    <!-- <a href="${contextPath}/detail2.mem?no=1">회원상세조회</a> -->
    <a href="${contextPath}/member/detail2.do?no=1">회원상세조회</a> <br><br><br>


    <br><br>


    <!-- (3) 커맨드 객체 방법 -->
    <form action="${ contextPath }/member/enroll3.do" method="post">
        이름 : <input type="text" name="name"> <br>
        나이 : <input type="text" name="age"> <br>
        주소 : <input type="text" name="addr"> <br>
        <button>등록</button>
    </form>
   
   
    <br><br>
   
    <h3>4. 응답페이지로 포워딩시 필요한 데이터를 담는 방법</h3>
    <a href="${ contextPath }/notice/list.do">공지사항 목록페이지로 이동</a>




</body>
</html>
 

 

- "4. 응답페이지로 포워딩시 필요한 데이터를 담는 방법" 부분을 추가한다.

 

 

 

 

 

ㅁ com.br.mvc.dto 패키지에 NoticeDto 클래스를 만든다.

 

 
package com.br.mvc.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class NoticeDto {

    private int no;
    private String title;
    private String content;
   
}

 

- 사실 이제 매개변수 생성자로 생성할 일이 없다. 그래도 혹시 모르니 매개변수 생성자 어노테이션도 적어놓는다.

- lobok 라이브러리를 사용해서 생성자, getter, setter, toString 메소드를 만든다.

 

 

 

 

 

ㅁ com.br.mvc.service 패키지에 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);
   
   
}

 

- public abstract를 안써도 붙는다.

 

 

 

 

ㅁ com.br.mvc.service 패키지에 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);
    }

}

 


- NoticeServiceImpl도 NoticeDao 객체가 필요하다. 직접 new로 생성하진 않는다.

선언만 하고 주입받아서 사용한다.

그러기 위해서는 NoticeDao를 빈으로 등록해야 한다.

- dao 역할을 하는 클래스에 부여하는 component 어노테이션은 @Repository다.

 

- @Service

- @RequiredArgsConstructor

 

 

 

 

ㅁ com.br.mvc.dao 패키지에 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 반환.
       
    }
   
   
}
 

 

- 회사마다 어떤 회사는 service 단계가 없을 수도 있고, dao도 인터페이스를 두는 회사도 있다.

- 일단은 SqlSession 객체는 매개변수에 두지 않았다.

 

- @Repository 추가.

 

 

 

 

ㅁ NoticeController 

 

 
package com.br.mvc.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.br.mvc.service.NoticeService;

import lombok.AllArgsConstructor;

@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 noticeSerivce;
   
   
/*
    public NoticeController(NoticeService noticeService) {   <- 롬복
        this.noticeService = noticeService;
    }
*/
   
   
   
    // =========== 포워딩할 응답페이지에 필요한 데이터를 담는 방법 ===========
   
    @GetMapping("/list.do")
    public String noticeList() {
        List<NoticeDto> list = noticeService.selectNoticeList();
    }
   
   
   
   
}
 

 

- 서비스 객체의 메소드를 쓰고 싶다면 서비스 객체가 있어야 한다.

- service 객체를 전역 필드로 두고 쓴다.

 

- @AllArgsConstructor를 줬다.

 

 

- (1) 필드 주입 방식은 등록된 빈 중 타입으로 찾는다. 10개의 필드가 있다면 10개의 @Autowired 어노테이션을 작성해야 한다.

- (2) 메소드 주입 방식은 ~ 메소드가 자동으로 실행된다.

 

 

 

※ (3) 생성자 주입 방식을 자주 쓰는 이유.

- @Autowired 어노테이션을 쓰지 않고 생성자만 쓰면 된다.

- 롬복 라이브러리를 사용할 경우 매개변수 생성자를 만들어주는 어노테이션으로 쉽게 대체 가능하다.

왠만한 회사는 스프링을 쓸때 lombok기능을 쓴다.

lombok 기능으로 필드만 작성하면 ~된다.

- @AllArgsConstructor는 모든 필드들에 매개변수 생성자를 만들어 준다. (주입한다)

- @RequiredArgsConstructor는 final 필드만 매개변수 생성자를 만들어 준다. (주입한다)

 

 

 

 

 

 

 

 

- 필드만 두면 lombok으로 매개변수 생성자가 만들어져있다.

- 그런데 @AllArgsConstructor 어노테이션을 사용하진 않는다. 일반 필드들이 있을 때는 문제가 된다.

 

 

 

 

 

 

 

 

 

 

 

ㅁ NoticeController 수정

 

 
package com.br.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

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 noticeSerivce;
   
   
/*
    public NoticeController(NoticeService noticeService) {   <- 롬복
        this.noticeService = noticeService;
    }
*/
   
   
   
    // =========== 포워딩할 응답페이지에 필요한 데이터를 담는 방법 ===========
   
    @GetMapping("/list.do")
    public String noticeList() {
        List<NoticeDto> list = noticeService.selectNoticeList();
    }
   
   
   
   
}
 

 

 

- controller 클래스 상단의 @AllArgsConstructor 어노테이션을 주석처리하고 @RequiredArgsConstructor를 사용한다.

 

 

- 앞으로 사용할 DI 방식은 (final + @RequiredArgsConstructor)다.

일반 필드들이 추가될 수도 있기 때문에 모든 필드들에 주입이 되어서는 안된다.

 

 

 

 

 

ㅁ 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 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 noticeSerivce;
   
   
/*
    public NoticeController(NoticeService noticeService) {   <- 롬복
        this.noticeService = noticeService;
    }
*/
   
   
   
    // =========== 포워딩할 응답페이지에 필요한 데이터를 담는 방법 ===========
   
    @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";
       
    }
   
   
   
   
}
 

 

 

※ Model 객체 이용하기

- requestScope 영역에 데이터를 맵형식(key-value)로 담을 수 있는 객체

- 단 setAttribute가 아닌 addAttribute 메소드를 사용한다.

- 포워딩 처리가 되면 알아서 ~

 

 

 

 

 

 

 

ㅁ /WEB-INF/views에 notice 폴더를 만들고 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" %>    
<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>
   
    <table border="1">
        <thead>
            <tr>
                <th>번호</th>
                <th>제목</th>
                <th>내용</th>
            </tr>
        </thead>
        <tbody>
            <c:forEach var="n" items="${ list }">
                <tr>
                    <td>${ n.no }</td>
                    <td>${ n.title }</td>
                    <td>${ n.content }</td>
                </tr>
            </c:forEach>
        </tbody>
    </table>

</body>
</html>
 

 

 

 

 

 

 

ㅁ 서버 실행해서 이용해보기

 

 

 

 

 

 

 

 

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

 

 

 

(2) ModelAndView 객체 이용하기

- Model과 View가 합쳐져 있는 객체.

- Model은 데이터를 담는 객체, View는 응답 뷰에 대한 정보를 담는 객체

 

- ModelAndView 객체에 데이터와 응답 뷰에 대한 정보를 담고 해당 객체를 반환한다

 

 

 

 

ㅁ 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" %>    
<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>
   
    <table border="1">
        <thead>
            <tr>
                <th>번호</th>
                <th>제목</th>
                <th>내용</th>
            </tr>
        </thead>
        <tbody>
            <c:forEach var="n" items="${ list }">
                <tr>
                    <td>${ n.no }</td>
                    <td><a href="${contextPath}/notice/detail.do?no=${ n.no }">${ n.title }</a></td>
                    <td>${ n.content }</td>
                </tr>
            </c:forEach>
        </tbody>
    </table>

</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;
       
    }
   
   
   
}
 

 

 

- Model 객체, ModelAndView 객체 중 원하는 방식으로 하면 된다.

 

- 매개변수에 ModelAndView 객체를 추가한다. 

메소드의 반환형도 ModelAndView 객체로 작성한다.

 

 

- ModelAndView 객체의 addObject 메소드는 ModelAndView 객체를 반환한다.

그래서 메소드 체이닝이 가능하다.

- addObject 메소드를 쓰고 또 addObject 메소드를 쓰고 setViewName 메소드를 쓸 수 있다.

 

 

 

 

 

 

ㅁ notice 폴더에 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 }
        </c:otherwise>
    </c:choose>
   

</body>
</html>
 

 

 

 

 

 

 

 

 

'Spring' 카테고리의 다른 글

[Spring] Spring MVC 정리  (1) 2024.10.17
[Spring] MVC (7) 실습 - 공지사항 수정하기  (0) 2024.10.17
[Spring] MVC (5) Lombok 사용  (1) 2024.10.17
[Spring] MVC (4) 요청시 parameter 처리  (0) 2024.10.16
[Spring] MVC (3) book 서비스  (1) 2024.10.16