티스토리 뷰

etc/CHEAT SHEET

타임리프 문법 및 사용 방법

기억용블로그 2022. 5. 24. 00:25
728x90

인텔리제이가 타임리프 문법을 잡아주지 않아 자주 헷갈려 항상 보고 참고하기 위해 작성

 

form 작성

GET 요청을 할때 form말고 다른 방법도 많으므로 거의 항상 POST 요청을 위해 사용

th:action="@{/XXX/XXX}"으로 작성하여 어떤 URL로 보낼지 명시

th:object="${signupRequest}"와 같이 어떤 객체가 값을 받게 될지 명시한다.

Controller에서 GET 요청할때 정확히 똑같은 이름으로(signupRequest) 미리 보내야 하며

(GET에 미리 보내지 않으면 템플릿 엔진 에러 발생)

POST로 받을 때도 정확히 똑같은 이름(signupRequest)으로 받아야 한다. 

 

th:field="*{email}" 등의 input으로 받게 되는 값은 signupRequest에 있는 field값과 정확히 일치시켜야 한다.

 

th:if="${#fields.hasErrors('email')}"는 Controller에서 BindingResult에 의해 에러가 잡힐 경우 발생

 

th:object의 이름과 th:field의 이름이 같으면 ambiguous 문제가 발생하므로 다르게 할 것

 

<form class="user" method="post" th:action="@{/auth/signup}" th:object="${signupRequest}">
    <div class="form-group row">
    </div>
    <div class="form-group">
        <input th:field="*{email}" type="email" class="form-control form-control-user"
               id="InputEmail"
               placeholder="이메일 주소" required autofocus>
        <div class="text-center" th:if="${#fields.hasErrors('email')}">
            <hr>
            <p class="alert alert-danger" role="alert"
               th:errors="*{email}">이메일 주소가 올바르지 않습니다.</p>
        </div>
    </div>
    <hr>
    <div class="form-group row">
        <div class="col-sm-6 mb-3 mb-sm-0">
            <input th:field="*{password}" type="password"
                   class="form-control form-control-user"
                   id="InputPassword" placeholder="비밀번호">
        </div>
        <div class="col-sm-6">
            <input th:field="*{repeatPassword}" type="password"
                   class="form-control form-control-user"
                   id="RepeatPassword" placeholder="비밀번호 재입력"
                   pattern="^[A-Za-z[0-9]]{10,20}$" required>
        </div>
    </div>

    <div class="text-center" th:if="${#fields.hasErrors('password')}">
        <hr>
        <p class="alert alert-danger" role="alert"
           th:errors="*{password}">비밀번호가 올바르지 않습니다.</p>
    </div>
    <hr>
    <button type="submit" class="btn btn-primary">회원가입</button>
</form>

 

Controller에서 form 받는 방법

 

Validation을 Controller에서 직접 할 수도 있으나 길어질 경우 전체 로직에 방해되므로 따로 작성.

Validation을 할 경우 그 객체 바로 뒤에 BindingResult를 꼭 붙일 것

BindingResult에 의해 에러가 반송됨

@PostMapping("/signup")
public String signupAction(@Valid SignupRequest signupRequest, BindingResult result) {
    userValidator.validate(signupRequest, result);

    if (result.hasErrors()) {
        return "auth/signup";
    }

    authService.signup(signupRequest);

    return "redirect:/auth/signup-done";
}

 

errors.rejectValue에서 첫번째 값으로 어떤 필드에서 에러가 발생했는지 명시

두번째 값은 코드(표기되지않음)

세번째 값이 실제로 렌더링되는 메시지

 

th:if="${#fields.hasErrors('email')}"에 실제로 표시되는건 세번째 파라미터

@Component
@RequiredArgsConstructor
public class UserValidator implements Validator {

    private final UserRepository userRepository;

