Java

Spring JDBC로 알아보는 예외 덩어리 처리 방법

Recfli 2024. 2. 22. 17:48

[ 문제 상황 ]

 JDBC관련 강의를 공부하다가 JDBC로 쿼리를 작성할 때 매 줄마다 예외가 터지는 모습을 보았다. 이런 상황일 때 어떻게 처리해야 하는지를 기록해두고 싶었고 한번 상황을 봐보자.

 

 JDBC로 DB와 연결을 해서 데이터를 주고 받으려면 총 아래의 3단계의 과정을 순서대로 거쳐야 한다.

 

1. DB 커넥션

2. SQL 전달

3. 결과 응답

 

 이 각각의 상황마다 SQLException이 터진다. 이걸 처리하려면 순서대로 객체를 획득하는 순서에 따라 예외를 잡게 하고 반대 순서대로 객체 예외를 처리해주어야 한다. 예를 들어 SQL 전달 객체까지 획득했다면 SQL 전달 객체 처리 -> DB 커넥션 객체 처리 순서대로 해주어야 한다. 이 과정을 한번 봐보자.

 

[ 해결 과정 ]

 우선 아래의 상황은 JDBC를 이용해서 getConnection()에서 DB와의 연결을 얻고 con.prepareStatement(sql)로 sql을 전달한 뒤 응답을 받는 과정이다. 이 때 Connection이 실패하면 getConnection에서는 예외를 던진다. 그리고 prepareStatement를 실패해도 예외를 던진다. Result를 제대로 받지 않아와도 예외를 던진다.

 

 이 코드에서 조심해야 할 점은 connection을 얻고 prepareStatment를 실패해서 예외가 던져졌을 때 con.close()를 제대로 호출해주지 않으면  con은 계속 유지된 채로 방치된다는 것이다. 이런 현상이 지속되면 나중엔 커넥션이 필요할 때 제대로 동작하지 않을 가능성이 있다. Result를 받았을 때도 아래의 두 개가 제대로 반환되지 못한 채로 쌓인다. 이 때 어떻게 처리해야 할까?

public Member findById(String memberId) throws SQLException {
    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){
        log.error("db error", e);
        throw e;
    } finally{
        close(con, pstmt, rs);
    }
}

 

그 방법은 finally 부분에 있다. 예외가 딱 던져졌을 때 catch 문에서 SQLException이 발생했을 때 예외를 던지고 행동하는 부분이 finally 부분이다. 앞에서는 커넥션을 얻고 SQL을 전달하고 결과를 얻은 것과 반대 순서대로 close()를 try 문을 통해 아래처럼 다 처리해주어야 한다. 그리고 close도 Exception을 뱉을 수 있는 상황이니 그 부분에 대한 처리도 각각 try-catch문으로 해줘야 한다. 안해주면 저기서 종료돼버리기 때문에 아직 close 못한 게 그대로 남아있게 된다.

private void close(Connection con, Statement stmt, ResultSet rs){

    if(rs != null){
        try{
            rs.close();
        } catch (SQLException e){
            log.info("error", e);
        }
    }

    if(stmt != null){
        try{
            stmt.close(); // Exception
        } catch(SQLException e){
            log.info("error", e);
        }
    }

    if(con != null){
        try{
            con.close(); // Exception
        } catch(SQLException e){
            log.info("error", e);
        }
    }
}

 

 사실상 맨 위의 findById까지도 예외를 던지는 상황이니, Service나 Controller 단에서도 저 예외를 또 처리해야줘야 할 것이다. 아무튼 JDBC는 Exception 지옥이다. 저렇게 개발을 안해도 돼서 너무 좋다.

[ 참고 자료 ]

스프링 DB 1편 - 김영한 강의