Weekly I Learned (4월 5주차)

YEON·2022년 4월 25일
0

Weekly I Learned (2022)

목록 보기
2/15
그 주의 화요일, 금요일을 지정 날짜로 업데이트 합니다

1. JPA non Collection 객체와의 맵핑

일급 컬렉션을 통해 Java 단일 객체로 맵핑을 시켜주려했을시 오류가 발생하였다.

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private Answers answer;

@ManyToOne, @OneToMany, @CollectionOfElements annotation 의 경우 non Collection 객체와는 매핑될 수 없다.
(일급 컬렉션의 선언된 부분에 들어가서 맵핑을 해줘야 오류가 발생하지 않는다.)

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Answer> answer = new ArrayList<>();

발생한 에러 메시지
AnnotationException: Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements





2. JPA Embedded Type 사용

JPA의 엔티티 타입은 기본값, 임베디드, 컬렉션 타입으로 나눌 수 있다.
이때 임베디드 타입(Embedded Type)은 JPA에서 사용자가 직접 정의한 값 타입이다.

임베디드 타입 사용할 때의 장점
엔티티가 상세한 데이터를 그대로 가지고 있는 것은 응집력을 떨어트리고 객체지향적이지 않다.
때문에 유사한 상세한 데이터들을 묶어서 새로 정의한 값 타입으로 사용할 수 있고, 임베디드 타입을 사용함으로써 새로 정의한 값 타입들을 재사용할 수 있으며 엔티티의 응집도를 높일 수 있다. 다만, 공유 참조로 인해 버그가 발생할 수 있으므로 불변 객체로 설계해야한다.

또한, 임베디드 타입 덕분에 객체와 테이블을 세밀하게 매핑할 수 있다.
(잘 설계한 ORM 애플리케이션은 테이블 컬럼과 객체 필드가 1:1로 매핑되는 것이 아니라 매핑한 테이블의 수보다 클래스의 수가 더 많아지게 된다.)

@Entity
public class Member {
  @Id @GeneratedValue
  private Long id;
  @Column(name="name")
  private String name;
  @Embedded
  private Address address;
}


@Embeddable
public class Address {
  private String country;
  private String city;
}

객체 지향의 이상적인 설계 "응집도는 높이고, 결합도는 낮춰야한다"

핵심은 객체 설계시 객체가 여러 기능을 모두 한번에 짊어지게 하는 것보다 객체 고유의 기능이 무엇인지 생각해보는 것이다. 객체의 역할책임을 맞게 잘 분배하였는지를 생각하면 된다. 또한 결합에 있어서는 객체간 협력에 필요한 적절한 수준의 관계만을 유지하는 것이 중요하다.





3. 이펙티브 자바의 예외처리 가이드

  • 예외는 예외 상황에서만 써야한다.
  • 표준 예외의 사용을 선호하는 것이 좋다.
    표준 예외를 재사용함으로써 예외 클래스의 수가 적어 메모리 사용량과 클래스 적재 시간도 줄어들며 다른 사람들도 이해하기 쉽다. (ex. null이 금지된 곳에서 IllegalArgumentException 대신 NullPointerException Throw)
  • 예외를 적절하게 추상화하여야 한다.
    상위계층에서는 하위계층에서 발생하는 예외를 받아서 상위계층 추상화 수준에 맞는 예외로 바꿔서(예외번역) 던져야 한다.
  • 예외가 발생하여도 객체는 호출 전 상태를 유지하는 것이 중요하다. (실패 원자적으로 설계)
  • 각 메서드에서 발생하는 모든 예외를 문서화하는 것이 좋다.
  • 실패에 대한 자세한 정보를 상세 메시지에 담아야한다.
    실패원인을 포착하려면, toString 등을 통해 예외 발생에 영향을 준 모든 필드와 인자의 값이 들어 있어야 한다. 이를 통해 예외를 메시지를 보고 실패 원인을 알 수 있어야한다.

  • catch절에서 의미없는, 아무 것도 하지 않는 코드는 바람직하지 않다.
  • Layer에 맞는 Exception 던져야한다.
    → DAO에서의 SQLException, Service layer에서는 Business 로직의 custom exception 등
  • e.printStackTrace() 보다는 로깅 프레임워크를 사용하는 편이 좋다.
    → 여러 서버의 로그를 한곳에서 모아서 보는 등 활용이 가능

예외처리에 대한 리팩터링을 진행하면서 받은 피드백

  • 실패에 대한 자세한 정보를 상세 메시지에 담아야한다.
    회원에 대한 정보를 업데이트 하고자 할 때, 회원 아이디가 맞지 않으면 예외를 발생시키는 코드를 구현하였다. 하지만 다음과 같은 코드에서는 Exception에는 실패에 대한 메시지가 부족한 문제점이 있다. Exception과 관련하여 충분한 메시지를 남겨야 추후에 트래킹이 가능할 수 있다.
public void update(UserInfo loginUser, UserInfo target) {
   if (!matchUserId(loginUser.userId)) {
     throw new UnAuthorizedException("사용자가 일치하지 않습니다.");
     //throw new UnAuthorizedException();
   }
   /...
   this.name = name;
 }    
  • 추상화 수준에 맞는 예외를 던져라
    모든 예외를 상위로 던지기보다는 추상화 수준에 맞는 예외로 바꿔서(예외번역) 던져야 한다.
public void validateDelete(User loginUser) throws CannotDeleteException {
   for (Answer answer : answers) {
       answer.validateDelete(loginUser);
   }
}

추상화 수준에 맞는 예외로 던진다는 것은 무슨 의미일까?
의미 있는 예외로 변환하여 던지는 것을 생각하면 될 것 같다.
메서드가 저수준 예외를 처리하지 않고 바깥으로 전파(throws) 하는 경우 수행하려는 일과 관련없는 예외가 발생할 수 있기 때문이다.

 public void validateDelete(User loginUser) throws Exception {
    try {
       for (Answer answer : answers) {
           answer.validateDelete(loginUser);
       }
    } catch (CannotDeleteException e) {
        throw new Exception("");
    }
 }





[참조]
https://codingexplore.tistory.com/74
JPA 프로그래밍
https://github.com/mgp/book-notes/blob/master/effective-java-2nd-edition.markdown#chapter-9-exceptions
https://www.slipp.net/questions/350
https://jjingho.tistory.com/124
https://github.com/Meet-Coder-Study/book-effective-java/blob/main/10%EC%9E%A5/73_%EC%B6%94%EC%83%81%ED%99%94_%EC%88%98%EC%A4%80%EC%97%90_%EB%A7%9E%EB%8A%94_%EC%98%88%EC%99%B8%EB%A5%BC_%EB%8D%98%EC%A0%B8%EB%9D%BC_%EB%B0%95%EC%86%8C%EC%A0%95.md

profile
- 👩🏻‍💻

0개의 댓글