개발 공부/Spring 10

테스트에서만 @Async 적용되지 않도록 하기

⭐️ 방법 매우 간단함 주의.. 문제 상황 알림 발송 로직을 비동기로 처리하고 테스트를 수행하려니 아래와 같이 알림 발송을 검증하는 테스트가 깨지는 문제가 생겼다. 원인은 비동기 메서드인 notificationService.send()가 완료되기 이전에 값이 생성되었는지를 검증하려고 했기 때문이다. 더 큰 문제로, 비단 알림 발송 자체를 검증하는 테스트 뿐만 아니라 다른 테스트에서도 해당 테스트가 수행하는 로직이 특정 작업을 마치고 알림 발송을 수행하는 경우 동일하게 비동기 로직이 수행되어 테스트가 깨지고 있었다. 그 예시로, 위의 경우 테스트 완료 후 수행하는 reset.sql 에서 알림 테이블을 지울 때 존재하지 않던 알림이 (비동기로 실행되어) 회원 테이블을 지울 때 존재하게 되는 상황이다. 때문에..

[플랭고] 주요/부가 로직 트랜잭션 분리하기 - TransactionalEventListener와 REQUIRES_NEW

플랭고는 친구와 함께 일정을 수정하고 장소를 추가하는 등 일정 공유 기능을 제공한다. 이때 새로운 일정에 초대되거나, 일정 초대를 수락/거절하거나 공유 중인 일정이 수정되는 등의 상황에 알림(자체 알림 목록 + 푸시)이 발송되도록 기능을 구현해두었다. 기존에 알림을 발송하는 흐름은 다음과 같다. 먼저 일정 서비스에서 알림을 발송해야 하는 경우에 알림 DTO를 만들어 알림 서비스를 호출한다. 그러면 알림 서비스는 알림을 생성하고 외부 서비스인 FCM을 통해 대상 회원의 기기로 푸시 알림을 보낸다. 이러한 흐름으로 알림을 발송하면 위 그림처럼 일정 -> 알림 -> 외부 서비스를 거치는 흐름 전체가 하나의 트랜잭션으로 묶이게 된다. 이런 식으로 코드를 구현하면 크게 다음과 같은 문제가 생길 수 있다고 생각했다..

[플랭고] JPA delete() 쿼리 안 나가는 문제 해결

