Ian's Archive 🏃🏻

Profile

Ian

Ian's Archive

Developer / React, SpringBoot ...

📍 Korea
Github Profile →
Categories
All PostsAlgorithm19Book1C1CI/CD2Cloud3DB1DesignPattern9ELK4Engineering1Front3Gatsby2Git2IDE1JAVA7JPA5Java1Linux8Nginx1PHP2Python1React9Security4SpatialData1Spring26
thumbnail

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

JPA
2022.10.13. (수정됨)

Series

  • 1자바 ORM 표준 JPA 프로그래밍 (기본편)
  • 2실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
  • 3실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
  • 4실전 스프링 데이터 JPA
  • 5Querydsl

테이블, 엔티티 설계 시 주의사항

  • 다대다 관계 사용x -> 일대다, 다대일로 풀어서 사용
  • 외래키가 있는 곳을 연관관계의 주인으로 설정
  • getter는 가급적 열어두고 setter는 비즈니스 메서드를 별도로 작성해 변경지점이 명확히 설계
  • 값 타입 사용시 Immutable 하게 설계
  • 연관관계는 지연로딩 설정

계층형 구조

  • Controller : 웹 계층
  • Service : 비즈니스 로직, 트랜잭션 처리
  • Repository : JPA 사용 계층
  • Domain : 엔티티가 모여있는 계층

서비스, 리포지토리 계층 개발 -> 테스트 케이스 작성 후 검증 -> 웹 계층 적용

어노테이션

Repository 관련

복사
@Repository // 빈으로 등록, JPA 예외를 스프링 기반 예외로 변환
@PersistenceUnit // 엔티티 매니저 팩토리(EntityManagerFactory) 주입
@PersistenceContext // 엔티티 매니저(EntityManager) 주입
// -> SpringBoot 사용 시 @Autowird지원
// -> @RequiredArgsConstruct 사용해 생성자 주입 가능

Service 관련

복사
@Service

// 트랜잭션, 영속성 컨텍스트
// 데이터의 변경이 없는 읽기 전용 메서드에 사용
// 영속성 컨텍스트를 플러시 않아 약간의 성능 향상(읽기 전용에는 다 적용)
@Transactional(readOnly=true)

Test 관련

복사
@RunWith(SpringRunner.class) // Junit4 스프링과 테스트 통합
@ExtendWith(SpringExtension.class) // Junit5

@SpringBootTest // 스프링 부트 띄우고 테스트 - @Autowired시 필요
@Transactional // 테스트를 실행할 때마다 트랜잭션 시작 - 테스트 종료 시 롤백

Pattern

비즈니스 로직에 따른 패턴

1. 도메인 모델 패턴

  • 엔티티가 비즈니스 로직을 가지고 객체지향 특성을 적극 활용
  • 서비스 계층은 단순히 엔티티에 필요한 요청만 위임
  • 엔티티에 대해 테스트 코드 작성 가능 (단위 테스트)

2. 트랜잭션 스크립트 패턴

  • 일반적으로 사용하던 패턴
  • 서비스 계층에서 대부분의 비즈니스 로직 처리

Form 객체 vs 엔티티 직접 사용

엔티티는 핵심 비즈니스 로직만 소유
-> 화면이나 API에 맞는 폼 객체DTO를 사용
-> API는 절대 엔티티 반환x -> 엔티티가 변경되면 스펙이 변경되기 때문-> DTO 사용

변경 감지(dirty check)와 병합(Merge)

JPA는 플러시 할 때 변경 감지가 일어나 업데이트 해준다 -> 준영속 상태의 엔티티 변경시 문제 발생

준영속 엔티티란?

영속성 컨텍스트가 관리하지 않는 엔티티 -> 기존 식별자를 가지고 있는 엔티티

해결 방법

  1. 변경 감지 기능 사용
    -> 영속성 컨텍스트에서 엔티티를 다시 조회한 후에 데이터 수정하는 방법

  2. 병합 사용
    -> 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능

복사
em.merge(item);

병합 동작 방식

  1. 준영속 엔티티의 식별자 값으로 영속 엔티티 조회
  2. 영속 엔티티의 값을 준영속 엔티티로 병합
  3. 트랜잭션 커밋 시점에 변경 감지 기능이 동작해 DB에 UPDATE

=> 병합 사용 시 모든 필드를 변경, 데이터가 없으면 null로 업데이트

결론

엔티티 변경 시 변경 감지를 사용하는 것이 좋다
-> 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회 -> 변경 (트랜잭션 커밋 시점 변경 감지 실행)

복사
@PostMapping(value = "/items/{itemId}/edit")
public String updateItem(@ModelAttribute("form") BookForm form) {
  // 변경해야 하는 엔티티 값을 넘겨준다.
  itemService.updateItem(form.getId(), form.getName(), form.getPrice());

  return "redirect:/items";
}

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ItemService {
  ...

  @Transactional
  public void updateItem(Long itemId, String name, int price, int stockQuantity) {
    Item item = itemRepository.findOne(itemId);
    item.setName(name);
    item.setPrice(price);
    item.setStockQuantity(stockQuantity);
  }

  ...
}

Tip!!

  • 테스트 코드 작성 시 스프링, DB없이 순수한 메서드만 단위테스트 하는게 좋다
  • @Test(expected = …) -> junit5에서 사용법 변경 Migrating from JUnit 4 to JUnit 5 참고
  • 동적 쿼리를 작성 할 때(ex - 검색 기능) JPQL로 구현하면 String 붙여줘서 문자열로 만들어줘야 함
    -> QueryDSL 사용

값 검증

  • 값 검증은 implementation ‘org.springframework.boot:spring-boot-starter-validation’ 추가
복사
// @Valid 어노테이션 사용해 값 검증
// @Valid 어노테이션 사용한 부분에 BindingResult 사용해주면 에러를 result값으로 생성
// -> 화면에 노출 가능
public String create(@Valid MemberForm form, BindingResult result) {
  ,,,
 }

Reference

Previous Post
자바 ORM 표준 JPA 프로그래밍 (기본편)
Next Post
실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
Thank You for Visiting My Blog, I hope you have an amazing day 😆
© 2023 Ian, Powered By Gatsby.