티스토리 뷰

반응형

https://golf-dev.tistory.com/33

 

프로젝트 JPA 성능 개선기 (1) - 로그인 시 성능을 올려보자~!

OneToOne은 기본적으로 Lazy 로딩을 지원하지 않는다. 그렇기 때문에 조회 시 Lazy로 설정 시 다음과 같은 문제가 발생한다. 일단 사전에 코드를 보여주면 다음과 같다. Member.java public class Member extends.

golf-dev.tistory.com

 

전 글을 꼭 읽고 오시기 바란다. 이제부터 불필요한 Join문을 없애 보도록 하겠다. 

 

추가 된 기술 스택 : QueryDSL

 

QueryDSL은 Projections를 통해 DTO나 다른 객체에 접근하여 그에 필요한 데이터만을 조회하여 반환할 수 있다. 이 방법을 통해 우린 영속화된 Entity에 접근하지 않아 연관관계에 영향을 받지 않고 쿼리를 날릴 수 있다. 

 

AuthService.java

public TokenDTO login(final Email email, final Password password) {
    final String userPw = password.password();

    CustomUserDetails userDetails = memberRepository.findByEmail(email)
    	    .map(CustomUserDetails::of)
            .orElseThrow(() -> new MemberNotFoundException(ErrorCode.USER_NOT_FOUND));

    UsernamePasswordAuthenticationToken token
            = new UsernamePasswordAuthenticationToken(userDetails, userPw);

    Authentication authenticate = managerBuilder.getObject().authenticate(token);
    SecurityContextHolder.getContext().setAuthentication(authenticate);

    return tokenProvider.createToken(userDetails.getId(), authenticate);
}

로그인 시 CustomUserDetails 클래스를 갖고 오게 된다. 그렇다면 Entity에 기반해서 쿼리를 날리지 않고 CustomUserDetails에 기반하여 쿼리를 날리게 되면 불필요한 Join문이 생성되지 않을 것이다.

 

CustomUserDetails에 기반한 QueryRepository가 필요하다. 

 

MemberCustomRepositoryImpl.java

public Optional<CustomUserDetails> findByIdWithDetails(Long memberId) {
    return Optional.ofNullable(
            query.select(Projections.constructor(CustomUserDetails.class,
                            member.id.as("id"),
                            member.email,
                            member.password,
                            member.role))
                    .from(member)
                    .where(member.id.eq(memberId), member.activated.eq(true))
                    .fetchOne());
}

CustomUserDetails에는 id, email, password, role이 필요하다. 그렇기 때문에 Projections를 이용해서 CustomUserDetails에 public 생성자(@AllArgs)에 접근하여 쿼리를 날려준다. 그러면 이제 AuthService 로직을 바꿔보자.

 

AuthService.java

public TokenDTO login(final Email email, final Password password) {
    final String userPw = password.password();

    CustomUserDetails userDetails = memberQueryRepository.findByEmail(email)
            .orElseThrow(() -> new MemberNotFoundException(ErrorCode.USER_NOT_FOUND));

    UsernamePasswordAuthenticationToken token
            = new UsernamePasswordAuthenticationToken(userDetails, userPw);

    Authentication authenticate = managerBuilder.getObject().authenticate(token);
    SecurityContextHolder.getContext().setAuthentication(authenticate);

    return tokenProvider.createToken(userDetails.getId(), authenticate);
}

CustomUserDetails를 바로 불러오는 것을 알 수 있다. 그렇다면 이제 결과를 볼 차례이다. 테스트를 해보자. 

보다 싶이 Join문이 완전 사라진 것을 볼 수 있다. 이제 더 이상 로그인을 할 때 불필요한 데이터를 조회하지 않는다.(불필요한 데이터를 추가로 지우기 위해 password 삭제) 

 

고찰

성능을 개선하며 JPA 성능에 대한 고민을 하게 되었다. JPA는 쿼리 기반의 개발을 줄이고 좀 더 객체지향적인 개발에 집중할 수 있도록 나온 ORM 프레임 워크이다. 하지만 객체기반의 쿼리는 오히려 전체적인 성능을 떨어트릴 때가 있다. 우리는 이러한 실수를 하지 않기 위해 JPA에 대한 학습을 꾸준히 하여 스키마 기반의 쿼리를 날리는 정도의 성능을 내야할 필요가 있다고 생각한다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함