Code

2023.06.16 코드스테이츠 47회차. ( Spring JDBC )

ChoHD 2023. 6. 16. 15:10
반응형

JDBC?

- Java 기반 애플리케이션의 코드 레벨에서 사용하는 데이터를 데이터베이스에 저장 및 업테이트하거나 반대로 데이터베이스에 저장된 데이터를 Java 코드 레벨에서 사용할 수 있도록 해주는 Java에서 제공하는 표준 사양( 또는 명세, Specification) 이다

- JDBC는 Java 애플리케이션에서 데이터베이스에 액세스하기 위해 Java 초창기(JDK 1.1) 버전부터 제공되는 표준 사양(또는 명세, Specification)으로 Java 개발자는 JDBC API를 사용해서 다양한 벤더(Oracle, MS SQL, MySQL 등)의 데이터베이스와 연동할 수 있다.

 

 

JDBC의 동작 흐름

- JDBC는 Java 애플리케이션 내에서 JDBC API를 사용하여 데이터베이스에 액세스하는 단순한 구조이기 때문에 JDBC의 동작 흐름은 심플하다

- Java 애플리케이션에서 JDBC API를 이용해 적절한 데이터베이스 드라이버를 로딩한 후, 데이터베이스와 인터랙션 한다

- JDBC API를 사용해 데이터베이스와 구체적인 인터랙션을 하기 위해서는 JDBC 드라이버를 먼저 로딩한 후에 데이터베이스와 연결을 해야 한다

 

JDBC 드라이버

- JDBC 드라이버는 데이터베이스와의 통신을 담당하는 인터페이스인데, Oracle이나 MS SQL, MySQL 같은 다양한 벤더에서는 해당 벤더에 맞는 JDBC 드라이버를 구현해서 제공을 하게 되고, 우리는 이 JDBC 드라이버의 구현체를 이용해서 특정 벤더의 데이터베이스에 액세스 할 수 있다

 

 

JDBC API 사용 흐름

 

  1. JDBC 드라이버 로딩 : 사용하고자 하는 JDBC 드라이버를 로딩합니다. JDBC 드라이버는 DriverManager라는 클래스를 통해서 로딩된다
  2. Connection 객체 생성 : JDBC 드라이버가 정상적으로 로딩되면 DriverManager를 통해 데이터베이스와 연결되는 세션(Session)인 Connection 객체를 생성한다
  3. Statement 객체 생성 : Statement 객체는 작성된 SQL 쿼리문을 실행하기 위한 객체로써 객체 생성 후에 정적인 SQL 쿼리 문자열을 입력으로 가진다
  4. Query 실행 : 생성된 Statement 객체를 이용해서 입력한 SQL 쿼리를 실행 한다
  5. ResultSet 객체로부터 데이터 조회 : 실행된 SQL 쿼리문에 대한 결과 데이터 셋
  6. ResultSet 객체 Close, Statement 객체 Close, Connection 객체 Close : JDBC API를 통해 사용된 객체들은 사용 이후에 사용한 순서의 역순으로 차례로 Close를 해주어야 한다

 

Connenction Pool 이란?

- JDBC API를 사용해서 데이터베이스와의 연결을 위한 Connection 객체를 생성하는 작업은 비용이 많이 드는 작업 중 하나 이다

- 애플리케이션 로딩 시점에 Connection 객체를 미리 생성해 두고 애플리케이션에서 데이터베이스에 연결이 필요할 경우, Connection 객체를 새로 생성하는 것이 아니라 미리 만들어 둔 Connection 객체를 사용함으로써 애플리케이션의 성능을 향상할 수 있다

- 이처럼 데이터베이스 Connection을 미리 만들어서 보관하고 애플리케이션이 필요할 때 이 Connection을 제공해 주는 역할을 하는 Connection 관리자를 바로 Connection Pool이라고 한다

