• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

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

23.10.22 14:52 작성 조회수 677

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에서도 그 값들을 일일히 받아서 컨트롤러로 넘겨줘야 하는데..

 

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

 

 

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

}

 

도움이 되셨길 바래요 :)

답변 감사합니다.

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를 여러개 사용하는 경우도 있을까요?

 

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

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

감사합니다.