12월 2일 토요일

장숭혁·2023년 12월 2일
0

TIL작성

목록 보기
23/60

@PostMappting url 엔드포인트 호출되고난 후 과정 알아보기

@PostMapping("/products")
    public ProductResponseDto createProduct(@RequestBody ProductRequestDto requestDto){
        return productService.createProduct(requestDto);
    }

🚛 호출되고 리턴되는 과정

  • 스프링 프레임워크에서 @PostMapping으로 지정된 엔드포인트의 메서드가 호출되면, 해당 메서드는 클라이언트로부터 들어온 HTTP 요청을 처리하게 된다.

  • @RequestBodyHTTP 요청의 본문(body)에 있는 데이터를 해당 메서드의 매개변수로 매핑한다.

  • 메서드가 리턴하는 값은 스프링의 MVC (Model-View-Controller) 모델에 따라 처리된다.

  • productService의 createProduct 메소드에서 ProductRequestDto를 매개변수로 받아 ProductRepository를 통해 저장한다. 그리고 생성된 Product를 ProductResponseDto로 변환하여 반환한다.

    🚧 Service 비즈니스 로직 처리 과정 중 매커니즘

  • Repository (ProductRepository):
    JPA의 JpaRepository를 상속하며, 데이터베이스에 접근하여 CRUD(Create, Read, Update, Delete) 연산을 수행한다.
    ProductRepository는 Product 엔티티와 상호작용하여 영속성 컨텍스트를 통해 데이터베이스에 저장된 엔티티를 관리한다.

  • 영속성 컨텍스트 (Persistence Context):
    영속성 컨텍스트는 JPA가 엔티티를 관리하는 데 사용하는 환경이다. 이 컨텍스트에서 엔티티들은 영구 저장소(데이터베이스)와 상호작용하기 전까지 메모리에 유지된다.

  • productRepository.save() 메서드를 호출할 때, JPA는 Product 엔티티를 영속성 컨텍스트에 저장하고, 이후에 데이터베이스에 저장된다.

  • JPA와 데이터베이스 상호작용: JpaRepository는 JPA의 구현체로, 데이터베이스와의 상호작용을 추상화한다. save() 메서드는 새로운 엔티티를 데이터베이스에 저장하거나 이미 존재하는 엔티티를 수정할 수 있다.
    Product 엔티티를 데이터베이스에 저장하거나 검색할 때, JPA는 해당하는 SQL을 생성하여 데이터베이스와 통신한다.

Entity와 Entity Context

Entity (엔티티):

엔티티는 데이터베이스 테이블에 매핑되는 객체이다. 이 코드에서 Product 클래스가 해당된다. 엔티티 클래스는 @Entity 어노테이션을 통해 정의되며, 데이터베이스의 각 행과 일치하는 필드들을 포함한다.

Entity Manager와 Persistence Context:

JPA에서는 Entity Manager가 엔티티를 관리하고, Persistence Context를 유지한다. Persistence Context는 엔티티의 생명 주기를 추적하고 엔티티와 데이터베이스 간 변경 사항을 관리한다.
EntityManager는 데이터베이스 트랜잭션 단위에서 엔티티를 생성, 수정, 삭제 등의 작업을 수행하고, 영속성 컨텍스트를 통해 데이터베이스와 통신한다.

트랜잭션이란?

  • 데이터 베이스 관리 시스템에서 수행되는 작업의 단위를 말한다.
  • JPA에서 트랜잭션은 데이터베이스 작업을 단일 논리적 단위로 묶는 것을 말한다.
  • 엔티티 매니저를 통해 데이터베이스 작업을 그룹화하여 일관성, 안정성, 격리성, 지속성을 보장한다.

🥅 트랜잭션의 목표

  • ACID를 준수하는것(Atomic, Consistency, Isolation, Durability)
    • 원자성 : 트랜잭션은 모든 작업을 완전히 수행하거나 아무것도 수행하지 않는 두 상태 중 하나로만 전이된다. 작업 중 하나라도 실패하면 전체 트랜잭션은 롤백되어 이전 상태로 돌아간다.
    • 일관성 : 트랜잭션 전후에 데이터베이스는 일관된 상태를 유지해야한다.
    • 격리성 : 동시에 실행되는 여러 트랜잭션이 서로 영향을 주지 않도록 격리되어야 한다. 한 트랜잭션의 결과가 다른 트랜잭션에 영향을 주지 않고 독립적으로 수행되어햐 한다.
    • 지속성 : 트랜잭션이 성공적으로 완료된 후에 그결과가 영구적으로 저장되어야 한다.시스템에 문제가 발생해도 트랜잭션의 결과는 유지되어야 한다.
  • JPA에서는 트랜잭션을 시작하고 커밋, 롤백 하여 데이터베이스 작업을 관리하고, 데이터의 무결성을 보장하여 데이터베이스 상태를 일관적으로 유지한다.