( Spring Boot 2.0 이전 버전에는 Apache 재단의 오픈 소스인 Apache Commons DBCP(Database Connection Pool, DBCP)를 주로 사용했지만 Spring Boot 2.0부터는 성능면에서 더 나은 이점을 가지고 있는 HikariCP기본 DBCP로 채택했습니다.)

 

  • JDBC(Java Database Connectivity)는 Java 기반 애플리케이션의 코드 레벨에서 사용하는 데이터를 데이터베이스에 저장 및 업데이트하거나 반대로 데이터베이스에 저장된 데이터를 Java 코드 레벨에서 사용할 수 있도록 해주는 Java에서 제공하는 표준 API이다.
  • JDBC의 구체적인 API 사용법을 알 필요는 없지만 JDBC의 동작 흐름을 알면 Spring에서 지원하는 데이터 액세스 기술을 사용하는데 도움이 된다.
  • 데이터베이스 Connection 객체를 미리 만들어서 보관하고 애플리케이션이 필요할 때 이 Connection을 제공해 주는 역할을 하는 Connection 관리자를 바로 Connection Pool이라고 한다.
  • Spring Boot 2.0부터 HikariCP가 기본 DBCP로 채택되었다.

 

 

Spring Data JBDC

데이터 액세스 기술 유형

- Spring에서 사용할 수 있는 대표적인 데이터 액세스 기술에는 mybatis, Spring JDBC, Spring Data JDBC, JPA, Spring Data JPA 등이 있다

 

SQL 중심 기술

- mybatisSpring JDBC는 대표적인 SQL 중심 기술 이다

- SQL 중심 기술은 애플리케이션에서 데이터베이스에 접근하기 위해 SQL 쿼리문을 애플리케이션 내부에 직접적으로 작성하는 것이 중심이 되는 기술 이다

 

<select id="findMember" resultType="Member">
  SELECT * FROM MEMBER WHERE member_id = #{memberId}
</select>

- mybatis에서 사용되는 SQL Mapper의 예

- mybatis의 경우, SQL Mapper라는 설정 파일이 존재하는데 이 SQL Mapper에서 SQL 쿼리문을 직접적으로 작성한다

- 작성된 SQL 쿼리문을 기반으로 데이터베이스의 특정 테이블에서 데이터를 조회한 후, Java 객체로 변환해 주는 것이 mybatis의 대표적인 기술적 특징이다

 

Member member = this.jdbcTemplate.queryForObject(
			"select * from member where member_id=?", 1, Member.class);

- Spring JDBC의 JdbcTemplate이라는 템플릿 클래스를 사용한 데이터베이스 접근 예

- Spring JDBC의 경우에도 Java 코드에 SQL 쿼리문이 직접적으로 포함이 되어 있다

 

## 이처럼 SQL 쿼리문이 직접적으로 포함이 되는 방식은 과거부터 많이 사용하던 방식이고, 현재도 사용이 되고 있긴 하지만 Java 진영에서는 SQL 중심의 기술에서 객체(Object) 중심의 기술로 지속적으로 이전을 하고 있는 추세이다

 

 

객체(Object) 중심 기술

- 객체(Object) 중심 기술은 데이터를 SQL 쿼리문 위주로 생각하는 것이 아니라 모든 데이터를 객체(Object) 관점으로 바라보는 기술이다

- 즉, 객체(Object) 중심 기술은 데이터베이스에 접근하기 위해서 SQL 쿼리문을 직접적으로 작성하기보다는 데이터베이스의 테이블에 데이터를 저장하거나 조회할 경우, Java 객체(Object)를 이용해 애플리케이션 내부에서 이 Java 객체(Object)를 SQL 쿼리문으로 자동 변환 한 후에 데이터베이스의 테이블에 접근한다

- 객체(Object) 중심의 데이터 액세스 기술을 ORM(Object-Relational Mapping)이라고 한다

- Java에서 대표적인 ORM 기술이 바로 JPA(Java Persistence API) 이다

( JPA를 사용하면 SQL쿼리문을 직접적으로 다룰일은 많지 않다, 복잡한 조건의 데이터 조회를 위해 SQL쿼리문을 사용하기도 하지만 그 사용 빈도수는 이전보다 줄어 든다 )

 

 

Spring Data JDBC vs JPA vs Spring Data JPA

Spring Data JDBC 기술은 2018년에 1.0 버전이 처음 릴리스되었기 때문에 기술의 역사가 아직 짧은 편입니다. 따라서 현재도 기능 업그레이드가 꾸준히 이루어지고 있지만 아직까지는 JPA보다 상대적으로 적게 사용되고 있ek

