ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 웹 계층 개발 - 회원 등록, 목록 조회
    JPA/JPA 활용 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로 회원가입 한 후 회원목록 조회

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

Designed by Tistory.