🛢 데이터베이스 그룹화

  • 고객 정보를 업데이트하기 위해서는 여러 데이터베이스 테이블에 걸쳐 작업을 수행해야 할 수 있다. 고객의 주문 내역을 변경하거나 고객 정보 자체를 수정한다면 , 주문 테이블과 고객 정보 테이블 모두에 대한 작업이 필요할 수 있다.
    이럴때 , JPA에선 이 작업들을 단일 트랜잭션으로 묶어서 처리한다.
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();

try {
    transaction.begin(); // 트랜잭션 시작

    // 고객 정보 업데이트 작업 수행
    Customer customer = entityManager.find(Customer.class, customerId);
    customer.setName("새로운 이름");
    entityManager.merge(customer);

    // 주문 정보 업데이트 작업 수행
    Order order = entityManager.find(Order.class, orderId);
    order.setStatus("배송중");
    entityManager.merge(order);

    transaction.commit(); // 트랜잭션 커밋
} catch (Exception e) {
    if (transaction != null) {
        transaction.rollback(); // 예외 발생 시 롤백
    }
    e.printStackTrace();
} finally {
    entityManager.close();
}

🙎 EntityManager 메소드

persist(entity): 새로운 엔티티를 영속성 컨텍스트에 추가한다. 이 메서드는 새로운 엔티티를 데이터베이스에 저장하기 위해 사용된다. 그러나 이 메서드 호출 후에도 트랜잭션이나 커밋이 이루어지기 전까지는 실제 데이터베이스에 저장되지 않는다.

merge(entity): 준영속(detached) 상태의 엔티티를 영속성 컨텍스트에 병합한다. 이는 영속성 컨텍스트에 이미 존재하는 엔티티데이터베이스에서 가져온 엔티티병합하거나, 영속성 컨텍스트에 존재하지 않는 엔티티를 새로 영속성 컨텍스트에 추가할 때 사용된다.

find(entityClass, primaryKey): 주어진 엔티티 클래스와 기본 키(primary key)에 해당하는 엔티티를 찾는다.

remove(entity): 영속성 컨텍스트에서 엔티티를 제거하고 데이터베이스에서 삭제될 수 있도록 표시한다. 그러나 실제 삭제는 트랜잭션 커밋 시에 이루어진다.

flush(): 영속성 컨텍스트의 변경사항을 데이터베이스에 즉시 반영한다. 이는 영속성 컨텍스트의 변경사항을 데이터베이스에 동기화하는데 사용된다.

clear(): 영속성 컨텍스트의 모든 엔티티를 제거하여 영속성 컨텍스트를 초기화한다.

detach(entity): 영속성 컨텍스트에서 엔티티를 분리하여 준영속 상태로 만든다. 이는 해당 엔티티를 영속성 컨텍스트의 관리 밖으로 빼고 싶을 때 사용된다.

이 메서드들은 JPA의 EntityManager 인터페이스를 통해 제공되며, 엔티티의 관리, 영속성 컨텍스트에서의 작업, 데이터베이스와의 상호작용 등을 돕는다.

  • flush(): 이 메서드는 영속성 컨텍스트의 변경 내용을 데이터베이스에 즉시 동기화한다. 즉, 영속성 컨텍스트에 있는 엔티티의 변경 사항을 데이터베이스에 반영하는데 트랜잭션이 커밋되지 않은 상태라면 변경 사항은 실제 데이터베이스에 반영되지 않을 수 있다. flush()를 호출한다고 해서 변경 사항이 바로 영구적으로 적용되는 것은 아니다. 변경 사항을 데이터베이스에 보내지만 트랜잭션이나 커밋이 이루어지기 전까지는 롤백될 수 있다.
  • commit(): 이 메서드는 트랜잭션의 모든 변경 사항을 데이터베이스에 영구적으로 적용한다. 트랜잭션의 모든 작업이 성공적으로 수행되었고, 데이터베이스에 영구적으로 저장되어야 할 때 commit()이 호출된다. 이렇게 하면 트랜잭션이 완료되고, 데이터베이스에 있는 변경 사항이 영구적으로 적용된다.

