Joshua게시판(SSR)-4-회원가입 초기 기능

jaegeunsong97·2023년 8월 13일
0

SSR 기반 JoshuaBlog

목록 보기
4/23

깃허브링크

노션링크

  • user entity
package coffee.pastry.joshuablog.model.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.PreUpdate;
import javax.persistence.Table;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Builder
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "user_tb")
@AllArgsConstructor
@Getter
public class User {

     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
     @Column(unique = true, length = 20)
     private String username;
     @Column(length = 60)
     private String password;
     @Column(length = 50)
     private String email;
     private String role;
     private String profile;
     private Boolean status;
     private LocalDateTime createdAt;
     private LocalDateTime updatedAt;

	 public void changeProfile(String profile) {
          this.profile = profile;
     }

     @PrePersist
     protected void onCreate() {
          this.createdAt = LocalDateTime.now();
     }

     @PreUpdate
     protected void onUpdate() {
          this.updatedAt = LocalDateTime.now();
     }
}

@Builder + @AllArgsConstructor (쌍으로 움직임)

@NoArgsConstructor : 하이버네이트가 User new 하기 위해서

access = AccessLevel.PROTECTED : 개발자 직접 new 못하게 하기 위해서

  • UserRepository
package coffee.pastry.joshuablog.model.user;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface UserRepository extends JpaRepository<User, Long> {

     @Query("select u from User u where u.username = :username")
     Optional<User> findByUsername(@Param("username") String username);
}

namedQuery -> JPQL 쿼리

  • Dto 생성
package coffee.pastry.joshuablog.dto.user;

import coffee.pastry.joshuablog.model.user.User;
import lombok.Getter;
import lombok.Setter;

public class UserRequestDto {

     @Getter
     @Setter
     public static class JoinInDto {

          private String username;
          private String password;
          private String email;

          public User toEntity() {
               return User.builder()
                         .username(username)
                         .password(password)
                         .email(email)
                         .role("USER") // ENUM(check)
                         .status(true)
                         .profile("person.png")
                         .build();
          }
     }
}
  • UserService
package coffee.pastry.joshuablog.service;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import coffee.pastry.joshuablog.dto.user.UserRequestDto;
import coffee.pastry.joshuablog.model.user.UserRepository;
import lombok.RequiredArgsConstructor;

@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class UserService {

     private final UserRepository userRepository;
     private final BCryptPasswordEncoder passwordEncoder;

     @Transactional
     public void 회원가입(UserRequestDto.JoinInDto joinInDto) {
          joinInDto.setPassword(passwordEncoder.encode(joinInDto.getPassword()));
          userRepository.save(joinInDto.toEntity());
     } // 더티채킹, 세션종료 
}

OSIV가 false -> 트랜젝션 종료시, 더치채킹 + 세션종료 -> 컨트롤러 LAZY 로딩 X

  • UserController 추가
@RequiredArgsConstructor
@Controller
public class UserController {

     private final UserService userService;

     @PostMapping("/join")
     public String join(UserRequestDto.JoinInDto joinInDto) {
          userService.회원가입(joinInDto);
          return "redirect:/loginForm";
     }

     @GetMapping("/joinForm")
     public String joinForm() {
          return "user/joinForm";
     }

     @GetMapping("/loginForm")
     public String loginForm() {
          return "user/loginForm";
     }
}
  • 포스트맨 테스트

로그인 화면이 나옴 = 리다이렉션이 됨

1번더 누르면?

username 유니크 제약조건 에러 발생

어디서 터진걸까? -> save에서, save가 throws Exception을 가지고 있지 않기 때문에

따라서 insert, update, delete -> try-catch 하기(우리가 제어를 해야함)

  • UserService 추가
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class UserService {

     private final UserRepository userRepository;
     private final BCryptPasswordEncoder passwordEncoder;

     @Transactional
     public void 회원가입(UserRequestDto.JoinInDto joinInDto) {
          try {
               joinInDto.setPassword(passwordEncoder.encode(joinInDto.getPassword()));
               userRepository.save(joinInDto.toEntity());
          } catch (Exception e) {
               throw new RuntimeException("회원가입 오류 : " + e.getMessage());
          }

     }
}

일단 Exception 코드를 안만들어서 일단 가장 조상인 RuntimeException으로 처리

다시 포스트맨으로 회원가입 2번 연속하면? 잘 처리함.

  • 더미데이터 추가
package coffee.pastry.joshuablog;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import coffee.pastry.joshuablog.model.user.User;
import coffee.pastry.joshuablog.model.user.UserRepository;

@SpringBootApplication
public class BlogApplication {

	@Bean
	CommandLineRunner init(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
		return args -> {
			User song = User.builder()
					.username("song")
					.password(passwordEncoder.encode("1234"))
					.email("song@nate.com")
					.role("USER")
					.profile("person.png")
                    .status(true)
					.build();
			userRepository.save(song);
		};
	}

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

}

insert 쿼리 + TRACE로 detail 확인

h2 확인 -> 막힘 -> 시큐리티 떄문

  • SecurityConfig 추가
@Bean // 권한 주소 설정
     SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
          http.csrf().disable();
          http.headers().frameOptions().disable(); // 추가
          http.formLogin()
                    .loginPage("/loginForm")
                    .loginProcessingUrl("/login")
                    .successHandler(((request, response, authentication) -> {
                         log.debug("디버그 : 로그인 성공");
                    }))
                    .failureHandler(((request, response, exception) -> {
                         log.debug("디버그 : 로그인 실패 : " + exception.getMessage());
                    }));

          http.authorizeRequests(
                    authorize -> authorize.antMatchers("/s/**").authenticated()
                              .anyRequest().permitAll());

          return http.build();
     }

h2-console 때문에 iframe 해제

profile
현재 블로그 : https://jasonsong97.tistory.com/

0개의 댓글