본문 바로가기
Spring

IoC Container, Bean, JPA

by 스니펫 2023. 11. 4.

3 Layer Architecture

한 개의 클래스에 너무 많은 양의 코드가 존재하기 때문에 코드를 이해하거나 변경하기 어렵다.

이러한 문제점을 해결하기 위해 서버에서의 처리 과정은 크게 Controller, Service, Repository 3개로 분리된다.

  • Controller : 클라이언트의 요청을 받고, 로직 처리는 Service에게 전담 ( Request 데이터가 있다면 같이 전달 ), Service에서 처리 완료된 결과를 클라이언트에게 응답
  • Service : 사용자의 요구사항을 처리 ('비즈니스 로직'), DB 저장 및 조회가 필요할 때는 Repository에게 요청
  • Repository : DB 관리 (연결, 해제, 자원 관리), DB CRUD 작업을 처리

IoC(제어의 역전), DI(의존성 주입)

IoC, DI는 객체지향의 SOLID 원칙 그리고 GoF 의 디자인 패턴과 같은 설계 원칙 및 디자인 패턴이다. ( IoC : 설계 원칙, DI : 디자인 패턴 ) 요리에 비교 해보면 설계 원칙은 맛있는 요리를 만들기 위한 원칙, 디자인 패턴은 요리 레시피 라고 할 수 있다. IoC와 DI는 좋은 코드 작성을 위한 Spring의 핵심 기술 중 하나이다.

DI 패턴을 사용하여 IoC 설계 원칙을 구현하고 있다.

  • 의존성 : 코드끼리 강하게 결합되어 있을 수록 의존성이 강하다고 할 수 있다. 필요로 하는 객체를 해당 객체에 전달하는 주입을 통해 결합을 약화할 수 있다. ( 필드에 직접 주입, 메서드를 통한 주입, 생성자를 통한 주입 )
  • 제어의 역전 : 의존성 주입을 통해 코드 제어의 흐름을 역전 시키는 것

IoC Container와 Bean

  • 빈 (Bean) : Spring 이 관리하는 객체
  • Spring IoC 컨테이너 : 'Bean' 을 모아둔 컨테이너

Spring 'Bean' 등록 방법

‘Bean’으로 등록하고자하는 클래스가 있다면 위에 @Component 를 설정한다.

@Component
public class MemoService { ... }

Spring이 Spring 서버가 뜰 때 IoC 컨테이너에 'Bean'을 저장 해준다. 클래스의 앞글자만 소문자로 변경해서 Spring 'Bean' 이름이 저장된다. ( public class MemoService → memoServce )

// 1. MemoService 객체 생성
MemoService memoService = new MemoService();

// 2. Spring IoC 컨테이너에 Bean (memoService) 저장
// memoService -> Spring IoC 컨테이너

Spring 'Bean' 사용 방법

  •  필드 위에 @Autowired
@Component
public class MemoService {
		
    @Autowired
    private MemoRepository memoRepository;
		
		// ...
}
  •  'Bean'을 주입할 때 사용할 메서드 위에 @Autowired : 객체의 불변성을 확보할 수 있기 때문에 일반적으로는 생성자를 사용하여 DI하는 것이 좋다. 생성자 선언이 1개일 때는 생략 가능
@Component
public class MemoService {

    private final MemoRepository memoRepository;

    @Autowired
    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }
		
		// ...
}

3 Layer Annotation

Controller, Service, Repository의 역할로 구분된 클래스들을 ‘Bean’으로 등록할 때 해당 ‘Bean’ 클래스의 역할을 명시하기위해 사용

  1. @Controller, @RestController
  2. @Service
  3. @Repository

JPA CORE

  • ORM ( Object-Relational Mapping ) : "객체(Object)"지향 언어 (자바, 파이썬) + "관계형(Relational)" 데이터베이스 (H2, MySQL), 객체와 DB의 관계를 매핑 해주어 애플리케이션 단에서의 SQL 작업을 줄여줌
  • JPA ( Java Persistence API ) : 자바 ORM 기술에 대한 대표적인 표준 명세, 애플리케이션과 JDBC 사이에서 동작

Entity

JPA에서 관리되는 클래스 즉, 객체를 의미한다. Entity 클래스는 DB의 테이블과 매핑되어 JPA에 의해 관리된다.

// Entity 클래스 만들기

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Table(name = "memo") // 매핑할 테이블의 이름을 지정 (default: Entity 명)
public class Memo {
    @Id // 테이블의 기본 키를 지정, 식별자 역할을 수행
    // @GeneratedValue 옵션을 추가하면 기본 키 생성을 DB에 위임할 수 있음
    @GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increment 조건이 추가
    private Long id;

    // @Column: 필드와 매핑할 테이블의 컬럼을 지정할 수 있음
    // nullable: null 허용 여부
    // unique: 중복 허용 여부 (false 일때 중복 허용)
    @Column(name = "username", nullable = false, unique = true) 
    private String username;

