Spring Boot_스프링 데이터 JPA

이세미·2023년 5월 22일
0

SpringBoot

목록 보기
21/23
post-thumbnail

스프링 데이터 JPA 사용 시 장점

스프링 부트와 JPA만 사용해도 개발 생산성이 정말 많이 증가하고,
개발해야할 코드도 확연히 줄어든다.

여기에 스프링 데이터 JPA를 사용하면 기존의 한계를 넘어 마치 마법처럼,
리포지토리에 구현 클래스 없이 인터페이스 만으로 개발을 완료할 수 있다.

그리고 반복 개발해온 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공한다.

지금까지 조금이라도 단순하고 반복이라고 생각했던 개발 코드들이 확연하게 줄어든다.

따라서 개발자는 핵심 비즈니스 로직을 개발하는 데 집중할 수 있다.

실무에서 관계형 데이터베이스를 사용한다면 스프링 데이터 JPA는 이제 선택이 아니라 필수이다.

주의: 스프링 데이터 JPA는 편리하게 사용하도록 도와주는 기술이다.
따라서 JPA를 꼭 먼저 학습한 후에 스프링 데이터 JPA를 학습해야 한다.

  • 앞의 JPA 설정을 그대로 사용한다.

직접 해보기

repository에 SpringDataJpaMemberRepository라는 interface를 생성한다.
그리고 JpaRepository를 상속받는다.
인터페이스가 인터페이스를 받을 때는 implements가 아니라 extends를 사용한다.

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

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

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {

첫 번째 매개변수에는 사용하는 entity 즉, Member를 넣고, 두 번째 매개변수에는 Id의 타입 즉, Long을 넣는다.

그리고 interface는 다중상속이 가능하므로, MemberRepository도 함께 상속받는다.

@Override
Optional findByName(String name);

그 다음 findByName을 넣어주면, 끝난다.

지금 현재 SpringDataJpaMemberRepository라는 interface만 존재하는데,
JpaRepository를 받고 있으면

스프링데이터 Jpa가 SpringDataJpaMemberRepository를 보고,
자동으로 구현체를 만들어서 스프링 빈에 자동으로 등록을 해 준다.

이제 작성한 코드를 사용하려면, SpringConfig에서

전의 코드들을 모두 주석처리 하고,

@Configuration
public class SpringConfig {

  private final MemberRepository memberRepository;

  public SpringConfig(MemberRepository memberRepository) {
      this.memberRepository = memberRepository;
  }
@Bean
  public MemberService memberService() {
      return new MemberService(memberRepository);
  }
}

이 코드를 작성해 주었다.
우선,

private final MemberRepository memberRepository;

를 정의하고,

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

injection 해준다.

그러면, 스프링데이터 Jpa가 구현체를 만들어오는 것이 등록이 된다.

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

그리고 MemberService에 의존관계를 setting 해주어야 한다.

**public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}에는 @Autowired를 붙여줘도, 안 붙여줘도 된다. (생성자가 1개이기 때문에)

이렇게 작성을 해주면,
스프링 컨테이너에서 MemberRepository를 찾는다.

현재 등록해놓은 것이 없지만,
아까 만든 interface, SpringDataJpaMemberRepository 덕분에
스프링데이터Jpa가 interface에 대한 구현체를 직접 만들어낸다.

그리고 스프링 빈에 등록을 해준다.

그래서 우리는 springConfig에서 injection을 받아서 등록해 줄 수 있는 것이다.

자 이렇게 SpringConfig에서의 구현까지 끝났으니,
MemberServiceIntegrationTest 회원가입 코드를 돌려보겠다.

코드가 잘 돌아간다.

*사실은 처음에 강의에서 나온 그대로 코드를 작성하여 돌려보았을 때, 계속해서 오류가 나며 회원가입 코드가 돌아가지 않았다.
오류의 내용은 이랬었다.

Parameter 0 of constructor in hello.hellospring.service.SpringConfig required a single bean, but 2 were found:
	- memoryMemberRepository: defined in file [/Users/isemi/Downloads/hello-spring/build/classes/java/main/hello/hellospring/repository/MemoryMemberRepository.class]
	- springDataJpaMemberRepository: defined in hello.hellospring.repository.SpringDataJpaMemberRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration

대체 무슨 말인지 모르겠어서 ChatGpt에게 물어봤더니,
"주입될 MemberRepository 빈이 두 개 이상인 경우, 스프링은 어떤 빈을 주입해야 하는지 결정할 수 없기 때문에 이 오류가 발생합니다."
라는 답변이 왔다.

memoryMemberRepository와,
springDataJpaMemberRepository
이 두 개의 빈이 정의되어 있어서,
SpringConfig 클래스의 생성자 매개변수로 주입될
MemberRepository 빈이 하나만 존재하도록 해야 한다는 것이다.

즉, 두 개의 MemberRepository 빈 중 하나를 사용하도록 스프링에게 명시해야 한다고 하였다.

그렇게 하기 위해선
@Primary 어노테이션 을 사용하여 우선적으로 주입될 빈을 지정하라는 것이다.

난 강의에서 하라는 대로 한 것 밖에 없는데 대체 무슨 빈이 두 개나 주입되었다는 것인지 이해 불가;; 였지만

@Primary 어노테이션을 해당하는 MemberRepository 구현 클래스에 추가하여,
해당 빈이 주입되도록 스프링에게 알려주어야 하니까

현재 구현하고자 하는 클래스인 SpringDataJpaMemberRepository에
@Primary 어노테이션을 추가해 주었다.

(거짓말 안 치고 3시간 넘게 걸려서 오류를 찾았다.; 어이없음)

하여튼 이렇게 해서 정상적으로 코드가 돌아가게 할 수 있었다..

사담 끝----------------

어떻게 이 코드가 잘 실행이 되는지 정리를 해 보자면,

스프링 데이터 JPA가 아까 만든 인터페이스, SpringDataJpaMemberRepository를 보고 객체를 자동 생성해서 스프링 빈으로 자동 등록해준다.

이전에는 직접 구현했었던 save메소드와 같은 것들은 모두
스프링 데이터에 구현이 되어 있다.

SpringDataJpaMemberRepository에서 상속받았던 JpaRepository에 들어가 보면,

위와 같이 기본적인 CRUD와 단순 조회 등을 하는 메소드들이 모두 제공되고 있다.
우리가 머릿속으로 상상할 수 있는,
공통화할 수 있는 메소드들을 모두 공통화해서 제공을 해 주어서,
그냥 가져다가 사용하면 되는 것이다.

스프링 데이터 JPA 제공 기능

  • 인터페이스를 통한 기본적인 CRUD
  • findByName() , findByEmail() 처럼 메서드 이름 만으로 조회 기능 제공
  • 페이징 기능 자동 제공

**참고: 실무에서는 JPA와 스프링 데이터 JPA를 기본으로 사용하고,
복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용하면 된다.
Querydsl을 사용하면 쿼리도 자바 코드로 안전하게 작성할 수 있고,
동적 쿼리도 편리하게 작성할 수 있다.
이 조합으로 해결하기 어려운 쿼리는
JPA가 제공하는 네이티브 쿼리를 사용하거나,
앞서 학습한 스프링 JdbcTemplate를 사용하면 된다.

스프링 DB 접근 기술 정리

  • H2 데이터베이스 설치
  • 순수 Jdbc
  • 스프링 통합 테스트
  • 스프링 JdbcTemplate
  • JPA
  • 스프링 데이터 JPA

0개의 댓글