[
{
"createdAt": "2023-04-17T12:22:37.542175",
"modifiedAt": "2023-04-17T12:22:37.542175",
"id": 3,
"author": "11",
"title": "11",
"content": "11"
},
{
"createdAt": "2023-04-17T12:22:36.939707",
"modifiedAt": "2023-04-17T12:22:36.939707",
"id": 2,
"author": "11",
"title": "11",
"content": "11"
},
{
"createdAt": "2023-04-17T12:22:36.187211",
"modifiedAt": "2023-04-17T12:22:36.187211",
"id": 1,
"author": "11",
"title": "11",
"content": "11"
}
]
{
"createdAt": "2023-04-17T12:22:36.187211",
"modifiedAt": "2023-04-17T12:22:43.910917",
"id": 1,
"author": "11",
"title": "11",
"content": "11"
}
{
"author":"11",
"password":"11",
"title":"11",
"content":"11"
}
{
"createdAt": "2023-04-17T12:22:37.5421747",
"modifiedAt": "2023-04-17T12:22:37.5421747",
"id": 1,
"author": "11",
"title": "11",
"content": "11"
}
{
"author":"11",
"password":"11",
"title":"11 수정",
"content":"11 수정"
}
{
"createdAt": "2023-04-17T12:22:36.187211",
"modifiedAt": "2023-04-17T12:22:36.187211",
"id": 1,
"author": "11",
"title": "11 수정",
"content": "11 수정"
}
{
"password" :"11"
}
성공적으로 삭제되었습니다.
// Client <-Dto-> Controller <-Dto-> Service <-Dto-> Repository <-Entity-> DB
package com.sparta.spring_post.controller;
import com.sparta.spring_post.dto.PostRequestDto;
import com.sparta.spring_post.dto.PostResponseDto;
import com.sparta.spring_post.service.PostService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequiredArgsConstructor
public class PostController {
// PostService 연결
private final PostService postService;
// 목록 조회
@GetMapping("/api/posts")
public List<PostResponseDto> getAllPosts() {
return postService.getAllPosts();
}
// 상세 조회
@GetMapping("/api/posts/{id}")
public PostResponseDto getPost(@PathVariable Long id) {
return postService.getPost(id);
}
// 추가
@PostMapping("/api/post")
public PostResponseDto createPost(@RequestBody PostRequestDto postRequestDto) {
return postService.createPost(postRequestDto);
}
// 수정
@PutMapping("/api/post/{id}")
public PostResponseDto updatePost(@PathVariable Long id, @RequestBody PostRequestDto postRequestDto) {
return postService.updatePost(id, postRequestDto);
}
// 삭제
@DeleteMapping("/api/post/{id}")
public String deletePost(@PathVariable Long id, @RequestParam("password") String password) {
return postService.deletePost(id, password);
}
}
// Client <-Dto-> Controller <-Dto-> Service <-Dto-> Repository <-Entity-> DB
package com.sparta.spring_post.dto;
import com.sparta.spring_post.entity.Post;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class PostRequestDto {
private String author;
private String password;
private String title;
private String content;
public Post toEntity() {
return Post.builder()
.author(author)
.password(password)
.title(title)
.content(content)
.build();
}
}
// Client <-Dto-> Controller <-Dto-> Service <-Dto-> Repository <-Entity-> DB
package com.sparta.spring_post.dto;
import com.sparta.spring_post.entity.Post;
import com.sparta.spring_post.entity.Timestamped;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Getter
@NoArgsConstructor
public class PostResponseDto extends Timestamped {
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
private Long id;
private String author;
private String title;
private String content;
public PostResponseDto(Post post) {
this.createdAt = post.getCreatedAt();
this.modifiedAt = post.getModifiedAt();
this.id = post.getId();
this.author = post.getAuthor();
this.title = post.getTitle();
this.content = post.getContent();
}
}
// Client <-Dto-> Controller <-Dto-> Service <-Dto-> Repository <-Entity-> DB
package com.sparta.spring_post.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sparta.spring_post.dto.PostRequestDto;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@Entity
@NoArgsConstructor
public class Post extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String author;
@Column(nullable = false)
@JsonIgnore // 데이터를 주고받을 때, 해당 데이터 ignore. 응답값 보이지 않음
private String password;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String content;
@Builder
public Post(String author, String password, String title, String content) {
this.author = author;
this.password = password;
this.title = title;
this.content = content;
}
public void update(PostRequestDto postRequestDto) {
this.author = postRequestDto.getAuthor();
this.password = postRequestDto.getPassword();
this.title = postRequestDto.getTitle();
this.content = postRequestDto.getContent();
}
}
package com.sparta.spring_post.entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class Timestamped {
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime modifiedAt;
}
// Client <-Dto-> Controller <-Dto-> Service <-Dto-> Repository <-Entity-> DB
package com.sparta.spring_post.repository;
import com.sparta.spring_post.entity.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findAllByOrderByModifiedAtDesc();
}
// Client <-Dto-> Controller <-Dto-> Service <-Dto-> Repository <-Entity-> DB
package com.sparta.spring_post.service;
import com.sparta.spring_post.dto.PostRequestDto;
import com.sparta.spring_post.dto.PostResponseDto;
import com.sparta.spring_post.entity.Post;
import com.sparta.spring_post.repository.PostRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
@RequiredArgsConstructor
public class PostService {
// PostRepository 연결
private final PostRepository postRepository;
// 목록 조회
@Transactional(readOnly = true)
public List<PostResponseDto> getAllPosts() {
List<Post> posts = postRepository.findAllByOrderByModifiedAtDesc();
List<PostResponseDto> dtos = new ArrayList<>();
for (Post post : posts) {
dtos.add(new PostResponseDto(post));
}
return dtos;
}
// 상세 조회
@Transactional(readOnly = true)
public PostResponseDto getPost(Long id) {
Post post = postRepository.findById(id).orElseThrow();
PostResponseDto dtos = new PostResponseDto(post);
dtos.getClass();
return dtos;
}
// 추가
@Transactional
public PostResponseDto createPost(PostRequestDto postRequestDto) {
Post post = postRepository.save(postRequestDto.toEntity());
return new PostResponseDto(post);
}
// 수정
@Transactional
public PostResponseDto updatePost(Long id, PostRequestDto postRequestDto) {
Post post = postRepository.findById(id).orElseThrow();
PostResponseDto dtos = new PostResponseDto(post);
if (!post.getPassword().equals(postRequestDto.getPassword())) {
return dtos;
}
post.update(postRequestDto);
return new PostResponseDto(post);
}
// 삭제
@Transactional
public String deletePost(Long id, String password) {
Post post = postRepository.findById(id).orElseThrow();
if (!post.getPassword().equals(password)) {
return "비밀번호가 일치하지 않습니다.";
}
postRepository.deleteById(id);
return "성공적으로 삭제되었습니다.";
}
}
Entity
를 그대로 반환하지 말고 Dto
에 담아서 반환하라는 말의 의미를 처음에는 이해하지 못해서 그 말의 의미부터 구글링을 했다.
Entity
는 실제 DB 테이블과 매핑되는 핵심 클래스이므로 Entity를 그대로 반환했을 경우 객체의 일관성, 안전성을 보장하기 어렵기 때문에 목적 자체가 전달인 Dto
에 데이터를 담아서 반환해야한다.
처음엔 Dto를 RequestDto, ResponseDto 이렇게 둘로만 나누지 않고 CRUD별로 Dto를 나눴다. 그걸 본 기술매니저님이 그럴 필요없다 오히려 비효율적이다 라는 피드백을 주셔서 요청과 응답으로만 Dto를 다시 만들고 그걸 어떻게 적용해야하나 고민과 구글링의 연속이었다.
비밀번호같은 개인정보는 보안을 위해 데이터를 Response 해줄 때 나타나지 않도록 해야했는데 Request와 Response 둘의 정확한 쓰임새나 정의, 또 언제 어떻게 값을 담아서 무엇으로 응답을 해줘야하는지 너무 헷갈렸다. 그래서 일단 비밀번호까지 응답이 오도록 코드를 만든 다음 그것에 대해 고민을 해봤다. ResponseDto에서 password값을 포함하지 않도록 하면 되는 간단한 문제였고 또 그 후에 다시 찾아보니 Entity에서 애초에 @JsonIgnore
라고 선언을 해놓으면 그 값은 응답값이 보이지 않는다는 것을 알아냈다.