공부기록/Study

[자바 웹개발 워크북] 5장 스프링에서 스프링 부트로

메델 2023. 2. 23. 05:56

스프링부트

 

엔터프라이즈급 애플리케이션을 개발하기 위해서 필요한 기능들을 제공하는 개발 도구 

Auto Configuration(자동설정)이 특징(자동 설정: 라이브러리만으로 설정을 인식하려는 특성)

→  추가한 모듈 설정이 전혀 필요하지 않고 설정 자체도 단순

내장 톰캣과 단독 실행 가능한 도구 → 별도의 서버 설정 없이 개발/실행 가능 

 

스프링 부트에서 웹 개발

 

Thymeleaf

 

  • JSP를 대신하는 목적으로 작성된 라이브러
  • JSP와 동일하게 서버에서 결과물을 생성해서 보내는 방식
  • JSP보다 HTML에 가깝게 작성 가능
  • Model로 전달된 데이터를 출력하기 위해서는 HTML 태그 내에 'th:'로 시작하는 속성을 이용하거나 inlining 이용

 

JSON 데이터 만들기

 

  • API 서버는 순수한 데이터만 전송하는 방식
  • ex) 모바일 환경은 앱을 개발하고 네트워크로 데이터를 가져와 이를 앱에서 실행하는 방식
  • 과거에는 주로 XML을 이용했지만 최근에는 JSON을 이용하는 것이 일반적 
  • JSON(JavaScript Object Notation)
    • 데이터(객체)를 자바스크립트의 '객체 표기법'으로 표현한 순수한 문자열
    • JSON은 문자열이기 때문에 데이터 교환 시에 프로그램 언어에 독립적

 

Spring Data JPA

 

JPA를 이용하는 개발의 핵심 → 객체지향을 통해서 영속 계층 처리

JPA를 이용할 때는 테이블과 SQL을 다루는 것  X

→ 데이터 객체를 엔티티 객체로 다루고 JPA로 데이터베이스와 연동해서 관리

→ 엔티티 객체 = PK를 가지는 자바의 객체,  고유의 식별을 위해 @Id를 이용해서 객체를 구분하고 관리

 

Spring Data JPA

 

엔티티 객체를 이용해서 JPA를 이용하는데 편리한 방법들을 제공하는 스프링 관련 라이브러리

Spring Data JPA는 자동으로 객체를 생성하고 이를 통해서 예외 처리 등을 자동으로 처리

→ 이를 위해서 제공되는 인터페이스 JpaRepository

 

Board.java

 

package org.zerock.b01.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long bno;

    private String title;
    private String content;
    private String writer;

}

 

엔티티 객체를 위한 엔티티 클래스는 반드시 @Entity를 적용해야하고 @Id 필요 

 

자동으로 키 생성하는  전략 

 

게시물은 데이터 베이스에 추가될때 생성되는 번호를 이용

IDENTITY DB에 위임 auto_increment
SEQUENCE DB 스퀀스 오브젝트 사용 @SequenceGenerator 필요
TABLE 키 생성용 테이블 사용, 모든 DB에서 사용 @TableGenerator 필요
AUTO 방언에 따라 자동 지정, 기본값   

* 방언 = JPA는 종속되지 않은 추상화된 방언 클래스를 제공하고 있어 변경된 DBMS라도 자동으로 처리할 수 있어 시간과 비용을 아낄 수 있음 

 

 

@MappedSuperClass를 이용한 공통 속성 처리

 

@MappedSuperClass를 이용해서 공통으로 사용되는 칼럼들을 지정하고 해당 클래스를 상속해 처리

AuditingEntityListener를 적용하면 엔티티가 DB에 추가되거나 변경될때 자동으로 시간 값 지정 가능 

AuditingEntityListener를 활성화 시키기 위해서는 프로젝트 설정에 @EnableJpaAuditing 추가 

 

BaseEntity.java

 

@MappedSuperclass
@EntityListeners(value={AuditingEntityListener.class})
@Getter
abstract class BaseEntity {
    @CreatedDate
    @Column(name="regdate", updatable = false)
    private LocalDateTime regDate;

    @LastModifiedDate
    @Column(name="moddate")
    private LocalDateTime modDate;

}

 

Board.java 수정

 

package org.zerock.b01.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Board extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long bno;

    @Column(length=500, nullable=false)
    private String title;

    @Column(length = 2000, nullable = false)
    private String content;

    @Column(length = 50, nullable = false)
    private String writer;

}

 

JpaRepository 인터페이스

 

Spring Data JPA를 이용할 때는 JpaRepository라는 인터페이스를 이용해서 인터페이스 선언만으로 DB 관련 작업을 어느 정도 처리 가능 (개발 단계에서 JpaRepository 인터페이스를 상속하는 인터페이스를 선언하는 것만으로도 CRUD와 Paging 처리 완료)

 

package org.zerock.b01.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.zerock.b01.domain.Board;

