전체 글 364

QueryDsl에서 Index Hint 사용하기!

MySQL 5.7을 사용하는 프로젝트의 QueryDSL 동적 쿼리에서 특정 경우에 인덱스를 안타는 문제가 발생했다. 운영 중인 서비스에서 커버링인덱스를 탈 수 있는 상황에서는 인덱스를 선택했지만, 인덱스에 없는 컬럼 정렬 등에서 PK를 타서 쿼리가 밀리는 현상이 자주 생겼다. 그래서 Index Hint를 QueryDsl에서 사용할 수 있도록 조치가 필요했다. 아래는 해당 처리를 위해 길을 떠나면서 얻게된 방법들이다. 1. JPASQLQuery querydsl-jpa는 JPASQLQuery라는 것을 제공한다. 아래는 해당 클래스의 설명이다. JPASQLQuery is an SQLQuery implementation that uses JPA Native SQL functionality to execute q..

Server/Spring JPA 2023.12.08

Spring 공식 문서 정독 후 몰랐던 것 정리!

최근에 Spring 기술을 정확하게 모른다는 생각이 들었다. 그래서 Spring에 대해 더 자세히 알고 사용하고 싶고 더 깊은 레벨로 문제를 해결하고 싶어서 공식 문서를 정독하기로 했다. 아래는 Spring 공식 문서를 정독하여 얻은 지식을 기록한 것이다. 공유보다는 기록이 목적이라 가독성이 매우 좋지 않다. 새롭게 알게 되었거나 리마인드할 필요가 있는 것 정리 빈은 정적 팩토리 메서드로도 생성할 수 있다. 빈은 2개 이상의 이름을 가질 수 있다. @Bean 애노테이션을 사용할 경우 name 옵션을 ,로 연결하면 된다. 지연 초기화를 사용하면 특수한 경우 성능을 Safe 할 수 있다. (DI도 지연이 가능하다.) final class는 프록시를 생성할 수 없다. (final method가 있어서도 안된다..

Etc./개발 일기 2023.12.02

MySQL - 인덱스를 사용해도 느린 이유! (key_len, filtered)

Optimizer가 원하는 Index를 사용했으면 최적의 쿼리인걸까?! 그렇지 않다! 예시를 통해 알아보자. 아래는 예시를 위해 생성한 Index이다. 아래의 쿼리를 실행시키면 어떻게 될까? explain select * from employees where office_id = 1 and money = 50; key로 custom_index가 선택되었다. 글자가 너무 작아서 중요한 부분을 확대했다. 실행 시간 해당 쿼리의 실행 시간은 548ms 이다. Index도 원하는 대로 탔고, 매우 간단한 데 쿼리임에도 불구하고 매우 오래걸린다. 뭐가 잘못된 걸까..? 1. key_len 다시 실행 계획을 보자. 여기서 가장 중요한 지표는 key_len이다. key_len은 쿼리 조건으로 사용된 인덱스 컬럼들의 ..

Database/SQL 2023.11.27

SQL - WHERE 절, ON 절 제대로 이해하기!

최근에 팀원 분이 LEFT OUTER JOIN의 ON 절에 일반 조건이 포함된 쿼리를 작성하신 것을 봤다. SELECT * FROM team t LEFT OUTER JOIN member m ON t.id = m.team_id AND m.team_id = 4; 해당 부분이 예상대로 동작하지 않을 것을 예상하고 리뷰를 드리면서, 생각보다 잘 모르시는 분이 많으실 것 같아 정리하게 되었다. 미리 말하지만 해당 SQL은 의도대로 동작하지 않는다. ON 절과 WHERE 절의 차이에 대해 알아보자. Sample Data 삽입 테스트를 위해 데이터를 삽입했다. Team을 5개 삽입 각 팀별 멤버를 2개 삽입 WHERE SELECT * FROM team t LEFT OUTER JOIN member m ON t.id =..

Database/SQL 2023.11.21

JPA - OSIV 제대로 이해하기!

OSIV(Open Session In View) OSIV(Open Session In View)는 영속성 컨텍스트를 뷰까지 개방하는 기능이다. 이를 사용하면 트랜잭션이 종료되어도 영속성 컨텍스트가 관리될 수 있다. 여기까지는 사실 다 아는 내용이고 OSIV의 동작 원리에 대해 알아보자. OpenInView Spring 환경에서 보통 아래의 property로 OSIV 설정에 접근한다. spring.jpa.open-in-view 아래는 JpaWebConfiguration 클래스의 애노테이션이다. open-in-view 속성에 따라 해당 Configuration이 등록된다는 것을 알 수 있다. 이를 통해 알 수 있는 놀라운 사실은 'Spring JPA도 웹과 연관이 있다.'는 것이다. JpaProperties..

Server/Spring JPA 2023.11.19

Spring에서의 Proxy, AOP 동작원리 이해하기!

지난번 Spring AOP를 적용하면서 생겼던 문제에 대해 소개했다. https://jaehoney.tistory.com/375 이번 포스팅에서는 실무보다는 기본 개념에 대해 집중적으로 알아보자. 해당 포스팅은 김영한님의 스프링 핵심 원리 - 고급편의 프록시 관련 내용을 정리한 것이며, 프록시와 AOP의 동작의 기본 개념이라고 보면 된다. 프록시 패턴 프록시 패턴에서는 프록시가 너무 많이 생기는 문제가 있다. 아래는 GOF 프록시 패턴의 예시이다. 예로 들면 Repository 1개마다 전부 프록시 클래스를 생성해야 한다. 프록시를 적용할 클래스가 100개라면 100개의 프록시를 적용하는 코드를 만들어야 한다. 즉, 단일 책임 원칙에 어긋나고 기능 변경 시 다수의 클래스에 변경이 전파된다. 이 문제를 해..

Server/Spring 2023.11.16

Hibernate @Where 애노테이션이 동작하지 않는 이유!

@Where 애노테이션이 동작하지 않는 이유실무에서 Learning 테이블 레코드를 일괄 수정 시 인덱스를 타지 않는 문제가 발생했다.이유는 @Where(clause = "del_flag = 'N'")을 했는데, QueryDsl을 사용한 조회에서는 해당 조건을 수행하지 않아서 인덱스를 탈 수 없었다.요즘 Hibernate 및 JPA에 대한 관심이 부쩍 늘어서 이 문제에 접근해보면 경험치를 얻을 수 있을 것 같아서 디버깅해봤다.문제 파악다른 코드에 대한 영향 없이 확인해야 하기 때문에 새로 프로젝트를 만들었다.아래의 JPA Entity를 만들었다.@Entity@Getter@NoArgsConstructor(access = AccessLevel.PROTECTED)@Where(clause = "name = 'v..

Server/Spring JPA 2023.11.05

Spring의 Servlet에 X-Forwarded-For 헤더가 왜 안들어올까?!

아래는 실무 중에 만난 이슈와 해결 과정에 대해 다룬다.정확한 원인과 해결 방법보다는 풀어나간 과정에 집중되어 있다.원인과 해결 방안이 궁금한 분은 가장 아래로 내리시면 될 것 같습니다!헤더에 X-Forwarded-For이 안들어오는 문제 발견아래는 공통 라이브러리의 코드의 일부 코드를 요약한 것이다.public class HeaderUtil { static public HeaderInfo initHeaderInfo(HttpServletRequest request){ return HeaderInfo.builder() .token(request.getHeader(HeaderInfo.JWT_HEADER)) .clientIp(request..

Server/Spring 2023.10.26

단위 테스트 대상 분리하기!

아래는 최범균님의 유튜브를 보고 느낀 점을 나름대로 정리한 것이다. https://www.youtube.com/watch?v=qZ1R0C_iiV4 테스트 불가능한 문제 아래의 코드가 테스트가 불가능한 문제가 있었다고 한다. ResultBuilder builder = ...; InputPeriod realPeriod = mapper.selectPeriod(param); // 1. DB에서 읽음 if (realPeriod == null) { // 2. 없으면 다른 값 읽음 InputPeriod expectedPeriod = mapper.selectExpectedPeriod(otherParam); builder.period(expectedPeriod).type(EXPECTED); } else if (realP..

Accept 헤더가 포함된 REST API에서 에러를 내려주는 방법!

이번 포스팅은 회사에서 메일 원문 다운로드 API를 개발하면서 생긴 이슈에 대해 공유한다. 이해하기 쉽도록 메일 원문 대신 첨부 파일로 재해석해서 작성했다. 요즘은 파일 스토리지로 외부 인프라를 많이 사용해서 직접 설계해야 되는 상황이 드물긴 하다. 파일 접근 게시판의 첨부파일 기능을 생각해보자. 메타 데이터에는 빨간 네모와 같이 파일명과 확장자, 파일 사이즈, 다운로드 경로 등이 저장될 것이다. 다운로드를 클릭하면 실제 파일 스토리지에 접근해서 파일을 Binary 형태로 가져올 것이다. 게시판을 노출하기 위해서 메타 데이터를 가져올 때 파일 정보까지 모두 가져오면 오버헤드가 발생할 것이다. 그래서 2개의 End-point로 분리했다. Accept REST API에서는 url에 자원에 대한 경로를 지정한..

Server/Spring 2023.10.20