이전 게시글에서 스프링 빈들의 의존관계를 설정하고, MemberController의 뼈대까지 만들어보았다. 이 게시글에서는 홈화면, 회원 등록과 조회를 웹 MVC로 구현해 볼 것이다.
먼저 HomeController
를 만들자. @Controller
애노테이션을 달아주고, @GetMapping
을 통해 주소와 매핑시켜준다. 그리고 home
이라는 이름의 template을 리턴한다.
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
return "home";
}
}
HomeController가 home.html을 리턴하므로 view도 작성해주자. 주목할 것은 두가지의 a태그가 있는데, 하나는 회원가입 페이지로 이동하는 것이고 하나는 회원목록을 조회하는 페이지로 이동하는 하이퍼링크이다. 회원 가입 페이지는 /members/new
이고 회원 목록 조회 페이지는 /members
이다
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
<div>
<h1>Hello Spring</h1>
<p>회원 기능</p>
<p>
<a href="/members/new">회원 가입</a>
<a href="/members">회원 목록</a>
</p>
</div>
</div> <!-- /container -->
</body>
</html>
서버를 실행하고 localhost:8080에 접속해보면
이렇게 잘 작동한다
#참고 :
index.html
(welcome page)보다HomeController
가 우선순위가 높기 때문에HomeController
가 먼저 작동한다.
두번째로 회원 가입을 구현해 볼 것이다. 우선 회원가입 페이지를 띄워야 하니 MemberController
에 @GetMapping
을 통해 주소와 매핑시켜주자. 그리고 templates/members/createMemberForm
을 띄워주자.
@GetMapping("/members/new")
public String createForm() {
return "members/createMemberForm";
}
createMemberForm.html
을 작성하자.
form
태그로 이름을 입력받으면 된다. /members/new
로 데이터를 보낼것이기 때문에action="/members/new
이고, 입력을 받아오기 때문에 method="post"
이다.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
<form action="/members/new" method="post">
<div class="form-group">
<label for="name">이름</label>
<input type="text" id="name" name="name" placeholder="이름을
입력하세요">
</div>
<button type="submit">등록</button>
</form>
</div> <!-- /container -->
</body>
</html>
서버를 실행해보면
이런식으로 나타난다. 그런데 지금 이름을 적고 등록을 누르면 에러가 난다. 이름을 받아와 등록버튼을 누르면 어떻게 할지에 대한 Controller를 작성해주지 않았기 때문이다.
form태그에서 이름을 받아 저장할 MemberForm
클래스를 작성하자.(MemberContoroller
에서만 사용할 것이기 때문에 Controller
패키지 안에 작성한다) name
이라는 private변수를 선언하고, 게터와 세터를 작성한다.
public class MemberForm {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
그리고 MemberController
에 다음과 같이 작성한다.
@PostMapping("members/new")
public String create(MemberForm form) {
Member member = new Member();
member.setName(form.getName());
memberService.join(member);
return "redirect:/";
}
이번에는 @GetMapping
이 아니라 @PostMapping
이다. 폼태그에서 name을 받아오기 때문이다. 폼태그에서 받아온 name을 저장한 객체인 MemberForm
을 매개변수로 받아오고, 새 멤버 인스턴스를 생성한다. 그리고 새 멤버 인스턴스의 setName
과 form.getName()
을 통해 이름을 저장한다.
그런데 나는 여기서 이런 의문이 생겼다
Q.
form.setName
으로 폼태그에서 받은 name을 설정해주지 않았는데form.getName()
실행이 가능한건가?
A. 스프링 컨트롤러에서 해당 컨트롤러 메서드가 호출되기 직전에
form.setName()
이 자동으로 실행되어 폼태그로 받은 name을 저장해준다. (스프링 MVC의 기능! 자바빈 프로퍼티 규약)
마지막으로 memberService.join(member);
에서 새 멤버의 정보가 회원 리스트로 등록되게 한다. 리턴값인 redirect:/
는 홈 화면으로 이동하게 한다.
MemberController
에 메소드를 작성한다.
@GetMapping("/members")
public String list (Model model) {
List<Member> members = memberService.findMembers();
model.addAttribute("members",members);
return "members/memberList";
}
Model
객체는 Controller
에서 생성된 데이터를 담아서 View
로 전달할 때 사용하는 객체이다. addAttribute("키", "값")
메소드를 사용하여 전달할 데이터 세팅한다.
출처: https://lopicit.tistory.com/224 [로픽의 IT블로그]
개발자가 직접 model을 생성할 필요 없이 파라미터로 선언만 해주면 스프링이 알아서 만들어준다.
출처: https://hoyhouse.tistory.com/19 [hoyhouse]
그럼이제 templates/members/memberList
를 작성하자.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
<div>
<table>
<thead>
<tr>
<th>#</th> //table heading
<th>이름</th>
</tr>
</thead>
<tbody>
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
</tr>
</tbody>
</table>
</div>
</div> <!-- /container -->
</body>
</html>
여기서 주목해야할 부분은
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
</tr>
이다.
${}
는 model안에 있는 값을 꺼낸다는 뜻이다.th:each
는 thymeleaf
문법으로, for each문과 같은 기능 (members에서 하나씩 꺼내 member에 담음)th:text
는 출력 기능member.id
와 member.name
을 호출하면 자동으로 member.getXx
메소드가 호출되어 값을 가져온다 (자바 프로퍼티 방식의 접근)