≣ 목차
/ 오늘의 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;
};
}
'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 |
댓글