김영한 개발자님의 스프링 DB 2편 강의를 듣고 ppt 자료를 참고하여 중요한 내용들을 정리했습니다.
앞으로 배울 데이터 접근 기술은 크게 2가지로 분류할 수 있다.
개발자는 SQL만 작성하면 해당 SQL의 결과를 객체로 편리하게 매핑해준다.
JDBC를 직접 사용할 때 발생하는 여러가지 중복을 제거해주고, 기타 개발자에게 여러가지 편리한 기능을
제공한다.
JdbcTemplate
이나 MyBatis
같은 SQL 매퍼 기술은 SQL을 개발자가 직접 작성해야 하지만, JPA를 사용하면 기본적인 SQL은 JPA가 대신 작성하고 처리해준다. 개발자는 저장하고 싶은 객체를 마치 자바 컬렉션에 저장하고 조회하듯이 사용하면 ORM 기술이 데이터베이스에 해당 객체를 저장하고 조회해준다.
JPA
는 자바 진영의 ORM 표준이고, Hibernate
(하이버네이트)는 JPA에서 가장 많이 사용하는 구현체이다.
스프링 데이터 JPA
, Querydsl
은 JPA를 더 편리하게 사용할 수 있게 도와주는 프로젝트이다.
스프링 MVC 1편에서 마지막에 완성한 상품 관리 프로젝트는 메모리에 상품 데이터를 저장했는데, 메모리가 아닌 실제 데이터 접근 기술들을 하나씩 적용해보자.
상품 이름과 가격제한을 적용하여 검색하는 기능이 추가되었다.
@Data // cond -> condition
public class ItemSearchCond {
//상품명의 일부만 포함되어도 검색이 가능해야 한다. ( like 검색)
private String itemName;
private Integer maxPrice;
public ItemSearchCond() {}
public ItemSearchCond(String itemName, Integer maxPrice) {
this.itemName = itemName;
this.maxPrice = maxPrice;
}
}
DTO는 기능은 없고 데이터를 전달만 하는 용도로 사용되는 객체를 뜻한다.
@Data
public class ItemUpdateDto {
private String itemName;
private Integer price;
private Integer quantity;
public ItemUpdateDto() {}
public ItemUpdateDto(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
public class TestDataInit {
private final ItemRepository itemRepository;
//확인용 초기 데이터 추가
//스프링 컨테이너가 완전히 초기화를 다 끝내고, 실행 준비가 되었을 때 발생하는 이벤트
@EventListener(ApplicationReadyEvent.class)
public void initData() {
log.info("test data init");
itemRepository.save(new Item("itemA", 10000, 10));
itemRepository.save(new Item("itemB", 20000, 20));
}
}
@EventListener
대신 @PostConstruct
를 사용할 경우 AOP 같은 부분이 아직 다 처리되지 않은 시점에 호출될 수 있기 때문에, 간혹 문제가 발생할 수 있다.
// Application 실행 클래스
public class ItemServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ItemServiceApplication.class, args);
}
@Bean
@Profile("local")
public TestDataInit testDataInit(ItemRepository itemRepository) {
return new TestDataInit(itemRepository);
}
}
spring.profiles.active=local // or test
/src/main/resources
하위의 application.properties
에 위와 같이 입력하면 스프링은 local 이라는 프로필로 동작한다. 따라서 직전에 설명한 @Profile("local")
가 동작하고, testDataInit
가 스프링 빈으로 등록된다.
이번엔 테스트 폴더인 /src/test/resources
하위의 application.properties
에 test라고 입력하면 테스트할 경우엔 @Profile("local")
프로필 정보가 맞지 않아서 동작하지 않는다. 따라서 테스트에 영향을 주지 않는다.
H2 데이터베이스에 접근해서 item 테이블을 생성하자.
drop table if exists item CASCADE;
create table item
(
id bigint generated by default as identity,
item_name varchar(10),
price integer,
quantity integer,
primary key (id)
);
identity 전략이고 하는데, 기본 키 생성을 데이터베이스에 위임하는 방법이다. MySQL의 Auto Increment
와 같은 방법이다.
여기서 PK로 사용되는 id 는 개발자가 직접 지정하는 것이 아니라 비워두고 저장하면 데이터베이스가 순서대로 증가하는 값을 사용해서 넣어준다.
데이터베이스 기본 키는 다음 3가지 조건을 모두 만족해야 한다.
테이블의 기본 키를 선택하는 전략은 크게 2가지가 있다.
비즈니스에 의미가 있는 키
예: 주민등록번호, 이메일, 전화번호
비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불린다.
예: 오라클 시퀀스, auto_increment, identity, 키생성 테이블 사용
자연 키와 대리 키는 일장 일단이 있지만 될 수 있으면 대리 키의 사용을 권장한다. 예를 들어 자연 키인 전화번호를 기본 키로 선택한다면 그 번호가 유일할 수는 있지만, 전화번호가 없을 수도 있고 전화번호가 변경될 수도 있다.
문제는 주민등록번 호처럼 그럴듯하게 보이는 값이다. 이 값은 null 이 아니고 유일하며 변하지 않는다는 3가지 조건을 모두 만족하는 것 같다. 하지만 현실과 비즈니스 규칙은 생각보다 쉽게 변한다. 주민등록번호 조차도 여러 가지 이유로 변경될 수 있다.
비즈니스 요구사항은 계속해서 변하는데 테이블은 한 번 정의하면 변경하기 어렵다. 그런면에 서 외부 풍파에 쉽게 흔들리지 않는 대리 키가 좋은 선택이다.