Oracle 사용 및 Thymeleaf

TrainingDummy1·2021년 1월 13일
0

https://velog.io/@tladbstjsdl/%EB%93%9C%EB%94%94%EC%96%B4-Spring-boot-Kotlin%EC%9C%BC%EB%A1%9C-jsp-%EB%9D%84%EC%9A%B0%EA%B8%B0-%EC%84%B1%EA%B3%B5
 저번 글에 이어서...는 아니고 중간에 약간 바뀌었다.
일단 jsp를 때려치고 요즘 스프링에서 밀어준다는 thymeleaf를 사용하려 한다. thymeleaf를 쓰다 보니 jsp의 taglib은 천사였더라

http://thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
 일단 공식 문서를 보는데 이게 뭔소린가 싶다. 스프링부트 기준으로 설명 된 문서가 없는건지 자바 코드로 열심히 설정파일 만드는데, 스프링부트에서 어디까지 자동으로 해준건지를 모르니 이해하기도 어렵다.
 여기서 시간 잔뜩 쓰다가 직접 해보니 역시 실패. 그래서 여기저기 찾아다니며 만드는데도 계속 실패... getter setter가 필요하다는 둥 여러 정보가 쏟아지지만 내게 필요한 것은 안나오고...
 그러다 마침내! 이해했다.
 이제 본론으로 넘어가자.

index.html

<!DOCTYPE html >
<html lang="ko" xmlns:th="http://thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>인덱스 페이지!</title>
</head>
<body>
<p th:if="${member != null}" th:text="|로그인 중(${member.memberId})|">
    <form th:if="${member != null}" th:object="${memberForm}" method="post" th:action="@{/insertMember}">
        <label for="memberId">ID: </label>
            <input th:field="*{memberId}" id="memberId" name="memberId">
        <label for="password">비밀번호: </label>
            <input th:field="*{password}" id="password" name="password" type="password">
        <label for="email">이메일: </label>
            <input th:field="*{email}" id="email" name="email" type="email"  />
        <label for="phone">휴대폰: </label>
            <input th:field="*{phone}" id="phone" name="phone" type="tel">
        <label for="address">주소: </label>
            <input th:field="*{address}" id="address" name="address">
        <button>ㅇㅅㅇ!!!</button>
    </form>
</p>
<p th:unless="${member != null}">로그인 필요</p>
</body>
</html>

가장 먼저 띄우려는 페이지다. id 파라미터를 받아서 member를 조회하고 결과가 있으면 form이 뜨게, 없으면 뜨지 않게 만들었다.
form의 th:object의 값인 memberForm의 객체의 각 필드를 받아서 컨트롤러로 전달하면, 컨트롤러는 memberForm이라는 매개변수로 받아서 내부 값을 사용할 수 있다.

th:if나 th:unless는 흔히 쓰는 if-else고, th:text는 내용물을 Model로 받아와서 출력하는 것이다.
 문제는 th:object와 th:field. 이 두 녀석이 내 시간을 거의 다 잡아먹었다.
th:object는 form의 property로 쓰는 것으로, Model로 받은 객체를 쓴다. th:field는 th:object의 각 속성(field)에 대응한다. 그리고 submit 시 각 속성을 th:object에 담아서 재전송한다.
 그렇기에 index.html을 띄울 때 컨트롤러에서 객체를 하나 더 만들어서 보내야한다는 번거로움이 있다. jsp에서는 form 페이지에 접근할 때 Model을 쓰는 경우는 계정 권한 확인용으로밖에 쓸 일이 없었는데, 여기선 굳이 객체를 만들어서 보내야하나보다.
 덕분에 테스트용으로 모든 속성이 nullable한 유사data클래스를 하나 더 만들었다.

MemberForm.kt

class MemberForm {
	var memberId: String? = null
	var password: String? = null
	var phone: String? = null
	var email: String? = null
	var address: String? = null
}

테스트를 위해 임시로 만든 클래스

  컨트롤러에서 이 클래스로 객체를 만들어 Model로 보내고 th:object로 받아서 th:field로 채우는 것에 성공했다. 이젠 기존의 Dto로 바꾸고, 일단은 백에서만이라도 유효성 검사를 만들어야겠다.

MemberController.kt

import com.dummy.wordbook.dto.MemberDto
import com.dummy.wordbook.form.MemberForm
import com.dummy.wordbook.service.MemberService
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import javax.servlet.http.HttpServletRequest

@Controller
class MemberController(private val memberService: MemberService) {
    @RequestMapping("/")
    public fun indexPage(req: HttpServletRequest, m: Model): String {
        var idParam: String? = req.getParameter("id")
        if(idParam != null && idParam.toIntOrNull() != null) {
            println("ID: " + idParam.toInt())
            var loginedMember: MemberDto = memberService.selectMemberById(idParam.toInt())
            m.addAttribute("member", loginedMember)
            m.addAttribute("memberForm", MemberForm())
            println("member = $loginedMember")
        }
        return "index"
    }

    @PostMapping("/insertMember")
    public fun insertMember(req: HttpServletRequest, m: Model, memberForm: MemberForm): String {
        /*val newMember: MemberDto = MemberDto(null, memberForm.memberId!!
                , req.getParameter("password"), req.getParameter("email")
                , req.getParameter("phone"), req.getParameter("address")
                , null, 0)
        memberService.insertMember(newMember).run {
            m.addAttribute("newMember", memberService.selectMemberByMemberId(newMember.memberId))
        }*/
        val newMember: MemberDto = MemberDto(null, memberForm.memberId!!
                , memberForm.password!!, memberForm.email!!, memberForm.phone!!
                , when(memberForm.address.isNullOrBlank()) {
                    true -> null
                    else -> memberForm.address
                }, null, 0)
        memberService.insertMember(newMember).run {
            m.addAttribute("newMember", memberService.selectMemberByMemberId(newMember.memberId))
        }
        return "insertComplete"
    }
}

바뀐 컨트롤러. /insertMember는 password가 포함되기에 postmapping으로 받는다. 중간에 when 문이 있는데, JAVA의 switch의 진화형이라 보면 된다. ->의 왼쪽이 when 옆 괄호의 조건, ->의 오른쪽이 조건에 따른 진행 방향이다. Kotlin에서는 조건문이 return 값을 가질 수 있다!!(이게 코딩이지) 즉, newMember.address 값이 memberForm.address가 공백이나 null이면 null, 그 외엔 그대로 값이 입력된다.
 또 그 밑의 .run은 callback함수라고 생각하면 된다. 즉, newMember가 insert 완료 후 값을 확실히 받아온 뒤에 진행이 된다.

중간에 when도 쓰고, 굳이 select를 한 번 더 하고 했지만, 사실 크게 의미있는 부분은 아니다. when 부분은 테스트를 위해서 넣어본 것이고, run 내부도 Oracle에 id가 제대로 자동생성되어서 들어갔나 보기 위해서 넣은 것이다.

  아직도 갈 길이 멀다. Oracle은 MySQL과 다르게 쉽게 Column의 속성을 넣기가 어렵고, Thymeleaf는 홈페이지에 나와있는 표면상의 장점 외에는 jsp를 대체할 장점이 전혀 느껴지지도 않는다. 앞으로 남은 것들을 하다 보면 어떻게 느껴질 지 모르겠지만, 지금으로선 '참 별로다' 라는 생각이 든다.
 다음엔 Controller에서 유효성 검사를 하고 회원가입 페이지, 로그인 페이지를 만들어야겠다.

profile
연습용더미1

0개의 댓글