[Springboot] 15 예외처리 및 코드 리팩토링 ~ing

김호정's avatar
Aug 23, 2024
[Springboot] 15 예외처리 및 코드 리팩토링 ~ing
 
notion image
 
에러 패키지 안에 ex 패키지 생성해주기
 
++
예외는 enum 으로 관리한다.
“public enum 예외” 클래스를 만들어서 거기다 변수로 다 셋팅함.
변수를 쓰면 오타가 안난다. 그래서 에러메시지를 변수로 저장해둠
상수로는 관리하기 힘듦. 상수는 무조건 변수화 시켜야 함. 회사가서 배우기.
우리가 하는건 인증안되는 경우를 하나로 처리하지만
실제로 인증이 안되는 경우가 매우 많은데(다양), 회사에서는 이걸 다 만듦
 
++
인증안됨 → 401
 
++
엑세스 토큰
리프레쉬 토큰
 
 
++
http 상태코드
 
notion image
 
 
** 이 5가지 정도는 기억하자 !
 
10X → 기다려 ( 서버가 바쁠때 )
20X → 성공 ( ex. 201 은 Created. ⇒ insert 잘 되었다고 하는거. 우리는 200으로 퉁쳐서 쓸 것)
30X → 딴 걸 돌려줄게 (→ 니가 요청한 것 말고 다른거 돌려줄게.
ex. 회원가입할때 요청은 아이디 insert 이고, 돌려줄건 로그인이나 메인페이지이니까
딴걸 돌려주는 형태가 된다(= redirection 리다이렉션) → 그때는 30X 를 쓴다)
40X → 클라이언트의 잘못 ( 요청자의 잘못 )
50X → 서버의 잘못. 내가 예상치 못한 예외가 터진 것. ( → 500번대가 터지면 로그를 남겨야 한다.
스택 트레이스 ( 에러가 난 트레이스 ) 를 보관하고 있어야 한다.
에러가 났을 때 상황을 봐야지 알 수 있는데, 앞뒤 전후 사정이랑 문맥을 알아야 디버깅이 된다.
뭐하다가 에러가 났는지. → 그래서 디버깅을 위해서는 500번대는 모두 로그를 남겨야 한다.)
 
 
 
 
// exception을 핸들러로 구분지음// 400번대는 모두 클라이언트가 요청을 잘못했을때 터트리는 것// 유효성 검사 할 때 터트릴 것
notion image
먼저 Exception400 을 만들고 RuntimeException을 상속받자.
(자바 파일로 만들어로 인텔리제이에서는 exception 파일 (노랑색)로 바꿔줌)
 
notion image
 
 
구조를 보면
 
Throwable - message 를 들고 있음. & getMessage(); 를 들고있음
Exception
ReuntimeException
Exception400 → get여기 getMessage 를 만들면 동적바인딩 되어서 여기 getMessage가 때려짐. → 그래서 여기에는 getMessage 를 만들면 안됨. → Throwable 의 getMessage 를 때려야 하니까.
→ exception400에는 getter만 있으면 됨!
 
 
notion image
 
복사해서 500까지 만들어주기
 
이제 분기 시킬 수 있음
 
404 와 403은 부모만 같지 다르다! 분기
 
notion image
 
util 패키지에 Script 파일 만들어주기
 
 
notion image
 
낮은 버전으로 가면 “”” 문법 안하고 스트링 빌더, 버퍼를 사용한다.
 
위의 메시지 복사해서 Script 에 복붙하기
notion image
위처럼 적어주기.
메시지 받아서 메시지에 넣고 errMsg 리턴하기
 
notion image
GlobalExceptionHandler 에 다시 가서 깔끔하게 수정
 
 
++
인증이 완료되면서 메시지가 뜨거나 화면이 전환되어야 하는 경우를 대비해
1개를 더 만든다.
notion image
href 매서드 생성 ( 위에꺼 복사해서 )
 
msg 뿐만 아니라 url 을 같이 받아서 msg를 띄우고 전달받은 url 로 보내버림.
 
예를 들어 로그인을 안하고 유저만 할 수있는 것에 접근하는 경우,
메시지를 띄우고 로그인으로 가게 해줘야 한다. 그럴땐 href 매서드를 쓴다.
 
notion image
GlobalExceptionHandler 에 5가지 생성 → 400, 401, 403, 404, 500
 
