inblog logo
|
code-sudal
    blog

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

    윤주헌's avatar
    윤주헌
    Sep 02, 2024
    11. 스프링부트(블로그만들기) 유효성검사 V2
    Contents
    유효성 검사!시작인터셉터 먼저 해볼거임 해야 할 것config에 클래스 만들기 WebConfig테스트로그인 인터셉터클래스로 이동보드 컨트롤러컨피그 가서보드 컨트롤러 이동해더 머스테치 주소 수정디테일 머스테치세이브 머스테치업데이트 폼 머스테치유저 컨트롤러보드 리스폰스 수정 (로그인 안하고 게시글에 들어갈 수 있게)AOP클래스 생성연습정규 표현식다시

    유효성 검사!

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

    리플랙션 배울거임

    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

    RSS·Powered by Inblog