blog 예제 로그인(statefull)
[Spring] blog 예제 회원가입, 로그인 까지
Jan 30, 2024
기본 세팅
코드 가져와서 “화면 초기 세팅 완료” 로 reset해서 시작하기
application
application-prod.yml 수정

application-dev.yml 수정

application.yml


-prod이면 application-prod.yml으로 실행됨 개발 환경에서는 -dev로 할것
패키지 구성 변경

전

후
회원가입 코드 작성
요청은 url, uri로 받는다
데이터는 DTO로 받는다.
UserController - join 추가
@PostMapping("/join")
public String join() {
return "redirect:/loginForm";
}

UserRequest 생성, DTO작성


UserController - join 에 파라미터를 JoinDTO로 넣어줌
H2 DataBase 사용
application-dev.yml 에 h2 DB사용을 위한 코드 작성
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:test;MODE=MySQL
username: sa
password:
h2:
console:
enabled: true

http://localhost:8080/h2-console 접속하여 데이터베이스 연결
DB 화면캡처


console url 설정 → url로 접속가능하게 하는 코드

User 클래스 생성, Table 생성 → Java에서 코드로 테이블 생성이 가능

코드
@Data
@Entity
@Table(name = "user_tb")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private String email;
}

DB화면 캡처

@Entity는 application-dev.yml에

Spring 서버가 실행될때 Entity를 찾아 분석하여 create함 - 개발모드에서만 사용할 것
제약조건 설정
User
@Data
@Entity
@Table(name = "user_tb")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(unique = true)
private String username;
@Column(length = 60, nullable = false)
private String password;
private String email;
@CreationTimestamp
private LocalDateTime createAt;
}

application-dev.yml → 코드 추가(Run에서 쿼리 편하게 볼 수있음)



UserRepository 생성(DAO로 생각)

UserRepository
@Repository
public class UserRepository {
private EntityManager em; // 의존성 주입
// 생성자
public UserRepository(EntityManager em) {
this.em = em;
}
public void save(UserRequest.JoinDTO requestDTO){
System.out.println("UserRequest에 save메서드 호출됨");
}
}
의존성 주입
계속 필요할때 마다 new를 하게되면 메모리가 감당하지 못하기 때문에 DI(Dependency Injection)

UserController 코드 전체
package shop.mtcoding.blog.user;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class UserController {
private UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@PostMapping("/join")
public String join(UserRequest.JoinDTO requestDTO) {
System.out.println(requestDTO);
// 1. 유효성 검사
if(requestDTO.getUsername().length()<3){
return "error/400";
}
// 2. Model에 위임하기
userRepository.save(requestDTO);
// 3. 응답
return "redirect:/loginForm";
}
@GetMapping("/joinForm")
public String joinForm() {
return "user/joinForm";
}
@GetMapping("/loginForm")
public String loginForm() {
return "user/loginForm";
}
@GetMapping("/user/updateForm")
public String updateForm() {
return "user/updateForm";
}
@GetMapping("/logout")
public String logout() {
return "redirect:/";
}
}
error패키지 만들어서 400이름으로 에러 페이지 하나 만들기
400
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>에러 : 잘못된 요청을 했습니다. 400</h1>
</body>
</html>

쿼리 작성 UserRepository
save
@Transactional public void save(UserRequest.JoinDTO requestDTO){ Query query = em.createNativeQuery("insert into user_tb(username,password,email) values(?,?,?)"); query.setParameter(1,requestDTO.getUsername()); query.setParameter(2,requestDTO.getPassword()); query.setParameter(3,requestDTO.getEmail()); query.executeUpdate(); }

위에 어노테이션 붙이면됨
@Transactional을 안붙이면 쿼리를 전송하지 않는다(insert, delete, update 같은 경우는 데이터베이스를 바꾸는 위험한 쿼리로 인식하기 때문에)

다른 기술(알아서 쿼리를 작성해준다)
saveV2
@Transactional
public void saveV2(UserRequest.JoinDTO requestDTO){
User user = new User();
user.setUsername(requestDTO.getUsername());
user.setPassword(requestDTO.getPassword());
user.setEmail(requestDTO.getEmail());
em.persist(user);
}

현재까지의 코드 실행


회원가입 후 DB에 insert된 것을 확인

로그인 코드
UserController-login코드 작성
@PostMapping("/login")
public String login(UserRequest.LoginDTO requestDTO){
// 1. 유효성 검사
if(requestDTO.getUsername().length()<3){
return "error/400";
}
// 2. Model 필요
userRepository.findByUsernameAndPassword(requestDTO);
// 3. 응답
return "redirect:/";
}

findByUsernameAndPassword에 Alt+Enter하면 바로 생성됨
UserRepository
findByUsernameAndPassword
public User findByUsernameAndPassword(UserRequest.LoginDTO requestDTO) {
Query query = em.createNativeQuery("select * from user_tb where username=? AND password=?", User.class);
query.setParameter(1,requestDTO.getUsername());
query.setParameter(1,requestDTO.getPassword());
User user = (User) query.getSingleResult();
return user;
}

실행
회원가입

로그인

로그인 완료

로그인의 목적 → Statefull 만들기
session 영역에 한번이라도 접근하면 session이 만들어짐
@RequiredArgsConstructor → final들만 생성자를만들어줌



session과 Request에 접근할 수 있음
코드
mustache:
servlet:
expose-session-attributes: true
expose-request-attributes: true

응답 유저가 null이 아니면, session 만들고, index 페이지로 이동
코드
User user = userRepository.findByUsernameAndPassword(requestDTO);
if(user == null){
return "error/401";
}else{
session.setAttribute("sessionUser",user);
return "redirect:/";
}

401
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>인증에 실패하였습니다. 401</h1>
</body>
</html>
로그인 전에는 회원가입과 로그인만 navbar에 보이도록
로그인이 완료되면 navbar의 회원가입과 로그인을 안보이게
layout/header.mustache


실행
회원가입 후 로그인

navbar 구성 달라짐

검토사항
테이블생성시 password의 nullable = false 가 안먹힘 null값이 아니라 공백이 입력되는것으로 확인된다.
유효성 검사에서 password의 길이를 어느정도 이상으로 하면 상관없을 것이다.
Share article