북스터디 : 스프링 부트 핵심가이드(3)

윤장원·2023년 3월 24일
0

6. 데이터베이스 연동

ORM

ORM은 Object Relational Mapping의 줄임말로 객체 관계 매핑을 의미한다. 자바와 같은 객체지향 언어에서 의미하는 객체(클래스)와 RDB(Relatoinal Database)의 테이블을 자동으로 매핑하는 방법이다. 클래스는 데이터베이스의 테이블과 매핑하기 위해 만들어진 것이 아니기 때문에 RDB 테이블과 어쩔 수 없는 불일치가 존재한다. ORM은 이 둘의 불일치와 제약사항을 해결하는 역할이다. ORM을 이용하면 쿼리문 작성이 아닌 코드(메서드)로 데이터를 조작할 수 있다.

ORM의 장점

  1. ORM을 사용하면서 데이터베이스 쿼리를 객체지향적으로 조작할 수 있다.
  2. 재사용 및 유지보수가 편리하다.
  3. 데이터베이스에 대한 종속성이 줄어든다.

ORM의 단점

  1. ORM만으로 온전한 서비스를 구현하기에는 한계가 있다.
  2. 애플리케이션의 객체 관점과 데이터베이스의 관계 관점의 불일치가 발생한다.

JPA

JPA(Java Persistence API)는 자바 진영의 ORM 기술 표준으로 채택된 인터페이스의 모음입니다. JPA 매커니즘을 보면 내부적으로 JDBC를 사용한다. 개발자가 직접 JDBC를 구현하면 SQL에 의존하게 되는 문제 등이 있어 개발의 효율성이 떨어지는데, JPA는 이 같은 문제점을 보완해서 개발자 대신 적절한 SQL을 생성하고 데이터베이스를 조작해서 객체를 자동 매핑하는 역할을 수행한다.

JPA 기반의 구현체는 대표적으로 하이버네이트(Hibernate), 이클립스 링크(EclipseLink), 데이터 뉴클리어스(DataNucleus)이며, 그중 가장 많이 사용되는 구현체는 하이버네이트이다.

영속성 컨텍스트

영속성 컨텍스트는 애플리케이션과 데이터베이스 사이에서 엔티티와 레코드의 괴리를 해소하는 기능과 객체를 보관하는 기능을 수행한다. 엔티티 객체가 영속성 컨텍스트에 들어오면 JPA는 엔티티 객체의 매핑 정보를 데이터베이스에 반영하는 작업을 수행한다. 이처럼 엔티티 객체가 영속성 컨텍스트에 들어와 JPA의 관리 대상이 되는 시점부터는 해당 객체를 영속 객체라고 부른다.

데이터베이스 연동

프로젝트 생성

의존성 선택 단계에서 다음의 라이브러리를 선택한다. DB는 MariaDB를 사용한다.

  • Developer Tools : Lombok, Spring Configuration Processor
  • Web : Spring Web
  • SQL : Spring Data JPA, MariaDB Driver

Spring Data JPA 의존성을 추가한 후에는 별도의 설정이 필요하다. 즉, 애플리케이션이 정상적으로 실행될 수 있게 연동할 데이터베이스의 정보를 application.properties에 작성해야 한다.

spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=password

spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

1번 줄인 spring.datasource.driverClassName에는 연동하려는 데이터베이스의 드라이버를 정의한다.
2번 줄의 spring.datasource.url 항목에서 마리아DB의 경로임을 명시하고 경로와 데이터베이스명을 입력한다.
3~4번 줄에는 데이터베이스를 설치할 때 설정한 계정 정보를 기입한다.
6~8번 줄은 하이버네이트를 사용할 때 활성화할 수 있는 선택사항이다. 6번 줄의 ddl-auto는 의미 그대로 데이터베이스를 자동으로 조작하는 옵션이다. 여기서 사용할 수 있는 옵션에는 create, create-drop, update, validate, none이 있다.

  • create : 애플리케이션이 가동되고 SessoinFactory가 실행될 때 기존 테이블을 지우고 새로 생성한다.

운영 환경에서는 create, create-drop, update 기능은 사용하지 않는다. 반면 개발 환경에서는 create, update를 사용하는 편이다.

