오티스의개발일기

[SPRING BOOT + REACT-NATIVE] (8) jpaQueryFactory 와 queryDsl 를 사용하여 이메일 중복확인 기능 만들기 -git 첨부 - 본문

개발/spring boot

[SPRING BOOT + REACT-NATIVE] (8) jpaQueryFactory 와 queryDsl 를 사용하여 이메일 중복확인 기능 만들기 -git 첨부 -

안되면 될때까지.. 2022. 12. 29. 17:31
728x90

 

 

 


< 이전글

2022.12.29 - [개발/spring boot] - [SPRING BOOT + REACT-NATIVE] jpaRepository 를 사용하여 유저 데이터베이스에 저장하기

 

[SPRING BOOT + REACT-NATIVE] jpaRepository 를 사용하여 유저 데이터베이스에 저장하기

오늘은 저번시간에 만든 컨트롤러와 서비스를 가지고 유저를 실제 데이터베이스에 저장해볼것이다. 데이터베이스에 만들기위해서는 repository 가 필요한데 repository 를 구현하는 방법은 여러가지

otis.tistory.com



다음글 >

2022.12.30 - [개발/react-native] - [REACT NATIVE] 리엑트 네이티브 인스타그램 클론 코딩 (9) 구글 로그인후 데이터베이스에 저장하기 + 예외 처리를 통한 비동기 처리 -git 참조-

 

[REACT NATIVE] 리엑트 네이티브 인스타그램 클론 코딩 (9) 구글 로그인후 데이터베이스에 저장하기 +

오늘은 드디어 백엔드와 프론트엔드를 연결 하는날이다.... 여기까지 오느라 고생 내가 제일 많았다 ㅎㅎ 오늘은 구글로 로그인을 시도후 구글로 받은 정보를 실제 데이터베이스에 저장하고 일

otis.tistory.com


이번시간에는 저번 포스팅에이어

유저 아이디 중복확인 하는 기능을

JpaQueryFactory 를 사용하여 구현해보겠다.

 

 

일단 JpaQueryFactory 를 사용하기위해선 디펜던시와 configuration 설정을 해줘야한다

오늘 다룰 파일들 목록을 나열해보겠다.

 

1. build.gradle : 쿼리 dsl 을 사용하기위한 디펜던시 추가

2. QuerydslConfiguration  : 쿼리dsl 설정파일

3. UserRepositorySupport :  jpaRepositroy 처럼 querydslRepositorySupport 를 extends 한 파일

4. UserService : 비지니스 로직을 처리할 파일

5. UserController : 컨트롤러

 

 

시작해보겠다

 

 

# 0. 폴더 구조

 

 

 

 

# 1 . build.gradle 디펜던시 및 설정

 

dsl 관련 주석을 확인하면된다 하나도 빠짐없이 기입하자

buildscript {  // <-- query dsl
    ext {
        queryDslVersion = "5.0.0"
    }
}

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.7.7'   // < ------- 스프링 부트 버전
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" // <-- query dsl
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11' // <------- java 11

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("io.springfox:springfox-boot-starter:3.0.0") // <--------- spring boot starter

    implementation("io.springfox:springfox-swagger-ui:3.0.0") // <--------- 스웨거 ui

    implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"  // <-- query dsl
    implementation "com.querydsl:querydsl-apt:${queryDslVersion}"  // <-- query dsl

    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-aop'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.mysql:mysql-connector-j'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}



//querydsl 추가 시작
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}
sourceSets {
    main.java.srcDir querydslDir
}
compileQuerydsl{
    options.annotationProcessorPath = configurations.querydsl
}
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    querydsl.extendsFrom compileClasspath
}
//querydsl 추가 끝

 

 

 

# 2 . QuerydslConfiguration  설정

package com.example.backend.config;

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Configuration
@EnableJpaAuditing
public class QuerydslConfiguration {

    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

EntityManger 를 JPAQueryFactory 에 등록후 Bean 에 등록해야 동작한다.

 

 

 

 

# 2 . UserRepositorySupport 생성

package com.example.backend.repository.user;

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;

@Repository
public class UserRepositorySupport extends QuerydslRepositorySupport {

}

여기까지 작성하고 나면

 

 

 

 

이렇게 뜰것이다.

 

create 를 클릭하고 부모에있는 메서드를 상속시켜주자

 

 

 

package com.example.backend.repository.user;

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;

@Repository
public class UserRepositorySupport extends QuerydslRepositorySupport {

