스프링부트 백엔드 서버 코드를 입력하기 전에, 필요한 클래스들을 미리 세팅해 볼 것이다.
먼저, 다음과 같이 각각의 엔티티명으로 된 폴더를 만들고 안에
Entity
클래스와Controller
,Service
,Repository
파일을 만든다.
그리고request
와response
폴더를 만들자.
Entity
,Controller
,Service
,Repository
에 대해서는 다음 포스트에 설명되어 있다.
https://velog.io/@phraqe/Sekkison03
이번 포스트에서 설명할 것은
request
와response
이다.
프론트에서 API 요청이나, 페이지 요청이 있을 때 데이터를 서버로 넘기게 되는데, 이 데이터를 서버에서 사용하기 편하게 가공하기 위해 필요하게 된다.
예시로,
user/request
의Joinrequest
를 보자.
JoinRequest.class
@Data public class JoinRequest { @NotBlank(message = "아이디는 필수 입력 값입니다.") @Size(min = 4, max = 10, message = "아이디는 4~10자만 가능합니다") private String username; @NotBlank(message = "비밀번호는 필수 입력 값입니다.") @Pattern(regexp = "(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,16}", message = "비밀번호는 8~16자 영문 대 소문자, 숫자, 특수문자를 사용하세요.") private String password; @NotBlank(message = "별명을 설정해주세요") @Pattern(regexp = "^[ㄱ-ㅎ가-힣a-z0-9-_]{2,10}$", message = "닉네임은 특수문자를 제외한 2~10자리여야 합니다.") private String name; public User toUser() { User user = new User(); user.setUsername(this.username); user.setPassword(this.password); user.setName(this.name); return user; } }
@NotBlank
,@Pattern
,@Size
어노테이션을 이용하여 클래스의 변수에 조건을 걸어놓고toUser()
함수에서User
객체를 반환하게 된다.
컨트롤러를 보면, 다음과 같이 프론트에서 받아온 데이터를
@Validated
를 사용하여JoinRequest
객체에 담게 된다.UserController.class
@PostMapping("/join") public JoinResponse join(@RequestBody @Validated JoinRequest request) { User user = request.toUser(); return JoinResponse.of(userService.upsertUser(user)); }
@Validated
어노테이션으로 인해, 만약 프론트에서 받아온 데이터가 유효하지 않다면,Exception
을throw
하게 된다.
이것만으로 충분한 것 같지만, 에러가 발생했을 때를 대비해
Exception
을 관리하는 클래스를 만들 것이다.
Exception
을 관리하기 위해 다음과 같이ExceptionController
,프로젝트명 + Exception
클래스를 생성한다.
먼저, 프로젝트명 + Exception 클래스에서는
@Validated
에서 발생한 에러가 아닌, 직접throw
해주는Exception
들이 담길 것이다.MyMemoryException.class
@Getter @AllArgsConstructor public class MyMemoryException extends RuntimeException { private int status; private String message; }
사용방법은 다음과 같이 데이터를 넘겨줄 수 없을 때
throw
하기만 하면 된다.UserService.class
public User upsertUser(User user) { User dupUser = userRepository.findByUsername(user.getUsername()); if (dupUser != null) throw new MyMemoryException(404, "이미 존재하는 아이디입니다."); user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); return userRepository.save(user); }
그리고
ExceptionController
에서는@Validated
와 직접throw
한 예외들을 관리해서 프론트로 에러를 담아 넘겨줄 것이다.ExceptionController.class
@RestControllerAdvice public class ExceptionController { @ExceptionHandler(MethodArgumentNotValidException.class) public ErrorResponse handle(MethodArgumentNotValidException e) { BindingResult bindingResult = e.getBindingResult(); StringBuilder builder = new StringBuilder(); FieldError fieldError = bindingResult.getFieldErrors().get(0); builder.append(fieldError.getDefaultMessage()); return new ErrorResponse(400, builder.toString()); } @ExceptionHandler(MyMemoryException.class) public ErrorResponse handle(MyMemoryException e) { return new ErrorResponse(e.getStatus(), e.getMessage()); } @Data @AllArgsConstructor public static class ErrorResponse { private int status; private String message; } }
MethodArgumentNotValidException.class
@Validated
를 통한 예외가 발생하게 되면ErrorResponse
객체를 프론트로 보내게 된다. 이 경우status
는 400으로 설정하였다.
MyMemoryException.class
직접 예외를
throw
하면ErrorResponse
객체를 프론트로 보내게 된다.
예외 발생 없이 성공했을 때 프론트로 넘겨줄 객체 또한 필요하다.
예시로,user/response
패키지의DeleteResponse
를 보자.
DeleteResponse.class
@Data public class DeleteResponse { private int status; private String message; public static DeleteResponse of(int status) { DeleteResponse response = new DeleteResponse(); response.setStatus(status); response.setMessage("회원 탈퇴 성공"); return response; } }
컨트롤러에서
JoinResponse.of(유저객체)
를 리턴하면,status
와message
가 담긴Response
객체를 프론트로 보내게 된다.그러면 프론트에서는 다음 코드처럼
status
가 200이 아닐 때 message를 보여주면 된다.javascript
$.ajax({ contentType: 'application/json', url: `/api/join`, type:"POST", dataType: 'json', data: JSON.stringify({ "username": username, "password": password, "name": name }), success : function(data) { if (data.status == 200) alert("회원가입 완료"); else alert(data.message); } })