JPA/JPA 활용

웹 계층 개발 - 회원 등록, 목록 조회

chanhee01 2023. 7. 11. 18:11

home 화면

위의 사진은 강의를 들으면서 개발할 기능들이다. 회원가입을 하고 상품 등록을 하고 주문을 할 수 있는 웹 애플리케이션을 만들 것이다. 이번 포스팅에서는 회원가입을 할 수 있는 회원가입 폼을 만들 것이다.

 

 

html과 css 코드가 있긴 한데 스프링과 스프링부트, JPA를 공부하는 과정이기 때문에 프론트엔드 개발쪽은 직접 코드를 작성하지 않고 복사, 붙여넣기를 사용했다.

 

 

 

MemberController

@Controller
@RequiredArgsConstructor
public class MemberController {

    private final MemberService memberService;

    @GetMapping("/members/new")
    public String createForm(Model model) {
        model.addAttribute("memberForm", new MemberForm());
        return "members/createMemberForm";
    }

홈 화면에서 회원 가입 버튼을 누르면 http://localhost:8080/members/new로 이동하는 것으로 html을 작성해놨다. GetMapping을 통해 /members/new로 이동하고, 모델을 통해 model.addAttribute로 html에 MemberForm 객체를 넘겨주었다. return을 통해 members/createMemberForm으로 연결시켜주면 resource에 있는 templates에서 createMemberForm.html을 찾아서 해당 html을 연결시켜준다.

 

 

 

 

creatMemberForm.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<style>
 .fieldError {
 border-color: #bd2130;
 }
</style>
<body>
<div class="container">
 <div th:replace="fragments/bodyHeader :: bodyHeader"/>
 <form role="form" action="/members/new" th:object="${memberForm}" method="post">
 <div class="form-group">
 <label th:for="name">이름</label>
 <input type="text" th:field="*{name}" class="form-control" placeholder="이름을 입력하세요"
 th:class="${#fields.hasErrors('name')}? 'form-control fieldError' : 'form-control'">
 <p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect date</p>
 </div>
 <div class="form-group">
 <label th:for="city">도시</label>
 <input type="text" th:field="*{city}" class="form-control" placeholder="도시를 입력하세요">
 </div>
 <div class="form-group">
 <label th:for="street">거리</label>
 <input type="text" th:field="*{street}" class="form-control" placeholder="거리를 입력하세요">
 </div>
 <div class="form-group">
 <label th:for="zipcode">우편번호</label>
 <input type="text" th:field="*{zipcode}" class="form-control" placeholder="우편번호를 입력하세요">
 </div>
 <button type="submit" class="btn btn-primary">Submit</button>
 </form>
 <br/>
 <div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>

createMemberForm이다. 내가 직접 짠 코드는 아니지만 간단히 요약을 하자면 타임 리프를 사용했고, th:object="${memberForm}"으로 memberForm의 객체에 th:field=*{city} 등으로 사용자가 화면에서 입력하는 것을 데이터에 넘길 수 있는 기능을 작동한다.

 

 

http://localhost:8080/members/new

회원가입 버튼을 눌렀을 때 나오는 화면이다. 이름, 도시, 거리, 우편번호를 th:filed로 받아온다.

 

 

 

 

 

PostMapping으로 회원가입 완료

@PostMapping("/members/new")
public String create(@Valid MemberForm form, BindingResult result) {

    // BindingResult는 valid에서 오류가 발생했을 때 오류를 가지고 동작을 해줄 수 있게 해줌
    // 스프링이 MemberForm에 있는 message를 출력해줌
    // 물론 thymeleaf로 html에서 에러 메세지 출력하게 해서 가능한거
    if (result.hasErrors()) {
        return "members/createMemberForm";
    }

    Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());

    Member member = new Member();
    member.setName(form.getName());
    member.setAddress(address);

    memberService.join(member);
    return "redirect:/";
}