( 애플리케이션의 규모가 상대적으로 크지 않고, 복잡하지 않을 경우에는 Spring Data JDBC가 뛰어난 생산성을 보여줄 거라 기대합니다 )

- Spring Data JDBC를 통해 기본적인 ORM의 개념Spring에서 Data에 접근하는 일관된 접근 방식을 먼저 접하게 된다

 

 

 

Spring Data JDBC를 사용하기 위해서는 Spring Boot Starter를 추가해야 한다

dependencies {
	...
	...
	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
	runtimeOnly 'com.h2database:h2'
}

- 지금부터는 데이터베이스에서 데이터를 관리할 것이므로 개발 환경에서 손쉽게 사용할 수 있는 인메모리(In-memory) DBH2를 사용하기 위해 의존 라이브러리 설정에 추가하였다

 

 

인메모리 (In-Memory) DB

- 우리가 알고 있는 데이터베이스는 삭제를 하지 않는 이상 데이터베이스 서버를 내렸다가 다시 가동해도 데이터베이스 안에 데이터가 그대로 유지가 됩니다

- 인메모리(In-memory) DB는 이름 그대로 메모리 안에 데이터를 저장하는 데이터베이스 이다

- 메모리는 휘발성이기 때문에 컴퓨터 전원을 내렸다가 다시 올리면 메모리에 저장되어 있는 데이터는 모두 지워지게 된다

- 인메모리(In-memory) DB는 애플리케이션이 실행되는 동안에만 데이터를 저장하고 있기 때문에 애플리케이션 실행을 중지했다가 다시 실행시키면 인메모리(In-memory) DB안에 저장되어 있던 데이터는 모두 사라지게 된다

 

인메모리 DB를 사용하는 이유

- 운영 환경에서는 당연히 인메모리(In-memory) DB를 사용하지 않고, 사용해서도 안된다

- 여러분의 PC에서 개발을 할 경우에는 인메모리(In-memory) DB를 사용하는 것이 애플리케이션의 테스트 측면에서 많은 이점을 가진다

- 발을 진행하면서 작업한 코드에 대한 테스트는 꾸준히 진행을 해야 할 텐데, 테스트를 진행하기 위해서는 테스트에 필요한 데이터 이외에 나머지 쓸데없는 데이터는 테이블에 없는 것이 테스트의 정확도 면에서 유리하기 때문이다

- 로컬 개발 환경에서는 테스트가 끝나고 나면 데이터베이스의 테이블에 남아있는 데이터는 깨끗이 비워져 있는 것이 좋다

- 로컬 개발 환경에서는 인메모리(In-memory) DB를 주로 사용한다

- Spring에서 지원하는 테스트 기능에서는 테스트가 끝나면 테스트에 사용한 데이터를 자동으로 지워주는 기능이 있지만 기본적으로 로컬 테스트 환경에서는 인메모리(In-memory) DB 사용을 권장한다

 

 

 

application.yml 파일에 H2 Browser 활성화 설정 추가

- Spring Boot Initializr를 통해 샘플 프로젝트를 생성하면 기본적으로 ‘src/main/resources’ 디렉토리 하단에 application.properties라는 비어 있는 파일이 보인다

- Spring에서는 application.properties 또는 application.yml 파일을 통해 Spring에서 사용하는 다양한 설정 정보들을 입력할 수 있다

- .yml 파일은 애플리케이션의 설정 정보(프로퍼티)를 depth 별로 입력할 수 있는 더 나은 방법을 제공하기 때문에 application.properties의 파일 확장자를 application.yml로 변경하겠습니다

- 위 코드와 같이 입력을 할 시 웹 브라우저 상(H2 콘솔)에서 H2 DB에 접속한 후, 데이터베이스를 관리할 수 있다

( .yml(또는 yaml) 파일에 indent를 주어서 depth를 설정할 때에는 스페이스 바를 눌러서 indent를 설정해도 상관없지만 일반적으로는 Tab(탭) 키를 사용해서 일관성을 유지하는 것이 좋다 )

 

 

H2 DB 정상 동작 유무 확인

- 빨간색 박스로 표시한 로그는 H2 데이터베이스를 정상적으로 사용할 준비가 되었으며, 웹 브라우저로 접속해서 데이터베이스를 관리할 수 있음을 의미한다

