Back-End/Spring (SpartaCC)

[Spring] 스파르타코딩클럽 Spring공부(3) - 3 Layer Architecture, JPA

남건욱 2023. 6. 20. 00:30
반응형
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 Container, Bean
DI를 사용하기 위해서는 객체 생성이 우선 되어야 한다. 그러면 이 객체는 누가 언제 생성해야 할까?

바로 Spring 프레임워크가 필요한 객체를 생성하고 관리하는 역할을 대신해 준다.

 

- 빈 (Bean) : Spring이 관리하는 객체

- Spring IoC 컨테이너 : 'Bean'을 모아둔 컨테이너

 

 

 

 

 

Bean 등록 방법
@Component
public class HelloService{}

- Bean으로 등록하고자 하는 클래스 위에 설정한다.

- Spring 서버가 뜰 때 IoC컨테이너에 Bean을 저장해 준다.

- 클래스의 앞글자만 소문자로 변경하여 사용된다. HelloService -> helloService

 

 

 

 

 

Bean 사용 방법
@Component
public class HelloService{

	@Autowired
    private HelloRepository helloRepository;
    
    ///...

}

위처럼 필드 위에 @Autowired를 선언해 준다. 이렇게 선언하게 되면 Spring에서 IoC 컨테이너에 저장된 Bean을 해당 필드에 의존성을 주입해 준다.

- Autowired 적용 조건 : Spring IoC컨테이너에 의해 관리되는 클래스에서만 가능하다.

- AUtowired 생략 조건 : Spring4.3 버전부터 @Autowired 생략이 가능하다. 단 생성자 선언이 1개 일 때만 가능하다.

 

 

 

 

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

Spring 3 Layer Annotation은 Controller, Service, Repository의 역할로 구분된 클래스들을 Bean으로 등록할 때 해당 Bean 클래스의 역할을 명시하기 위해 사용된다. 위와 같은 Annotation은 모두 @Component가 추가되어 있다.

 

 

 

 

ORM
ORM : Object-Relational Mapping

Object : 객체지향 언어 (자바, 파이썬)

Relational : 관계형 데이터베이스 (H2, MySQL)

 

- 반복적이고 번거로운 애플리케이션 단계에서의 SQL 작업을 줄여주기 위해 ORM(객체 관계 매핑) 기술들이 등장하게 됐다.

- 객체 즉, 자바의 클래스와 DB의 데이터를 직접 매핑하려면 앞서 살펴본 것처럼 매우 번거롭고 많은 작업들이 필요했지만 ORM을 사용하면 이를 자동으로 처리하여 준다.

 

 

 

 

 

 

JPA
JPA : Java Persistence API

- 자바 ORM 기술에 대한 표준 명세

- JPA는 애플리케이션과 JDBC사이에서 동작되고 있다.

- JPA를 사용하면 DB연결 과정을 직접 개발하지 않아도 자동으로 처리해 준다.

- 객체를 통해 간접적으로 DB데이터를 다룰 수 있기 때문에 매우 쉽게 DB작업을 처리할 수 있다.

 

 

 

 

 

Entity
Entity : JPA에서 관리되는 클래스 즉, 객체를 의미한다.

- Entity 클래스는 DB의 테이블과 매핑되어 JPA에 의해 관리된다.

 

 

 

 

 

영속성 콘텍스트
Persistence를 한글로 번역하면 영속성, 지속성 이라는 뜻이다.

- Entity 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간이다.

- 개발자들은 이제 직접 SQL을 작성하지 않아도 JPA를 사용하여 DB에 데이터를 저장하거나 조회할 수 있으며, 수정, 삭제 또한 가능하다.

- JPA는 영속성 콘텍스트에 Entity 객체들을 저장하여 관리하면서 DB와 소통한다.

 

 

 

 

트랜잭션
DB데이터들과의 무결성과 정합성을 유지하기 위한 하나의 논리적인 개념이다.

- DB의 데이터들을 안전하게 관리하기 위해서 생겨난 개념이다.

- 가장 큰 특징은 여러 개의 SQL이 하나의 트랜잭션에 포함될 수 있다는 점이다.

- 모든 SQL이 성공적으로 수행이 되면 DB에 영구적으로 변경을 반영하지만 SQL 중 단 하나라도 실패한다면 모든 변경을 되돌린다.

 

 

 

 

 

ActionQueue
JPA의 트랜잭션에서는 JPA가 트랜잭션처럼 SQL을 모아서 한번에 DB에 반영한다고 알게됐다.

- JPA는 이를 구현하기 위해 쓰기 지연 저장소를 만들어 SQL을 모아두고 있다가 트랜잭션을 commit 후 한 번에 DB에 반영한다.

 

 

 

 

 

Spring Data JPA
@PersistenceContext
EntityManager em;

- SpringBoot 환경에서는 ENtityManagerFactory와 EntityManager를 자동으로 생성해 준다.

- application.properties에 DB정보를 전달해 주면 이를 토대로 EntityManagerFactory가 생성된다.

- @PersistenConext 애너테이션을 사용하면 자동으로 생성된 EntityManager를 주입받아 사용이 가능하다.

 

 

@Test
@Transactional 
@Rollback(value = false) // 테스트 코드에서 @Transactional 를 사용하면 테스트가 
						// 완료된 후 롤백하기 때문에 false 옵션 추가
@DisplayName("메모 생성 성공")
void test1() {
    Memo memo = new Memo();
    memo.setUsername("Robbert");
    memo.setContents("@Transactional 테스트 중!");

    em.persist(memo);  // 영속성 컨텍스트에 메모 Entity 객체를 저장합니다.
}

