본문 바로가기

강의/실전! 스프링 부트와 JPA 활용1

회원 등록

 

 


1. 컨트롤러

url로 타고 들어오면 view page를 뿌려주기 위한 controller 를 생성한다. (GET)

 

@Controller
@RequiredArgsConstructor
public class MemberController {
    private final MemberService memberService;

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

@Getter @Setter
public class MemberForm {
    String name;

    String city;
    String street;
    Long zipcode;
}

GetMapping 파트 설명
@GetMapping부터 보자.

“/members/new” url로 타고 들어오면 Spring에서 제공하는 Model을 사용한다.

그 model 안에 memberForm 빈껍데기를 담아준다. 

“/members/new”로 들어온 화면에서는 return members/createMemberForm을 통해 createMemberForm.html을 화면에 뿌려준다. 

빈껍데기를 담아준 model은 프론트쪽으로 보내지고, thymeleaf 기능을 이용하여 model안에 있는 정보를 이용하여 view를 구성한다.

 

 

 

회원 가입 페이지는 위 그림과 같이 생겼다.

submit를 눌렀을 때 사용자가 입력한 정보를 database에 저장하는 기능을 아래에서 구현해보자.

 

@Controller
@RequiredArgsConstructor
public class MemberController {
    private final MemberService memberService;

    @PostMapping("members/new")
    public String create(@Valid MemberForm form, BindingResult result){
        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:/";
    }
 }

 

PostMapping 파트 설명
프론트쪽에서 form 태그에 action=”/members/new” method = post라고 적혀있을 것이다.

백쪽에서 보낸 빈껍데기 memberForm을 프론트에서 채워서 action의 url로 다시 보내준다.

첫 파라미터로 MemberForm을 적어줬고 프론트쪽에서 보낸 MemberForm이 이 파라미터로 들어온다.

 

@validate : MemberForm에서 적어줬던 @NotEmpty 같은 애노테이션이 동작하게 해준다.

 

BindingResult : spring이 제공하는 기능이다.

만약 BindingResult 파라미터를 적어주지 않는다면 프론트에서 name값을 적지 않고 넘겨줬다면 MemberForm에서 @NotEmpty 애노테이션에 의해 오류가 발생해서 컨트롤러로 코드가 안넘어가고 팅긴다.

하지만 BindingResult를 파라미터로 넣어주면 오류가 발생하면 BindingResult에 오류가 담기고 컨트롤러 코드가 실행된다.

따라서 이 오류를 이용한 코드를 만들 수 있다. if문을 통해 오류가 담겨있다면 createMemberForm.html을 화면에 다시 뿌려주도록 했다. 이때 spring이 BindingResult를 화면까지 끌고 가서 프론트쪽에서 사용할 수 있도록 해준다.

@NotEmpty에 메시지를 넣어줬었는데 BindingResult는 이 오류가 담겨있으니 프론트에서 코딩으로 이 메세지를 사용할 수 있다.


2. 서비스

Controller와 Repository 사이를 연결해주는 단계

일반적으로 logic 구현을 하는 곳이다.

 

@Service
@Transactional(readOnly = true)
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    // 회원 가입
    @Transactional
    public Long join(Member member){
        validateDuplicateMember(member);
        memberRepository.save(member);
        return member.getId();
    }

    public void validateDuplicateMember(Member member){
        List<Member> findMembers = memberRepository.findByName(member.getName());
        if (!findMembers.isEmpty()){
            throw new IllegalStateException("이미 존재하는 회원입니다.");
        }

    }
}
  • @AllArgsConstructor : 모든 필드를 가지고 생성자를 만들어줌
  • @RequiredArgsConstructor : final 필드만 가지고 생성자를 만들어줌
  • @NoArgsConstructor : 파라미터가 없는 기본 생성자를 만들어줌
  • @NoArgsConstructor(access = AccessLevel.PROTECTED) : 속성으로 접근제한자 지정 가능

눈에 보이진 않지만 애노테이션으로 인해 컴파일 시점에 생성자가 만들어진다.

생성자가 1개일 때는 Autowired를 생략해도 되므로 Autowired가 없어서 자동으로 주입된다.

 

EntityManager은 @PersistenceContext를 붙여주면 자동으로 주입된다.

 

Transactional

보통은 @Transactional 옵션에 readOnly 옵션을 붙이고 데이터가 변경되는 부분에 따로 @Transactional을 붙여서 코딩한다.

그런데 만약 클래스의 메서드들 대부분 커맨드성이여서 거의 쓰기만 한다면 전체를 @Transactional로 해주고 조회부분만 @Transactional(readOnly=true)로 해주면 된다.

readOnly옵션은 false가 기본값인데 true 주면 flush를 생략해서 데이터를 조회하는 부분에 있어서 약간의 성능 향상이 가능하다.
@Transactional 애노테이션을 Service에 달던가 Repository에 달던가 어디에 달든 상관없다.

하지만 롤백을 생각해보면 비즈니스 로직이 있는 곳에서 트랜잭션을 시작하는 것이 좋으므로 비즈니스 로직이 있는 service에 트랜잭션을 붙여주는게 좋다.

참고로 @Transactional를 test에서 사용하게 되면 데이터가 DB에 들어가지 않고 롤백된다.

 


3. 리포지토리

리포지토리는 데이터베이스에 정보를 저장하거나 조회하는 기능으로 구성된다.

 

@Repository
public class MemberRepository {
    @PersistenceContext
    private EntityManager em;

    public MemberRepository(EntityManager em) {
        this.em = em;
    }

    public void save(Member member){
        em.persist(member);
    }
}

em.persist(entity) 로 영속성 컨텍스트에 저장.

 


4. 결과 확인

이름, 도시, 거리, 우편번호를 각각 입력한 후 submit 버튼을 누른다.

submit 버튼을 누르게 되면 입력한 정보들이 MemberController에 존재하는 PostMapping("members/new") 부분이 동작하게 된다.

이 후 MemberService , MemberRepository를 거쳐 Database에 저장되는 것을 아래에서 확인할 수 있다.

 

 

 

참고

인프런 김영한님의 ‘실전! 스프링 부트와 JPA 활용1’ .

'강의 > 실전! 스프링 부트와 JPA 활용1' 카테고리의 다른 글

회원 목록 조회  (0) 2021.07.07
h2 database 초기 실행  (0) 2021.06.21
프로젝트 생성  (0) 2021.06.21