Spring 숙련 주차 개인 과제 LV2
숙련 LV2 과제까지 구현한 github repository
🏁 Goal: "회원가입, 로그인, 댓글 작성/조회/수정/삭제 기능이 추가된 나만의 항해 블로그 백엔드 서버 만들기"
추가적으로 구현할 과제 명세
1. 회원 가입 API
- username, password를 Client에서 전달받기
- username은
최소 4자 이상, 10자 이하이며 알파벳 소문자(a~z), 숫자(0~9)
로 구성되어야 한다.
- password는
최소 8자 이상, 15자 이하이며 알파벳 대소문자(a~z, A~Z), 숫자(0~9), 특수문자
로 구성되어야 한다.
- DB에 중복된 username이 없다면 회원을 저장하고 Client 로 성공했다는 메시지, 상태코드 반환하기
- 회원 권한 부여하기 (ADMIN, USER) - ADMIN 회원은 모든 게시글, 댓글 수정 / 삭제 가능
- 참고자료
- https://mangkyu.tistory.com/174
- https://ko.wikipedia.org/wiki/정규_표현식
- https://bamdule.tistory.com/35
2. 로그인 API
- username, password를 Client에서 전달받기
- DB에서 username을 사용하여 저장된 회원의 유무를 확인하고 있다면 password 비교하기
- 로그인 성공 시, 로그인에 성공한 유저의 정보와 JWT를 활용하여 토큰을 발급하고,
발급한 토큰을 Header에 추가하고 성공했다는 메시지, 상태코드 와 함께 Client에 반환하기
3. 댓글 작성 API
- 토큰을 검사하여, 유효한 토큰일 경우에만 댓글 작성 가능
- 선택한 게시글의 DB 저장 유무를 확인하기
- 선택한 게시글이 있다면 댓글을 등록하고 등록된 댓글 반환하기
4. 댓글 수정 API
- 토큰을 검사한 후, 유효한 토큰이면서 해당 사용자가 작성한 댓글만 수정 가능
- 선택한 댓글의 DB 저장 유무를 확인하기
- 선택한 댓글이 있다면 댓글 수정하고 수정된 댓글 반환하기
5. 댓글 삭제 API
- 토큰을 검사한 후, 유효한 토큰이면서 해당 사용자가 작성한 댓글만 삭제 가능
- 선택한 댓글의 DB 저장 유무를 확인하기
- 선택한 댓글이 있다면 댓글 삭제하고 Client 로 성공했다는 메시지, 상태코드 반환하기
6. 예외 처리
- 토큰이 필요한 API 요청에서 토큰을 전달하지 않았거나 정상 토큰이 아닐 때는 "토큰이 유효하지 않습니다." 라는 에러메시지와 statusCode: 400을 Client에 반환하기
- 토큰이 있고, 유효한 토큰이지만 해당 사용자가 작성한 게시글/댓글이 아닌 경우에는 “작성자만 삭제/수정할 수 있습니다.”라는 에러메시지와 statusCode: 400을 Client에 반환하기
- DB에 이미 존재하는 username으로 회원가입을 요청한 경우 "중복된 username 입니다." 라는 에러메시지와 statusCode: 400을 Client에 반환하기
- 로그인 시, 전달된 username과 password 중 맞지 않는 정보가 있다면 "회원을 찾을 수 없습니다."라는 에러메시지와 statusCode: 400을 Client에 반환하기
구현된 코드를 요구사항에 맞게 수정할 사항들
1. 전체 게시글 목록 조회 API
- 제목, 작성자명(username), 작성 내용, 작성 날짜를 조회하기
- 작성 날짜 기준 내림차순으로 정렬하기
- 각각의 게시글에 등록된 모든 댓글을 게시글과 같이 Client에 반환하기
- 댓글은 작성 날짜 기준 내림차순으로 정렬하기
2. 게시글 작성 API
- 토큰을 검사하여, 유효한 토큰일 경우에만 게시글 작성 가능
- 제목, 작성자명(username), 작성 내용을 저장하고
- 저장된 게시글을 Client 로 반환하기
3. 선택한 게시글 조회 API
- 선택한 게시글의 제목, 작성자명(username), 작성 날짜, 작성 내용을 조회하기
(검색 기능이 아닙니다. 간단한 게시글 조회만 구현해주세요.)
- 선택한 게시글에 등록된 모든 댓글을 선택한 게시글과 같이 Client에 반환하기
- 댓글은 작성 날짜 기준 내림차순으로 정렬하기
4. 선택한 게시글 수정 API
수정을 요청할 때 수정할 데이터와 비밀번호를 같이 보내서 서버에서 비밀번호 일치 여부를 확인 한 후
- 토큰을 검사한 후, 유효한 토큰이면서 해당 사용자가 작성한 게시글만 수정 가능
- 제목, 작성 내용을 수정하고 수정된 게시글을 Client 로 반환하기
5. 선택한 게시글 삭제 API
삭제를 요청할 때 비밀번호를 같이 보내서 서버에서 비밀번호 일치 여부를 확인 한 후
- 토큰을 검사한 후, 유효한 토큰이면서 해당 사용자가 작성한 게시글만 삭제 가능
- 선택한 게시글을 삭제하고 Client 로 성공했다는 메시지, 상태코드 반환하기
코드리뷰 by 튜터
- 전반적인 코드리뷰보단 교육생들이 막혔던 포인트, 추가적으로 짚고 넘어가면 좋을 포인트 위주로 리뷰가 진행되었다는 점을 고지하고 시작합니다.
1. Restful
- url에는 명사(복수형)을 넣어주세요. 동사는 Restful하지 않습니다.
- 백엔드 입장에서는 API가 다른 팀들과 소통하는 창구입니다. (우리 팀의 얼굴이므로 예쁘게 만드는게 좋습니다.)
- 댓글controller의 url은 "/posts/{postId}/comments"가 Restful합니다.
2. JWT 인증관련
-
항상 '필요한 것들만 넘겨야한다. 객체를 그대로 넘기면 안된다.' 그러한 맥락에서 우리가 Dto를 사용하는 것이다.
-
JWT 인증 관련하여 코드들이 중복된다. 이에 대한 해결책으로 또 다른 서비스(인증을 전담해주는 JwtService)를 만드는 것이 있다.
-
꼭 필요한 값들만 넘겨줘야 의존성을 낮출 수 있다.(변경사항 발생시 부가적으로 따라오는 리소스들을 절약할 수 있다.)
-
컨트롤러 단의 JWT 토큰을 또 다른 DTO에 담아서 서비스단에는 필요한 정보만 넘겨라(DTO를 통해서 결합도를 낮춰라)
-
JWT를 사용하는 이유는 DB access를 안해도 되기 때문이다.(DB access는 굉장히 리소스가 큰 작업)
(userRole은 이미 JWT에 담겨있으므로 JWT에서 꺼내 쓰면 된다.)
(userDB에서 user.userRole을 꺼내오면 JWT를 쓰는 보람이 없다.)
-
JWT에서 userRole을 꺼내는 방법
UserRoleEnum role = UserRoleEnum.valueOf(claims.get("auth").toString())
3. Util 관련
- Util은 static을 붙여서 사용한다.
- 사실상 jwtUtil은 @Component를 붙여서 사용한다는 점에서 사실상 Service라고 보는 것이 적절하다.
4. 테이블관계
-
JPA는 우리가 작성한 코드를 기준으로 DB의 구조를 유추하는 것이다. (명확하게 DB구조를 알지는 못한다.)
-
annotation을 통해서 우리가 '적어줘야' JPA가 DB구조를 올바르게 유추할 수 있다.
(그래서 @ManyToOne, @JoinColumn과 같은 것들로 연결고리를 알려줌으로써 JPA에게 DB구조 가이드를 해준다.)
-
게시글에 User객체를 넣어주는 이유에는 User에 만들어둔 기능(method)를 재사용하기 위한 측면도 있다.
-
그러나 게시글에 들어가는 username은 사실 Writer로서 들어가는 것이다.
(그렇다는건 User라는 객체자체는 게시물의 관심사가 아니다. -> 굳이 User객체를 넣어줄 필요 없이 username만 Writer라는 변수에 넣어주면 된다.)
-
User 객체를 넣어주면 실제로 쓰지도 않는 user.password같은 값들도 다 가지게 된다.
-> 필요 이상으로 정보를 가진다(역할 이상의 책임을 가지게 되어버린다)
-
객체 간의 연관관계를 맺을 때는 '이게 정말 필요한가?'라는 생각을 꼭 해보고 신중하게! 해야한다.
5. FetchType.EAGER
- N+1 문제가 발생한다. (getAllPost 해버리면 전체 게시물 사이즈만큼 쿼리를 날려버린다.)
- FetchType.LAZY로 할 경우 실제 User를 쓰기 전까지 쿼리가 날아가지 않아서 DB access를 최소화&최적화 할 수 있다.
6. 순환참조가 왜 생겼는지 원인설명
- 잭슨 때문이다.(직렬화, 역직렬화 과정에서 잭슨이 getter를 사용하면서 발생한다.)
- List는 게시글을 get하고, 게시글은 또 그 안에서 List를 get하고, List는 게시글을 get하고...... 무한반복(순환참조)
- CommentResponseDto를 사용한다면? 만나지 않을 수 있다.
순환참조 핵심정리: DTO 꼭 쓰세요 두번쓰세요
7. 컨트롤러에서 url로 ADMIN/USER를 분리하자
- 서비스단에서 userRole에 따라 ifelse로 구분해서 다른 로직을 실행하는 방식은 '인가'를 서비스단에서 처리하는 것이다.' (인증/인가는 컨트롤러단에서 끝내자)