7번 줄의 show-sql은 로그에 하이버네이트가 생성한 쿼리문을 출력하는 옵션이다. 아무 설정이 없으면 저장에 용이한 형태로 출력되기 때문에 사람이 보기에는 불편하게 한 줄로 출력된다. 8번 줄에서 사용한 format_sql 옵션으로 사람이 보기 좋게 포매팅할 수 있다.

엔티티 설계

Spring Data JPA를 사용하면 데이터베이스에 테이블을 생성하기 위해 직접 쿼리를 작성할 필요가 없다. 이 기능을 가능하게 하는 것이 엔티티이다. JPA에서 엔티티는 데이터베이스의 테이블에 대응하는 클래스이다. 엔티티에는 데이터베이스에 쓰일 테이블가 칼럼을 정의한다. 엔티티에 어노테이션을 사용하면 테이블 간의 연관관계를 정의할 수 있다.

엔티티 관련 기본 어노테이션

@Entity

해당 클래스가 엔티티임을 명시하기 위한 어노테이션이다. 클래스 자체는 테이블과 일대일로 매칭되며, 해당 클래스의 인스턴스느 ㄴ매핑되는 테이블에서 하나의 레코드를 의미한다.

@Id

@Id 어노테이션이 선언된 필드는 테이블의 기본값 역할로 사용된다. 모든 엔티티는 @Id 어노테이션이 필요하다.

@GeneratedValue

일반적으로 @Id 어노테이션과 함께 사용된다. 해당 필드의 값을 어떤 방식으로 자동으로 생성할지 결정할 때 사용한다. 값 생성 방식에는 AUTO, IDENTITY, SEQUENCE, TABLE이 있다.

@Column

엔티티 클래스의 필드는 자동으로 테이블 칼럼으로 매핑된다. 그래서 별다른 설정을 하지 않을 예정이라면 이 어노테이션을 명시하지 않아도 괜찮다. 필드에 설정을 더할 때 @Column 어노테이션을 사용한다.(name, nullable, length, unique)

@Transient

엔티티 클래스에는 선언돼 있는 필드지만 데이터베이스에서는 필요 없을 경우 이 어노테이션을 사용해 데이터베이스에서 이용하지 않게 할 수 있다.

리포지토리 인터페이스 설계

Spring Data JPA는 JpaRepository를 기반으로 더욱 쉽게 데이터베이스를 사용할 수 있는 아키텍처를 제공한다. 스프링 부트로 JpaRepository를 상속하는 인터페이스를 생성하면 기존의 다양한 메서드를 쉽게 활용할 수 있다.

엔티티를 데이터베이스의 테이블과 구조를 생성하는데 사용했다면 리포지토리는 엔티티가 생성한 데이터베이스에 접근하는 데 사용된다.

리포지토리를 생성하기 위해서는 접근하려는 테이블과 매핑되는 엔티티에 대한 인터페이스를 생성하고, JpaRepository를 상속받으면 된다.

public interface ProductRepository extends JpaRepository<Product, Long> {

}

JpaRepository를 상속받을 때는 대상 엔티티와 기본값 타입을 지정해야 한다. 생성된 리포지토리는 별도의 메서드 구현 없이도 많은 기능을 제공한다. (find, save, delete)

리포지토리 메서드의 생성 규칙

메서드에 이름을 붙일 때는 첫 단어를 제외한 이후 단어들의 첫 글자를 대문자로 설정해야 JPA에서 정상적으로 인식하고 쿼리를 자동으로 만들어준다. 조회 메서드(find)에 조건으로 붙일 수 있는 몇가지 기능은 다음과 같다.

  • FindBy : SQL문의 where 절 역할
  • AND, OR : 조건을 여러개 설정하기 위해 사용
  • Like/NotLike : SQL문의 like와 동일한 기능을 수행하며, 특정 문자를 포함하는지 여부를 조건으로 추가
  • StartsWith/StartingWith : 특정 키워드로 시작하는 문자열 조건
  • EndsWith/EndingWith : 특정 키워드로 끝나는 문자열 조건
  • IsNull/IsNotNull : 레코드 값이 Null이거나 Null이 아닌 값을 검색
  • True/False : Boolean 타입의 레코드를 검색할 때 사용
  • Before/After : 시간을 기준으로 값을 검색
  • LessThan/GreaterThan : 특정 값(숫자)을 기준으로 대소 비교를 할 때 사용
  • Between : 두 값(숫자) 사이의 데이터를 조회
  • OrderBy : SQL 문에서 order by와 동일한 기능 수행
  • countBy : SQL 문의 count와 동일한 기능을 수행하며, 결괏값의 개수(count)를 추출