    /**
     * Creates a new {@link QuerydslRepositorySupport} instance for the given domain type.
     *
     * @param domainClass must not be {@literal null}.
     */
    public UserRepositorySupport(Class<?> domainClass) {
        super(domainClass);
    }
}

그러면 이런식으로 생성될것이다 이제 아래부터 우리가 커스터마이징 해줄것이니 집중하자

 

 

 

*중요*

package com.example.backend.repository.user;

import com.example.backend.domain.User;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;

@Repository
public class UserRepositorySupport extends QuerydslRepositorySupport {

    private final JPAQueryFactory jpaQueryFactory;

    /**
     * Creates a new {@link QuerydslRepositorySupport} instance for the given domain type.
     *
     * @param jpaQueryFactory
     */
    public UserRepositorySupport(JPAQueryFactory jpaQueryFactory) {
        super(User.class);
        this.jpaQueryFactory = jpaQueryFactory;
    }
}

 

이런식으로 매게변수로 클래스를 따로받지않고 도메인(= 엔티티) 마다 각각의 repositorySupport 를 생성해줄것이기 때문에

그냥 super 안에 원하는 엔티티의 클래스를 적어주자

 

 

자이제 쿼리문을 작성해줄건데 그전에 추가해야할것과 확인해야 할것이있다

우리는 이제 QUser.user 를 임포트 해줄것이다 이클래스는 dslQuery에서 자동으로 생성하는데 이미 우리가 build.gradle 에 설정을 해놓았다.

 

이부분이다 dsl이 자동으로 Q클래스를 만들어주고 우리는 사용하기만 하면된다.

이제 email 중복확인 쿼리문을 만들어보자

 

 

아 쿼리문을 만들기전에 서버를 한번 재시작을 해줘야 Q클래스가 생성되니 주의 바란다.

 

 

 

# 2 . UserRepositorySupport  아이디 중복확인 쿼리 생성

package com.example.backend.repository.user;

import com.example.backend.domain.User;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;
import static com.example.backend.domain.QUser.user;

@Repository
public class UserRepositorySupport extends QuerydslRepositorySupport {

    private final JPAQueryFactory jpaQueryFactory;

    /**
     * Creates a new {@link QuerydslRepositorySupport} instance for the given domain type.
     *
     * @param jpaQueryFactory
     */
    public UserRepositorySupport(JPAQueryFactory jpaQueryFactory) {
        super(User.class);
        this.jpaQueryFactory = jpaQueryFactory;
    }
    
    
    public User userEmailDoubleCheck (String email) {
        return jpaQueryFactory.select(user)
                .from(user)
                .where(user.email.eq(email))
                .fetchOne();
    }
}

 

 

자 이제 쿼리문을 만들었으니 서비스와 컨트롤러에 등록하고 기능확인을 해보자

 

 

# 3 . UserService  아이디 중복확인 로직 처리

 

package com.example.backend.service;

import com.example.backend.common.exception.CustomApiException;
import com.example.backend.domain.User;
import com.example.backend.dto.user.request.RequestUserEmailDoubleCheckDto;
import com.example.backend.dto.user.request.RequestUserRegisterDto;
import com.example.backend.repository.user.UserRepository;
import com.example.backend.repository.user.UserRepositorySupport;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;
    private final UserRepositorySupport userRepositorySupport;

    @Transactional(readOnly = false)
    public void register (RequestUserRegisterDto requestUserRegisterDto) {
        User user = requestUserRegisterDto.toEntity();
        userRepository.save(user);
    }


    @Transactional(readOnly = true)
    public Boolean userEmailDoubleCheck (RequestUserEmailDoubleCheckDto requestUserRegisterDto) {
        User user = userRepositorySupport.userEmailDoubleCheck(requestUserRegisterDto.getEmail());
        if (user != null) {
            throw  new CustomApiException("이미 사용중인 아이디 입니다.", HttpStatus.BAD_REQUEST);
        } else {
            return true;
        }
    }
}

 

같은 이메일의 유저가있으면 이전에 만든 CustomApiException 을 호출해 사용자에게 전달한다.

 

 

