JAVA

[SpringBoot]인스타그램 3강 회원가입

instagram

[SpringBoot]인스타그램 3강 회원가입

시큐리티 세팅

시큐리티 라이브러리를 사용하여 인증이 되지 않은 모든 사용자의 진입을 로그인 페이지로 이동하게 만들 수 있다.

package com.cos.photogramstart.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity // 해당 파일로 시큐리티를 활성화
@Configuration // Ioc 
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // super 삭제 - 기존 시큐리티가 가지고 있는 기능을 모두 비활성화.
    http.authorizeRequests()
      .antMatchers("/","/user/**","/image/**","/subscribe/**","/comment/**").authenticated() // 이 주소는 인증이 필요한 페이지다. 
      .anyRequest().permitAll() // 그 외 나머지 페이지는 인증이 필요 없다.
      .and()
      .formLogin() 
      .loginPage("/auth/signin")  // 인증이 필요한 페이지로 이동하면 자동으로 이동하는 페이지(로그인 페이지)   
      .defaultSuccessUrl("/"); // 로그인이 정상적으로 이루어지면 이동하는 페이지 
  }

}
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  1. config 패키지에 SecurityConfig 클래스를 만든다.
  2. 스프링부트의 시큐리티를 사용하기 위해 WebSecurityConfigurerAdapter를 extends 한다.
  3. IoC 컨테이너에 띄우기 위해 @Configuration 어노테이션을 추가한다.
  4. SecurityConfig.java 파일로 시큐리티를 활성화하기 위해 @EnableWebSecurity 어노테이션을 추가한다.

CSRF 토큰 비활성화

CSRF(Cross Site Request Forgery) 토큰이란 스프링부트 시큐리티가 자동 생성해주는 임의의 난수를 모든 요청에 포함시켜 비정상적인 접근을 막아주는 기술이다. 이 프로젝트에서는 CSRF 토큰을 비활성화하여 사용한다. 다음 코드를 SecurityConfig.java 파일에 추가하면 CSRF 토큰을 비활성화 할 수 있다.

package com.cos.photogramstart.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity // 해당 파일로 시큐리티를 활성화
@Configuration // Ioc
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // super 삭제 - 기존 시큐리티가 가지고 있는 기능을 모두 비활성화.
    http.csrf().disable(); // csrf 토큰 비활성화
    http.authorizeRequests().antMatchers("/", "/user/**", "/image/**", "/subscribe/**", "/comment/**")
        .authenticated() // 이 주소는 인증이 필요한 페이지다.
        .anyRequest().permitAll() // 그 외 나머지 페이지는 인증이 필요 없다.
        .and().formLogin().loginPage("/auth/signin") // 인증이 필요한 페이지로 이동하면 자동으로 이동하는 페이지(로그인 페이지)
        .defaultSuccessUrl("/"); // 로그인이 정상적으로 이루어지면 이동하는 페이지
  }

}

DTO 생성

회원가입 정보를 받기위한 데이터 트렌스 오브젝트 (DTO)를 생성하고 데이터베이스에 저장된 데이터를 담을 함수를 빌드패턴으로 만들어준다.

package com.cos.photogramstart.web.dto.auth;

import com.cos.photogramstart.domain.user.User;

import lombok.Data;

@Data
public class SignupDto {

  private String username;
  private String password;
  private String email;
  private String name;
  
  public User toEntity() {
    return User.builder()
        .username(username)
        .password(password)
        .email(email)
        .name(name)
        .build();
  }
  
}

모델과 레파지토리 생성

데이터베이스에 인서트하기 위한 모델과 데이터베이스에 접근하기 위한 레파지토리를 생성한다.

package com.cos.photogramstart.domain.user;

import java.time.LocalDateTime;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//JPA - Java Persistence API(자바로 데이터를 영구적으로 저장(DB)할 수 있는 API를 제공)

@Table(name = "user")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class User {
  
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY) // 번호 증가 전략이 데이터 베이스를 따라간다. 
  private int id;
  
  private String username;
  private String password;
  
  private String name;
  private String website;
  private String bio; // 자기소개 
  private String email;
  private String phone;
  private String gender;
  
  private String profileImageUrl; // 사진 
  private String role; // 권한 
  
  private LocalDateTime createData;
  
  @PrePersist // DB에 INSERT 되기 직전에 실행  
  public void createDate() {
    this.createData = LocalDateTime.now();
  }

}
package com.cos.photogramstart.domain.user;

import org.springframework.data.jpa.repository.JpaRepository;

// 어노테이션이 없어도 JpaRepository를 상속하면 IoC 등록이 자동으로 된다. 
public interface UserRepository extends JpaRepository<User, Integer>{

}

서비스 생성

데이터베이스 관련 작업을 관리할 서비스를 생성한다.

spring
스프링부트 서비스 레이어 사용하는 이유컨트롤러에서 이러한 트렌젝션을 관리하게 되면 데이터베이스에 write하는 동안 다른 사용자의 접근이 불가능해진다. 이러한 문제를 해결하기 위해서 데이터베이스 접근을 하지 못하게 만드는 트렌젝션을 서비스 레이어에서 관리하게한다. ...
package com.cos.photogramstart.Service;

import org.springframework.stereotype.Service;

import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service // IoC 컨테이너에 띄우고 트렌젝션 관리한다.
public class AuthService {
  
  private final UserRepository userRepository;
  
  public User 회원가입(User user) {
    User userEntity = userRepository.save(user);
    return userEntity;
  }
}

컨트롤러

로그인과 회원가입 페이지로 이동하는 컨트롤러를 만들고 회원가입 작업을 수행할 컨트롤러를 만든다.

package com.cos.photogramstart.web;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.cos.photogramstart.Service.AuthService;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.web.dto.auth.SignupDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor // final 필드를 DI할 때 
@Controller
public class AuthController {
  
  private static final Logger log = LoggerFactory.getLogger(AuthController.class);
  
  private final AuthService authService;

  // 로그인 페이지 이동
  @GetMapping("/auth/signin")
  public String signinForm() {
    return "auth/signin";
  }

  // 회원가입 페이지 이동
  @GetMapping("/auth/signup")
  public String signupForm() {  
    return "auth/signup";
  }

  // 회원가입 하기 
  @PostMapping("/auth/signup")
  public String signup(SignupDto dto) {
    User user = dto.toEntity();
    User UserEntity = authService.회원가입(user);
    System.out.println(UserEntity);
    return "auth/signin";
  }

}

회원가입 폼 연결

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Photogram</title>
    <link rel="stylesheet" href="/css/style.css">
    <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css"
        integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous" />
</head>

<body>
    <div class="container">
        <main class="loginMain">
           <!--회원가입섹션-->
            <section class="login">
                <article class="login__form__container">
                  
                   <!--회원가입 폼-->
                    <div class="login__form">
                        <!--로고-->
                        <h1><img src="/images/logo.jpg" alt=""></h1>
                         <!--로고end-->
                         
                         <!--회원가입 인풋-->
                        <form class="login__input" action="/auth/signup" method="post">
                            <input type="text" name="username" placeholder="유저네임" required="required" />
                            <input type="password" name="password" placeholder="패스워드" required="required" />
                            <input type="email" name="email" placeholder="이메일" required="required" />
                            <input type="text" name="name" placeholder="이름" required="required" />
                            <button>가입</button>
                        </form>
                        <!--회원가입 인풋end-->
                    </div>
                    <!--회원가입 폼end-->
                    
                    <!--계정이 있으신가요?-->
                    <div class="login__register">
                        <span>계정이 있으신가요?</span>
                        <a href="/auth/signin">로그인</a>
                    </div>
                    <!--계정이 있으신가요?end-->
                    
                </article>
            </section>
        </main>
    </div>
</body>

</html>

비밀번호 암호화 & 권한 부여

public class SecurityConfig extends WebSecurityConfigurerAdapter { 

@Bean
public BCryptPasswordEncoder encode() {
  return new BCryptPasswordEncoder();
}
@Transactional // Write(inter, update, delete) 시에 붙여주는 어노테이션 
public User 회원가입(User user) {
  String rawPawssword = user.getPassword();
  String encPassword = bCryptPasswordEncoder.encode(rawPawssword); // 비밀번호를 암호화(해쉬)하여 넣기. 
  user.setPassword(encPassword); // 암호화한 비밀번호를 모델에 담기.
  user.setRole("ROLE_USER"); // 모든 사용자에게 ROLE_USER 권한 부여. 관리자에게는 ROLE_ADMIN으로 권한을 부여할 예정.
  User userEntity = userRepository.save(user);
  return userEntity;
}
데이터베이스
같은 ID로 회원가입 했을 경우(제약조건 위배)

유효성 검사(Validation)

회원가입과 같은 핵심기능에는 다른 핵심기능(로그인, 회원정보 수정 등)과 공통적인 전후처리 부가기능을 넣는데 이러한 프로그래밍 방법을 관점지향프로그래밍(AOP)이라고 한다. 일반적으로 전처리로 유효성 검사를 하고 후처리로 예외처리를 한다. 데이터베이스에 접근할 필요가 없는 경우 전처리, 데이터베이스에 접근할 필요가 있는 경우 후처리를 한다.

메이븐 레파지토리에서 validation 라이브러리를 가져온다. 스프링 프로젝트를 생성할 때 미리 라이브러리를 가지고 오는 방법도 있다.

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.5.4</version>
</dependency>
validation 라이브러리가 가지고 있는 어노테이션
어노테이션 종류 의미
@NotNull Null 불가
@NotEmpty Null, 빈 문자열 불가
@NotBlank Null, 빈 문자열, 스페이스만 있는 문자열 불가
@Size(min = 10, max=30) 문자열, 배열 등의 크기 체크
@Email 이메일 형식 체크
package com.cos.photogramstart.domain.user;

import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

//JPA - Java Persistence API(자바로 데이터를 영구적으로 저장(DB)할 수 있는 API를 제공)

@Table(name = "user")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class User {
  
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY) // 번호 증가 전략이 데이터 베이스를 따라간다. 
  private int id;
  
  @Column(length = 20, unique = true)
  private String username;
  @Column(nullable = false)
  private String password;
  @Column(nullable = false)
  private String name;
  private String website;
  private String bio; // 자기소개
  @Column(nullable = false)
  private String email;
  private String phone;
  private String gender;
  
  private String profileImageUrl; // 사진 
  private String role; // 권한 
  
  private LocalDateTime createData;
  
  @PrePersist // DB에 INSERT 되기 직전에 실행  
  public void createDate() {
    this.createData = LocalDateTime.now();
  }

}
package com.cos.photogramstart.web.dto.auth;

import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;

import com.cos.photogramstart.domain.user.User;

import lombok.Data;

@Data
public class SignupDto {

  @size(min = 2, max = 20)
  private String username;
  @NotBlank
  private String password;
  @NotBlank
  private String email;
  @NotBlank
  private String name;
  
  public User toEntity() {
    return User.builder()
        .username(username) 
        .password(password)
        .email(email)
        .name(name)
        .build();
  }
  
}
어노테이션 종류 의미
@Column(length = 20, unique = true) 칼럼에 데이터 길이와 unique 제약 조건을 걸어준다.

컨트롤러에서 bindingResult를 사용하여 에러가 발생할 경우 에러 메시지를 출력하게 만들어준다.

	// 회원가입 하기 
	@PostMapping("/auth/signup")
	public @ResponseBody String signup(@Valid SignupDto dto, BindingResult bindingResult) {
		
		if(bindingResult.hasErrors()) {
			Map<String, String> errorMap = new HashMap<>();
			
			for(FieldError error : bindingResult.getFieldErrors()) {
				errorMap.put(error.getField(), error.getDefaultMessage());
			}
			return "유효성검사 실패함";
		}else {
			User user = dto.toEntity();
			User UserEntity = authService.회원가입(user);
			System.out.println(UserEntity);
			return "auth/signin"; 
		}
		
	}
프론트에서 유효성을 체크하고 백에서도 유효성을 체크하는 이유는 비정상적인 접근할 가능성이 있기 때문입니다.(ex. postman)
결과 화면

예외처리(Exception)

위와 같이 유효성 검사에서 실패하게 되었을 때 개발자가 보는 에러 페이지를 사용자에게 보여주는 것은 UX가 좋지 않다. UX를 좋게 하기 위해서 예외처리를 일괄할 파일을 따로 만들어 데이터를 리턴할 수 있게 만든다.

RuntimeExcetion 처리

package com.cos.photogramstart.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

@RestController // 메시지를 리턴
@ControllerAdvice // 익셉션을 모두 낚아챈다. 
public class ControllerExceptionHandler {

