inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술

form 안에 form이 있을 경우 vo를 어떻게 사용해야 할까

1240

인프러너

작성한 질문수 58

0

 

 

<form th:action=@"{/board/create}" th:object="${board}">
    <input type="text" th:field="*{name}">
    <button type="button" th:onclick="boardSubmit">글작성</button>
    <form th:action=@"{/board/create2}" th:object="${notice}">    
        <input type="hidden" th:field="*{boardId}">    
        <input type="text" th:field="*{title}">    
        <button type="button" th:onclick="noticeSubmit">글작성</button>

위에 적은 소스는 샘플로 간단하게 Vo 안에 하나의 필드들만 들어갔을 경우를 예로 작성한 내용입니다.

현업에서 작업을 하다보면 글 작성 form 안에 여러개의 form 이 들어가야 하는 경우가 한번씩 발생이 됩니다.

 

이럴 경우에는 어떤식으로 구조를 잡아야 할지 궁금합니다.

@Getter
@Setter
public class RequestBoard{
    private String name;
    private RequestNotice Notice;

    @Getter
    @Setter
    private static class RequestNotice {
        private String notice
    }
}

위와같이 innerClass를 이용해서 작성을 했는데 잘 안됩니다.

 

구현하고자 하는 기능은.

boardSubmit을 클릭했을 때는 board의 name값이 저장되고,

noticeSubmit을 클릭했을때는 title이 저장이 되게 하고 싶습니다.

javascript에서 값을 받아서 ajax로 넘겨서 저장을 할려고 하면 input 값들이 많이 있을 경우에는 해당 vo도 만들어야하고, script에서도 그 값들을 일일히 받아서 컨트롤러로 넘겨줘야 하는데..

 

타임리프를 이용해서 할 수 있는 방법이 있을까요?

 

 

spring mvc

답변 1

0

김영한

안녕하세요. 인프러너님

우선 HTML에서는 form 안에 또 다른 form을 중첩해서 넣는 것이 허용되지 않습니다. 즉, form 요소는 다른 form 요소 안에 중첩될 수 없습니다.

만약 중첩된 form들을 사용하려 하면, 웹 브라우저는 예상치 못한 동작을 할 수 있습니다. 이를 피하기 위해 별도의 form들로 분리하거나, JavaScript를 사용하여 복잡한 제출 동작을 수행해야 합니다.

대안으로 하나의 form을 만들고, 그 안에서 name을 .(dot)을 이용해서 구분하는 방법이 있습니다.

아래 예제를 보시면 어떻게 작동하는지 이해가 되실거에요.

 

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Sample Form</title>
</head>
<body>
<form action="/submit" method="post">
    User Name: <input type="text" name="userName"><br>
    
    Address:
    Street: <input type="text" name="address.street"><br>
    City: <input type="text" name="address.city"><br>
    Zip Code: <input type="text" name="address.zipCode"><br>
    
    <input type="submit" value="Submit">
</form>
</body>
</html>

 

DTO

package com.example.mvcmodel;

import lombok.Data;

@Data
public class UserDto {
    private String userName;
    private Address address;

    // getters, setters, etc.

    @Data
    public static class Address {
        private String street;
        private String city;
        private String zipCode;

        // getters, setters, etc.
    }
}

여기 getters, setters가 필요할꺼에요.

 

컨트롤러

package com.example.mvcmodel;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Slf4j
@Controller
public class FormController {

    @GetMapping("/form")
    public String form() {
        return "form";
    }

    @PostMapping("/submit")
    public String submit(UserDto userDto) {
        log.info("userDto={}", userDto);
        return "form";
    }

}

 

도움이 되셨길 바래요 :)

0

인프러너

답변 감사합니다.

thymeleaf 문법으로 사용을 해서

<form action="/form/test/submit" th:object="${userDto}" method="post">
    User Name: <input type="text" th:field="*{userName}"><br>

    Address:
    Street: <input type="text" th:field="*{address.street}"><br>
    City: <input type="text" th:field="*{address.city}"><br>
    Zip Code: <input type="text" th:field="*{address.zipCode}"><br>
    <input type="submit" value="Submit">
