49일차(2)/Spring(3) : 요청 파라미터 추출 / forward, redirect 이동 / Dependency Injection
- Spring에서 요청 파라미터 추출하는 방법 3가지
- 페이지 이동 방법(forward, redirect 방식 비교)
- ModelAndView 객체 사용
- Dependency Injection 의 개념 및 사용방법
- 하단 Servers 탭의 서버 설정
- 서버 우클릭- Add and Remove - 지난 프로젝트는 좌측으로 빼두기(remove)
- 동시에 여러개의 프로젝트에서 서버를 쓰면 시간도 오래 걸리고 에러가 날 수도 있으므로
필요없는 프로젝트는 remove로 빼놓기!
- 새 프로젝트 생성 Spring01_Basic
- com.sy.spring01 : 패키지 이름의 세번째 단어가 context 경로가 된다.
home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/home.jsp</title>
</head>
<body>
<div class="container">
<h1>인덱스 페이지입니다.</h1>
<ul>
<li><a href="${pageContext.request.contextPath}/member/insertform">요청 파라미터 추출 테스트</a></li>
<li> <a href="${pageContext.request.contextPath}/move/test">이동 테스트</a></li>
<li><a href="di/test">Dependency Injection 테스트</a></li>
</ul>
<h3>공지사항</h3>
<ul>
<c:forEach var="tmp" items="${noticeList }">
<li>${tmp }</li>
</c:forEach>
</ul>
</div>
</body>
</html>
- com.sy.spring01 패키지 안에 member.dto 패키지 만들기
- MemberDto
package com.sy.spring01.member.dto;
public class MemberDto {
private int num;
private String name;
private String addr;
public MemberDto() {}
public MemberDto(int num, String name, String addr) {
super();
this.num = num;
this.name = name;
this.addr = addr;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
};
}
- com.sy.spring01패키지 안에 member 패키지 만들기
MemberController.java
package com.sy.spring01.member;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.sy.spring01.member.dto.MemberDto;
//component scan 을 통해서 bean이 될 수 있도록 어노테이션을 붙인다.
@Controller
public class MemberController {
@RequestMapping("/member/insertform")
public String insertform() {
// WEB-INF/view/member/insertform.jsp로 forward이동해서 응답
return "member/insertform";
}
@ResponseBody //리턴하는 문자열을 클라이언트에게 직접 출력하기
@RequestMapping("/member/insert")
public String insert(HttpServletRequest request) {
/*
* [ 요청 파라미터 추출하는 방법1 ]
*
* HttpServletReqest 객체를 Controller메소드로 전달받아서 추출
*/
int num=Integer.parseInt(request.getParameter("num"));
String name=request.getParameter("name");
String addr=request.getParameter("addr");
System.out.println(num+" | "+name+" | "+addr);
return "ok1";
}
@ResponseBody
@RequestMapping("/member/insert2")
public String insert2(int num, String name, String addr) {
/*
* [ 요청 파라미터 추출하는 방법2 ]
*
* 파라미터명과 동일하게 메소드의 매개변수를 선언해 놓으면 자동으로 추출해서 넣어준다.
* <input name="num"> 이면 int num or String num
* <input name="email"> 이면 String email 이런식으로 선언하면 된다.
*
* insert2(@RequestParam int num,@RequestParam String name,@RequestParam String addr)
* 에서 @RequestParam 어노테이션이 생략된 형태이다.
*/
System.out.println(num+" | "+name+" | "+addr);
return "ok2";
}
@ResponseBody
@RequestMapping("/member/insert3")
public String insert3(MemberDto dto) {
/*
* [ 요청 파라미터 추출하는 방법3 ]
*
* 파라미터명과 동일한 필드명을 가지고 있는 dto 클래스 type을 메소드의 매개변수로 선언해 놓으면
* 자동으로 추출해서 dto에 추출한 값을 setter메소드를 이용해서 넣은 다음 해당 dto 객체의
* 참조값에 전달한다.
*
* pubilc class MemberDto{
* private int num; => <input name="num">
* private String name; => <input name="name">
* private String addr; => <input name="addr">
*
* insert(@ModelAttribute MemberDto dto)에서 @ModelAtrribute가 생략된 형태이다.
*/
System.out.println(dto.getNum()+" | "+dto.getName()+" | "+dto.getAddr());
return "ok3";
}
}
@ResponseBody 어노테이션
- 리턴하는 문자열이 클라이언트에게 직접 출력되게 하는 어노테이션이다.
- 이것을 사용하면 리턴하는 문자열이 바로 클라이언트에게 응답되게 할 수 있다.
@RequestMapping(" / xxx/yyy "){
return " xxx/yyy "; }
- / 슬래시가 들어가고 들어가지 않는 자리 잘 기억하기!
views / member 폴더안에 만들기
/views/member/insertform.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/views/member/insertform.jsp</title>
</head>
<body>
<div class="container">
<h1>회원추가 폼</h1>
<form action="${pageContext.request.contextPath}/member/insert" method="post">
번호 <input type="text" name="num" /><br />
이름 <input type="text" name="name" /><br />
주소 <input type="text" name="addr" /><br />
<button type="submit">추가</button>
</form>
<h1>회원추가 폼2</h1>
<form action="${pageContext.request.contextPath}/member/insert2" method="post">
번호 <input type="text" name="num" /><br />
이름 <input type="text" name="name" /><br />
주소 <input type="text" name="addr" /><br />
<button type="submit">추가</button>
</form>
<h1>회원추가 폼3</h1>
<form action="${pageContext.request.contextPath}/member/insert" method="post">
번호 <input type="text" name="num" /><br />
이름 <input type="text" name="name" /><br />
주소 <input type="text" name="addr" /><br />
<button type="submit">추가</button>
</form>
</div>
</body>
</html>
- 주의!! form에서 이동하는 링크에 .jsp 는 안 들어간다. (기본으로 접미사로 들어가게 되어있으므로)
* 요청 패러미터 추출 방식 3가지!!
public String insert(HttpServletRequest request) { }
- 1번: 함수값을 호출하면서 알아서 넣어주니까 필요한 객체가 있으면 매개변수로 선언만 하면 된다
public String insert2(int num, String name, String addr) { }
- 2번: 파라미터명과 동일하게 메소드의 매개변수를 선언해 놓으면 자동으로 추출해서 넣어준다.
파라미터의 name=" " 값에 맞춰서!
- insert2(@RequestParam int num,@RequestParam String name,@RequestParam String addr) 에서
@RequestParam 어노테이션이 생략된 형태이다.
- int num 이렇게 넣으면 숫자로 변환하는 작업도 알아서 해준다.
- 위와 같이 parseInt() 를 하지 않아도 된다.
public String insert3(MemberDto dto) { }
- 3번 : 파라미터명과 동일한 필드명을 가지고 있는 dto 클래스 type을 메소드의 매개변수로 선언해 놓으면
자동으로 추출해서 dto에 추출한 값을 setter메소드를 이용해서 넣은 다음 해당 dto 객체의 참조값에 전달한다.
- 이 필드명이 일치하면 불러올 수 있다.
- dto 의 setter 메소드를 활용해서 넣어주는 것이다.(setNum, setName, setAddr)
- 이것을 사용하려면 DTO에 getter,setter가 필요한 구조로 잘 만들어져 있어야 한다.
- insert(@ModelAttribute MemberDto dto)에서 @ModelAtrribute가 생략된 형태이다.(어노테이션 생략 가능)
- 2번, 3번이 가장 잘 쓰인다.
- 2번은 게시판 글 상세보기 등에서 get방식 전송에서 읽어오는 데에 자주 사용된다.
(저런 값을 굳이 dto에 넣을 필요는 없기 때문에)
- 요청 파라미터를 자동으로 추출하고싶으면 controller에 선언만 해놓으면 된다.
- 새 예제 추가(이동)
com.sy.spring01/ TestController.java (최종)
package com.sy.spring01;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestController {
@Autowired //spring bean container에 Remoncon type객체가 있으면 자동 주입된다.(DI)
private RemoconService service; //RemoconServiceImpl 객체의 참조값을 주입받을 필드
@ResponseBody
@RequestMapping("/di/up")
public String diUp() {
/*
* 채널을 올리는 로직을 수행할 때 필요한 객체는?
* (원래는 RemoconServiceImpl 객체가 필요하다.)
* DI되어서 필드에 참조값이 있으므로 단순히 사용만 하면 된다.
*/
service.up();
return "up ok!";
}
@ResponseBody
@RequestMapping("/di/down")
public String diDown() {
service.down();
return "down ok!";
}
@RequestMapping("/di/test")
public String diTest() {
return "di/test";
}
@RequestMapping("/move/test")
public String test() {
/*
* Controller 메소드에서 return type을 String 으로 설정한 후
* 문자열을 리턴해주면 해당 문자열은 view page(jsp) 페이지의 위치가 된다.
* 즉 해당 jsp 페이지로 자동 forward 이동되어서 응답하게 된다.
*/
// /WEB-INF/views/move/test.jsp 페이지로 forward 이동
return "move/test";
}
@RequestMapping("/move/update")
public String update() {
//무언가 수정을 했다고 가정
System.out.println("무언가 수정했습니다.");
//클라이언트에게 새로운 경로로 요청을 다시 하라고 강요하기(redirect 이동)
// "redirect: 리다일렉트 이동할 절대경로" (context path는 쓰지 않는다)
return "redirect:/move/test";
}
@RequestMapping("/move/fortune")
public ModelAndView fortune() {
//Model과 view page의 위치를 동시에 넣을 수 있는 ModelAndView 객체 생성
ModelAndView mView=new ModelAndView();
//view page에 전달할 모델(데이터)라고 가정하자
String fortuneToday="동쪽으로 가면 귀인을 만나요!";
// HttpServletRequest 객체에 담는 대신, .addObject(key, value) 형태로 ModelAndView 객체에 담으면 된다.
mView.addObject("fortuneToday", fortuneToday);
//view page의 위치를 담는다.
mView.setViewName("move/fortune");
//리턴해주기
return mView;
}
// ModelAndView객체가 필요하다면 직접 생성하지 않고 메소드의 매개변수로 선언하면 자동으로 전달된다.
@RequestMapping("/move/fortune2")
public ModelAndView fortune2(ModelAndView mView) {
//view page에 전달할 모델(데이터)라고 가정하자
String fortuneToday="동쪽으로 가면 귀인을 만나요!";
// HttpServletRequest 객체에 담는 대신, .addObject(key, value) 형태로 ModelAndView 객체에 담으면 된다.
mView.addObject("fortuneToday", fortuneToday);
//view page의 위치를 담는다.
mView.setViewName("move/fortune");
//리턴해주기
return mView;
}
@RequestMapping("/move/fortune3")
public ModelAndView fortune3() {
ModelAndView mView=new ModelAndView("move/fortune");
//view page에 전달할 모델(데이터)라고 가정하자
String fortuneToday="동쪽으로 가면 귀인을 만나요!";
// HttpServletRequest 객체에 담는 대신, .addObject(key, value) 형태로 ModelAndView 객체에 담으면 된다.
mView.addObject("fortuneToday", fortuneToday);
//리턴해주기
return mView;
}
}
- 컨트롤러가 추가되었으면 새로고침만으로는 반영이 안 되고, 서버를 껐다 켜야 한다.
/views/move/test.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/views/move/test.jsp</title>
</head>
<body>
<div class="container">
<h3>페이지 이동 테스트</h3>
<p>이 페이지는 /move/test 요청에 대해서 forward 이동된 jsp페이지 입니다.</p>
<ul>
<li><a href="update">리다일렉트 이동하기</a></li>
<li><a href="fortune">forward 이동하는 다른 방법</a></li>
<li><a href="fortune2">forward 이동하는 다른 방법2</a></li>
<li><a href="fortune3">forward 이동하는 다른 방법3</a></li>
</ul>
</div>
</body>
</html>
- 문자열만 넣어놓으면 자동으로 forward된다.
- update 링크 추가 (상대경로)
- 새로운 <a>를 추가하면 이렇게 나온다. 이 주소에 따른 controller를 만들어야 한다!
@RequestMapping("/move/update")
public String update() {
//무언가 수정을 했다고 가정
System.out.println("무언가 수정했습니다.");
//클라이언트에게 새로운 경로로 요청을 다시 하라고 강요하기(redirect 이동)
// "redirect: 리다일렉트 이동할 절대경로" (context path는 쓰지 않는다)
return "redirect:/move/test";
}
- testController에 update() 메소드를 추가
- 콘솔창에 인쇄되면서 창은 새로고침되듯이 깜박거린다.
- 주소창에 .jsp가 나오지는 않는다.
- 지금 2가지 요청을 수행한 것이다!
return "redirect:/move/test";
" redirect: 리다일렉트 이동할 절대경로 "
- 이렇게 입력하면 리다일렉트 요청이 된다. (context path는 쓰지 않는다)
- test.jsp에 링크 추가. 새로운 forward 이동
/move/fortune.jsp 만들기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>views/move/fortune.jsp</title>
</head>
<body>
<div class="container">
<p>오늘의 운세: <strong>${fortuneToday }</strong></p>
</div>
</body>
</html>
- TestController에 메소드 추가 (test/fortune 에 대한 이동)
@RequestMapping("/move/fortune")
public ModelAndView fortune() {
//Model과 view page의 위치를 동시에 넣을 수 있는 ModelAndView 객체 생성
ModelAndView mView=new ModelAndView();
//view page에 전달할 모델(데이터)라고 가정하자
String fortuneToday="동쪽으로 가면 귀인을 만나요!";
// HttpServletRequest 객체에 담는 대신, .addObject(key, value) 형태로 ModelAndView 객체에 담으면 된다.
mView.addObject("fortuneToday", fortuneToday);
//view page의 위치를 담는다.
mView.setViewName("move/fortune");
//리턴해주기
return mView;
}
* ModelAndView : 데이터와 뷰 페이지의 정보를 보여주는 객체
- Model과 view page의 위치를 동시에 넣을 수 있다.
- HttpServletRequest 객체에 담지 않고 ModelAndView 객체에 담기
- .addObject() 로 key, value 형태로 담을 수 있다.
- ModelAndView 타입을 리턴타입으로 설정하는 것의 장점!
- 데이터와 view page를 같이 담아서 리턴해준다.
- 페이지도 이동하고, String(데이터)도 전달한다.
- 여러가지 정보를 담아서 한번에 리턴해주는 객체!
- Controller에서는 원하는 메소드를 아무거나 만들 수 있다. 메소드의 인자도 자유롭게 정할 수 있다.
fortune2 링크 추가
- TestController에 메소드 추가
@RequestMapping("/move/fortune2")
public ModelAndView fortune2(ModelAndView mView) {
//view page에 전달할 모델(데이터)라고 가정하자
String fortuneToday="동쪽으로 가면 귀인을 만나요!";
// HttpServletRequest 객체에 담는 대신, .addObject(key, value) 형태로 ModelAndView 객체에 담으면 된다.
mView.addObject("fortuneToday", fortuneToday);
//view page의 위치를 담는다.
mView.setViewName("move/fortune");
//리턴해주기
return mView;
}
- ModelAndView 객체를 따로 new 하지 않고 그 대신 매개변수로 받을 수도 있다.
- 매개변수로 선언하면 자동으로 객체가 전달된다.
fortune3 링크추가
- TestController에 메소드 추가
@RequestMapping("/move/fortune3")
public ModelAndView fortune3() {
ModelAndView mView=new ModelAndView("move/fortune");
//view page에 전달할 모델(데이터)라고 가정하자
String fortuneToday="동쪽으로 가면 귀인을 만나요!";
// HttpServletRequest 객체에 담는 대신, .addObject(key, value) 형태로 ModelAndView 객체에 담으면 된다.
mView.addObject("fortuneToday", fortuneToday);
//리턴해주기
return mView;
}
- ctrl+space로 보면 viewpage의 정보를 생성자의 인자로 전달할 수도 있다.
- 이렇게 작성하면 addObject() 만 하고, setViewName() 은 따로 해주지 않아도 된다. 지워주기!
- home.jsp에 DI 링크 추가
- Dependency Injection : 의존관계 주입. 스프링의 중요한 개념.
* 대규모 프로젝트에서 spring framework를 사용하는 이유는 무엇일까?
- 유지보수가 편리해서이다. 이 유지보수를 편리하게 하기 위해서 나온 개념이 Dependency Injection 이다.
- 유지보수가 편하려면 객체들 간의 의존관계가 느슨해야 한다.
의존관계를 느슨히게 하려면 핵심 의존 객체의 생성과 관리를 spring에 맡긴다.
- 객체를 spring이 생성하게 하고, spring으로부터 받아서 쓴다.
- 필요한 객체가 있으면 spring bean container로부터 주입받아서 사용하는 구조로 프로그래밍을 해야한다.
- 단. 주입받을 때는 반드시 interface type으로 받아서 사용한다.
- container에서 컴포넌트 스캔을 해서 어떤 객체를 관리할지 정해서 bean이 만들어진다는 것 기억!
기본 spring01 패키지에 interface를 만들어준다.
RemoconService (인터페이스)
package com.sy.spring01;
public interface RemoconService {
public void up();
public void down();
}
- 추삼메소드 2개가 있다.
RemoconServiceImpl (클래스)
package com.sy.spring01;
import org.springframework.stereotype.Service;
@Service
public class RemoconServiceImpl implements RemoconService{
@Override
public void up() {
System.out.println("채널을 올려요!");
}
@Override
public void down() {
System.out.println("채널을 내려요!");
}
}
- 인터페이스를 상속받고 override 해준다.
- @Service 어노테이션을 붙여주기
- TestController에 diTest 메소드 추가
@RequestMapping("/di/test")
public String diTest() {
return "di/test";
}
views/di/ test.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/views/di/test.jsp</title>
</head>
<body>
<div class="container">
<h2>DI 테스트 링크</h2>
<ul>
<li><a href="up">채널 올리기</a></li>
<li><a href="down">채널 내리기</a></li>
</ul>
</div>
</body>
</html>
- 현재 상태
TestController에 메소드추가(up, down)
@Autowired //spring bean container에 Remoncon type객체가 있으면 자동 주입된다.(DI)
private RemoconService service; //RemoconServiceImpl 객체의 참조값을 주입받을 필드
@ResponseBody
@RequestMapping("/di/up")
public String diUp() {
service.up();
return "up ok!";
}
@ResponseBody
@RequestMapping("/di/down")
public String diDown() {
service.down();
return "down ok!";
}
- 이렇게 해도 잘 작동하지만, 스프링의 관점에서는 그다지 좋지 않다.
- 이렇게 객체를 메소드 안에서 바로 new해서 사용하는 것은 좋지 않다.
- 이러면 RemoconServiceImpl 객체가 수정되거나 삭제되면 전체를 다 뜯어고쳐야 한다.
- 따라서 객체 생성을 spring에 맡기는 것이 좋다.
@Service
- 이미 이 annotation을 붙임으로서 핵심 의존 객체의 생성과 관리를 spring에 맡긴 것이다.
- 그렇다면 이제 1)주입받기 2)인터페이스 타입으로 받기 를 해주면 된다.
- 여기에 service 필드를 하나 생성해준다. RemoconServiceImpl 객체의 참조값을 주입받을 필드!
- 하지만 생성한 상태이기만 하면 null이기 때문에 null point exception이 발생한다.
- 저 필드에 @Autowired 어노테이션을 추가하면 작동한다.
- 이렇게 하면 Spring bean container에 Remoncon type객체가 있으면 자동 주입된다.(DI)
- 원래는 null인데 저걸 추가해주면 객체가 들어간다. 인터페이스 타입으로 의존하는 구조로 만들어주는 것이다.
- 객체의 생성과 관리를 spring에 맡기는것. 인터페이스 타입인지 확인하고 spring이 찾아서 넣어준다.
- 이런 형태로 인터페이스를 만들 일이 많다.
- Dao를 만들때에도 인터페이스를 미리 정의해서 implement 해서 만들 수 있다.
의존 관계를 느슨하게 만들기 위해!
- 이렇게 하면 RemoconServiceImpl 클래스 하나를 삭제하거나 Rename 해도 에러가 발생하지 않는다.
TestController에서는 RemoconServiceImpl 을 사용하고 있지 않다.
- 코딩 양은 좀 많아지지만, 이것이 스프링을 사용하는 이유이다.
- down도 동일하다. service 필드에 핵심 의존 객체가 들어있다고 가정하고 그냥 사용만 하면 된다.
'국비교육(22-23)' 카테고리의 다른 글
50일차(2)/Spring(5) : MyBatis / 회원 추가, 삭제 기능 구현 (insert, delete) (0) | 2022.12.17 |
---|---|
50일차(1)/Spring(4) : MyBatis 설치 / DB 데이터 불러오기 (getList) (0) | 2022.12.17 |
49일차(1)/Spring(2) : MVC Project 초기 세팅 / Spring 기본 개념 익히기 (0) | 2022.12.15 |
48일차(2)/Spring(1) : java, maven, STS 설치 및 기본 세팅 (1) | 2022.12.15 |
48일차(1)/React(14) : SPA(Single Page Application) 예제(2) (0) | 2022.12.14 |