Mustache로 Updateform 구현하기

Mustache로 update form을 구현할 일이 구현하면서 정리해본다.

update form의 특징이라면, 사용자가 수정하기 위해서는 수정이 필요한 데이터를 먼저 보여주어야 한다. 그러려면 Model 데이터를 불러와서 input 박스의 value로 자동으로 채워넣어줘야 한다. 이 과정에서 mustache 문법이 어떻게 사용되는지를 알아보자.

Hexo 에서는 mustache의 { {id} }사용할 경우 화면에 출력되지 않아 이렇게 표기를 하게되었다..


update.html로 id값 전달하기

update form 페이지로 이동하기 위해서는 a 태그를 이용해서 페이지를 전달하는게 기본적이다.

개인정보 수정 기능을 하는 update form을 구현하기 위해서는 어떤 사용자의 정보를 수정하고자 하는지를 알아야 한다. 그래야 서버에서 사용자 정보를 가져와서 수정하고 다시 서버에 저장할 수 있기 때문이다. 이 때 사용되는게 id값이다. 그리고 이 id값을 전달받아야 repository에서도 사용자 정보를 꺼내어서 model을 이용해 화면에 사용자 정보를 전달할 수 있다.

그러려면 a태그의 href 속성값에 mustache 문법으로 id값을 전달해줘야 한다.

1
<a href="/user/{{id}}/update" class="btn btn-success" role="button">개인정보 수정하기</a>

그럼 이렇게 update form 페이지로 이동했을 때 사용자 정보를 불러와서 화면에 전달해서 보여준다.


/user{id}/update URI 맵핑 메서드 작성

a 태그를 통해 /user{id}/update URI를 처리해줄 메서드가 컨트롤러에서 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Controller
@RequestMapping("/user")
public class userController {

...

@GetMapping("/update/{id}")
public String update(@PathVariable Long id, Model model) {
model.addAttribute("users", userRepository.findById(id).get());
return "user/update";
}
}

userController의 경우 URI에 공통적으로 /user/ 가 사용되므로 @RequestMapping("/user") 어노테이션을 사용해서 리팩토링하였다.

update 메서드를 들여다보면..
화면에 데이터를 뿌려줄 model 객체에 데이터를 추가하고 있는데, 이 데이터는 전체 사용자 정보가 담긴 userRepository 에서 어노테이션 파라미터로 입력된 id 에 해당하는 사용자 정보를 찾아서(findById()) 가져오는(get()) 역할을 수행하고 있다.

1
model.addAttribute("users", userRepository.findById(id).get());

그리고 마지막으로 updatem.html을 return하고 있다.

컨트롤러(class)와 화면(html)에서의 특징이 하나 있다. 이것때문에 삽질을 좀 오래했었는데….
html페이지에서는 id값을 가져올 때 { {id} } 로 표기하지만, 컨트롤러에서 맵핑하는 URI에는 {id} 로 표기해야 정상적으로 작동한다.


html 페이지에 mustache 문법 작성

컨트롤러에서 가져온 사용자 정보가 담긴 model 을 가져오려면 mustache의 문법을 알아야 한다. mustache에서는 model을 가져와서 화면에 맵핑할 때 아래와 같은 형식을 사용한다.