    // length: 컬럼 길이 지정, default 길이는 255
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;
}

영속성 컨텍스트

Persistence(영속성, 지속성) : 객체가 생명(객체가 유지되는 시간)이나 공간(객체의 위치)을 자유롭게 유지하고 이동할수 있는 객체의 성질이다.

영속성 컨텍스트는 Entity 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간이다. JPA는 영속성 컨텍스트에 Entity 객체들을 저장하여 관리하면서 DB와 소통한다. 영속성 컨텍스트에 접근하여 Entity 객체들을 조작하기 위해서는 EntityManager가 필요하다. EntityManager는 EntityManagerFactory를 통해 생성하여 사용할 수 있다. EntityManagerFactory는 일반적으로 DB 하나에 하나만 생성되어 애플리케이션이 동작하는 동안 사용된다.

JPA의 트랜잭션

트랜잭션은 DB 데이터들의 무결성과 정합성을 유지하기 위한 하나의 논리적 개념으로 여러 개의 SQL이 하나의 트랜잭션에 포함될 수 있다. 모든 SQL이 성공적으로 수행이 되면 DB에 영구적으로 변경을 반영하지만 SQL 중 단 하나라도 실패한다면 모든 변경을 되돌린다.

Spring Data JPA

SpringBoot 환경에서는 EntityManagerFactory와 EntityManager를 자동으로 생성해준다. ( application.properties에 DB 정보를 전달해 주면 이를 토대로 EntityManagerFactory가 생성됨 )

  • build.gradle 에 jpa 추가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  • application.properties : Hibernate 설정
spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

Spring Data JPA에서는 JpaRepository 인터페이스를 구현하는 클래스를 자동으로 생성

JpaRepository 등록

  • JpaRepository<"@Entity 클래스", "@Id 의 데이터 타입">를 상속받는 interface 로 선언
  • Spring Data JPA에 의해 자동으로 Bean 등록
  • 제네릭스의 @Entity 클래스 위치에 Memo Entity를 추가했기 때문에 해당 MemoRepository는 DB의 memo 테이블과 연결되어 CRUD 작업을 처리하는 인터페이스가 되었음

JPA Auditing

Spring Data JPA에서는 시간에 대해서 자동으로 값을 넣어주는 기능인 JPA Auditing을 제공

@Getter
// JPA Entity 클래스들이 해당 추상 클래스를 상속할 경우 createdAt, modifiedAt 처럼 추상 클래스에 선언한 멤버변수를 컬럼으로 인식할 수 있음
@MappedSuperclass
// 해당 클래스에 Auditing 기능을 포함시켜 줌
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {
	
    // Entity 객체가 생성되어 저장될 때 시간이 자동으로 저장
    @CreatedDate
    @Column(updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime createdAt;

    // 조회한 Entity 객체의 값을 변경할 때 변경된 시간이 자동으로 저장
    @LastModifiedDate
    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime modifiedAt;
}

@Temporal은 날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용하며 DB에는  Date(날짜), Time(시간), Timestamp(날짜와 시간)라는 세 가지 타입이 별도로 존재

  • DATE : ex) 2023-01-01
  • TIME : ex) 20:21:14
  • TIMESTAMP : ex) 2023-01-01 20:22:38.771000

@SpringBootApplication 이 있는 class에 @EnableJpaAuditing 추가 ( JPA Auditing 기능을 사용하겠다는 정보를 전달 )

Query Methods

Spring Data JPA에서는 메서드 이름으로 SQL을 생성할 수 있는 Query Methods 기능을 제공한다. JpaRepository 인터페이스에서 해당 인터페이스와 매핑되어있는 테이블에 요청하고자하는 SQL을 메서드 이름을 사용하여 선언할 수 있다.

public interface MemoRepository extends JpaRepository<Memo, Long> {
    // Memo 테이블에서 ModifiedAt 을 기준으로 전체 데이터를 내림차순으로 가져오는 SQL을 실행하는 메서드를 생성
    List<Memo> findAllByOrderByModifiedAtDesc();
}

SimpleJpaRepository 클래스가 생성될 때 위처럼 직접 선언한 JpaRepository 인터페이스의 모든 메서드를 자동으로 구현해준다. JpaRepository 인터페이스의 메서드 즉, Query Methods는 개발자가 이미 정의 되어있는 규칙에 맞게 메서드를 선언하면 해당 메서드 이름을 분석하여 SimpleJpaRepository에서 구현이 된다.

cf) List<Memo> findAllByUsername(String username);

'Spring' 카테고리의 다른 글

정적 컨텐츠, MVC, API  (0) 2024.06.24
라이브러리, view 환경설정  (0) 2024.06.23
MySQL 실행 오류, 데이터 처리  (0) 2023.11.03
Spring Boot  (0) 2023.11.01