11. 스프링부트(블로그만들기) 유효성검사 V2

윤주헌's avatar
Sep 02, 2024
11. 스프링부트(블로그만들기) 유효성검사 V2

유효성 검사!

유효성, 인터셉터 배우면 끝난다

리플랙션 배울거임

DS

배울거 설명

  • 그림
notion image
💡
DS와 같은 위치에 ViewResolver, GlobalExceptionHandler이 있다
DS와 Controller 사이 그림
notion image
컨트롤러에서 Get 요청하면 리턴해서
  1. 파일을 찾아서 파일리더로 읽는다
  1. 머스테치 라이브러리로 해석한다(compile 해서) → 순수 HTML 나온다
  1. 여기를 View Resolver가 해준다!!
 
GEH, ViewResolver는 DS가 때리는 거고 DS가 모든 Requset를 받는다.
 
파싱해서 값을 넣어주는데 DS와 C 사이에 유효성 검사 해주는게 좋지 않을까? 해서
꺼지라고 할거임
Controller에서 처리가 아닌 DS 전에 프록시 패턴으로 할거임
하지만 프레임 워크는 손댈 수 없음
그래서 프록시로 할 수 있는 방법을 배워야 한다.
 
스프링이 AOP를 제공해 준다!!
AOP
💡
관점 지향 프로그램 핵심 → 핵심 로직이 아닌 부가 로직을 (프록시로? ) 해결 해 준다! 프록시패턴과 리플랙션을 사용할 거임!!
OOP
💡
객체 지향 프로그램
 
프록시는 리플랙션으로 매개변수 분석할 수 있다
AOP는 리플랙션으로 사용가능하다
 
if(saveDTO.getTitle().length() >10){}
이게 확인인데 이거를 컨트롤러 앞으로 끌어내서 부가 로직을 따로 해결해 준다!!

시작

빌드 그래이들에 디팬던시에 추가해주자
implementation 'org.springframework.boot:spring-boot-starter-validation'
코끼리 모양도 눌러주자!
notion image

유저 리퀘스트로 이동

 
💡
AOP, 인터셉터 (1)

설명 인터셉터, AOP배울거임

인터셉터 먼저 해볼거임

코어 가서 패키지 config만들기

notion image
  • interceptor
💡
어떤 행위 , 뭘 실행할 것인가?
  • config
💡
언제 실행할 것인가 정함

인터셉트에 클래스 만들기 Logininterceptor

//타입은 HandlerInterceptor다
왜 implement했는데 오류 안나는가? HandlerInterceptor는 디폴트 매서드이기 때문이다 쓰고 싶은 것만 사용해라
만약 추상 메서드면 3개 다 설정 해야함

해야 할 것

HandlerInterceptor의 3가지 조금 있다가
 
pre치고 뜨는 거 엔터
notion image
return 에 쭉 지우고 true로 설정
notion image
false면 진입 안됨 어디에??(로그인 안 한 사람들?)
package shop.mtcoding.blog.core.interceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; //타입은 HandlerInterceptro다 public class Logininterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("프리핸들 동작합 ---------------------------"); return true; } }
reqeust, response를 DS가 값을 전달해준다!
 
모든 설정은 config에서 설정함

config에 클래스 만들기 WebConfig

add 치고 interceptors 클릭
 
notion image
중간에 지우고
// 설정은 configuration으로 띄운다 IoC에 저장됨 @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { //특정한 곳에서 할 수 있게 2번째 . 해서 추가해줌 ///user, /board 때만 사용됨 registry.addInterceptor(new Logininterceptor()) .addPathPatterns("/user/**") .addPathPatterns("/board/**"); } }

테스트

notion image
notion image
잘 동작한다! 하지만
이 때는 동작 안 함
notion image
  • 결국 /user, /board 인 애들에서 만 프리핸들이 동작한다!!
 

로그인 인터셉터클래스로 이동

애는 컨트롤러와 DS 사이에 있고
글로벌익셉션 핸들러는 DS에 있어서
throw 하면 글로벌익셉션으로 들어갈 수 있다!!
하지만
필터는 스프링 컨텍스트에 있지 않아서 예외 나 이런 것들 내가 직접 다 해야 한다 !!
필터는 tomcat꺼다! 아파치 톰켓은 스프링 전에 동작해서 그럼!!
 
잘 모르면
public class Logininterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); User sessionUser = (User) session.getAttribute("sessionUser"); //System.out.println("프리핸들 동작합 ---------------------------"); if (sessionUser == null) { // 잘 모르면 response.sendRedirect("/login-form"); } return true; } }
이렇게 할거임
 
