코드네임 JY

[스프링 입문] JPA & 스프링 데이터 JPA 본문

백엔드 공부

[스프링 입문] JPA & 스프링 데이터 JPA

영재임재영 2022. 12. 14. 18:31

미리보기

✅ JPA에 대해 알아보자!

✅ 스프링 데이터 JPA에 대해 알아보자!


🍟 JPA가 나오게 된 배경

기존 JDBC 방식에서는 직접적으로 SQL 쿼리를 많이 작성하여 DB와 연결을 진행하였다.

하지만 객체 자체를 직접적으로 DB에 저장할 수는 없었는데, 이를 해결하기 위해 나온 개념이 바로 'ORM' 이다.

 

ORM(Object-Relational Mapping)객체(Object)와 관계형(Relational) 데이터베이스를 매핑해주는 기술이다.

이런 ORM 기술을 구현하기 위해 사용되어 온 프레임워크'Hibernate' 인데, 이후에도 여러 프레임워크가 생겨났다.

(참고 : DataNucleus, EclipseLink 등이 또 다른 JPA 구현체이다. 하지만 Hibernate를 많이 사용하는 편이다!)

따라서 이런 프레임워크들의 표준화가 이루어진 인터페이스'JPA(Java Persistence API)' 라고 한다.

 

JPA를 풀어서 설명하면, '자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스' 라고 말할 수 있다!

그렇다면 Hibernate는 'JPA라는 인터페이스를 구현한 하나의 클래스' 라고 설명할 수 있을 것이다.

JPA의 약자에서 'Persistence' 라는 단어를 볼 수 있는데, 실제 코드를 보면 DB에 저장할 때 'persist( )' 라는 함수를 사용한다.

따라서 위 단어가 쓰인 이유는 자바 객체를 관계형 DB에 '(영속적으로)저장' 한다는 의미가 강하다!

 

ORM의 장점은 개발자가 직접 SQL 쿼리를 작성하지 않아도 된다는 것인데, 예시를 들어 설명해보겠다.

전체 멤버 목록을 가져오고 싶다면, SQL은 'SELECT * FROM MEMBER' 라는 쿼리를 작성해야한다.

하지만 객체를 사용하여 관계형 DB에 접근할 수 있다면, 'member.findAll( )' 라고 적으면 데이터 조회가 끝난다.

(멤버 객체의 이름을 member라고 할 때)

 

즉, 개발자가 SQL 쿼리를 직접 작성하지 않고, 메소드 호출만으로 프레임워크가 적절한 쿼리문을 생성해줘서 동작하는 것이다!

SQL 쿼리들은 중복되는 부분이 상당히 많기 때문에, JPA에서는 코드의 중복을 줄일 수 있다는 것도 큰 장점이다!

코드를 어디에 짜냐면.. DB와의 상호작용이 필요하기 때문에, JDBC나 JPA 기술을 사용한다면 Repository 부분에 연결해야한다!

또한 JDBC는 단순한 DB Connectivity 부분에 의의를 두었다면, JPA 객체를 만들어서 이를 DB에 매핑하는 것이 핵심이다!

참고! Hibernate가 지원하는 메소드 내부에서 JDBC API가 동작하고 있으며, 단지 개발자가 직접 SQL을 직접 작성하지 않을 뿐!

 

  JDBC JPA
데이터베이스 상호작용의 목적 DB Connectivity Bind Java objects to relational DB
데이터베이스와 상호작용 할 때 Using full SQL Queries Using Java syntax (Annotation etc)
코드적인 측면에서 Too many queries & Code duplicate No query. Just calling methods

크게 보면 이 정도로 JDBC와 JPA의 차이점을 정리할 수 있겠다!! 그렇다면 실제 코드는 어떻게 작성하는지 알아보도록 합시다!

 

build.gradle 파일 내 설정
application.properties 파일 내 설정

(여기서 참고! spring-boot-starter-data-jpa 라이브러리는 내부에 jdbc 관련 라이브러리를 포함하고 있음)

@Entity
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    
    ...
}

위에서도 알 수 있듯이, 객체와 관계형 DB는 서로 연동을 염두에 두고 만들어진 것이 아니다.

따라서 관계형 DB에게 객체가 가지고 있는 정보들에 대한 설명이 필요한데, 이는 '엔티티(Entity)' 라는 개념으로 해결할 수 있다.

 

'엔티티(Entity)'는 데이터베이스 테이블에 1:1로 대응되는 하나의 틀이라고 생각하면 된다! (과거 포스팅에서 설명한 붕어빵 틀!!)

JPA를 사용하여 DB와 상호작용하는 것은 '엔티티 객체' 이고, '엔티티' 는 하나의 틀이라고 생각하면 된다.

@Entity라는 어노테이션이 붙으면 JPA가 관리하는 엔티티라는 뜻이다. 엔티티와 테이블을 정확하게 매칭하는 것은 아주 중요하다!

private final EntityManager em;

public JpaMemberRepository(EntityManager em) {
  this.em = em;
}

JPA를 사용하려면 'EntityManager' 라는 객체가 필요하다. 전에 JDBC에서 'DataSource' 객체를 사용했던 것과 비슷하다.

이는 DB 커넥션에 관련된 정보를 가지고 있기 때문에, 반드시 주입받아야 한다.

