들어가며Spring 프로젝트를 하다 보면 Entity DTO 변환, 객체 간 매핑 등 반복적인 코드를 많이 작성하게 된다. 이런 보일러 플레이트 코드들이 쌓이면서 유지보수성과 가독성이 떨어지는 경험이 있을 것이다.이번 글을 통해 Mapper 패턴을 통해 이런 문제들을 어떻게 해결할 수 있는지 실제 사례와 함께 알아보자.문제 상황: 반복되는 변환 코드Before: 보일러플레이트 지옥@RestControllerpublic class UserController { @PostMapping("/users") public ResponseEntity createUser(@RequestBody UserCreateDto request) { // DTO → Entity 변환 (Setter ..
@TransactionalSpringBoot로 프로젝트를 해본 사람이라면 한 번쯤은 이 어노테이션을 써봤을 것이다. 이 기능은 메서드나 클래스에 적용하면 해당 메서드 혹은 클래스 내의 메서드들이 하나의 트랜잭션으로 묶인다.즉, @Transactional은 스프링 프레임워크에서 데이터의 일관성을 보장하고 트랜잭션을 효과적으로 관리하기 위한 중요한 도구이다.AOP(Aspect Oriented Programming)AOP는 관점 지향 프로그래밍이라고도 하며, 애플리케이션의 핵심적인 기능에서 로깅, 트랜잭션, 보안 등 공통 관심사를 비즈니스 로직에서 분리하여 객체로 관리한다. 이를 통해 코드의 가독성, 유지보수성, 재사용성을 높인다. Aspect: 부가기능 모듈. 여기서 부가기능이란 로깅, 트랜잭션, 보안 등과..
문제 발생CRUD 비즈니스 로직을 구현하는 중 특정 엔티티의 삭제를 위해 쿼리를 날렸지만 JPA의 DELETE 쿼리가 실행되지 않았다. SELECT 쿼리만 로그에 찍힐 뿐 분명 삭제쿼리를 실행했음에도 데이터베이스에는 데이터가 그대로 남아있고, 로그에도 DELETE의 'D'도 찾을 수 없었다. 현재 데이터베이스내에 테이블간 관계는 다음과 같다.비즈니스 로직은 아래와 같다.캠페인이 광고를 진행하면 일반적으로 캠페인 내 광고제품을 등록, 수정, 삭제를 할 수 있다. 캠페인 수정 API 호출수정을 하려는 캠페인 ID(PK)를 파라미터로 넣는다.RequestBody에 광고제품명의 수정(UPDATE), 삭제(DELETE) , 등록(INSERT)에 맞게 작성한다. (등록의 경우에는 AUTO INCREMENT 전략..
JPA로 개발을 진행하다보면 테이블간 맵핑관계에서 CascadeType.ALL(또는 REMOVE, PERSIST)와 orphanRemoval = true를 붙이는 경우를 볼 수 있다. 나 역시 그랬다. CascadeType.ALL을 쓰게되면 부모 엔티티 생성 또는 삭제 시, 자식의 생명주기까지 관리할 수 있어서 깊이 생각하지 않고 사용했었다. 그러나 orphanRemoval는 자식이 연관관계가 끊어질 경우 고아객체가 되는 것을 방지한다. 정도로만 알고 있었지 CascadeType.ALL와 orphanRemoval의 상관관계에 대해서는 크게 신경쓰지 않았다. 각각은 어떤 속성을 가지고 있는 걸까?결론부터 말하면 다음과 같다.상황CascadeType.REMOVEorphanRemoval=true부모 엔티티 삭..
@RequiredArgsConstructor는 Lombok에서 제공하는 어노테이션으로, 클래스의 final 필드나 @NonNull로 선언된 필드에 대해 자동으로 생성자를 생성해주어 의존성 주입을 간편하게 만들어준다. 문제의 코드는 아래와 같다.@Service@RequiredArgsConstructorpublic class MyService { @Qualifier("restClientSSE") private final RestClient restClient; ...} @Qualifier를 사용하여 특정 빈(restClientSSE)을 주입하여 "http://localhost:8080/"의 baseUrl을 포함한 API를 호출하려고 했는데, 다른 빈(restClient)이 주입되어 "www...
Spring Security에서 JWT를 통한 인증 / 인가를 위해 FilterChain 단 개발 중 JWT가 만료되어서 블랙리스트에 있는 AccesToken일 경우 예외를 발생시켜 아래와 같이 ExceptionHandler에서 핸들링하려고 하였다. 하지만 원하는 예외 메세지 "ACCESS_TOKEN_IS_BLACKLIST("블랙리스트에 포함된 Access Token 입니다." 가 출력되지 않았다. 왜일까? 결론부터 말하면, 필터에서 발생한 예외는 필터 체인 내에서 처리되어야 하며, 필터가 실행되는 시점에서 발생한 예외는 DispatcherServlet을 넘어가지 않기 때문에 @RestControllerAdvice 같은 컨트롤러 기반의 예외 처리기가 이를 인식하지 못한다.@RestControllerAdv..