오늘은 지난번에 알아보았던 JPA를 스프링에서 어떻게 적용하는 지 함께 알아보겠습니다.

저는 Spring Boot를 이용해서 실습을 진행해보겠습니다.

본 실습은 jojoldu님의 블로그를 보면서 진행했습니다. (https://jojoldu.tistory.com/251?category=635883)

1.Dependency 추가

먼저 저는 maven을 사용하므로 pom.xml에 관련 dependency를 넣어주겠습니다!

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

data jpa는 JPA관련이고, spring-boot-starter-test는 테스트 관련인데 H2는 무엇인지 들어보셨나요?

H2는 MySQL, oracle, 등과 같은 평범한 DB이지만, 초경량이라는 점과 In Memory DB 를 지원하는 점이 특징입니다.

그래서 타 DB에 비해 속도가 빠르지만 지속성이 없기때문에?( 컴퓨터 종료 혹은 메모리 종료시 내용이 날라감.) 테스트용으로 매우 좋고, 캐싱용으로도 많이 사용된다고 합니다. 더 자세한 설명은 Google 혹은위키로.....


다음은 lombok이라는 라이브러리도 들어보신 분도 많겠지만, 아직 모르는 분도 계실 거라고 생각합니다. lombok 은 자바에서 클래스를 생성할 때 보통 반복적으로 적게되는 ToString, Getter, Setter, 등을 Annotation 하나로 간단히 처리해줍니다.

@Getter
@Setter
public class User {
    private String userName;
    private String userAge;
}

이런 식으로 작동한답니다.

lombok은 다른 라이브러리와 다르게 dependency만 추가해준다고 바로 적용되지않고, IDE에 적용을 추가적으로 해줘야하는데, 이 설치법과 lombok을 사용할 때 주의사항은 Hyoj 님의 블로그에 설명이 매우 잘되어있어서 링크로 대체하겠습니다.


2. Entity 클래스 생성

이제 드디어 JPA를 테스트해보기위한 Entity클래스를 하나 만들어보겠습니다!

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString
@Getter
@Entity
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 20, nullable = false)
    private String name;

    @Column(length = 20, nullable = false, unique = true)
    private String phone;

    @Builder
    public Customer(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }

}

Annotation이 너무 많아서 처음엔 조금 헷갈리실 수도 있습니다. 혹시 lombok을 사용하지 않으시는 분들은 코드내의 @ToString, @Getter, @Builder를 빼고 직접 생성해주시고 나서 진행해주세요!

Customer 에는 총 3개의 필드가 존재합니다.

  • id 필드는 @id 를 사용하여 기본키(PK)로 지정합니다. 이때 키를 직접할당하는 방식이 아닌, 자동으로 생성되도록 하기위해 @GeneratedValue를 사용합니다.

    GenerationType.IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 방식이고, 다른 방식들은

    • IDENTITY
    • SEQUENCE
    • TABLE
    • AUTO

    정도가 있습니다.

  • name, phone 필드의 @Column Annotation은 데이터베이스 컬럼으로 지정해줍니다. 안에 lengthnullable 또는 unique와 같은 설정들이 가능합니다. 이외에도 다영한 설정이 들어갈 수 있습니다.

    또한, @Column 을 생략할 경우 필드명을 사용하여 컬럼명과 매핑하기 때문에 만약 DB가 대소문자를 구분하는 경우에는 반드시 @Column Annotation을 사용하는 것이 좋습니다.

3. Repository 클래스 생성

JPA에서는 단순히 Repository 인터페이스를 생성한후 JpaRepository<Entity, 기본키 타입> 을 extends하면 기본적인 Create, Read, Update, Delete가 자동으로 생성됩니다! 그래서 저흰 그냥 인터페이스를 만들고, 상속만 잘해주면 기본적인 동작을 테스트해볼 수 있는거죠 :D

@Repositoryd
public interface CustomerRepository extends JpaRepository<Customer, Long>{
    //비워있어도 잘 작동함.
    // long 이 아니라 Long으로 작성. ex) int => Integer 같이 primitive형식 사용못함
}

