티스토리 뷰
📌 230922 TIL : Spring boot로 게시판 만들기 - 2. 게시글 삽입, 조회, 삭제 구현
패스트캠퍼스 강의를 참고하여 공부를 진행하였습니다.
📝 개요
지난 번에는 익명 게시판을 만들기 위해 ERD 설계를 하였고, MySql에 테이블을 만들었다.
오늘은 게시판 및 게시글을 삽입, 조회, 삭제할 수 있는 기능을 설계했다
1. 클래스 구조
게시판을 담당하는 board 패키지, 게시글을 담당하는 post 패키지, 댓글을 담당하는 reply 패키지로 구성하였다.
각 패키지 별로 컨트롤러 / 서비스 / 모델 / DB 패키지로 나누었다.
컨트롤러 : 뷰와 모델부분 (Service, Repository)를 연결하는 역할
서비스 : 비즈니스 로직이 담긴 부분, repository를 이용하여 데이터를 관리한다.
모델 : 컨트롤러와 서비스에 사용될 DTO 클래스를 정의한 곳이다.
각 역할을 Post 패키지를 통해 알아보자.
2. 컨트롤러 - PostController
뷰와 서비스(모델)을 잇는 역할을 진행한다.
GET / POST / PUT / DELETE 같은 RESTful 방식이 들어오면, 적절한 역할을 수행하도록 PostService 메소드와 연결시켜준다.
Json 형식을 주고받으므로, @RestController 를 사용하였다.
@RestController
@RequestMapping("api/post")
@RequiredArgsConstructor
public class PostApiController {
private final PostService postService;
@PostMapping("/create")
private void create(
@Valid
@RequestBody
PostRequest postRequest
){
postService.create(postRequest);
}
@PostMapping("/view")
public PostResponse view(
@Valid
@RequestBody
PostViewRequest postViewRequest
){
return postService.view(postViewRequest);
}
@GetMapping("/all")
public List<PostResponse> list(
){
return postService.all();
}
// 왜 @DeleteMapping 대신 @PostMapping을 사용했냐면,
// 비밀번호와 postId를 담는 PostViewRequest를 Json 형식으로 받아와야 했기 때문에
// Get 방식을 지원하는 @DeleteMapping을 사용하지 않았다.
@PostMapping("/delete")
public void delete(
@Valid
@RequestBody PostViewRequest postViewRequest
){
postService.delete(postViewRequest);
}
}
3. 서비스 - PostSerivce / 레포지토리 - PostRepository (interface)
서비스는 도메인 객체를 관리하는 곳으로, 서비스 클래스에서 도메인 객체를 이용하여 데이터를 관리한다.
레포지토리를 이용하여 데이터의 영속성 계층에 접근한다.
말이 어려운데 코드를 보면 이해가 됐다.
레포지토리는 Spring JPA를 구현한 JpaRepository를 상속받아, 데이터의 CRUD를 처리한다.
다만 JpaRepository에 없는 쿼리는 따로 메소드를 정의하여 쿼리를 실행할 수 있다.
📝 PostSerivce는 다음과 같은 메소드를 정의하였다.
create(PostRequest postRequest); -> 게시글 삽입.
view(PostViewRequest postViewRequest); -> 게시글 조회
all() -> 게시글 전체 조회
delete(PostViewRequest postViewRequest); -> 게시글 삭제
* 반환 객체는 PostResponse를 사용했는데 그 이유는 ,
'JPA Entity를 Http Response Dto로 절대 사용하지 말아야 하는 이유, Hans L' 님 글을 참고하자.
게시글 삽입 : create(PostRequest postRequest)
- 게시글의 현재 시간을 테이블에 저장하기 위해 여기서 LocalDateTime.now를 넣어주었다.
- 게시글이 등록됐다는 상태를 저장해주어야한다.
public PostEntity create(
PostRequest postRequest
){
var entity = PostEntity.builder()
.boardId(1L) // << 임시 고정!
.userName(postRequest.getUserName())
.password(postRequest.getPassword())
.email(postRequest.getEmail())
.title(postRequest.getTitle())
.content(postRequest.getContent())
.postedAt(LocalDateTime.now()) //<< 지금 시간 담는 방법!
.status(Config.REGISTERED)
.build()
;
return postRepository.save(entity);
}
게시글 조회 : view(PostViewRequest postViewRequest)
- 확인해야하는 것은 2가지이다.
- 게시글이 있는가? -> 없으면 RuntimeException으로 예외처리 진행.
- 비밀번호가 맞는가? -> 틀리면 RuntimeException 으로 예외처리 진행.
- 도메인 클래스(Entity)는 서비스에서만 사용할 수 있도록 반환 객체는 PostResponse 클래스 정의하여 사용했다.
/*
1. 게시글이 있는가?
2. 비밀번호가 있는가?
*/
public PostResponse view(
PostViewRequest postViewRequest
){
return postRepository.findFirstByIdAndStatusOrderByIdDesc(
postViewRequest.getPostId(), Config.REGISTERED)
.map(it -> {
//entity존재 하는지
if (!it.getPassword().equals(postViewRequest.getPassword())) {
var format = "패스워드 맞지 않습니다. %s vs %s";
throw new RuntimeException(String.format(format, it.getPassword(), postViewRequest.getPassword()));
}
return PostResponse.of(it);
//data가 없다면.
}).orElseThrow( () ->
new RuntimeException("해당 게시글이 존재 하지 않습니다. : " + postViewRequest.getPostId())
);
}
게시글 전체 조회 : all()
- List<PostEntity> 로 받은 객체를 PostReponse 객체로 바꾸어 전달.
- PostEntity -> PostReponse 로 만드는 방법은 PostResponse 클래스에서 이루어진다.
public List<PostResponse> all() {
List<PostEntity> list = postRepository.findAllByStatusOrderByIdDesc(Config.REGISTERED);
List<PostResponse> postList = new ArrayList<>();
for (PostEntity post : list) {
postList.add(PostResponse.of(post));
}
return postList;
}
게시글 삭제 : delete(PostViewRequest postViewRequest)
- 게시글 조회와 같이 PostViewRequest 사용.
- 게시글 삭제에서 실제로 테이블에서 레코드를 삭제하는 것이 아닌, status 상태를 변경시켜주는 것이다.
- Register -> unRegister
public void delete(PostViewRequest postViewRequest) {
postRepository.findById(
postViewRequest.getPostId())
.map(it -> {
//entity 존재 하는지
if (!it.getPassword().equals(postViewRequest.getPassword())) {
var format = "패스워드 맞지 않습니다. %s vs %s";
throw new RuntimeException(String.format(format, it.getPassword(), postViewRequest.getPassword()));
}
it.setStatus(Config.UNREGISTERED);
postRepository.save(it);
return it;
//data가 없다면.
}).orElseThrow( () ->
new RuntimeException("해당 게시글이 존재 하지 않습니다. : " + postViewRequest.getPostId())
);
}
📝 PostRepository
- 메소드 작성법 알아놔야겠다..~ !
public interface PostRepository extends JpaRepository<PostEntity, Long> {
//select * from post where id = ? and status = ? order by id desc limit 1
public Optional<PostEntity> findFirstByIdAndStatusOrderByIdDesc(Long id, String status);
//select * from post where status = ? order by id desc
public List<PostEntity> findAllByStatusOrderByIdDesc(String status);
}
4. 모델 - PostRequest / PostViewRequest / PostResponse
뷰 ~ 컨트롤러 ~ 서비스 단계에서 사용되는 DTO 객체를 정의했다.
서로간의 의존성 분리를 위하여, Entity 객체와 분리하도록 해야한다.
PostRequest
뷰 -> 컨트롤러 -> 서비스 까지 사용되는 객체
요청에 들어온 값들을 담는다.
그래서 Vaild 할 수 있도록 @NotNull 같은 vaildation 처리 해주었다.
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class PostRequest {
@NotBlank
private String userName;
@NotBlank
@Size(min = 4, max = 8)
private String password;
@NotBlank
@Email
private String email;
@NotBlank
private String title;
@NotBlank
private String content;
}
PostViewRequest
- 게시글 조회 및 삭제 시 사용될 postId, password 만 담는 요청 객체이다.
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class PostViewRequest {
@NotNull
private long postId;
@NotNull
private String password;
}
PostResponse
- 응답 반환 객체
- 서비스 ~ 컨틀롤러 ~ 뷰 에서 사용
- repesponse -> entity 는 of 메소드 사용 ..
- java 17부터 record 기능이 있다는데 찾아봐야겠다.
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class PostResponse {
private Long id;
private Long boardId;
private String userName;
private String password;
private String email;
private String status;
private String title;
private String content;
private LocalDateTime postedAt;
public static PostResponse of(PostEntity post) {
//entity to dto
return PostResponse.builder()
.userName(post.getUserName())
.password(post.getPassword())
.email(post.getEmail())
.title(post.getTitle())
.content(post.getContent())
.status(post.getStatus())
.postedAt(post.getPostedAt())
.boardId(post.getBoardId())
.id(post.getId())
.build();
}
}
댓글도 같은 방식으로 구현하면 될 것 같다!!
글 쓰는게 더 오래걸리는듯! ^^
화이팅~
'백엔드 공부하기 > TIL' 카테고리의 다른 글
231005 TIL : [Spring] Bean, IoC, DI (0) | 2023.10.06 |
---|---|
231002 TIL : 알고리즘 트라이 (Trie) (1) | 2023.10.02 |
230921 TIL : Spring boot로 게시판 만들기 - 1. MySQL 다이어그램 제작 및 테이블 만들기, Entity 설정 (0) | 2023.09.21 |
Spring MVC 공부해보자 (1) | 2023.09.18 |
230912 TIL : 해시 테이블 충돌 피하는 방법, Merge Sort, Quick Sort (0) | 2023.09.12 |
- Total
- Today
- Yesterday
- 부트캠프
- 패스트캠퍼스
- 백엔드
- 과정중간회고
- 스터디후기
- qjzl
- 국비지원취업
- 백엔드개발자
- TiL
- 야놀자X패스트캠퍼스부트캠프
- 데이터베이스
- 국비지원
- springboot
- Java
- 국비지원캠프
- 채팅기능개발
- 프로젝트후기
- 패스트캠퍼스강의
- 야놀자
- #국비지원취업
- 백준
- 카카오API
- boj
- 백엔드부트캠프
- 자료구조
- 커리어멘토링
- 그룹스터디워크샵
- 그룹스터디
- 자료구조 #스택 #큐 #덱 #선형자료구조
- be
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |