diff --git a/build.gradle b/build.gradle index 2acb55c..a0ae3b5 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ plugins { id 'org.springframework.boot' version '3.5.0' id 'io.spring.dependency-management' version '1.1.7' id 'com.diffplug.spotless' version '6.21.0' + id 'org.flywaydb.flyway' version '11.11.2' } group = 'com.ject' @@ -32,7 +33,6 @@ dependencies { implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0") compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' - runtimeOnly 'com.mysql:mysql-connector-j' // Validation implementation 'org.springframework.boot:spring-boot-starter-validation' @@ -54,6 +54,11 @@ dependencies { // Redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' + // Flyway (MySQL) + implementation "org.flywaydb:flyway-core" + implementation 'org.flywaydb:flyway-mysql' + runtimeOnly 'com.mysql:mysql-connector-j' + // Firebase-admin implementation 'com.google.firebase:firebase-admin:9.2.0' diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index ab39ea0..72553e9 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -5,7 +5,15 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: none # 스키마 변경 책임은 전부 Flyway로. Hibernate는 생성/수정 금지 + open-in-view: false + + flyway: + enabled: true # Flyway 켜기 + locations: classpath:db/migration # 마이그레이션 SQL 파일 위치(V1, V2, V3 등 여기서 읽음) + validate-on-migrate: true # 마이그레이션 전체 검증 + fail-on-missing-locations: true # 경로 누락 시 실패 + clean-disabled: true # Flyway Clean(스키마 삭제) 금지 logging: level: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index f4679f7..388a36d 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -5,14 +5,20 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: none # 스키마 변경 책임은 전부 Flyway로. Hibernate는 생성/수정 금지 show-sql: true + open-in-view: false properties: hibernate: format_sql: true dialect: org.hibernate.dialect.MySQL8Dialect - defer-datasource-initialization: true - open-in-view: false + + flyway: + enabled: true # Flyway 켜기 + locations: classpath:db/migration # 마이그레이션 SQL 파일 위치(V1, V2, V3 등 여기서 읽음) + validate-on-migrate: true # 마이그레이션 전체 검증 + fail-on-missing-locations: true # 경로 누락 시 실패 + clean-disabled: false logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 940b53c..32a60c7 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -5,4 +5,12 @@ spring: jpa: hibernate: - ddl-auto: none + ddl-auto: none # 스키마 변경 책임은 전부 Flyway로. Hibernate는 생성/수정 금지 + open-in-view: false + + flyway: + enabled: true # Flyway 켜기 + locations: classpath:db/migration # 마이그레이션 SQL 파일 위치(V1, V2, V3 등 여기서 읽음) + validate-on-migrate: true # 마이그레이션 전체 검증 + fail-on-missing-locations: true # 경로 누락 시 실패 + clean-disabled: true # Flyway Clean(스키마 삭제) 금지 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 339ca79..e253547 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,6 +5,10 @@ spring: - redis - security + sql: + init: + mode: never + server: forward-headers-strategy: framework diff --git a/src/main/resources/db/migration/V1__create_tables.sql b/src/main/resources/db/migration/V1__create_tables.sql new file mode 100644 index 0000000..0147971 --- /dev/null +++ b/src/main/resources/db/migration/V1__create_tables.sql @@ -0,0 +1,201 @@ +CREATE TABLE member ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- 도메인 필드 + social_provider ENUM('KAKAO','GOOGLE') NOT NULL, + social_id VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + nickname VARCHAR(50) NOT NULL, + profile_image VARCHAR(512) NULL, + category ENUM('STUDENT','WORKER','FREELANCER','JOBSEEKER') NOT NULL, + role ENUM('ROLE_USER','ROLE_ADMIN','ROLE_OWNER') NOT NULL, + + -- Auditing + created_at DATETIME(6) NOT NULL, + updated_at DATETIME(6) NOT NULL, + deleted_at DATETIME(6) NULL +); + +CREATE TABLE trip ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- 도메인 필드 + name VARCHAR(255) NOT NULL, + memo VARCHAR(255) NULL, + start_date DATE NOT NULL, + end_date DATE NULL, + total_stamps INT NULL, + completed_stamps INT NULL, + completed BOOLEAN NOT NULL, + category ENUM('COURSE', 'EXPLORE') NOT NULL, + + -- 외래키 필드 + member_id BIGINT NOT NULL, + + -- Auditing + created_at DATETIME(6) NOT NULL, + updated_at DATETIME(6) NOT NULL, + deleted_at DATETIME(6) NULL, + + -- 외래키 제약조건: trip.member_id → member.id + FOREIGN KEY (member_id) REFERENCES member (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT +); + +CREATE TABLE stamp ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- 도메인 필드 + name VARCHAR(255) NOT NULL, + stamp_order INT NULL, + completed BOOLEAN NOT NULL, + + -- 외래키 필드 + trip_id BIGINT NOT NULL, + + -- Auditing + created_at DATETIME(6) NOT NULL, + updated_at DATETIME(6) NOT NULL, + deleted_at DATETIME(6) NULL, + + -- 외래키 제약조건: stamp.trip_id → trip.id + FOREIGN KEY (trip_id) REFERENCES trip (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT +); + +CREATE TABLE mission ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- 도메인 필드 + name VARCHAR(255) NOT NULL, + completed BOOLEAN NOT NULL, + + -- 외래키 필드 + stamp_id BIGINT NOT NULL, + + -- Auditing + created_at DATETIME(6) NOT NULL, + updated_at DATETIME(6) NOT NULL, + deleted_at DATETIME(6) NULL, + + -- 외래키 제약조건: mission.stamp_id → stamp.id + FOREIGN KEY (stamp_id) REFERENCES stamp (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT +); + +CREATE TABLE daily_goal ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- 도메인 필드 + title VARCHAR(255) NOT NULL, + completed BOOLEAN NOT NULL, + + -- 외래키 필드 + trip_id BIGINT NOT NULL, + + -- Auditing + created_at DATETIME(6) NOT NULL, + updated_at DATETIME(6) NOT NULL, + deleted_at DATETIME(6) NULL, + + -- 외래키 제약조건: stamp.trip_id → trip.id + FOREIGN KEY (trip_id) REFERENCES trip (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT +); + +CREATE TABLE study_log ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- 도메인 필드 + title VARCHAR(255) NOT NULL, + content VARCHAR(255) NOT NULL, + + -- 외래키 필드 + member_id BIGINT NOT NULL, + daily_goal_id BIGINT NOT NULL, + + -- Auditing + created_at DATETIME(6) NOT NULL, + updated_at DATETIME(6) NOT NULL, + deleted_at DATETIME(6) NULL, + + -- 외래키 제약조건 + FOREIGN KEY (member_id) REFERENCES member (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT, + + FOREIGN KEY (daily_goal_id) REFERENCES daily_goal (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT +); + +CREATE TABLE daily_mission ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- 외래키 필드 + mission_id BIGINT NOT NULL, + daily_goal_id BIGINT NOT NULL, + + -- Auditing + created_at DATETIME(6) NOT NULL, + updated_at DATETIME(6) NOT NULL, + deleted_at DATETIME(6) NULL, + + -- 외래키 제약조건 + FOREIGN KEY (mission_id) REFERENCES mission (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT, + + FOREIGN KEY (daily_goal_id) REFERENCES daily_goal (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT +); + +CREATE TABLE study_log_daily_mission ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- 외래키 필드 + study_log_id BIGINT NOT NULL, + daily_mission_id BIGINT NOT NULL, + + -- Auditing + created_at DATETIME(6) NOT NULL, + updated_at DATETIME(6) NOT NULL, + deleted_at DATETIME(6) NULL, + + -- 외래키 제약조건 + FOREIGN KEY (study_log_id) REFERENCES study_log (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT, + + FOREIGN KEY (daily_mission_id) REFERENCES daily_mission (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT +); + +CREATE TABLE pomodoro ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- 도메인 필드 + focus_duration_in_seconds INT NULL, + focus_session_count INT NULL, + break_duration_in_seconds INT NULL, + total_focus_time_in_seconds INT NULL, + + -- 외래키 필드 + daily_goal_id BIGINT NOT NULL, + + -- Auditing + created_at DATETIME(6) NOT NULL, + updated_at DATETIME(6) NOT NULL, + deleted_at DATETIME(6) NULL, + + -- 외래키 제약조건 + FOREIGN KEY (daily_goal_id) REFERENCES daily_goal (id) + ON UPDATE RESTRICT + ON DELETE RESTRICT +); diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index 85d0292..acd769a 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -5,9 +5,17 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: none # 스키마 변경 책임은 전부 Flyway로. Hibernate는 생성/수정 금지 show-sql: true + open-in-view: false properties: hibernate: format_sql: true dialect: org.hibernate.dialect.MySQL8Dialect + + flyway: + enabled: true # Flyway 켜기 + locations: classpath:db/migration # 마이그레이션 SQL 파일 위치(V1, V2, V3 등 여기서 읽음) + validate-on-migrate: true # 마이그레이션 전체 검증 + fail-on-missing-locations: true # 경로 누락 시 실패 + clean-disabled: false