진행기간 : 2022-12-04 ~2022-12-05
소요시간 : 약 13시간 이상
깃허브 : https://github.com/GoF9490/NodeToSpring-Board
이번에 진행해본 자그마한 프로젝트는 전에 만들어봤던 node.js 기반의 게시판 프로젝트를 spring 으로 재구축 해보자는 생각으로 시작했습니다.
목표는 만들어 둔 node.js 에서 웹 프론트 기능을 그대로 이용하여 새로만든 spring 의 서버와 json으로 데이터를 주고받으며 기존의 기능을 구현해내는것. (html 템플릿이랑 css 를 다시짜기 싫어서 이렇게 계획했지만, http 통신을 비롯해 처음 써보며 적용한 기능들이 있어서 시간은 비슷하게 걸렸을 것 같습니다.)
// entity
우선 entity 객체를 만들기 위해 기존 db구조를 확인해봅시다.
그대로 쓰기에는 몇몇 (의도는 알겠지만) 불필요한 컬럼들이 보이고, 컬럼 이름들도 마음에 안드는게 보이네요.
어짜피 테이블도 3개밖에 안되는거 그냥 처음부터 깔끔하게 만들어보자 생각했습니다.
db는 h2 db를 사용했으며, 추후에 mysql에 구조를 그대로 적용해 볼 생각입니다.
User
@Entity
@Getter @Setter
public class User {
/**
* id, email, createdDate - 수정제한
*/
@Id
@GeneratedValue
@Column(name = "user_id")
@Setter(AccessLevel.NONE)
private Long id;
@Setter(AccessLevel.NONE)
private String email;
private String password;
private String nickname;
private Integer question;
private String answer;
@Column(name = "created_date")
@Setter(AccessLevel.NONE)
private LocalDateTime createdDate;
protected User() {
}
public User(String email, String password, String nickname, Integer question, String answer) {
this.email = email;
this.password = password;
this.nickname = nickname;
this.question = question;
this.answer = answer;
this.createdDate = LocalDateTime.now();
}
}
우선은 user입니다.
일단 만들면서 무지성 setter는 피하고자 추후에 변경될 값과 변경되서는 안될 값들을 구분지었습니다.
(id , email , 생성날짜는 변경불가)
생성자를 통해 만들어지며, 추후에 생성 메서드로 리팩토링을 해 볼 생각입니다.
처음에 GenerateValue 전략을 시퀀스 전략으로 작성했으나, 첫 실행은 잘되고 두번째 실행부터 테이블만 초기화되고 시퀀스는 초기화 되지않고 마이너스값이 찍혀있어서 그런지 오류가 발생, 좀더 공부하고 적용하는게 맞는것 같아서 일단은 오토로 구현했습니다.
Board
@Entity
@Getter
@Setter
public class Board {
@Id
@GeneratedValue
@Column(name = "board_id")
@Setter(AccessLevel.NONE)
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
private String title;
private String content;
@Column(name = "created_date")
@Setter(AccessLevel.NONE)
private LocalDateTime createdDate;
@OneToMany(mappedBy = "board")
private List<Comment> comments = new ArrayList<>();
protected Board() {}
public Board(User user, String title, String content) {
this.user = user;
this.title = title;
this.content = content;
this.createdDate = LocalDateTime.now();
}
public BoardForm toBoardForm(boolean needComment) {
BoardForm boardForm = new BoardForm();
boardForm.setBoardId(id);
boardForm.setWriter(user.getNickname());
boardForm.setTitle(title);
boardForm.setContent(content);
boardForm.setCommentCount(comments.size());
boardForm.setCreateDate(createdDate);
if (needComment) {
boardForm.setComments(comments.stream()
.map(Comment::toCommentForm)
.collect(Collectors.toList()));
}
return boardForm;
}
}
다음은 board입니다.
특이점으로는 comment 와 양방향 연관관계를 맺었고, getWriter() 메서드를 통해 user의 nickname을 바로 가지고오도록 만들었습니다.
board를 단일로 조회한다면 해당 페이지를 열람하는 일밖에 없다고 생각해, 페이지를 열면 해당 페이지의 모든 댓글을 가져올 수 있도록 양방향 연관관계를 설정했습니다.
json 형식으로 보낼 때 쓸데없는 comments 데이터로 인해 이슈가 생길까봐 DTO 로 변환할 때 comment 리스트를 추가로 넣어줄지 말지를 boolean 파라미터로 받아서 처리했습니다만 가장 좋은 방법인지는 의구심이 듭니다.
예로 양방향 관계로 db에서 가져올때 조건을 통해 comment를 가져오지 않도록 하는 기능이 있을것 같은데 아직 배운게 없습니다.
후술할 controller를 다루는 글에서 언급하겠지만, 밑의 comment와 더불어 가장 오랜시간을 잡아먹은 오류가 발생한 코드였습니다.
(연관관계 문제는 아니였고, 연관관계가 잘되서 일어난 문제? 하여튼 위의 코드는 최종적으로 수정된 코드입니다.)
Comment
package jpa.board.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import jpa.board.controller.form.CommentForm;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Getter
public class Comment {
@Id
@GeneratedValue
@Column(name = "comment_id")
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@ManyToOne
@JoinColumn(name = "board_id")
private Board board;
@Setter
private String content;
@Column(name = "created_date")
private LocalDateTime createdDate;
protected Comment() {
}
public Comment(User user, Board board, String content) {
this.user = user;
this.board = board;
this.content = content;
this.createdDate = LocalDateTime.now();
}
public CommentForm toCommentForm() {
CommentForm commentForm = new CommentForm();
commentForm.setCommentId(id);
commentForm.setWriter(user.getNickname());
commentForm.setBoardId(board.getId());
commentForm.setContent(content);
commentForm.setCreateDate(createdDate);
return commentForm;
}
}
마지막으로 comment 입니다.
setter를 쓸 필드값이 하나뿐이라 어노테이션을 클래스가 아닌 필드값에다가 지정해줬습니다만, 나머지 둘과 형식을 통일시켜주는게 맞을지 좀 고민했습니다.
위의 board와 동일한 이유로 엄청나게 시간을 잡아먹게한 녀석입니다.
'프로젝트' 카테고리의 다른 글
토이프로젝트 - NodeToSpring 3. controller (0) | 2022.12.04 |
---|---|
토이프로젝트 - NodeToSpring 2. repository / service (C, R) (0) | 2022.12.04 |
spring - 댓글 기능 (0) | 2022.10.07 |
Node.js - 게시판 ( 게시물 및 댓글 작성 / 삭제 ) (0) | 2022.09.07 |
Node.js - 게시판 ( 작성 / 수정 / 삭제 ) (0) | 2022.09.05 |