    @Override
    public boolean supports(final Class<?> clazz) {
        return SignupRequest.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(final Object target, final Errors errors) {
        SignupRequest signupRequest = (SignupRequest) target;

        if (!ObjectUtils.isEmpty(signupRequest.getEmail()) && userRepository.findByEmail(signupRequest.getEmail()).isPresent()) {
            errors.rejectValue("email", "DuplicatedEmail", "이미 해당 이메일로 가입이 되어있습니다.");
        }

        if (!signupRequest.getPassword().equals(signupRequest.getRepeatPassword())) {
            errors.rejectValue("password", "PasswordNotMatch", "비밀번호가 같지 않습니다.");
            errors.rejectValue("repeatPassword", "PasswordNotMatch", "비밀번호가 같지 않습니다.");
        }
    }
}

 


타임리프 시큐리티

 

꼭 붙여야 하는 태그

<html xmlns:sec="http://www.w3.org/1999/xhtml">

 

어떠한 형태로든 인가가 되었는지 안 되었는지 확인.인가 여부에 따라서 해당 tag 내의 모든 내용이 전부 보이거나 보이지 않음!를 붙일 수 있다

<div sec:authorize="isAuthenticated()">

<div sec:authorize="!isAuthenticated()">

 

부여된 Role에 따라 보이게 될 컨텐츠를 구분해야 할 경우 사용

hasAnyRole처럼 사용하여 여러 Role에 부여할 수도 있다.

(하지만 스프링 시큐리티 Hierachy 설정으로 어느 정도 커버 가능)

<div sec:authorize="hasRole('ADMIN')">

<div sec:authorize="hasAnyRole('USER', 'ADMIN')">

 

현재 세션에 부여된 Role을 보여준다. EX) [ROLE_USER]

<div class="text-white" sec:authentication="principal.authorities"></div>

 


타임리프 기본 제공 편의 기능

 

list가 비어있을 경우에 대한 처리 가능

cartList를 빼고는 전부 정해진 문법이다.

<div class="container" th:if="${#lists.isEmpty(cartList)}">
    <h3>장바구니가 비어있습니다</h3>
</div>

 

렌더링 될 값이 숫자일때 가격의 형태처럼 표기할 수 있다.

DEFAULT가 가능한 부분은 DEFAULT로 쓰는 것이 좋으며(LOCALE에 따라 알아서 변경)

첫번째 인자에 들어가는 부분이 ${} 형태가 아닌 것에 주의

한국에서 값을 표기하는 용도로 사용한다면 뒤 4개의 인자는 고정시켜도 무방

th:text="${#numbers.formatDecimal(item.price, 0, 'DEFAULT', 0, 'DEFAULT')}">

//곱하기도 가능
th:text="${#numbers.formatDecimal(item.price * cart.quantity, 0, 'DEFAULT', 0, 'DEFAULT')}">

 

session에 들어있는 attribute를 가져올 수 있다

미리 attribute를 넣어야 하는 것은 당연하며 여기서 건들 부분은 'password' 부분뿐이다.

th:value="${#session.getAttribute('password')}"

 

nullsafe하게 값을 처리하는 방법. nullable한 값을 받고 없을시 ''를 넣어준다.

getAddress()에도 ?를 붙여 nullable을 막을 수 있음

th:value="${#objects.nullSafe(user.getAddress()?.getExtraAddress(),'')}"

기본 문법

 

th:block은 타임리프에만 존재하는 몇 안 되는 문법으로 가상의 tag를 나타내준다

th:each는 for문을 돌때 사용(문법이 좀 헷갈릴 수 있으니 주의)

th:with는 일종의 변수로 cart.item.name의 형태가 아닌 item.name의 형태로 나타낼 수 있게 해준다.

객체 안의 객체나 Embeddable을 사용할 때 유용

<th:block th:each="cart : ${cartList}">
<div class="card rounded-3 mb-2" th:with="item = ${cart.item}">

 

기본적인 값을 꺼내는 형태

<div class="card-body p-4" th:id="${item.id}">
<p class="lead fw-normal mb-2" th:text="${item.name}">이름</p>

//input에 들어갈 value를 미리 렌더링 해놓을 수도 있다
<input name="quantity" th:value="${cart.quantity}"/>

 

링크와 값을 연결하는 형태

<a th:href="@{'/item/details/' + ${item.id}}"

2022.05.24 추가: 위 방법은 야매이므로 주의할 것!

 

객체의 필드에 직접 접근해서 값을 넘기는 item.id의 형태는 위처럼 사용해도 무방하나

