Today I Learned

2024 스파르타 내일배움캠프 Sping 트랙 참여 // day47

shinelee26 2024. 12. 5. 23:06

오늘 진행한 학습 요약

1. 스탠다드반 강의 (트랜잭션)

  • 트랜잭션이란?
  • ACID
  • 전파 속성 (Propagation)
  • 격리 수준 (Isolation)
  • 스프링 트랜잭션 어노테이션
  • 트랜잭션 상태
  • 스프링의 롤백
  • 트랜잭션 내부 호출 문제

2. 아웃소싱 프로젝트

  • 개발 프로세스 가이드
  • 트러블 슈팅

3. 알고리즘 코드카다  Day51 (작성 생략)

CodingTest Git-hub 링크 : https://github.com/chews26/CodingTest

 


학습 정리

1. 스탠다드반 강의 (트랜잭션)

 

트랜잭션이란?

  • Trans + Action의 합성어
  • 교환을 하는 행동을 말하는 것으로 물물교환을 의미
  • 1960년대 초 IBM은 컴퓨터 업계에서 트랜잭션이라는 개념을 정리하고 발전
  • 지금의 DB와 프로그래밍 언어에 쓰이는 트랜잭션도 그 뿌리는 IBM이 정립한 트랜잭션의 개념에 두고 있음

 

ACID

  • 하나의 트랜잭션이 발생했을 때 안전성을 보장하기 위한 중요한 4가지 속성
  • Atomicity (원자성)
    모든 작업이 성공하거나 아무 작업도 일어나지 않음
    Consistency (일관성)
    하나의 트랜잭션 끝난 뒤에도 모든 상태는 이전과 같이 유효
    Isolation (격리성)
    모든 트랜잭션은 다른 트랜잭션으로부터 독립적
    Durability (지속성)
    하나의 트랜잭션이 완료되었다면 영구적으로 저장
  • 이 개념은 1970년대 말에 짐 그레이(Jim Gray)가 신뢰할 수 있는 트랜잭션 시스템의 이러한 특성으로 정의
  • 자동으로 이들을 수행하는 기술을 개발

 

전파 속성 (Propagation)

  • 트랜잭션에서 전파 속성은 하나의 트랜잭션이 시작된 상태에서, 또 다른 트랜잭션이 시작되었을 때 이를 어떻게 처리할지를 정의
  • 종류
    기존 트랜잭션 X
    기존 트랜잭션 O
    적용 사례
    REQUIRED
    새 트랜잭션 생성
    기존 트랜잭션에 참여
    기본값. 대부분의 비지니스 로직
    REQUIRES_NEW
    새 트랜잭션 생성
    기존 트랜잭션 일시중단, 새로운 트랜잭션 생성
    독립적인 작업 처리
    SUPPORTS
    트랜잭션 없이 진행
    기존 트랜잭션 참여
    트랜잭션이 필수가 아닌 작업
    NOT_SUPPORTED
    트랜잭션 없이 진행
    기존 트랜잭션 일시중단, 트랜잭션 없이 진행
    로그 저장 등 트랜잭션과 독집적인 작업
    MANDATORY
    IllegalTransactionStateException 발생
    기존 트랜잭션 참여
    트랜잭션 내부에서만 호출 가능한 메소드
    NEVER
    트랜잭션 없이 진행
    IllegalTransactionStateException 발생
    외부 시스템 호출시
    NESTED
    새 트랜잭션 생성
    중첩 트랜잭션 생성
    부분적으로 롤백 가능한 작업

 

