본문 바로가기

Backend

(20)
Spring-Retry를 활용한 상품 이미지 업로드 트랜잭션 처리 방법 라이브러리@Retryable, @EnableRetry는 AOP 프록시 기반으로 동작하므로 AOP 의존성이 필요하다.// build.gradledependencies { implementation 'org.springframework.retry:spring-retry' implementation 'org.springframework.boot:spring-boot-starter-aop'}Bean 등록@EnableRetry@SpringBootApplicationpublic class CommerceApplication { public static void main(String[] args) { SpringApplication.run(CommerceApplication.class, ..
[JPA] 동시에 들어오는 모든 요청을 처리하는 방법, 두 번의 갱신 분실 문제 1. 여러 사용자가 동시에 서버에 요청을 보낸다면, 서버는 모든 요청을 어떻게 순차적으로 처리할 수 있을까?웹 서비스 이용자가 많고, 특정 서비스에 대한 요청이 동시에 몰리게 되면, 서버에서 적절한 처리를 하지 않는 경우 일부 요청이 유실되는 문제가 발생할 수 있다. 예를 들어, 두 사용자가 동시에 물건을 구매했을 때 재고가 100개라면 정상적으로는 98개가 되어야 하지만, 첫 번째 사용자의 요청이 유실되면 DB에는 99개로 잘못 저장될 수 있다. 서버는 여러 사용자가 동시에 동일한 데이터에 접근하더라도 모든 요청을 순차적으로 처리할 수 있도록 관리해야 한다. 하지만 모든 요청을 완벽히 처리하려고 하면 서비스 속도가 느려질 수 있는 한계가 있다. MySQL에서는 배타적 락(Pessimistic Lock)..
[JPA] 낙관적 락(Optimistic Lock)과 예제 코드 1. 낙관적 락(Optimistic Lock)이란?낙관적 락은 동시성 문제가 발생할 가능성이 낮다고 가정하고, 여러 트랜잭션이 동시에 데이터에 접근할 수 있도록 허용한다. 그러나 실제로 데이터를 변경할 때 충돌이 발생하면, 해당 트랜잭션은 롤백되고 예외를 발생시킨다.  JPA에서는 @Version 필드를 사용하여, 해당 엔티티가 변경될 때마다 버전 값이 증가한다. 트랜잭션이 종료되기 전에 다른 트랜잭션이 이 엔티티를 변경하여 버전이 증가한 것을 감지하면, OptimisticLockException이 발생하고 해당 트랜잭션은 롤백된다. 또한 데이터베이스의 물리적 락 기능을 사용하지 않고, 엔티티의 버전을 통해 동시성을 제어한다.2. OptimisticLockingFailureException 예외란?Opt..
[Spring] 테스트 데이터 클렌징 시에 deleteAll(), deleteAllInBatch(), @Transactional 중에 무엇을 사용해야할까? 여러 개의 테스트를 동시에 실행하기 위해서는 각 테스트가 독립적으로 수행되어야 한다. 즉, 이전 테스트에서 수행한 내용이 다음 테스트에 영향을 미쳐서는 안 된다. 스프링을 사용한다면 @AfterEach 또는 @BeforeEach를 사용하여 각 테스트가 종료될 때마다 클렌징 처리가 가능하다. 이때, deleteAll(), deleteAllInBatch(), @Transactional 중에 하나를 사용하여 클렌징 처리가 가능하다. 셋 다 테스트에서 사용한 데이터를 지운다는 것은 동일하지만, 성능 상에 차이점이 존재한다.  각 방식의 차이점과 무엇을 사용해야 할지 알아보자. 1. 테스트 환경하나의 주문(Order)에는 여러 개의 상품이 포함될 수 있다.하나의 상품(Product)은 여러 개의 주문에 포함될 수..
[Spring] BDD 패턴의 테스트 코드 작성 시 Mockito의 어색함과 BDDMockito의 활용 Mockito와 BDDMockito 둘 다 Java 기반의 테스트 프레임워크로, 모킹(mocking)을 지원한다. 이 둘은 기능상에 차이점은 없으나, 모킹하는 스타일 방식이 차이가 있다.1. Mockito를 통해 Mock 객체 생성하는 방법@Mock 애너테이션을 통해 Mock 객체를 생성하고, @InjectMocks 애너테이션을 통해 Mock 객체를 주입할 수 있다. 예를 들어, MailService 객체를 생성하기 위해서 MailSendClient와 MailSendHistoryRepository가 필요하다. 이를 위해 @Mock 애너테이션을 사용하여 MailSendClient와 MailSendHistoryRepository 클래스의 Mock 객체를 생성하고, @InjectMocks 애너테이션을 통해 이..
[Spring] MockMvc 테스트에서 with(csrf()) 자동화 하기 1. 사건 발달스프링 시큐리티 환경에서 MVC를 테스트하기 위해서는 요청 헤더에 CSRF 토큰이 필요하다. 따라서 아래 코드와 같이 perform() 시에 요청 헤더에 스프링 시큐리티가 생성한 CSRF 토큰을 포함할 수 있다.근데 테스트 코드를 작성할 때마다 CSRF 토큰을 일일이 추가하는 것보단, 테스트 실행 전에 자동으로 추가할 수 있지 않을까? @WithMockUser@WebMvcTest(controllers = SectionController.class)class SectionControllerTest { @Test @DisplayName("신규 회고카드를 등록한다.") void createSection() throws Exception { //given ..
[Spring] 스프링 시큐리티 환경에서 @WebMvcTest 사용 시에 발생하는 401, 403 에러 해결하기 우리 프로젝트는 Spring Security를 사용하여 사용자의 인증 정보와 권한 정보를 관리하고 있다. 따라서 사용자의 인증과 권한 정보에 따라서 API 요청이 서버로 도달하게 할 것인지 결정한다. @WebMvcTest 애너테이션을 사용하여 Spring MVC의 Presentation Layer 테스트가 가능하다. 즉, 스프링에서는 Controller에 대한 테스트를 하게 된다.  1. 테스트 환경1-1. Controller@CurrentUser 애너테이션을 사용하여, 요청 헤더에 담긴 Bearer 토큰을 통해 인증된 사용자 정보를 가져온다.@ResponseStatus(HttpStatus.CREATED)를 통해 응답 상태 코드로 201을 갖는다.@RestController@RequestMapping("..
[JPA] 읽기 전용 트랜잭션과 JPA 1차 캐시의 동작 원리 1. @Transactional트랜잭션이란 데이터베이스의 상태를 변화시키기 위해 수행하는 작업 단위이다. 스프링 프레임워크로 개발하는 경우 보통 Business Layer에 @Transactional을 사용한다. 기본적으로 @Transactional은 CRUD 시에 사용할 수 있다. 즉, Query와 Command를 구분하지 않고 사용할 수 있다.예를 들어, @Transactional을 사용하여 save() 메서드에서 생성된 member 객체를 데이터베이스에 저장할 수 있다. @Transactional을 사용하면 트랜잭션 AOP가 동작하면서 메서드 시작과 끝에 transactional.begin()과 transactional.commit()을 추가하기 때문이다. 단, 중간에 예외가 발생하면 transac..