현재 front-end단에서 데이터를
{"id":"werf","password":"asdf"}
위와 같은 JSON형태로 보내고 있다. 이는 URL에 데이터를 담아서 보내는 것이 아니다.
🔽 MasterLoginController@PostMapping("/signin")
public ResponseEntity<String> jwtLogin
(@RequestParam String id, @RequestParam String password) {
...
MasterLoginController는 위와 같은 형태로 되어 있는데 id파라미터를 찾지 못해서
: Resolved [org.springframework.web.bind.MissingServletRequestParameterException:
Required request parameter 'id' for method parameter type String is not present]
위와 같은 에러를 보내고 있다.
일단 @RequestParam은 URL에 담긴 쿼리 파라미터 정보를 읽는다. 하지만 위처럼 front-end단에서 JSON으로 데이터를 뿌려주면 URL에 정보를 담는 것이 아니기때문에 @RequestParam으로 받을 수가 없는 것이다.
Spring MVC에서 HTTP body에 담긴 데이터를 자바 객체로 변환하는데 사용한다. 주로 JSON형식의 데이터를 java객체로 변환할 때 사용되며 Spring의 HttpMessageConverter에 의해 처리된다.
HTTP 요청 본문의 데이터를 자바 객체에 바인딩 한다. 주로 JSON, XML의 포맷을 사용한다.
Spring의 HttpMessageConverter는 Jackson라이브러리를 사용해서 JSON데이터를 자바 객체로 자동으로 변환한다.
@Valid 애노테이션과 함께 사용해 요청 본문 데이터의 유효성을 검사할 수 있다.
Body에 큰 데이터를 포함할 경우 서버의 메모리 사용량이 증가할 수 있다.
요청 본문 데이터의 구조가 맞지 않거나 필수 값이 누락된 경우 예외가 발생할 수 있어 예외처리가 필요하다.
package burgerput.project.zenput.web;
//burgerput 첫 페이지진입시 API를 주고받는 컨트롤러
import burgerput.project.zenput.Services.jwtLogin.JwtTokenProvider;
import burgerput.project.zenput.domain.MasterAccount;
import burgerput.project.zenput.repository.masterAccountRepository.MasterAccountRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.security.sasl.AuthenticationException;
import java.util.Optional;
@RestController
@RequiredArgsConstructor
@Slf4j
public class MasterLoginController {
private final MasterAccountRepository masterAccountRepository;
private final JwtTokenProvider jwtTokenProvider;
@PostMapping("/signin")
public ResponseEntity<String> jwtLogin(@RequestBody User user) {//@RequestBody를 이용해
//본문의 JSON객체를 가져오도록 한다.
Optional<MasterAccount> foundAccount = masterAccountRepository.findById(user.getId());
try{
// ID 검사
if(foundAccount.isEmpty()){ //Account의 값을 찾을 수 없는 경우(인증 실패)
//Account 계정이 없는경우
throw new AuthenticationException("Account not found");
}
MasterAccount master = foundAccount.get();
//password 검사
if(!master.getMaster_pw().equals(user.getPassword())){
throw new AuthenticationException("Invalid username/password supplied") {};
}
//JWT토큰 생성 및 반환 로직
String token = jwtTokenProvider.createToken(master.getMasterId());
return new ResponseEntity<>(token, HttpStatus.OK);
}catch(AuthenticationException e){
return handleAuthenticationException(e);
}
}
//내부 정적 클래스 Jackson라이브러리는 비정적 내부 클래스를 인스턴스화 할 수 없다.
//그래서 정적 클래스로 선언해야 한다.
@Data //@Getter와 Setter를 포함하는 @Data 애노테이션으로 붙여서 간단하게? 표현
private static class User{
//객체의 변수 이름은 JSON key값과 같아야 한다.
private String id;
private String password;
}
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<String> handleAuthenticationException(AuthenticationException ex){
log.info("Exception authentication");
return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED);
}
}
놓칠 수 있던 부분은
Jackson 라이브러리는 비정적 클래스를 인스턴스화 할 수 없다. 따라서 static인 정적 클래스로 지정해줘야 한다.