💥 문제 상황 JPA를 사용하다가 xxxRepository.delete(Entity entity) 메서드를 통해 엔티티를 삭제하고 싶었는데 아무리 해도 delete 쿼리가 발생하지 않는 문제가 있었다. 엔티티 삭제 코드 @Transactional public void rejectOrExitSchedule(Long memberId, Long scheduleId) { Schedule schedule = findScheduleById(scheduleId); validateMemberNotOwner(memberId, schedule); ScheduleMember scheduleMember = findScheduleMemberByMemberIdAndScheduleId(memberId, scheduleId); sc..

[플랭고] JPQL fetch join + where절 사용 방법과 조건

프로젝트 개발 중에 fetch join 과 where 절을 함께 사용하고 싶은 상황이 생겼다. 아무생각 없이 코드를 치다가 순간 섬뜩해서 영한님 JPA 강의 자료를 뒤져보니 역시.. fetch join의 대상에 별칭을 주고 where 절에서 필터링하는건 불가능하다고 되어있었다. 그래서 고민을 좀 해봤는데 고민을 할수록 다음과 같은 고민들이 꼬리에 꼬리를 물고 생겨났다. fetch join은 별칭을 아예 줄 수 없나? where 절에 XToOne 쪽 필드를 조건으로 주는 경우는 어떻게 동작하나? fetch join 말고 join은 사용 해도 되는건가 그럼? 이 경우에는 어떻게 동작하지? 애초에 fetch join을 왜 where 절과 함께 사용하면 안 되는거지..? … JPA의 어려움에 멘붕이 올뻔했지만 ..

[플랭고] 일대일에서 일대다로 변경 시 validation 관련 문제 (Custom ConstraintValidator)

문제 상황 개발 중인 애플리케이션 플랭고의 '기록하기' 기능은 현재 1장의 이미지만 첨부할 수 있다. 이를 20장까지 첨부할 수 있도록 변경하는 작업을 진행했다. (사진은 2023 인프콘 🥹) 먼저 다음과 같은 작업들을 진행했다. 1. 이미지 url을 저장할 DiaryImage 테이블, 엔티티 생성 @Getter @NoArgsConstructor(access = PROTECTED) @Entity public class DiaryImage extends BaseTimeEntity { @Id @GeneratedValue(strategy = IDENTITY) private Long id; @ManyToOne(fetch = LAZY) @JoinColumn(name = "diary_id") private Diar..

[플랭고] 스프링 시큐리티 실패 핸들러 에서 직접 예외를 던지면 안 되는 이유

문제 상황 개발 중인 프로젝트에서 json 형태로 로그인을 처리하기 위해 AbstractAuthenticationProcessingFilter을 상속받는 커스텀 AuthenticationFilter를 구현하고 시큐리티에 필터로 등록해 사용하려고 했다. 다음과 같이 커스텀 Json 로그인 처리 필터를 구현하고 public class JsonEmailPasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { ... @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws Au..

DTO의 사용범위

배경 넥스트스텝의 학습테스트로 배우는 Spring 과정을 수강하고 있는데, 과정 진행 중 controller 패키지의 DTO를 어떤 계층까지 사용하면 좋을지에 대한 고민이 들었다. 처음엔 간단하게 생각했지만 생각보다 고민할 내용이 많았어서 고민한 내용과 결론을 정리해보려고 한다. DTO란 먼저, DTO는 Data Transfer Object의 약자로 말그대로 계층 간에 데이터를 주고 받기 위해 사용하는 객체이다. 보통 비지니스 로직은 두지 않고 필드와 그에 대한 getter와 생성자 등만 구현해두고 사용하는 경우가 많다. 예를 들어 아래와 같은 객체가 DTO이다. @NoArgsConstructor @Getter public class PlayRequestDto { private String names; ..

[Spring Security] 웹 시큐리티

스프링 시큐리티 ignoring() 아래와 같이 index 페이지에 css를 적용하고 다시 요청을 해보자. Hello #hello { font-size: 100px; } css로 폰트 크기를 변경했음에도 실제로는 반영이 되지 않은 것을 확인할 수 있다. 개발자 콘솔로 요청과 응답 상태를 확인해보면, css 파일을 요청하는데 302(리다이렉션) 코드가 뜨고 login 페이지를 추가로 요청한 것을 확인할 수 있다. 이는 WebSecurityConfigurerAdapter를 통해 시큐리티를 설정할 때 지정한 경로 외의 모든 요청은 인증을 받도록 했기 때문에 정적 리소스를 요청하는 경로 또한 인증을 필요로 하게 되었기 때문에 발생하는 문제이다. 이를 해결하기 위해서는 시큐리티 필터가 적용되지 않도록 ignori..

[Spring Security] 아키텍처

SecurityContextHolder와 Authentication SecurityContextHolder SecurityContext 를 제공. 기본적으로 ThreadLocal을 사용하기 때문에 SecurityContext는 동일 쓰레드 내에서 항상 접근이 가능 SecurityContext Authentication을 제공 // SecurityContextHolder -> SecurityContext -> Authentication Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // Principal Object principal = authentication.getPrincipal(); //..

Gradle plugins vs dependencies

이동욱님의 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 을 따라 진행하며 웹 앱을 개발하고 배포하는 전체 과정을 쭉 따라고 보고 싶었는데 첫 장부터 막혀버렸다..ㅋㅋ start.spring.io에서 완성된 프로젝트 파일을 다운로드하여 import 해 사용하는 방식으로 공부해왔어서 gradle 설정에서 문제가 발생하니 해결하기가 어려웠다. (동욱님께서도 책에서 이러한 문제 때문에 직접 gradle 설정 파일을 작성해보기를 권장하신다.) 그래서 이 참에 평소 gradle 설정 파일에서 궁금했던 정보를 찾아 정리해봤다. 워낙 기능과 정보가 방대해서 지금 내 수준에서 필요한 정도로만 정보를 찾고 정리했다. 다른 때와 달리 최대한 gradle 공식 문서만을 참고하려고 노력했다..! Gradle Gradle은 ..