회원가입 완료 버튼을 누를때 <form action="/user/join" method="POST"> 방식으로 하지않고
username, password, email 세개의 값을 자바스크립트에 들고가서 처리해보기
1. '회원가입 완료' 버튼 클릭시 alert 뜨는지 테스트
[joinForm]
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!-- ../layput 는 user보다 한 칸위의 폴더라는 뜻 -->
<%@ include file="../layout/header.jsp"%>
<div class="container">
<form action="/action_page.php">
<div class="form-group">
<label for="username">UserName : </label> <input type="text" class="form-control" placeholder="Enter UserName" id="username">
</div>
<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="pwd">Password : </label> <input type="password" class="form-control" placeholder="Enter password" id="password">
</div>
<button type="submit" class="btn btn-primary">회원가입 완료</button>
</form>
</div>
<%@ include file="../layout/footer.jsp"%>
- js 폴더를 만들고 user.js 파일생성 → 스프링이 static을 기본적으로 찾고있는데 정적파일인 js를 넣어준다.
[ joinForm.jsp ]
form이 submit 안되도록 버튼 태그를 form 밖으로 내보내고 버튼 태그에 id를 넣어 자바스크립트와 연결해준다.
<form>
...
</form>
<button id="btn-save" class="btn btn-primary">회원가입 완료</button>
</div><script src="/js/user.js"></script>
<%@ include file="../layout/footer.jsp"%>
마지막 스크립트 추가하여 src="/js/user.js"를 참조한다.
/blog 경로수정?
▽
[user.js]
/js/user.js
let index = {
init: function() {
$("#btn-save").on("click", ()=>{
this.save();
});
},
save: function(){
alert('user의 save함수 호출 됨');
}
}
index.init();
- $("#btn-save").on("click", ()=>{ });
id가 btn-save 인 '회원가입 완료' 버튼을 클릭했을 때 this.save(); 함수가 실행할 것이다.
.on(첫번째 파라미터, 두번째 파라미터) ▶ 첫번째 파라미터 : click / 두번째 파라미터는 무엇을 할건지? : this.save();
- this.save();
클릭했을때 save함수가 실행된다.
- index.init();
마지막에 index에 있는 init을 호출해줘야 실행된다.
= save함수 메세지가 성공적으로 뜬다.
2. 이번엔 id가 username, password, email 값들을 가져와 data에 담고 잘 불러오는지 console.log 테스트
...
save: function(){
//alert('user의 save함수 호출 됨');
let data = {
username: $("#username").val(),
password: $("#password").val(),
email: $("#email").val()
}
console.log(data);
}
회원가입 완료 버튼 클릭시 콘솔창에 잘 불러온다.
회원가입시 AJAX 를 사용하는 2가지 이유
1. 요청에 대한 응답을 html이 아닌 Data (json) 를 받기 위해서.
- 웹과 앱의 통신
- 웹 (html로 응답)
클라이언트가 회원가입 화면을 요청 → 서버가 html로 만들어서 화면을 보여줌
클라이언트가 회원가입 자체를 요청 → 서버가 회원가입 수행 → DB → 메인화면 html 보여줌
- 앱 (data로 응답)
앱은 자바코드로 화면디자인이 돼있어서 응답은 html로 보여주면 안되니까
data만 리턴받고 메인페이지로 이동해면 된다
= 앱과 웹브라우저의 html리턴, 데이터리턴 응답방식이 다르다.
서버를 두번 만들지말고 Data를 리턴하는 서버로 만들면 된다.
- data 리턴하는 서버 통신
브라우저 : 회원가입 요청 → 서버 → DB → 서버가 Data(정상)를 리턴 → 브라우저 응답
다시 브라우저 : 메인페이지 요청 → 서버가 .html 메인페이지 응답
앱 : 회원가입 요청 → 서버 → DB →서버가 Data리턴 → 앱 자체적으로 화면이동
따라서 앱, 브라우저에서 data를 리턴해주는 서버로 통일시키기!
2. 비동기 통신을 하기 위해
비동기 통신이란? 순서에 상관없이 일처리한다.
1을 다운로드 하면서 기다릴때 다른것을 할 수 있는 다음절차 2로 넘어가서 일처리 ,다운로드가 완료되면 호출해줘!
- 2에서 하던일을 멈추고 1로 돌아가는것을 콜백
- 2에서 완료 후 쉬던 중 1로 돌아가는것을 비동기적
회원가입도 통신이기 때문에 잠깐 멈춰질때 이걸 비동기가 막아준다.
즉 순서대로 하지 않아도 돼서 효율적이라는 것
이제 username, password, email 값을 담은 data를 ajax 호출하기
ajax 호출시 default가 비동기 호출
: 회원가입 호출이 100초동안 실행되고 있다면 다음 단계를 실행하던 와중에 실행완료가 되면 다시 돌아가서 수행완료.
[user.js]
$.ajax({
type: "POST",
url: "/auth/joinProc",
data: JSON.stringify(data), // http body데이터
contentType: "application/json; charset=utf-8",// body데이터가 어떤 타입인지(MIME)
dataType: "json" //javascript오브젝트로 변경
}).done(function(resp){
if(resp.status === 500){
alert("회원가입에 실패하였습니다.");
}else{
alert("회원가입이 완료되었습니다.");
location.href = "/"; // 회원가입 완료 후 메인페이지로
}
}).fail(function(error){
alert(JSON.stringify(error));
});
- 회원가입 insert 할거니까 POST 로 보내기
- data: JSON.stringify(data) : data가 자바스크립트 오브젝트라서 자바가 못읽기 때문에 JSON타입으로 바꾸기
- dataType: "json" : 서버로부터 응답이 왔을 때 기본적으로 모든 것이 문자열이고 만약 생긴게 json이라면 자바스크립트 오브젝트로 변경해서 응답해줌
// 경로설정
자바스크립트 오브젝트 vs JSON 형태 비교
<body>
<script>
let data ={
username:"love",
password:"1234",
email:"love@naver.com"
};
console.log(data); // 자바스크립트 오브젝트
console.log(JSON.stringify(data)); // JSON 문자열
</script>
</body>
ajax의 url: "/auth/joinProc" 요청주소 Controller 만들기
[UserApiController]
@RestController //데이터만 리턴
public class UserApiController {
@PostMapping("/auth/joinProc")
public int save(@RequestBody User user) {
System.out.println("UserApiController : save 호출됨");
return 1;
}
}
성공적으로 회원가입이 완료 시 return 1 이 되면 ajax에 resp에 1이 들어가고
.done(function(resp){
if(resp.status === 500){
alert("회원가입에 실패하였습니다.");
}else{
alert("회원가입이 완료되었습니다.");
location.href = "/"; // 회원가입 완료 후 메인페이지로
}
회원가입 완료 팝업창이 뜨고 메인페이지로 이동
패키지 dto 를 만들고 ResponseDto 클래스 생성 - 응답할 때 쓸 것
[ResponseDto]
UserApiController 의 리턴 값이 되는 클래스
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ResponseDto<T> {
int status;
T data;
}
[UserApiController]
@PostMapping("/auth/joinProc")
public ResponseDto<Integer> save(@RequestBody User user) {
System.out.println("UserApiController : save 호출됨");
return new ResponseDto<Integer>(HttpStatus.OK.value(),1);
}
return new ResponseDto<Integer>(HttpStatus.OK.value(),1);
- 첫번째 파라미터는 status값 ( status 값이 200이면 = 통신이 정상적으로 성공했다. )
- 두번째 파라미터는 data
리턴 값을 resp에 넣고 ajax에서 console.log(resp) 했을 때
ajax가 통신을 성공하고 서버가 json을 리턴해주면 자동으로 자바 오브젝트로 변환해줌
이제 회원가입을 하면 유저정보를 DB에 insert 하자
UserApiController 에서 userRepository.save(user); 를 써서 바로 insert 해도 되지만 중간에 Service가 필요한 이유
이유1 . 서비스 의미
예를들어 '송금서비스'
홍길동이 임꺽정에게 빌린돈 일부를 갚았다.
= 두사람에게 두 개의 업데이트 발생.
repository 의 crud 처럼 송금서비스는 u(update)를 두 개 들고있다
둘다 오류가 없으면 commit 을 하지만 한개가 업뎃 실패? 둘다 원상복귀 rollback 을 해야한다.
즉 홍길동, 임꺽정 각 하나의 트랜젝션을 두 트랜잭션을 하나의 트랜잭션으로 묶어서 서비스화 할 수 있다.
[UserService]
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public int 회원가입(User user) {
try {
user.setRole(RoleType.USER)
userRepository.save(user);
return 1;
} catch (Exception e) {
e.printStackTrace();
System.out.println("UserService : 회원가입() : " + e.getMessage());
}
return -1;
}
}
@Service 은 스프링이 컴포넌트 스캔을 통해서 Bean에 등록을 해준다 (= IoC) 즉 메모리에 대신 띄워준다.
@Transactional ( import org.springframework.transaction.annotation.Transactional; )
: '회원가입' 전체 서비스가 하나의 트랜잭션으로 묶이게 된다.
try catch 문 전체가 성공하면 commit , 실패시 rollback (원상복귀)
▼ UserService 를 여기에 리턴/요청하기
[UserApiController]
@RestController //데이터만 리턴
public class UserApiController {
@Autowired
private UserService userService; // DI 하기
@PostMapping("/auth/joinProc")
public ResponseDto<Integer> save(@RequestBody User user) { // 직접 받는건 username, password, email
System.out.println("UserApiController : save 호출됨");
int result = userService.회원가입(user);
return new ResponseDto<Integer>(HttpStatus.OK.value(), result);
}
}
User객체에서 id는 DB가 알아서 넣고 username, password, email 은 회원가입 페이지에 직접 받고
role은 서비스에서 받기. userService.회원가입에 넣었음 → user.setRole(RoleType.USER);
createDate 은 자동으로 넣어진다.
- DB에 insert가 된 결과
만약 UserService 에서 회원가입 실패시 -1 로 리턴을 받고 UserApiController에서도 -1
[GlobalExceptionHandler] 로 호출가서 오류메세지가 응답이 된다. ( 예외 모든것을 여기서 처리하니까)
[GlobalExceptionHandler]
@ControllerAdvice
@RestController
public class GlobalExceptionHandler {
@ExceptionHandler(value = IllegalArgumentException.class)
public String handleArgumentException(IllegalArgumentException e) {
return "<h1>" + e.getMessage() + "<h1>";
}
}
리턴값을 e.getMessage() 메세지로 리턴하기보다 통신상태와 정상적으로 insert를 했는지 알기위해
ResponseDto 로 리턴하기
▶ return new ResponseDto<String>(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
@ControllerAdvice
@RestController
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseDto<String> handleArgumentException(Exception e) {
return new ResponseDto<String>(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
}
}
ResponseDto 의 첫번째 파라미터가 int status 라서 .value() 를 붙인다.
- [GlobalExceptionHandler]
HttpStatus.INTERNAL_SERVER_ERROR.value()
- [UserApiController]
HttpStatus.OK.value()
[ResponseDto]
public class ResponseDto<T> {
int status;
T data;
}
정상적으로 회원가입 save를 했을때
'Spring boot | 블로그 만들기' 카테고리의 다른 글
스프링 시큐리티 로그인 | 사용자 정보 가져오기 principal (0) | 2022.11.09 |
---|---|
로그인 방식 | 세션을 이용한 로그인 (0) | 2022.11.09 |
메인화면, 회원가입 화면, 로그인 화면 (0) | 2022.11.06 |
회원삭제 테스트 | Exception 처리 (0) | 2022.11.05 |
회원수정 테스트 | save()와 @Transactional 더티체킹 (1) | 2022.11.05 |