우선 현재 내 mysql에는 member 테이블밖에 없다. 게시글 등록을 위해서는 Category 테이블과 SubCategory테이블, 그리고 Board테이블을 만들어야 한다!
우선 Category테이블부터 시작했다.
원래는 카테고리id라는 컬럼은 없고 카테고리명을 pk로 해서 만들라고 했는데, 멘토님이 따로 id컬럼을 만드는 걸 추천해주셔서 반영했다. 나는 카테고리명 자체가 고유한 이름이 될 것이니까 pk로 둬도 된다고 생각했지만, 카테고리명을 나중에 바꾸게 되는 상황이 생길 때 카테고리명이 pk라면 대처가 어렵다고 말씀해주셨다. pk는 고유한 이름이 되는 것 뿐만 아니라 변경될 여지가 없는 컬럼에 박아주는게 좋다고 하셨다.
또한 변경이 거의 없고 데이터를 소규모로 가지는 테이블에 한해서는 Enum으로 하면 좋다고 해서 카테고리명은 Enum으로 바꿔줬다.
public enum Category {
QNA, // QnA
KNOWLEDGE, // 지식
COMMUNITY, // 커뮤니티
NOTICE // 공지사항
}
그리고 위와 같이 Enum클래스를 만들어줬다. 만들면서 "근데 내가 왜 이 클래스를 작성하고 있지?" 라는 생각이 들었다. 사실 그에 대한 근거는 없다. 단순히 member를 등록하는 예제에서 멤버의 role에 Enum으로 작성한 것을 넣어줬던 기억이 있어, 나중에 게시글을 등록할 때도 Category값에 저 Enum을 넣어주지 않을까라는 추측에서 만든 거다. 실제로 쓰일지는 나중에 봐야 알 듯 하다..
암튼 이렇게 dokky에 category테이블이 잘 생겼다. 그리고 바로 쿼리문을 통해 category테이블들에 데이터들을 추가해줬다.
음..잘 된 것 같다. 이젠 SubCategory 테이블을 만들 차례.
다음과 같이 Enum 클래스를 만들어줬다.
public enum SubCategory {
SKILL, // 기술 in QNA
CAREER, // 커리어 in QNA
COLUMN, // 칼럼 in 지식
REVIEW, // 리뷰 in 지식
DAILY, // 사는얘기 in 커뮤니티
STUDY, // 스터디 in 커뮤니티
NOTICE // 공지사항 in 공지사항
}
다음과 같이 쿼리를 멕여서 테이블을 만들어줬다.
CREATE TABLE `dokky`.`subcategory` (
`sub_category_id` INT NOT NULL AUTO_INCREMENT,
`parent_category_id` INT NOT NULL,
`sub_category_name` ENUM("SKILL", "CAREER", "COLUMN", "REVIEW", "DAILY", "STUDY", "NOTICE") NOT NULL,
FOREIGN KEY (`parent_category_id`) REFERENCES `category` (`category_id`),
PRIMARY KEY (`sub_category_id`));
암튼 SubCategory테이블도 잘 생겼다. 간단한 실험을 위해 데이터 하나를 추가해보기로 했다! sql쿼리를 잘 모르는 만큼 외래키 값을 insert into를 통해 어떻게 넣는지 궁금했기 때문.
별 거 없었다. 참조하는 테이블에서 사용하는 값을 그냥 넣어주면 됐다..암튼 SubCategory까지 만드는데 성공!
마지막으로 Board 테이블을 만들 차례다.
created_date와 updated_date컬럼의 경우, 추가/삭제가 빈번하거나 추적하기 어려운 테이블에 넣어주면 좋다고 한다. 운영하는 관점에서 해당 데이터가 언제 수정되고 생성됐는지에 대한 정보는 중요하기 때문이라고 한다.
아 mysql에서 이미지는 BLOB타입으로 저장한다고 한다. 이유는 간단한데, 이미지는 바이너리 파일이고 BLOB이 그런 녀석을 위한 타입이어서이다. (BLOB = Binary Large Object의 약자)
이렇게 Board 테이블을 만들어준 후, 간단한 실험을 해보기로 했다! 이번엔 datetime값을 insert into를 통해 어떻게 넣는지 궁금했기 때문이다.
역시나 간단했다. datetime같은 경우 지금처럼 create_date에 주는거면 now()를 쓰면 된다고 한다!
insert into board (writer, title, content, hits, like, created_date, category_id)
values("success@google.com", "도서관에서 공부중",
"수지도서관 맛돌이네여ㅎㅎ 다들 한번씩 오세요", 0, 0, now(), 5);
근데 에러가 난다..문법적인 에러란다. 찾아보니까 like필드가 mysql에서 예약어로 사용되는 애들이라고 한다! 이 경우 이런 필드들을 ``으로 묶어주니까 간단하게 해결됐다.
잘 되는 것 같다!! 중간에 멤버테이블에 테스트용 데이터 급하게 추가해주고 오류나는거 찾아보느라 시간이 꽤 걸렸다,,ㅠ
hits와 like, created_date의 경우 기본값을 설정해주는 게 좋을 것 같은데(각자 0, 0, 만들어지는 시간으로), 이걸 DB에서 지금 설정해주는게 맞는지 스프링 쪽에서 해줘야 되는지는 모르겠다. 일단 보류하기로 했다.
이제 DB 만지는 거에서 넘어와서, 게시글의 Entity 클래스를 만들어줄 차례..근데 여기서 많이 헷갈렸다. 찾아보니까 이것저것 이상한게 많기 때문
- 방금 mysql에서 테이블만들 땐 Board테이블의 writer 컬럼에 member의 pk, 즉 이메일을 했는데 그걸 그대로 쓰면 안되는건가? Entity클래스 설계할 땐 Member객체를 필드로 줘야 하남?
- 이미지는 어떻게 주지?
- 시간은 어떻게 처리하지? 그냥 따로 시간에 대한 컬럼 만들지말고 DB에서 알아서 하게 해야 하나?
등등..
뭐 그래도 모르는 걸 쭉 뽑아 놓았으니, 하나하나 찾으면 된다!
@Getter
@Setter
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "board_id")
private Long boardId;
private String writer;
@Column(length = 80)
private String title;
@Column(columnDefinition = "TEXT") // 필드의 타입을 텍스트로 지정
private String content;
private Long hits;
private Long like;
@Column(name = "created_date")
private LocalDateTime createdDate;
@Column(name = "updated_date", nullable = true)
private LocalDateTime updatedDate;
}
이렇게 하고 다음과 같은 테스트 코드를 실행해줬다.
@SpringBootTest
@Transactional
class BoardServiceIntegrationTest {
@Autowired BoardService boardService;
@DisplayName("게시글 등록 테스트")
@Test
void registerNewBoard() {
Board board = new Board();
board.setWriter("success@google.com");
board.setTitle("제에목");
board.setContent("보오오오오오온문");
board.setHits(0L);
board.setLike(0L);
board.setCreatedDate(LocalDateTime.now());
Long boardId = boardService.registerNewBoard(board).getBoardId();
Board foundBoard = boardService.findBoard(boardId).get();
System.out.println(foundBoard.getWriter());
System.out.println(foundBoard.getTitle());
System.out.println(foundBoard.getContent());
System.out.println(foundBoard.getCreatedDate());
assertThat(foundBoard.getBoardId()).isEqualTo(boardId);
}
}
근데 에러가 난다..! 알아보니, Board클래스의 like필드가 문제였다. 아까와 마찬가지로 mysql에서 쓰는 예약어였기 때문에, 별도의 조치가 필요했다.
다음과 같은 방법으로 해결해줬다.
@Column(name = "`like`")
private Long like;
ㅋㅋㅋㅋㅋ 컬럼이름을 ``로 감싸주기! 진짜 야매가 따로 없다 이게 아니면 application.properties에 다음을 추가해줘도 된다고 함.
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
이 값을 요렇게 true로 설정해주면 sql문이 실행될 때 테이블과 컬럼명이 자동으로 ``로 감싸진다고 한다.
암튼 그렇게 한 결과..
조금 develop을 해보자. 매번 이렇게 게시글 생성할 때마다 LocalDateTime.now()를 박아줘야 할까? (참고로 요놈은 스태틱 메서드로 현재 시간 객체를 반환해줌) 귀찮다. 이를 위해 @CreatedDate라는 어노테이션이 존재하는데, 이 놈은 엔티티가 생성되어 저장될 때 자동으로 생성시간을 박아준다.
@CreatedDate // Entity가 생성되어 저장될 때 시간이 자동으로 저장되게 하는 어노테이션
private LocalDateTime createdDate;
즉 테스트 코드에서 더 이상 setter를 통해 LocalDateTime.now()를 박아줄 필요가 없을 것이다! 그러나,,
ㅌ..통수 지대로 당해버렸다. @CreatedDate박으면 시간이 알아서 들어가진다며! 근데 null이 들어가버려 오류가 뜨는 모습;
찾아보니까, Auditing이란 것이 제대로 적용되지 않아 발생한다고 한다. Audit이라고 해서 JPA에서 제공하는 게 있는데 이게 바로 생성일/ 수정일 등의 자동화를 돕는다고 한다. CreatedDate같은 건 이 녀석의 일종인 거고.
암튼, 다음과 같이 해결가능하다. 먼저 JpaAuditing을 쓰기 위해 main메서드가 있는 클래스에 다음과 같은 어노테이션을 먹여준다.
@EnableJpaAuditing // 바로 이거! JpaAuditing을 활성화하는 기능
@SpringBootApplication
public class DokkyApplication {
public static void main(String[] args) {
SpringApplication.run(DokkyApplication.class, args);
}
}
그 다음 Entity클래스에 다음과 같은 어노테이션을 멕인다.
@Getter
@Setter
@Entity
@EntityListeners(AuditingEntityListener.class) // 이거! 이 클래스에 Auditing기능을 멕인다는 의미
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "board_id")
private Long boardId;
private String writer;
// .. 생략
결과는..
테스트에 성공하는 모습!!
'PROJECT > 개발일지' 카테고리의 다른 글
[백엔드 야생 개발일지] 게시글 조회/수정/삭제 기능 만들어보기 (0) | 2023.02.28 |
---|---|
[백엔드 야생 개발일지] 게시글 등록 기능 만들어보기 (2) (0) | 2023.02.27 |
[백엔드 야생 도전일기] 인터셉터를 통해 응답값 통일해보기 (2) - 인터셉터 뜯어보기 (0) | 2023.02.05 |
[백엔드 야생 도전일기] 인터셉터를 통해 응답값 통일해보기 (1) - ResponseEntity 뜯어보기 (0) | 2023.02.01 |
[백엔드 야생 도전일기] 스프링으로 회원가입/로그인기능 만들어보기 (4) (0) | 2023.01.31 |