Skip to content

Commit 62258d2

Browse files
authored
chore: Flyway 기반 데이터베이스 스키마 형상관리 활성화 (#109)
* [BOOK-280] fix: infra - userBook 엔티티의 length와 통일 * [BOOK-280] feat: infra - 초기 데이터베이스 스키마 생성(users, books, user_books, reading_records, tags, reading_record_tags 테이블 추가 및 제약조건) * [BOOK-280] feat: infra - 기본 태그 데이터 삽입 SQL 스크립트 추가 * [BOOK-280] chore: infra - application-persistence.yml 파일 수정 및 Flyway 설정 변경 * [BOOK-280] docs: infra - 데이터베이스 스키마 거버넌스 전략 및 초기 데이터 관리 가이드라인 추가 * [BOOK-280] docs: infra - 데이터 삽입 가이드라인 수정 및 성능 고려 사항 추가 * [BOOK-280] refactor: infra - bookIsbn13 필드 길이 13으로 변경 * [BOOK-280] feat: infra - bookIsbn13 필드 길이 13자로 변경하는 Flyway 마이그레이션 스크립트 추가
1 parent a6b0ef2 commit 62258d2

File tree

7 files changed

+172
-9
lines changed

7 files changed

+172
-9
lines changed

infra/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,60 @@ Batch ────────→ Infra
5151
- Repository Implementation은 실제 데이터 저장소와의 연동을 담당하며, Repository Interface를 구현하여 JPA, MongoDB 등 구체적인 기술로 데이터를 처리한다.
5252
- Client Implementation은 외부 시스템과의 실제 통신을 구현하며, Domain Client Interface를 구현하여 REST API, gRPC 등 구체적인 프로토콜로 통신한다.
5353
- Import Selector는 모듈 간 설정 조합을 관리하며, admin, batch, apis 모듈 내부에서 애플리케이션 요구사항에 따라 필요한 Infrastructure 구성을 동적으로 선택한다.
54+
55+
## **💿Database Schema Governance Strategy**
56+
- 개발 환경에서는 ddl-auto를 활용해 스키마를 신속히 반영하고, 운영 환경에서는 Flyway 기반 마이그레이션을 통해 데이터베이스 스키마를 형상관리한다.
57+
- Flyway 스크립트는 스키마 구조를 다루는 migration과 초기 데이터를 다루는 seed로 역할을 분리하여 관리한다.
58+
59+
### 1. 스키마 관리 (migration)
60+
- 스크립트 위치는 infra 모듈의 resources/db/migration 디렉터리에 저장한다.
61+
- 스크립트는 `V<YYYYMMDD>_<NNN>__<Action>_<Object>_<Details>.sql` 형식으로 명명한다.
62+
- 구체적인 네이밍 규칙은 아래와 같다.
63+
- `V` (접두사): Versioned Migration을 의미하는 Flyway의 표준 접두사로 이 스크립트는 DB 당 단 한 번만 실행된다.
64+
- 예시: V
65+
- `<YYYYMMDD>` (버전 - 날짜): 스크립트가 작성된 날짜로, 버전 충돌을 방지하고 시간순으로 마이그레이션을 정렬하는 기준이된다.
66+
- 예시: 20250820
67+
- _`<NNN>` (버전 - 일련번호): 같은 날짜에 여러 스크립트가 생성될 경우, 실행 순서를 보장하기 위한 3자리 일련번호로 매일 001부터 시작한다.
68+
- 예시: _001, _002
69+
- __ (구분자): 버전과 설명을 구분하는 이중 밑줄 표준 구분자이다.
70+
- `<Action>` (동작): 스크립트가 수행하는 주요 DDL 동작을 대문자로 명시한다.
71+
- 예시: CREATE, ALTER, ADD, DROP, RENAME, INSERT
72+
- `<Object>` (객체): 변경이 일어나는 주된 객체의 이름으로, 테이블 이름을 뜻한다.
73+
- 예시: USERS, BOOKS, USER_BOOKS
74+
- `<Details>` (상세 설명): 변경 사항을 더 구체적으로 설명한다. 여러 단어는 밑줄(_)로 연결하고, 첫 글자만 대문자로 표기한다.
75+
- 예시: mail_Column, Add_Nickname, Fk_To_Users
76+
77+
| 상황 | 파일 이름 예시 |
78+
|------------------------------------|---------------------------------------------------------------------|
79+
| Users 테이블 생성 | `V20250820_001__Create_Users.sql` |
80+
| Books 테이블에 nickname 컬럼 추가 | `V20250820_002__Add_Books_Nickname.sql` |
81+
| Users 테이블의 nickname 컬럼 이름 변경 | `V20250821_001__Rename_Users_Nickname_To_Username.sql` |
82+
| User_books 테이블에 인덱스 추가 | `V20250821_002__Add_User_books_Index_On_UserId.sql` |
83+
| Books 테이블의 title 컬럼 타입 변경 | `V20250822_001__Alter_Books_Title_To_Varchar500.sql` |
84+
| 기본 Role 데이터 삽입 | `V20250822_002__Insert_Default_Roles.sql` |
85+
86+
### 2. 초기 데이터 관리 (seed)
87+
- 스크립트 위치는 infra 모듈의 resources/db/seed 디렉터리 구조로 관리한다.
88+
- `db/seed/common/`: 모든 환경(개발, 스테이징, 운영)에 필요한 필수 기본 데이터
89+
- `db/seed/dev/`: 개발 환경 전용 테스트 데이터 (향후 필요시 적용)
90+
- 스크립트는 `R__<Object>_<Data_Type>.sql` 형식으로 명명한다.
91+
- 구체적인 네이밍 규칙은 아래와 같다.
92+
- `R` (접두사): Repeatable Migration을 의미하는 Flyway의 표준 접두사로, 스크립트 내용이 변경될 때마다 다시 실행된다.
93+
- 예시: R
94+
- __ (구분자): 접두사와 설명을 구분하는 이중 밑줄 표준 구분자이다.
95+
- `<Object>` (객체): 데이터가 삽입되는 주된 테이블 이름을 소문자로 명시한다.
96+
- 예시: tags, roles, categories
97+
- `<Data_Type>` (데이터 유형): 삽입되는 데이터의 성격을 설명한다. 여러 단어는 밑줄(_)로 연결하고, 소문자로 표기한다.
98+
- 예시: default_data, master_data, essential_data
99+
100+
| 데이터 유형 | 파일 이름 예시 | 설명 |
101+
|------------------------------------|---------------------------------------------------|--------------------------------------------------|
102+
| 기본 태그 데이터 | `R__tags_default_data.sql` | 시스템에서 사용하는 기본 태그들 |
103+
| 필수 역할 데이터 | `R__roles_essential_data.sql` | USER, ADMIN 등 필수 사용자 역할 |
104+
| 카테고리 마스터 데이터 | `R__categories_master_data.sql` | 도서 분류 등 비즈니스 카테고리 |
105+
| 시스템 설정 데이터 | `R__system_config_default_data.sql` | 애플리케이션 기본 설정값들 |
106+
107+
#### 데이터 삽입 가이드라인
108+
- **기본 원칙**: 모든 초기 데이터는 R__ 스크립트를 사용하여 관리한다.
109+
- **중복 방지**: INSERT IGNORE 또는 ON DUPLICATE KEY UPDATE 구문을 활용하여 중복 삽입을 방지한다.
110+
- **성능 고려**: 대용량 데이터(만 건 이상)는 R__ 스크립트 사용을 지양하고, 별도 프로세스로 관리한다.

infra/src/main/kotlin/org/yapp/infra/book/entity/BookEntity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class BookEntity private constructor(
4141
var author: String = author
4242
protected set
4343

44-
@Column(length = 255)
44+
@Column(length = 300)
4545
var publisher: String = publisher
4646
protected set
4747

infra/src/main/kotlin/org/yapp/infra/userbook/entity/UserBookEntity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class UserBookEntity(
3434
@JdbcTypeCode(Types.VARCHAR)
3535
val bookId: UUID,
3636

37-
@Column(name = "book_isbn13", nullable = false)
37+
@Column(name = "book_isbn13", nullable = false, length = 13)
3838
val bookIsbn13: String,
3939

4040
coverImageUrl: String,

infra/src/main/resources/application-persistence.yml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ spring:
1717

1818
flyway:
1919
enabled: true
20-
baseline-on-migrate: true
21-
baseline-version: 0
22-
clean-disabled: true
20+
baseline-on-migrate: false
21+
validate-on-migrate: true
2322
locations:
24-
- classpath:db/migration
25-
- classpath:db/reed
23+
- classpath:db/migration/mysql
24+
- classpath:db/seed/common
2625

2726
---
2827
spring:
@@ -32,10 +31,14 @@ spring:
3231

3332
jpa:
3433
hibernate:
35-
ddl-auto: update
34+
ddl-auto: validate
3635

3736
flyway:
38-
enabled: false
37+
enabled: true
38+
baseline-on-migrate: false
39+
locations:
40+
- classpath:db/migration/mysql
41+
- classpath:db/seed/common
3942

4043
---
4144
spring:
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
CREATE TABLE users
2+
(
3+
id VARCHAR(36) NOT NULL,
4+
created_at datetime(6) NOT NULL,
5+
updated_at datetime(6) NOT NULL,
6+
deleted_at datetime(6) NULL,
7+
email VARCHAR(100) NOT NULL,
8+
provider_type ENUM ('APPLE', 'KAKAO') NOT NULL,
9+
provider_id VARCHAR(100) NOT NULL,
10+
nickname VARCHAR(100) NOT NULL,
11+
profile_image_url VARCHAR(255) NULL,
12+
`role` ENUM ('ADMIN', 'USER') NOT NULL,
13+
terms_agreed BIT(1) NOT NULL,
14+
apple_refresh_token VARCHAR(1024) NULL,
15+
CONSTRAINT pk_users PRIMARY KEY (id)
16+
);
17+
18+
CREATE TABLE books
19+
(
20+
id VARCHAR(36) NOT NULL,
21+
created_at datetime(6) NOT NULL,
22+
updated_at datetime(6) NOT NULL,
23+
deleted_at datetime(6) NULL,
24+
isbn13 VARCHAR(13) NOT NULL,
25+
title VARCHAR(255) NOT NULL,
26+
author VARCHAR(255) NULL,
27+
publisher VARCHAR(300) NULL,
28+
publication_year INT NULL,
29+
cover_image_url VARCHAR(2048) NULL,
30+
`description` VARCHAR(2000) NULL,
31+
CONSTRAINT pk_books PRIMARY KEY (id)
32+
);
33+
34+
CREATE TABLE user_books
35+
(
36+
id VARCHAR(36) NOT NULL,
37+
created_at datetime(6) NOT NULL,
38+
updated_at datetime(6) NOT NULL,
39+
deleted_at datetime(6) NULL,
40+
user_id VARCHAR(36) NOT NULL,
41+
book_id VARCHAR(36) NOT NULL,
42+
book_isbn13 VARCHAR(255) NOT NULL,
43+
cover_image_url VARCHAR(2048) NOT NULL,
44+
publisher VARCHAR(300) NOT NULL,
45+
title VARCHAR(255) NOT NULL,
46+
author VARCHAR(255) NULL,
47+
`status` ENUM ('BEFORE_READING', 'BEFORE_REGISTRATION', 'COMPLETED', 'READING') NOT NULL,
48+
reading_record_count INT NOT NULL,
49+
CONSTRAINT pk_user_books PRIMARY KEY (id)
50+
);
51+
52+
CREATE TABLE reading_records
53+
(
54+
id VARCHAR(36) NOT NULL,
55+
created_at datetime(6) NOT NULL,
56+
updated_at datetime(6) NOT NULL,
57+
deleted_at datetime(6) NULL,
58+
user_book_id VARCHAR(36) NOT NULL,
59+
page_number INT NOT NULL,
60+
quote VARCHAR(1000) NOT NULL,
61+
review VARCHAR(1000) NOT NULL,
62+
CONSTRAINT pk_reading_records PRIMARY KEY (id)
63+
);
64+
65+
CREATE TABLE tags
66+
(
67+
id VARCHAR(36) NOT NULL,
68+
created_at datetime(6) NOT NULL,
69+
updated_at datetime(6) NOT NULL,
70+
deleted_at datetime(6) NULL,
71+
name VARCHAR(10) NOT NULL,
72+
CONSTRAINT pk_tags PRIMARY KEY (id)
73+
);
74+
75+
CREATE TABLE reading_record_tags
76+
(
77+
id VARCHAR(36) NOT NULL,
78+
created_at datetime(6) NOT NULL,
79+
updated_at datetime(6) NOT NULL,
80+
deleted_at datetime(6) NULL,
81+
reading_record_id VARCHAR(36) NOT NULL,
82+
tag_id VARCHAR(36) NOT NULL,
83+
CONSTRAINT pk_reading_record_tags PRIMARY KEY (id)
84+
);
85+
86+
ALTER TABLE books
87+
ADD CONSTRAINT uc_books_isbn13 UNIQUE (isbn13);
88+
89+
ALTER TABLE tags
90+
ADD CONSTRAINT uc_tags_name UNIQUE (name);
91+
92+
CREATE INDEX idx_user_books_title ON user_books (title);
93+
94+
CREATE INDEX idx_user_books_user_id_title ON user_books (user_id, title);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE user_books
2+
MODIFY COLUMN book_isbn13 VARCHAR(13) NOT NULL;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
INSERT INTO tags (id, name, created_at, updated_at)
2+
VALUES ('0198c238-dd86-737c-af79-8875fe2290c1', '깨달음', NOW(6), NOW(6)),
3+
('0198c238-dd86-737c-af79-8875fe2290c2', '슬픔', NOW(6), NOW(6)),
4+
('0198c238-dd86-737c-af79-8875fe2290c3', '즐거움', NOW(6), NOW(6)),
5+
('0198c238-dd86-737c-af79-8875fe2290c4', '따뜻함', NOW(6), NOW(6))
6+
ON DUPLICATE KEY UPDATE name = VALUES(name),
7+
updated_at = NOW(6);

0 commit comments

Comments
 (0)