blog 예제 로그인(statefull)
[Spring] blog 예제 회원가입, 로그인 까지
Jan 30, 2024
기본 세팅
코드 가져와서 “화면 초기 세팅 완료” 로 reset해서 시작하기
application
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 접속하여 데이터베이스 연결
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;
}
@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);
}
로그인 코드
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


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
















