μ΄λ² κΈμμλ μλ‘μ΄ κΈ°λ₯μ μμ±νκΈ° μ μ, κΈ°μ‘΄ μ½λλ₯Ό λ°κΎΈλ μκ°μ κ°κ² μ΅λλ€.
μ§κΈκΉμ§ μμΈκ° λ°μν΄λ νμ μνμ½λλ₯Ό 200μΌλ‘ λ°μμμΌ°μλλ°, μ΄λ₯Ό λ€μ μμΈ μΌμ΄μ€μ λ§κ² μνμ½λλ₯Ό λ°νν΄λ³΄λ € ν©λλ€.
κ·Έλ κ² μ€μν λΆλΆμ μλκΈ°μ 건λλ°μ λ λ©λλ€. λ¨μ§ μ΄ν μ μ μ½λκ° μΌλΆ λ€λ₯Ό μ μμΌλ, κ·ΈλΆλΆλ§ μ£Όμν΄μ£Όμλ©΄ λ©λλΉ.
- μν리ν°λ₯Ό μ΄μ©ν JSON λ°μ΄ν°λ‘ λ‘κ·ΈμΈ (μλ£)
- JWTλ₯Ό μ΄μ©ν μΈμ¦ (μλ£)
- λλ©μΈ, ν μ΄λΈ μ€κ³, μν°ν° μμ± (μλ£)
- λκΈ μμ λ‘μ§ κ΅¬ν (μλ£)
- νμκ°μ + μ 보μμ λ± νμ μλΉμ€ ꡬν (μλ£)
- κ²μν μλΉμ€ ꡬν
- λκΈ μλΉμ€ ꡬν (1λκΈ -> *(무ν) λλκΈ κ΅¬μ‘°)
- μμΈ μ²λ¦¬ (μ§ν μ€)
- μμΈ λ©μΈμ§ κ΅μ ν
- μΉ΄ν κ³ λ¦¬λ³ κ²μν λΆλ₯
- κ²μκΈ νμ΄μ§
- λμ μΈ κ²μ 쑰건μ μ¬μ©ν κ²μ
- μ¬μ©μ κ° μͺ½μ§ κΈ°λ₯
- 무ν μͺ½μ§ μ€ν¬λ‘€
- κ²μλ¬Ό & λκΈμ λν μλ
- μͺ½μ§μ λν μλ
- μ μν μ¬μ©μ κ° μ€μκ° μ±ν
- νμκ°μ μ κ²μ¦(μ: XXλνκ΅ XXκ³Όκ° μλλ©΄ κ°μ ν μ μκ²)
- Swaggerλ₯Ό μ¬μ©ν API λ¬Έμ λ§λ€κΈ°
- μ κ³ & λΈλ리μ€νΈ κΈ°λ₯
- AOPλ₯Ό ν΅ν λ‘κ·Έ
- μ΄λλ―Ό νμ΄μ§
- μΊμ
- λ°°ν¬ (+ 무μ€λ¨ λ°°ν¬)
- λ°°ν¬ μλν
- ν¬νΈμ μ΄λν° μ€κ³λ₯Ό λ°λ₯΄λ ν¨ν€μ§ ꡬ쑰 μ€κ³νκΈ°
- ...
MemberExceptionType μ½λ μμ
μλλ λͺ¨λ μν©μμ HttpStatus.OKλ₯Ό μ§μ ν΄ μ£Όμλλ° μ΄λ₯Ό μλμ κ°μ΄ λ°κΎΈμμ΅λλ€.
public enum MemberExceptionType implements BaseExceptionType {
//== νμκ°μ
, λ‘κ·ΈμΈ μ ==//
ALREADY_EXIST_USERNAME(600, HttpStatus.CONFLICT, "μ΄λ―Έ μ‘΄μ¬νλ μμ΄λμ
λλ€."),
WRONG_PASSWORD(601,HttpStatus.BAD_REQUEST, "λΉλ°λ²νΈκ° μλͺ»λμμ΅λλ€."),
NOT_FOUND_MEMBER(602, HttpStatus.NOT_FOUND, "νμ μ λ³΄κ° μμ΅λλ€.");
LoginFailureHandler μ½λ μμ
@Slf4j
public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//response.setStatus(HttpServletResponse.SC_OK);//TODO: μλ¬μ½λ λ³κ²½ SC
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);//
response.getWriter().write("fail");
log.info("λ‘κ·ΈμΈμ μ€ν¨νμ΅λλ€");
}
}
μ£Όμ μ²λ¦¬λ λΆλΆμμ, κ·Έ μλ μ½λλ‘ λ°κΎΈμμ΅λλ€.
ExceptionAdvice μ½λ μμ
@RestControllerAdvice
@Slf4j
public class ExceptionAdvice {
//HttpMessageNotReadableException => json νμ± μ€λ₯
@ExceptionHandler(BaseException.class)
public ResponseEntity handleBaseEx(BaseException exception){
log.error("BaseException errorMessage(): {}",exception.getExceptionType().getErrorMessage());
log.error("BaseException errorCode(): {}",exception.getExceptionType().getErrorCode());
return new ResponseEntity(new ExceptionDto(exception.getExceptionType().getErrorCode()),exception.getExceptionType().getHttpStatus());
}
//@Valid μμ μμΈ λ°μ
@ExceptionHandler(BindException.class)
public ResponseEntity handleValidEx(BindException exception){
log.error("@ValidException λ°μ! {}", exception.getMessage() );
return new ResponseEntity(new ExceptionDto(2000),HttpStatus.BAD_REQUEST);
}
//HttpMessageNotReadableException => json νμ± μ€λ₯
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity httpMessageNotReadableExceptionEx(HttpMessageNotReadableException exception){
log.error("Jsonμ νμ±νλ κ³Όμ μμ μμΈ λ°μ! {}", exception.getMessage() );
return new ResponseEntity(new ExceptionDto(3000),HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity handleMemberEx(Exception exception) {
exception.printStackTrace();
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
@Data
@AllArgsConstructor
static class ExceptionDto {
private Integer errorCode;
}
}
νμκ°μ μ, νλλ₯Ό μ λ ₯νμ§ μμ μκΈ°λ μ€λ₯μ, JSON νμ± κ³Όμ μμ μκΈ°λ μ€λ₯λ€μ μνμ½λλ₯Ό 400μΌλ‘ λ°κΎΈμ΄ μ£Όμμ΅λλ€.
μ΄μ λ°λ μν μ½λμ λ°λΌ μλνμ§ μλ ν μ€νΈμ½λλ₯Ό μμ νκ² μ΅λλ€.
LoginTest λ³κ²½
λ³κ²½λ ν μ€νΈμ½λλ λ€μκ³Ό κ°μ΅λλ€.
@Test
public void λ‘κ·ΈμΈ_μ€ν¨_μμ΄λνλ¦Ό() throws Exception {
//given
Map<String, String> map = new HashMap<>();
map.put("username",USERNAME+"123");
map.put("password",PASSWORD);
//when
MvcResult result = mockMvc.perform(MockMvcRequestBuilders
.post(LOGIN_RUL)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(map)))
.andDo(print())
//.andExpect(status().isOk())//TODO μν
μ½λλ³κ²½
.andExpect(status().isBadRequest())
.andReturn();
//then
assertThat(result.getResponse().getHeader(accessHeader)).isNull();
assertThat(result.getResponse().getHeader(refreshHeader)).isNull();
}
@Test
public void λ‘κ·ΈμΈ_μ€ν¨_λΉλ°λ²νΈνλ¦Ό() throws Exception {
//given
Map<String, String> map = new HashMap<>();
map.put("username",USERNAME);
map.put("password",PASSWORD+"123");
//when
MvcResult result = mockMvc.perform(MockMvcRequestBuilders
.post(LOGIN_RUL)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(map)))
.andDo(print())
//.andExpect(status().isOk())//TODO μνμ½λλ³κ²½
.andExpect(status().isBadRequest())
.andReturn();
//then
assertThat(result.getResponse().getHeader(accessHeader)).isNull();
assertThat(result.getResponse().getHeader(refreshHeader)).isNull();
}
// λ‘κ·ΈμΈ_λ°μ΄ν°νμ_JSONμ΄_μλλ©΄_200
@Test
public void λ‘κ·ΈμΈ_λ°μ΄ν°νμ_JSONμ΄_μλλ©΄_400() throws Exception {
//given
Map<String, String> map = new HashMap<>();
map.put("username",USERNAME);
map.put("password",PASSWORD);
//when
MvcResult result = mockMvc.perform(MockMvcRequestBuilders
.post(LOGIN_RUL)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.content(objectMapper.writeValueAsString(map)))
.andDo(print())
//.andExpect(status().isOk()) TODO μνμ½λλ³κ²
.andExpect(status().isBadRequest())
.andReturn();
//then
assertThat(result.getResponse().getContentAsString()).isEqualTo(LOGIN_FAIL_MESSAGE);
}
JwtFilterAuthenticationTest λ³κ²½
@Test
public void λ‘κ·ΈμΈ_μ£Όμλ‘_보λ΄λ©΄_νν°μλ_X() throws Exception {
//given
Map accessAndRefreshToken = getAccessAndRefreshToken();
String accessToken= (String) accessAndRefreshToken.get(accessHeader);
String refreshToken= (String) accessAndRefreshToken.get(refreshHeader);
//when, then
MvcResult result = mockMvc.perform(post(LOGIN_RUL) //getμΈ κ²½μ° configμμ permitAllμ νκΈ°μ notFound
.header(refreshHeader, BEARER + refreshToken)
.header(accessHeader, BEARER + accessToken))
//.andExpect(status().isOk()) TODO : ν
μ€νΈμ½λμμ
.andExpect(status().isBadRequest())
.andReturn();
assertThat(result.getResponse().getContentAsString()).isEqualTo(LOGIN_FAIL_MESSAGE);
}
MemberControllerTest λ³κ²½
@Test
public void λΉλ°λ²νΈμμ _μ€ν¨_κ²μ¦λΉλ°λ²νΈκ°_νλ¦Ό() throws Exception {
//given
String signUpData = objectMapper.writeValueAsString(new MemberSignUpDto(username, password, name, nickName, age));
signUp(signUpData);
String accessToken = getAccessToken();
Map<String, Object> map = new HashMap<>();
map.put("checkPassword",password+"1");
map.put("toBePassword",password+"!@#@!#@!#");
String updatePassword = objectMapper.writeValueAsString(map);
//when
mockMvc.perform(
put("/member/password")
.header(accessHeader,BEARER+accessToken)
.contentType(MediaType.APPLICATION_JSON)
.content(updatePassword))
//.andExpect(status().isOk());//TODO ν
μ€νΈμ½λ μμ
.andExpect(status().isBadRequest());//TODO ν
μ€νΈμ½λ μμ
//then
Member member = memberRepository.findByUsername(username).orElseThrow(() -> new Exception("νμμ΄ μμ΅λλ€"));
assertThat(passwordEncoder.matches(password, member.getPassword())).isTrue();
assertThat(passwordEncoder.matches(password+"!@#@!#@!#", member.getPassword())).isFalse();
}
@Test
public void νμνν΄_μ€ν¨_λΉλ°λ²νΈνλ¦Ό() throws Exception {
//given
String signUpData = objectMapper.writeValueAsString(new MemberSignUpDto(username, password, name, nickName, age));
signUp(signUpData);
String accessToken = getAccessToken();
Map<String, Object> map = new HashMap<>();
map.put("checkPassword",password+11);
String updatePassword = objectMapper.writeValueAsString(map);
//when
mockMvc.perform(
delete("/member")
.header(accessHeader,BEARER+accessToken)
.contentType(MediaType.APPLICATION_JSON)
.content(updatePassword))
//.andExpect(status().isOk()); //TODO ν
μ€νΈμ½λ μμ
.andExpect(status().isBadRequest());
//then
Member member = memberRepository.findByUsername(username).orElseThrow(() -> new Exception("νμμ΄ μμ΅λλ€"));
assertThat(member).isNotNull();
}
@Test
public void νμμ 보쑰ν_μ€ν¨_μλνμμ‘°ν() throws Exception {
//given
String signUpData = objectMapper.writeValueAsString(new MemberSignUpDto(username, password, name, nickName, age));
signUp(signUpData);
String accessToken = getAccessToken();
//when
MvcResult result = mockMvc.perform(
get("/member/2211")
.characterEncoding(StandardCharsets.UTF_8)
.header(accessHeader, BEARER + accessToken))
//.andExpect(status().isOk()).andReturn();//TODO: ν
μ€νΈμ½λ μμ okμμ λ³κ²
.andExpect(status().isNotFound()).andReturn();
//then
Map<String, Integer> map = objectMapper.readValue(result.getResponse().getContentAsString(), Map.class);
assertThat(map.get("errorCode")).isEqualTo(MemberExceptionType.NOT_FOUND_MEMBER.getErrorCode());//λΉ λ¬Έμμ΄
}
@Test
public void νμκ°μ
_μ€ν¨_νλκ°_μμ() throws Exception {
//given
String noUsernameSignUpData = objectMapper.writeValueAsString(new MemberSignUpDto(null, password, name, nickName, age));
String noPasswordSignUpData = objectMapper.writeValueAsString(new MemberSignUpDto(username, null, name, nickName, age));
String noNameSignUpData = objectMapper.writeValueAsString(new MemberSignUpDto(username, password, null, nickName, age));
String noNickNameSignUpData = objectMapper.writeValueAsString(new MemberSignUpDto(username, password, name, null, age));
String noAgeSignUpData = objectMapper.writeValueAsString(new MemberSignUpDto(username, password, name, nickName, null));
//when, then
/* signUp(noUsernameSignUpData);//μμΈκ° λ°μνλλΌλ μνμ½λλ 200
signUp(noPasswordSignUpData);//μμΈκ° λ°μνλλΌλ μνμ½λλ 200
signUp(noNameSignUpData);//μμΈκ° λ°μνλλΌλ μνμ½λλ 200
signUp(noNickNameSignUpData);//μμΈκ° λ°μνλλΌλ μνμ½λλ 200
signUp(noAgeSignUpData);//μμΈκ° λ°μνλλΌλ μνμ½λλ 200*/
signUpFail(noUsernameSignUpData);//μμΈκ° λ°μνλ©΄ μνμ½λλ 400
signUpFail(noPasswordSignUpData);//μμΈκ° λ°μνλ©΄ μνμ½λλ 400
signUpFail(noNameSignUpData);//μμΈκ° λ°μνλ©΄ μνμ½λλ 400
signUpFail(noNickNameSignUpData);//μμΈκ° λ°μνλ©΄ μνμ½λλ 400
signUpFail(noAgeSignUpData);//μμΈκ° λ°μνλ©΄ μνμ½λλ 400
assertThat(memberRepository.findAll().size()).isEqualTo(0);
}
signUpFailμ λ€μκ³Ό κ°μ΅λλ€.
private void signUpFail(String signUpData) throws Exception {
mockMvc.perform(
post(SIGN_UP_URL)
.contentType(MediaType.APPLICATION_JSON)
.content(signUpData))
.andExpect(status().isBadRequest());
}
@Test
public void λΉλ°λ²νΈμμ _μ€ν¨_λ°κΎΈλ €λ_λΉλ°λ²νΈ_νμ_μ¬λ°λ₯΄μ§μμ() throws Exception {
//given
String signUpData = objectMapper.writeValueAsString(new MemberSignUpDto(username, password, name, nickName, age));
signUp(signUpData);
String accessToken = getAccessToken();
Map<String, Object> map = new HashMap<>();
map.put("checkPassword",password);
map.put("toBePassword","123123");
String updatePassword = objectMapper.writeValueAsString(map);
//when
mockMvc.perform(
put("/member/password")
.header(accessHeader,BEARER+accessToken)
.contentType(MediaType.APPLICATION_JSON)
.content(updatePassword))
//.andExpect(status().isOk());
.andExpect(status().isBadRequest());
//then
Member member = memberRepository.findByUsername(username).orElseThrow(() -> new Exception("νμμ΄ μμ΅λλ€"));
assertThat(passwordEncoder.matches(password, member.getPassword())).isTrue();
assertThat(passwordEncoder.matches("123123", member.getPassword())).isFalse();
}
λ€ μ΄λ κ² ν΄μ μ½λ μμ μ΄ λλ¬μ΅λλ€.
μ΄μ λ€μ μκ°μλ Post Serviceμ, νμΌμ μ μ₯νλ File Serviceλ₯Ό λ§λ€μ΄ 보λλ‘ νκ² μ΅λλ€.
μ 체 μ½λλ κΉνλΈμμ νμΈνμ€ μ μμ΅λλ€.
https://github.com/ShinDongHun1/SpringBoot-Board-API