버퍼드 라이터
public class Logininterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); User sessionUser = (User) session.getAttribute("sessionUser"); //System.out.println("프리핸들 동작합 ---------------------------"); if (sessionUser == null) { //어떤 텍스튼지 몰라서 아래 줄 적어줘야 함 response.setContentType("text/html;charset=utf-8"); PrintWriter pw = response.getWriter(); pw.print("<script> alert('인증되지 않았엉요');\n"); pw.print("location.href='login-form';\n"); //버퍼에 담았으니 flush()해줘야 한다 //엔터키로 받는데 파싱할 때 한즐로 받아서 몰라서 무조건 \n을 적어줘야 한다!! 안적으면 뭔지 몰라서 파싱 못함!!! //기본이 flush와 \n이다!! //프린터 라이터 좋은 점은 println으로 자동으로 줄 띄워주고 , 버퍼드 라이터는 플러시 알아서 해줘서 안 적어도 된다!! // pw.flush(); } return true; } }
  • 주의
💡
엔터키로 받는데 파싱할 때 한 줄로 받아서 못 읽을 수 있다 그래서 무조건 끝에 \n을 해주자 아니면 println을 사용하자
 
우리는 이렇게 적을 거다!!
package shop.mtcoding.blog.core.interceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import org.springframework.web.servlet.HandlerInterceptor; import shop.mtcoding.blog.core.error.ex.Exception401; import shop.mtcoding.blog.user.User; import java.io.PrintWriter; //타입은 HandlerInterceptro다 public class Logininterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); User sessionUser = (User) session.getAttribute("sessionUser"); //System.out.println("프리핸들 동작합 ---------------------------"); if (sessionUser == null) { throw new Exception401("인증되지 않았어요"); } return true; } }
throw했으니 리턴 안 해도 됨?
 
그냥 동작 될 거니까

보드 컨트롤러

if (sessionUser == null) { throw new Exception401("로그인이 필요합니다"); }
삭제함
 
결과
package shop.mtcoding.blog.board; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import shop.mtcoding.blog.core.error.ex.Exception401; import shop.mtcoding.blog.user.User; import java.util.List; //식별자 요청 받기, 응답하기가 이 친구의 책임 //1. 이거 안붙이면 식별자 요청 못 받는다 @RequiredArgsConstructor @Controller //2. 식별자 요청을 받을 수 있다 이런거를 어노테이션이라 한다 왜 식별자 요청이 되는가 이거는 나중에 설명해준다 public class BoardController { private final HttpSession session; private final BoardService boardService; @GetMapping("/test/board/1") public @ResponseBody void testBoard() { //여기까지는 레이지 유저 안 땡겨옴 // List<Board> boardList = boardRepository.findAll(); System.out.println("---------------------------------"); //보드 리스트를 리턴해버리면? getter다 때려서 json로 바꿔야 함 // System.out.println(boardList.get(2).getUser().getPassword()); System.out.println("---------------------------------------------"); } // url : http://localhost:8080/board/1/update //바디 데이터가 title=제목1 변경&content=내용1변경 //content-type : x-www-form-urlencoded @PostMapping("/board/{id}/update") public String update(@PathVariable("id") int id, BoardRequest.UpdateDTO updateDTO) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.게시글수정(id, updateDTO, sessionUser); // boardRepository.updateById(title, content, id); //상세보기로 가야함 return "redirect:/board/" + id; } @PostMapping("/board/{id}/delete") public String delete(@PathVariable("id") int id) { User sessionUser = (User) session.getAttribute("sessionUser"); //원래는 조회를 하고 삭제해야하는데 V1이라 그냥 함 boardService.게시글삭제(id, sessionUser); return "redirect:/board"; } //글쓰기 할게 -> 글쓰기 완료되면 메인으로 보내는게 좋다 @PostMapping("/board/save") public String save(BoardRequest.SaveDTO saveDTO) { //스프링 기본 견략 = x-www-form-urlencoded 파싱 매개변수만 같으면 됨 화면에 폼테그 name 과 반드시 같아야 한다!! //세션유저가 null이면 인증 안됨 null아니면 인증됨 User sessionUser = (User) session.getAttribute("sessionUser"); //인증 체크 필요함 //터트려라! 401이면 href하는게 좋다 boardService.게시글쓰기(saveDTO, sessionUser); //보드 레파지토리 객체가 어디있나? Ioc에 있다 autoWrid해서 가져옴 return "redirect:/board"; } /* 외부에서 이 메서드 어떻게 때리냐면 방법 4가지 get(가지고 오고 요청할 때), post(보내고 insert, delete update 요청할 때), put, delete(지울때) */ //리플랙션은 메서드 이름 필요 없다 주소만 필요하지 @GetMapping("/board") public String list(HttpServletRequest request) { List<Board> boardList = boardService.게시글목록보기(); //key값 모델스 //외부에서 /board요청 -> 톰캣에 감 rqeust객체로 만들어둠 -> 때림 -> Model에 rqeust객체 주입됨 -> 이 데이터를 reqeust객체에 넣어버림 request.setAttribute("models", boardList); return "board/list";//파일의 경로 넣으면 되는데 고정적으로 되어 있음 확장자 자동으로 해주는가? 라이브러리로 머스테치 설정해줘서(templates에 머스테치로 찾아줌) } /* 1. 메서드 : get을 사용 2. 주소 : /board/1 3. 응답 : board/detail 1. 메서드 : get을 사용 2. 주소 : /board/1/save-form 3. 응답 : board/save-form 1. 메서드 : get을 사용 2. 주소 : /board/1/update-form 3. 응답 : board/update-form */ @GetMapping("/board/{id}") public String detail(@PathVariable("id") Integer id, HttpServletRequest request) { //Board board = boardRepository.findById(id); //한건이니까 model //여러건이면 models // request.setAttribute("model", board); request.setAttribute("isOwner", false); User sessionUser = (User) session.getAttribute("sessionUser"); BoardResponse.DetailDTO detailDTO = boardService.상세보기(id, sessionUser); request.setAttribute("model", detailDTO); return "board/detail"; } @GetMapping("/board/save-form") public String saveForm() { return "board/save-form"; } @GetMapping("/board/{id}/update-form") public String updateForm(@PathVariable("id") int id, HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.게시글수정화면가기(id, sessionUser); request.setAttribute("model", board); //이 친구도 오류 잡자! //못찾으면 터진다! null 안들어옴 우리가 설정해서 // Board board = boardRepository.findById(id); //리퀘스트에 담는다 // request.setAttribute("model", board); return "board/update-form"; } }
 