4. Test 코드 작성 및 테스트

이제 JUnit 을 이용하여 테스트를 작성해봅니다. 테스트 코드는

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest
public class CustomerRepositoryTests {

    @Autowired
    CustomerRepository customerRepository;

    @Test
    public void testCustomerRepository(){
        Customer customer = Customer.builder().name("크리스").phone("010-1224-1225").build();
        customerRepository.save(customer);

        List<Customer> customerList = customerRepository.findAll();

        Customer chris = customerList.get(0);
        assertThat(chris.getName(), is("크리스"));
        assertThat(chris.getPhone(), is("010-1224-1225"));
    }

    @After
    public void deleteAll() {
        customerRepository.deleteAll();
    }

}

이런 식으로 작성되어 있습니다.

테스트 클래스를 우클릭 후 Run As 에서 JUnit Test를 눌러서 테스트를 진행하였더니,

테스트에 성공하였습니다! :-)

1545273276797.png

4. Repository 수정

이번에는 CustomerRepository에 수정을 가해서 테스트 코드의 findAll() 부분을 findByName 혹은findByPhone`으로 바꿔보기위해 CustomerRepository부분을 수정해보겠습니다.

쿼리를 유추할 수 있는 메소드의 이름으로 쿼리를 정의하여주면 자동으로 이에 맞는 쿼리를 실행하여 줍니다.

먼저 CustomerRepository를 수정합니다.

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long>{
    public List<Customer> findByName(String name);
    public List<Customer> findByPhone(String phone);
    //like검색도 가능
    public List<Customer> findByNameLike(String keyword);
}

findBy뒤에 컬럼명을 붙여주면 이를 이용한 검색이 됩니다. 따로 내부 구현을 하지 않아도 알아서 마법처럼 작동합니다!

이외에도 메소드 생성시 다양한 키워드들을 지원하는 데 대표적으로는

  • And
  • Or
  • Is, Equals
  • LessThan, LessThanEqual
  • GreaterThan, GreaterThanEqual
  • 기타 등등( 아라한사님이 번역해주신 스프링 데이터 JPA 레퍼런스 를 보시면 더 많은 키워드와 정보를 얻을 수 있습니다. )

5.컨트롤러를 이용한 테스트

이번에는 JUnit 이 아니라 PostMan을 이용해서 작성한 메소드들이 작동하는 지 확인해보겠습니다.

먼저 Controller를 작성해야겠죠?

@RestController
@EnableAutoConfiguration
@RequestMapping(value = "/customer")
public class CustomerController {

    @Autowired
    CustomerRepository customerRepository;

    @PostMapping("/")
    public @ResponseBody List<Customer> createCustomer(@RequestBody Map<String,String> param){
        String name = param.get("name");
        String phone = param.get("phone");
        Customer customer = Customer.builder().name(name).phone(phone).build();
        customerRepository.save(customer);

        return customerRepository.findAll();
    }
}

이렇게 확인을 위해 간단히 만들어줬습니다. 이제 PostMan으로 요청을 보내보겠습니다.

1545279854420.png

이렇게 응답이 오는 것을 확인할 수 있습니다. 이 상태에서 phone은 유니크이므로 변경한 후 한번 더 요청을 보내면 id2인 데이터가 하나 더 생겨있겠죠?

1545279985811.png

저희가 원하는 결과가 나온 걸 확인할 수 있습니다!

다른 메소드들도 테스트해보면 잘 작동합니다.

직접 사용해보니 JPA가 MyBatis 에 비하여 왜 더 생산속도가 빠른지 느낄 수 있었습니다.

긴 글 읽어주셔서 감사합니다! :D

모두 좋은하루 되세요

샘플코드는 여기서 보실 수 있어요!
https://github.com/junwoochoi/spring-jpa-practice-sample

아직 모르는게 많아 게시글에 잘못된 정보가 있을 수 있습니다. 혹시 잘못된 정보가 있다면, 댓글 혹은 메일로 알려주시면 최대한 빨리 수정하겠습니다!