JDBC 반복 문제 해결 - JdbcTemplate
2022. 8. 10. 16:43
JDBC
지금까지 서비스 계층의 순수함을 유지하기 위해 수 많은 노력을 했고, 덕분에 서비스 계층의 순수함을 유지하게 되었다. 이번에는 리포지토리에서 JDBC를 사용하기 때문에 발생하는 반복 문제를 해결해보자 JDBC 반복 문제 ● 커넥션 조회, 커넥션 동기화 ● PreparedStatement 생성 및 파라미터 바인딩 ● 쿼리 실행 ● 결과 바인딩 ● 예외 발생시 스프링 예외 변환기 실행 ● 리소스 종료 리포지토리의 각각의 메서드를 살펴보면 상당히 많은 부분이 반복된다. 이런 반복을 효과적으로 처리하는 방법이 바로 템플릿 콜백 패턴이다. 스프링은 JDBC의 반복 문제를 해결하기 위해 JdbcTemplate 이라는 템플릿을 제공한다. JdbcTemplate 에 대한 자세한 사용법은 뒤에서 설명하겠다. 지금은 전체..
스프링 예외 추상화 적용
2022. 8. 10. 16:05
JDBC
MemberRepositoryV4_2 package hello.jdbc.repository; import hello.jdbc.domain.Member; import hello.jdbc.repository.ex.MyDbException; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; import org.springframework.jdbc.support.SQLEx..
스프링 예외 추상화 이해
2022. 8. 9. 23:19
JDBC
스프링 데이터 접근 예외 계층 ● 스프링은 데이터 접근 계층에 대한 수십 가지 예외를 정리해서 일관된 예외 계층을 제공한다. ● 각각의 예외는 특정 기술에 종속적이지 않게 설계되어 있다. 따라서 서비스 계층에서도 스프링이 제공하는 예외를 사용하면 된다. 예를 들어서 JDBC 기술을 사용하든, JPA 기술을 사용하든 스프링이 제공하는 예외를 사용하면 된다. ● JDBC나 JPA를 사용할 때 발생하는 예외를 스프링이 제공하는 예외로 변환해주는 역할도 스프링이 제공한다. ● 참고로 그림을 단순화 하기 위해 일부 계층을 생략했다. ● 예외의 최고 상위는 org.springframework.dao.DataAccessException 이다. 그림에서 보는 것 처럼 런타임 예외를 상속 받았기 때문에 스프링이 제공하는..
데이터 접근 예외 직접 만들기
2022. 8. 9. 22:02
JDBC
데이터베이스 오류에 따라서 특정 예외는 복구하고 싶을 수 있다. 예를 들어서 회원 가입시 DB에 이미 같은 ID가 있으면 ID 뒤에 숫자를 붙여서 새로운 ID를 만들어야 한다고 가정해보자. ID를 hello 라고 가입 시도 했는데, 이미 같은 아이디가 있으면 hello12345 와 같이 뒤에 임의의 숫자를 붙여서 가입하는 것이다. 데이터를 DB에 저장할 때 같은 ID가 이미 데이터베이스에 저장되어 있다면, 데이터베이스는 오류 코드를 반환하고, 이 오류 코드를 받은 JDBC 드라이버는 SQLException 을 던진다. 그리고 SQLException 에는데이터베이스가 제공하는 errorCode 라는 것이 들어있다. 데이터베이스 오류 코드 그림 H2 데이터베이스의 키 중복 오류 코드 e.getErrorCod..
런타임 예외 적용
2022. 8. 9. 19:44
JDBC
MemberRepository 인터페이스 package hello.jdbc.repository; import hello.jdbc.domain.Member; public interface MemberRepository { Member save(Member member); Member findById(String memberId); void update(String memberId, int money); void delete(String memberId); } MyDbException 런타임 예외 package hello.jdbc.repository.ex; public class MyDbException extends RuntimeException{ public MyDbException() { } publi..
체크 예외와 인터페이스
2022. 8. 9. 18:36
JDBC
서비스 계층은 가급적 특정 구현 기술에 의존하지 않고, 순수하게 유지하는 것이 좋다. 이렇게 하려면 예외에 대한 의존도 함께 해결해야한다. 예를 들어서 서비스가 처리할 수 없는 SQLException 에 대한 의존을 제거하려면 어떻게 해야할까? 서비스가 처리할 수 없으므로 리포지토리가 던지는 SQLException 체크 예외를 런타임 예외로 전환해서 서비스 계층에 던지자. 이렇게 하면 서비스 계층이 해당 예외를 무시할 수 있기 때문에, 특정 구현 기술에 의존하는 부분을 제거하고 서비스 계층을 순수하게 유지할 수 있다. 인터페이스 도입 먼저 MemberRepository 인터페이스도 도입해서 구현 기술을 쉽게 변경할 수 있게 해보자. 인터페이스 도입 그림 ● 이렇게 인터페이스를 도입하면 MemberServ..
예외 포함과 스택 트레이스
2022. 8. 9. 15:23
JDBC
예외를 전환할 때는 꼭! 기존 예외를 포함해야 한다. 그렇지 않으면 스택 트레이스를 확인할 때 심각한 문제가 발생한다. @Test void printEx() { Controller controller = new Controller(); try { controller.request(); } catch (Exception e) { log.info("ex", e); } } ● 로그를 출력할 때 마지막 파라미터에 예외를 넣어주면 로그에 스택 트레이스를 출력할 수 있다. ● 예) log.info("message={}", "message", ex) , 여기에서 마지막에 ex 를 전달하는 것을 확인할 수 있다. 이렇게 하면 스택 트레이스에 로그를 출력할 수 있다. ● 예) log.info("ex", ex) 지금 예에..
언체크 예외 활용
2022. 8. 9. 14:56
JDBC
런타임 예외 사용 - 그림 ● SQLException 을 런타임 예외인 RuntimeSQLException 으로 변환했다. ● ConnectException 대신에 RuntimeConnectException 을 사용하도록 바꾸었다. ● 런타임 예외이기 때문에 서비스, 컨트롤러는 해당 예외들을 처리할 수 없다면 별도의 선언 없이 그냥 두면 된다. 런타임 예외 사용 변환 - 코드 - UncheckedAppTest package hello.jdbc.exception.basic; import org.junit.jupiter.api.Test; import java.net.ConnectException; import java.sql.SQLException; import static org.assertj.core.a..
체크 예외 활용
2022. 8. 9. 04:10
JDBC
그렇다면 언제 체크 예외를 사용하고 언제 언체크(런타임) 예외를 사용하면 좋을까? 기본 원칙은 다음 2가지를 기억하자. ● 기본적으로 언체크(런타임) 예외를 사용하자. ● 체크 예외는 비즈니스 로직상 의도적으로 던지는 예외에만 사용하자. ● 이 경우 해당 예외를 잡아서 반드시 처리해야 하는 문제일 때만 체크 예외를 사용해야 한다. 예를 들어서 다음과 같은 경우가 있다. ● 체크 예외 예) ● 계좌 이체 실패 예외 ● 결제시 포인트 부족 예외 ● 로그인 ID, PW 불일치 예외 ● 물론 이 경우에도 100% 체크 예외로 만들어야 하는 것은 아니다. 다만 계좌 이체 실패처럼 매우 심각한 문제는 개발자가 실수로 예외를 놓치면 안된다고 판단할 수 있다. 이 경우 체크 예외로 만들어 두면 컴파일러를 통해 놓친 예..
언체크 예외 기본 이해
2022. 8. 9. 02:06
JDBC
● RuntimeException 과 그 하위 예외는 언체크 예외로 분류된다. ● 언체크 예외는 말 그대로 컴파일러가 예외를 체크하지 않는다는 뜻이다. ● 언체크 예외는 체크 예외와 기본적으로 동일하다. 차이가 있다면 예외를 던지는 throws 를 선언하지 않고, 생략할 수 있다. 이 경우 자동으로 예외를 던진다. 체크 예외 VS 언체크 예외 ● 체크 예외: 예외를 잡아서 처리하지 않으면 항상 throws 에 던지는 예외를 선언해야 한다. ● 언체크 예외: 예외를 잡아서 처리하지 않아도 throws 를 생략할 수 있다. 언체크 예외 전체 코드 package hello.jdbc.exception.basic; import lombok.extern.slf4j.Slf4j; import org.assertj.co..
체크 예외 기본 이해
2022. 8. 8. 21:34
JDBC
● Exception 과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다. 단 RuntimeException 은 예외로 한다. ● 체크 예외는 잡아서 처리하거나, 또는 밖으로 던지도록 선언해야한다. 그렇지 않으면 컴파일 오류가 발생한다. 체크 예외 전체 코드 package hello.jdbc.exception.basic; import lombok.extern.slf4j.Slf4j; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; @Slf4j public class CheckedTest { @Test void checked_ca..
예외 기본 규칙
2022. 8. 8. 19:36
JDBC
예외는 폭탄 돌리기와 같다. 잡아서 처리하거나, 처리할 수 없으면 밖으로 던져야한다. 예외 처리 5번에서 예외를 처리하면 이후에는 애플리케이션 로직이 정상 흐름으로 동작한다. 예외 던짐 예외를 처리하지 못하면 호출한 곳으로 예외를 계속 던지게 된다 예외에 대해서는 2가지 기본 규칙을 기억하자. 1. 예외는 잡아서 처리하거나 던져야 한다. 2. 예외를 잡거나 던질 때 지정한 예외뿐만 아니라 그 예외의 자식들도 함께 처리된다. ▶예를 들어서 Exception 을 catch 로 잡으면 그 하위 예외들도 모두 잡을 수 있다. ▶예를 들어서 Exception 을 throws 로 던지면 그 하위 예외들도 모두 던질 수 있다. 참고: 예외를 처리하지 못하고 계속 던지면 어떻게 될까? 자바 main() 쓰레드의 경우 ..
자바 예외 이해
2022. 8. 8. 19:28
JDBC
예외 계층 스프링이 제공하는 예외 추상화를 이해하기 위해서는 먼저 자바 기본 예외에 대한 이해가 필요하다. 예외는 자바 언어의 기본 문법에 들어가기 때문에 대부분 아는 내용일 것이다. 예외의 기본 내용을 간단히 복습하고, 실무에 필요한 체크 예외와 언체크 예외의 차이와 활용 방안에 대해서도 알아보자 예외 계층 그림 ● Object : 예외도 객체이다. 모든 객체의 최상위 부모는 Object 이므로 예외의 최상위 부모도 Object 이다. ● Throwable : 최상위 예외이다. 하위에 Exception 과 Error 가 있다. ● Error : 메모리 부족이나 심각한 시스템 오류와 같이 애플리케이션에서 복구 불가능한 시스템 예외이다. 애플리케이션 개발자는 이 예외를 잡으려고 해서는 안된다. ▶상위 예외..
스프링 부트의 자동 리소스 등록
2022. 8. 8. 15:58
JDBC
데이터소스와 트랜잭션 매니저를 스프링 빈으로 직접 등록 @Bean DataSource dataSource() { return new DriverManagerDataSource(URL, USERNAME, PASSWORD); } @Bean PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } 기존에는 이렇게 데이터소스와 트랜잭션 매니저를 직접 스프링 빈으로 등록해야 했다. 그런데 스프링 부트가 나오면서 많은 부분이 자동화되었다. (더 오래전에 스프링을 다루어왔다면 해당 부분을 주로 XML로 등록하고 관리했을 것이다.) 데이터소스 - 자동 등록 ● 스프링 부트는 데이터소스(..
트랜잭션 문제 해결 - 트랜잭션 AOP 정리
2022. 8. 8. 05:38
JDBC
트랜잭션 AOP 적용 전체 흐름 선언적 트랜잭션 관리 vs 프로그래밍 방식 트랜잭션 관리 ● 선언적 트랜잭션 관리(Declarative Transaction Management) ▶@Transactional 애노테이션 하나만 선언해서 매우 편리하게 트랜잭션을 적용하는 것을 선언적 트랜잭션 관리라 한다. ▶선언적 트랜잭션 관리는 과거 XML에 설정하기도 했다. 이름 그대로 해당 로직에 트랜잭션을 적용하겠다 라고 어딘가에 선언하기만 하면 트랜잭션이 적용되는 방식이다. ● 프로그래밍 방식의 트랜잭션 관리(programmatic transaction management) ▶트랜잭션 매니저 또는 트랜잭션 템플릿 등을 사용해서 트랜잭션 관련 코드를 직접 작성하는 것을 프로그래밍 방식의 트랜잭션 관리라 한다. ● 선..
트랜잭션 문제 해결 - 트랜잭션 AOP 적용
2022. 8. 7. 21:42
JDBC
MemberServiceV3_3 package hello.jdbc.service; import hello.jdbc.domain.Member; import hello.jdbc.repository.MemberRepositoryV3; import lombok.extern.slf4j.Slf4j; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import java.sql.SQLException; /** * ..
트랜잭션 문제 해결 - 트랜잭션 AOP 이해
2022. 8. 7. 17:53
JDBC
● 지금까지 트랜잭션을 편리하게 처리하기 위해서 트랜잭션 추상화도 도입하고, 추가로 반복적인 트랜잭션 로직을 해결하기 위해 트랜잭션 템플릿도 도입했다. ● 트랜잭션 템플릿 덕분에 트랜잭션을 처리하는 반복 코드는 해결할 수 있었다. 하지만 서비스 계층에 순수한 비즈니스 로직만 남긴다는 목표는 아직 달성하지 못했다. ● 이럴 때 스프링 AOP를 통해 프록시를 도입하면 문제를 깔끔하게 해결할 수 있다. 참고 스프링 AOP와 프록시에 대해서 지금은 자세히 이해하지 못해도 괜찮다. 지금은 @Transactional 을 사용하면 스프링이 AOP를 사용해서 트랜잭션을 편리하게 처리해준다 정도로 이해해도 된다. 스프링 AOP 와 프록시에 대한 자세한 내용은 스프링 핵심 원리 - 고급편을 참고하자. 프록시를 통한 문제 ..
트랜잭션 문제 해결 - 트랜잭션 템플릿
2022. 8. 7. 16:59
JDBC
트랜잭션 사용 코드 //트랜잭션 시작 TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { //비즈니스 로직 bizLogic(fromId, toId, money); transactionManager.commit(status); //성공시 커밋 } catch (Exception e) { transactionManager.rollback(status); //실패시 롤백 throw new IllegalStateException(e); } ● 트랜잭션을 시작하고, 비즈니스 로직을 실행하고, 성공하면 커밋하고, 예외가 발생해서 실패하면 롤백한다. ● 다른 서비스에서 트랜잭션을 시작하..
트랜잭션 문제 해결 - 트랜잭션 매니저2
2022. 8. 7. 03:03
JDBC
트랜잭션 매니저1 - 트랜잭션 시작 클라이언트의 요청으로 서비스 로직을 실행한다. 1. 서비스 계층에서 transactionManager.getTransaction() 을 호출해서 트랜잭션을 시작한다. 2. 트랜잭션을 시작하려면 먼저 데이터베이스 커넥션이 필요하다. 트랜잭션 매니저는 내부에서 데이터소스를 사용해서 커넥션을 생성한다. 3. 커넥션을 수동 커밋 모드로 변경해서 실제 데이터베이스 트랜잭션을 시작한다.4. 커넥션을 트랜잭션 동기화 매니저에 보관한다. 5. 트랜잭션 동기화 매니저는 쓰레드 로컬에 커넥션을 보관한다. 따라서 멀티 쓰레드 환경에 안전하게 커넥션을 보관할 수 있다. 트랜잭션 매니저2 - 로직 실행 6. 서비스는 비즈니스 로직을 실행하면서 리포지토리의 메서드들을 호출한다. 이때 커넥션을 ..
트랜잭션 문제 해결 - 트랜잭션 매니저1
2022. 8. 6. 21:21
JDBC
MemberRepositoryV3 package hello.jdbc.repository; import hello.jdbc.domain.Member; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.jdbc.support.JdbcUtils; import javax.sql.DataSource; import java.sql.*; import java.util.NoSuchElementException; /** * 트랜잭션 - 트랜잭션 매니저 * DataSourceUtils.getConnection() * DataSourceUtils.release..