격리 수준 (Isolation)

  • 트랜잭션 격리 수준은 여러 트랜잭션이 동시 발생할 때 어떻게 데이터 일관성을 보장 할지를 정의하는 것
  • Isolation Level
    설명
    방지되는 문제
    적용 사례
    READ_UNCOMMITTED
    커밋되지 않은 데이터도 읽을 수 있음
    없음
    데이터 정확성보다 성능이 중요한 경우
    READ_COMMITTED
    커밋된 데이터만 읽을 수 있음
    Dirty Read
    대부분의 애플리케이션 기본 설정
    REPEATABLE_READ
    같은 트랜잭션 내에서 읽은 데이터가 변경되지 않음
    Dirty Read, Non-repeatable Read
    은행 계좌 조회와 같은 데이터 무결성 요구
    SERIALIZABLE
    트랜잭션이 순차적으로 실행, 가장 엄격한 격리 수준
    Dirty Read, Non-repeatable Read, Phantom Read
    금융 시스템의 이체 작업 등 높은 신뢰성이 필요한 경우
  • 수준
    Dirty Read
    Non-repeatable Read
    Phantom Read
    READ_UNCOMMITTED
    O
    O
    O
    READ_COMMITTED
    X
    O
    O
    REPEATABLE_READ
    X
    X
    O
    SERIALIZABLE
    X
    X
    X

 

스프링 트랜잭션 어노테이션

  • 스프링에는 2가지 @Tx 어노테이션이 존재
    • Java에서 지원하는 어노테이션
    • 스프링이 지원하는 어노테이션
  • 두 가지는 트랜잭션의 기본 기능은 모두 동일하게 지원
  • 하지만 스프링의 어노테이션이 더 많은 기능을 지원
  • 옵션
    설명
    isolation
    Transaction의 Isolation level을 설정한다.
    label
    Transaction label을 설정한다.
    noRollbackFor
    transaction rollback 처리가 되지 않아야 할 예외 클래스를 명시한다.
    noRollbackForClassName
    transaction rollback 처리가 되지 않아야 할 예외 클래스 이름을 명시한다.
    propagation
    Transaction Propagation 타입을 설정한다.
    readOnly
    Transaction을 readOnly로 설정한다.
    rollbackFor
    transaction rollback이 되어야하는 예외 클래스를 명시한다.
    rollbackForClassName
    transaction rollback이 되어야하는 예외 클래스 이름을 명시한다.
    timeout
    Transaction의 Timeout을 설정한다. (단위 : 초,seconds)
    timeoutString
    Transaction의 Timeout을 설정한다. (단위 : 초,seconds)
    transactionManager
    특정 Transaction의 qualifier value를 설정한다.
    value
    transactionManager의 alias(별칭)을 설정한다.

 

트랜잭션 상태

  • 활동 상태 (active) : 초기 상태, 트랜잭션이 실행 중일 때 가지는 상태
  • 부분 완료 상태 (partically committed) : 마지막 명령문이 실행된 후에 가지는 상태
  • 완료 상태 (committed) : 트랜잭션이 성공적으로 완료된 후 가지는 상태
  • 실패 상태 (failed) : 정상적인 실행이 더 이상 진행될 수 없을 때 가지는 상태
  • 철회 상태 (aborted) : 트랜잭션이 취소되고 데이터베이스가 트랜잭션 시작 전 상태로 환원된 상태

 

