
controller
, repository
와 같은 레이어별로 패키지를 나누지만, 이 경우 테이블별로 패키지를 구성했습니다.
1. user
테이블 생성
package shop.mtcoding.blog.user;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.sql.Timestamp;
@Setter
@Getter
@Table(name = "user_tb")
@NoArgsConstructor
@Entity
public class User {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Integer id;
@Column(unique = true, nullable = false)
private String username; //아이디
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String email;
private Timestamp createdAt;
@Builder
public User(Integer id, String username, String password, String email, Timestamp createdAt) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.createdAt = createdAt;
}
}
위 코드를 실행한 후 콘솔을 확인하면,
user_tb
테이블이 정상적으로 생성된 것을 확인할 수 있습니다.
Hibernate: drop table if exists board_tb cascade
Hibernate: drop table if exists user_tb cascade
Hibernate: create table board_tb (id integer generated by default as identity, created_at timestamp(6), content varchar(255) not null, title varchar(255) not null, primary key (id))
Hibernate: create table user_tb (id integer generated by default as identity, created_at timestamp(6), email varchar(255) not null, password varchar(255) not null, username varchar(255) not null unique, primary key (id))
이제
Board
와 User
테이블 간의 외래 키 관계를 설정해보겠습니다.(외래키가 궁금하면 다음 게시글 참고 https://inblog.ai/hj/ddl-data-definition-language-제약조건-27095?traffic_type=internal)

@ManyToOne
어노테이션은 외래 키 관계를 설정할 때 사용됩니다.package shop.mtcoding.blog.board;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import shop.mtcoding.blog.user.User;
import java.sql.Timestamp;
@NoArgsConstructor // 빈생성자
@Getter // private 설정이기 때문에 게터, 세터가 필요함
@Setter
@Table(name = "board_tb")
@Entity // DB에서 조회하면 자동 매핑이 됨
public class Board {
@GeneratedValue(strategy = GenerationType.IDENTITY) // Auto_increment 열의 증가를 자동으로 시켜줌
@Id // PK (DB 테이블의 기준이 되는 키를 설정하는것)
private Integer id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String content;
private Timestamp createdAt;
@ManyToOne
private User user;
// 생성자
@Builder
public Board(Integer id, String title, String content, Timestamp createdAt) {
this.id = id;
this.title = title;
this.content = content;
this.createdAt = createdAt;
}
}

이후 H2 콘솔에서 테이블 구조를 확인하면,
user_id
외래 키 컬럼이 추가된 것을 확인할 수 있습니다.
2. 더미 데이터 삽입
data.sql
파일을 수정하여 더미 데이터를 삽입합니다.insert into user_tb(username, password, email, created_at)
values ('ssar', '1234', 'ssar@nate.com', now());
insert into user_tb(username, password, email, created_at)
values ('cos', '1234', 'cos@nate.com', now());
insert into user_tb(username, password, email, created_at)
values ('love', '1234', 'love@nateSELECT * FROM BOARD_TB .com', now());
insert into board_tb(title, content, created_at, user_id)
values ('제목1', '내용1', now(), 1);
insert into board_tb(title, content, created_at, user_id)
values ('제목2', '내용2', now(), 1);
insert into board_tb(title, content, created_at, user_id)
values ('제목3', '내용3', now(), 2);
insert into board_tb(title, content, created_at, user_id)
values ('제목4', '내용4', now(), 2);
insert into board_tb(title, content, created_at, user_id)
values ('제목5', '내용5', now(), 2);
→ 순서대로 insert 되면
love
는 3번이됩니다. 따라서 board_tb에 love 는 존재하지 않습니다.위 코드를 실행하면 H2 데이터베이스에 데이터가 정상적으로 삽입된 것을 확인할 수 있습니다.


이제 글을 insert 하기 위해서는
user_id
는 user
테이블의 pk 를 갖거나 null
값을 가져야합니다.→ 즉 board 테이블의 튜플 추가를 위해
insert
쿼리와 USER_ID 선택을 위한 select
문이 추가되어야 됩니다.3. 쿼리 최적화
만약 게시글을 40명이 작성했다고 가정하면 유저를 찾기 위한
select
쿼리는 40번 실행되고 이는 비효율적인 쿼리입니다. 이를 해결하기 위해 FetchType.LAZY
를 사용 하겠습니다.
fetch 전략을 이용하지 않으면 기본적으로
EAGER
이 적용됩니다. 이를 FetchType.LAZE
로 변경하겠습니다.이렇게 하면
FindALL
할 때 마다 조회되던 유저 테이블을 조회하지 않고, 테이블에 저장된 1
이라는 숫자 만을 리턴하게 됩니다.
필요한 시점에만 관련 데이터를 조회하여 성능을 최적화 할 수 있는것이죠결과를 테스트 코드로 확인해 보겠습니다.


첫번째 조회에서는
boardList 를 위한
board_tb
조회가 이루어 지고 있습니다.
board 가 갖고 있는 userId 를 출력해도
테이블을 조회하지 않고 있어요.(쿼리문이 더이상 출력되지 않았음)
조인된 유저 객체의 컬럼인
username
을 출력하려 하자 user_tb
에서의 조회가 이루어집니다.이는
FetchType.LAZY
전략에 의해 이루어 지며, 관련된 엔티티를 실제로 필요로 할 때 까지 데이터를 지연 로드 하는것입니다.스프링부트 게시판 시리즈 v2 -1. https://inblog.ai/hj/27190 (User 테이블 생성 및 쿼리 수정) -2. https://inblog.ai/hj/27193 (User, Board 테이블 조인 과 JPQL) -3. https://inblog.ai/hj/27224 (회원 가입) -4. https://inblog.ai/hj/27225 DTO 를 통한 리팩토링 -5. https://inblog.ai/hj/27310 로그인, 로그아웃 -6. https://inblog.ai/hj/27316 서비스 레이어 추가 및 DTO 활용 -7. https://inblog.ai/hj/27430 예외처리 핸들러 설정과 User 서비스 리팩토링 -8. https://inblog.ai/hj/27431 Board 기능 리팩토링 -9. https://inblog.ai/hj/27560 게시글 수정, 더티체킹(flush) -10. https://inblog.ai/hj/27561 인터셉터, AOP 사용 / 마무리
Share article