전 게시물에서는 테이블을 하나만 만들어서 사용하였다.
이제는 실제 게시판처럼 유저가 작성한 모든 게시글을 출력해보도록 해보자!
※ JPA를 쓰고있다. MyBatis는 아직 뭔지도 몰?루
먼저, post 테이블을 만들어보자.
패키지로 만드는 것이 관리하기 편하여 post를 담당하는 패키지, user를 담당하는 패키지 구조로 변경하였다.
기존에 작성한 Test% 이름의 파일들은 전부 User로 리팩토링 하였다.
사랑해요 IntelliJ!
// Post.java
@Entity
@Data
public class Post {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long Id;
private String title;
private String content;
@ManyToOne
private User user;
}
@ManyToOne 어노테이션을 통해 하나의 유저가 여러 개의 게시글을 작성할 수 있다고 알려준다.
기존의 User Entity에는 아래를 추가하자.
// User.java
@OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE)
private List<Post> posts;
@OneToMany를 통해 하나의 유저에는 여러 게시글이 있다는 것을 알려준다.
이때 mappedBy를 통해 Post의 어떤 항목과 Join되어있는지 명시해준다.
cascade를 통해 해당 유저가 삭제되면 모든 게시글을 삭제한다.
이전 글에 작성한 내용을 참고한다.
// PostRepository.java
public interface PostRepository extends JpaRepository<Post, Long> {
}
// PostService.java
@RequiredArgsConstructor
@Service
public class PostService {
private final PostRepository postRepository;
public Post push(Post post) {
return postRepository.save(post);
}
public Post getPost(Long id) {
Optional<Post> postOptional = postRepository.findById(id);
if (postOptional.isPresent()) {
return postOptional.get();
}
return null;
}
}
// PostController.java
@RequiredArgsConstructor
@Controller
@RequestMapping("/posts")
public class PostController {
private final UserService userService;
private final PostService postService;
@GetMapping("/{id}")
public Post getPost(@PathVariable Long id) {
Post post = postService.getPost(id);
return post;
}
@PostMapping("")
public Post postPost(@RequestBody PostRequest req, @RequestHeader String token) {
User user = userService.findByName(token);
Post post = new Post();
post.setUser(user);
post.setTitle(req.getTitle());
post.setContent(req.getContent());
return postService.push(post);
}
}
원래는 위와 같이 짜면 안되지만, 간단한 테스트를 위해 위와 같이 작성하였다.
// UserController.java
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getTest(@PathVariable("id") Long id) {
User t = userService.findById(id);
UserResponse res = new UserResponse();
res.setName(t.getName());
res.setPosts(t.getPosts());
return ResponseEntity.status(HttpStatus.OK).body(res);
}
유저 id를 받아와 해당 유저가 작성한 모든 게시글을 return해 줄 것으로 추측된다.
데이터를 다음과 같이 넣었다.
이제 /users로 모든 유저의 데이터를 가져와보자.
어라... 무한 참조가 발생하였다.
대체 왜일까...
검색해보니 JPA는 양방향 Entity일때 기본적으로 엔티티를 전부 참조한다고 한다. 그러니 무한 참조가 발생할수밖에...
DTO를 만들어 반환하도록 하였다. 추가될 게시판 -> 게시글 -> 댓글
형식만 봐도, DTO를 만드는 것이 좋다고 생각하였다.
다른 방법은 이곳으로.
// PostResponse.java
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class PostResponse {
private Long id;
private String title;
private String content;
}
// UserResponse.java
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class UserResponse {
private String name;
private List<Post> posts;
}
DTO(Data Access Object)를 위와 같이 생성한다.
// UserController.java
@Controller
@RequiredArgsConstructor
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
@GetMapping("")
public ResponseEntity<List<UserResponse>> getAllUsers() {
List<UserResponse> res = getUserDetails(userService.getAll());
return ResponseEntity.status(HttpStatus.OK).body(res);
}
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable("id") Long id) {
UserResponse res = getUserDetail(userService.findById(id));
return ResponseEntity.status(HttpStatus.OK).body(res);
}
private UserResponse getUserDetail(User t) {
UserResponse res = new UserResponse();
res.setName(t.getName());
res.setPosts(getUserWritingDetail(t.getPosts()));
return res;
}
private List<UserResponse> getUserDetails(List<User> t) {
List<UserResponse> res = new ArrayList<>();
t.forEach(a -> res.add(new UserResponse(a.getName(), getUserWritingDetail(a.getPosts()))));
return res;
}
private List<PostResponse> getUserWritingDetail(List<Post> p) {
List<PostResponse> res = new ArrayList<>();
p.forEach(a -> res.add(new PostResponse(a.getId(), a.getTitle(), a.getContent())));
return res;
}
}
새로 추가된 함수를 알아보자.
private UserResponse getUserDetail(User t);
UserResponse DTO를 이용하여 반환값을 설정한다.
private List<PostResponse> getUserWritingDetail(List<Post> p);
모든 유저의 결과를 반환한다. 실제 서비스에서는 사용하지 않는다.
private List<UserResponse> getUserDetails(List<User> t);
유저가 작성한 글 목록을 받아와 PostResponse DTO List 형식으로 반환한다.
자, 이제 get 요청을 보내보자!
정상적으로 전부 받아와지는 것을 볼 수 있다!
velog 쓰기 전, 이 부분에서 많이 헤맸는데 Entity간 참조 문제였다...
축하한다! 이게 간단한 Join을 실행할 수 있다!