스프링의 롤백

  • Atomicity(원자성) 속성
    • 모든 작업이 성공하거나 아무 작업도 일어나지 않음
    • 이 속성에 의하면 트랜잭션 중에 실패하면 반드시 아무 작업도 일어나지 않아야함
    • 즉 기존 완료된 작업이 Rollback이 되어야 한다는 의미.
    • 실패는 Exception 상황
  • Tx 내부에서 Exception 발생 → Atomicity 보장 필요 → Rollback
  • Unchecked/Checked Exception에 따라 각각 처리가 다름
  • 스프링의 롤백 기본 동작
    • 조건
      롤백 여부
      설명
      Unchecked Exception (RuntimeException 또는 Error 계열)
      O
      기본적으로 스프링 트랜잭션은 RuntimeException 또는 Error 발생 시 롤백 처리
      Checked Exception (Exception을 상속하고 RuntimeException이 아닌 예외)
      X
      Checked Exception은 기본적으로 롤백 트리거가 아님 (명시적으로 설정해야 롤백 가능)
      @Transactional의 rollbackFor 설정에 포함된 Checked Exception 발생
      O
      rollbackFor 속성에 지정된 Checked Exception은 롤백 트리거로 작동
      try-catch로 RuntimeException을 처리한 경우
      O
      RuntimeException 발생 시 이미 롤백 상태로 마킹되므로 catch 처리 여부와 무관하게 롤백
      try-catch로 Checked Exception을 처리한 경우
      X
      Checked Exception은 기본적으로 롤백되지 않으며 catch로 처리 시 트랜잭션 상태 유지
      @Transactional의 noRollbackFor 설정에 포함된 Exception 발생
      X
      noRollbackFor 속성에 지정된 Exception은 롤백하지 않음
      Transactional 내부에서 catch 후 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(false) 호출
      X
      catch 이후 롤백 플래그를 false로 명시적으로 변경 시 롤백 방지
      Transaction이 외부 트랜잭션에서 관리되는 경우 (Propagation.REQUIRED)
      외부 트랜잭션에 따름
      내부 트랜잭션에서 롤백 마킹이 되어도 외부 트랜잭션의 최종 상태에 의존
      Transaction이 새 트랜잭션 (Propagation.REQUIRES_NEW)인 경우
      내부 트랜잭션에 따름
      내부 트랜잭션의 롤백은 외부 트랜잭션에 영향을 주지 않음

 

트랜잭션 내부 호출 문제

  • 스프링은 트랜잭션을 적용하기 위해서는 반드시 프록시 객체를 통해야 함
  • 프록시 객체는 우리가 Contoller, @Service, @Repository와 같은 어노테이션을 적용하면 사용할 수 있는 것
  • 이 프록시 객체를 통해 메소드 호출이 일어나면 메소드가 호출 되는 시점, 종료되는 시점에 각각 트랜잭션 처리가 되는 원리

2. 아웃소싱 프로젝트

 

개발 프로세스 가이드

  • 0단계 : 목표 정하기! (Growth Mindset)
    • 배달의 인간 (배달 어플 만들기)
  • 1단계 : 팀 노션 작성하기!
    • 프로젝트 소개
    • 기획관련메모
    • WBS & Tasks 작성
    • 깃헙 커밋큐칙 작성
  • 2단계 : 프로젝트 아이디어 구상하기!
    • 배달의 인간은 사장님과 고객을 연결하는 통합 배달 플랫폼!
  • 3단계 : 와이어프레임 작성하기!
  • 4단계 : ERD 작성하기!
  • 5단계 : API 명세 작성하기!

  • 6단계 : S.A 작성 및 피드백 받기!
  • 7단계 : 본격적인 백엔드 개발하기!
    • 로그인, 유저관리, 인증/인가 부분을 담당!
  • 8단계 : 테스트 및 버그 수정하기!
    • 진행중...

 

👾 트러블 슈팅

  • 🤔 문제
    • StoreRepository에서 쿼리 메서드 네이밍 규칙과 엔티티 매핑 정보 불일치로 인한 Spring이 실행이 불가했다!
Optional<Store> findByMember_IdAndStore_Id(Long userId, Long storeId);
  • 😎 해결방법
    • 팀원과 상황을 즉시 공유하였다. 팀원 한분이 문제를 해결할 수 있는 방법을 제시해 주시고 코드 수정을 도와주셨다!
    • JPA는 연관 관계(@ManyToOne, @OneToMany)를 사용하는 필드에서 하위 속성을 탐색할 때 필드명을 정확히 알아야하는데 자동 생성된 메서드 네이밍에서 잘못된 필드명을 사용하면 오류가 발생하기 때문에 메서드 네이밍과 다르다면 명시적으로 쿼리를 작성하여 해결해야 했다
    • JPA가 네이밍 규칙을 기반으로 쿼리를 자동 생성하지 않도록 명시적으로 쿼리 작성하여 문제 해결하였다
    • 관련 링크
    @Query("SELECT s FROM Store s WHERE s.member.userId = :memberId AND s.id = :storeId")
    Optional<Store> findByMember_IdAndStore_Id(@Param("memberId") Long memberId, @Param("storeId") Long storeId);