Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -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 <host> -u <user> -p <database> < 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 <host> -u <user> -p <database> < src/main/resources/db/migration/V2__align_pk_and_fk_types.sql

# 검증: PK/FK 타입 확인
mysql -h <host> -u <user> -p -e "
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = '<database>'
AND COLUMN_NAME IN ('college_id', 'department_id')
ORDER BY TABLE_NAME;
"

# V3 실행
mysql -h <host> -u <user> -p <database> < src/main/resources/db/migration/V3__convert_varchar_to_enum.sql

# 검증: Enum 타입 확인
mysql -h <host> -u <user> -p -e "
SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = '<database>'
AND DATA_TYPE = 'enum'
ORDER BY TABLE_NAME;
"

# V4 실행
mysql -h <host> -u <user> -p <database> < src/main/resources/db/migration/V4__add_constraints_and_defaults.sql

# 검증: 제약조건 확인
mysql -h <host> -u <user> -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 = '<database>'
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 = '<database>'
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 <prod-host> -u <user> -p <database> < V2__align_pk_and_fk_types.sql
mysql -h <prod-host> -u <user> -p <database> < V3__convert_varchar_to_enum.sql
mysql -h <prod-host> -u <user> -p <database> < 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

롤백 명령어 예시에 포함된 스냅샷 식별자(eatssu-dev-pre-migration-20260207)에 미래 날짜가 사용되었습니다. 이는 예시일 수 있으나, 실제 실행 시 혼동을 주거나 복사-붙여넣기 오류를 유발할 수 있습니다. your-snapshot-identifier와 같이 더 일반적인 플레이스홀더로 변경하는 것을 제안합니다.

Suggested change
--db-snapshot-identifier eatssu-dev-pre-migration-20260207
--db-snapshot-identifier your-snapshot-identifier

```

---

## 🐛 문제 발생 시

### 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 테스트 통과
- [ ] 로그에 오류 없음
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
31 changes: 31 additions & 0 deletions check_fk_constraints.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-- =========================
-- FK 제약조건명 확인
-- =========================
-- 실행 방법: mysql -h <host> -u <user> -p <database> < 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;
45 changes: 45 additions & 0 deletions run_validation.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/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 ""

# 검증 쿼리 실행
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The script passes the database password directly to the mysql command using the -p flag. This makes the password visible in the system's process list (e.g., via ps aux) to any other user on the same machine, as the mysql client itself warns: [Warning] Using a password on the command line interface can be insecure. It is recommended to use the MYSQL_PWD environment variable to pass the password, which is a more secure approach to prevent exposure in process lists or shell history.

Suggested change
mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" < validate_enum_data.sql > "$RESULT_FILE" 2>&1
# Use MYSQL_PWD environment variable to avoid exposing password in process list
export MYSQL_PWD="$DB_PASS"
mysql -h "$DB_HOST" -u "$DB_USER" "$DB_NAME" < validate_enum_data.sql > "$RESULT_FILE" 2>&1
unset MYSQL_PWD


if [ $? -eq 0 ]; then
echo "✅ 검증 완료! 결과 파일을 확인하세요: $RESULT_FILE"
echo ""
echo "⚠️ 중요: 결과 파일에서 아래 내용을 확인하세요"
echo " - 불일치 데이터가 있는 경우: 데이터 정리 후 다시 검증"
echo " - 결과가 비어있는 경우: varchar → enum 변환 진행 가능"
echo ""
else
echo "❌ 검증 실패. 접속 정보를 확인하세요."
exit 1
fi
11 changes: 9 additions & 2 deletions src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -41,8 +48,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:
Expand Down
7 changes: 7 additions & 0 deletions src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading