Spring Boot BE 전환

SangYeon Min·2024년 3월 15일
0

PROJECT-HEARUS

목록 보기
3/12
post-thumbnail

BE Milestone

다른 팀원들과 프로젝트를 조율하고 진행사항을 효과적으로 추적하기 위해 위와 같이 BE 파트의 Milestone을 정리하고, 각 파트 또한 정리를 요청하였다.
또한 위와 같이 팀 Slack에 Github을 연동하여 Repo의 변화를 추적할 수 있게 하였다

기능적 요구사항 정리

Spring Boot, FastAPI 서버에서 기능 구현을 위해 필요한 요소 정리

실시간 Speech To Text

  • Socket 연결을 통한 실시간 음성 Blob 데이터 전송
  • React-Native <> Spring Boot <> FastAPI 양방향 Socket 필요

모바일 화면 자막 표시

Socket 처리 결과 스크립트 추가와 동시에 자막 표시
특정 키워드 하이라이트 자막 표시

  • 전송되는 스크립트 구조화

자연어처리 기능 (문맥 파악, 키워드 추출)

  • 전송되는 스크립트 구조화

강의 페이지 (스크립트, 키워드 시각화)

  • 전송되는 스크립트 구조화
  • 강의 페이지 데이터 저장을 위한 NoSQL Schema 필요

음성 녹음 기능

  • 음성 데이터 저장을 위한 NoSQL Schema 필요

시간표 연동 강의 페이지 저장

  • 시간표 저장 및 표현을 위한 DB Schema 필요

MVP Model

수정된 MVP 모델은 위와 같다
기존의 Express.js 서버의 역할을 Sprigng Boot가 대신하고
MariaDB, MongoDB 모두를 활용하여 여러 종류의 데이터에 대한 유지보수성을 높였다

또한 기존의 Node.js 서버에서 Spring 서버로 변경한 이유는 Single Thread라는 Node.js 서버의 단점 때문이다. 현 서비스의 특성상 실시간 데이터 전송이 빈번하고 전송되는 데이터의 양도 작지 않기 때문에 Muli Threading을 지원하는 Spring Boot로 서버를 변경하였다.

또한 추후 프로젝트의 규모가 커질 경우를 대비하여 코드 구조적으로 더 안정적이고, 테스트 환경 또한 더욱 자세한 Spring Boot로 서버를 변경하였다.


요구 데이터 도식화

요구 데이터는 위와 같이 MariaDB에 저장할 정형 데이터와 MongoDB에 저장할 비정형 데이터로 나누었다.


Spring Boot 프로젝트 세팅

build.gradle

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-rest'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	developmentOnly 'org.springframework.boot:spring-boot-docker-compose'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

위와 같은 Dependencies를 추가하여 프로젝트를 생성한다.

Trouble Shooting

> Could not resolve all files for configuration ':classpath'.
   > Could not resolve org.springframework.boot:spring-boot-gradle-plugin:3.2.3.
     Required by:
         project : > org.springframework.boot:org.springframework.boot.gradle.plugin:3.2.3

이때 위와 같은 에러가 발생한다면 아래 코드처럼 sourceCompatibility를 17로 변경하고

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.3'
	id 'io.spring.dependency-management' version '1.1.4'
}

group = 'com.hearus'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

Gradle의 JVM 버전을 17로 변경해준 후

Project의 SDK 버전을 17로 변경하면

Download https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-engine/1.10.2/junit-platform-engine-1.10.2-sources.jar, took 10 ms (142.16 kB)
Download https://repo.maven.apache.org/maven2/org/objenesis/objenesis/3.3/objenesis-3.3-sources.jar, took 10 ms (52.69 kB)

BUILD SUCCESSFUL in 59s

위와 같이 정장적으로 Gradle Build가 완료되는 것을 볼 수 있다.

Docker Startup

java.lang.IllegalStateException: No Docker Compose file found in directory 

또한 Spring Boot 프로젝트 생성 시 Docker 지원을 활성화했기 때문에 별도로 dockerfile을 설정해주지 않아 위와 같은 에러가 발생하였다.

// application.properties
spring.application.name=HEARUS-SPRING

