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 |