response.getId()와 같이 메서드로 접근하고자 하면 반드시 아래와 같은 형태로 보내야한다!

<a th:href="@{/item/details/{id}(id = ${response.getId()})}">

 

 

id값을 각각 다르게 렌더링할 수도 있다

실제 렌더링 된 HTML에 id="eachPrice5"와 같은 형태로 표기됨

JSON 등을 이용한 fetch를 할 때 유용

<h5 class="mb-0" th:id="'eachPrice' + ${item.id}"

 

JSON에 타임리프로 렌더링 된 값을 변수로 넣을 수도 있다

아주 복잡한 형태이므로 조심히 복사해서 가져다 쓸 것 (이게 그나마 심플한 버전이다)

<a th:onclick="|updateItem('${item.id}');|">

 

어떤 조건에 따라 값을 다르게 표기해야 할 때 th:if와 th:unless 사용

좀 귀찮지만 거의 똑같은 코드를 2번 적어야 한다. 더 좋은 방법을 아직 찾지 못함

<th:block th:if="${total} < 30000" th:text="'3,000'"></th:block>
    <th:block th:unless="${total} < 30000" th:text="'0'"></th:block>

 

${param.error}에서 error빼고는 기본적인 형태

URL에 ?error라고 표기되는 경우를 의미하며 

파라미터에 에러가 없다면(unless) autofocus 즉 맨 처음 로그인을 시도할때

파라미터에 에러가 있다면(if) 시도했던 로그인정보에서 username을 받아와 넣고

autofocus는 비밀번호로 갈 수 있게 설정

 

코드는 지저분해지지만 유저 만족도는 올라갈 수 있는 확실한 방법같다

th:autofocus도 있는걸 확인해서 좀 더 공부 필요

<input type="email" th:if="${param.error}" th:value="${username}">
<input type="email" th:unless="${param.error}" th:value="${username}" autofocus>

<input type="password" th:if="${param.error}">
<input type="password" th:unless="${param.error}" autofocus>

 

자바스크립트에서 타임리프를 받는 형태

첫번째 형태처럼 받아야만 하는줄 알았으나 최근에 밑에처럼 받는 방법을 알게 됨!

HTML 내에서는 인라인 형태가 좋으나 자바스크립트에 값을 넘겨야 한다면 두번째 방법이 더 좋다고 생각

<script>
    function deleteItem(id) {
        const quantity = document.getElementById('quantity' + id).value;
        const totalPrice = '[[${total}]]';

 

text는 이와 같은 형태로 작성할 수도 있다. 문법에 주의

<span th:text="${user.getEmail() + ' 님의 회원탈퇴를 진행합니다'}"></span>

 

user?를 통해 nullable한 값에 의한 템플릿 파싱 에러를 방지할 수 있다 

개사기급..남발은 좋지 않지만 ?는 어느 정도 남발해도 되지 않을까 생각한다

<input th:value="${user?.getEmail()}">

 

이와 같은 형태도 가능하다

밑의 코드는 Embeddable이라 좀 복잡해지는데 스프링에서는 자동완성을 해주니 

컨트롤러나 서비스 레이어에서 쭉 작성하고 복붙해오는것 추천

<div th:if="${user.getAgree()?.getIsMandatoryAgree() == null}">

 

최댓값을 서버에서 받아와서 지정 가능

너무 당연하지만 저 값 이상을 넘길 수 없다

<input th:max="${response.stock}" />

 


공부할 때는 쌩고생해가면서 공부했는데 막상 다 모아놓고 보니 별거 없다

공부하면서 타임리프가 이런 것도 된다고? 싶었던게 참 많았다 근데 가장 중요한건 서버사이드에서 값을 잘 내려주는게 베스트다

 

예를 들어 물건들의 총 가격을 계산할때 타임리프의 문법을 이용해도 되지만

그냥 서버에서 계산해서 변수 하나 더 addAttribute하면 된다 (타임리프 문법으로 계산하는거 드럽게 복잡하다)

템플릿 엔진, AJAX, 서버사이드를 적절한 곳에 적절하게 잘 사용하는 것이 가장 중요한 것같다!

 

'etc > CHEAT SHEET' 카테고리의 다른 글

타임리프 기본 설정  (0) 2022.05.23
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함