What to do?
API 작성하기
- 회원가입
- 로그인
- 포스팅 조회/작성/수정/삭제
- 댓글 조회/작성/수정/삭제
에러처리
- 에러코드 정의
- HttpStatus와 에러메세지를 담고 잇는 에러코드 정의
@Getter
@AllArgsConstructor
public enum CustomErrorCode {
// duplicated
DUPLICATED_USERNAME(HttpStatus.CONFLICT, "User is duplicated..."),
DUPLICATED_NICKNAME(HttpStatus.CONFLICT, "Nickname is duplicated..."),
DUPLICATED_EMAIL(HttpStatus.CONFLICT, "Email is duplicated..."),
// not found
USERNAME_NOT_FOUND(HttpStatus.NOT_FOUND, "Username is not found..."),
POST_NOT_FOUND(HttpStatus.NOT_FOUND, "Post is not found..."),
COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "Comment is not found..."),
NOT_LIKED(HttpStatus.NOT_FOUND, "Not liked..."),
// auth failure
INVALID_PASSWORD(HttpStatus.FORBIDDEN, "Password is wrong..."),
NOT_GRANTED_ACCESS(HttpStatus.FORBIDDEN, "Access denied due to grant..."),
INVALID_TOKEN(HttpStatus.FORBIDDEN, "Token is invalid..."),
// conflict
ALREADY_LIKED(HttpStatus.CONFLICT, "User Already like post"),
// internal server error
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error...")
;
private final HttpStatus status;
private final String message;
}
- 예외처리
- Usage
- throw CustomException.of(에러코드);
- throw CustomException.of(에러코드, 에러메세지);
- Usage
@Getter
@Setter
public class CustomException extends RuntimeException {
private CustomErrorCode code;
private String message;
private CustomException(CustomErrorCode code) {
this.code = code;
this.message = null;
}
private CustomException(CustomErrorCode code, String message) {
this.code = code;
this.message = message;
}
protected CustomException(){}
public static CustomException of(CustomErrorCode code){
return new CustomException(code);
}
public static CustomException of(CustomErrorCode code, String message){
return new CustomException(code, message);
}
}
- GlobalControllerAdvice
- CustomException 발생시 CustomErrorCode의 status 반환
- RunTimeException발생시 INTERNAL_SERVER_ERROR 반환
@Slf4j
@RestControllerAdvice
public class GlobalControllerAdvice {
/**
* @param error
* CustomException에서 정의된 에러
* @return
* body에 CustomErrorCode 이름
*/
@ExceptionHandler(CustomException.class)
public ResponseEntity<?> applicationHandler(CustomException error){
log.error("Error occurs... {}", error.toString());
return ResponseEntity.status(error.getCode().getStatus())
.body(CustomResponse.error(error.getCode().name()));
}
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<?> applicationHandler(RuntimeException error){
log.error("Error occurs... {}", error.toString());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(CustomResponse.error(CustomErrorCode.INTERNAL_SERVER_ERROR.name()));
}
}
Repository
- 유저 Repository
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
Optional<UserEntity> findByUsername(String username);
Optional<UserEntity> findByEmail(String email);
Optional<UserEntity> findByNickname(String nickname);
}
- 포스팅 Repository
@Repository
public interface PostRepository extends JpaRepository<PostEntity, Long> {
Page<PostEntity> findAll(Pageable pageable);
Page<PostEntity> findAllByUser(UserEntity userEntity, Pageable pageable);
}
- 댓글 Repository
@Repository
public interface CommentRepository extends JpaRepository<CommentEntity, Long> {
Page<CommentEntity> findAllByPost(PostEntity post, Pageable pageable);
}
Service 코드
- UserService
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Value("${jwt.secret-key}") private String secretKey;
@Value("${jwt.duration}") private Long duration;
/**
* 회원가입
* @param email 이메일
* @param username 유저명
* @param nickname 닉네임
* @param password 비밀번호
* @return 회원가입 성공시 UserDto
*/
@Transactional
public UserDto register(String email, String username, String nickname, String password){
// 중복체크
checkDuplicated(_field.EMAIL, email);
checkDuplicated(_field.NICKNAME, nickname);
checkDuplicated(_field.USERNAME, username);
// 비밀번호 인코딩
String encodedPassword = bCryptPasswordEncoder.encode(password);
return UserEntity.dto(userRepository.save(UserEntity.of(email, username, nickname, encodedPassword, RoleType.USER, null, null)));
}
/**
* 로그인
* @param username 유저명
* @param password 비밀번호
* @return JWT 토큰값
*/
@Transactional(readOnly = true)
public String login(String username, String password){
// 존재하는 회원여부
UserEntity user = findByUsernameOrElseThrow(username);
// 비밀번호 일치여부 확인
if (!bCryptPasswordEncoder.matches(password, user.getPassword())){
throw CustomException.of(CustomErrorCode.INVALID_PASSWORD);
}
return JwtUtil.generateToken(username, secretKey, duration);
}
/**
* 중복체크
* @param f 중복체크할 필드
* @param value 중복체크할 값
*/
private void checkDuplicated(_field f, String value){
switch (f){
case EMAIL -> {
// 중복체크 - 유저명, 닉네임, 이메일
userRepository.findByUsername(value).ifPresent(it->{
throw CustomException.of(
CustomErrorCode.DUPLICATED_USERNAME,
String.format("Username [%s] is duplicated...", value)
);
});
}
case USERNAME -> {
userRepository.findByEmail(value).ifPresent(it->{
throw CustomException.of(
CustomErrorCode.DUPLICATED_USERNAME,
String.format("Email [%s] is duplicated...", value)
);
});
}
case NICKNAME -> {
userRepository.findByNickname(value).ifPresent(it->{
throw CustomException.of(
CustomErrorCode.DUPLICATED_NICKNAME,
String.format("Nickname [%s] is duplicated...", value)
);
});
}
}
}
/**
* 중복체크할 필드
*/
private enum _field{
USERNAME, NICKNAME, EMAIL;
}
@Transactional(readOnly = true)
public UserEntity findByUsernameOrElseThrow(String username){
return userRepository.findByUsername(username)
.orElseThrow(()->{throw CustomException.of(CustomErrorCode.USERNAME_NOT_FOUND);});
}
}
- PostService
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1")
public class PostController {
private final PostService postService;
// 포스팅 단건조회
@GetMapping("/post/{postId}")
public CustomResponse<PostDto> getPost(@PathVariable Long postId){
return CustomResponse.success(postService.getPost(postId));
}
// 포스팅 페이지 조회
@GetMapping("/post")
public CustomResponse<Page<PostDto>> getPostsByUser(@PageableDefault Pageable pageable, @RequestParam("username") String username){
if (username==null){
return CustomResponse.success(postService.getPosts(pageable));
}
return CustomResponse.success(postService.getPostsByUser(pageable, username));
}
// 포스팅 작성
@PostMapping("/post")
public CustomResponse<Long> createPost(@RequestBody CreatePostRequest req, Authentication authentication){
return CustomResponse.success(postService.createPost(req.getTitle(), req.getContent(), authentication.getName()));
}
// 포스팅 수정
@PutMapping("/post/{postId}")
public CustomResponse<Long> modifyPost(@PathVariable Long postId, @RequestBody ModifyPostRequest req, Authentication authentication){
return CustomResponse.success(postService.modifyPost(postId, req.getTitle(), req.getContent(), authentication.getName()));
}
// 포스팅 삭제
@DeleteMapping("/post/{postId}")
public CustomResponse<Long> deletePost(@PathVariable Long postId, Authentication authentication){
return CustomResponse.success(postService.deletePost(postId, authentication.getName()));
}
// 댓글 조회
@GetMapping("/comment/{postId}")
public CustomResponse<Page<CommentDto>> getComment(@PathVariable Long postId, @PageableDefault Pageable pageable){
return CustomResponse.success(postService.getComments(postId, pageable));
}
// 댓글 작성
@PostMapping("/comment")
public CustomResponse<CommentDto> createPost(@RequestBody CreateCommentRequest req, Authentication authentication){
return CustomResponse.success(postService.createComment(req.getPostId(), req.getContent(), authentication.getName()));
}
// 댓글 수정
@PutMapping("/comment")
public CustomResponse<CommentDto> modifyPost(@RequestBody ModifyCommentRequest req, Authentication authentication){
return CustomResponse.success(postService.modifyComment(req.getPostId(), req.getCommentId(), req.getContent(), authentication.getName()));
}
// 댓글 삭제
@DeleteMapping("/comment")
public CustomResponse<Void> deletePost(@RequestBody DeleteCommentRequest req, Authentication authentication){
postService.deleteComment(req.getPostId(), req.getCommentId(), authentication.getName());
return CustomResponse.success();
}
// 좋아요 & 싫어요 개수 가져오기
@GetMapping("/like/{postId}")
public CustomResponse<GetLikeResponse> getLikeCount(@PathVariable Long postId){
return CustomResponse.success(GetLikeResponse.from(postId, postService.getLikeCount(postId)));
}
// 좋아요 & 싫어요 요청
@PostMapping("/like")
public CustomResponse<Void> likePost(@RequestBody LikePostRequest req, Authentication authentication){
postService.likePost(req.getPostId(), req.getLikeType(), authentication.getName());
return CustomResponse.success();
}
}
Controller
- Controller 응답
- 성공시
- return CustomResponse.success();
- return CustomResponse.success(result);
- 성공시
@Getter
@Setter
public class CustomResponse<T> {
private String statusCode;
private T result;
private CustomResponse(String statusCode, T result) {
this.statusCode = statusCode;
this.result = result;
}
protected CustomResponse(){}
public static <T> CustomResponse<T> success() {
return new CustomResponse<T>("SUCCESS", null);
}
public static <T> CustomResponse<T> success(T result) {
return new CustomResponse<T>("SUCCESS", result);
}
public static CustomResponse<Void> error(String resultCode) {
return new CustomResponse<Void>(resultCode, null);
}
public static String json(CustomResponse res){
if (res.result == null) {
return "{" +
"\"resultCode\":" + "\"" + res.statusCode + "\"," +
"\"result\":" + null + "}";
}
return "{" +
"\"resultCode\":" + "\"" + res.statusCode + "\"," +
"\"result\":" + "\"" + res.result.toString() + "\"" + "}";
}
}
- UserController
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/user")
public class UserController {
private final UserService userService;
/**
* 회원가입
* @Param - username nickname email password
* @Return - nickname
*/
@PostMapping("/register")
public CustomResponse<String> register(@RequestBody RegisterRequest req){
return CustomResponse.success(userService.register(req.getEmail(), req.getUsername(), req.getNickname(), req.getPassword()).getNickname());
}
/**
* 로그인
* @Param - username password
* @Return - Authorization token (JWT)
*/
@PostMapping("/login")
public CustomResponse<String> login(@RequestBody LoginRequest req){
return CustomResponse.success(userService.login(req.getUsername(), req.getPassword()));
}
}
- PostController
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1")
public class PostController {
private final PostService postService;
// 포스팅 단건조회
@GetMapping("/post/{postId}")
public CustomResponse<PostDto> getPost(@PathVariable Long postId){
return CustomResponse.success(postService.getPost(postId));
}
// 포스팅 페이지 조회
@GetMapping("/post")
public CustomResponse<Page<PostDto>> getPosts(@PageableDefault Pageable pageable){
return CustomResponse.success(postService.getPosts(pageable));
}
// 포스팅 페이지 조회 by 유저
@GetMapping("/post")
public CustomResponse<Page<PostDto>> getPostsByUser(@PageableDefault Pageable pageable, @RequestParam("username") String username){
return CustomResponse.success(postService.getPostsByUser(pageable, username));
}
// 포스팅 작성
@PostMapping("/post")
public CustomResponse<Long> createPost(@RequestBody CreatePostRequest req, Authentication authentication){
return CustomResponse.success(postService.createPost(req.getTitle(), req.getContent(), authentication.getName()));
}
// 포스팅 수정
@PutMapping("/post/{postId}")
public CustomResponse<Long> modifyPost(@PathVariable Long postId, @RequestBody ModifyPostRequest req, Authentication authentication){
return CustomResponse.success(postService.modifyPost(postId, req.getTitle(), req.getContent(), authentication.getName()));
}
// 포스팅 삭제
@DeleteMapping("/post/{postId}")
public CustomResponse<Long> deletePost(@PathVariable Long postId, Authentication authentication){
return CustomResponse.success(postService.deletePost(postId, authentication.getName()));
}
// 댓글 조회
@GetMapping("/comment/{postId}")
public CustomResponse<Page<CommentDto>> getComment(@PathVariable Long postId, @PageableDefault Pageable pageable){
return CustomResponse.success(postService.getComments(postId, pageable));
}
// 댓글 작성
@PostMapping("/comment")
public CustomResponse<CommentDto> createPost(@RequestBody CreateCommentRequest req, Authentication authentication){
return CustomResponse.success(postService.createComment(req.getPostId(), req.getContent(), authentication.getName()));
}
// 댓글 수정
@PutMapping("/comment")
public CustomResponse<CommentDto> modifyPost(@RequestBody ModifyCommentRequest req, Authentication authentication){
return CustomResponse.success(postService.modifyComment(req.getPostId(), req.getCommentId(), req.getContent(), authentication.getName()));
}
// 댓글 삭제
@DeleteMapping("/comment")
public CustomResponse<Void> deletePost(@RequestBody DeleteCommentRequest req, Authentication authentication){
postService.deleteComment(req.getPostId(), req.getCommentId(), authentication.getName());
return CustomResponse.success();
}
}
'Java > Spring' 카테고리의 다른 글
[Spring] 간단한 SNS 만들기 #5 (0) | 2023.01.15 |
---|---|
[Spring] 간단한 SNS 만들기 #4 (3) | 2023.01.01 |
[Spring] 간단한 SNS 만들기 #2 (0) | 2022.12.22 |
[Spring] 간단한 SNS 만들기 #1 (0) | 2022.12.22 |
[Spring] 길찾기 서비스 #6 (0) | 2022.12.11 |