https://rookie-programmer.tistory.com/211
이전 포스팅에서 Create, Read 기능과 Transactional 처리를 끝내고
Update 기능과 Delete 기능을 추가해 CRUD를 완성했다.
이전 포스팅 하단에 이전 포스팅까지의 코드가 있다.
1. 삭제
(1) MemberRepository에 void delete(Member member) 추가
(2) Repository.delete(Member) 실행 시 객체 삭제
(3) 목록조회 화면 return
[ MemberRepository.java ]
MemberRepository에 delete() 메서드를 만들어주면, JPA가 내부적으로 사용하는 delete 기능을 좌라락 불러와 쓸 수 있다.
우리는 그저 메서드의 선언부만 작성해주면 됨. 아주아주 편리.
public interface MemberRepository {
List<Member> findAll();
Member save(Member member);
Optional<Member> findById(int id);
void delete(Member member);
}
사용할 메서드들의 정의되어 있는 MemberRepository 인터페이스를 SDJRepo에서 extends해주면 게임 끝
아래 코드처럼 Entity명과 해당 Entitiy의 PK 타입을 명시 <Class명, PK타입> 해주면 메서드를 구현하지 않아도
JPA가 런타임에 알잘딱 코드를 끼워넣어 원하는 기능을 대신 수행해준다. --> 이거시 "리플렉션"
@Repository
public interface SpringDataJpaMemberRepository extends MemberRepository, JpaRepository<Member, Integer> { //인터페이스 간의 관계는 extends.
}
[ member-find-screen.html ]
회원 상세조회 HTML에 추가해 준 "삭제" 버튼을 누르면, 해당 회원의 id 값이 담긴 url - member/delete?id=xx이 호출된다.
<a th:href="@{/member/delete(id=${member.id})}"><button>삭제</button></a>
[ MemberController.java ]
url로 넘어오는 id를 @RequestParam으로 멋지게 받고, memberService에게 해당 회원을 지워달라고 요청한다.
이때, Service에서 던지는 예외가 있다면 적절히 처리해 주어야 고객 경험을 향상시키는 믓찐 백엔드 개발자가 될 수 있다.
사용자가! 잘못된 id 값을 넣거나 누르면 사용자 잘못이기 때문에 4xx 화면으로 가!
@GetMapping("member/delete")
public String deleteMember(@RequestParam("id") int id){
try{
memberService.deleteMember(id);
return "redirect:/members";
} catch (EntityNotFoundException e){
return "404-error-page";
}
}
[ MemberService.java ]
MemberService는 DB에서 id 값을 가지는 회원을 찾아 전해준다.
Repository의 findById()는 리턴 타입이 Optional<Member>이기 때문에,
id 값을 가지는 회원이 존재하지 않으면 Optional.empty()가 넘어올 가능성이 있어 .orElseThrow로 예외를 가볍게 잡아준다.
public void deleteMember(int id) throws EntityNotFoundException {
Member member = memberRepository.findById(id).orElseThrow(EntityNotFoundException::new);
memberRepository.delete(member);
}
➡️ 삭제 버튼을 누르게 되면 DB에서 값이 삭제되고, 값이 잘 삭제된다면 회원 목록조회로 redirect된다.
2. 수정
(1) findById(int id)를 통해 객체 조회
(2) 조회된 객체에 update 화면에서 넘겨받은 변경값 set 후 save
(3) 상세조회 화면 return
[ member-find-screen.html ]
회원 상세조회에서 "삭제" 버튼 옆에 "수정" 버튼을 추가했다.
버튼을 onclick하면 <script>에서 구현한 showUpdateForm() 메서드가 호출된다.
script의 'block'은 update-form 전체가 사용자 눈에 "보이게" 바꿔주는 기능이다. ('none'은 안보이게 바꾸는 설정이다)
<script>
function showUpdateForm(){
document.getElementById("update-form").style.display = 'block';
}
</script>
<button onclick="showUpdateForm()">수정</button>
<div id="update-form">
<form action="/member/update" method="POST">
<p><label>id</label><input type="text" name="id" th:value="${member.id}" readonly></p>
<p><label>이름</label><input type="text" name="name" th:value="${member.name}"></p>
<p><label>email</label><input type="text" name="email" th:value="${member.email}" readonly></p>
<p><label>password</label><input type="password" name="password" th:value="${member.password}"></p>
<input type="submit" value="수정완료">
</form>
</div>
readOnly 옵션을 id와 email에 설정해서 사용자는 자신의 이름과 비밀번호 정보만을 수정할 수 있게 만들었다.
타임리프 th:value=${} 문법을 통해 기존 가입된 사용자의 정보를 미리 띄워주게 만들어, 사용자 편의가 쪼꼼 상승했다.
[ MemberController.java ]
사용자가 "수정완료" 버튼을 누르자마자 POST요청으로 member/update 화면으로 보내버린다.
바로~ Controller에서 @PostMapping으로 url로 들어오는 MemberRequestDto를 받는다.
이때 MemberRequestDto에 @Data 어노테이션 > @Setter를 통해 자동으로 데이터 바인딩되는 것이다.
(JSON 타입 요청이 아니니 @RequestBody 붙이면 안된다...... 이르케 하나 배워가는거지 모...)
어쨌든 사용자가 수정한 값으로 들어온 MemberRequestDto 객체를 MemberService에 넘겨 Update해달라고 요청한다.
여기도 Service에서 던지는 예외가 있다면 적절히 처리해 주자.
@PostMapping("member/update")
public String updateMember(MemberRequestDto memberRequestDto, Model model){
try{
memberService.updateMember(memberRequestDto);
model.addAttribute("member", memberRequestDto);
return "redirect:/member/find?id=" + memberRequestDto.getId();
}
catch (EntityNotFoundException e){
return "404-error-page";
}
}
[ MemberService.java ]
이름과 비밀번호가 바뀐 MemberRequestDto 객체를 받아 처리.
DB에 해당 id 값을 가지는 데이터가 있어야 Update를 할 수 있기 때문에 Repository.findById()해준다.
만약 이때 DB에 값이 없다면 .orElseThrow()에 걸려 뒤에 로직이 실행되지 않고 Controller의 404 에러 페이지로 들어간다.
정상 리턴된다면 member에 바뀐 값들을 넣어주고 save()해주면 된다.
DB에 이미 id 값이 있는데, 그 데이터의 속성 값을 변경해줄 때 save()를 쓰면 다른 값은 유지되고 바뀐 값만 update 된다.
public void updateMember(MemberRequestDto memberRequestDto) throws EntityNotFoundException {
Member member = memberRepository.findById(memberRequestDto.getId()).orElseThrow(EntityNotFoundException::new);
member.setName(memberRequestDto.getName());
member.setPassword(memberRequestDto.getPassword());
memberRepository.save(member);
}
➡️ 값이 잘 수정된다면 해당 회원의 상세조회 페이지로 redirect된다.
여기서 편하고 간지나는 코드를 배웠다.
다시 id 값을 찾아올 필요 없이 RequestDto의 id 붙여서 넘겨버리기.
웹 브라우저에 return -> 리다이렉션은 웹 브라우저가 수행하는거지 스프링이 하는게 아니다
302 코드를 주면서 리다이렉션을 명시하는 것이다.
location header 태그에 localhost:8080//member/find?id=1
return "redirect:/member/find?id=" + memberRequestDto.getId();
[ MemberService.java ]
public void updateMember(MemberRequestDto memberRequestDto) throws EntityNotFoundException {
Member member = memberRepository.findById(memberRequestDto.getId()).orElseThrow(EntityNotFoundException::new);
//set 방식보다는 update 함수를 생성하는 방식을 주로 사용함
// member.setName(memberRequestDto.getName());
// member.setPassword(memberRequestDto.getPassword());
member.updateMember(memberRequestDto.getName(), memberRequestDto.getPassword());
memberRepository.save(member);
}
[ Member.java ]
public void updateMember(String name, String password){
this.name = name;
this.password = password;
}
위 updateMember 함수에서 EntityNotFoundException에러(Checked Exception)터지면 Runtime Exception으로 변환해서 던져준다. Runtime Exception은 UnChecked Exception이기 때문에 transaction을 타 예외 발생 시 rollback이 되는 것이다.
'Back-End 공부 > Spring' 카테고리의 다른 글
스프링 RestFul - 기본 CRUD 만들기 프로젝트 (0) | 2024.01.18 |
---|---|
스프링 HTML 화면없이 PostMan으로 테스트하기 (0) | 2024.01.18 |
스프링 DB 연결 및 Transactional 처리 (0) | 2024.01.16 |
스프링 용어 쉽게 정리하기 (빈, 싱글톤, DI, 제어의 역전, IoC 컨테이너) (0) | 2024.01.16 |
Controller, Service, Repository에 Dto 객체 추가해 실습하기 (1) | 2024.01.15 |