TIL - JAVA spring DAY 17

jihan kong·2022년 1월 27일
0

JAVA spring 입문

목록 보기
20/20
post-thumbnail

오늘은 스프링 데이터 JPA를 학습하였다. 이와 동시에 드디어 DB 접근 기술에 대해서 모두 학습하였다. 그동안 배웠던 많은 기술 스택이 있었지만 스프링 데이터 JPA의 편리성과 실용성은 실로 대단했다.

스프링 데이터 JPA

사실 우리가 스프링 부트와 JPA만 사용해도 개발 생산성이 많이 증가했고, 직접 개발해야하는 코드도 눈에 띄게 줄어듬을 확인했다. 그런데 여기에 스프링 데이터 JPA를 사용하게 되었을 때... 그야말로 신세계를 경험했다.

지금까지 우리는 Jdbc도 그렇고 JdbcTemplate, JPA, Memory 등 어떤 기술을 사용하기 위해서 일단 Repository를 철저히 구현했었다. save, findById, findByName, findAll 같은 메소드를 구현해야 그것을 토대로 기술들이 동작하기 때문이었다.

그런데 스프링 데이터 JPA는 Repository에 구현 클래스 없이 인터페이스 하나만으로 개발이 끝났다....(OMG)

반복해서 개발해왔고 기본이라고 생각되었던 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공했다. 이처럼 스프링 부트와 JPA라는 기반 위에서 스프링 데이터 JPA라는 프레임 워크가 더해지니 개발이 정말 쉽고 재밌어졌다. 이렇게 혁신적인 프레임워크이다보니 이제는 실무에서 관계형 데이터베이스를 사용한다면 스프링 데이터 JPA는 이제 필수조건이라고 하셨다.

대체 어떤 놈이길래 이렇게 극찬을 받는지 코드를 통해 한번 알아보자.

먼저, 스프링 데이터 JPA를 사용하기 위해서는 인터페이스를 생성해야한다. 다음과 같이 SpringDataJpaMemberRepository interface 를 생성한다.

SpringDataJpaMemberRepository

package hello.hellospring.repository;

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

import java.util.Optional;
// 1.
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository{
// 2.
    @Override
    Optional<Member> findByName(String name);
}

정말 놀랍게도 위의 내용만으로 구현이 끝이 났다. 그래도 코드를 한번 살펴보자.

  1. interface가 interface를 상속 받을 때는 extends를 사용한다. 또한,
    <T>(Generic) 타입에 들어갈 첫 번째 인자는 member, 두 번째는 식별자(PK)인 id이다. 마지막으로 인터페이스는 다중 상속이 되는 특징을 가지고 있다. 그래서 MemberRepository 를 마지막에 삽입했다.

  2. findByName만 구현했다.

이렇게 빈약(?)해 보이는데 어떻게 동작을 한다는 것일까...? 반신반의한 상태이지만 나중에 설명을 듣기로하고 일단은 SpringConfig 설정을 해보자.

SpringConfig

@Configuration
public class SpringConfig {

    private final MemberRepository memberRepository;

    @Autowired		// 1.
    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Bean			// 2.
    public MemberService memberService() {
        return new MemberService(memberRepository);
    }
}
//    @Bean			// 3.
//    public MemberRepository memberRepository() {
        // return new MemoryMemberRepository();
        // return new JdbcMemberRepository(dataSource);
        // return new JdbcTemplateMemberRepository(dataSource);
        // return new JpaMemberRepository(em);
//    }
//}
  1. 위에서 언급한 대로 그냥 SpringConfig에 memberRepository를 단지 injection 받기만 했다.
  2. MemberService에도 memberRepository 의존관계를 주입해준다.
  3. 따라서 따로 memberRepository 메소드는 필요가 없게 되었다.(주석처리함)

자, 이로써 모든 개발이 끝났다. 우리는 단지 interface를 생성했고, SpringConfig 에 interface에 관한 사항들을 주입시켜 주었을 뿐인데 이것이 과연 잘 동작할 것인가? 통합 테스트를 한번 돌려보자.

모두 녹색 불이 뜬 것을 확인할 수 있다.

그렇다면 이제는 작동원리에 대해 알아볼 차례이다. 일단 SpringDataJpaMemberRepository 는 인터페이스로만 이루어져있는 상태이다. SpringDataJpaJpaRepository가 extends 되어있는 것을 보고, 구현체를 자동으로 만들어준다. 즉, 스프링 빈에 자동으로 등록이 되는 것이다. 결국 우리는 이것을 가져다 injection 해서 쓰기만 한 것이다.
스프링 데이터 JPA가 제공하는 클래스들을 살펴 보자.

(출처: 인프런, 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술, 강사: 김영한)

위와 같이 save, find, delete, paging 메소드 등과 같은 기본 메소드들이 탑재가 되어있는 것을 볼 수 있다. 즉, 공통적으로 우리가 사용하는 메소드들은 이미 디폴트로 제공해준다.
그런데 우리가 기본 메소드 내에서 사용할 수 없는 것들이 있다. 상식적으로 생각해봐도 실무에서는 비즈니스 내용과 성격이 각기 다르기 때문에 수 많은 객체들에 대해서 공통적으로 적용하기란 불가능에 가깝다. 예를 들면, 이름으로 생성자를 만든다고 했을 때, 우리는 단순히 name으로 뽑아냈다고 한다면 다른 데에서는 userName으로 뽑을 수도 있는 것이다.

그래서 우리는 위의 코드에서 interface를 구성할 때, findByName과 같은 생성자를 만들어서 이른바, 커스텀 코드를 만들 수 있다. 이와 같은 원리로 findByEmail() 과 같이 메소드 이름 만으로 조회할 수 있는 기능을 제공해준다.

번외로 실무에서는 JPA와 스프링 데이터 JPA를 기본으로 사용하고, 복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용하면 된다. Querydsl을 사용하면 쿼리를 자바 코드로 안전하게 작성할 수 있고, 동적 쿼리도 편리하게 작성할 수 있다. 만약, 이 조합으로 해결하기 어려운 쿼리는 JPA가 제공하는 네이티브 쿼리(직접 SQL쿼리를 짜는것)를 사용하거나, 앞서 학습한 스프링 JdbcTemplate를 사용하면 된다.
이렇듯, JPA를 실무에서 밀접하게 사용하시는 분들은 JPA, 스프링 데이터 JPA, Querydsl 이 세가지 기술을 조합을 해서 사용한다고 하셨다.

Finally

마침내 스프링 DB의 대단원이 끝이 났다. 스프링을 자유자재로 다룰 수 있기 위해서는 크게 웹 MVC에 대한 개념, DB 접근 기술들, ORM 관련 지식들이 필요하다.(쉽지않다...) 입문 강의는 이러한 기술들을 디테일하게 들여다보기보단 한번 슥 훑고 지나가는 것에 의의를 두어 나와 같은 백엔드 개발 문외한도 알기 쉽고 흥미롭게 공부할 수 있었다. 다음 시간의 AOP에 관한 학습이 끝나면 강의가 모두 끝난다. 그렇지만 이제 입문 단계가 끝났을 뿐이다. 스프링을 제대로 내것으로 만들기 입문을 넘어 실무를 공부하며 스프링 전문가가 되고 싶다.

profile
학습하며 도전하는 것을 즐기는 개발자

0개의 댓글