Spring Framework
-
JPA cascade 멈춰!Spring Framework 2022. 2. 2. 18:23
1) 서론 업무 중 수십 개의 테이블에 걸친 데이터를 한 번에 지워야 하는 API를 만들어야 했습니다. 처음에는 고작 CRUD인데, 금방 하겠지라는 마음을 가지고 시작했습니다. 이때 생각지도 못 한 어려움을 만났는데요. 하마터면 관련 없는 데이터까지 삭제할 뻔했습니다. 왜 이러한 일이 발생했는지 재현 후 기록합니다. 2) 전체 구조 2. 1) DB 스키마 User, Role, Post 테이블이 있습니다. User, Role을 연결시켜주는 User_Role 매핑 테이블이 존재합니다. 2. 2) Entity 글과 관련 없는 애노테이션들은 모두 생략했습니다. @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) p..
-
@Transactional rollback과 테스트 의문점Spring Framework 2022. 1. 24. 00:52
1) 서론 업무 중 특정한 경우에 count +1 후 exception을 발생시켜야 하는 로직이 필요했었습니다. 하지만 해당 메서드는 @Transaction이 반드시 필요했고, exception이 발생하면 rollback 되는 현상이 있었습니다. 이를 해결하기 위해서 테스트 코드를 나름대로 작성했는데요. 이 과정에서 의문점이 생긴 이야기입니다. 2) 전체 흐름 실제 업무에서 사용한 내용이 아닌, 기록을 위해 작성한 코드입니다. 상황 설정이 매우 억지스러운 것은 이해 부탁드립니다. private final PostRepository postRepository; private static final String unAuthorizedTitle = "어드민"; @Transactional public void ..
-
JPA 지연 로딩, 결국 ProxySpring Framework 2021. 11. 21. 23:42
1) 서론 JPA가 적용된 서버의 로그를 보면, LazyInitializationException이 빠지지 않고 항상 올라오곤 합니다. 일주일에 한 개씩은 발생하는 에러 같은데요. 사실 이는 단순한 문법적 오류이며, 하이버네이트가 어떤 식으로 지연 로딩하는지에 대한 깊은 이해가 부족하기 때문이라고 생각합니다. "그냥 get()하면 가지고 오는 거 아닌가?"라고 생각하기 쉽습니다. 저 또한 그랬습니다. 결론부터 말씀드리면, 스프링 프레임워크에서 지겹게 사용되는 프록시가 원인입니다. 해당 에러들을 해결하며 공부한 것을 정리해보겠습니다. 2) 에러 코드 (모든 코드는 임의로 만들어낸 상황입니다) 테스트 코드는 아직 부족합니다. 참고만 해주세요. public class Player { ... 생략 ... @Ma..
-
JPA N+1 문제를 해결해보자Spring Framework 2021. 8. 20. 17:41
1) N+1 문제란? ORM은 객체의 관계를 연결하는데요. 이를 활용하는 JPA는 엔티티 기준으로 쿼리를 사용하고, 엔티티에 연관된 모든 객체를 조회합니다. 모든 연관된 객체를 조회 하기에 문제가 발생합니다. N개의 결과를 얻기 위해 DB를 조회했는데, 추가적으로 쿼리가 발생하는 것입니다. 즉 얻고자 하는 결과에서 1개가 더 붙어서 조회가 되는 현상입니다. 마트에서 하는 1+1 행사라고 생각하시면 됩니다. "파를 샀더니, 마늘을 주네? 근데 난 마늘 안 먹는데... 이걸 굳이 왜..?" 얻고자 하는 결과 외에 추가로 결과를 얻는다는 것은, 추가 쿼리가 발생합니다. 그만큼 성능적으로 손해 볼 수밖에 없습니다. 2) 전체 구조 // 애노테이션들 생략 했습니다. public class Post { privat..
-
[JPA, Redis]페이지별 결과 캐싱Spring Framework 2021. 8. 11. 17:45
1. 서론 Spring Data Redis와 JPA의 페이징을 이용해서 해당 결과를 캐싱하겠습니다. 2. JPA 페이징 2. 1. query 작성 @Query("from Post") Optional findAllPosts(Pageable pageable); 모든 게시물 목록을 조회합니다 Pageable 인터페이스를 파라미터로 받습니다 2. 2. Test @Test public void findPosts() { Pageable pageRequest = PageRequest.of(0, 10); List posts = postRepository.findAllPosts(pageRequest).orElseThrow(RuntimeException::new); posts.forEach(p -> System.out.p..
-
SpringBoot 테스트 중 Bean을 찾을 수 없을 때Spring Framework 2021. 7. 25. 00:47
에러 내용 스프링부트 테스트 도중 Repository 객체를 @Autowired로 빈 주입을 시도했습니다. 하지만 계속해서 테스트 실패와 함께, 빈을 찾을 수 없다는 에러가 발생했습니다. 그리고 결과적으로 Repository 부분에서 NPE가 발생합니다. 시도 1. @Repository 유무 스프링부트에서 @Component로 편리하게 빈 주입을 할 수 있습니다. @SpringBootAplication이 자동으로 이들을 스캔 후 모든 빈들이 생겨나기 전에 가장 먼저 빈으로 등록해 줍니다. 그리고 @Repository, @Service, @Controller는 좀 더 명확한 layer 구분을 위해서 @Component를 상속받고 있습니다. 하지만 @Repository는 잘 달려있습니다.. 문제가 뭘까요? ..
-
Redis, SpringBoot 연동하기Spring Framework 2021. 7. 16. 17:08
서론 포트폴리오용 백엔드 서버를 구성 후 문득 생각이 들었습니다. "만약에 사람들이 몰려서 DB의 조회가 많이 일어나면 어떡하지?" 물론 아무도 사용하지 않는 사이트지만요ㅎㅎ 검색을 해본 결과 Redis 오픈소스를 사용하면 캐싱을 통해서, 다수의 처리를 쉽게 할 수 있을 것 같았습니다. Spring Data Redis 공식 문서를 참고하여, 기본적인 캐싱을 해보겠습니다. 이 글은 아주 기본적인 Hello World 수준이므로, 더 깊은 내용을 원하시는 분들은 뒤로 가기 누르셔도 됩니다. Redis란? Redis는 in-memory 방식의 데이터 저장소 입니다. 여기서 in-memory 방식이라는 것은 주 메모리(RAM)를 데이터 저장소로 사용하는 데이터베이스 관리 시스템입니다. 즉 SSD, HDD 같은 ..
-
Entity, DTO 그리고 @ServiceSpring Framework 2021. 7. 13. 22:24
서론 Spring Data JPA를 사용하면서 발생하는 여러 문제들 때문에 Entity와 DTO를 분리하는 과정을 담았습니다. 그리고 이 DTO를 어떻게 스프링 컨테이너가 관리하는 Bean으로 등록할 것인지에 대한 고민을 담았습니다. Entity 사용의 문제점 Post(게시글)이 Board(게시판)의 boardNo(게시판 번호) 참조 1개의 게시판에 여러 개의 게시물 --> 1:N (일대다) 매핑 // 애노테이션 생략 public class Board implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long..