Spring MVC 2

김정훈·2024년 7월 10일

Spring

목록 보기
9/24

스프링 MVC : 요청 매핑, 커맨드 객체, 리다이렉트, 폼 태그, 모델

1. 요청 매핑 애노테이션을 이용한 경로 매핑

요청 메서드(method)

  • GET, DELETE
  • POST,PATCH, PUT

@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활용

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

참고) 통합테스트 MockMvc

컨트롤러 테스트
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
    }
}

2. 요청 파라미터 접근

1) @RequestParam
2) 커맨드 객체를 이용해서 요청 파라미터 사용하기

커맨드 객체 클래스명 👉 EL 속성으로 자동 추가
예) RequestJoin 👉 requestJoin

회원가입할때 검증실패시 이전에 입력했던 값들을 유지할 수 있음.

<input type="text" name="userName" value="${requestJoin.userName}">

3. 뷰 JSP 코드에서 커맨드 객체 사용하기

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>

4. @ModelAttribute

1) 빈 커맨드 객체 자동 생성

항상 커맨드 객체를 정의해야한다는 번거로움이 있지만, @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";
}

2) 커맨드 객체 속성 이름 변경

커맨드 객체명는 기본적으로 클래스명에서 맨앞자만 소문자로 변경되어 정해진다. 특정 커맨드객체의 속성이름을 변경 하고싶을 때 @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";
}

3) 공통 데이터(속성)으로 사용할 데이터 지정

Controller, RestController : 컨트롤러에서의 공통 데이터
ControllerAdvice, RestControllerAdvice : 지정된 패키지 범위에서의 공통 데이터

@ModelAttribute("CommonValue")
public String CommonValue(){
    return "공통 속성값...";
}

5. 커맨드 객체와 스프링 폼 연동

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
  • <form:form>
  • <form:input>
  • <form:password>

6. 컨트롤러 구현 없는 경로 매핑

컨트롤러 구성을 할 필요 없는 간단한 페이지 구성시 사용
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";
}

7. 커맨드 객체 : 중첩 · 콜렉션 프로퍼티

@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>

8. Model을 통해 컨트롤러에서 뷰에 데이터 전달하기

9. ModelAndView를 통한 뷰 선택과 모델 전달

10. GET 방식과 POST 방식에 동일 이름 커맨드 객체 사용하기

주요 폼 태그 설명

  1. <form> 태그를 위한 커스텀 태그: <form:form>
  2. <input> 관련 커스텀 태그 : <form:input>, <form:password>, <form:hidden>
  3. <select> 관련 커스텀 태그 : <form:select>, <form:options>, <form:option>
  4. 체크박스 관련 커스텀 태그 : <form:checkboxes>, <form:checkbox>
  5. 라디오버튼 관련 커스텀 태그: <form:radiobuttons>, <form:radiobutton>
  6. <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 특수 문자를 엔티티 레퍼런스로 변환할지를 결정할 수 있다.

profile
안녕하세요!

0개의 댓글