스프링 MVC : 요청 매핑, 커맨드 객체, 리다이렉트, 폼 태그, 모델
요청 메서드(method)
@RequestMapping : 모든 요청 메서드에 매핑, method설정에 GET, POST, DELETE 등등 설정 가능.
@GetMapping
@PostMapping
@PatchMapping
@DeleteMapping
@DeleteMapping
params : 요청 데이터를 한정해서 매핑
headers : 요청쪽 헤더 데이터 체크
consumes : 요청쪽 Content-Type 체크
producdes : 응답 헤더쪽 Content-Type 설정
@Controller
@RequestMapping("/member")
public class MemberController {
private final Logger log = LoggerFactory.getLogger(MemberController.class);
@GetMapping("/join")
public String join1() {
log.info("mode없음");
return "member/join";
}
@GetMapping(value = "/join", params = {"mode=join"})
public String join() {
log.info("mode=join");
return "member/join";
}
}
private final Logger log = LoggerFactory.getLogger(MemberController.class);
log.fatal(...)log.error(...)log.warn(...)log.info(...)log.debug(...)log.trace(...)lombok활용시 다음 애노테이션 사용하면 로거 연동 코드 자동 생성, 변수명은 log
@slf4j@log4j@log4j2@Slf4j //로거사용
@Controller
@RequestMapping("/member")
public class MemberController {
@GetMapping("/join")
public String join1() {
log.info("mode없음"); //로그
return "member/join";
}
@GetMapping(value = "/join", params = {"mode=join"})
public String join() {
log.info("mode=join"); //로그
return "member/join";
}
}
컨트롤러 테스트
params : 요청 데이터를 한정해서 매핑
headers : 요청쪽 헤더 데이터 체크
consumes : 요청쪽 Content-Type 한정해서 체크
producdes : 응답 헤더쪽 Content-Type 설정
@Slf4j
@Controller
@RequestMapping("/member")
public class MemberController {
//요청헤더에 appkey가 1234인 요청만 받는다.
//consumes는 요청데이터의 contentType만 한정할 수 있다.
//응답헤더의 contentType은 Json이다
@PostMapping(path ="/join", headers="appKey=1234", consumes=MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public String joinPs(RequestJoin form) {
log.info("joinPs실행..");
return "redirect:/member/login";
}
}
@SpringJUnitWebConfig
@ContextConfiguration(classes = MvcConfig.class)
public class MemberControllerTest {
@Autowired
private WebApplicationContext ctx; //다형성을 위해 WebApplicationContext을사용
private MockMvc mockMvc;
@BeforeEach
void init(){
mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build(); //mocMvc 설정
}
@Test
void test1() throws Exception {
mockMvc.perform(post("/member/join")
.header("appKey","1234") //요청헤더
.contentType("application/json")
)
.andDo(print());
}
}
redirect
응답헤더의 Location을 주소추가, 페이지 이동(302)을 함.
forward : 주소의 출력 데이터로 버퍼를 치환, (200)
RequestDispatcher - forward
@Slf4j
@Controller
@RequestMapping("/member")
public class MemberController {
@GetMapping("/join")
public String join() {
return "member/join";
}
@PostMapping("/join")
public String joinPs(RequestJoin form){
return "redirect:/member/join"; //Location : /day05/member/login
}
}
1) @RequestParam
2) 커맨드 객체를 이용해서 요청 파라미터 사용하기
커맨드 객체 클래스명 👉 EL 속성으로 자동 추가
예) RequestJoin 👉 requestJoin
회원가입할때 검증실패시 이전에 입력했던 값들을 유지할 수 있음.
<input type="text" name="userName" value="${requestJoin.userName}">
Post메서드에만 커맨드객체가 존재하면 Get메서드에서는 커맨드객체가 null값으로 오류가 발생하기 때문에. Get메서드에도 커맨드객체가 존재해야하기 때문에 비어있더라도 커맨드객체를 정의해야한다.
@Slf4j
@Controller
@RequestMapping("/member")
public class MemberController {
@GetMapping("/join")
public String join(Model model) {
RequestJoin form = new RequestJoin();
model.addAttribute("requestJoin", form);
return "member/join";
}
@PostMapping("/join")
public String joinPs(RequestJoin form){
log.info(form.toString());
return "member/join";
}
@GetMapping("/login")
public String login() {
return "member/login";
}
}
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%--커스텀태그--%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<c:url var="actionUrl" value="/member/join"/>
<h1>회원가입</h1>
<form:form method="post" action="${actionUrl}" autocomplete="off" modelAttribute="requestJoin">
<dl>
<dt>이메일</dt>
<dd>
<form:input path="email"/>
</dd>
</dl>
<dl>
<dt>비밀번호</dt>
<dd>
<form:password path="password" />
</dd>
</dl>
<dl>
<dt>비밀번호확인</dt>
<dd>
<form:password path="confirmPassword" />
</dd>
</dl>
<dl>
<dt>회원명</dt>
<dd>
<form:input path="userName" />
</dd>
</dl>
<dl>
<dt>약관동의</dt>
<dd>
<form:checkbox path="agree" value="true" label="회원가입 약관에 동의합니다."/>
</dd>
</dl>
<button type="submit">가입하기</button>
</form:form>
항상 커맨드 객체를 정의해야한다는 번거로움이 있지만, @ModelAttribute을 사용하면 자동으로 빈 커맨드 객체가 생성되어진다.
//자동 빈 커맨드 객체 생성
@GetMapping
public String join(@ModelAttribute RequestJoin form){
return "member/join";
}
//번거로움
@GetMapping("/join")
public String join(Model model) {
RequestJoin form = new RequestJoin();
model.addAttribute("requestJoin", form);
return "member/join";
}
커맨드 객체명는 기본적으로 클래스명에서 맨앞자만 소문자로 변경되어 정해진다. 특정 커맨드객체의 속성이름을 변경 하고싶을 때 @ModelAttribute사용
//커맨드 객체 속성 이름명 변경
@GetMapping
public String join(@ModelAttribute("command") RequestJoin form){
return "member/join";
}
@PostMapping("/join")
public String joinPs(@ModelAttribute("command") RequestJoin form){
log.info(form.toString());
return "member/join";
}
Controller, RestController : 컨트롤러에서의 공통 데이터
ControllerAdvice, RestControllerAdvice : 지정된 패키지 범위에서의 공통 데이터
@ModelAttribute("CommonValue")
public String CommonValue(){
return "공통 속성값...";
}
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<form:form><form:input><form:password>컨트롤러 구성을 할 필요 없는 간단한 페이지 구성시 사용
WebMvcConfigurer 인터페이스 - addViewControllers 설정
MvcConfig
@Configuration
@EnableWebMvc
@ComponentScan("org.choongang")
@Import(DBConfig.class)
public class MvcConfig implements WebMvcConfigurer {
// 컨트롤러 구현 없이 main페이지 mypage 매핑
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("main/index");
registry.addViewController("/mypage").setViewName("mypage/index");
}
}
public record RequestLogin2(
String email,
String password
) {}
@GetMapping("/login")
public String login(RequestLogin2 form){
if(form != null){
log.info("이메일 : {}, 비밀번호 : {}", form.email(), form.password());
}
return "member/login";
}
@Data
public class RequestJoin {
private String email;
private String password;
private String confirmPassword;
private String userName;
private List<String> hobby;
//private String hobby; //String으로 할경우 ,로 구분하여 저장됨.
private boolean agree;
}
@ModelAttribute("hobbies")
public List<String> hobbies(){
return List.of("취미1", "취미2", "취미3", "취미4");
}
<dl>
<dt>취미</dt>
<dd>
<form:checkboxes path="hobby" items="${hobbies}" />
</dd>
</dl>
중첩 : 커맨드 객체 안에 커맨드 객체
중첩된 커맨드 객체는 배열형태로 저장되어진다.
Address
@Data
public class Address {
private String zipCode;
private String address;
private String addressSub;
}
RequestJoin
@Data
public class RequestJoin {
private String email;
private String password;
private String confirmPassword;
private String userName;
private List<String> hobby;
private boolean agree;
private Address addr;
}
<dl>
<dt>주소</dt>
<dd>
<form:input path="addr.zipCode" placeholder="우편번호"/>
<form:input path="addr.address" placeholder="주소1"/>
<form:input path="addr.addressSub" placeholder="주소2"/>
</dd>
</dl>
<form> 태그를 위한 커스텀 태그: <form:form><input> 관련 커스텀 태그 : <form:input>, <form:password>, <form:hidden><select> 관련 커스텀 태그 : <form:select>, <form:options>, <form:option><form:checkboxes>, <form:checkbox><form:radiobuttons>, <form:radiobutton><textarea〉 태그를 위한 커스텀 태그 : <form:textarea>CSS 및 HTML 태그와 관련된 공통 속성
1. cssClass: HTML의 class 속성값
2. cssErrorClass : 폼 검증 에러가 발생했을 때 사용할 HTML의 class 속성값
3. cssStyle: HTML의 style 속성값
HTML 태그가 사용하는 다음 속성도 사용 가능하다.
1. id, title, dir
2. disabled, tabindex
3. onfocus, onblur, onchange onclick, ondblclick
4. onkeydown, onkeypress, onkeyup
5. onmousedown, onmousemove, onmouseup
6. onmouseout, onmouseover
각 커스텀 태그는 htmlEscape 속성을 사용해서 커맨드 객체의 값에 포함된 HTML 특수 문자를 엔티티 레퍼런스로 변환할지를 결정할 수 있다.