  @ExceptionHandler(RuntimeException.class) // 모든 예외를 처리 
  public String validationException(RuntimeException e) {
    return e.getMessage();
  }
  
}
// 회원가입 하기 
  @PostMapping("/auth/signup")
  public @ResponseBody String signup(@Valid SignupDto dto, BindingResult bindingResult) { 

    if(bindingResult.hasErrors()) {
      Map<String, String> errorMap = new HashMap<>(); 
      
      for(FieldError error : bindingResult.getFieldErrors()) {
        errorMap.put(error.getField(), error.getDefaultMessage());
      }
      throw new RuntimeException("유효성 검사 실패"); 
    }else {
      User user = dto.toEntity();
      User UserEntity = authService.회원가입(user);
      System.out.println(UserEntity);
      return "auth/signin"; 
    }
    
  }
결과 화면

예외처리 커스터마이징

위와 같이 Java에 내장되어 있는 RuntimeException을 사용하면 errorMap에 담긴 에러 메시지를 받아올 수 없다. 그 이유는 errorMap은 String을 리턴하기 때문에 파일을 리턴해야할 경우(회원가입 성공후 로그인 페이지로 보낼때) 사용할 수 없게 되므로 확장성을 위해 예외처리를 커스터마이징하는 것이 좋다.

package com.cos.photogramstart.handler.ex;

import java.util.Map;

public class CustomValidationException extends RuntimeException{ //RuntimeException 상속받아 Exception으로 만들어준다.
  
  private static final long serialVersionUID = 1L; // 객체를 구분 
  
  private Map<String, String> errorMap;
  
  public CustomValidationException(String message, Map<String, String> errorMap) {
    super(message);
    this.errorMap = errorMap;
  }
  
  public Map<String, String> getErrorMap(){
    return errorMap;
  }
  

}
package com.cos.photogramstart.handler;

import java.util.Map;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

import com.cos.photogramstart.handler.ex.CustomValidationException;

@RestController
@ControllerAdvice // 예외처리 
public class ControllerExceptionHandler {

  @ExceptionHandler(CustomValidationException.class) // 모든 예외를 처리 
  public Map<String, String> validationException(CustomValidationException e) {
    return e.getErrorMap();
  }
  
}
// 회원가입 하기 
  @PostMapping("/auth/signup")
  public @ResponseBody String signup(@Valid SignupDto dto, BindingResult bindingResult) { // 1.에러가 터지면 BindingResult에 담아온다.

    // 2.담아온 에러들을 모두 Map에 담아준다.
    if(bindingResult.hasErrors()) {
      Map<String, String> errorMap = new HashMap<>(); 
      
      for(FieldError error : bindingResult.getFieldErrors()) {
        errorMap.put(error.getField(), error.getDefaultMessage());
      }
      throw new CustomValidationException("유효성 검사 실패", errorMap); // 3.내가 만든 파일로 예외처리를 한다. 
    }else {
      User user = dto.toEntity();
      User UserEntity = authService.회원가입(user);
      System.out.println(UserEntity);
      return "auth/signin"; 
    }
    
  }
결과 화면

errorMap에 담긴 에러 메시지가 출력된다.

blog app
스프링부트 블로그 만들기 - 예외처리프로그램을 만들 때 핵심기능을 만들기 전후로 부가기능을 추가해줘야 한다. 핵심기능을 부가기능과 분리시켜 함수로 만들어 재사용하면 아주 편하게 코딩을 할 수 있다. 하지만 함수로 만들기 위한 공통 로직을 찾는 것은 결코 쉬운 일이 아니다....

공통 응답 DTO

커스텀 익셉션 처리를 하면 errorMap의 에러 메시지는 출력되지만 유효성 검사 실패했다는 메시지를 띄울 수 없다.하지만 ControllerExceptionHandler.java 파일의 validationException 함수에서 두 가지를 리턴할 수가 없다. 이를 해결하는 방법이 바로 공통 응답을 처리할 DTO를 만들어주는 것이다.

package com.cos.photogramstart.web.dto;

import java.util.Map;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class CMRespDto {
  
  private String message; // 컨트롤러별 지정된 에러 메시지를 담는다.
  private Map<String, String> errorMap; // ErrorMap에 있는 메시지를 담는다.

}
package com.cos.photogramstart.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

import com.cos.photogramstart.handler.ex.CustomValidationException;
import com.cos.photogramstart.web.dto.CMRespDto;

@RestController
@ControllerAdvice // 예외처리 
public class ControllerExceptionHandler {