- 그 후에 웹 브라우저에 주소를(localhost:8080/h2-console)를 입력하고 밑의 사진과 같은 화면이 뜨면 

- 애플리케이션 로그에 출력된 ‘jdbc:h2:mem:26d0d5d3-dcef-47f8-8e6b-67898bdcfbd0’ 을 [JDBC URL]이라는 항목에 복사/붙여넣기 한 후, [Connect] 버튼을 클릭한다

- 샘플 애플리케이션을 구현하고 테스트를 해보면서 H2 DB에 데이터가 잘 저장되었는지 등을 H2 콘솔을 통해서 확인할 수 있다

 

H2 DB 디폴트 설정의 문제점

- H2 DB는 애플리케이션을 재시작할 때마다 애플리케이션 로그에 출력되는 JDBC URL이 매번 랜덤하게 바뀌기 때문에 매번 랜덤하게 변경된 JDBC URL을 다시 입력하는 것은 상당히 불편하다

- 이 문제는 application.yml 파일에 H2에 대한 추가 설정을 함으로써 해결할 수 있다

- (1)에서는 H2 콘솔의 접속 URL Context path를 조금 더 간결하게 ‘/h2’로 설정하였다

- (2)에서는 JDBC URL이 매번 랜덤하게 바뀌지 않도록 ‘jdbc:h2:mem:test’로 설정하였다

- 주소값이 동일해져서 같은 주소같으로 접속이 가능해 진다

 

 

Message 입력 / 저장 / 출력 샘플코드 구현

- 샘플 코드에서 구현해야 되는 클래스 또는 인터페이스는 다음과 같다

  • MessageDto(DTO 클래스)
  • MessageController
  • MessageMapper
  • MessageService
  • Message(엔티티 클래스)
  • MessageRepository

 

 

- 데이터 액세스 계층에서 데이터베이스와의 연동을 담당하는 Repository인 MessageRepository 인터페이스 이다

- CrudRepository라는 인터페이스를 상속하고 있고, 이 CrudRepository의 제너릭 타입이 <Message, Long>으로 선언되어 있다

- CrudRepository는 데이터베이스에 CRUD(데이터 생성, 조회, 수정, 삭제) 작업을 진행하기 위해 Spring에서 지원해 주는 인터페이스 이다

- CrudRepository<Message, Long>와 같이 제너릭 타입을 지정해 줌으로써 Message 엔티티 클래스 객체에 담긴 데이터를 데이터베이스 테이블에 생성 또는 수정하거나 데이터베이스에서 조회한 데이터를 Message 엔티티 클래스로 변환할 수 있

- <Message, Long>에서 LongMessage 엔티티 클래스의 멤버 변수 중에 식별자를 의미하는 @Id라는 애너테이션이 붙어있는 멤버 변수의 데이터 타입 이다

- MessageRepository 인터페이스 내부에 아무런 코드도 없지만 우리는 이 MessageRepository 인터페이스를 서비스 계층에서 DI를 통해 주입받은 후, 데이터베이스 작업을 위해 사용하게 된다

 

- (1) 에서는 MessageRepository 인터페이스를 MessageService 클래스에서 DI를 통해 주입받은 후 (2)에서 Message 엔티티 클래스에 포함된 데이터를 데이터베이스에 저장하는 데 사용하고 있다

- MessageRepository 인터페이스 내부에는 별도로 구현한 메서드가 없는데도 불구하고 (2)에서는 messageRepository.save(message)와 같이 save() 메서드를 사용하고 있는 것을 볼 수 있다

- save() 메서드는 MessageRepository가 상속받은 CrudRepository에 이 save() 메서드가 정의 되어 있

- 개발자가 데이터의 생성, 조회, 수정, 삭제 작업을 위한 별도의 코드를 구현하지 않아도 CrudRepository가 이 작업을 대신해 주는 역할을 한다

- (2)에서는 데이터베이스에 데이터를 저장하고 난 후, 데이터베이스에 저장된 데이터를 다시 리턴해 준다

 

- (1)의 Message라는 클래스 명은 데이터베이스의 테이블 명에 해당한다

- (2)의 @Id 애너테이션을 추가한 멤버 변수는 해당 엔티티의 고유 식별자 역할을 하고, 이 식별자는 데이터베이스의 Primary key로 지정한 열에 해당 된다

