스프링 부트 with JPA - 로그인 기초

최준호·2024년 2월 16일

@Transactional(readOnly = true) 오류

원인

  • 코드 작성중 readOnly에 에러가 발생하였는데, 이건 제가 import를 다른 위치에서 하고 있기 때문입니다.

import jakarta.transaction.Transactional;

해결
import org.springframework.transaction.annotation.Transactional;
하면 오류가 해결됩니다.


Jsp 프론트 섹션

  • Header 섹션
<div class="collapse navbar-collapse" id="collapsibleNavbar">
			<c:choose>
				<c:when test="${empty sessionScope.principal}">
					<ul class="navbar-nav">
						<li class="nav-item"><a class="nav-link" href="/blog/user/loginForm">로그인</a></li>
						<li class="nav-item"><a class="nav-link" href="/blog/user/joinForm">회원가입</a></li>
					</ul>
				</c:when>
				<c:otherwise>
					<ul class="navbar-nav">
						<li class="nav-item"><a class="nav-link" href="/blog/board/writeForm">글작성</a></li>
						<li class="nav-item"><a class="nav-link" href="/blog/user/userInfo">회원정보</a></li>
						<li class="nav-item"><a class="nav-link" href="/blog/user/logout">로그아웃</a></li>
					</ul>
				</c:otherwise>
			</c:choose>
		</div>
        
================================================= form =================================================

<form action="/user/join">
		<div class="form-group">
			<label for="email">Email</label> <input type="email" class="form-control" placeholder="Enter email" id="email">
		</div>
		<div class="form-group">
			<label for="Password">Password</label> <input type="password" class="form-control" placeholder="Enter password" id="password">
		</div>
		<div class="form-group form-check">
			<label class="form-check-label"> <input class="form-check-input" type="checkbox"> Remember me
			</label>
		</div>
	</form>
	<button id="btn-login" class="btn btn-primary">login</button>
    
    ================================================ = Ajax =================================================
    let index = {
	init: function() {
		$('#btn-save').on('click', function() { // function(){} , () => {} this를 바인딩하기 위해서!
			index.save();
		})
		
		$('#btn-login').on('click', function() { // function(){} , () => {} this를 바인딩하기 위해서!
			index.login();
		})
	},
	save: function() {
		let data = {
			username: $('#username').val(),
			email: $('#email').val(),
			password: $('#password').val()
		};
		console.log(data);

		// ajax 호출시 default가 비동기 호출
		// ajax 통신을 이용해서 3개의 데이터를 json으로 변경하여 insert 요청
		$.ajax({
			type: "POST",
			url: "/blog/api/user/join",
			data: JSON.stringify(data), // HTTP body 데이터
			contentType: "application/json; charset=utf-8", // body 데이터가 어떤 타입인지(MIME)
			dataType: "json", // 요청을 서버로해서 응답이 왔을 때 json이라면 javascript 타입으로 변경시켜줍니다.
			success: function(response) {
				alert("회원가입이 완료되었습니다.");
				console.log(response);
				// location.href = "/blog";
			},
			error: function(xhr, status, error) {
				alert("오류가 발생하였습니다. 상태 코드: " + xhr.status);
				console.error("에러: ", error);
			}
		});

	},
	login: function() {
		let data = {
			email: $('#email').val(),
			password: $('#password').val()
		};
		console.log(data);
		$.ajax({
			type: "POST",
			url: "/blog/api/user/login",
			data: JSON.stringify(data), // HTTP body 데이터
			contentType: "application/json; charset=utf-8", // body 데이터가 어떤 타입인지(MIME)
			dataType: "json", // 요청을 서버로해서 응답이 왔을 때 json이라면 javascript 타입으로 변경시켜줍니다.
			success: function(response) {
				
				if(response.data == 1){
					alert("로그인.");
					location.href = "/blog";	
				} else{
					alert("로그인 실패.");
				}
				console.log(response);
			},
			error: function(xhr, status, error) {
				alert("오류가 발생하였습니다. 상태 코드: " + xhr.status);
				console.error("에러: ", error);
			}
		});

	}

}
console.log(1111);

index.init();

 ================================================ = Controller =================================================
 
 @PostMapping("/api/user/login")
	public ResponseDto<Integer> login(@RequestBody User user, HttpSession session) {
		log.info("login User {}", user);
		User principal = userService.login(user); // principal (접근주체)
		log.info("login User after service{}", user);
		int result = -1;
		if(principal.getEmail() != null && principal.getPassword() != null) {
			session.setAttribute("principal", principal); // 세션 값 생성
			result = 1;
		}

		return new ResponseDto<Integer>(HttpStatus.OK.value(), result);
	}
    
 ================================================ = Service =================================================
 
 	@Transactional(readOnly = true) // Select 할 때 트랜잭션 시작, 서비스 종료시에 트렌잭션 종료 (정합성 높여줍니다)
	public User login(User user) {
		return userRepository.findByEmailAndPassword(user.getEmail(), user.getPassword()); 
	}
    
 ================================================ = UserRepository =================================================
 
 public interface UserRepository extends JpaRepository<User,Integer> { //JpaRepository<테이블,프라이머리 키 타입> 
	
	// JPA Naming 쿼리 전략
	// Select * FROM user WHERE email = ? AND password = ?;
	User findByEmailAndPassword(String email, String password);
	
//	@Query(value="Select * FROM user WHERE email = ? AND password = ?", nativeQuery = true)
//	User login(String email, String password); 
}


    
  • <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    태그를 추가하여 jstl 함수를 사용할 수 있게 할 수 있습니다.

  • 로그인 하기 위해서 JPARepository를 수정해주는 것이 좋습니다. JPA Naming 쿼리 전략을 사용하여 이메일과 패스워드를 가지고 User 값을 불러와 사용할 수 있습니다.

profile
변화를 두려워하는 사람이 가장 불행한 사람이다.

0개의 댓글