  @ExceptionHandler(CustomValidationException.class) // 모든 예외를 처리 
  public CMRespDto validationException(CustomValidationException e) {
    return new CMRespDto(e.getMessage(), e.getErrorMap());
  }
  
}

signup.jsp required=”required” 요소를 지우면 테스트할 수 있다.

공통 응답 DTO의 확장성을 고려하여 제네릭타입을 사용하여 어떤 타입의 데이터도 받을 수 있게 만들고 응답에 필요한 코드를 만든다.

package com.cos.photogramstart.web.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class CMRespDto<T> {
  
  private int code; // 1 : 성공, -1 : 실패 
  private String message;
  private T data;

}
package com.cos.photogramstart.handler;

import java.util.Map;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

import com.cos.photogramstart.handler.ex.CustomValidationException;
import com.cos.photogramstart.web.dto.CMRespDto;

@RestController
@ControllerAdvice // 예외처리 
public class ControllerExceptionHandler {

  @ExceptionHandler(CustomValidationException.class) // 모든 예외를 처리 
  public CMRespDto<?> validationException(CustomValidationException e) { // ? : 타입추론 
    return new CMRespDto<Map<String,String>>(-1, e.getMessage(), e.getErrorMap());
  }
  
}

 

결과 화면

사용자 경험(UX) : Script

사용자가 회원가입에 실패 후 에러 페이지로 이동하게 되면 다시 회원가입 페이지를 돌아와야한다는 불편함을 감수해야한다. 이를 해결하는 방법으로 자바스크립트 알림창을 사용하여 에러 메시지만 전달할 수 있다.

package com.cos.photogramstart.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

import com.cos.photogramstart.handler.ex.CustomValidationException;
import com.cos.photogramstart.util.Script;

@RestController
@ControllerAdvice // 예외처리 
public class ControllerExceptionHandler {

  @ExceptionHandler(CustomValidationException.class) // 모든 예외를 처리 
  public String validationException(CustomValidationException e) {
    return Script.back(e.getErrorMap().toString());
  }
  
}
결과 화면

CMRespDto와 Script 비교

CMRespDto 클라이언트에게 응답할 때
Script Ajax 통신, Android 통신

Annotation 요약

어노테이션 종류 의미
@Controller 해당 파일을 IoC 컨테이너에 등록하고 파일로 뷰를 리턴한다.
@RestController 해당 파일을 IoC 컨테이너에 등록하고 데이터로 뷰를 리턴한다.
@Service 해당 파일을 IoC 컨테이너에 등록하고 서비스 레이어로 설정한다.
@Configuration 해당 파일을 설정파일로 만들고 Bean에 등록한다.
@Table(name = “user”) 데이터베이스에 생성될 테이블의 이름을 커스터마이징한다.
@Builder 빌더 패턴을 사용하여 객체를 생성할 수 있게 한다.
@Data Getter & Setter 등을 자동으로 생성한다.
@AllArgsConstructor 모든 필드 생성자를 자동으로 생성한다.
@NoArgsConstructor 디폴트 생성자를 자동으로 생성한다.
@Entity 클래스 안에 있는 정보를 스캔하여 자동으로 테이블을 생성한다.
@Id 자동으로 Primary Key를 생성한다.
@GeneratedValue(strategy = GenerationType.IDENTITY) 번호 증가 전략이 데이터 베이스를 따라간다. (Oracle : sequence, MariaDB/Mysql : auto increment)
@PrePersist DB에 INSERT 되기 직전에 실행한다.
@Lob (Large Object) 4GB 용량의 데이터 타입으로 만들어준다.
@RequiredArgsConstructor final이 붙은 모든 필드의 생성자를 자동으로 생성한다.
@EnableWebSecurity 해당 파일을 사용하여 시큐리트를 활성화한다.
@ControllerAdvice 해당 파일을 사용하여 예외처리를 한다.
@ExceptionHandler(RuntimeException.class) 런타입시 발행하는 모든 예외를 처리한다.
JpaRepository UserRepository.java를 메모리에 띄우기 위해서 JpaRepository를 User 타입(Generic Type)으로 상속받는다
blog app
스프링부트 블로그 만들기 - 5강 회원가입작은 프로젝트를 하나 만들어 보면서 스프링부트를 다루는 법을 배워요. 단순히 코드를 따라 치는 것이 아닌 프로그램이 돌아가는 원리를 이해합시다....
최신글