DAO 설계

DAO(Data Access Object)는 데이터베이스에 접근하기 위한 로직을 관리하기 위한 객체이다. 비즈니스 로직의 동작 과정에서 데이터를 조작하는 기능은 DAO 객체가 수행한다. 스프링 데이터 JPA에서 DAO의 개념은 리포지토리가 대체하고 있다.

DAO 클래스 생성

DAO 클래스는 일반적으로 '인터페이스-구현체'구성으로 생성한다. DAO 클래스는 의존성 결합을 낮추기 위한 디자인 패턴이며, 서비스 레이어에 DAO 객체를 주입받을 때 인터페이스를 선언하는 방식으로 구성할 수 있다. 예시는 다음과 같다.

ProductDAO 인터페이스

public interface ProductDAO {
	
    Product insertProduct(Product product);
}

ProductDAO 인터페이스의 구현체 클래스

@Component
public class ProductDAOImpl implements ProductDAO {
	
    private final ProductRepository productRepository;
    
    @Autowired
    public ProductDAOImpl(ProductRepository productRepository) {
    	this.productRepository = productRepository;
    }
    
    @Override
    public Product insertProduct(Product product) {
    	Product savedProduct = productRepository.save(product);
        
        return savedProduct;
    }
}

DAO 연동을 위한 컨트롤러와 서비스 설계

데이터베이스와 밀접한 관련이 있는 데이터 액세스 레이어까지는 엔티티 객체를 사용하고, 클라이언트와 가까워지는 다른 레이어에서는 데이터를 교환하는 데 DTO 객체를 사용하는 것이 일반적이다.

반복되는 코드의 작성을 생략하는 방법 - 롬복

롬복(Lombok)은 데이터(모델) 클래스를 생성할 때 반복적으로 사용하는 getter/setter 같은 메서드를 어노테이션으로 대체하는 기능을 제공하는 라이브러리이다. 롬복을 활용하면 다음과 같은 장점이 있다.

  • 어노테이션 기반으로 코드를 자동 생성하므로 생산성이 높아진다.
  • 반복되는 코드를 생략할 수 있어 가독성이 좋아진다.
  • 롬복을 안다면 간단하게 코드를 유추할 수 있어 유지보수에 용이하다.

롬복의 주요 어노테이션

  1. @Getter, @Setter
    클래스에 선언돼 있는 필드에 대한 getter/setter 메서드를 생성한다.

  2. 생성자 자동 생성 어노테이션
    데이터 클래스의 초기화를 위한 생성자를 자동으로 만들어주는 어노테이션은 다음과 같다.

  • NoArgsConstructor : 매개변수가 없는 생성자를 자동 생성한다.
  • AllArgsConstructor : 모든 필드를 매개변수로 갖는 생성자를 자동 생성한다.
  • RequiredArgsConstructor : 필드 중 final이나 @NotNull이 설정된 변수를 매개변수로 갖는 생성자를 자동 생성한다.
  1. @ToString
    toString() 메서드를 생성하는 어노테이션이다. 숨겨야 할 정보가 있다면 exclude 속성을 사용해 특정 필드르 자동 생성에서 제외할 수 있다.

  2. @EqualsAndHashCode
    객체의 동등성(Equality)와 동일성(Identity)을 비교하는 연산 메서드를 생성한다.

  • equals : 두 객체의 내용이 같은지 동등성을 비교
  • hashCode : 두 객체가 같은 객체인지 동일성을 비교
  1. @Data
    @Getter/@Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode를 모두 포함하는 어노테이션이다.

0개의 댓글