From 244712f6c5c6ba95cf57e20f3b8967ca59e36a86 Mon Sep 17 00:00:00 2001 From: Eunseo Song <162149585+eunseo9311@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:31:08 +0900 Subject: [PATCH 1/6] =?UTF-8?q?chore:=20dev=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EB=A7=8C=EB=A3=8C=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=9B=90=EB=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e201296e..c328707f 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -41,8 +41,8 @@ spring: jwt: secret: key: ${EATSSU_JWT_SECRET_DEV} - token-validity-in-seconds: 60 - refresh-token-validity-in-seconds: 180 + token-validity-in-seconds: 86400 + refresh-token-validity-in-seconds: 604800 #S3 cloud: From 92fd5752fd0e312afc25d2ef6fd7d70d06ac7fbc Mon Sep 17 00:00:00 2001 From: Eunseo Song <162149585+eunseo9311@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:05:50 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20DB=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - V1: 베이스라인 스키마 정의 - V2: PK/FK 타입 정렬 (int → bigint) - V3: varchar → enum 타입 변환 - V4: 제약조건 및 기본값 추가 - 검증 SQL 및 마이그레이션 가이드 문서 추가 --- MIGRATION_GUIDE.md | 208 +++++++++++++++++ check_fk_constraints.sql | 31 +++ run_validation.sh | 44 ++++ .../resources/db/migration/V1__baseline.sql | 212 ++++++++++++++++++ .../migration/V2__align_pk_and_fk_types.sql | 38 ++++ .../migration/V3__convert_varchar_to_enum.sql | 52 +++++ .../V4__add_constraints_and_defaults.sql | 37 +++ validate_enum_data.sql | 80 +++++++ validate_simple.sql | 49 ++++ 9 files changed, 751 insertions(+) create mode 100644 MIGRATION_GUIDE.md create mode 100644 check_fk_constraints.sql create mode 100755 run_validation.sh create mode 100644 src/main/resources/db/migration/V1__baseline.sql create mode 100644 src/main/resources/db/migration/V2__align_pk_and_fk_types.sql create mode 100644 src/main/resources/db/migration/V3__convert_varchar_to_enum.sql create mode 100644 src/main/resources/db/migration/V4__add_constraints_and_defaults.sql create mode 100644 validate_enum_data.sql create mode 100644 validate_simple.sql diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md new file mode 100644 index 00000000..b2de3946 --- /dev/null +++ b/MIGRATION_GUIDE.md @@ -0,0 +1,208 @@ +# DB 마이그레이션 실행 가이드 + +## 📁 마이그레이션 파일 목록 + +``` +src/main/resources/db/migration/ +├── V2__align_pk_and_fk_types.sql (PK/FK bigint 통일) +├── V3__convert_varchar_to_enum.sql (varchar → enum 변환) +└── V4__add_constraints_and_defaults.sql (제약조건 및 기본값) +``` + +## 🚀 실행 순서 + +### 1️⃣ 사전 준비 (필수) + +#### ✅ 스냅샷 확인 +- [ ] Dev DB 스냅샷 생성 완료 +- [ ] Prod DB 스냅샷 생성 완료 (실제 배포 전) + +#### ✅ 데이터 검증 (필수) +```bash +# varchar 데이터가 Enum과 일치하는지 검증 +./run_validation.sh +``` + +**중요:** 검증 결과가 비어있어야 정상 (불일치 데이터 없음) + +#### ✅ FK 제약조건명 확인 (선택) +```bash +# department, partnership의 FK 이름 확인 +mysql -h -u -p < check_fk_constraints.sql +``` + +--- + +### 2️⃣ Dev 환경 마이그레이션 + +#### 방법 1: Flyway 자동 실행 (권장) +```bash +# application-dev.yml의 spring.flyway 설정 확인 +# spring.jpa.hibernate.ddl-auto: none 확인 + +# 애플리케이션 실행 시 자동으로 마이그레이션 실행 +./gradlew bootRun --args='--spring.profiles.active=dev' +``` + +#### 방법 2: 수동 실행 +```bash +# V2 실행 +mysql -h -u -p < src/main/resources/db/migration/V2__align_pk_and_fk_types.sql + +# 검증: PK/FK 타입 확인 +mysql -h -u -p -e " + SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = '' + AND COLUMN_NAME IN ('college_id', 'department_id') + ORDER BY TABLE_NAME; +" + +# V3 실행 +mysql -h -u -p < src/main/resources/db/migration/V3__convert_varchar_to_enum.sql + +# 검증: Enum 타입 확인 +mysql -h -u -p -e " + SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = '' + AND DATA_TYPE = 'enum' + ORDER BY TABLE_NAME; +" + +# V4 실행 +mysql -h -u -p < src/main/resources/db/migration/V4__add_constraints_and_defaults.sql + +# 검증: 제약조건 확인 +mysql -h -u -p -e " + SHOW CREATE TABLE college; + SHOW CREATE TABLE menu; + SHOW CREATE TABLE review; +" +``` + +--- + +### 3️⃣ 마이그레이션 검증 + +#### 애플리케이션 테스트 +```bash +# 애플리케이션 재시작 +./gradlew bootRun --args='--spring.profiles.active=dev' + +# 로그 확인 +# - Enum 매핑 오류 없는지 +# - DB 연결 정상인지 +# - API 호출 정상인지 +``` + +#### DB 데이터 확인 +```sql +-- Enum 값 조회 테스트 +SELECT restaurant, time_part FROM meal LIMIT 10; +SELECT provider, role, status FROM user LIMIT 10; + +-- PK/FK 타입 확인 +SELECT + TABLE_NAME, + COLUMN_NAME, + DATA_TYPE, + COLUMN_TYPE +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = '' + AND COLUMN_NAME IN ('college_id', 'department_id') +ORDER BY TABLE_NAME, COLUMN_NAME; + +-- 제약조건 확인 +SELECT + TABLE_NAME, + CONSTRAINT_NAME, + CONSTRAINT_TYPE +FROM information_schema.TABLE_CONSTRAINTS +WHERE TABLE_SCHEMA = '' + AND TABLE_NAME IN ('college', 'menu', 'review'); +``` + +--- + +### 4️⃣ Prod 환경 마이그레이션 + +#### 실행 전 체크리스트 +- [ ] Dev 환경 마이그레이션 완료 및 검증 +- [ ] Dev 환경 애플리케이션 정상 동작 확인 +- [ ] Prod DB 스냅샷 생성 완료 +- [ ] 데이터 검증 (validate_enum_data.sql) 완료 +- [ ] 사용량이 적은 시간대 선택 (새벽 2-4시 권장) +- [ ] 롤백 계획 수립 + +#### 실행 +```bash +# Prod 환경에서 동일하게 실행 +./gradlew bootRun --args='--spring.profiles.active=prod' + +# 또는 수동 실행 +mysql -h -u -p < V2__align_pk_and_fk_types.sql +mysql -h -u -p < V3__convert_varchar_to_enum.sql +mysql -h -u -p < V4__add_constraints_and_defaults.sql +``` + +--- + +## ⚠️ 주의사항 + +### 1. metadata lock 방지 +- 사용량이 적은 시간대에 실행 +- 실행 중 다른 세션에서 DDL 작업 금지 + +### 2. 데이터 검증 필수 +```bash +# V3 실행 전 반드시 검증 +./run_validation.sh +``` + +### 3. FK 제약조건 처리 +- V2에서 `SET FOREIGN_KEY_CHECKS = 0/1` 사용 +- FK 제약조건은 자동으로 유지됨 + +### 4. 롤백 방법 +```bash +# AWS RDS 스냅샷으로 복원 +aws rds restore-db-instance-from-db-snapshot \ + --db-instance-identifier your-db-restored \ + --db-snapshot-identifier eatssu-dev-pre-migration-20260207 +``` + +--- + +## 🐛 문제 발생 시 + +### 1. Enum 변환 실패 +``` +ERROR 1265: Data truncated for column 'restaurant' at row 1 +``` +**원인:** DB에 Enum에 없는 값이 존재 +**해결:** `validate_enum_data.sql` 실행하여 불일치 데이터 찾기 + +### 2. PK 타입 변경 실패 +``` +ERROR 1833: Cannot change column: used in a foreign key constraint +``` +**원인:** FK 제약조건이 걸려있음 +**해결:** V2 파일에 `SET FOREIGN_KEY_CHECKS = 0` 이미 포함됨 + +### 3. unique 제약조건 실패 +``` +ERROR 1062: Duplicate entry for key 'UK_college_name' +``` +**원인:** college.name에 중복 데이터 존재 +**해결:** 중복 데이터 정리 후 다시 실행 + +--- + +## ✅ 완료 확인 + +- [ ] V2, V3, V4 모두 정상 실행 +- [ ] flyway_schema_history 테이블에 기록 확인 +- [ ] 애플리케이션 정상 동작 확인 +- [ ] API 테스트 통과 +- [ ] 로그에 오류 없음 diff --git a/check_fk_constraints.sql b/check_fk_constraints.sql new file mode 100644 index 00000000..b8eb04b1 --- /dev/null +++ b/check_fk_constraints.sql @@ -0,0 +1,31 @@ +-- ========================= +-- FK 제약조건명 확인 +-- ========================= +-- 실행 방법: mysql -h -u -p < check_fk_constraints.sql + +-- department, partnership 테이블의 FK 제약조건 확인 +SELECT + CONSTRAINT_NAME AS 'FK 이름', + TABLE_NAME AS '테이블', + COLUMN_NAME AS '컬럼', + REFERENCED_TABLE_NAME AS '참조 테이블', + REFERENCED_COLUMN_NAME AS '참조 컬럼' +FROM information_schema.KEY_COLUMN_USAGE +WHERE TABLE_SCHEMA = DATABASE() + AND REFERENCED_TABLE_NAME IS NOT NULL + AND TABLE_NAME IN ('department', 'partnership') +ORDER BY TABLE_NAME, CONSTRAINT_NAME; + +-- ========================= +-- 전체 FK 제약조건 확인 (참고용) +-- ========================= +SELECT + TABLE_NAME AS '테이블', + CONSTRAINT_NAME AS 'FK 이름', + COLUMN_NAME AS '컬럼', + REFERENCED_TABLE_NAME AS '참조 테이블', + REFERENCED_COLUMN_NAME AS '참조 컬럼' +FROM information_schema.KEY_COLUMN_USAGE +WHERE TABLE_SCHEMA = DATABASE() + AND REFERENCED_TABLE_NAME IS NOT NULL +ORDER BY TABLE_NAME, CONSTRAINT_NAME; diff --git a/run_validation.sh b/run_validation.sh new file mode 100755 index 00000000..cc14d6be --- /dev/null +++ b/run_validation.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# ========================= +# Enum 데이터 검증 스크립트 +# ========================= + +echo "==========================================" +echo "Enum 데이터 검증 시작" +echo "==========================================" +echo "" + +# 사용자에게 환경 선택 +read -p "검증할 환경을 선택하세요 (dev/prod): " ENV + +if [ "$ENV" != "dev" ] && [ "$ENV" != "prod" ]; then + echo "오류: 'dev' 또는 'prod'만 입력 가능합니다." + exit 1 +fi + +# DB 접속 정보 입력 +read -p "DB 호스트를 입력하세요: " DB_HOST +read -p "DB 사용자명을 입력하세요: " DB_USER +read -sp "DB 비밀번호를 입력하세요: " DB_PASS +echo "" +read -p "DB 이름을 입력하세요: " DB_NAME + +echo "" +echo "[$ENV 환경] 데이터 검증을 시작합니다..." +echo "" + +# 검증 쿼리 실행 +mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" < validate_enum_data.sql > validation_result_${ENV}_$(date +%Y%m%d_%H%M%S).txt 2>&1 + +if [ $? -eq 0 ]; then + echo "✅ 검증 완료! 결과 파일을 확인하세요: validation_result_${ENV}_$(date +%Y%m%d_%H%M%S).txt" + echo "" + echo "⚠️ 중요: 결과 파일에서 아래 내용을 확인하세요" + echo " - 불일치 데이터가 있는 경우: 데이터 정리 후 다시 검증" + echo " - 결과가 비어있는 경우: varchar → enum 변환 진행 가능" + echo "" +else + echo "❌ 검증 실패. 접속 정보를 확인하세요." + exit 1 +fi diff --git a/src/main/resources/db/migration/V1__baseline.sql b/src/main/resources/db/migration/V1__baseline.sql new file mode 100644 index 00000000..3c7a8d25 --- /dev/null +++ b/src/main/resources/db/migration/V1__baseline.sql @@ -0,0 +1,212 @@ +create table college +( + college_id bigint auto_increment + primary key, + name varchar(255) not null, + constraint UK_nyc2rxbj71rdhcw055436agb5 + unique (name) +); + +create table department +( + department_id bigint auto_increment + primary key, + name varchar(255) not null, + college_id bigint null, + constraint FK6p7527hkl1k5a92k34d8yye97 + foreign key (college_id) references college (college_id) +); + +create table meal +( + meal_id bigint auto_increment + primary key, + date date null, + price int null, + restaurant enum ('DODAM', 'DORMITORY', 'FOOD_COURT', 'SNACK_CORNER', 'HAKSIK', 'FACULTY') null, + time_part enum ('MORNING', 'LUNCH', 'DINNER') null +); + +create table menu_category +( + menu_category_id bigint auto_increment + primary key, + name varchar(255) null, + restaurant enum ('DODAM', 'DORMITORY', 'FOOD_COURT', 'SNACK_CORNER', 'HAKSIK', 'FACULTY') null +); + +create table menu +( + menu_id bigint auto_increment + primary key, + is_discontinued bit null, + like_count int null, + name varchar(255) null, + price int null, + restaurant enum ('DODAM', 'DORMITORY', 'FOOD_COURT', 'SNACK_CORNER', 'HAKSIK', 'FACULTY') null, + unlike_count int null, + menu_category_id bigint null, + constraint FKpyeafaiqn171tt9u4atxl9de7 + foreign key (menu_category_id) references menu_category (menu_category_id) +); + +create table meal_menu +( + meal_menu_id bigint auto_increment + primary key, + meal_id bigint null, + menu_id bigint null, + constraint FK4csi2rxcc80koaiybdjkpfntx + foreign key (meal_id) references meal (meal_id), + constraint FK5ywj42p0hun7ifyfd3p3pxxnt + foreign key (menu_id) references menu (menu_id) +); + +create table partnership_restaurant +( + partnership_restaurant_id bigint auto_increment + primary key, + latitude double not null, + longitude double not null, + restaurant_type enum ('RESTAURANT', 'CAFE', 'PUB') not null, + store_name varchar(255) not null +); + +create table partnership +( + partnership_id bigint auto_increment + primary key, + description varchar(255) not null, + end_date date not null, + start_date date not null, + partnership_restaurant_id bigint null, + partnership_college_id bigint null, + partnership_department_id bigint null, + constraint FK3we90emq62kx2w4qvcje88soq + foreign key (partnership_restaurant_id) references partnership_restaurant (partnership_restaurant_id), + constraint FKf71ovavgpo8nn0eusnwfxi58b + foreign key (partnership_department_id) references department (department_id), + constraint FKp5qgfntyy03gl0n2t4t9bar7 + foreign key (partnership_college_id) references college (college_id) +); + +create table user +( + user_id bigint auto_increment + primary key, + created_date datetime(6) not null, + modified_date datetime(6) not null, + credentials varchar(255) null, + email varchar(255) null, + nickname varchar(255) null, + provider enum ('EATSSU', 'KAKAO', 'APPLE') null, + provider_id varchar(255) null, + role enum ('USER', 'ADMIN') null, + status enum ('ACTIVE', 'INACTIVE') null, + department_id bigint null, + device_type enum ('IOS', 'ANDROID') null, + constraint UK_ob8kqyqqgmefl0aco34akdtpe + unique (email), + constraint FKgkh2fko1e4ydv1y6vtrwdc6my + foreign key (department_id) references department (department_id) +); + +create table inquiry +( + user_inquiry_id bigint auto_increment + primary key, + created_date datetime(6) not null, + modified_date datetime(6) not null, + content varchar(255) null, + email varchar(255) null, + status enum ('WAITING', 'ANSWERED', 'HOLD') null, + user_id bigint null, + constraint FKff1ylwlwujmed7diqs8ykf6pv + foreign key (user_id) references user (user_id) +); + +create table partnership_like +( + partnership_like_id bigint auto_increment + primary key, + partnership_restaurant_id bigint null, + user_id bigint null, + constraint FKcwju0bkiwim9w4lc9v27pkdit + foreign key (user_id) references user (user_id), + constraint FKhb06n2mees4p3gxqqrj414q3u + foreign key (partnership_restaurant_id) references partnership_restaurant (partnership_restaurant_id) +); + +create table review +( + review_id bigint auto_increment + primary key, + created_date datetime(6) not null, + modified_date datetime(6) not null, + content varchar(300) null, + rating int null, + amount_rating int null, + main_rating int null, + taste_rating int null, + meal_id bigint null, + menu_id bigint null, + user_id bigint null, + constraint FKbsl9qcmq2sb7otlbp6ibmmwer + foreign key (meal_id) references meal (meal_id), + constraint FKiyf57dy48lyiftdrf7y87rnxi + foreign key (user_id) references user (user_id), + constraint FKkythy7xd59wvq6hwhv23xh7gw + foreign key (menu_id) references menu (menu_id) +); + +create table report +( + review_report_id bigint auto_increment + primary key, + created_date datetime(6) not null, + modified_date datetime(6) not null, + content varchar(255) null, + report_type enum ('NO_ASSOCIATE_CONTENT', 'IMPROPER_CONTENT', 'IMPROPER_ADVERTISEMENT', 'COPY', 'COPYRIGHT', 'EXTRA') null, + status enum ('PENDING', 'IN_PROGRESS', 'RESOLVED', 'REJECTED', 'FALSE_REPORT') null, + review_id bigint null, + reporter_id bigint null, + constraint FKmcui10qh03nnf6h3glch6pvmy + foreign key (review_id) references review (review_id), + constraint FKndpjl61ubcm2tkf7ml1ynq13t + foreign key (reporter_id) references user (user_id) +); + +create table review_image +( + review_image_id bigint auto_increment + primary key, + image_url varchar(255) null, + review_id bigint null, + constraint FK16wp089tx9nm0obc217gvdd6l + foreign key (review_id) references review (review_id) +); + +create table review_like +( + review_like_id bigint auto_increment + primary key, + review_id bigint null, + user_id bigint null, + constraint FK68am9vk1s1e8n1v873meqkk0k + foreign key (review_id) references review (review_id), + constraint FKq4l36vpqal6v4ehikh67g8e49 + foreign key (user_id) references user (user_id) +); + +create table review_menu_like +( + review_menu_like_id bigint auto_increment + primary key, + is_like bit not null, + menu_id bigint null, + review_id bigint null, + constraint FK6ey698wnlhnv3xtn2r60kt3jw + foreign key (review_id) references review (review_id), + constraint FKo33fcdj516ypc6vjy5kamu2ck + foreign key (menu_id) references menu (menu_id) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V2__align_pk_and_fk_types.sql b/src/main/resources/db/migration/V2__align_pk_and_fk_types.sql new file mode 100644 index 00000000..89c1aa1e --- /dev/null +++ b/src/main/resources/db/migration/V2__align_pk_and_fk_types.sql @@ -0,0 +1,38 @@ +-- ========================= +-- V2: PK/FK 타입 정렬 (int → bigint) +-- ========================= +-- 실행 순서: +-- 1. FK 제약조건 해제 +-- 2. PK 타입 변경 +-- 3. FK 컬럼 타입 변경 +-- 4. FK 제약조건 재생성 +-- ========================= + +SET FOREIGN_KEY_CHECKS = 0; + +-- ========================= +-- 1. PK 타입 변경: int → bigint +-- ========================= + +ALTER TABLE college + MODIFY college_id BIGINT NOT NULL AUTO_INCREMENT; + +ALTER TABLE department + MODIFY department_id BIGINT NOT NULL AUTO_INCREMENT; + +-- ========================= +-- 2. FK 컬럼 타입 변경: int → bigint +-- ========================= + +ALTER TABLE department + MODIFY college_id BIGINT NOT NULL; + +ALTER TABLE partnership + MODIFY partnership_college_id BIGINT, + MODIFY partnership_department_id BIGINT; + +-- ========================= +-- 3. FK 제약조건 복구 +-- ========================= + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/main/resources/db/migration/V3__convert_varchar_to_enum.sql b/src/main/resources/db/migration/V3__convert_varchar_to_enum.sql new file mode 100644 index 00000000..150791cd --- /dev/null +++ b/src/main/resources/db/migration/V3__convert_varchar_to_enum.sql @@ -0,0 +1,52 @@ +-- ========================= +-- V3: varchar → enum 변환 +-- ========================= +-- ⚠️ 주의: 이 마이그레이션 실행 전 반드시 데이터 검증 필요 +-- 실행: ./run_validation.sh +-- ========================= + +-- ========================= +-- meal 테이블 +-- ========================= + +ALTER TABLE meal + MODIFY restaurant ENUM ('DODAM','DORMITORY','FOOD_COURT','SNACK_CORNER','HAKSIK','FACULTY'), + MODIFY time_part ENUM ('MORNING','LUNCH','DINNER'); + +-- ========================= +-- menu_category 테이블 +-- ========================= + +ALTER TABLE menu_category + MODIFY restaurant ENUM ('DODAM','DORMITORY','FOOD_COURT','SNACK_CORNER','HAKSIK','FACULTY'); + +-- ========================= +-- menu 테이블 +-- ========================= + +ALTER TABLE menu + MODIFY restaurant ENUM ('DODAM','DORMITORY','FOOD_COURT','SNACK_CORNER','HAKSIK','FACULTY'); + +-- ========================= +-- user 테이블 +-- ========================= + +ALTER TABLE user + MODIFY provider ENUM ('EATSSU','KAKAO','APPLE'), + MODIFY role ENUM ('USER','ADMIN'), + MODIFY status ENUM ('ACTIVE','INACTIVE'); + +-- ========================= +-- inquiry 테이블 +-- ========================= + +ALTER TABLE inquiry + MODIFY status ENUM ('WAITING','ANSWERED','HOLD'); + +-- ========================= +-- report 테이블 +-- ========================= + +ALTER TABLE report + MODIFY report_type ENUM ('NO_ASSOCIATE_CONTENT','IMPROPER_CONTENT','IMPROPER_ADVERTISEMENT','COPY','COPYRIGHT','EXTRA'), + MODIFY status ENUM ('PENDING','IN_PROGRESS','RESOLVED','REJECTED','FALSE_REPORT'); diff --git a/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql b/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql new file mode 100644 index 00000000..443ea3d5 --- /dev/null +++ b/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql @@ -0,0 +1,37 @@ +-- ========================= +-- V4: 제약조건 및 기본값 정렬 +-- ========================= +-- 1. NOT NULL 제약조건 +-- 2. default 값 +-- 3. rating 타입 통일 +-- 4. unique 제약조건 +-- ========================= + +-- ========================= +-- 1. NOT NULL 제약조건 +-- ========================= + +ALTER TABLE menu + MODIFY is_discontinued BIT NOT NULL; + +-- ========================= +-- 2. default 값 설정 +-- ========================= + +ALTER TABLE menu + MODIFY like_count INT DEFAULT 0, + MODIFY unlike_count INT DEFAULT 0; + +-- ========================= +-- 3. rating 타입 통일 (bigint → int) +-- ========================= + +ALTER TABLE review + MODIFY rating INT; + +-- ========================= +-- 4. unique 제약조건 추가 +-- ========================= + +ALTER TABLE college + ADD CONSTRAINT UK_college_name UNIQUE (name); \ No newline at end of file diff --git a/validate_enum_data.sql b/validate_enum_data.sql new file mode 100644 index 00000000..f5a78a38 --- /dev/null +++ b/validate_enum_data.sql @@ -0,0 +1,80 @@ +-- ========================= +-- varchar → enum 변환 전 데이터 검증 SQL +-- ========================= + +-- meal.restaurant 검증 (허용값: DODAM, DORMITORY, FOOD_COURT, SNACK_CORNER, HAKSIK, FACULTY) +SELECT DISTINCT restaurant, COUNT(*) as count +FROM meal +WHERE restaurant NOT IN ('DODAM','DORMITORY','FOOD_COURT','SNACK_CORNER','HAKSIK','FACULTY') +GROUP BY restaurant; + +-- meal.time_part 검증 (허용값: MORNING, LUNCH, DINNER) +SELECT DISTINCT time_part, COUNT(*) as count +FROM meal +WHERE time_part NOT IN ('MORNING','LUNCH','DINNER') +GROUP BY time_part; + +-- menu_category.restaurant 검증 +SELECT DISTINCT restaurant, COUNT(*) as count +FROM menu_category +WHERE restaurant NOT IN ('DODAM','DORMITORY','FOOD_COURT','SNACK_CORNER','HAKSIK','FACULTY') +GROUP BY restaurant; + +-- menu.restaurant 검증 +SELECT DISTINCT restaurant, COUNT(*) as count +FROM menu +WHERE restaurant NOT IN ('DODAM','DORMITORY','FOOD_COURT','SNACK_CORNER','HAKSIK','FACULTY') +GROUP BY restaurant; + +-- user.provider 검증 (허용값: EATSSU, KAKAO, APPLE) +SELECT DISTINCT provider, COUNT(*) as count +FROM user +WHERE provider NOT IN ('EATSSU','KAKAO','APPLE') +GROUP BY provider; + +-- user.role 검증 (허용값: USER, ADMIN) +SELECT DISTINCT role, COUNT(*) as count +FROM user +WHERE role NOT IN ('USER','ADMIN') +GROUP BY role; + +-- user.status 검증 (허용값: ACTIVE, INACTIVE) +SELECT DISTINCT status, COUNT(*) as count +FROM user +WHERE status NOT IN ('ACTIVE','INACTIVE') +GROUP BY status; + +-- inquiry.status 검증 (허용값: WAITING, ANSWERED, HOLD) +SELECT DISTINCT status, COUNT(*) as count +FROM inquiry +WHERE status NOT IN ('WAITING','ANSWERED','HOLD') +GROUP BY status; + +-- report.report_type 검증 (허용값: NO_ASSOCIATE_CONTENT, IMPROPER_CONTENT, IMPROPER_ADVERTISEMENT, COPY, COPYRIGHT, EXTRA) +SELECT DISTINCT report_type, COUNT(*) as count +FROM report +WHERE report_type NOT IN ('NO_ASSOCIATE_CONTENT','IMPROPER_CONTENT','IMPROPER_ADVERTISEMENT','COPY','COPYRIGHT','EXTRA') +GROUP BY report_type; + +-- report.status 검증 (허용값: PENDING, IN_PROGRESS, RESOLVED, REJECTED, FALSE_REPORT) +SELECT DISTINCT status, COUNT(*) as count +FROM report +WHERE status NOT IN ('PENDING','IN_PROGRESS','RESOLVED','REJECTED','FALSE_REPORT') +GROUP BY status; + +-- ========================= +-- 전체 데이터 분포 확인 +-- ========================= + +SELECT 'meal.restaurant' as table_column, restaurant as value, COUNT(*) as count FROM meal GROUP BY restaurant +UNION ALL +SELECT 'meal.time_part', time_part, COUNT(*) FROM meal GROUP BY time_part +UNION ALL +SELECT 'menu.restaurant', restaurant, COUNT(*) FROM menu GROUP BY restaurant +UNION ALL +SELECT 'user.provider', provider, COUNT(*) FROM user GROUP BY provider +UNION ALL +SELECT 'user.role', role, COUNT(*) FROM user GROUP BY role +UNION ALL +SELECT 'user.status', status, COUNT(*) FROM user GROUP BY status +ORDER BY table_column, value; \ No newline at end of file diff --git a/validate_simple.sql b/validate_simple.sql new file mode 100644 index 00000000..cc94c02e --- /dev/null +++ b/validate_simple.sql @@ -0,0 +1,49 @@ +-- ========================= +-- 간단 검증: Enum에 없는 값 찾기 +-- ========================= +-- DB 클라이언트에서 이 쿼리를 복사해서 실행하세요 +-- 결과가 0 rows면 정상! + +-- meal.restaurant 검증 +SELECT 'meal.restaurant' AS 테이블_컬럼, restaurant AS 불일치_값, COUNT(*) AS 개수 +FROM meal +WHERE restaurant NOT IN ('DODAM','DORMITORY','FOOD_COURT','SNACK_CORNER','HAKSIK','FACULTY') +GROUP BY restaurant + +UNION ALL + +-- meal.time_part 검증 +SELECT 'meal.time_part', time_part, COUNT(*) +FROM meal +WHERE time_part NOT IN ('MORNING','LUNCH','DINNER') +GROUP BY time_part + +UNION ALL + +-- user.provider 검증 +SELECT 'user.provider', provider, COUNT(*) +FROM user +WHERE provider NOT IN ('EATSSU','KAKAO','APPLE') +GROUP BY provider + +UNION ALL + +-- user.role 검증 +SELECT 'user.role', role, COUNT(*) +FROM user +WHERE role NOT IN ('USER','ADMIN') +GROUP BY role + +UNION ALL + +-- user.status 검증 +SELECT 'user.status', status, COUNT(*) +FROM user +WHERE status NOT IN ('ACTIVE','INACTIVE') +GROUP BY status; + +-- ========================= +-- 결과 해석: +-- - 0 rows 또는 Empty set → ✅ 마이그레이션 진행 가능 +-- - 데이터가 나오면 → ❌ 해당 데이터 정리 필요 +-- ========================= \ No newline at end of file From 773f1f7cc4a28aa086c95b8c759b640e9017bf39 Mon Sep 17 00:00:00 2001 From: Eunseo Song <162149585+eunseo9311@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:14:12 +0900 Subject: [PATCH 3/6] =?UTF-8?q?fix:=20V4=EC=97=90=EC=84=9C=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20UNIQUE=20=EC=A0=9C=EC=95=BD=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V1에 이미 college.name의 UNIQUE 제약조건이 존재하므로 V4에서 중복 추가하는 부분 제거 --- .../db/migration/V4__add_constraints_and_defaults.sql | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql b/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql index 443ea3d5..94f0d694 100644 --- a/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql +++ b/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql @@ -27,11 +27,4 @@ ALTER TABLE menu -- ========================= ALTER TABLE review - MODIFY rating INT; - --- ========================= --- 4. unique 제약조건 추가 --- ========================= - -ALTER TABLE college - ADD CONSTRAINT UK_college_name UNIQUE (name); \ No newline at end of file + MODIFY rating INT; \ No newline at end of file From e9ac4e660555da2a8bf6ae8850fa632f58c8cc71 Mon Sep 17 00:00:00 2001 From: Eunseo Song <162149585+eunseo9311@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:16:15 +0900 Subject: [PATCH 4/6] Update run_validation.sh Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- run_validation.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/run_validation.sh b/run_validation.sh index cc14d6be..d0ba237a 100755 --- a/run_validation.sh +++ b/run_validation.sh @@ -29,10 +29,11 @@ echo "[$ENV 환경] 데이터 검증을 시작합니다..." echo "" # 검증 쿼리 실행 -mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" < validate_enum_data.sql > validation_result_${ENV}_$(date +%Y%m%d_%H%M%S).txt 2>&1 +RESULT_FILE="validation_result_${ENV}_$(date +%Y%m%d_%H%M%S).txt" +mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" < validate_enum_data.sql > "$RESULT_FILE" 2>&1 if [ $? -eq 0 ]; then - echo "✅ 검증 완료! 결과 파일을 확인하세요: validation_result_${ENV}_$(date +%Y%m%d_%H%M%S).txt" + echo "✅ 검증 완료! 결과 파일을 확인하세요: $RESULT_FILE" echo "" echo "⚠️ 중요: 결과 파일에서 아래 내용을 확인하세요" echo " - 불일치 데이터가 있는 경우: 데이터 정리 후 다시 검증" From 4e0a89ef50309d94d96aaa7bfd5bacea709fc0b6 Mon Sep 17 00:00:00 2001 From: Eunseo Song <162149585+eunseo9311@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:52:42 +0900 Subject: [PATCH 5/6] =?UTF-8?q?style:=20V4=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98=20=ED=8C=8C=EC=9D=BC=20=EB=81=9D?= =?UTF-8?q?=EC=97=90=20newline=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/db/migration/V4__add_constraints_and_defaults.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql b/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql index 94f0d694..e6089f83 100644 --- a/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql +++ b/src/main/resources/db/migration/V4__add_constraints_and_defaults.sql @@ -27,4 +27,4 @@ ALTER TABLE menu -- ========================= ALTER TABLE review - MODIFY rating INT; \ No newline at end of file + MODIFY rating INT; From 0356bd9c095abff90dd3ed270e97a4a16250b16b Mon Sep 17 00:00:00 2001 From: Eunseo Song <162149585+eunseo9311@users.noreply.github.com> Date: Sun, 15 Feb 2026 00:35:42 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20Flyway=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ src/main/resources/application-dev.yml | 7 +++++++ src/main/resources/application-prod.yml | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/build.gradle b/build.gradle index 9799a10a..5d8312ed 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,8 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.flywaydb:flyway-core' + implementation 'org.flywaydb:flyway-mysql' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.jetbrains:annotations:20.1.0' implementation 'org.springframework.boot:spring-boot-starter-security' diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index c328707f..239f8e1c 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -5,6 +5,13 @@ server: spring: + ## Flyway + flyway: + enabled: true + baseline-on-migrate: true + baseline-version: 1 + locations: classpath:db/migration + ## Database datasource: driver-class-name: com.mysql.cj.jdbc.Driver diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 945f7fa8..aa2221ac 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -5,6 +5,13 @@ server: spring: + ## Flyway + flyway: + enabled: true + baseline-on-migrate: true + baseline-version: 1 + locations: classpath:db/migration + ## Database datasource: driver-class-name: com.mysql.cj.jdbc.Driver