
CRUD Operation의 마지막, Delete 입니다.
Controller 의 deleteMemer method를 변경합니다. memberService 의 deleteMember method 의 결과를 item 에 담아 ItemResponse 를 리턴하게 합니다.
@DeleteMapping(value = "/member/{memberId}"
, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ItemResponse<Long>> deleteMember(
@PathVariable("memberId") String memberId) {
return ResponseEntity.ok()
.body(ItemResponse.<Long>builder()
.status(messageConfig.getCode(NormalCode.MODIFY_SUCCESS))
.message(messageConfig.getMessage(NormalCode.MODIFY_SUCCESS))
.item(memberService.deleteMember(memberId))
.build());
}
@Override
public Long deleteMember(String memberId) {
Member memberEntity = memberRepository.findById(memberId)
.orElseThrow(() -> new EntityNotFoundException(
"삭제할 회원 ID 가 존재하지 않습니다. -> " + parameter.memberId())
);
memberRepository.delete(memberEntity);
return 1L;
}
삭제할 Id 체크 후 memberRepository 의 delete method 를 호출합니다. @Transactional 은 interface에 있으니 생략하고 delete 요청을 합니다.

정상적으로 응답이 왔고, 데이터도 삭제되었습니다.
SimpleJpaRepository 의 delete method 코드를 보겠습니다.
@Override
@Transactional
@SuppressWarnings("unchecked")
public void delete(T entity) {
Assert.notNull(entity, "Entity must not be null");
if (entityInformation.isNew(entity)) {
return;
}
Class<?> type = ProxyUtils.getUserClass(entity);
T existing = (T) entityManager.find(type, entityInformation.getId(entity));
// if the entity to be deleted doesn't exist, delete is a NOOP
if (existing == null) {
return;
}
entityManager.remove(entityManager.contains(entity) ? entity : entityManager.merge(entity));
}
null 체크를 하고,
Assert.notNull(entity, "Entity must not be null");
isNew 메소드를 호출하여 신규 entity인 경우 method를 중지합니다. isNew method는 이전에 설명 했으니 참고 하세요.
Class<?> type = ProxyUtils.getUserClass(entity);
proxy 객체가 사용됐을 수 도 있기 때문에 ProxyUtils 의 getUserClass 메소드를 호출하여 실제 entity class type 을 가져옵니다.
T existing = (T) entityManager.find(type, entityInformation.getId(entity));
추출한 entity 의 class type 과 id 로 영속성 컨텍스트에서 객체를 조회합니다. 만약 영속성 컨텍스트에 entity 가 없는 경우 Database 에서 조회하여 있는 경우 영속성 컨텍스트에 추가하고 그 entity 를 반환합니다.
if (existing == null) {
return;
}
영속성 컨텍스트와 Database에 존재하지 않는 경우 method 를 중지합니다.
entityManager.remove(entityManager.contains(entity)
? entity
: entityManager.merge(entity)
);
contains 를 통해 entity 가 영속성 컨텍스트에 포함되었는지 확인하고, 있다면 entity 를, 없다면 merge method 를 실행하여 entity 를 영속 상태로 만든 후에 remove method로 전달합니다.
❓entityManager.find() 로 entity 를 영속 상태로 만드는데, 왜 contains 로 또 영속성 컨텍스트에 포함되어 있는지 확인하는 걸까요?
예를들어 아래와 같은 두가지 경우가 있을 수 있습니다. 위의 삭제 코드와 같이 findById method를 통해 영속성 엔티티에서 Entity를 조회하여 전달한 경우, 아래와 같이 Entity를 생성하여 전달하는 경우. 예시를 위해 BaseEntity 에 @Setter 를 추가하고 isNew method 결과가 true 가 되도록 createDate 를 set 해줍니다.
Member memberEntity = new Member(memberId, null, null, null, null);
memberEntity.setCreateDate(LocalDateTime.now());
memberRepository.delete(memberEntity);
이 경우 delete로 전달한 memberEntity 는 비영속 상태 입니다. 하지만 같은 키의 데이터는 있기 때문에 entityManager.find 의 결과는 null 이 아닙니다. 그래서 remove method 까지 호출되지만 entity 가 비영속 상태이기 때문에 entityManager.contains 의 결과는 false 가 되고, 결국 merge method 가 실행되어 영속화 후에 삭제되게 되는 것이죠. entityManager.find() 는 키가 있는지 체크하고, contains 로는 영속화 객체인지 체크 하는 것 입니다.
@Override
@Transactional
public void deleteById(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
findById(id).ifPresent(this::delete);
}
findById 코드를 보면 알 수 있듯이 결국 delete method 를 호출합니다. null 처리도 해주고, findById 도 해주니 deleteById method 를 쓰면 코드가 좀 더 깔끔해 질순 있지만 findById method 에서 throw 하는 Exception 과 message 를 그래도 써야 하죠. 처음에 작성한 코드와 같이 Exception 과 message 를 원하는 데로 커스텀 할 수 없습니다. 팀의 룰에 맞게, 로직에 맞게 활용하시면 됩니다.😉