- JPA를 사용하여 DB에 데이터를 저장, 수정, 삭제하려면 트랜잭션 적용이 반드시 필요하다.

  (@Transactional을 사용하면 된다.)

 

 

 

 

 

 

 

영속성 콘텍스트와 트랜잭션의 생명주기

- 스프링 컨테이너 환경에서는 영속성 콘텍스트와 트랜잭션의 생명주기가 일치한다.

- 트랜잭션에 유지되는 동안은 영속성 콘텍스트도 계속 유지가 되기 때문에 영속성 컨텍스트 기능을 사용할 수 있다.

- 작성한 코드 메서드에 트랜잭션이 적용되지 않으면 영속성 콘텍스트가 유지되지 못해 오류가 발생한다.

 

 

 

 

 

 

 

Spring Data JPA란?
JPA를 쉽게 사용할 수 있게 만들어놓은 하나의 모듈이다.

- JPA를 추상화시킨 Repository 인터페이스를 제공한다.

- Repository 인터페이스는 Hibernate와 같은 JPA구현체를 사용해서 구현한 클래스를 통해 사용된다.

 

 

 

 

Spring Data JPA 사용방법
JpaRepository 등록
public interface HelloRepository extends JpaRepository<Hello, Long>{

}

- JpaRepository <"@Entity 클래스", "@Id의 데이터 타입">을 상속받는 interface로 선언한다.

- Spring Data JPA에 의해 자동으로 Bean 등록이 된다.

- 제네릭스의 @Entity 클래스 위치에 Hello Entity를 추가했기 때문에 해당 HelloRepository는 DB의 Hello 테이블과 연결되어 CRUD작업을 처리하는 인터페이스가 되었다.

 

 

 

 

 

 

 

JPA Auditing 
Timestamped
데이터의 생성(created_at), 수정(modified_at) 시간은 포스팅, 게시글, 댓글 등 다양한 데이터에
매우 자주 활용된다. 각각의 Entity의 생성 수정 시간을 매번 작성하는건 매우 비효율적이다.

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

 

 

 

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {

    @CreatedDate
    @Column(updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime modifiedAt;
}

@MappedSuperclass

- JPA Entity 클래스들이 해당 추상 클래스를 상속할 경우 createdAt, modifiedAt처럼 추상 클래스에 선언한 멤버변수를 칼럼으로 인식할 수 있다.

 

@EntityListeners(AuditingEntityListener.class)

- 해당 클래스에 AUditing 기능을 포함시켜 준다.

 

@CreatedDate

- Entity 객체가 생성되어 저장될 때 시간이 자동으로 저장된다.

- 최초 생성 시 간이 저장되고 그 이후에는 수정되면 안 되기 때문에 updateble = false 옵션을 추가한다.

 

@LastModifiedDate

- 조회한 Entity 객체의 값을 변경할 때 변경된 시간이 자동으로 저장된다.

- 처음 생성 시간이 저장된 이후 변경이 일어날 때마다 해당 변경시간으로 업데이트된다.

 

@Temporal

- 날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용한다.

- DB에는 Date(날짜), Time(시간), Timestamp(날짜와 시간)이라는 세 가지 타입이 별도로 존재한다.

 

- @SpringBootApplication이 있는 클래스에 @EnableJpaAuditing을 추가해 준다.

 

 

 

 

 

 

Query Methods
Spring Data JPA에서는 메서드 이름으로 SQL을 생성할 수 있는 Query Methods 기능을 제공한다.

- JpaRepository 인터페이스에서 해당 인터페이스와 매핑되어 있는 테이블에 요청하고자 하는 SQL을 메서드 이름을 사용하여 선언할 수 있다.

 

 

public interface MemoRepository extends JpaRepository<Hello, Long> {
    List<Hello> findAllByOrderByModifiedAtDesc();
}

- SimpleJpaRepository클래스가 생성될 때 위처럼 직접 선언한 JpaRepository 인터페이스의 모든 메서드를 자동으로 구현해 준다.

- 인터페이스에 필요한 SQL에 해당하는 메서드 이름 패턴으로 메서드를 선언하기만 하면 따로 구현하지 않아도 사용할 수 있다.

- findAllByOrderByModifiedAtDes 해당 메서드 이름은 Hello 테이블에서 ModifiedAt 즉, 수정 시간을 기준으로 전체 데이터를 내림차순으로 가져오는 SQL을 실행은 메서드를 생성할 수 있다.

 

List <Hello> findAllByUsername(String username)

- 이렇게 Query Method를 선언했을 경우 ByUsername에 값을 전달해줘야 하기 때문에 파라미터에 해당값의 타입과 변수명을 선언해 준다.

- Query Methods는 메서드의 파라미터를 통해 SQL에 필요한 값을 동적으로 받아 처리할 수 있다.

 

 

 

 

정리

천천히 배워나가고 있다. 처음에는 수동으로 받아오는 방식을 사용하고 점점 더 편리하게 구현하도록 배웠다. 하나의 클래스에 모든 동작을 포함시키지 않고 3개의 컨트롤러, 서비스, 레포지토리로 나누어 관리하니 더욱 깔끔하고 기능들을 수정해 나갈 때 편리하게 이용이 가능해졌다. Spring에는 정말 많은 기능들이 포함되어 있는 것 같다. 중요한 개념인 IoC와 DI를 잘 숙지하도록 하고 JPA 사용에 익숙해지도록 노력해야겠다. 아직 헷갈리는 개념들이 너무 많다. 

반응형
프로필사진

남건욱's 공부기록