이제 파라미터로 받을 Dto 생성후 컨트롤러에 적용해보자

 

# 3 . RequestUserEmailDoubleCheckDto 생성

package com.example.backend.dto.user.request;

import lombok.Data;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;

@Data
public class RequestUserEmailDoubleCheckDto {

    @NotBlank(message = "이메일을 입력해주세요")
    @Email(message = "정확한 이메일을 입력해주세요")
    private String email;
}

 

이곳에서도 이메일형식인지 확인하고 정확한 이메일이 아니라면 예외처리를 할것이다.

 

 

 

 

# 3 . UserController 적용

package com.example.backend.controller;

import com.example.backend.common.exception.CustomApiException;
import com.example.backend.dto.CMRespDto;
import com.example.backend.dto.user.request.RequestUserEmailDoubleCheckDto;
import com.example.backend.dto.user.request.RequestUserRegisterDto;
import com.example.backend.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Email;

@RestController
@RequiredArgsConstructor
@CrossOrigin(origins = {"*"})
@RequestMapping("/api/user")
public class UserController {

    private final UserService userService;
    @PostMapping("/register")
    public CMRespDto<?> register (@Valid @RequestBody RequestUserRegisterDto requestUserRegisterDto) {
        userService.register(requestUserRegisterDto);
        return new CMRespDto<>(200, "회원 가입 완료", requestUserRegisterDto);
    }

    @PostMapping("/userEmailDoubleCheck")
    public CMRespDto<?> userEmailDoubleCheck (@Valid  @RequestBody RequestUserEmailDoubleCheckDto requestUserRegisterDto) {
        return new CMRespDto<>(200, "사용 가능한 이메일 입니다.", userService.userEmailDoubleCheck(requestUserRegisterDto));
    }

}

 

아무런 예외가 발생하지 않는다면 자연스럽게 사용가능한 이메일이라는 메시지와 함께 true 를 리턴할것이다.

이제 swagger2 를 통해 확인해보자

 

 

아 그리고 테스트 하기전에 application.yml 의 ddl-auto : create => ddl-auto : update 로 바꾸어주자

create 로 하면 데이터까지 삭제되기 때문에 어떤 이메일을 넣어도 true 가 반환될것이다.

 

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/instagram?serverTimezone=Asia/Seoul
    username: user
    password: '1234'
  mvc:
    pathmatch:
      matching-strategy: path_pattern_parser
  jpa:
    hibernate:
      ddl-auto: update # <-- 매 서버 실행때마다 테이블이 삭제됬다가 다시 만들어짐 당연히 데이터 역시 날라감 하지만 테스트용으로 잠시 씀
    properties:
      hibernate:
        format_sql: true # <-- 실행쿼리를 가독성있게 표현
        show-sql: true # <-- 콘솔에 jpa 실행쿼리
    generate-ddl: true # < -- true로 설정 시, Entity 어노테이션(@Entity)이 명시된 클래스를 찾아서 ddl을 생성하고 실행

 

 

 

테스트 해볼 상황은 3가지이다

1. 이미 존재하는 이메일

2. 형식에 맞지않는 이메일

3. 정상적인 존재하지않는 이메일

 

 

 

1. 이미 존재하는 이메일을 넣어보겠다.

저장되어있는 이메일

 

 

 

2. 형식에 맞지않는 이메일

 

 

 

 

3. 정상적인 존재하지않는 이메일

 

 

 

3개의 상황모두 정상적으로 동작하는걸 볼수있다.

 

 

 

이것으로 포스팅을 마치도록 하겠다

 

 

다음포스팅을 다시 react-native 로 돌아가

회원가입 기능을 구현해보도록하겠다.....

 

 

그럼 다음시간에!!

 

 

 

 

 

깃 첨부

 

https://github.com/1domybest/react-native-ig-clone.git

 

GitHub - 1domybest/react-native-ig-clone

Contribute to 1domybest/react-native-ig-clone development by creating an account on GitHub.

github.com

 


다음글 >

2022.12.30 - [개발/react-native] - [REACT NATIVE] 리엑트 네이티브 인스타그램 클론 코딩 (9) 구글 로그인후 데이터베이스에 저장하기 + 예외 처리를 통한 비동기 처리 -git 참조-


 

728x90
Comments