- pring에서는 (1)과 같이 테이블 생성을 위한 SQL 문이 추가된 ‘schema’라는 파일명으로 .sql 파일의 경로를 지정해 주면 이 schema.sql 파일에 있는 스크립트를 읽어서 애플리케이션 실행 시, 데이터베이스에 테이블을 자동으로 생성해 준다

- 메모리 DB를 사용할 경우, 애플리케이션이 실행될 때마다 schema.sql 파일의 스크립트가 매번 실행된

 

- ‘message_id’는 MESSAGE 테이블의 Primary key이고 AUTO_INCREMENT를 지정했기 때문에 데이터가 insert될 때마다 자동으로 증가 된다

- 애플리케이션 쪽에서 데이터베이스에 데이터를 insert할 때 ‘message_id’ 열에 해당하는 값을 지정해주지 않아야 한다는 의미이다

(MESSAGE 테이블은 Message 클래스 명과 매핑되고 ‘message_id’ 열은 Message 클래스의 messageId 멤버 변수와 매핑된다 )

( ‘message’ 열은 Message 클래스의 message 멤버 변수와 매핑 된다 )

- ORM(Object-Relational Mapping)에서는 객체의 멤버 변수와 데이터베이스 테이블의 열이 대부분 1대1로 매핑이 된다

 

- MESSAGE 테이블의 식별자(Primary key)인 ‘message_id’ 열에 AUTO_INCREMENT 설정이 되어 있으므로 ‘message_id’ 열에 값을 입력하지 않더라도 데이터가 저장될 때마다 자동으로 포함이 된다

 

 

 

Spring Date JDBC 적용순서

  1. build.gradle에 사용할 데이터베이스를 위한 의존 라이브러리를 추가합니다.
  2. application.yml 파일에 사용할 데이터베이스에 대한 설정을 합니다.
  3. ‘schema.sql’ 파일에 필요한 테이블 스크립트를 작성합니다.
  4. application.yml 파일에서 ‘schema.sql’ 파일을 읽어서 테이블을 생성할 수 있도록 초기화 설정을 추가합니다.
  5. 데이터베이스의 테이블과 매핑할 엔티티(Entity) 클래스를 작성합니다.
  6. 작성한 엔티티 클래스를 기반으로 데이터베이스의 작업을 처리할 Repository 인터페이스를 작성합니다.
  7. 작성된 Repository 인터페이스를 서비스 클래스에서 사용할 수 있도록 DI 합니다.
  8. DI 된 Repository의 메서드를 사용해서 서비스 클래스에서 데이터베이스에 CRUD 작업을 수행합니다.

 

 

 

  • 데이터 액세스 기술의 유형은 크게 SQL 중심의 기술과 객체(Object) 중심의 기술로 나눌 수 있다.
  • SQL 중심의 기술에는 mybatis, Spring JDBC 등이 있다.
  • 객체(Object) 중심의 기술에는 JPA, Spring Data JDBC 등이 있다.
  • JPA 같은 객체(Object) 중심의 기술을 ORM(Object-Relational Mapping) 기술이라고 한다.
  • 인메모리(In-memory) DB는 애플리케이션이 실행된 상태에서만 데이터를 저장하고 애플리케이션 실행이 중지되면 인메모리 DB 역시 실행이 중지되어 저장된 데이터가 사라진다.
  • Spring에서 지원하는 CrudRepository 인터페이스는 CRUD에 대한 기본적인 메서드를 정의하고 있기 때문에 별도의 CRUD 기능을 개발자가 직접 구현할 필요가 없다.
  • application.properties 또는 application.yml 파일의 설정 정보 등록을 통해 데이터베이스 설정, 데이터베이스의 초기화 설정 등의 다양한 설정을 할 수 있다.
  • application.yml 방식은 중복되는 프로퍼티의 입력을 줄여주기 때문에 application.properties 방식보다 더 선호되는 추세이다.
  • 엔티티(Entity) 클래스 이름은 데이터베이스 테이블의 이름에 매핑되고, 엔티티 클래스 각각의 멤버 변수는 데이터베이스 테이블의 열에 매핑된다.
  • 엔티티 클래스의 멤버 변수에 @Id 애너테이션을 추가하면 데이터베이스 테이블의 기본키(Primary key) 열과 매핑된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90