유효성 검사!
유효성, 인터셉터 배우면 끝난다
리플랙션 배울거임
DS
배울거 설명
- 그림

DS와 같은 위치에 ViewResolver, GlobalExceptionHandler이 있다
DS와 Controller 사이 그림

컨트롤러에서 Get 요청하면 리턴해서
- 파일을 찾아서 파일리더로 읽는다
- 머스테치 라이브러리로 해석한다(compile 해서) → 순수 HTML 나온다
- 여기를 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'
코끼리 모양도 눌러주자!

유저 리퀘스트로 이동
설명 인터셉터, AOP배울거임
인터셉터 먼저 해볼거임
코어 가서 패키지 config만들기

- interceptor
어떤 행위 , 뭘 실행할 것인가?
- config
언제 실행할 것인가 정함
인터셉트에 클래스 만들기 Logininterceptor
//타입은 HandlerInterceptor다
왜 implement했는데 오류 안나는가? HandlerInterceptor는 디폴트 매서드이기 때문이다 쓰고 싶은 것만 사용해라
만약 추상 메서드면 3개 다 설정 해야함
해야 할 것
HandlerInterceptor의 3가지 조금 있다가
pre치고 뜨는 거 엔터

return 에 쭉 지우고 true로 설정

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 클릭

중간에 지우고
// 설정은 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/**");
}
}
테스트


잘 동작한다! 하지만
이 때는 동작 안 함

- 결국 /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??
해더 머스테치 주소 수정

디테일 머스테치

세이브 머스테치

업데이트 폼 머스테치

유저 컨트롤러

보드 리스폰스 수정 (로그인 안하고 게시글에 들어갈 수 있게)
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'
클래스 생성

@Aspect
AOP 등록
@Component
???
비포, 에프터 어라운드 3개가 있다
비포
//특정한 어노테이션 전에 발동될 수 있게
// 메서드나 매개변수 이후에 하기 힘들기 때문
// 사용할 때 풀 내임 적어야 하는데
//지금 getMapping실행되면 될 수 있게
@Before("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void hello() {
System.out.println("aop hello1 호출됨");
// 언제 발동될 것인지
}
풀 내임 적어야 하는데 그냥 임포트 하는 것 뒤에 있는 것 복사해서 넣어 버리자

누르면

결과

표현식으로 적기도 한다
블로그 안의 모든 페키지 안에 모든 컨트롤러 이후에 사용됨
@Before("shop.mtcoding.blog.*.*Controller(..)")
public void hello() {
System.out.println("aop hello1 호출됨");
// 언제 발동될 것인지
}
이래 잘 안적음
.. 하면 매개변수 몇개 상관없이 여러개 , . 한 개면 매개변수 한 개만
- 리플랙션은 무조건 어노테이션 사용하는게 제일 좋다!!
어노테이션 한번 만들어 보자
- 라이브러리 넣기

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



이때는 안 나옴

매개변수 처리할 수 있다
어라운드
비포, 에프터 믹스 버전임
이전 이후 다 관리할 수 있다.
→ 코드 시작
JoinPoint로 매개변수 접근 가능




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);
}
}

보드 리퀘스트
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 때려버린다

@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("유효성검사 실패입니다."); 이게 아님 그래서

@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();
}
만약 로그인에도 걸고 싶으면
유저 리퀘스트로 이동


이러면 컨트롤러에 동작할까? 안함
Errors 없어서 동작이 안된다!!

매번 if로 할 필요 없다
결과

만약 이메일도 하고싶으면

보드리퀘스트에도

이제 컨트롤러에 @Valid 다 넣어주고 @NotEmpty DTO에 넣어주자
유저리퀘스트

유저 컨트롤러

보드 리퀘스트

보드 컨트롤러


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