
🚨 문제 상황
자바 스프링 애플리케이션을 만들어서 데이터베이스와 통신하는데, DB 벤더가 바뀐다? 그렇게 되면 SQL 문법도 조금씩 다르고, DB 연결 코드도 바뀔 수 있기 때문에 바뀔 때마다 코드도 수정해야하고, 테스트도 바꿔야 한다. 이걸 어떻게 해결할 수 있을까?
Java 애플리케이션과 다양한 데이터베이스 관리 시스템(DBMS)간의 연결을 제공하는 표준 API로서, Java진영에서 데이터베이스 연동을 가능하게 하는 핵심 기술이다. JDBC는 SQL 질의 실행, 데이터베이스 연결 관리, 트랜잭션 처리, 결과 집합(ResultSet) 처리 등 데이터베이스와 관련된 모든 작업을 Java 애플리케이션에서 직접 수행할 수 있도록 하는 기능을 제공한다.
특징
Connection, Statement, ResultSet 등 인터페이스를 제공한다.데이터베이스 커넥터는 응용 프로그램과 데이터베이스 간의 상호작용을 가능하게 하는 소프트웨어 구성 요소로, 이들 사이의 통신을 관리하고 중재하는 핵심적인 역할을 수행한다. 이는 애플리케이션과 데이터베이스 사이의 데이터 흐름을 원활하게 하기 위해 사용되며, 다양한 데이터베이스 시스템 (MySQL, PostgreSQL, Oracle, SQL Server 등)과의 연결을 표준화된 방식으로 처리한다.
크게 드라이버(Driver)와 클라이언트 라이브러리(Client Library)로 나눌 수 있다.
드라이버
데이터베이스 엔진과 직접적으로 상호작용하며, SQL 명령문을 전송하고 결과를 받아오는 역할을 수행한다.
클라이언트 라이브러리
이러한 드라이버를 추상화하여 개발자가 더 편리하게 사용할 수 있는 고수준의 인터페이스를 제공하고 있다.
데이터베이스 커넥터는 데이터베이스와의 연결을 효율적으로 재사용하기 위해 커넥션 풀링을 지원한다.
❓풀링이란?
자원을 필요할 때마다 생성하고 폐기하는 방법 대신, 미리 자원을 할당해두고 이를 필요한 시점에 재활용하는 것
✅ 데이터베이스 커넥션 풀링이란?
데이터베이스와의 연결을 미리 생성해 두고, 애플리케이션에서 필요할 때마다 이를 할당해 사용한 후 다시 풀에 반환하는 방식으로, 데이터베이스 연결의 생성 및 폐기에 따른 오버헤드를 줄여준다. 이 기법은 데이터베이스 서버와의 빈번한 연결 및 해제를 방지하여 시스템의 성능을 높이고, 연결 수에 대한 제어를 통해 자원 고갈을 방지함으로써 안정성을 제공할 수 있다.
즉, JDBC(클라이언트 라이브러리) + JDBC 드라이버(드라이버) = Java에서 DB 접속 가능한 환경이 만들어지고, 이 조합을 데이터베이스 커넥터라고 부른다.
🚨 문제 상황
JDBC로 연결 방식 표준화하여 연결에서는 문제 해결 !
하지만, 아직 아래와 같은 문제점이 남아있다.
객체 지향 프로그래밍 언어는 클래스(객체) 중심이며, 객체 간의 연결 관계를 찾을 때 참조로 해결할 수 있는 것들이 많다. 또한, 상속/다형성/캡슐화 등의 특징이 존재한다.
하지만, 관계형 DB에서는 테이블(데이터) 중심의 구조이며, 다른 테이블과의 관계를 외래키, JOIN + SQL로 해결해야 한다. 또한, DB는 상속 개념이 없기 때문에 테이블로 나타내야 한다.
이 불일치를 해결하기 위해 매번 SQL로 수동 해결해야 하는데, SQL은 직접 반복해서 작성해야 한다. 객체와 테이블 매핑을 수작업으로 관리하고, 연관된 객체를 조회할 때 JOIN+ 매핑 + 수작업을 하기 시작하면 SQL에 비즈니스 로직이 녹아들기 시작한다. 그렇게 되면 SQL에 대한 의존도가 높아지고 객체 지향의 장점들을 놓치게 된다 !
또, 테이블은 언제든지 변경될 수 잇는데, 애플리케이션이 SQL에 의존적이라면 테이블이 변경되었을 때 전체 애플리케이션에 미치는 영향이 커지면서 유지보수성이 떨어진다.
즉, 패러다임 불일치에서 오는 모든 문제를 SQL이 다 해결하려고 하면 비용이 많이 발생하고 SQL 중심의 개발이 되어 버린다 !!
DB마다 SQL 문법 자체는 여전히 다르기 때문에 벤더가 바뀌면 연결 문제는 해결되더라도 문법 관련 문제가 있을 수 있다.
LIMIT, ROWNUM, AUTO_INCREMENT)그럼 여전히 DB 벤더가 바뀌었을 때 SQL도 수정해줘야 한다.
객체지향 프로그래밍 언어의 객체와 관계형 데이터베이스의 테이블 간의 불일치를 해소하는 방법이다. ORM은 객체 지향 시스템의 객체를 관계형 데이터베이스의 레코드와 매핑하는 과정을 자동화한다.
단점
ORM만 맹신해서 객체의 구성이나 비즈니스 로직의 구성을 객체 쪽에만 편향되게 구성이 되어있다면, 성능 방면에서 치명적일 수 있다. SQL을 자동 생성해주는 기능을 포함하는데, 이 때 객체의 상태에 따라 ORM이 이상한 쿼리를 만들어낼 수도 있다.
또, 복잡한 쿼리나 대량의 데이터를 처리할 경우, 잘못하면 성능 문제가 발생할 수 있다. 이는 특정 상황에서 자동으로 생성된 쿼리가 최적화되어 있지 않을 수 있기 때문이다. 특정 상황에서는 SQL을 직접 작성하는 게 나을 수도 있기 때문에 상황에 따라 유동적으로 판단하여 사용하는 것이 현명하다.
자바 플랫폼에서 ORM을 표준화하기 위해 정의된 일종의 인터페이스이다. JPA는 Java 객체와 관계형 데이터베이스의 테이블 간의 매핑을 수행하고, 데이터베이스와의 상호작용을 추상화하여 데이터 접근 계층을 단순화하는 역할을 수행한다.
소프트웨어 시스템에서 생성된 데이터가 프로그램 실행이 종료된 후에도 사라지지 않고 지속적으로 유지되는 특성을 의미한다. 데이터 영속성은 데이터베이스, 파일 시스템, 클라우드 스토리지와 같은 영구 저장 매개체를 통해 구현되며, 이는 데이터의 장기적 보존과 접근성을 보장하는 데 필수적인 요소이다.
Java 기반 애플리케이션에서 JPA를 구현한 구현체 프레임워크 중 하나이다. 하이버네이트는 객체 지향 프로그래밍의 객체와 관계형 데이터베이스의 테이블 간의 매핑을 자동화하여, 데이터베이스와 상호작용하는 과정을 크게 단순화하여 생산성을 극도로 향상시킨다. 하이버네이트는 JPA 표준을 따르면서도 추가적인 기능과 최적화를 제공하여 강력한 데이터 지속성(혹은 영속성, Data Persistence) 관리 기능을 제공한다.
의존성 주입
dependencies {
implementation 'org.hibernate.orm:hibernate-core' // hibernate를 직접 사용할 때
}
dependencies { // spring 환경에서 Hibernate를 JPA 구현체로 사용할 때
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}
JPA 설정 파일
/resources/META-INF/persistence.xml
persistence.xml은 JPA에서 엔티티 매니저 팩토리(EntityManagerFactory)를 설정하기 위한 표준 XML 설정 파일이다. 하지만, Spring boot에서는 보통 스프링부트가 설정을 자동으로 처리해주기 때문에 persistence.xml을 사용하지 않는다고 한다.
xml란?
XML(Extensible Markup Language)은 데이터를 정의하는 규칙을 제공하는 마크업 언어이다. 즉, 데이터를 표현하는 데만 사용하는 마크업 언어이다.
엔티티 매니저는 Spring 애플리케이션의 엔티티 객체의 생명주기를 관리하며 이를 통해 데이터베이스 레코드와 객체 간의 매핑 및 조작을 보다 쉽게 수행할 수 있게 도와주는 객체이다. EntityManager은 영속성 컨텍스트(Persistence Context)를 통해 엔티티 객체의 상태를 관리한다. 영속성 컨텍스트는 일종의 1차 캐시 역할을 하여, DB와의 불필요한 트랜잭션을 줄이고, 애플리케이션의 성능을 향상시킨다. 또한, EntityManager은 트랜잭션 관리를 담당하고 있다. 즉, 트랜잭션의 시작(BEGIN), 커밋(COMMIT), 롤백(ROLLBACK)을 제어한다.
엔티티 객체의 상태를 추적하고, 해당 엔티티가 언제, 어떻게 데이터베이스와 상호작용할지 결정한다.
특징
비영속(새로운 상태) (new/transient)
비영속 상태의 엔티티 객체는 아직 영속성 컨텍스트에 등록되지 않은 상태이다. 그렇기 때문에 영속 컨테이너에 의해 관리되지 않을뿐더러 데이터베이스와도 연관이 없다.
엔티티가 비영속 상태에 있을 경우, 해당 인스턴스를 DB에 추가하기 위해서는 영속성 컨텍스트에 병합(merge)되거나 저장(persist)되어야 한다. 만일 병합이나 저장이 수행되면 데이터베이스의 식별자와 매핑되어 식별자 값을 가진다. (비영속 상태에서는 식별자 값을 가지지 않는다.)
영속(관리) (managed)
영속 상태의 엔티티 객체는 영속성 컨텍스트에 의해 관리되고 있는 상태이다. 영속성 컨텍스트는 영속 상태의 엔티티의 변경 사항을 추적 (Dirty Checking)하며, 트랜잭션이 커밋될 때 변경 사항을 DB에 자동으로 반영한다.
준영속(detached)
준영속 상태의 엔티티는 더 이상 영속성 컨텍스트에 의해 관리되지 않는 상태이다. 준영속 상태의 엔티티는 대개 해당 엔티티 인스턴스가 영속성 컨텍스트의 생명주기를 벗어나거나 명시적으로 분리된 경우에 해당한다.
준영속 상태의 엔티티 인스턴스는 필드의 변화가 일어난 후 커밋이 발생하더라도 영속성 컨텍스트의 관리를 받고 있지 않기 때문에 DB에 변경사항이 반영되지 않는다. (더티 체킹이 일어나지 않는다.) 준영속 상태의 엔티티는 영속 컨테이너에 들어온 적이 있으므로 반드시 식별자 값을 가진다.
삭제(removed)
삭제 상태의 엔티티는 영속성 컨텍스트 뿐만 아니라 데이터베이스에서도 삭제를 하기 위한 상태이다. 삭제 상태에서 엔티티는 트랜잭션이 커밋될 때 실제 데이터베이스에서도 데이터가 삭제된다.
Dirty Checking는 영속성 컨텍스트에 의해 관리되고 있는 엔티티 객체의 상태 변화를 영속성 컨텍스트가 자동으로 감지하고, 이를 데이터베이스에 반영하는 것을 의미한다.
더티 체킹은 기본적으로 엔티티 객체의 상태를 추적하기위해 스냅샷을 사용한다.
스냅샷
영속성 컨텍스트에 의해 관리되는 각 엔티티의 초기 상태를 복사한 것이다. 엔티티가 처음 영속성 컨텍스트에 의해 관리될 때, 해당 엔티티의 모든 필드 값들이 스냅샷으로서 저장된다.
이후, 엔티티 객체의 상태가 변경되면 영속성 컨텍스트는 가장 최신의 상태와 스냅샷을 비교하여 어떤 필드가 변경되었는지 검사한다. 이 때, 변경된 필드가 있으면 해당 엔티티는 변경(Dirty)상태로 간주된다.
Q1-1. 강의 자료에 "fetch()에서는 우선 Response 객체를 반환합니다." 라고 되어있어서 조금 헷갈렸다. fetch에서는 Promise 객체 즉, 비동기 연산의 결과를 반환하는 걸로 알았는데 무슨 말일까?
A1-1. fetch()가 반환하는 건 Promise 객체이다. Promise란 비동기 작업이 완료되었을 때의 결과를 나타내는 객체이다. 그니까 fetch로 비동기적으로 HTTP 요청을 보내고 응답을 처리하는데, 그 결과를 가지고 있는 Promise 객체를 반환한다는 것 !
Q1-2. 그렇다면, Response는 뭐지? 어디서 반환되는 거야?
A1-2. 이 Promise가 해결(resolve)되었을 때의 값이 바로 Response 객체이다. fetch()는 비동기 함수라서 바로 결과를 반환할 수 없다. 그래서 바로 Promise를 반환한다. 이 promise는 아직 응답을 받지 못했기 때문에 Pending(대기상태)이다. 이 때 서버가 응답을 보내면 Fulfilled(성공)상태가 되고, Response 객체가 결과로 반환된다. 즉, fetch()가 반환하는 Promise의 결과값(fulfilled value)이 Response 객체라는 것!
그렇기 때문에 .then()을 했을 때 매개변수로 response인 응답이 들어오는 것이다. 그리고, .catch()에는 Promise가 실패했을 때는 Error 객체를 받게 된다.
오늘은 RBF를 하는 금요일이다 ! 매번 RBF 주제를 고민하는 것도 쉽진 않은 것 같다... 그래도 궁금한 거 하나를 탁 정해서 적어놓으면 팀원들이 잘 설명해주서 궁금증이 풀리는 편인 것 같다. 그리고 다른 팀원들의 질문도 들어보면서 옹.. 저런 걸 궁금해할 수도 있군 ! 하는 생각도 들고, 내가 아는 거면 설명하면서 내 머리도 정리할 수 있어서 좋은 것 같다. 편안한 분위기에서 주제에 대해서 얘기하는 이 활동이 꽤 멋있는 것 같다. 호호
오늘은 자습 두 번째 시간 때 강사님이 들어오셨다. 들어오셔서 또 여러 얘기를 나누는데, 팀원분들의 고민(?)들을 듣는데 대단쓰했다... (그들의 고민 조차 멋지다는 마인드) 많은 걸 얘기할 순 없겠지만, 잘하고 열심히 하시는 분들이니 다 잘 되길 바라는 마음이다 !!! 그리고, 강사님이 어제 TIL을 보셨는지 생일이라는 걸 말해주셔(?)서 슬랙에 팀원분들이 다 생일 축하를 해주셨다 ㅋㅋㅋㅋ!!! F는 이런 것에 감동 받아요. 감사합니다.. (소중)
어제 REST API Post로 만들어본 걸 어제 내가 다시 Movie로 만들어보았는데 오늘은 거기에서 더 나아가 fetch를 적용했다. TIL 다 적고 오늘 한 실습 부분(fetch)을 내가 만든 api로 다시 한 번 구현해봐야겠다 ! 동적이다 뭐 이런 거 사실 감이 잘 안 왔는데, 이젠 감이 온다 ! 게다가 fetch()를 왜 쓰는지, 언제 쓰는 건지 등 정리하니까 더더욱 !! 자바 스크립트.. 재밌잖아?!?!
그러고 점심 먹기 한 20분 전..?부터 JPA에 대한 설명이 시작되었는데... 참 산 넘어 산이다 ㅎㅎㅎ 정말 하루하루를 생존하고 있는 나. 그래도 그 날 배운 건 그 날 다 이해하고 넘어가고 싶은데 TIL을 적으면서 그걸 잘 지키고 있는 것 같다. 벌써 또 일주일이 끝났다. 다음주도 열심히 달려야지.. ! 아프지 말고 파이팅합시다 ! 한 주간 고생했다 (self 토닥)
while(true) {
휴~ 어렵다! 오케이 따라잡았서
?? : 아직 어려운 건 시작도 안 했어요 ~
네?
}