Todo ์๋น์ค์ ํ์ CRUD, ํ์๋ณ ๊ฒ์๋ฌผ CRUD ๊ธฐ๋ฅ์ด ๋ชจ๋ ๊ตฌํ๋์๋ค
์ด์ ํ์๊ฐ์ ํ ์ฟ ํค์ ์ธ์ ์ ํ์ฉํด ์ธ์ฆ ๊ธฐ๋ฅ์ ๋ง๋ค์ด๋ณด์
๐ ์๊ตฌ์ฌํญ
1. ํ์๊ฐ์ ํ ๋ก๊ทธ์ธํ๊ธฐ
2. ๋ก๊ทธ์ธํ๋ฉด ์ฟ ํค์ ์ธ์ ๋จ๊ธฐ๊ธฐ
3. @Configuration ์ ํ์ฉํด ํ์ด์ง๋ณ๋ก ํํฐ๋ฅผ ๋ฑ๋กํ๊ธฐ
4. ๋ก๊ทธ์์ํ๋ฉด ์ฟ ํค&์ธ์ ์ญ์ ํ๊ธฐ
5. ์ด๋ฉ์ผ๊ณผ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์ ๊ฒฝ์ฐ HTTP Status 401์ ๋ฐํ
์ ์ฒด์ ์ธ ํ์๊ฐ์
๋ก๊ทธ์ธ ์ธ์
์ ์ง ๋ก๊ทธ์์ ๋ก์ง์ ์ ๋ฆฌํด๋ณด์
ํ์ ๊ฐ์
/signup
: name email password ์
๋ ฅํด์ ํ์๊ฐ์
โ MemberService.signUp()์ ํตํด ๊ตฌํ๋จ
โ ์ฌ๊ธฐ๋ ๋ก๊ทธ์ธ ์ํด๋ ์ ๊ทผ ๊ฐ๋ฅํ๊ฒ ๊ตฌ์ฑํด์ค์ผํจ
๋ก๊ทธ์ธ /login
: email password๋ก ์์ฒญ ๋ณด๋ด๋ฉด ์ ์ฅ๋ ํ์์ ๋ณด์์ ๊ฒ์ฆํ๊ณ , ๋ง์ผ๋ฉด session ์์ฑํด์ฃผ๊ธฐ
โ ์ด๊ฑธ ์ฟ ํค์ ์ ์ฅํด๋๊ณ ๋์ค์ ํ์ํ ๋๋ง๋ค ์ฟ ํค๋ฅผ ๋ณด๋ด์ ์ธ์ฆํ์
์ํ ํ์ธ /check
: ์ฌ์ฉ์๊ฐ ์ด ํ์ด์ง์ ์ ์ํ ๊ฑฐ์ผ!๋ผ๊ณ ํ ๋๋ง๋ค ๋ก๊ทธ์ธํ๋์ง ๊ฒ์ฆ
โ ์๊น ์์ฑ๋ session์ฟ ํค๋ฅผ ์ ์กํ๊ณ , ์๋ฒ๊ฐ ์ด๋ฅผ ์กฐํํด์ ๊ฒ์ฆ
โ ๋ก๊ทธ์ธ๋ ์ํ๋ฉด ํ์์ ๋ณด๋ฅผ ๋ฑ๊ธฐ / ์๋๋ฉด ๋ก๊ทธ์ธํ๋ผ๊ณ ์๋ ค์ฃผ๊ธฐ
๋ก๊ทธ์์ /logout
: ์ธ์
์ญ์ ํด์ฃผ๊ธฐ
โ ์ด๋ filter๋ฅผ ์ฌ์ฉํด์ ์ฌ์ฉ์์ ์์ฒญ์ด ์ปจํธ๋กค๋ฌ๋ก ๊ฐ๊ธฐ ์ ์ ๋ฏธ๋ฆฌ ๊ฒ์ฆํ ์ ์๋๋ก ํด์ฃผ์
1๏ธโฃ ์ฌ์ฉ์๊ฐ email, password๋ฅผ JSON์ผ๋ก ๋ณด๋
2๏ธโฃ memberService.login()์์ ์ด๋ฉ์ผ & ๋น๋ฐ๋ฒํธ ๊ฒ์ฆ
3๏ธโฃ ์ธ์
์ ์์ฑํ๊ณ session.setAttribute("user", email) ์ ์ฅ
4๏ธโฃ ์ดํ ํด๋ผ์ด์ธํธ๋ ๋ชจ๋ ์์ฒญ์ ๋ก๊ทธ์ธ ์ํ ์ ์ง
@PostMapping์ผ๋ก ๋ก๊ทธ์ธ์ ๊ตฌํํ์//๋ก๊ทธ์ธ
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody MemberRequestDto dto,
HttpServletRequest request,
HttpServletResponse response) {}
์ฌ์ฉ์์๊ฒ์ email password ์ ๋ณด๋ฅผ ์
๋ ฅ๋ฐ์์ผํจ โ @RequestBoy MemberRequestDto dto
request โ ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ธ ์์ฒญ ์ ๋ณด๋ฅผ ๋ด๊ณ ์์
response โ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ต์ ๋ณด๋ผ ๋ ์ฌ์ฉ
์ด์ login ๋ฉ์๋ ๋ด๋ถ๋ฅผ ๊ตฌํํด๋ณด์
์ด๋ฉ์ผ๊ณผ ๋น๋ฒ์ ๋ฃ์ด์ ๊ทธ ํ์์ด ์กด์ฌํ๊ณ ๋น๋ฒ๋ ๋ง์ผ๋ฉด(service์์) ๋ก๊ทธ์ธ ์ธ์
&์ฟ ํค์ ์ ๊ณตํด์ค๋ค
Member member = memberService.login(dto.getEmail(), dto.getPassword()); //ํ์์ด ์กด์ฌํ๋์ง ๊ฒ์ฆ
HttpSession session = request.getSession(); //์ธ์
์์ฒญํ๊ธฐ
session.setAttribute(Const.LOGIN_USER, member.getId());
return ResponseEntity.ok("๋ก๊ทธ์ธ ์ฑ๊ณต");
request.getSession()
โ ํ์ฌ ์ธ์
์ด ์์ผ๋ฉด ์๋ก์ด ์ธ์
์ ์์ฑ / ๊ธฐ์กด์ ์ธ์
์ด ์์ผ๋ฉด ํด๋น ์ธ์
์ ๋ฐํ
session.setAttribute("user", member.getEmail())
โ ์ธ์
์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ ์ฅ (user ํค์ ์ด๋ฉ์ผ ์ ์ฅ)
โก๏ธ ์ดํ ์ธ์ฆ์ด ํ์ํ API์์ session.getAttribute("user")๋ก ๋ก๊ทธ์ธ ์ฌ๋ถ๋ฅผ ํ์ธํ ์ ์๋ค!!
์ธ์ ์ ์ญ์ ํด์ฃผ์ด์ผํ๋ค
//๋ก๊ทธ์์
//๋ก๊ทธ์์
@PostMapping("/logout")
public ResponseEntity<String> logout(HttpServletRequest request, HttpServletResponse response) {
// ๋ก๊ทธ์ธํ์ง ์์ผ๋ฉด HttpSession์ด null๋ก ๋ฐํ๋๋ค.
HttpSession session = request.getSession(false);
// ์ธ์
์ด ์กด์ฌํ๋ฉด -> ๋ก๊ทธ์ธ์ด ๋ ๊ฒฝ์ฐ
if(session != null) {
session.invalidate(); // ํด๋น ์ธ์
(๋ฐ์ดํฐ)์ ์ญ์ ํ๋ค.
return ResponseEntity.ok("๋ก๊ทธ์์ ์ฑ๊ณต");
} else{
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
HttpSession session = request.getSession(false) ์ฌ๊ธฐ false๋ก ๋์ด์์ผ๋ฉด ์ธ์
์ด ์์๋ ์๋์ผ๋ก ์์ฑํ์ง ์๋๋ค => ๋ก๊ทธ์ธํ์ง ์์๋ค๋ฉด session์ด null๋ก ์ค์ ๋จ!
๋ก๊ทธ์ธ์ด ๋์ด์์ผ๋ฉด session.invalidate()๋ก ์ธ์
์ ์ญ์ !
: HTTP ์์ฒญ/์๋ต์ ๊ฐ๋ก์ฑ์ ์ฒ๋ฆฌํ๋ ์ญํ
โ ๋ก๊ทธ์ธ ์ํ ํ์ธ, JWT ๊ฒ์ฆ, ์์ฒญ ๋ก๊น
๋ฑ์ ์์
์ ์ํ
@Slf4j
public class LoginFilter implements Filter {
private static final String[] WHITE_LIST = {"/", "/members/signup", "/members/login", "/members/logout"};
WHITE_LIST์ ๊ฒ์ฆ์ ํ์ง ์์๋ ๋๋ ํ์ด์ง๋ฅผ ๋ฃ์ด์ฃผ์
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
// ๋ค์ํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ ์ํด ๋ค์ด ์บ์คํ
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
String requestURI = httpRequest.getRequestURI();
// ๋ค์ํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ ์ํด ๋ค์ด ์บ์คํ
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
log.info("๋ก๊ทธ์ธ ํํฐ ๋ก์ง ์คํ");
์ด ํด๋์ค ์๋์ ํ์ดํธ๋ฆฌ์คํธ์ธ์ง ์๋์ง ๊ฒ์ฆํ๋ ๋ฉ์๋๋ฅผ ๋จผ์ ๋ง๋ค์ด๋ณด์!
boolean ํ์
์ผ๋ก ๋ฐํํ๋ค
private boolean isWhiteList(String requestURI) {
// request URI๊ฐ whiteListURL์ ํฌํจ๋๋์ง ํ์ธ
// ํฌํจ๋๋ฉด true, ํฌํจ๋์ง ์์ผ๋ฉด false ๋ฐํ
return PatternMatchUtils.simpleMatch(WHITE_LIST, requestURI);
}
}
์ด ๋ฐ์ ๊ฒฐ๊ณผ๋ฅผ ํ ๋๋ก ๋ก๊ทธ์ธํ๋ฉด ์ธ์
๊ฐ์ด ์์ผ๋๊น doFilter ํด์ฃผ๊ณ
๋ก๊ทธ์ธํ์ง ์์๋ฐ๋ฉด ์ธ์
๊ฐ์ด ์์ผ๋๊น ์๋ฌ๋ฅผ ๋์ ธ์ค๋ค!
// ๋ก๊ทธ์ธ์ ์ฒดํฌ ํด์ผํ๋ URL์ธ์ง ๊ฒ์ฌ
// whiteListURL์ ํฌํจ๋ ๊ฒฝ์ฐ true ๋ฐํ -> !true = false
if (!isWhiteList(requestURI)) {
// ๋ก๊ทธ์ธ ํ์ธ -> ๋ก๊ทธ์ธํ๋ฉด session์ ๊ฐ์ด ์ ์ฅ๋์ด ์๋ค๋ ๊ฐ์ .
// ์ธ์
์ด ์กด์ฌํ๋ฉด ๊ฐ์ ธ์จ๋ค. ์ธ์
์ด ์์ผ๋ฉด session = null
HttpSession session = httpRequest.getSession(false);
// ๋ก๊ทธ์ธํ์ง ์์ ์ฌ์ฉ์์ธ ๊ฒฝ์ฐ
if (session == null || session.getAttribute(Const.LOGIN_USER) == null) {
throw new RuntimeException("๋ก๊ทธ์ธ ํด์ฃผ์ธ์ฉ์ฉ๊ฐ๋ฆฌ์นํจ ๋จน๊ณ ์ถ๋น");
}
// ๋ก๊ทธ์ธ ์ฑ๊ณต ๋ก์ง
}
// 1๋ฒ๊ฒฝ์ฐ : whiteListURL์ ๋ฑ๋ก๋ URL ์์ฒญ์ด๋ฉด ๋ฐ๋ก chain.doFilter()
// 2๋ฒ๊ฒฝ์ฐ : ํํฐ ๋ก์ง ํต๊ณผ ํ ๋ค์ ํํฐ ํธ์ถ chain.doFilter()
// ๋ค์ ํํฐ ์์ผ๋ฉด Servlet -> Controller ํธ์ถ
filterChain.doFilter(servletRequest, servletResponse);
}
๊ทธ๋ฆฌ๊ณ ๋ด๊ฐ ๋ง๋ ์ด ๋ก๊ทธ์ธ ํํฐ๋ฅผ
@Configuration์ ์ด์ฉํด ํํฐ๋ฅผ ์ง์ ๋ฑ๋กํด์ผํ๋ค!!
@Configuration
public class Config {
@Bean
public FilterRegistrationBean loginFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoginFilter()); // Filter ๋ฑ๋ก
filterRegistrationBean.setOrder(1); // Filter ์์ ์ค์
filterRegistrationBean.addUrlPatterns("/*"); // ์ ์ฒด URL์ Filter ์ ์ฉ
return filterRegistrationBean;
}
}
setFilter๋ฅผ ์ฌ์ฉํด์ ๋ก๊ทธ์ธํํฐ๋ฅผ ํํฐ์ ๋ฃ์ด์ฃผ๊ณ , ์ ์ฒด URL์ 1๋ฒ์งธ ์์๋ก ์ ์ฉํ๋ค!
์ง๊ธ๊น์ง์ ์๋ฌ ๋์ง๊ธฐ๋ ์ข์ง๋ง,,
{
"timestamp": "2025-03-26T14:26:45",
"status": 400,
"error": "BAD_REQUEST",
"code": "C001",
"message": "์๋ชป๋ ์
๋ ฅ๊ฐ์
๋๋ค",
"path": "/api/login",
"fieldErrors": [
{
"field": "username",
"message": "์์ด๋ ์
๋ ฅ์ ํ์์
๋๋ค"
}
]
}
{
"code": "C001",
"message": "์์ด๋ ์
๋ ฅ์ ํ์์
๋๋ค",
"status": "BAD_REQUEST"
}
์ด๋ฐ์์ผ๋ก ์๋ต์ ๋ฐ๊ณ ์ถ๋ค!
๊ทธ๋ฌ๋ ค๋ฉด~~
๐ ์์ธ ํด๋์ค(Custom Exception) ์์ฑ
โ ๋ก๊ทธ์ธ ์คํจ ์ ํน์ ์์ธ๋ฅผ ๋ฐ์์ํฌ ์ ์๋๋ก CustomException์ ๋ง๋ ๋ค.
๐งฉ ์์ธ ์ฒ๋ฆฌ ํธ๋ค๋ฌ(Global Exception Handler) ์์ฑ
โ@ControllerAdvice์@ExceptionHandler๋ฅผ ์ฌ์ฉํ์ฌ ์์ธ ๋ฐ์ ์ ์ผ๊ด๋ JSON ์๋ต์ ๋ฐํํ๋๋ก ์ค์ ํ๋ค.
| ๊ธฐ๋ฅ | ๋ฐฉ๋ฒ |
|---|---|
| ๋ก๊ทธ์ธ ์คํจ ์ ์์ธ ๋ฐ์ | CustomException ํด๋์ค ํ์ฉ |
| ์๋ฌ ์ฝ๋ ์ผ๊ด์ฑ ์ ์ง | Error Enum ์ ์ |
| ์ ์ญ ์์ธ ์ฒ๋ฆฌ | @RestControllerAdvice + @ExceptionHandler ์ฌ์ฉ |
| JSON ์๋ต ํํ | ErrorResponseDTO |
์ปค์คํ
์์ธ โ ๋ด๊ฐ ์ํ๋ ์ํฉ์์ ์ํ๋ ์๋ฌ๋ฅผ ๋ณด์ฌ์ค ์ ์๋ฐ
์ปค์คํ
์๋ฌ๋ฅผ ๋ง๋ค๋๋ RuntimeException์ ์์ํด์ ๋ง๋ค๋ฉด ๋๋ค
@Getter
public class CustomException extends RuntimeException {
private final Errors error;
public CustomException(Errors error) {
super(error.getMessage());
this.error = error;
}
}
super(error.getMessage()); ์ด ๊ณผ์ ์ ํตํด ์์๋ฐ์ ๋ถ๋ชจ์๊ฒ์๋ถํฐ ์๋ฌ ๋ฉ์ธ์ง๋ฅผ ๊ฐ์ ธ์จ๋ค
Enum์ ์ฌ์ฉํ๋ฉด ๋ด๊ฐ ์ํ๋ ์ด๋ฆ๊ณผ ์๋ฌ์ฝ๋๋ก ๋ฐํํด์ค ์ ์๋ค
(๊น๋ํจ์ ๋ณด๋์ค~!!)
@Getter
@AllArgsConstructor
public enum Errors {
INVALID_LOGIN("C001", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์ต๋๋ค.", HttpStatus.BAD_REQUEST),
REQUIRED_USERNAME("C002", "์์ด๋ ์
๋ ฅ์ ํ์์
๋๋ค.", HttpStatus.BAD_REQUEST);
private final String code;
private final String message;
private final HttpStatus status;
}
GlobalExceptionHandler๋ฅผ ํตํด ์๋ฌ๋ฅผ ํธ๋ค๋งํ ๊ฑฐ๋ค
์ด๋ฆ์์ ๋ณด์ด๋ค์ถ์ด, ์ ์ญ์์(ํ๋ก๊ทธ๋จ ์ ์ฒด์) ๋ฐ์ํ๋ ์๋ฌ๋ค์ ์ก์์ ์ฒ๋ฆฌํด์ฃผ๋ ํตํฉ ์๋ฌ์ฒ๋ฆฌ๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋น
์ด๋ค ํํ๋ก ๋ฐํํ ์ง ์ ํด์ฃผ์ด์ผํ๋ฏ๋ก ErrorResponseDto์ผ๋ก ํ์์ ๋ง๋ค์ด์ฃผ์
@Getter
@Builder
public class ErrorResponseDto {
private LocalDateTime timestamp;
private int status;
private String error;
private String code;
private String message;
private String path;
}
์๊ฐ, ์ํ, ์๋ฌ, ์ฝ๋, ๋ฉ์ธ์ง, ๊ฒฝ๋ก ํ์์ผ๋ก ๋ฐํํ๋๋ก ๋ง๋ค์๋ฐ
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
public ResponseEntity<ErrorResponseDto> handleCustomException(CustomException ex, HttpServletRequest request) {
Errors error = ex.getError();
ErrorResponseDto errorResponse = ErrorResponseDto.builder()
.timestamp(LocalDateTime.now())
.status(error.getStatus().value())
.error(error.getStatus().getReasonPhrase())
.code(error.getCode())
.message(error.getMessage())
.path(request.getRequestURI())
.build();
return ResponseEntity.status(error.getStatus()).body(errorResponse);
}
}
๊ทธ๋ผ ์ด์ ์ ์ญ์๋ฌ์ฒ๋ฆฌํด์ฃผ๋ ๊ธ๋ก๋ฒ ํธ๋ค๋ฌ์์ ๋ง๋ค์ด๋๊ฒ๋ค์ ๋ชจ๋ ์ ์ฉํด๋ณด์
@ExceptionHandler(CustomException.class)
์ต์
์
ํธ๋ค๋ฌ๋ก ์๊น ๋ง๋ค์ด๋ ์ปค์คํ
์๋ฌ๋ฅผ ๋ฃ์ด์ค๋ค
๋ฐํ๊ฐ์ public ResponseEntity<ErrorResponseDto> ์๊น ๋ง๋ค์ด๋ ํ์์ผ๋ก
์ด๋์ผ๋ก ๋ง๋์ด๋ Errors error = ex.getError(); ๋ฅผ ํตํด ์๋ฌ๋ฅผ ๊ฐ์ ธ์จ๋ค
ErrorResponseDto errorResponse = ErrorResponseDto.builder()
.timestamp(LocalDateTime.now())
.status(error.getStatus().value())
.error(error.getStatus().getReasonPhrase())
.code(error.getCode())
.message(error.getMessage())
.path(request.getRequestURI())
.build();
์ดํ error๋ฅผ ์ฌ์ฉํด์ getterํด์ฃผ๋ฉด ์๋ฌ๋ค์ ๋ด๊ฐ ์ํ๋๋๋ก ๊ฐ์ ธ์ฌ ์ ์๊ฒ ๋๋๊ฒ์ด๋ค~!!!!!!! ์ํธ
๊ทธ๋ฌ๋ฉด ์ด๋ ๊ฒ

์๋ฌ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ชจ์ต์ ๋ณผ ์ ์๋ฐ
์๋ฌ์ฝ๋๋ฅผ ๋ค๋ฅด๊ฒ ์ถ๋ ฅํ๊ณ ์ถ๋ค๋ฉด Enum์์ ์ด ๋ถ๋ถ๋ง ๋ฐ๊ฟ์ฃผ๋ฉด ๋๋ค!
