목차
- Skeema란?
- 설치 및 준비
- Skeema 설정
- 디렉터리 구조
- 기본 워크플로
- 스키마 Pull
- 스키마 Diff
- 스키마 Push
- 마이그레이션 SQL 생성
- 안전 옵션
- 무시/제외 규칙
- 뷰/루틴 처리
- 검증 및 테스트
- 트러블슈팅
Skeema란?
Skeema는 MySQL/MariaDB 스키마를 파일 시스템 기반으로 관리할 수 있게 해주는 CLI 도구이다.
- 데이터베이스 스키마를 SQL 파일로 추출(pull)
- 환경 간 스키마 차이를 비교(diff)
- 변경 사항을 데이터베이스에 적용(push)
Git과 함께 사용하면 스키마 변경 이력을 버전 관리할 수 있어 협업 환경에서 유용하다.
설치 및 준비
설치 방법
macOS (Homebrew)
Linux (apt)
1
2
3
| # Debian/Ubuntu
curl -LO https://github.com/skeema/skeema/releases/download/v1.13.1/skeema_1.13.1_linux_amd64.deb
sudo dpkg -i skeema_1.13.1_linux_amd64.deb
|
바이너리 직접 설치
1
2
3
4
| # Linux
curl -LO https://github.com/skeema/skeema/releases/download/v1.13.1/skeema_1.13.1_linux_amd64.tar.gz
tar -xzf skeema_1.13.1_linux_amd64.tar.gz
sudo mv skeema /usr/local/bin/
|
지원 버전
- MySQL 5.5, 5.6, 5.7, 8.0, 8.4
- MariaDB 10.1 ~ 10.11, 11.x
- Percona Server 5.5 ~ 8.0
필요 권한
Skeema가 제대로 동작하려면 다음 권한이 필요하다.
| 작업 | 필요 권한 |
|---|
| pull/diff | SELECT, SHOW VIEW, SHOW DATABASES |
| push | ALTER, CREATE, DROP, INDEX, REFERENCES |
| 뷰 관리 | CREATE VIEW, SHOW VIEW |
| 루틴 관리 | CREATE ROUTINE, ALTER ROUTINE, EXECUTE |
| 안전한 작업 | LOCK TABLES (pt-online-schema-change 사용 시) |
1
2
3
4
5
6
7
8
9
| -- 권한 부여 예시
-- 글로벌 권한
GRANT SHOW DATABASES ON *.* TO 'skeema_user'@'%';
-- 스키마별 권한
GRANT SELECT, SHOW VIEW,
ALTER, CREATE, DROP, INDEX, REFERENCES,
CREATE VIEW, LOCK TABLES
ON mydb.* TO 'skeema_user'@'%';
|
Skeema 설정
.skeema 파일에 기본 설정과 환경별 설정을 작성한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| # db/skeema/.skeema
default-character-set=utf8mb4
default-collation=utf8mb4_unicode_ci
generator=skeema:1.13.1-community
schema={db_schema}
allow-auto-inc=int, int unsigned, bigint unsigned, bigint
lint-reserved-word=ignore
lint-dupe-index=ignore
[dev]
skip-allow-unsafe
flavor={$database}
host={$db_host}
password={$db_pwd}
port={$db_port}
user={$db_user}
[prod]
skip-allow-unsafe
flavor={$database}
host={$db_host}
password={$db_pwd}
port={$db_port}
user={$db_user}
|
주요 설정 항목
| 옵션 | 설명 |
|---|
default-character-set | 기본 문자셋 |
default-collation | 기본 콜레이션 |
schema | 대상 스키마(데이터베이스) 이름 |
flavor | MySQL 또는 MariaDB 버전 지정 (예: mariadb:10.6, mysql:8.0) |
skip-allow-unsafe | unsafe 검사 건너뛰기 (allow-unsafe=true와 동일) |
lint-reserved-word | 예약어 사용 경고 무시 |
lint-dupe-index | 중복 인덱스 경고 무시 |
환경 섹션 상속
[dev], [prod] 섹션은 상위 설정을 상속받는다.
1
2
3
4
5
6
7
8
9
10
11
| # 공통 설정
default-character-set=utf8mb4
[dev]
# dev는 공통 설정 + dev 전용 설정 적용
host=dev-db.example.com
[prod]
# prod는 공통 설정 + prod 전용 설정 적용
host=prod-db.example.com
safe-below-size=10M # prod만 안전 옵션 적용
|
디렉터리 구조
Skeema는 스키마별로 하위 폴더를 생성한다.
1
2
3
4
5
6
7
8
9
10
| db/skeema/
├── .skeema # 루트 설정 (환경별 접속 정보)
├── mydb/ # 스키마(데이터베이스) 폴더
│ ├── .skeema # 스키마별 설정 (schema=mydb)
│ ├── users.sql # 테이블 DDL
│ ├── orders.sql
│ └── products.sql
└── another_db/
├── .skeema
└── ...
|
각 .sql 파일에는 CREATE TABLE 문이 포함된다.
1
2
3
4
5
6
7
8
| -- users.sql
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
기본 워크플로
Skeema를 활용한 스키마 관리 워크플로는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| 1. pull (dev) : 개발 서버 스키마를 파일로 추출
↓
2. Git 커밋/리뷰 : 변경 사항을 Git으로 관리, PR 리뷰
↓
3. diff (staging) : 스테이징 환경과 비교
↓
4. push (staging) : 스테이징에 먼저 적용
↓
5. 검증 : 애플리케이션 테스트
↓
6. diff (prod) : 운영 환경과 비교, SQL 검토
↓
7. 백업 : 운영 DB 백업
↓
8. push (prod) : 운영에 적용
|
스키마 Pull
개발 서버의 스키마를 파일로 추출한다.
실행 결과:
- 각 테이블별
.sql 파일이 생성됨 - 테이블 구조, 인덱스, 제약조건 등이 포함
1
2
3
4
5
| db/skeema/
├── .skeema
├── users.sql
├── orders.sql
└── products.sql
|
pull 옵션
1
2
3
4
5
6
7
8
| # 특정 스키마만 pull
skeema pull dev --schema=mydb
# 뷰 포함
skeema pull dev --include-views
# 루틴(프로시저/함수) 포함
skeema pull dev --include-routines
|
스키마 Diff
운영 서버와 현재 스키마 파일을 비교한다.
출력 예시:
1
2
3
4
| -- diffing mydb
ALTER TABLE `users` ADD COLUMN `phone` varchar(20) DEFAULT NULL;
ALTER TABLE `orders` MODIFY COLUMN `status` enum('pending','completed','cancelled') NOT NULL;
|
diff 명령은 실제로 변경을 적용하지 않고 차이점만 출력한다.
diff 종료 코드
0: 차이 없음1: 차이 있음 (적용 가능)2: 오류 발생
CI/CD에서 활용:
1
2
3
4
| skeema diff prod
if [ $? -eq 1 ]; then
echo "스키마 변경 감지됨"
fi
|
스키마 Push
diff로 확인한 변경 사항을 실제 데이터베이스에 적용한다.
1
2
3
4
5
6
7
8
| # 운영 서버에 적용
skeema push prod
# dry-run (실제 적용 없이 확인만)
skeema push prod --dry-run
# 특정 스키마만 적용
skeema push prod --schema=mydb
|
push 전 확인사항
1
2
3
4
5
6
7
8
| # 1. diff로 변경 내용 확인
skeema diff prod
# 2. dry-run으로 실행 계획 확인
skeema push prod --dry-run
# 3. 실제 적용
skeema push prod
|
마이그레이션 SQL 생성
diff 결과를 SQL 파일로 저장하여 마이그레이션 스크립트로 활용한다.
1
| skeema diff prod > db/migration/V$(date +%Y%m%d%H%M)__{$description}.sql
|
네이밍 규칙
Flyway/Liquibase와 호환되는 네이밍:
1
| V{VERSION}__{DESCRIPTION}.sql
|
VERSION: 정렬 가능한 버전 (YYYYMMDDHHMM 권장)DESCRIPTION: 변경 내용 설명 (snake_case)
1
2
3
4
| db/migration/
├── V202511211030__add_users_table.sql
├── V202511211145__add_phone_column.sql
└── V202511220900__create_orders_index.sql
|
Flyway 연동 팁
1
2
3
4
5
| # Flyway 마이그레이션 디렉터리에 직접 생성
skeema diff prod > flyway/sql/V$(date +%Y%m%d%H%M)__schema_update.sql
# Flyway 실행
flyway migrate
|
Idempotency 고려
Skeema가 생성하는 DDL은 기본적으로 idempotent하지 않다.
수동으로 조건문을 추가하거나, Flyway/Liquibase의 버전 관리에 의존한다.
안전 옵션
운영 환경에서는 안전 옵션을 적극 활용한다.
safe-below-size
지정된 크기 이하의 테이블에만 unsafe 작업 허용:
1
2
| [prod]
safe-below-size=100M # 100MB 이하 테이블만 unsafe 허용, 초과 시 거부
|
allow-unsafe
데이터 손실 가능성 있는 작업 허용 여부:
1
2
3
4
5
| # 기본값: false (안전 모드)
allow-unsafe=false
# 위험한 작업도 허용
allow-unsafe=true
|
위험한 작업 예시:
skip-verify
스키마 동기화 검증 건너뛰기:
1
| skeema push prod --skip-verify
|
alter-wrapper (pt-online-schema-change)
대용량 테이블에 온라인 DDL 적용:
1
2
| [prod]
alter-wrapper=/usr/bin/pt-online-schema-change --execute --alter {CLAUSES} D={SCHEMA},t={TABLE},h={HOST},P={PORT},u={USER},p={PASSWORD}
|
또는 gh-ost 사용:
1
| alter-wrapper=/usr/bin/gh-ost --alter "{CLAUSES}" --database={SCHEMA} --table={TABLE} --host={HOST} --user={USER} --password={PASSWORD} --execute
|
alter-wrapper-min-size
특정 크기 이상 테이블에만 wrapper 적용:
1
| alter-wrapper-min-size=50M
|
무시/제외 규칙
자동 생성 테이블이나 특정 객체를 제외할 수 있다.
ignore-table
특정 테이블 제외:
1
2
3
4
5
6
7
| # 단일 테이블
ignore-table=schema_migrations
# 패턴 사용 (정규식)
ignore-table=/^tmp_/
ignore-table=/^backup_/
ignore-table=/_bak$/
|
ignore-schema
특정 스키마 제외:
1
2
3
4
| ignore-schema=information_schema
ignore-schema=performance_schema
ignore-schema=mysql
ignore-schema=sys
|
루틴 제외
1
2
| # 프로시저/함수 무시 (기본값)
include-routines=false
|
참고: 트리거는 Skeema Community에서 지원하지 않음
.skeema 파일에서 무시 설정
1
2
3
4
| # db/skeema/.skeema
ignore-table=/^flyway_/
ignore-table=/^DATABASECHANGELOG/
ignore-table=schema_version
|
뷰/루틴 처리
뷰 포함
1
2
3
| skeema pull dev --include-views
skeema diff prod --include-views
skeema push prod --include-views
|
설정 파일에 추가:
주의사항
- 뷰 정의 시 참조하는 테이블이 먼저 존재해야 함
DEFINER 절이 환경마다 다를 수 있음- 뷰 간 의존성 순서 문제 발생 가능
1
2
| # .skeema 설정에서 DEFINER 제거
strip-definer=true
|
루틴(프로시저/함수) 포함
1
| skeema pull dev --include-routines
|
루틴 주의사항:
DEFINER, SQL SECURITY 설정 확인- 실행 권한 필요 (
EXECUTE privilege) - 루틴 간 의존성 순서 수동 관리 필요
검증 및 테스트
skeema lint
SQL 파일의 문법 및 스타일 검사:
1
2
3
4
| skeema lint
# 특정 규칙 무시
skeema lint --lint-pk=ignore
|
lint 규칙:
lint-pk: PRIMARY KEY 필수 여부lint-engine: 스토리지 엔진 검사lint-charset: 문자셋 검사lint-dupe-index: 중복 인덱스 검사
스테이징 먼저 적용
1
2
3
4
5
6
7
8
| # 1. 스테이징에 적용
skeema push staging
# 2. 애플리케이션 테스트
./run_tests.sh
# 3. 성공 시 운영 적용
skeema push prod
|
적용 후 확인 쿼리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| -- 테이블 크기 확인
SELECT
table_name,
ROUND(data_length/1024/1024, 2) AS data_mb,
ROUND(index_length/1024/1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = 'mydb'
ORDER BY data_length DESC;
-- 인덱스 확인
SHOW INDEX FROM mydb.users;
-- 쿼리 플랜 확인
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
|
CI/CD 통합
1
2
3
4
5
6
7
8
9
| # .gitlab-ci.yml 예시
schema-diff:
stage: test
script:
- skeema diff prod
allow_failure: true
only:
changes:
- db/skeema/**/*
|
트러블슈팅
Collation/Charset 드리프트
환경 간 기본 collation이 다를 때 발생:
1
2
| -- 오류 예시
ALTER TABLE `users` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
해결:
1
2
3
| # .skeema에 명시적 지정
default-character-set=utf8mb4
default-collation=utf8mb4_unicode_ci
|
DEFAULT CURRENT_TIMESTAMP 차이
MySQL 버전에 따라 표현 방식이 다름:
1
2
3
4
5
| -- MySQL 5.6
DEFAULT CURRENT_TIMESTAMP
-- MySQL 5.7+
DEFAULT CURRENT_TIMESTAMP()
|
해결:
1
2
| # flavor 정확히 지정
flavor=mysql:5.7
|
AUTO_INCREMENT 불일치
AUTO_INCREMENT 값은 diff에서 무시됨 (정상 동작):
1
2
| # 이미 기본 동작
allow-auto-inc=int, int unsigned, bigint unsigned, bigint
|
특정 AUTO_INCREMENT 값 강제 필요 시 수동 처리.
file-per-table 가정
Skeema는 테이블당 하나의 파일을 가정함.
파티션 테이블 사용 시 주의:
1
2
3
4
5
6
7
| -- 파티션 정보도 함께 포함됨
CREATE TABLE `logs` (
...
) PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026)
);
|
빈 diff인데 push 실패
1
2
3
| # 캐시 문제일 수 있음
skeema pull dev --skip-format
skeema diff prod
|
대용량 테이블 ALTER 타임아웃
1
2
3
4
5
| # 타임아웃 늘리기
connect-timeout=30
# 또는 pt-osc 사용
alter-wrapper=/usr/bin/pt-online-schema-change ...
|
권한 오류
1
| Error: Access denied for user 'admin'@'%'
|
필요 권한 확인:
1
| SHOW GRANTS FOR 'admin'@'%';
|
Skeema를 사용하면 스키마 변경을 코드처럼 관리할 수 있다.
개발/운영 환경 간 스키마 차이를 쉽게 파악하고 안전하게 마이그레이션을 진행할 수 있다.