본문 바로가기
Blog/TIL

[240520] 암기하지말고 이해하기

by 코젼 2024. 5. 20.
728x90
반응형

 

🔶 재귀함수 

🔶 Java Exception

🔶 JdbcTemplate

 

목차

     

    / 오늘의 TIL /

    매일매일 열심히 공부하지만 내 뇌의 메모리가 휘발성(ㅠㅠ)인 것 같아서 TIL 작성을 시작한다.

    내가 공부에 투자한 시간을 아깝지 않게 하기 위해서 내가 이해한 것을 다시 글로 작성하는 것이 큰 도움이 될 것이라고 생각한다.


    재귀함수

    코딩 테스트 문제를 풀 때 항상 어떻게 구현할지 먼저 생각하는데, 막상 생각한 것을 실제로 구현하려고 하면 어떤 알고리즘을 사용할지 고민이 되는 것 같다.

    이전에 재귀함수에 대해서 완벽하게 이해했다고 생각했지만 사실은 그 순간에만 이해했었던 것 같다.

     

    이번 백준 문제(14889)를 풀면서 모든 경우의 수를 찾을 때(백트래킹, DFS)를 진행하며 재귀함수에 대해서 다시 이해하는 시간을 가졌다.

     

    코드의 일부분을 발췌했다.

    private static void combi(int idx, int count) {
        //팀 구성원이 다 찬 경우
        if (count == N/2) {
            //차이 값 계산 후 min 구하기
            diff();
            return;
        }
    
        for (int i=idx; i<N; i++) {
            if (!visited[i]) {
                visited[i] = true;
                combi(i + 1, count + 1);
                visited[i] = false;
            }
        }
    }

     

    [1, 2, 3, 4, 5, 6] 팀 중에서 [1, 2, 3 | 4, 5, 6] 로 팀을 구성할 때

    종료 조건: count(팀의 개수)가 반 개로 나누어지면 종료한다.

    [1, 2, 3 | 4, 5, 6], [1, 2, 4 | 3, 5, 6] ... 등 모든 경우의 수를 구할 때, index에 따라서 재귀함수를 돌리면 된다.

     

    위의 경우, 3까지 도달했을 때 팀의 개수가 반 개로 나누어졌기 때문에 종료 조건이 충족한다.

    visited를 false로 바꾸고, for문에 따라 idx가 증가하면서 3 -> 4 로 변경된다.

    그 이후 visited되지 않은 2, 5, 6을 팀으로 구성한다.

     

    이처럼 백트래킹을 진행할 때 재귀함수를 사용하게 되는데, 정확한 로직을 이해하고 사용하는 것이 관건이다.


    Java Exception

    자바 예외는 `checked exception`과 `unchecked exception`로 이루어져 있다.

    대부분 언체크 예외는 `Runtime Exception`을 말한다.

     

    - 체크 예외는 exception을 throws로 던지거나, try-catch문으로 잡아야 한다.

    - 언체크 예외는 throws는 생략이 가능하다.

     

    [주의할 점]

    - 체크 예외를 사용하게 되면 호출 클래스에도 모두 exception 의존 관계가 생기기 때문에 OCP, DIP 원칙을 지킬 수 없게 된다.

    - 상위 예외를 throw하거나 try-catch로 잡을 경우, 하위 예외까지 모두 적용된다. (상위 예외를 던지는 것은 Anti-Pattern)

     

    언체크 예외 활용

    RuntimeException가 제공하는 메서드 중 `Throwable cause`를 매개변수로 가지는 메서드를 사용할 경우, 이전 예외와 함께 출력된다.

    만약 SQLException 예외를 던지는 메서드를 호출할 경우 호출하는 메서드에서 SQLException을 try-catch로 잡아서 새로운 Exception으로 던질 수 있다.

     

    추가적으로, RuntimeSQLException처럼 언체크 예외를 사용하게 된다면 throws를 사용하지 않게 되고 의존 관계를 없앨 수 있다.

    stackTrace :: log를 찍으면 RuntimeException 내용이 출력되고, Caused by로 SQLException이 출력된다.

    Root Cause는 중요하다!

    static class Repository {
        public void call() {
            try {
                runSQL();
            } catch (SQLException e) {
                //SQLException을 잡고 RuntimeSQLException으로 던진다.
                throw new RuntimeSQLException(e);
                
                //빈 생성자인 경우 stackTrace가 되지 않는다.
                //throw new RuntimeSQLException();
            }
        }
    
        public void runSQL() throws SQLException {
            throw new SQLException();
        }
    }

     

    DB 에러 코드, 스프링 예외 추상화

    db마다 예외 에러 코드를 가져올 수 있다.

    예시로, h2 db의 키 중복 에러 코드는 '23505'이고, 예외로 잡고 싶을 때는 e.getErrorCode()를 통해 잡을 수 있다.

     

    그러나 하나씩 다 예외 처리할 수 없기 때문에, 스프링이 예외 추상화를 지원한다.

    스프링은 예외의 최상위 계층으로 DataAccessException을 사용하는데, 이 Exception은 RuntimeException을 상속받고 있다.

     

    스프링 SQL 예외 변환기

    문법 오류인 경우스프링이 제공해주는 SQLErrorCodeSQLEXceptionTranslator 예외 변환기를 통해 받은 결과 값의 클래스는 BadSqlGrammarException과 동일하다.

    변환기가 여러 개 있는데, SQL 관한 변환기는 SQLExceptionTranslator(SQL 최상위 예외)를 사용하면 된다.

    catch(SQLException e) {
    	SQLErrorCodeSQLExceptionTranslator exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
    	DataAccessException resultEx = exTranslator.translate("select", sql, e); //작업 명, sql구문, catch로 잡은 예외
    
    	assertThat(resultEx.getClass()).isEqualto(BadSqlGrammarException.class);
    }

    SQLErrorCodeSQLEXceptionTranslator 클래스 상속 관계를 파악해보면 SQLExceptionTranslator를 상속받고 있는 것을 확인할 수 있다.

    SQLErrorCodeSQLExceptionTranslator
    -> AbstractFallbackSQLExceptionTranslator
    -> SQLExceptionTranslator

    BadSqlGrammarException의 클래스 상속 관계

    BadSqlGrammarException
    -> InvalidDataAccessResourceUsageException
    -> NonTransientDataAccessException
    -> DataAccessException //예외 최상위 계층

    JdbcTemplate

    반복되는 부분을 템플릿으로 변경해서 해결할 수 있다.

     

    Connection 연결, PreparedStatement를 통한 sql 세팅, 파라미터 연결, 예외 반환, 

    트랜잭션 동기화(DataSourceUtils), Connection 닫기, Statement 닫기... 등등

     

    -> 트랜잭션을 위한 커넥션 동기화, 스프링 예외 변환기 등등! WOW!!!

    private final JdbcTemplate template;
    
    //생성자
    public Constructor(DataSource datasource) {
    	this.template = new JdbcTemplate(datasource);
    }
    
    @Override
    public Member save(Member member) {
    
    	String sql = "insert into member(member_id, money) values (?,?)";
        
        //PreparedStatement의 executeUpdate 메서드 대체
        //sql, 파라미터 대입
        template.update(sql, member.getMemberId(), member.getMoney());
        return member;
    }

     

    ResultSet Cursor 이동

    원래 ResultSet의 next()를 이용해서 cursor를 이동해서 db의 각 컬럼에 값을 설정하지만, template를 사용하면 queryForObject를 통해 값을 조회할 수 있다.

    - 파라미터 값: [String sql, Class<T>, requiredType]

    template.queryForObject(sql, memberRowMapper(), memberId);
    
    //람다식 사용, RowMapper 리턴
    private RowMapper<Member> memberRowMapper() {
    	return (rs, rowNum) -> {
        	Member member = new Member();
            member.setMemberId(rs.getString("member_id");
            member.setMoney(rs.getInt("money");
            return member;
        };
    }
    728x90
    반응형

    'Blog > TIL' 카테고리의 다른 글

    [240526] API 개발하기  (0) 2024.05.26
    [240524] 수학 지식을 넓히자  (0) 2024.05.24
    [240523] 배워도 배워도 끝이 없네  (0) 2024.05.23
    [240522] 드디어 JPA를 접하다  (0) 2024.05.22
    [240521] JdbcTemplate 파헤쳐보기  (0) 2024.05.21

    댓글