# Deactivate Docker
spring.docker.compose.enabled=false

이는 application.propertiesspring.docker.compose.enabled를 명시해 해결한다.


Signup Logic

MariaDB Connection

CREATE DATABASE hearus_test;

먼저 Signup Logic을 구현하기 위해, hearus_test DB를 생성해준다

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-rest'
	implementation 'org.mariadb.jdbc:mariadb-java-client'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	developmentOnly 'org.springframework.boot:spring-boot-docker-compose'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

이후 위와 같이 'org.mariadb.jdbc:mariadb-java-client' 의존성을 추가해준다.

application-private.properties

# MariaDB Properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://127.0.0.1:3306/hearus_test
spring.datasource.username=root
spring.datasource.password=root

이때 DB에 관한 정보를 private하게 관리해주기 위해 위와 같이 Profile을 생성하고

application.properties

spring.application.name=HEARUS-SPRING

# Deactivate Docker
spring.docker.compose.enabled=false

# Private Profile Include
spring.profiles.include=private

생성한 Private Profile을 application.properties에 추가한다.

.gitginore 추가

...
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/

### Application Properties ###
application-private.properties

이후 Private Profile에 관한 정보를 .gitginore에 추가해주어 정보를 숨긴다.

2024-03-13T21:22:05.708+09:00  INFO 24456 --- [HEARUS-SPRING] [  restartedMain] c.h.h.HearusSpringApplication            : Starting HearusSpringApplication using Java 17.0.9 with PID 24456 (C:\Users\judem\Git\HEARUS\HEARUS-SPRING-BACKEND\build\classes\java\main started by judem in C:\Users\judem\Git\HEARUS\HEARUS-SPRING-BACKEND)
2024-03-13T21:22:05.711+09:00  INFO 24456 --- [HEARUS-SPRING] [  restartedMain] c.h.h.HearusSpringApplication            : The following 1 profile is active: "private"
2024-03-13T21:22:05.793+09:00  INFO 24456 --- [HEARUS-SPRING] [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
...
2024-03-13T21:22:08.468+09:00  INFO 24456 --- [HEARUS-SPRING] [  restartedMain] c.h.h.HearusSpringApplication            : Started HearusSpringApplication in 3.345 seconds (process running for 4.448)

Data Class Configuration

Signup Logic을 위한 여러 객체들의 데이터 교환 정보는 위와 같다.

Controller와 Service, Handler는 DTO를 활용해 데이터를 교환하고
Handler에서 DTO를 Entitiy로 변환하여 Repository로 보내고 DB에 반영한다.

/data
	/entitiy : Domain, DB에 쓰일 Column과 여러 Entity 연관관계 정의
    			하나의 Entitiy를 DB Table로 생각해도 무방함
    /repository : Entitiy에 의해 생성된 DB에 접근하는 메소드를 사용하기 위한 인터페이스
    			Service와 DB를 연결하는 고리 역할 수행
        		DB에 적용하고자 하는 CRUD를 정의하는 영역
    	/impl
    /dao DataAccessObject, DB에 접근하기 위한 객체
    			직접 DB에 접근하며 Persistent Layer, Service가 DB에 연결할 수 있게 해주는 역할
    	/impl
    /dto DataTransferObject, 계층간 데이터 교환을 위한 객체
dependencies {
	...
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	...
}

이러한 구조를 구현하기 위해 먼저 위와 같은 의존성을 build.gradle에 추가한다.

// BaseEntitiy.java
package com.hearus.hearusspring.data.entitiy;


import lombok.Getter;
import lombok.Setter;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;

import java.time.LocalDateTime;

@Getter
@Setter
@MappedSuperclass
public class BaseEntitiy {
    // @MappedSuperClass
    // 부모 클래스는 테이블과 매핑하지 않고 오로지 부모 클래스를 상속 받는 자식 클래스에게
    // 부모 클래스가 가지는 Column만 매핑정보로 제공
    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

이후 모든 Entitiy에 기본적으로 쓰일 BaseEntitiy를 정의한다.

@MappedSuperclass : 객체의 입장에서 공통 매핑 정보가 필요할 때 사용

// /enumType/GradeType.java
package com.hearus.hearusspring.data.enumType;

public enum GradeType {

    FRESHMEN("FRESHMEN"),
    SOPHOMORE("SOPHOMORE"),
    JUNIOR("JUNIOR"),
    SENIOR("SENIOR"),
    LEAVE_ABSENCE("LEAVE_ABSENCE"),
    GRADUATE("GRADUATE"),
    POST_GRADUATE("POST_GRADUATE");

    private final String key;

    GradeType(String key) {
        this.key = key;
    }
}
// /enumType/OAuthType.java
package com.hearus.hearusspring.data.enumType;

public enum OAuthType {
    KAKAO("KAKAO"),
    GOOGLE("GOOGLE"),
    NAVER("NAVER");

    private final String key;
    OAuthType(String key) {
        this.key = key;
    }
}

이후 추후 데이터 검증을 위한 enum 클래스들을 정의한다

// UserEntitiy.java
package com.hearus.hearusspring.data.entitiy;

import com.hearus.hearusspring.data.dto.UserDTO;
import jakarta.persistence.*;
import lombok.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
@Table(name = "user")
public class UserEntity extends BaseEntitiy{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    String id;

    String name;

    @Column(unique=true)
    String email;

    String password;

    // OAuth인지의 여부 판단
    boolean isOAuth;

    // Oauth 종류
    // KAKAO, GOOGLE, NAVER
    String oauthType;

    String school;
    String major;

    // 재학생 (1, 2, 3, 4), 휴학생, 졸업생
    String grade;

    // DB에는 일반적인 String으로 저장하고
    // ','를 기준으로 ArrayList로 변환
    String savedLectures;

    String schedule;
    String usePurpose;

    public UserDTO toDTO(){
        // ','를 기준으로 split
        ArrayList<String> savedLecturesList = Arrays.stream(
                savedLectures.split(","))
                .map(String::trim)
                .collect(Collectors.toCollection(ArrayList::new));

        return UserDTO.builder()
                .userId(id)
                .userName(name)
                .userEmail(email)
                .userPassword(password)
                .userIsOAuth(isOAuth)
                .userOAuthType(oauthType)
                .userSchool(school)
                .userMajor(major)
                .userGrade(grade)
                .userSavedLectures(savedLecturesList)
                .userSchedule(schedule)
                .userUsePurpose(usePurpose)
                .build();
    }
}

DB와 소통하기 위한 User 정보인 UserEntitiy객체를 위와 같이 선언한다.

이때 toDTO 메소드에서 String 형태로 저장된 UserEntitiy의 savedLecturesList를 ArrayList 형태로 변환할 수 있는 코드를 추가한다.

// UserDTO.java
package com.hearus.hearusspring.data.dto;

import com.hearus.hearusspring.data.entitiy.UserEntity;
import com.hearus.hearusspring.data.enumType.OAuthType;
import jakarta.persistence.Id;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import lombok.*;

import java.util.ArrayList;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class UserDTO {
    @Id
    @NotNull
    String userId;

    @NotNull
    String userName;

    @NotNull
    @Email
    String userEmail;

    @NotNull
    String userPassword;

    @NotNull
    boolean userIsOAuth;
    String userOAuthType;

    String userSchool;
    String userMajor;
    String userGrade;

    ArrayList<String> userSavedLectures;

    String userSchedule;
    String userUsePurpose;

    public UserEntity toEntitiy(){
        String stringLecture = String.join(",", userSavedLectures);

        return UserEntity.builder()
                .id(userId)
                .name(userName)
                .email(userEmail)
                .password(userPassword)
                .isOAuth(userIsOAuth)
                .oauthType(userOAuthType)
                .school(userSchool)
                .major(userMajor)
                .grade(userGrade)
                .savedLectures(stringLecture)
                .schedule(userSchedule)
                .usePurpose(userUsePurpose)
                .build();
    }
}

이후 UserDTO 객체를 추가한다.

// UserDAO.java
package com.hearus.hearusspring.data.dao;

import com.hearus.hearusspring.data.entitiy.UserEntity;

public interface UserDAO {
    UserEntity userLogin(UserEntity user);
    boolean userSignup(UserEntity user);
}
// UserDAOImpl.java
package com.hearus.hearusspring.data.dao.impl;

import com.hearus.hearusspring.data.dao.UserDAO;
import com.hearus.hearusspring.data.entitiy.UserEntity;
import com.hearus.hearusspring.data.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserDAOImpl implements UserDAO {

    UserRepository userRepository;
    private final Logger LOGGER = LoggerFactory.getLogger(UserDAOImpl.class);

    // Todo : Create Login Function
    @Override
    public UserEntity userLogin(UserEntity user) {
        return null;
    }

    @Override
    public boolean userSignup(UserEntity user) {
        LOGGER.info("[UserDAO]-[userSignup] UserEntitiy 저장 : {}", user.getEmail());

        // 이메일 중복 여부 확인
        if(userRepository.existsByEmail(user.getEmail())){
            LOGGER.info("[UserDAO]-[userSignup] 회원 이메일 중복 : {}", user.getEmail());
            return false;
        }

        userRepository.save(user);
        LOGGER.info("[UserDAO]-[userSignup] UserEntitiy 저장 성공 : {}", user.getEmail());
        return true;
    }
}

UserDAO 인터페이스를 생성하고 현재는 userRepository를 통해 Signup을 구현하는 코드만 추가한다.

// UserRepository.java
package com.hearus.hearusspring.data.repository;

import com.hearus.hearusspring.data.entitiy.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<UserEntity, String> {

    // Find By
    UserEntity findFirstById(String id);
    UserEntity findFirstByEmail(String email);
    UserEntity findFirstByEmailAndPassword(String email, String password);


    // Exist By
    boolean existsByEmail(String email);
}

UserRepository에서는 이메일 중복 여부를 확인하기 위해 위와 같은 쿼리만 추가한다.

Handler

// UserHandler.java
package com.hearus.hearusspring.data.handler;

import com.hearus.hearusspring.data.dto.UserDTO;

public interface UserHandler {
    boolean signupUserEntitiy(UserDTO user);
}
// UserHandlerImpl.java
package com.hearus.hearusspring.data.handler.impl;

import com.hearus.hearusspring.data.dao.UserDAO;
import com.hearus.hearusspring.data.dto.UserDTO;
import com.hearus.hearusspring.data.entitiy.UserEntity;
import com.hearus.hearusspring.data.handler.UserHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserHandlerImpl implements UserHandler {
    UserDAO userDAO;
    private final Logger LOGGER = LoggerFactory.getLogger(UserHandlerImpl.class);
    @Override
    public boolean signupUserEntitiy(UserDTO user) {
        LOGGER.info("[UserHandler]-[signupUserEntitiy] UserDAO로 UserEntitiy 회원가입 요청 : {}", user.getUserEmail());
        UserEntity userEntity = user.toEntitiy();
        return userDAO.userSignup(userEntity);
    }
}

UserHandler에서는 UserDTO를 받아 UserEntitiy로 변환하여 UserDAO로 요청을 보낸다.

Service

// UserService.java
package com.hearus.hearusspring.service;

import com.hearus.hearusspring.data.dto.UserDTO;

public interface UserService {
    boolean userSignup(UserDTO user);
}
// UserServiceImpl
package com.hearus.hearusspring.service.impl;

import com.hearus.hearusspring.data.dto.UserDTO;
import com.hearus.hearusspring.data.handler.UserHandler;
import com.hearus.hearusspring.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service
public class UserServiceImpl  implements UserService {
    UserHandler userHandler;
    private final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
    @Override
    public boolean userSignup(UserDTO user) {
        LOGGER.info("[UserService]-[userSignup] UserHandler로 회원가입 요청 : {}", user.getUserEmail());
        userHandler.signupUserEntitiy(user);
        return true;
    }
}

UserService에서는 UserController로부터 UserDTO 객체를 전달받아 UserHandler로 요청한다.

Controller

// /common/CommonResponse.java
package com.hearus.hearusspring.common;

import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpStatus;

@Getter
@Setter
public class CommonResponse {
    private boolean isSuccess;
    HttpStatus resCode;
    private String msg;

    public CommonResponse(boolean isSuccess, String msg) {
        this.isSuccess = isSuccess;
        this.msg = msg;
    }

    public CommonResponse(boolean isSuccess, HttpStatus resCode, String msg) {
        this.isSuccess = isSuccess;
        this.resCode = resCode;
        this.msg = msg;
    }
}

Controller를 구현하기 이전 하나의 요청에 대한 Response를 효율적으로 관리하기 위해 위와 같이 CommonResponse객체를 구현하고 Service, Handler, DAO의 메소드의 return 타입을 CommonResponse으로 설정하여 Client로의 HTTP Repsonse를 더 효과적으로 구현할 수 있게 하였다.

// UserDAO.java
public class UserDAOImpl implements UserDAO {

    UserRepository userRepository;
    private final Logger LOGGER = LoggerFactory.getLogger(UserDAOImpl.class);

    // TODO : Create Login Function
    @Override
    public UserEntity userLogin(UserEntity user) {
        return null;
    }

    @Override
    public CommonResponse userSignup(UserEntity user) {
        LOGGER.info("[UserDAO]-[userSignup] UserEntitiy 저장 : {}", user.getEmail());

        // 이메일 중복 여부 확인
        if(userRepository.existsByEmail(user.getEmail())){
            LOGGER.info("[UserDAO]-[userSignup] 회원 이메일 중복 : {}", user.getEmail());
            return new CommonResponse(false, HttpStatus.CONFLICT,"User Already Exists");
        }

        userRepository.save(user);
        LOGGER.info("[UserDAO]-[userSignup] UserEntitiy 저장 성공 : {}", user.getEmail());
        return new CommonResponse(true,HttpStatus.CREATED,"Signup Success");
    }
}

위와 같이 UserDAO에서 회원 이메일 중복으로 적용에 실패했을 경우 CommonResponse 객체에 정보를 담아 Controller까지 전달할 수 있도록 구조를 설계하였다.

package com.hearus.hearusspring.controller;

import com.hearus.hearusspring.common.CommonResponse;
import com.hearus.hearusspring.data.dto.UserDTO;
import com.hearus.hearusspring.service.UserService;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/auth")
public class UserAuthController {
    private final Logger LOGGER = LoggerFactory.getLogger(UserAuthController.class);

    private final UserService userService;
    private CommonResponse response;


    @Autowired
    public UserAuthController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping(value="/signup")
    public ResponseEntity<CommonResponse> signupUser(@Valid @RequestBody UserDTO userDTO){
        LOGGER.info("[UserAuthController]-[signupUser] API Call");

        // 요구되는 데이터 존재 여부 검증
        if(userDTO.getUserName().isEmpty() || userDTO.getUserEmail().isEmpty() || userDTO.getUserPassword().isEmpty()){
            LOGGER.info("[UserAuthController]-[signupUser] Failed : Empty Variables");
            response = new CommonResponse(false,HttpStatus.BAD_REQUEST,"Empty Variables");
            return ResponseEntity.status(response.getStatus()).body(response);
        }

        // UserService로 요청받은 UserDTO 회원가입 요청
        userService.userSignup(userDTO);

        LOGGER.info("[UserAuthController]-[signupUser] Success");
        response = new CommonResponse(true,HttpStatus.CREATED,"Signup Success");
        return ResponseEntity.status(response.getStatus()).body(response);
    }
}

최종적으로 위와 같이 UserController를 구현하여 테스트를 진행하였다.

Trouble Shooting

Error creating bean with name 'entityManagerFactory' defined in class path resource

implementation group: 'org.javassist', name: 'javassist', version: '3.15.0-GA'

위 오류는 JDBC관련 오류로 위와 같이 javassist 의존성을 추가하고

spring.datasource.url=jdbc:mariadb://127.0.0.1:3308/hearus_test

datasource.url를 다시 점검하여 해결할 수 있다.

NullPointerException: Cannot invoke ... because "this.userHandler" is null

2024-03-15T22:29:40.020+09:00  INFO 29780 --- [HEARUS-SPRING] [nio-8080-exec-3] c.h.h.controller.UserAuthController      : [UserAuthController]-[signupUser] API Call
2024-03-15T22:29:40.020+09:00  INFO 29780 --- [HEARUS-SPRING] [nio-8080-exec-3] c.h.h.service.impl.UserServiceImpl       : [UserService]-[userSignup] UserHandler로 회원가입 요청 : judemin@konkuk.ac.kr
2024-03-15T22:29:40.021+09:00 ERROR 29780 --- [HEARUS-SPRING] [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.NullPointerException: Cannot invoke "com.hearus.hearusspring.data.handler.UserHandler.signupUserEntitiy(com.hearus.hearusspring.data.dto.UserDTO)" because "this.userHandler" is null] with root cause

java.lang.NullPointerException: Cannot invoke "com.hearus.hearusspring.data.handler.UserHandler.signupUserEntitiy(com.hearus.hearusspring.data.dto.UserDTO)" because "this.userHandler" is null

해당 에러는 @Autowired되어야 하는 객체들이 주입되지 않아 발생하는 오류로 각 인터페이스, 그리고 해당 인터페이스를 활용하는 객체의 생성자에 @Autowired 해주어 해결할 수 있다.

@Service
@Transactional
public class UserHandlerImpl implements UserHandler {
    UserDAO userDAO;
    ...

@Service : 비즈니스 로직을 처리하는 서비스(Service) 클래스에 적용
@Transactional : 해당하는 메서드를 실행할 때 스프링은 트랜잭션을 시작하고, 메서드가 정상적으로 종료되면 트랜잭션을 commit하고, 예외가 발생하면 트랜잭션을 rollback
@Autowired : 필요한 의존 객체(Bean)의 Type에 대당하는 Bean을 찾아서 주입

SignupTest

위와 같이 정상적으로 회원가입이 동작하고
이메일 중복시 CommonResponse 객체를 통해 결과가 잘 전달되는 것을 볼 수 있다.

@EnableJpaAuditing
@SpringBootApplication
public class HearusSpringApplication {

	public static void main(String[] args) {
		SpringApplication.run(HearusSpringApplication.class, args);
	}

}
@Getter
@Setter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntitiy {
	...

EnableJpaAuditing을 통해 Auditing을 활성화해주면
위와 같이 정상적으로 BaseEntitiycreated_at 값이 들어가는 것을 볼 수 있다.

2024-03-15T22:59:53.527+09:00  INFO 2196 --- [HEARUS-SPRING] [nio-8080-exec-3] c.h.h.controller.UserAuthController      : [UserAuthController]-[signupUser] API Call
2024-03-15T22:59:53.527+09:00  INFO 2196 --- [HEARUS-SPRING] [nio-8080-exec-3] c.h.h.service.impl.UserServiceImpl       : [UserService]-[userSignup] UserHandler로 회원가입 요청 : judemin@konkuk.ac.kr
2024-03-15T22:59:53.528+09:00  INFO 2196 --- [HEARUS-SPRING] [nio-8080-exec-3] c.h.h.data.handler.impl.UserHandlerImpl  : [UserHandler]-[signupUserEntitiy] UserDAO로 UserEntitiy 회원가입 요청 : judemin@konkuk.ac.kr
2024-03-15T22:59:53.529+09:00  INFO 2196 --- [HEARUS-SPRING] [nio-8080-exec-3] c.h.h.data.dao.impl.UserDAOImpl          : [UserDAO]-[userSignup] UserEntitiy 저장 : judemin@konkuk.ac.kr
2024-03-15T22:59:53.624+09:00  INFO 2196 --- [HEARUS-SPRING] [nio-8080-exec-3] c.h.h.data.dao.impl.UserDAOImpl          : [UserDAO]-[userSignup] UserEntitiy 저장 성공 : judemin@konkuk.ac.kr
2024-03-15T22:59:53.654+09:00  INFO 2196 --- [HEARUS-SPRING] [nio-8080-exec-3] c.h.h.controller.UserAuthController      : [UserAuthController]-[signupUser] Success

0개의 댓글