🖇 JpaRepository와 데이터베이스 상호작용

  • CRUD 작업:
    JpaRepository를 확장한 인터페이스는 CRUD 작업을 위한 메서드들을 상속받는다. (save(), findById(), delete(), 등)
    productRepository.save(new Product(requestDto))는 새로운 Product 엔티티를 영속성 컨텍스트에 저장하고, 이후 JPA는 이를 데이터베이스에 적절한 SQL 쿼리로 변환하여 데이터베이스에 저장한다.
    productRepository.findById(id)와 같은 메서드는 데이터베이스에서 엔티티를 검색한다.

  • SQL 쿼리 생성:
    JpaRepository는 메서드 이름 규칙을 통해 쿼리를 자동으로 생성할 수 있다. 예를 들어, 메서드 이름에 findBy를 사용하면 해당 필드를 기반으로 검색 쿼리를 생성한다.
    복잡한 쿼리가 필요한 경우에는 @Query 어노테이션을 사용하여 직접 SQL 쿼리를 작성할 수 있다.

  • Spring Data JPA에서 제공하는 JpaRepository 인터페이스는 기본적인 CRUD(Create, Read, Update, Delete) 기능을 제공하는 여러 메서드들을 상속받아 사용할 수 있다. 일반적으로 사용되는 메서드들은 다음과 같다

    • save(S entity): 엔티티를 저장하거나 업데이트한다. 새로운 엔티티인 경우 저장하고, 이미 존재하는 엔티티인 경우에는 업데이트한다.

    • findOne(ID primaryKey): 주어진 기본 키에 해당하는 엔티티를 반환한다.

    • findById(ID primaryKey): 주어진 기본 키에 해당하는 엔티티를 Optional로 반환한다.

    • findAll(): 모든 엔티티를 조회한다.

    • delete(S entity): 주어진 엔티티를 삭제한다.

    • deleteById(ID primaryKey): 주어진 기본 키에 해당하는 엔티티를 삭제한다.

    • existsById(ID primaryKey): 주어진 기본 키에 해당하는 엔티티가 존재하는지 확인한다.

    • count(): 저장된 엔티티의 총 개수를 반환한다.

    • flush(): 영속성 컨텍스트의 변경 사항을 데이터베이스에 즉시 반영한다.

  • JpaRepository는 메서드 이름 규칙을 따라 쿼리를 자동으로 생성한다.

    @Entity
    public class Product {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
    
     private String name;
     private String category;
     private double price;
    
     // Getters, setters, constructors 등 생략
    }
  • 메서드 이름으로 쿼리 생성:

    • findByCategory(String category): category 필드를 기반으로 해당 카테고리에 속하는 상품을 조회한다. -> findByCategory()만 작성해보기
    • findByNameAndCategory(String name, String category): 이름과 카테고리 모두를 만족하는 상품을 조회한다.
    • deleteByCategory(String category): 주어진 카테고리에 해당하는 상품을 삭제한다.
  • 조건 연산자 사용:

    • findByPriceLessThan(double price): 가격이 주어진 값보다 낮은 상품을 조회한다.
    • findByPriceGreaterThanEqual(double price): 가격이 주어진 값보다 크거나 같은 상품을 조회한다.


트랜잭션 관리

JpaRepository는 Spring의 트랜잭션 관리 기능과 함께 동작한다. @Transactional 어노테이션을 이용하여 트랜잭션 범위를 설정할 수 있고, 여러 개의 CRUD 작업을 하나의 트랜잭션으로 묶어 데이터베이스 일관성을 유지할 수 있다.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Transactional
    public void updateProduct(Long productId, String newName) {
        Product product = productRepository.findById(productId).orElse(null);
        if (product != null) {
            product.setName(newName);
            productRepository.save(product);
        }
    }
}
  • @Transactional 어노테이션이 붙은 메서드 내에서 실행되는 모든 데이터베이스 작업은 하나의 트랜잭션으로 묶인다. 그래서 메서드가 정상적으로 실행될 경우 변경 사항이 커밋되고, 예외가 발생할 경우 롤백되어 변경 사항이 취소된다.
  • @Transactional 어노테이션을 사용하면 해당 메서드에서 일어나는 모든 데이터베이스 작업은 하나의 트랜잭션으로 관리된다. 예외 발생 시 롤백되어 데이터 일관성을 유지하고, 성공 시 커밋되어 변경 사항이 영구적으로 적용된다.

💼 트랜잭션으로 감싸여 있지 않은 이유

@Service
@RequiredArgsConstructor
public class ProductService {

   private final ProductRepository productRepository;
   public ProductResponseDto createProduct(ProductRequestDto requestDto) {
       Product product =  productRepository.save(new Product(requestDto));
       return new ProductResponseDto(product);
   }
}
  • 코드에서는 @Transactional 어노테이션이 보이지 않으므로 해당 createProduct 메서드는 트랜잭션으로 감싸여 있지 않다.

  • 일반적으로 Spring Data JPA의 save() 메서드는 이미 내부적으로 트랜잭션을 가지고 있다. save() 메서드는 엔티티를 저장하거나 업데이트할 때 내부적으로 트랜잭션을 시작하고 종료한다. 이는 각각의 save() 메서드 호출이 트랜잭션으로 감싸여 데이터베이스 작업을 수행하기 때문에, 해당 메서드 자체에 @Transactional 어노테이션을 추가할 필요가 없다.

  • save() 메서드와 findById() 메서드는 이미 Spring Data JPA에서 내부적으로 트랜잭션을 가지고 있다.

  • @Transactional 어노테이션을 명시하는 것은 코드를 이해하기 쉽게 만들어주며, 트랜잭션을 사용한다는 점을 명확하게 나타내어 주는 것이 좋다.

profile
코딩 기록

0개의 댓글

관련 채용 정보