이러면 보드 유저 url이 들어간 곳은 다 안됨
주소를 잘 설정해야 가능하다!!
인증 필요한 곳에서 만 주소에 /api를 적자

컨피그 가서

package shop.mtcoding.blog.core.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import shop.mtcoding.blog.core.interceptor.Logininterceptor; // 설정은 configuration으로 띄운다 IoC에 저장됨 @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { //특정한 곳에서 할 수 있게. 해서 추가해줌 ///user, /board 때만 사용됨 registry.addInterceptor(new Logininterceptor()) .addPathPatterns("/api/**"); } }

보드 컨트롤러 이동

주소 바꿈
package shop.mtcoding.blog.board; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import shop.mtcoding.blog.user.User; import java.util.List; //식별자 요청 받기, 응답하기가 이 친구의 책임 //1. 이거 안붙이면 식별자 요청 못 받는다 @RequiredArgsConstructor @Controller //2. 식별자 요청을 받을 수 있다 이런거를 어노테이션이라 한다 왜 식별자 요청이 되는가 이거는 나중에 설명해준다 public class BoardController { private final HttpSession session; private final BoardService boardService; @GetMapping("/test/board/1") public @ResponseBody void testBoard() { //여기까지는 레이지 유저 안 땡겨옴 // List<Board> boardList = boardRepository.findAll(); System.out.println("---------------------------------"); //보드 리스트를 리턴해버리면? getter다 때려서 json로 바꿔야 함 // System.out.println(boardList.get(2).getUser().getPassword()); System.out.println("---------------------------------------------"); } // url : http://localhost:8080/board/1/update //바디 데이터가 title=제목1 변경&content=내용1변경 //content-type : x-www-form-urlencoded @PostMapping("/api/board/{id}/update") public String update(@PathVariable("id") int id, BoardRequest.UpdateDTO updateDTO) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.게시글수정(id, updateDTO, sessionUser); // boardRepository.updateById(title, content, id); //상세보기로 가야함 return "redirect:/board/" + id; } @PostMapping("/api/board/{id}/delete") public String delete(@PathVariable("id") int id) { User sessionUser = (User) session.getAttribute("sessionUser"); //원래는 조회를 하고 삭제해야하는데 V1이라 그냥 함 boardService.게시글삭제(id, sessionUser); return "redirect:/"; } //글쓰기 할게 -> 글쓰기 완료되면 메인으로 보내는게 좋다 @PostMapping("/api/board/save") public String save(BoardRequest.SaveDTO saveDTO) { //스프링 기본 견략 = x-www-form-urlencoded 파싱 매개변수만 같으면 됨 화면에 폼테그 name 과 반드시 같아야 한다!! //세션유저가 null이면 인증 안됨 null아니면 인증됨 User sessionUser = (User) session.getAttribute("sessionUser"); //인증 체크 필요함 //터트려라! 401이면 href하는게 좋다 boardService.게시글쓰기(saveDTO, sessionUser); //보드 레파지토리 객체가 어디있나? Ioc에 있다 autoWrid해서 가져옴 return "redirect:/"; } /* 외부에서 이 메서드 어떻게 때리냐면 방법 4가지 get(가지고 오고 요청할 때), post(보내고 insert, delete update 요청할 때), put, delete(지울때) */ //리플랙션은 메서드 이름 필요 없다 주소만 필요하지 @GetMapping("/") public String list(HttpServletRequest request) { List<Board> boardList = boardService.게시글목록보기(); //key값 모델스 //외부에서 /board요청 -> 톰캣에 감 rqeust객체로 만들어둠 -> 때림 -> Model에 rqeust객체 주입됨 -> 이 데이터를 reqeust객체에 넣어버림 request.setAttribute("models", boardList); return "board/list";//파일의 경로 넣으면 되는데 고정적으로 되어 있음 확장자 자동으로 해주는가? 라이브러리로 머스테치 설정해줘서(templates에 머스테치로 찾아줌) } /* 1. 메서드 : get을 사용 2. 주소 : /board/1 3. 응답 : board/detail 1. 메서드 : get을 사용 2. 주소 : /board/1/save-form 3. 응답 : board/save-form 1. 메서드 : get을 사용 2. 주소 : /board/1/update-form 3. 응답 : board/update-form */ @GetMapping("/board/{id}") public String detail(@PathVariable("id") Integer id, HttpServletRequest request) { //Board board = boardRepository.findById(id); //한건이니까 model //여러건이면 models // request.setAttribute("model", board); request.setAttribute("isOwner", false); User sessionUser = (User) session.getAttribute("sessionUser"); BoardResponse.DetailDTO detailDTO = boardService.상세보기(id, sessionUser); request.setAttribute("model", detailDTO); return "board/detail"; } @GetMapping("/api/board/save-form") public String saveForm() { return "board/save-form"; } @GetMapping("/api/board/{id}/update-form") public String updateForm(@PathVariable("id") int id, HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.게시글수정화면가기(id, sessionUser); request.setAttribute("model", board); //이 친구도 오류 잡자! //못찾으면 터진다! null 안들어옴 우리가 설정해서 // Board board = boardRepository.findById(id); //리퀘스트에 담는다 // request.setAttribute("model", board); return "board/update-form"; } }
이제 HTML 가서 수정 해야한다
인터셉터? get??
 