public interface BoardRepository extends JpaRepository<Board,Long> {
}

 

JpaRepository 인터페이스를 상속할 때에는 엔티티 타입과 @Id 타입을 지정해 주어야한다는 것을 제외하면 코드 없이 개발 가능

 

Pageable과 Page<E> 타입

 

  • 페이징 처리는 Pageable 타입의 객체를 구성해 파라미터로 전달
  • 파라미터로 Pageable을 이용하면 리턴타입은 Page<T>타입을 이용할 수 있음 단순 목록 뿐만 아니라 페이징 처이에 데이터가 많은 경우 count 처리를 자동으로 실행
  • Pageable은 인터페이스로 설계
  • 일반적으로 PageRequest.of()라는 기능을 이용해서 개발
    • PageRequest.of(페이지 번호, 사이즈): 페이지 번호는 0부터
    • PageRequest.of(페이지 번호, 사이즈, Sort): 정렬 조건 추가
    • PageRequest.of(페이지 번호, 사이즈, Sort Direction, 속성): 정렬 방향과 여러 속성 지정  

 

@Test
public void testPaging(){
    Pageable pageable = PageRequest.of(0, 10, Sort.by("bno").descending());
    Page<Board> result = boardRepository.findAll(pageable);
}

 

JpaRepository는 findAll()이라는 기능을 제공하여 기본적인 페이징 처리 지원 

 

findAll()의 리턴 타입으로 나오는 Page<T> 타입은 내부적으로 페이징 처리가 필요한 여러 정보 처리

(ex. 다음 페이지가 존재하는지, 이전 페이지가 존재하는지, 전체 데이터의 수가 몇 개인지..)

 

@Test
public void testPaging(){
    Pageable pageable = PageRequest.of(0, 10, Sort.by("bno").descending());
    Page<Board> result = boardRepository.findAll(pageable);
    log.info("total count: "+ result.getTotalElements());
    log.info("total pages: "+ result.getTotalPages());
    log.info("page number: "+ result.getNumber());
    log.info("page size: "+ result.getSize());

    List<Board> todoList = result.getContent();
    todoList.forEach(board -> log.info(board));
}

 

쿼리 메소드와 @Query

 

  • 쿼리 메소드는 보통 SQL에서 사용하는 키워드와 칼럼들을 같이 결합해서 구성하면 그 자체가 JPA에서 사용하는 쿼리가 되는 기능 
  • 일반적으로 메소드 이름 'findBy..' 혹은 'get..'으로 시작하고 칼럼명과 키워드를 결합하는 방식으로 구성
  • 쿼리 메소드는 길고 복잡한 메소드를 작성하게 되는 경우가 많음 → 실제 개발에서 잘 안쓰임
  • 쿼리 메소드와 유사하게 별도의 처리 없이 @Query로 JPQL을 이용할 수 있음 

JPQL 

 

  • @Query 어노테이션의 value로 작성하는 문자열을 말함
  • JPA에서 사용하는 쿼리 언어라고 생각하면 됨
  • JPA는 DB에 독립적으로 개발이 가능하므로 특정한 DB에서만 동작하는 SQL 대신에 JPA에 맞게 사용하는 JPQL을 이용하는 것 
  • JPQL은 테이블 대신에 엔티티 타입을 이용하고 칼럼 대신에 엔티티의 속성을 이용해서 작성 
  • SQL을 대신하는 용도로 사용하기 때문에 SQL에 존재하는 여러 키워드나 기능들이 유사하게 제공 
  • 쿼리 메소드가 할 수 없는 기능 제공
    • 조인과 같이 복잡한 쿼리를 실행할 수 있는 기능
    • 원하는 속성들만 추출해서 Object[]로 처리하거나 DTO로 처리하는 기능
    • nativeQuery 속성값을 true로 지정해서 특정 DB에서 동작하는 SQL을 사용하는 기능 

 

Querydsl을 이용한 동작 쿼리 처리

 

Querydsl

 

  • Querydsl은 JPA의 구현체인 Hibernate 프레임워크가 사용하는 HQL을 동적으로 생성할 수 있는 프레임워크지만 JPA지원
  • 자바 코드를 이용 → 타입 안정성 유지한 상태에서 원하는 쿼리 작성 가능
  • Querydsl을 이용하기 위해서는 Q도메인 필요
    • Q도메인:  Querydsl의 설정을 통해서 기존의 엔티티 클래스를 Querydsl에서 사용하기 위해서 별도의 코드로 생성하는 클래스 

기존의 Repository와 Querydsl 연동하는 법

 

Querydsl을 이용할 인터페이스 선언

'인터페이스 이름 + Impl'이라는 이름의 클래스 선언 - 이때 QuerydslRepositorySupport라는 부모 클래스를 지정하고 인터페이스를 구현

기존의 Repository에는 부모 인터페이스로 Querydsl을 위한 인터페이스를 지정 

 

(참고: 자바 웹 개발 워크북)