우리는 여태 것 AOP를 지키려고 엄청난 노력을 해왔다..!! 레포지토리의 구현기술이 다양할 것이라는 예상을 해서 서비스가 레포지토리 인터페이스를 구현하게끔 설계를 하자!

image.png

근데 아직 그 전 코드에서는 아래 코드처럼 체크예외인 SQLException을 던질 수 밖에서 없어서, 인터페이스에도 체크예외를 처리해줘야 컴파일 오류가 안난다…! 근데 이렇게 된다면 레포지토리 계층에 체크예외들을 다 처리해줘야하는 불편함이 생긴다!(이럴거면 인터페이스 왜 만들어?!)

public interface MemberRepositoryEx {

	Member save(Member member) throws SQLException;
	Member findById(String memberId) throws SQLException;
	void update(String memberId, int money) throws SQLException;
	void delete(String memberId) throws SQLException;
	
}

아래와 같이 RuntimeExcpetion을 상속받은 (언체크)예외를 선언해주고,, 영한쌤이 강조하신 Error(e) 매개변수 꼭 넘겨서 caused by 시에 오류 안나게끔 하기!!!

public class MyDbException extends RuntimeException {

    public MyDbException() {
    }

    public MyDbException(String message) {
        super(message);
    }

    public MyDbException(String message, Throwable cause) {
        super(message, cause);
    }

    public MyDbException(Throwable cause) {
        super(cause);
    }
}

아래의 에러 발생시 throw new MyDbException을 하며 체크예외를 런타임예외로 바꾸어서 던지고 있다( 매개변수에 e 넣어서 caused by로 추적가능하게끔 했음)

예외를 변환할 때는 기존 예외를 꼭! 포함하자. 장애가 발생하고 로그에서 진짜 원인이 남지 않는 심각한 문제가 발생할 수 있다. 중요한 내용이어서 한번 더 설명했다.

    @Override
    public Member findById(String memberId) {
        String sql = "select * from member where member_id = ?";

        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            con = getConnection();
            pstmt = con.prepareStatement(sql);
            pstmt.setString(1, memberId);

            rs = pstmt.executeQuery();
            if (rs.next()) {
                Member member = new Member();
                member.setMemberId(rs.getString("member_id"));
                member.setMoney(rs.getInt("money"));
                return member;
            } else {
                throw new NoSuchElementException("member not found memberId=" + memberId);
            }

        } catch (SQLException e) {
            throw new MyDbException(e);
        } finally {
            close(con, pstmt, rs);
        }

    }

이렇게 각각 레포지토리 메서드들이 런타임에러를 던지게끔 한다면, 우리가 원하는 형식의 특정 예외에 종속되지않는 아래와 같은 인터페이스를 만들어서 JDBC, JPA별로 대처가 가능할 것이다.

public interface MemberRepository {
    Member save(Member member);

    Member findById(String memberId);

    void update(String memberId, int money);

    void delete(String memberId);

}

하지만~~ 인터페이스를 AOP 관점에 맞게끔 잘 나누었지만,,, 이렇게 되면 우리는 그냥 체크예외를 언체크예외로 바꾸어 준 것이기 때문에,,,, 항상 MyDbException만 날라와서 예외를 구분할 수 없다는 단점이 있다.

레포지토리에서 넘어온 특정 예외는 로직에서 복구를 시도할 수도 있는데,,, 만약 특정 예외를 구분해서 처리하고 싶다면 어떻게 해야할까?