해더 머스테치 주소 수정

notion image

디테일 머스테치

notion image

세이브 머스테치

notion image

업데이트 폼 머스테치

notion image

유저 컨트롤러

notion image

보드 리스폰스 수정 (로그인 안하고 게시글에 들어갈 수 있게)

public class BoardResponse { //보드 응답 DTO 상세보기 @Data public static class DetailDTO { private Integer boardId; private String title; private String content; private Boolean isOwner; private Integer userId; private String username; //가장 중요한 것 entitiy 총 2개 board, user //프라이머리키는 반드시 줘야해 프론트가 뭘 할 수도 있으니까 public DetailDTO(Board board, User sessionUser) { this.boardId = board.getId(); this.title = board.getTitle(); this.content = board.getContent(); this.isOwner = false; if (sessionUser != null) { if (board.getUser().getId() == sessionUser.getId()) { isOwner = true; } } this.userId = board.getUser().getId(); this.username = board.getUser().getUsername(); } }

AOP

추가 빌드 그레이들에 디팬던시에 추가
implementation 'org.springframework.boot:spring-boot-starter-aop'

클래스 생성

notion image
@Aspect
💡
AOP 등록
@Component
💡
???
비포, 에프터 어라운드 3개가 있다
 

비포

//특정한 어노테이션 전에 발동될 수 있게 // 메서드나 매개변수 이후에 하기 힘들기 때문 // 사용할 때 풀 내임 적어야 하는데 //지금 getMapping실행되면 될 수 있게 @Before("@annotation(org.springframework.web.bind.annotation.GetMapping)") public void hello() { System.out.println("aop hello1 호출됨"); // 언제 발동될 것인지 }
 
풀 내임 적어야 하는데 그냥 임포트 하는 것 뒤에 있는 것 복사해서 넣어 버리자
notion image
누르면
notion image
결과
notion image
표현식으로 적기도 한다
블로그 안의 모든 페키지 안에 모든 컨트롤러 이후에 사용됨 @Before("shop.mtcoding.blog.*.*Controller(..)") public void hello() { System.out.println("aop hello1 호출됨"); // 언제 발동될 것인지 }
이래 잘 안적음
.. 하면 매개변수 몇개 상관없이 여러개 , . 한 개면 매개변수 한 개만
  • 리플랙션은 무조건 어노테이션 사용하는게 제일 좋다!!

어노테이션 한번 만들어 보자

 
  1. 라이브러리 넣기
notion image
만든 것 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Hello { }
 
@Before("@annotation(shop.mtcoding.blog.core.Hello)") public void hello() { System.out.println("aop hello1 호출됨"); // 언제 발동될 것인지 }
글로벌에 넣기
헬로 어노테이션에 붙은 애들에서만 실행함
 

컨트롤러

 
notion image
notion image
notion image
 
이때는 안 나옴
notion image
매개변수 처리할 수 있다
 

어라운드

💡
비포, 에프터 믹스 버전임
이전 이후 다 관리할 수 있다.
→ 코드 시작
 
JoinPoint로 매개변수 접근 가능
notion image
notion image
 
notion image
notion image
before 호출 되고 loginFrom 호출되고 after 호출 된다!
트랜잭션도 AOP를 사용해서 진행됨
 
 
왜 포스트 매핑 때만 하는가?
GETMapping은 바디 데이터가 없어서 할 필요 없음
밸리대이션 체킹할 수 있다.
 
 
비포, 에프터 둘 다 전 후 관리 못함
 
인터셉터는
프리앤트 포스트 앤드에 넣으면 됨
 
인터셉터는 주소만 받을 수 있고
어노테이션으로는 못 받음
 
  • put 사용하고 싶으면
@Before("@annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping)")

연습

@Before("@annotation(org.springframework.web.bind.annotation.PostMapping)") public void validCheck(JoinPoint jp) { //메서드 매개변수 알려줌 //postMapping의 매개 변수 전부 가지고 온나 //@PathVariable("id") int id 는 1개로 칠까 2개로 칠까? Object[] args = jp.getArgs(); System.out.println("사이즈 : " + args.length); for (Object arg : args) { System.out.println(arg); } }
notion image

보드 리퀘스트

saveDTO
package shop.mtcoding.blog.board; import jakarta.validation.constraints.NotEmpty; import lombok.Data; import shop.mtcoding.blog.user.User; public class BoardRequest { @Data public static class UpdateDTO { private String title; @NotEmpty private String content; //포린키 받아야 하는데 클라이언트는 모름 세션에서 꺼내와야 한다 세션에 id있어서 //퍼시스트 할거다 보드 오브젝트로 해야함 } @Data public static class SaveDTO { @NotEmpty private String title; @NotEmpty private String content; //포린키 받아야 하는데 클라이언트는 모름 세션에서 꺼내와야 한다 세션에 id있어서 //퍼시스트 할거다 보드 오브젝트로 해야함 public Board toEntity(User sessionUser) { return Board.builder() .title(title) .content(content) .user(sessionUser) .build(); } } }
@NotEmpty 공백도 null도 안됨
 
넣은 후
 
보드 컨트롤러 이동
@PostMapping("/api/board/save") public String save(@Valid BoardRequest.SaveDTO saveDTO, Errors errors) { //스프링 기본 견략 = x-www-form-urlencoded 파싱 매개변수만 같으면 됨 화면에 폼테그 name 과 반드시 같아야 한다!! //세션유저가 null이면 인증 안됨 null아니면 인증됨 User sessionUser = (User) session.getAttribute("sessionUser"); //인증 체크 필요함 //터트려라! 401이면 href하는게 좋다 boardService.게시글쓰기(saveDTO, sessionUser); //보드 레파지토리 객체가 어디있나? Ioc에 있다 autoWrid해서 가져옴 return "redirect:/"; }
안에 타이틀 콘텐트 있는데
save 때릴 때 valid 붙어 있으면 어노테이션 분석해서 null 이거나 공백이면 Error 때려버린다
 
notion image
@Before("@annotation(org.springframework.web.bind.annotation.PostMapping)") public void validCheck(JoinPoint jp) { //메서드 매개변수 알려줌 //postMapping의 매개 변수 전부 가지고 온나 //@PathVariable("id") int id 는 1개로 칠까 2개로 칠까? Object[] args = jp.getArgs(); for (Object arg : args) { //만약 arg에 Errors가 있다면 두개중 타입이 Errors가 있다면 instanceof 타입 검사 다운케스팅 할거임 //에러스 없으면 밸리데이션 검사 안하고 잇으면 검사 한다 if(arg instanceof Errors) { Errors errors = (Errors) arg; //에서 있는지 검사 if(errors.hasErrors()) { throw new Exception400("유효성검사 실패입니다."); } } } }

정규 표현식

@Data public static class SaveDTO { @Pattern("정규 표현식 적기") @NotEmpty private String title; @NotEmpty private String content; //포린키 받아야 하는데 클라이언트는 모름 세션에서 꺼내와야 한다 세션에 id있어서 //퍼시스트 할거다 보드 오브젝트로 해야함 public Board toEntity(User sessionUser) { return Board.builder() .title(title) .content(content) .user(sessionUser) .build(); } }
@Pattern() 안에 넣으면 됨

다시

throw new Exception400("유효성검사 실패입니다."); 이게 아님 그래서
notion image
@Before("@annotation(org.springframework.web.bind.annotation.PostMapping)") public void validCheck(JoinPoint jp) { //메서드 매개변수 알려줌 //postMapping의 매개 변수 전부 가지고 온나 //@PathVariable("id") int id 는 1개로 칠까 2개로 칠까? Object[] args = jp.getArgs(); for (Object arg : args) { System.out.println("arg임 : " + arg); //만약 arg에 Errors가 있다면 두개중 타입이 Errors가 있다면 instanceof 타입 검사 다운케스팅 할거임 //에러스 없으면 밸리데이션 검사 안하고 잇으면 검사 한다 if (arg instanceof Errors) { Errors errors = (Errors) arg; //에서 있는지 검사 if (errors.hasErrors()) { if (errors.hasErrors()) { //field는 변수 명 만약 에러 2개 여도 하나 throw하면 끝난다 // 바디데이터 10개중에 10개 다 에러면 한번에 다 알려줄 필요 없다 한개만 날려도 됨 //한번에 주고 싶으면 hasMap에 담아서 한번에 주면 된다. for (FieldError error : errors.getFieldErrors()) { throw new Exception400(error.getDefaultMessage() + " : " + error.getField()); } } } } } }
 
만약 메시지 주고 싶으면
@Data public static class SaveDTO { @NotEmpty(message = "비워두지마") private String title; @NotEmpty private String content; //포린키 받아야 하는데 클라이언트는 모름 세션에서 꺼내와야 한다 세션에 id있어서 //퍼시스트 할거다 보드 오브젝트로 해야함 public Board toEntity(User sessionUser) { return Board.builder() .title(title) .content(content) .user(sessionUser) .build(); }
만약 로그인에도 걸고 싶으면

유저 리퀘스트로 이동

notion image
notion image
이러면 컨트롤러에 동작할까? 안함
Errors 없어서 동작이 안된다!!
notion image
매번 if로 할 필요 없다
 
결과
notion image
 
 
만약 이메일도 하고싶으면
notion image
보드리퀘스트에도
notion image

이제 컨트롤러에 @Valid 다 넣어주고 @NotEmpty DTO에 넣어주자

유저리퀘스트
notion image
유저 컨트롤러
notion image
보드 리퀘스트
notion image
보드 컨트롤러
notion image
notion image
 
BindingResult bindingResult 이렇게 적어도 Errors처럼 됨,
다 에러스 타입임
  • 주의!!! @Valid는 에러뜨면 바로 뒤에 있어야 적용됨, 아니면 적용되지 않는다
public String update( @Valid BoardRequest.UpdateDTO updateDTO,@PathVariable("id") int id, Errors errors) {
AOP가 before if 에러스 이게 Errors 있는 곳 즉 유효성 필요한 곳에서 전부 적어야 하는데 때 놓은 거임
 
에러 발생하면 바로 뒤에서 만 해주고 두 번째 있는 곳은 에러가 안 들어 간다
 
Share article

code-sudal