아까는 @GetMapping으로 /members/new에 연결했다. 하지만 이번에는 @PostMapping으로 연결한다. @GetMapping은 Get방식으로 페이지를 보여주는 기능이었다. @PostMapping이란 사용자가 페이지에서 값을 입력하고 버튼을 눌렀을 때의 동작을 Mapping하는 것이다. /members/new라는 페이지에서 버튼을 누르기 때문에 @PostMapping("/members/new")로 PostMapping이 진행된다.

 

일단 if문을 뒤로하고 보면 member을 만든 다음 name과 address를 set으로 입력하고 Service 계층에서 만들었던 join을 통해 member를 저장해주면 된다. return "redirect:/"가 되어있는데 redirect는 다시 보낸다는 뜻이고 / 로 보내는 것이기 때문에 회원 가입을 하면 홈화면으로 돌려보내 준다는 뜻이다. 그냥 화면을 이동시켜주는 것이 아니라 리다이렉트하는 것이다.

 

지금까지 설명해준 것은 memberForm의 조건에 부합해서 정상적으로 예외없이 회원가입될 때의 절차이다.

 

 

 

 

 

MemberForm

@Getter @Setter
public class MemberForm {

    @NotEmpty(message = "회원 이름은 필수입니다")
    private String name;
    private String city;
    private String street;
    private String zipcode;
}

위의 코드는 MemberForm객체인데, 여기에 name위에 @NotEmpty라는 애노테이션이 붙었다. 이 뜻은 이름은 무조건 공백이 될 수 없다는 뜻이다. 만약 이름을 공백으로 둔 상태에서 submit 버튼을 누르게 된다면 에러를 발생해야한다.

 

 

 

코드를 보면 파라미터안에 @Valid와 BindingResult가 들어가있다. 우선 Valid는 MemberForm에 있는 @NotEmpty처럼 조건이 있을 때 조건에 부합하는지 검증하는 애노테이션이다. NotEmpty가 있는데 회원 이름을 공백으로 두고 submit을 하게 된다면 검증에서 걸리게 된다. BindingResult는 valid에서 검증에 실패했을 때 검증을 가지고 에러 메시지를 출력하게 해준다. MemberForm에 message가 있는데, createMemberForm에 있는

 

 th:class="${#fields.hasErrors('name')}? 'form-control fieldError' : 'form-control'">
 <p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect date</p>

 

에러 발생 코드로 인해서 MemberForm에 있는 메시지가 사용자에게 보여지는 것이다.

 

이름을 입력하지 않으면 메시지를 출력

 

 

 

 

회원 목록 조회

 

회원 가입을 한 회원들의 목록을 조회하는 기능을 만들 것이다.

@GetMapping("/members")
public String list(Model model) {
    List<Member> members = memberService.findMembers();
    model.addAttribute("members", members);
    return "members/memberList";
}

코드는 간단한데, MemberService에서 findMembers를 한 다음, 그 객체를 모델에 넘겨주면 된다.

Service 계층에서 findMembers는 JPA를 이용해 모든 member를 찾아오는 기능이었다.

 

여기서 member 엔티티를 그대로 모델에 넘겨주었는데 이렇게하면 복잡한 프로젝트에서는 엔티티가 복잡해지기 때문에 실무에서 잘 사용하지 않는다고 한다. 회원 가입때 했던 것처럼 MemberForm 같은 컨트롤러 전용 DTO를 만들어서 엔티티를 그대로 보내는 것이 아니라 따로 만든 DTO로 변환해서 화면에 꼭 필요한 데이터들만 모델에 넘겨야한다.

 

이름 : 이름, 도시 : 도시, 주소 : 거리, 우편번호 : 1111로 회원가입 한 후 회원목록 조회

첫 번째 회원을 가입시키고 회원 목록을 들어가보니 첫 번째 회원이 잘 나온 것을 확인할 수 있었다.