안녕하세요. 오늘부터는 스프링 부트를 활용하여 블로그 API를 만들어 보도록 하겠습니다.
개발 환경은 아래와 같습니다.
- 스프링부트 2.4.5
- spring-boot-starter-web
- spring-data-jap, h2, mysql
- validation, lombok
- junit5
1. application.yml 파일에 database 정보 입력하기
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/board-rest?autoReconnect=true&useUnicode=true&serverTimezone=UTC&characterEncoding=UTF8
username: root
password: ghd9413
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
show_sql: true
format_sql: true
use_sql_comments: true
jackson:
property-naming-strategy: SNAKE_CASE
저는 mysql로 진행하였습니다.
property-naming-strategy는 기본값이 CamelCase인데 저는 SnakeCase를 선호하여 변경하였습니다.
2. 게시물 가져오기 API - Controller, Service, Repository, Entity, PostDTO
- controller
@RestController
@RequiredArgsConstructor
public class PostController {
private final PostService postService;
@GetMapping("post/{id}")
@ResponseStatus(value = HttpStatus.OK)
public SuccessResponse getPost(@PathVariable(name = "id") Long id) {
PostDTO post = postService.getPost(id);
return SuccessResponse.success(post);
}
}
- service
@Service
@RequiredArgsConstructor
public class PostService {
private final PostRepository postRepository;
public PostDTO getPost(Long postId) {
Optional<Post> byId = postRepository.findById(postId);
Post findPost = byId.orElseThrow(() -> new PostNotFound("해당 포스트가 존재하지 않습니다."));
return PostDTO.builder()
.id(findPost.getId())
.title(findPost.getTitle())
.contents(findPost.getContents())
.createdAt(findPost.getCreateAt().toString())
.build();
}
}
- repository
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}
- entity
@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String contents;
private LocalDateTime createAt;
}
- postDTO
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
public class PostDTO {
@NotBlank
private String title;
@NotBlank
private String contents;
private Long viewCount;
private String createdAt;
}
API를 만들기 앞서 클라이언트와 통신할때 사용할 PostDTO를 만들도록 하겠습니다.
Client - Controller - Service 간에는 PostDTO로 작업 하였습니다.
@NoArgsConstructor와 @AllArgsConstructor에 PROTECTED를 한 이유는 PostDTO를 만들 때 Builder를 사용할 것이기 때문에 생성자를 통한 생성은 막기 위함입니다.
title과 contents에 @NotBlank를 한 이유는 validation을 통해 값을 필수로 받기 위함입니다.
3. 게시물 등록 API - Controller, Service
-controller
@PostMapping("/post")
@ResponseStatus(value = HttpStatus.CREATED)
public SuccessResponse createPost(@Valid @RequestBody PostDTO postDTO) {
PostDTO post = postService.createPost(postDTO);
return SuccessResponse.success(post);
}
- service
public PostDTO createPost(PostDTO postDTO) {
Post post = Post.builder()
.title(postDTO.getTitle())
.contents(postDTO.getContents())
.createAt(LocalDateTime.now())
.build();
Post save = postRepository.save(post);
PostDTO postDTOResponse = PostDTO.builder()
.id(save.getId())
.build();
return postDTOResponse;
}
4. Response 양식 만들기
- SuccessResponse
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class SuccessResponse<T> {
private String message;
private T data;
public static <T> SuccessResponse success(T data) {
SuccessResponse responseUtil = SuccessResponse.builder()
.message("success")
.data(data)
.build();
return responseUtil;
}
}
- ErrorResponse
@Data
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ErrorResponse {
private String message;
@Builder.Default
private List<CustomError> errors = new ArrayList<>();
public List<CustomError> addError(CustomError error) {
this.errors.add(error);
return this.errors;
}
}
- CustomError
@Data
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CustomError {
private String field;
private String message;
}
Response할때 PostDTO를 바로 return해도 되지만 클라이언트와 통신을 생각하면 고정된 Response 양식이 필요하다고 생각하여 만들었습니다.
@JsonInclude(JsonInclude.Include.NON_NULL)은 필드중 null값이 있을경우 해당 필드는 제외하고 리턴합니다.
SuccessResponse의 경우 message에는 success가, data에는 결과 데이터가 들어가있으며
ErrorResponse의 경우 message에는 error가, erorrs에는 에러가 들어있습니다!
errors를 Array로 한 이유는 게시글 등록처럼 @Valid가 달린경우 제목과 내용을 둘다 적지 않으면 에러가 2개이기 때문에 한번에 보여주고자 하기 위함입니다.
다음 시간에는 테스트 코드를 작성하여 확인해보도록 하겠습니다.
진행 코드는 아래에서 확인할 수 있습니다.
'스프링 부트' 카테고리의 다른 글
스프링부트 게시판 API 만들기 - 4 (게시글 수정, 삭제 테스트 코드 작성) (0) | 2021.04.23 |
---|---|
스프링부트 게시판 API 만들기 - 3 (게시글 수정, 삭제 코드 작성) (0) | 2021.04.22 |
Thymeleaf와 @ControllerAdvice를 활용하여 원하는 에러페이지 만들기 (0) | 2021.04.19 |
스프링부트] CK Editor4 이미지 드래그&드롭 기능으로 AWS S3 이미지 업로드하기 (0) | 2021.04.16 |
도커를 활용하여 배포 환경별로 설정하는법 (0) | 2021.04.15 |