[자바/스프링] 데이터 액세스 층 설계
1. 데이터 액세스에 대한 이해
데이터 액세스를 돕는 여러가지 기술이 있다. 자바의 근본 데이터 액세스 기술인 JDBC, 역시 지존인 하이버네이트와 JPA, MyBatis 등의 기술말이다. 먼저 지금은 이러한 기술에 집중하지않고 데이터 액세스 자체에 대한 공통적인 이야기를 하려고 한다.
1.1. 데이터 액세스 층의 필요성
비즈니스로직에 빈번한 데이터액세스처리가 섞여있다면 상상만해도 가독성이 최악이다. 비즈니스 로직을 위한 소스코드에서 핵심 로직을 구분하기 힘들 것이 분명하다.
그래서 데이터 액세스를 담당하는 클래스를 따로 둔다. Data Access Object 약어로 DAO라고 한다.
계좌이체 서비스의 transfer() 기능을 생각해보자. transfer()기능 수행 중 Account 데이터에 접근해야 할 일이 생겼다. 직접 RDB에 접근하나? 아니다. AccoumtDao에게 맡기면 된다. AccountDao는 getXXX(), updateXXX() 과 같이 DB에 접근하는 메서드를 가지고 있다. 즉 Data Access의 역할을 가지는 것이 DAO이다.
DAO클래스는 DB의 테이블별로 만들어지는 것이 일반적이다. DAO는 CRUD 기능을 갖춘다. 또한 단순하게 그저 정의된 테이블 정보에 따라 자동으로 DAO를 생성할 수도 있다.
DAO를 두어 데이터 액세스 층을 따로 두면, 데이터 액세스 방식이 바뀌더라도 비즈니스로직은 건들지 않아도 된다. DAO만 건드리면 되잖아!
1.2. 자바의 데이터 액세스 기술
자바는 데이터 액세스 기술을 제공한다. DAO클래스에서는 아래 기술을 사용하여 데이터 액세스 하는 것이다.
- 근본 기술인 JDBC
- ORM프레임워크인 하이버네이트 혹은 JPA
JDBC가 근본 기술이라고 적었다. 자바에서 데이터 액세스를 하려면 어차피 내부적으로 JDBC를 사용하기 때문이다.
1.3. 스프링의 도움
자바의 데이터 액세스 기술로 JDBC, 하이버네이트, JPA 등이 있다고 했다. 스프링은 든든한 자바의 지원군으로서 , 이러한 데이터액세스 기술을 개발자가 쉽게 사용할 수 있도록 연계기능을 제공한다. 스프링 프로젝트 중 스프링 데이터 프로젝트(프로덕트)로 "스프링 데이터 JPA"가 있다. 자바의 JPA를 쉽게 사용할 수 있도록 한다.
데이터 액세스를 스프링과 연계하면 장점은?
- 데이터 액세스를 간결히 기술
- 스프링의 체계적인 예외 이용 가능
- 스프링 트랜젝션 기능 이용 가능
1.4. DataSource
데이터 액세스 자체의 더욱 근본적인 이야기를 해보자.
일단 데이터 액세스라는 것은 데이터베이스 접속을 꼭 수반한다. 그러한 데이터베이스 접속을 관리해주는 팩토리가 DataSource라는 것이다.
DataSource는 '데이터베이스 + Connection오브젝트'의 팩토리이다.
Connection오브젝트가 어색하다면, 흔히 들어봤을 커넥션 풀을 생각해보자. 커넥션 풀이라는 것이 있기에 커넥션을 재사용할 수 있음을 알고있다. 커넥션를 재사용함으로써, 커넥션을 수많이 생성할때의 자원고갈을 방지할 수도 있고 커넥션 오브젝트의 생성과 소멸시 부하를 줄일 수도 있다. 그러한 커넥션 오브젝트라는 것이 DataSource 팩토리에 포함되기에, 커넥션의 생명주기는 DataSource에게 맡겨진다. 자바의 근본적 데이터 액세스 기술인 JDBC는 DataSource 인터페이스를 제공하며, 다양한 구현체도 있다.
자바의 든든한 도우미인 스프링은 DataSource를 Bean으로 관리한다. 개발자가 DataSource 정보를 Bean정의파일 또는 JavaConfig로 정의한다. 정의한 Bean은 @Autowired 등 적절히 주입해서 사용하면 된다. 데이터베이스 드라이버명 등의 접속정보는 Bean에 직접 명시하기보다는, 별도의 프로퍼티 파일에 작성하여 Bean을 정의할 때 프로퍼티 파일을 불러오는 것이 좋다. JavaConfig에서는 @PropertySource를 이용할 수 있다.
자세한 내용은 지금은 생략한다.
* 자바에서 DB접속을 위한 JDBC의 Connection 객체에 대한 설명을 링크걸겠다.
https://splendidlolli.tistory.com/561
2. 데이터액세스 개별 기술
지금까지는 전반적인 데이터액세스 설명이었다면, 지금부터는 특정한 주요 기술 두가지를 공부한다. 그것은 바로 스프링 JDBC와 스프링 JPA이다.
2.1. Spring JDBC
스프링 JDBC는, 자바의 JDBC 기술을 쉽게 사용할 수 있도록 하는 스프링의 기능이다. 자바 JDBC를 직접 사용할 경우 수작업이 좀 많아서 코드가 다소 장황해진다. 그러다보니 세심하게 챙겨야 하는 부분도 놓칠 수가 있다(클로즈 처리 등). 이러한 문제점은 스프링 JDBC 기능을 활용하여 걱정 붙들어 맬 수 있다. 자바 JDBC 기능들을 래핑한 API를 깔끔하게 제공하기 때문에 장황한 코드를 숨겨버려 깔끔하게 코딩할 수 있다. 예를들면, Templete 클래스의 제공 (이름부터가 템플릿이다), SELECT INSERT UPDATE DELETE문 관련된 여러 메서드, 배치업데이트나 프로시저콜 등의 기능들! 이렇게 스프링 JDBC는 자바의 JDBC를 슥슥 사용할 수 있도록 하는 편리한 기능을 제공한다.
https://splendidlolli.tistory.com/563
2.2. Spring Data JPA
마찬가지로 Spring Data JPA는 자바의 JPA기술을 편하게 사용할 수 있도록 기능한다. 더 나아가 'DAO를 자동 구현'하여 단순작업의 수고스러움을 확 덜어준다. 그것이 Spring Data JPA의 매력적인 특징 중 하나다. 그래서 단순 CRUD는 코딩을 직접 할 필요가 전혀 없다.
2.2.1. JPA 알고가기
자바의 JPA는 ORM 표준화된 API를 제공한다. 그래서 하이버네이트 등 ORM을 이용하고 있다면 JPA를 이용할 수 있다.
원래 기본적으로 JPA를 이용하려면, 아래 두 작업이 모두 필요하다.
- Entity 클래스 작성하기
- EntityManager 이용하기
Entity클래스는 데이터베이스 테이블에 대응하는 필드들을 가지며, 필드의 게터 세터 등을 가지고 있다. 이때 이 Entity를 RDB측과 연결시켜주는 것이 EntityManager이다. EntityManager라.. 이름이 참 직관적이다.
이와같이, JPA를 이용하려면 Entity라는 것을 정의해야 하고, 그것을 EntityMamager를 통해 RDB와 연결해야 한다. 좀 더 상황을 자세히 설명하자면, XXXDao의 crud 각각의 메서드에서 구현사항에 맞게 Entity를 사용하고 EntityManager의 기능을 호출하여 Entity와 RDB를 연결한다. 그래서 Entity의 변동을 EntityManager를 통해 RDB에 반영할 수 있고, RDB의 데이터를 Entity에 담아 조회할 수 있다.
2.2.2. Spring Data JPA
놀랍게도 Spring Data JPA를 사용하면 위에서 말한 EntityManager따위 신경쓰지 않아도 된다. 간단히 DAO 인터페이스를 정의하는 것만으로도 데이터액세스(crud 등)가 가능하다. JpaRepository 인터페이스는 Spring Data Jpa가 제공하는 인터페이스인데, 데이터액세스 기능이 이미 구현되어있어 굉장히 편리하다. 단지 이것을 상속한 본인의 DAO 인터페이스를 정의하면 된다. 즉, 간단한 crud만 필요한 경우, 그냥 extends JpaRepository만 하면 하나도 구현하지 않아도 된다. 이미 JpaRepository가 깔쌈하게 구현해뒀기 때문이다. JpaRepository는 아래 코드와 같이 상속한다.
public interface PersonDao extends JpaRepository<Person, Integer> {
...
}
JpaRepository<Person, Integer>처럼 제네릭 안에 두가지의 형을 명시한다. 순서대로, DAO가 취급하는 Entity 클래스타입과 해당 Entity의 ID 타입이다. 즉 Person Entity의 ID는 Integer형이다.
그래서 이 Entity에 대해서 간단한 crud는 여러가지가 자동으로 존재하게된다. 다 JpaRepository에 구현되어 제공하기 때문이다. 예를들면 save()나 findAll()같은!
그러나 좀 더 조건을 지정하여 데이터에 액세스하고 싶을 것이다. deleteByXXX처럼 어떤 필드조건에 맞는 것을 삭제하고 싶다거나..! 이때 Spring Data JPA는 '명명규칙에 따른 메서드 정의' 기능을 제공한다. 아래 코드 두개로 설명 끝! 메서드 이름만 규칙에맞게 잘 부여하면, 그 기능은 내부적으로 자동구현해준다.
* 다양한 명명규칙은 Spring Data JPA 매뉴얼을 참고하자.
public interface PersonDao extends JpaRepository<Person, Integer> {
List<Person> findByPersonName(String name);
List<Person> findByPersonNameAndAgeLessThanEqual(Stirng name, int age);
}
좀 더 상세한 쿼리지정이 필요하면, @Quary를 통해 JPQL을 명시적으로 지정하여 쿼리메서드를 두면 된다. 나도 애용했던 기능! 설명은 생략한다.
+ 위와 같이 dao interface에서 extends JpaRepository를 할 수도 있다.
그러나 Entity class를 두어서 EntityManager를 주입받는 선택을 해도 된다.
이때 SpringBoot에서는 EntityManager를 생성하는 처리를 해주지 않아도 된다.
SpringBoot에서 jpa를 사용한다면 EntityManager에 대한 처리는 알아서 해준다. build.gradle을 보면 SpringBoot에서 jpa를 사용하기 위해 spring-boot-starter-data-jpa가 dependencies를 추가했다. 얘가 EntityManager 생성을 해주는 부분이 있기 때문에 SpringBoot에서는 우리가 직접 EntityManager를 생성하지 않아도 된다.
@Repository
public class MemberRepository {
@PersistenceContext // SpringBoot가 EM을 주입해준다.
public Long save(Member member){
em.persist(member);
return member.getId();
}
}
참고) EntityManager를 통한 데이터 접근은 항상 Transaction안에서 이루어져야 한다
2.3. Spring MyBatis
이부분은 그냥 간단하게 쿼리매퍼 관점에서 학습했던 MyBatis 실습 포스팅을 첨부한다.
https://splendidlolli.tistory.com/571