</form>

위와같은 식으로도 테스트를 해 보니 사용이 가능한데 만약 Address영역(Street, City, Zip Code)이 동적으로 추가가 되어지는 UI일 경우에는 위와같이 사용을 하면 id가 중복이 되어버려서

알려주신대로 아래와 같이 테스트를 해 보니 잘 되더라구요.

    Street: <input type="text" th:name="address.street"><br>
    City: <input type="text" th:name="address.city"><br>
    Zip Code: <input type="text" th:name="address.zipCode"><br>

 

그런데 궁금한점이..

타임리프를 사용할 경우에 아래 두 가지 경우 모두

Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "address.street" (template: "admin/test/list" - line 14, col 32)

...

org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'street' cannot be found on null

오류가 발생합니다.

    Street: <input type="text" th:name="${address.street}"><br>
    City: <input type="text" th:name="${address.city}"><br>
    Zip Code: <input type="text" th:name="${address.zipCode}"><br>
    Address:<br>
    Street: <input type="text" th:name="${userDto.address.street}"><br>
    City: <input type="text" th:name="${userDto.address.city}"><br>
    Zip Code: <input type="text" th:name="${userDto.address.zipCode}"><br>

 

타임리프를 사용한다고 하더라도 innerClass를 사용하는 경우에는

th:name="${address.street}" 이렇게 사용을 하면 안되고

th:name="address.street" 이런식으로 사용을 해야되는걸까요?

 

Dto를 UserDto와 Address로 따로 만든다면..컨트롤러에서 뷰단으로 모델을 두 번 넘겨주면 될 것 같은데 하나의 뷰에서 dto를 여러개 사용하는 경우도 있을까요?

 

0

김영한

안녕하세요. 인프러너님 아마도 컨트롤러를 거쳐서 처음 폼에 접근하는 시점인 것 같은데요. 컨트롤러에서 address 인스턴스가 생성되어있는 상태로 Model에 담아서 넘기셔야 합니다.

처음 폼을 사용한다고 해도 타임리프는 address.street를 호출해야 합니다. 이때 address가 null이어서 발생하는 문제입니다.

감사합니다.

이미지 업로드와 db 트랜잭션 묶는법

0

43

1

Could not resolve org.springframework.boot:spring-boot-starter-validation:2.4.4

0

53

2

MessageSourceTest 코드

0

47

1

인터셉터 에러 설정

0

48

1

resolveArgument()메서드 질문

0

57

1

43강 검증1 에서 실패 로직 관련 질문있습니다.

0

56

2

타임리프 3.X 버전 rendering, serializer 에러 해결 방법

2

133

3

스프링 빈에 등록이 안되는거 같은데 어떻게 하면 좋을까요?ㅠㅠ

0

90

3

pdf 오타 문의

0

57

1

ItemUpdateForm 검증 관련 질문입니다.

0

48

1

22page 링크 주소 변경

0

59

2

특정 데이터와 파일을 함께 저장 시, 테이블 구조 질문

0

53

1

섹션3번 수업에 대한 질문입니다.

0

80

2

@Autowired 보다 더 좋은 방법이 어떤 걸까요?

0

85

2

타입컨버터 가 람다랑 비슷해 보이는데 저의 생각이 맞는지?.

0

65

1

자바스크립트 인라인에서 객체 직렬화 시 오류가 납니다

0

142

3

스프링부트 - 오류페이지2 에서 500.html 에서 쓰인 객체 질문

0

63

1

톰캣 에러 페이지가 안보입니다.

0

102

2

apiEceptionController에서 센드 에러 호출하면 안되는지?

0

81

1

세션 타임아웃시 쿠키 삭제 방법이 없나요?

0

118

2

ApiExceptionController 질문드립니다.

0

64

1

셀렉박스 챕터에서 option value에 ==배송 방식 선택== 이것을 넣은 이유가 궁금함, 이렇게 구상해도 되는지?

0

66

1

MemberRepository 필드의 fianl 선언 유무

0

85

2

혹시 index.html 에서는 fragment 사용이 안되는건가요

0

58

1