📘JPA 사용하여 로그인 기능 구현
Entity 는 회원가입 기능에 사용한 Entity 를 그대로 사용한다.email 값이 로그인의 할 때의 ID로 사용된다.@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
//로그인 정보의 ID 역할
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
email, password를 요청받는다. Getter 와 생성자를 추가해준다.Controller 부분에서 유효성 검사를 위해 Validation 을 사용했다NotBlank : 값이 null 이거나 공백일경우 예외 처리ResponseDto로 넘기는 값은 id 하나이지만, 유지보수를 원활히 하기 위해 만들었다.//Client로 부터 받는 Dto
@Getter
public class LoginRequestDto {
@NotBlank(message = "이메일을 입력해주세요.")
private final String email;
@NotBlank(message = "비밀번호를 입력해주세요.")
private final String password;
public LoginRequestDto(String email, String password) {
this.email = email;
this.password = password;
}
}
//세션 저장을 위한 Dto
//유저의 id값을 반환하는 Dto도 만들어준다.
@Getter
public class LoginResponseDto
private final Long id;
public LoginResponseDto(Long id) {
this.id = id;
}
}
Repository에 메서드를 추가해준다.findByEmail : User 테이블에서 email 필드로 조회를한다. 이 메서드는 이름만으로 자동 구현된다.null 방지를 위해 Optional 타입으로 반환된다.public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
email 값을 통해 유저 정보를 DB 에서 찾아 반환한다.findByEmail 메서드의 반환값은 Optional 타입이기 때문에 null 예외처리를 바로 해준다.userId 값을 반환한다. (불일치 예외처리)public LoginResponseDto login(LoginRequestDto requestDto) {
//DB에서 Email로 User 정보 반환
User user = userRepository.findByEmail(requestDto.getEmail())
.orElseThrow((
)->new ResponseStatusException(HttpStatus.NOT_FOUND, "유저 정보 없음"));
//비밀번호 확인
if(!user.getPassword().equals(requestDto.getPassword())){
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "비밀번호 불일치");
}
//객체 반환
return new LoginResponseDto(user.getId());
}
/users/login 경로로 Post 요청을 받는다.LoginRequestDto 객체로 매핑.@Valid를 사용해 해당 DTO의 유효성 검사를 수행함.getSession() 을 통해 현재 로그인 상태인지(세션에 userId 값이 있는지) 확인한다. (존재하면 예외처리)HttpServletRequest 을 사용해 Service 에서 전달받은 userId 값을 세션에 저장한다.@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/login")
public ResponseEntity<String> login(@Valid @RequestBody LoginRequestDto requestDto,
HttpServletRequest httpServletRequest){
//세션으로 로그인 상태 확인
if(httpServletRequest.getSession(false) != null){
throw new ResponseStatusException(HttpStatus.CONFLICT, "로그인 해주세요.");
}
//Service 에서 UserDto 반환
LoginResponseDto loginResponseDto = userService.login(requestDto);
//세션에 "userId" 라는 ID 값으로 정보 저장
HttpSession session = httpServletRequest.getSession();
session.setAttribute("userId", loginResponseDto.getId());
//문자열 응답과 200(OK) 전달
return new ResponseEntity<>("로그인 되었습니다.",HttpStatus.OK);
}
}
200 OK 메세지가 표시되고 세션에 로그인 상태가 저장된다.요청 데이터 형식
URL 예시 : (POST)localhost:8080/users/signup
body 예시
{
"email" : "myEmail@gogle.com",
"password" : "password123!"
}
200 OK 와 로그인 성공 메시지

세션에 저장된 상태

invalidate() 만 사용해도 로그아웃 처리가 되지만, PostMan 에서는 자체적으로 쿠키를 계속 보내고 있기 때문에 쿠키도 즉시 만료시키는 로직을 실행시켜줘야 한다.200 OK 를 반환해준다.@PostMapping("/logout")
public ResponseEntity<String> logout(HttpServletRequest request,
HttpServletResponse response){
//세션 확인 후 비 로그인 상태라면 예외처리
HttpSession session = request.getSession(false);
if(session == null){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "로그인 해주세요.");
}
//세션 및 쿠키 만료 메서드
resetSessionAndCookies(session, response);
return new ResponseEntity<>("로그아웃 되었습니다.",HttpStatus.OK);
}
//세션 및 쿠키 만료 메서드
private void resetSessionAndCookies(HttpSession session, HttpServletResponse response){
//세션 만료
session.invalidate();
// postman 에서는 서버에서 자체적으로 쿠키(JSESSIONID)를 계속 보냄
Cookie cookie = new Cookie("JSESSIONID", null);
cookie.setPath("/");
cookie.setMaxAge(0); // 즉시 만료
response.addCookie(cookie);
}