package shop.mtcoding.blog.core.error; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import shop.mtcoding.blog.core.error.ex.*; import shop.mtcoding.blog.core.util.Script; @RestControllerAdvice public class GlobalExceptionHandler { // 유효성 검사 실패 (잘못된 클라이언트의 요청) @ExceptionHandler(Exception400.class) public String ex400(Exception e) { return Script.back(e.getMessage()); } // 인증 실패 (클라이언트가 인증없이 요청했거나, 인증을 하거나 실패했거나) @ExceptionHandler(Exception401.class) public String ex401(Exception e) { return Script.back(e.getMessage()); } // 권한 실패 (인증은 되어 있는데, 삭제하려는 게시글이 내가 적은게 아니다) @ExceptionHandler(Exception403.class) public String ex403(Exception e) { return Script.back(e.getMessage()); } // 서버에서 리소스(자원) 찾을 수 없을때 @ExceptionHandler(Exception404.class) public String ex404(Exception e) { return Script.back(e.getMessage()); } // 서버에서 심각한 오류가 발생했을때 @ExceptionHandler(Exception500.class) public String ex500(Exception e) { return Script.back(e.getMessage()); } }
 
여기에 내가 못 알아챈 예외를 처리하기 위한 매서드도 하나 생성해야 함
 
notion image
 
// 서버에서 심각한 오류가 발생했을때 (모를 때) @ExceptionHandler(Exception.class) // 그냥 exception public String ex(Exception e) { return Script.back(e.getMessage()); }
 
내가 제어를 못한 에러는 다 ex 로 온다.
 
 
이렇게 두고 조금씩 수정하면 됨.
 
서버의 모든 에러를 노출시키면 나중에 해킹당함.
그러니 로그는 서버에만 저장하고 클라이언트에게 보여주는 msg에는
알수없는 오류가 발생했다고만 적어줌.
notion image
 
400은 body 데이터가 잘못 들어왔을때 날려주는거고
게시글 아이디를 찾을 수 없는건 클라이언트의 잘못이기 때문에 404임
→ Exception404로 수정
 
 

User 리팩토링 시작

 
notion image
 
서비스 레이어 생성 → UserService
 
++
UserController 가 BoardSerivce 를 의존하게 하지마라!
그 기능이 필요하면 그냥 UserController ( or UserService ? )안에 하나 똑같이 만들어서 써라.
이 컨트로러가 저 서비스 때렸다가 이 서비스 때리면 의존관계가 복잡해진다.
매서드가 중복되어도 되니까 자기 서비스말고 다른 서비스 넣지마라!!!!!
 
Service에서는 어떤 Repository를 의존해도 상관없다
서비스에 다른 서비스를 들고오지는 말기! 서비스는 하나의 기능이니까 그걸 다른곳에서 섞어쓰면 트랜젝션 관리가 안됨!
 
notion image
UserService에 회원가입 & 로그인 매서드 생성
 
notion image
UserRepository에 중복체크를 위한 findByUsername 매서드 생성
 
NoResultException 이 터질것임. ( → 어떤 에러가 터질 지 추측하면서 쓰기 ? )
 
repository에서 매서드를 만들었으니 Test로 가서 매서드 테스트하기!!
 

UserRepositoryTest

notion image
 
( 예상대로 ) NoResultException이 터짐
 
왜 ? haha라는 username이 db에 없으니까!
 
→ 회원가입을 위해서는 회원이 입력한 username이 db에 없어야 한다!
 
그래서 db에 없다는 사실을 return 해서 서비스의 비즈니스 로직에서 다루어야 한다.
 
그러기 위해서 NoResultException을 터지게 두지말고
 
Exception으로 잡아서 null을 리턴하도록 코드를 작성한다.
 
 
notion image
 
Repository 에서 NoResultException이 아닌 null을 리턴하도록 수정
 
null 은 서비스에서 로직 구현에 사용되는 소중한 데이터다. ^^
 
매서드 수정했으니 다시 테스트 하기.
 
테스트해보면
notion image
 
→ null이 리턴되도록 잘 설정된것을 확인할 수 있다.
 
 
UserService 에 가서 코드를 작성할 건데
 
++ 개념
 
어떤 책에서는
 
조건문 작성할 때
“부정으로 비교하지마라. == 으로 비교하라! (긍정으로 비교하라!)”
고 나옴. → ~가 아니면( ! = )’ 보다는 그게 잘 읽히니까.
 
if(oldUser == null){
repository.save
}else {
throw new Exception
}
 
위처럼 긍정으로 비교하고 예외는 else 에 쓰라고 하는데 이렇게 하지마라
 
notion image
 
그냥 위에서 우리가 작성한 것처럼
 
정상적인 코드는 if else에 넣지말고 조건문 밖에서 그냥 써라.
비정상적인 경우만 if else로 걸러내고 정상로직은 메인 코드에 그냥 써라.
 
위에서 보면 userRepository.save(joinDTO.toEntity()) 도 조건문 밖에 썼다.
 
이게 가독성이 더 좋다.
 
++
 
예외 터트리는 거
 
만약 repository에서 예외터트리지말고 throw 해서 service에서 터트리자고 약속했다면
( → 팀의 컨벤션 ) repository 에서 throw 하고 서비스에서 처리하면 된다.
 

 
계속해서 로그인 처리하자.
 
notion image
레파지토리에서 터트리도록 코드를 작성함
 
notion image
로그인 예외는 repository에서 처리했으니까 service는 return user 만 하면됨
 
user를 받아라. 컨트롤러야.
 
++
회원가입 로그인 작성중인데
 
패스워드 암호화하는 비즈니스 로직은 service에서 추후에 구현할 예정.
 
해시해서 뭐 넣는다고 함.
 
 
notion image
컨트롤러 의존관계 userRepository 지우고 userService 로 바꾸기
 
Controller는 Service 에 의존한다!
 
밑에 userRepository 사용해서 에러난거 쭉 리팩토링 하기
 
notion image
join 매서드를 이렇게 userService를 사용하도록 수정함
 
컨트롤러의 역할은 값 잘받고( 매개변수 ), 요청잘받고 ( 앤드포인트 ), 응답잘받는( return 부분) 것 !
 
notion image
login 매서드도 이렇게 수정함
 
 
테스트해보기.
 
notion image
없는 아이디로 로그인 시도하면 repository에서 throw한게 잘 터진 것을 확인할 수 있다.
 
 
notion image
이미 DB에 있는 아이디로 회원가입을 시도하면 service 에서 throw한게 성공적으로 터진걸 볼수있다.
 
 
++ 추가로
 
notion image
 
history.back을 한 이유는 사용자가 적은 걸 남겨놔야 하기 때문.
Script.href로 적어두면 화면 돌아가는데 다 지워져 있음! (ux가 안좋아짐)
 
 
notion image
service에 게시글 쓰기 생성
 
 
notion image
 
게시글 삭제 기능을 구현한다고 치면
 
이렇게 4가지 단계가 있다.
 
 
++
 
notion image
 
이런식으로 작성하지 말기
 
notion image
 
이렇게 하나씩 처리하라!
 
notion image
 
++
단위테스트를 배우면 서비스만 테스트 할 수 있다.
 
 
++
 
“”” 을 에러메시지 코드에 사용했는데,
 
자바스크립트 문법인줄 알았는데 자바 문법이었다.
 
💡
JavaScript에서 """을 사용하는 문법은 없습니다. 아마도 혼동하셨을 가능성이 있습니다.
JavaScript에서 문자열을 작성하는 방법은 크게 세 가지입니다:
  1. 따옴표 (' '): 작은 따옴표로 문자열을 감싸는 방법입니다.
  1. 큰 따옴표 (" "): 큰 따옴표로 문자열을 감싸는 방법입니다.
  1. 백틱 (` `): 템플릿 리터럴(Template Literals)을 작성할 때 사용됩니다. 이를 사용하면 여러 줄에 걸쳐 문자열을 작성하거나, 문자열 내에서 변수나 표현식을 삽입할 수 있습니다.
예를 들어:
javascript코드 복사 let singleQuote = 'This is a string'; let doubleQuote = "This is also a string"; let templateLiteral = `This is a string with a variable: ${variable}`;
아마도 백틱(`)을 사용한 템플릿 리터럴을 의미하셨을 가능성이 큽니다. 이 문법은 ES6(ECMAScript 2015)부터 도입된 최신 문법이며, 여러 줄의 문자열을 작성하거나 문자열 내에서 변수나 표현식을 쉽게 삽입할 수 있게 해줍니다.
javascript코드 복사 let multiLineString = ` This is a multi-line string. `; let name = "John"; let greeting = `Hello, ${name}!`; // 템플릿 리터럴에서 변수를 삽입
정리하자면, JavaScript에서 """은 사용하지 않으며, 여러 줄 문자열과 변수를 삽입하는 기능은 **백틱(``)**을 사용한 템플릿 리터럴로 구현됩니다.
 
자바스크립트 문법아니라고 해서 아래코드에 대해서 물어보았더니
 
public static String href(String msg, String url) { String errMsg = """ <script> alert('$msg'); location.href = '$url'; </script> """.replace("$msg", msg) .replace("$url", url); return errMsg; // 자바스크립트 문자를 만들어서 리턴하는 것 }
 
💡
이 코드에서 사용된 """Java텍스트 블록(Text Blocks) 기능을 의미합니다. 이 기능은 Java 13부터 도입된 기능으로, 여러 줄 문자열을 더 쉽게 작성할 수 있도록 도와줍니다. JavaScript가 아닌 Java에서 여러 줄 문자열을 정의할 때 """를 사용합니다.

코드 설명

java코드 복사 public static String href(String msg, String url) { String errMsg = """ <script> alert('$msg'); location.href = '$url'; </script> """.replace("$msg", msg) .replace("$url", url); return errMsg; }
이 함수는 두 개의 파라미터 msgurl을 받아서, JavaScript 코드로 변환해 반환합니다. 여기서 """을 사용한 텍스트 블록은 HTML과 JavaScript를 포함한 여러 줄 문자열을 정의하는 데 사용됩니다.
  • 텍스트 블록: """을 사용하여 여러 줄에 걸친 문자열을 더 간편하게 작성할 수 있습니다.
  • replace 메서드: $msg$url 자리표시자(placeholder)를 실제 전달된 msgurl 값으로 대체합니다.
  • Java 13 이상의 기능: 이 문법은 Java 13부터 도입된 기능입니다.

예시

java코드 복사 href("Invalid input", "https://example.com");
위와 같은 호출이 있으면, 이 함수는 다음과 같은 JavaScript 문자열을 반환합니다:
html코드 복사 <script> alert('Invalid input'); location.href = 'https://example.com'; </script>
이 함수는 주로 서버 측에서 클라이언트에게 스크립트 태그를 포함한 응답을 보내기 위해 사용됩니다. JavaScript 코드를 문자열로 생성해 클라이언트에게 전달하는 방식입니다.

정리

이 코드에서 사용된 """은 Java의 텍스트 블록 기능이며, 이를 통해 HTML/JavaScript 코드를 포함하는 여러 줄 문자열을 쉽게 작성할 수 있습니다.
 
위의 문법이 나오기 전에는 StringBuilder 를 사용했다.
 
notion image
notion image
notion image
notion image
 
 
 
++
디스패쳐 서블릿이 throw 터트린걸 잡아서 try catch로 잡아서 처리한다.
 
GlobalExceptionHandler를 사용할거면 @restControlleradvice를 붙이고
매서드에 @exceptionHandler 를 붙여서 사용하라고 스프링 문서에 나와있음.
 
모든 try catch의 원리는
“터진걸 터진곳에서 관리하는게 힘들어서 throw를 터트려서 제일 앞에 있는거 한테 처리하도록 위임” 하는것.
 
이런건 사용법을 찾아서 먼저 익히고 나중에 자바 개념으로 이해하기.
 
 
삭제하기는
notion image
 
이렇게 작성할 수 있다.
 
notion image
 
이제 컨트롤러를 작성할건데 필요없는 레파지토리 의존 삭제하기.
 
그리고
 
notion image
게시글 저장하는 매서드 위와같이 수정.
 
인증과 관련된 예외는 401로 보낸다.
 
notion image
익셉션 핸들러로 가서 401 매서드 return 부분에
back 이 아닌 href 로 수정 하고 매개변수로 msg 와 url 을 넣어준다.
 
  • 401 은 이전 페이지로 back 보다 로그인하라고 보내주는게 낫다.
  • 그리고 e.getMesasge 해서 에러메세지를 그대로 보여주지 말고,
    • 인증되지 않았습니다! 와 같이 msg 작성하기. 안그럼 해킹당한다.
 
++
원래 로그인하면 로그를 남기는데
로그를 남기는 코드도 나중에 여기에 추가하는게 좋다 (나중에 해볼것)
 
notion image
컨트롤러 delete 이렇게 작성함
 
notion image
컨트롤러 게시글 목록보기 매서드도 리팩토링
 
 
notion image
서비스에서 게시글목록보기 만들어주기
 
 
 

BoardCotroller ( 리팩토링 ~ ing)

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.RequestParam; import shop.mtcoding.blog.core.error.ex.Exception401; import shop.mtcoding.blog.user.User; import java.util.List; // 책임 : 식별자 요청 받기 & 응답하기 // 컨트롤러는 REPOSITORY에 의존하고 있다. (Repository가 없으면 일을 못하기 때문에) @RequiredArgsConstructor @Controller // 식별자 요청을 받을 수 있다. public class BoardController { /* private final BoardRepository boardRepository; public BoardController(BoardRepository boardRepository) { System.out.println("BoardController 생성자"); this.boardRepository = boardRepository; } */ private final HttpSession session; private final BoardService boardService; // 다고치고 boardRepository 지우기 // url : http://localhost:8080/board/1/update // body : title=제목1변경&content=내용1변경 // content-type : x-www-form-urlencoded // 버퍼 -> 보조스트림 // 바구니에 담아서 줌. 10개를 한꺼번에 주면 받는사람은 하나씩 꺼내쓰면되고 주는사람은 기다리지않고 다른일할수있다. // 서로 통신하면 내 버퍼(Write buffer), 상대방 버퍼 (read buffer) -- bufferedWrite(->자바에서 편하게 쓰려고 prinwrite를 씀), bufferedRead // 수정하기 버튼을 클릭하면 브라우저의 application 버퍼에 먼저 쌓인다. // 스프링의 버퍼 (readBuffer) // request 로 버퍼에 접근할 수 있다. @PostMapping("/board/{id}/update") public String update(@PathVariable("id") int id, @RequestParam("title") String title, @RequestParam("content") String content) { //인증체크하고 업데이트 해라 //boardRepository.updateById(title, content, id); return "redirect:/board/" + id; //return "/board/detail" 하면 안됨 -> view에서 model.id 뿌려줘야하니까. 아까 request 객체에 담아서 보낸건 이미 사라짐ㄴ } @PostMapping("/board/{id}/delete") public String delete(@PathVariable("id") int id) { User sessionUser = (User) session.getAttribute("sessionUser"); // 인증 체크 if (sessionUser == null) { throw new Exception401("로그인이 필요합니다"); // 나중에 이 매서드 프록시 안에 넣을 예정 ! 그럼 이 코드 작성 안해도 되니 편리함 } boardService.게시글삭제(id, sessionUser); return "redirect:/board"; } @PostMapping("/board/save") public String save(BoardRequest.SaveDTO saveDTO) { // 스프링 기본전략 x-www-encoded User sessionUser = (User) session.getAttribute("sessionUser"); // session에 있는건 Object 여서 User 로 다운캐스팅 해주어야 한다. // 인증 체크 필요함. 안하면 누가 강제로 들어와서 글을 작성할 수 있다. User_id 에 null 이 들어가지 못하도록 설정해야함 if (sessionUser == null) { throw new Exception401("로그인이 필요합니다"); // 로그인 페이지로 리다이렉트 하지말고, 무조건 exception으로 터트리기! } boardService.게시글쓰기(saveDTO, sessionUser); return "redirect:/board"; //redirect는 다른 컨트롤러를 때리는 것 } // get, post, put, delete // insert, delete, update 할때 일단 지금은 post 를 쓰자 @GetMapping("/board") // 식별자 요청 public String list(HttpServletRequest request) { //System.out.println(request.getRemoteAddr()); // 내가 내껄 때려서 정확한 ip 주소가 안뜸 0 0 0 0 0 이렇게 뜨고 //System.out.println(request.getRequestURI()); List<Board> boardList = boardService.게시글목록보기(); // request 객체 저장 request.setAttribute("models", boardList); HttpSession session = request.getSession(); session.setAttribute("num", 1); return "board/list"; // return의 위치가 templates 폴더로 고정되어 있다. / 가 없어도 바로 templates 폴더로 연결된다. // 확장자도 mustache 로 자동설정 되어있기 때문에 파일명만 적어주면 거기로 리턴한다. } // 1. 매서드 : Get // 2. 주소 : /board/1 // 3. 응답 : board/detail @GetMapping("/board/{id}") public String detail(@PathVariable("id") Integer id, HttpServletRequest request) { /* Board board = boardRepository.findById(id); request.setAttribute("model", board); // 1건 이니까 그냥 model로 적어주기! request.setAttribute("isOwner", false); */ User sessionUser = (User) session.getAttribute("sessionUser"); BoardReponse.DetailDTO detailDTO = boardService.상세보기(id, sessionUser); request.setAttribute("model", detailDTO); return "board/detail"; } // 1. 매서드 : Get // 2. 주소 : /board/save-form // 3. 응답 : board/save-form @GetMapping("/board/save-form") public String saveForm() { return "board/save-form"; } // 1. 매서드 : Get // 2. 주소 : /board/1/update-form // 3. 응답 : board/update-form @GetMapping("/board/{id}/update-form") public String updateForm(@PathVariable("id") int id, HttpServletRequest request) { // PathVariable은 정규표현식으로 처리하는걸 해줌 -> /board/1 이든 /board/2 이든 다 받아줌 // 주석처리한거 나중에 만들쟈 //Board board = boardRepository.findById(id); // 만약 여기서 못찾으면 exception이 터짐 //request.setAttribute("model", board); // request 객체(model)에 담음 return "board/update-form"; } }
 

BoardService (리팩토링 ~ing)

package shop.mtcoding.blog.board; // C -> S -> R import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog.core.error.ex.Exception403; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @Service public class BoardService { // DI 생성자 주입 private final BoardRepository boardRepository; // 수정하기 // 게시글 목록보기 해보기 // v3 페이징이랑 댓글쓰기, 검색 배울예정 // 파이어베이스, 몽고디비, 웹소켓 public List<Board> 게시글목록보기() { // 나중에 페이징 처리하는거 여기 추가할 예정 List<Board> boardList = boardRepository.findAll(); return boardList; } @Transactional public void 게시글삭제(int id, User sessionUser) { // 1. 컨트롤러에서 게시글 id 받기 & 권한체크에 필요한 sessionUser 도 받기 // 2. 게시글 존재 여부 확인 (404) Board board = boardRepository.findById(id); // 3. 권한 체크 (내가 쓴 글인지 확인하기. 403(권한없음)) if (board.getUser().getId() != sessionUser.getId()) { throw new Exception403("권한이 없습니다."); } // 4. 게시글 삭제 boardRepository.deleteById(id); } @Transactional public void 게시글쓰기(BoardRequest.SaveDTO saveDTO, User sessionUser) { // 누가 적었는지 알아야하니까 session이 필요 boardRepository.save(saveDTO.toEntity(sessionUser)); // sessionUser 인증체크는 컨트롤러에서 함 // 그래서 여기 매개변수로 null이 들어올 경우는 없음 // sessionUser == null 이런조건은 컨트롤러에서 } // 기능명은 한글로 적는다. // 매서드는 행위 -> 동사 public BoardReponse.DetailDTO 상세보기(int id, User sessionUser) { // 매개변수는 컨트롤러로부터 받는다. // final Httpsession으로 받으면 session.getAttriute해서 user로 다운캐스팅 해야해서 귀찮아지니까 이렇게 안하고 // 세션 인증 체크는 컨트롤러에서 할거고 지금 서비스에서는 권한 체크(Board의 user랑 session의 user가 일치하는지) // 를 할거니까 sessionuSER는 매개변수로 받아도 충분 Board board = boardRepository.findById(id); // 조인한 쿼리 쓰니까 (Board - User) 둘다 있음 // 여기 null 처리 할 필요가 없음. findById 에서 null 이 들어오면 throw로 던지니까 서비스에서 board 값으로 null을 받을 일이 없음 return new BoardReponse.DetailDTO(board, sessionUser); // board 와 boolean 2개 리턴 불가능 해 dto 만들기 } }
 
 
오늘은 여기까지하고
 
남은건 board수정 , 게시글 목록보기 리팩토링 !
 
++
지금까지 작성한거 압축 파일
 
 
실력 . 열정 . 꾸준함
 
 
Share article

keepgoing