1
2
3
4
5
{{#model}}
<form method="" action="">
...
</form>
{{/#model}}

여기에 User에 저장한 사용자 정보의 컬럼을 { {id} } 으로 감싸서 input 박스의 value로 넣어주면 된다.

필자의 유저 컬럼은 다음과 같이 지정되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class User {
@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
public String userEmail;
public String userName;
public String userPassword;

...
}

여기서 수정에 필요한 데이터 컬럼인 userEmail, userName, userPassword를 사용할 것이다.

이를 적용하면 수정하는 페이지(update.html)에서 작성해야할 코드는 다음과 같다. form 태그 안의 속성값(method, action)에 주목하자.

/user/update.html

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
32
33
34
35
{{#users}}
<form method="post" action="/user/{{id}}">
<!--이메일 주소 입력 칸-->
<div class="form-group">
<label for="userEmail">이메일</label>
<input type="email" class="form-control"
id="userEmail" name="userEmail"
value="{{userEmail}}">
</div>

<!--이름 입력 칸-->
<div class="form-group">
<label for="userName">이름</label>
<input class="form-control"
id="userName" name="userName"
value="{{userName}}">
</div>

<!--비밀번호 입력 칸-->
<div class="form-group">
<label for="userPassword">비밀번호</label>
<input type="password" class="form-control"
id="userPassword" name="userPassword" >
</div>
<!-- 비밀번호의 경우 value를 가져오기보다 -->
<!-- 새비밀번호를 덮어씌워서 바로 수정이 가능하도록 하자 -->

<!--수정완료 버튼-->
<button type="submit"
class="btn btn-success clearfix pull-right">
정보수정
</button>
<div class="clearfix" />
</form>
{{/users}}

model로 가져온 값을 보여줄때 사용하는 mustache 문법은 마찬가지로 value/user/{id} 로 감싸서 보여준다.

여기까지 작성하면 아래 이미지를 구현할 수 있다.


form 태그 안의 action은 해당 form이 실행해서 어떤 URI로 값을 전달하는지를 알려준다. 위의 코드는 /user/ { {id} } 로 값을 전달할 것이다. 따라서 해당 URI를 맵핑하는 컨트롤러를 작성해주어야 한다.

/user/{id} URI 맵핑하는 메서드 작성

지금까지 update.html 페이지를 작성하고, 여기에서 요구하는 URI를 처리하는 메서드(컨트롤러)를 만드는 방식으로 진행되고 있음을 알 수 있다.

/controller/userController.class

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
32
33
34
35
36
37
package com.devandy.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.devandy.domain.User;
import com.devandy.repository.UserRepository;

@Controller
@RequestMapping("/user")
public class userController {

@Autowired
private UserRepository userRepository;

...

@GetMapping("/{id}/update")
public String update(@PathVariable Long id, Model model) {
model.addAttribute("users", userRepository.findById(id).get());
return "user/update";
}

// 추가로 작성한 메서드
@PostMapping("/{id}")
public String update(@PathVariable Long id, User updatedUser) {
User user = userRepository.findById(id).get(); // 업데이트 전의 프로필
user.update(updatedUser); // User 정보 업데이트
userRepository.save(user); // 업데이트된 user정보 userRepository에 저장
return "redirect:/user/list";
}
}

아까 작성한 get요청을 처리하는 메서드 아래에 post요청을 처리하는 메서드를 작성했다. id값은 노출되도 상관이 없기 때문에 get요청으로 처리했지만, id값 외의 사용자 정보는 유출되면 안되므로 값을 노출하지 않고 전달할 수 있는 post요청으로 처리한다.

메서드 이름이 같아도 상관은없다. (진짜 상관이 없는건지는 모르겠으나 아직 문제를 발견하지 못했다.)

이번엔 User 객체의 인스턴스를 2개 생성해서 사용했다. 하나는 DB(repository)에서 가져온 사용자 정보, 즉 업데이트 전의 사용자 정보(user), 다른 하나는 화면에서 생성해서 전달받은 새로운 사용자 정보(updatedUser).

메서드의 작동과정은 다음과 같다.

URI를 통해 전달받은 id에 해당하는 사용자를 DB(repository)에서 찾아서

1
User user =  userRepository.findById(id).get();

아직 생성하지 않았지만, User 도메인의 update 메서드를 통해 파라미터로 전달받은 새로운 사용자정보로 수정한다.

1
user.update(updatedUser);

update된 사용자정보를 다시 DB(repository)에 저장한다.

1
userRepository.save(user);

그리고 원래의 페이지였던 list.html로 리다이렉트 보낸다.

이제 데이터를 불러와서 수정하는 일이 끝났다. 이렇게 id값 1번에 해당하는 사용자 정보중 devandy라는 이름을 andy로 수정할 수 있다.