public Optional<Member> findById(Long id) {
      Member member = em.find(Member.class, id);
      return Optional.ofNullable(member);
      
      // 기존 JDBC Template 방법
      // List<Member> result
      //      = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id);
}

JDBC 코드와 비교하면? 일단 SQL 쿼리를 사용하지 않았다!

그리고 Member 객체를 이용한 find( ) 메소드 접근만으로 SQL 쿼리를 쓴 것과 똑같은 효과를 얻을 수 있다!

🍔 스프링 데이터 JPA

스프링 프레임워크에서 JPA를 조금 더 편리하게 사용할 수 있는 모듈을 하나 만들었는데, 그게 바로 '스프링 데이터 JPA' 이다.

일단 코드 먼저 보면서 이해해보자... 아 코드 그런거 없다... ㅋㅋㅋ 엥 왜? Repository 파일 새로 만들어야하는거 아닌가?

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
    Optional<Member> findByName(String name);
}

사실 스프링 데이터 JPA 방식은 새로운 Repository 파일을 만드는 것이 아니라,

정해진 규칙으로 인터페이스를 선언하기만 해도 자동으로 스프링 내부에서 구현체를 만들어 동작시켜준다.

 

JPA를 사용하려면, Repository 파일을 새로 만들어서 코드를 짜야했는데,

스프링이 JPA를 Repository 형태로 만들어놓은 것을 그대로 사용한다고 생각하면 된다!

 

실제로 JpaRepository에 연결된 인터페이스들이 가지고 있는 함수 목록들을 참고해보면,

위 그림처럼 save, delete, find 등 기본적인 기능들을 모두 포함하고 있다!!

스프링이 제공한 편의 기능으로 개발자들은 편하게 기능을 구현하는데만 집중할 수 있는 것이다.

@Configuration
public class SpringConfig {

    private final MemberRepository memberRepository;
    
    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository);
    }
}

그리고 스프링 데이터 JPA를 사용하면 우리가 사용할 Repository 인터페이스를 스프링 빈으로 자동으로 등록해주기 때문에,

위와 같이 설정 코드를 작성하면 되는 것이다! 아직 기초적인 단계라서 이 정도만 설명하고 넘어가겠다!

 

정리

사실 JDBC, JPA, ORM 등의 개념은 처음에 이해하기 매우 어렵다.

나도 실제로 지금 이 포스팅 하나를 쓰는데 거의 5-6시간을 투자해야했고, 그 중 90%가 거의 개념을 정리하는데 쓴다아악.. 🫠

지금까지 스프링에서 DB에 접근하는 기술을 배웠는데, 크게 3가지 방식이 있다.

 

1️⃣ JDBC(혹은 JDBC Template)에서 SQL 쿼리를 직접 작성하여 DB에 접근

2️⃣ JPA에서 쿼리를 직접 작성하지 않고 객체를 관계형 DB와 매핑하는 방식으로 DB에 접근

3️⃣ 스프링 데이터 JPA에서 만들어진 JpaRepository 인터페이스를 사용해 DB에 접근

 

1️⃣ 번 방식인 JDBC(혹은 JDBC Template)는 위 그림으로 정리하였다.

JDBC는 'SQL 쿼리를 직접 작성하여' + '데이터베이스와 Connect 하는 것' 으로 설명할 수 있다!

 

2️⃣ 번 방식인 JPA는 위 그림으로 정리하였다.

ORM은 '자바 객체와 관계형 DB를 매핑해서 사용할 수 있게 해주는 기술' 인데,

해당 기술을 실제로 구현하기 위해 'Hibernate' 라는 프레임워크(구현체)를 사용하게 되고,

이후 생겨난 많은 프레임워크들의 표준화를 만든 인터페이스가 바로 'JPA' 인 것이다!

 

그리고 Hibernate가 지원하는 메소드 내부에서 JDBC API가 동작하고 있는데,

이를 통해서 데이터베이스와 상호작용할 수 있는 것이다!

(어쨌든 자바에서 DB와 상호작용하려면 JDBC가 필요함!)

 

따라서 JPA 방식에서는 '객체가 관계형 DB와 매핑' + 'SQL 쿼리를 개발자가 직접 작성하지 않아도 됨' 으로 설명할 수 있다!

(객체가 DB에 매핑되었으니, SQL 쿼리를 굳이 작성하지 않아도 객체만으로 DB에 접근할 수 있다는 뜻이다!)

 

3️⃣ 번 방식인 스프링 데이터 JPA는 위 그림으로 정리하였다.

원래는 정확하게 그린다면 위 그림처럼 그리면 안 되는데, 위 그림처럼 이해하면 조금 쉽다!

왜냐면 결국 정해진 규칙으로 인터페이스를 선언해야하는데, 해당 인터페이스가 JpaRepository를 따르기 때문이다.

쉽게 말하면, JPA를 Repository로 구현해서 조금 더 구체적으로 우리 눈에 보이도록 했다고 할 수 있다!

 

위 2️⃣ 번 방식에서 JPA는 인터페이스인데, 이를 구현하려면 'Hibernate' 라는 구현체가 있어야 했다.

하지만 스프링 데이터 JPA 방식은 JPA를 Repository로 구현해놓아서,

이미 만들어진 함수를 간편하게 사용할 수 있다고 이해하면 조금 쉽게 개념을 잡을 수 있겠다!

Comments