카테고리 : MySQL/기술노트

MySQL 업그레이드 전 호환성 점검: 예약어·sql_mode·인증 플러그인

MySQL 업그레이드 전 예약어 충돌, 세션 sql_mode, 계정별 인증 플러그인을 점검하고 애플리케이션 재접속까지 검증하는 절차를 정리한다.

저자: MySQL 기술 노트 작성: 2026.10.25 약 10분 5,866자
다운로드

서버가 정상적으로 시작되고 테이블을 읽을 수 있어도 업그레이드가 성공한 것은 아니다. 기존 컬럼명이 새 버전의 예약어가 되면 특정 SQL만 파싱에 실패할 수 있다. 같은 SQL이 연결별 sql_mode에 따라 다른 결과를 반환하거나, 기존 연결은 살아 있지만 새 연결만 인증에 실패할 수도 있다. 이 세 가지 문제는 데이터 파일의 호환성만 검사해서는 발견하기 어렵다.

업그레이드 사전 점검의 단위는 서버 버전 하나가 아니라 목표 버전·객체 정의·실제 SQL·연결 초기화·계정·드라이버의 조합이다. 이 글은 예약어(reserved keyword), sql_mode, 인증 플러그인(authentication plugin)을 중심으로 점검 범위와 합격 기준을 설명한다.

검증 범위는 폐기용 Community MySQL 8.0의 메타데이터 조회, 예약어 파싱 오류 포착, SQL 모드에 따른 결과 변화다. 실제 서버 업그레이드, MySQL 8.4 서버 실행, 구형 드라이버 인증, MySQL Shell Upgrade Checker, Aurora 전환은 이 예제에서 수행하지 않았다. 버전별 지원 상태는 마지막의 공식 문서에 근거하며, 운영 승인은 목표 환경에서 별도 시험해야 한다.

1. 호환성을 세 층으로 나누어 검사한다

첫째는 문법 계약이다. 파서는 SQL 문자열을 토큰과 문법 구조로 해석한다. 예약어가 늘어나거나 문법이 제거되면 저장된 데이터가 같아도 문장 준비 단계에서 실패한다. ORM이 생성하는 SQL과 드물게 실행되는 배치도 검사 대상이다.

둘째는 실행 의미 계약이다. SQL 모드는 문법 해석과 값 검증에 관여한다. ANSI_QUOTES는 큰따옴표의 의미를 바꾸고, strict mode는 잘못된 값을 거부할지 변환할지에 영향을 준다. ONLY_FULL_GROUP_BY는 집계 쿼리의 적법성을 판단한다. 오류 없이 실행되지만 결과가 달라지는 경우도 있으므로 성공 코드만 비교해서는 안 된다.

셋째는 연결 계약이다. 서버의 인증 플러그인, 계정에 지정된 방식, 클라이언트의 플러그인 지원, TLS 설정이 맞아야 SQL을 실행할 수 있다. 연결 풀이 유지한 기존 세션은 새 인증 과정을 거치지 않으므로 정상 트래픽이 신규 연결 성공을 보장하지 않는다.

flowchart TD
    A[출발 버전과 목표 패치 확정] --> B[객체 정의와 실제 SQL 수집]
    B --> C[목표 예약어와 문법 검사]
    C --> D[실제 연결의 SQL 모드와 결과 비교]
    D --> E[계정별 인증과 새 연결 시험]
    E --> F{오류 및 의미 차이 해소}
    F -->|아니오| G[코드·설정·드라이버 보정]
    G --> C
    F -->|예| H[복구 절차와 전환 승인 검토]

점검 결과에는 대상 버전, 도구 버전, 실행 시각, 적용 설정, 검사한 계정과 애플리케이션 범위, 미실행 항목을 함께 기록한다. 미실행 항목을 오류가 없었다는 이유로 통과 처리하지 않는다.

2. 먼저 서버 기본값과 현재 세션을 구분한다

다음 쿼리는 현재 접속의 설정과 서버 기본값을 비교한다. 운영에서는 DBA 도구뿐 아니라 애플리케이션이 실제로 만든 연결에서도 같은 항목을 수집해야 한다.

SELECT VERSION() AS server_version,
       @@global.sql_mode = @@session.sql_mode AS mode_matches_global,
       FIND_IN_SET('ONLY_FULL_GROUP_BY', @@session.sql_mode) > 0 AS full_group_by,
       FIND_IN_SET('STRICT_TRANS_TABLES', @@session.sql_mode) > 0 AS strict_trans,
       FIND_IN_SET('ANSI_QUOTES', @@session.sql_mode) > 0 AS ansi_quotes;
SELECT @@character_set_client AS client_charset,
       @@character_set_connection AS connection_charset,
       @@collation_connection AS connection_collation;

실행 결과(MySQL 8.0.x):

mysql> SELECT VERSION() AS server_version,
    ->        @@global.sql_mode = @@session.sql_mode AS mode_matches_global,
    ->        FIND_IN_SET('ONLY_FULL_GROUP_BY', @@session.sql_mode) > 0 AS full_group_by,
    ->        FIND_IN_SET('STRICT_TRANS_TABLES', @@session.sql_mode) > 0 AS strict_trans,
    ->        FIND_IN_SET('ANSI_QUOTES', @@session.sql_mode) > 0 AS ansi_quotes;

+----------------+---------------------+---------------+--------------+-------------+
| server_version | mode_matches_global | full_group_by | strict_trans | ansi_quotes |
+----------------+---------------------+---------------+--------------+-------------+
| 8.0.46         |                   1 |             1 |            1 |           0 |
+----------------+---------------------+---------------+--------------+-------------+
1 row in set (0.00 sec)

mysql> SELECT @@character_set_client AS client_charset,
    ->        @@character_set_connection AS connection_charset,
    ->        @@collation_connection AS connection_collation;

+----------------+--------------------+----------------------+
| client_charset | connection_charset | connection_collation |
+----------------+--------------------+----------------------+
| latin1         | latin1             | latin1_swedish_ci    |
+----------------+--------------------+----------------------+
1 row in set (0.00 sec)

mode_matches_global=1은 검사한 연결의 모드가 서버 기본값과 같다는 뜻일 뿐, 모든 연결이 같다는 뜻은 아니다. 각 모드의 전체 목록은 @@global.sql_mode와 @@session.sql_mode를 별도로 보관한다. 위 쿼리의 strict 항목은 STRICT_TRANS_TABLES만 검사하므로 0이더라도 STRICT_ALL_TABLES가 켜져 있는지 확인해야 한다.

SET GLOBAL sql_mode는 이후 생성되는 연결의 기본값에 영향을 주지만 기존 연결의 세션 값을 일괄 교체하지 않는다. 연결 풀의 초기화 SQL이나 ORM 옵션이 다시 값을 덮어쓸 수도 있다. 따라서 설정 변경 시험에는 기존 연결, 신규 연결, 풀 재생성 이후 연결을 구분한다. 문자셋 역시 서버의 기본 문자셋과 클라이언트 연결 문자셋을 혼동하지 않는다.

3. 예약어는 목표 버전의 사전으로 판단한다

MySQL 8.0 이상의 information_schema.KEYWORDS는 접속한 서버가 아는 키워드와 예약 여부를 보여준다. 현재 서버의 목록은 미래 목표 버전의 목록이 아니다. 5.7 원본에 8.0 전용 조회를 그대로 실행하거나, 8.0 서버에서 충돌이 없었다는 이유로 8.4도 안전하다고 판단해서는 안 된다.

일반적으로 다음 두 방향을 병행한다.

  • 원본의 스키마·테이블·컬럼·저장 프로그램 이름을 수집하고 목표 버전의 예약어 목록과 대조한다.
  • 대상 버전의 복구본에서 실제 SQL을 준비·실행한다. ORM 생성 SQL, 동적 SQL, 보고서와 야간 배치도 포함한다.

다음은 rank라는 컬럼을 의도적으로 만든 축소 예제다. 예약어를 백틱으로 인용한 정의와 조회는 성공하지만, 인용하지 않은 동적 SELECT는 오류 1064를 발생시킨다. 프로시저의 핸들러는 그 오류를 기록해 전체 실험을 이어가기 위한 장치이며 운영 오류를 무시하기 위한 패턴이 아니다.

SELECT WORD, RESERVED
FROM information_schema.KEYWORDS
WHERE WORD IN ('RANK', 'WINDOW', 'WITH')
ORDER BY WORD;
CREATE TABLE compat_keyword (`rank` INT NOT NULL);
INSERT INTO compat_keyword VALUES (7);
SELECT c.COLUMN_NAME, k.RESERVED
FROM information_schema.COLUMNS AS c
JOIN information_schema.KEYWORDS AS k
  ON UPPER(c.COLUMN_NAME) = k.WORD
WHERE c.TABLE_SCHEMA = DATABASE()
  AND c.TABLE_NAME = 'compat_keyword'
  AND k.RESERVED = 1;
DELIMITER //
CREATE PROCEDURE compat_parse_probe()
BEGIN
  DECLARE CONTINUE HANDLER FOR 1064 SET @parse_errno = 1064;
  SET @parse_errno = 0;
  SET @query_text = 'SELECT rank FROM compat_keyword';
  PREPARE compat_stmt FROM @query_text;
  IF @parse_errno = 0 THEN
    DEALLOCATE PREPARE compat_stmt;
  END IF;
END//
DELIMITER ;
CALL compat_parse_probe();
SELECT @parse_errno AS unquoted_errno;
SELECT `rank` FROM compat_keyword;
DROP PROCEDURE compat_parse_probe;
DROP TABLE compat_keyword;

실행 결과(MySQL 8.0.x):

아래는 실제 출력에서 준비·정리 과정 일부를 생략한 발췌다. 조회 결과의 열은 그대로 유지하였다.

mysql> SELECT WORD, RESERVED FROM information_schema.KEYWORDS
    -> WHERE WORD IN ('RANK', 'WINDOW', 'WITH') ORDER BY WORD;
+--------+----------+
| WORD   | RESERVED |
+--------+----------+
| RANK   |        1 |
| WINDOW |        1 |
| WITH   |        1 |
+--------+----------+
3 rows in set (0.00 sec)

mysql> CREATE TABLE compat_keyword (`rank` INT NOT NULL);
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO compat_keyword VALUES (7);
Query OK, 1 row affected (0.01 sec)

mysql> SELECT c.COLUMN_NAME, k.RESERVED ...;
+-------------+----------+
| COLUMN_NAME | RESERVED |
+-------------+----------+
| rank        |        1 |
+-------------+----------+
1 row in set (0.00 sec)

mysql> CALL compat_parse_probe();
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @parse_errno AS unquoted_errno;
+----------------+
| unquoted_errno |
+----------------+
|           1064 |
+----------------+
1 row in set (0.00 sec)

mysql> SELECT `rank` FROM compat_keyword;
+------+
| rank |
+------+
|    7 |
+------+
1 row in set (0.00 sec)

CALL의 성공 메시지는 내부 SELECT의 파싱 성공이 아니다. 오류 핸들러가 실행을 이어갔으므로 unquoted_errno=1064를 함께 확인해야 한다.

충돌 목록은 곧바로 이름 변경 명령 목록이 아니다. 기존 SQL이 이미 적절히 인용한다면 동작할 수 있다. 반대로 테이블 정의가 정상이어도 일부 코드가 이름을 문자열로 조립하면서 인용을 빠뜨리면 실패한다. 스키마를 붙인 식별자처럼 문법상 예외가 있는 경우도 있으므로, 문자열 검색만으로 모든 실행 여부를 판정하지 않는다.

단기 보정은 SQL 생성기의 일관된 식별자 인용이다. 장기적으로 이름을 바꾸려면 뷰, 저장 프로그램, 배치, ORM 매핑과 배포 순서를 함께 설계한다. 백틱은 식별자에 사용하며, 작은따옴표로 컬럼명을 감싸는 것은 같은 해결책이 아니다. 또한 파라미터 바인딩은 값에 대한 것이므로 컬럼명을 값 파라미터로 바꾸어 예약어 문제를 해결할 수는 없다.

4. sql_mode는 오류뿐 아니라 결과도 바꾼다

다음 실험은 큰따옴표가 모드에 따라 문자열 또는 식별자가 되는 차이를 보여준다. 테스트 세션에서만 모드를 변경하고 마지막에 원래 값으로 되돌린다. 운영 연결 풀에서 모드를 임의로 비우는 절차로 사용해서는 안 된다.

SET @saved_mode = @@session.sql_mode;
CREATE TABLE compat_quotes (code VARCHAR(20) NOT NULL);
INSERT INTO compat_quotes VALUES ('actual-value');
SET SESSION sql_mode = '';
SELECT "code" AS observed_value FROM compat_quotes;
SET SESSION sql_mode = 'ANSI_QUOTES';
SELECT "code" AS observed_value FROM compat_quotes;
SELECT 'code' AS literal_value, `code` AS column_value
FROM compat_quotes;
SET SESSION sql_mode = @saved_mode;
SELECT @@session.sql_mode = @saved_mode AS mode_restored;
DROP TABLE compat_quotes;

실행 결과(MySQL 8.0.x):

mysql> SET @saved_mode = @@session.sql_mode;

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE compat_quotes (code VARCHAR(20) NOT NULL);

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO compat_quotes VALUES ('actual-value');

Query OK, 1 row affected (0.00 sec)

mysql> SET SESSION sql_mode = '';

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT "code" AS observed_value FROM compat_quotes;

+----------------+
| observed_value |
+----------------+
| code           |
+----------------+
1 row in set (0.00 sec)

mysql> SET SESSION sql_mode = 'ANSI_QUOTES';

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT "code" AS observed_value FROM compat_quotes;

+----------------+
| observed_value |
+----------------+
| actual-value   |
+----------------+
1 row in set (0.00 sec)

mysql> SELECT 'code' AS literal_value, `code` AS column_value
    -> FROM compat_quotes;

+---------------+--------------+
| literal_value | column_value |
+---------------+--------------+
| code          | actual-value |
+---------------+--------------+
1 row in set (0.00 sec)

mysql> SET SESSION sql_mode = @saved_mode;

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@session.sql_mode = @saved_mode AS mode_restored;

+---------------+
| mode_restored |
+---------------+
|             1 |
+---------------+
1 row in set (0.00 sec)

mysql> DROP TABLE compat_quotes;

Query OK, 0 rows affected (0.00 sec)

첫 SELECT는 code라는 문자열을, 두 번째 SELECT는 컬럼에 저장된 actual-value를 반환한다. 두 문장 모두 성공하지만 결과는 다르다. 문자열은 작은따옴표, 인용이 필요한 식별자는 백틱으로 표현하면 이 차이에 덜 의존한다.

운영 회귀 시험은 아래 항목을 분리한다.

  • 집계 의미: ONLY_FULL_GROUP_BY에 걸리는 쿼리에서 어떤 행의 값을 반환하려 했는지 명확히 한다. 함수적 종속성이 인정되는 경우는 예외가 될 수 있다. 모드를 끄거나 ANY_VALUE()를 추가하는 것만으로 업무 의미가 복구되는 것은 아니다.
  • 값 검증: 길이 초과, 숫자 변환, 날짜 경계, 범위 초과 입력을 정상·비정상 사례로 나눈다. InnoDB의 strict mode 시험에는 실제 DML과 저장된 값 비교가 필요하다. 단순 SELECT의 경고 여부를 INSERT의 거부 여부와 동일시하지 않는다.
  • 클라이언트 초기화: 제거된 모드가 포함된 접속 초기화 SQL은 로그인 후 첫 명령에서 실패할 수 있다. 서버 시작 성공과 애플리케이션 연결 준비 완료를 구별한다.
  • 저장 객체: 저장 루틴·트리거·이벤트는 생성 시 SQL 모드와 관련된 실행 특성이 있으므로 정의에 기록된 모드를 조사한다. 현재 세션 설정만 바꾸어 모든 저장 객체의 의미가 갱신된다고 가정하지 않는다. 뷰는 정의와 인용 방식, 생성 당시 문법 해석도 별도로 검사한다.

information_schema.ROUTINES, TRIGGERS, EVENTS의 SQL_MODE와 객체 정의를 권한 범위 안에서 수집할 수 있다. 이때 업무 데이터만 복구하지 말고 저장 프로그램과 이벤트 정의도 함께 복원해야 실제 검사 범위가 확보된다.

5. 인증 플러그인은 계정과 연결 경로별로 검사한다

인증은 서버 기본값 하나로 결정되지 않는다. 계정의 plugin 값, 서버가 해당 플러그인을 제공하는지, 실제 드라이버가 지원하는지, TLS 또는 RSA 기반 비밀번호 교환 조건이 맞는지를 함께 확인한다. 계정은 사용자명만이 아니라 User와 Host의 조합으로 구분되므로 운영 목록을 작성할 때 둘을 분리해서 보관한다.

아래 쿼리는 공개 예제에 계정 식별자를 노출하지 않도록 플러그인별 계정 수만 집계한다. 잠긴 계정이나 내부 계정도 포함되므로 운영 애플리케이션 계정 수와 같지 않다. mysql.user 접근 권한이 필요하며, 비밀번호 해시인 authentication_string을 보고서로 수집할 필요는 없다.

SELECT plugin, COUNT(*) AS account_count
FROM mysql.user
GROUP BY plugin
ORDER BY plugin;
SELECT PLUGIN_NAME, PLUGIN_STATUS
FROM information_schema.PLUGINS
WHERE PLUGIN_TYPE = 'AUTHENTICATION'
  AND PLUGIN_NAME IN ('caching_sha2_password', 'mysql_native_password')
ORDER BY PLUGIN_NAME;

실행 결과(MySQL 8.0.x):

mysql> SELECT plugin, COUNT(*) AS account_count
    -> FROM mysql.user
    -> GROUP BY plugin
    -> ORDER BY plugin;

+-----------------------+---------------+
| plugin                | account_count |
+-----------------------+---------------+
| caching_sha2_password |             5 |
+-----------------------+---------------+
1 row in set (0.00 sec)

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS
    -> FROM information_schema.PLUGINS
    -> WHERE PLUGIN_TYPE = 'AUTHENTICATION'
    ->   AND PLUGIN_NAME IN ('caching_sha2_password', 'mysql_native_password')
    -> ORDER BY PLUGIN_NAME;

+-----------------------+---------------+
| PLUGIN_NAME           | PLUGIN_STATUS |
+-----------------------+---------------+
| caching_sha2_password | ACTIVE        |
| mysql_native_password | ACTIVE        |
+-----------------------+---------------+
2 rows in set (0.00 sec)

이 결과는 테스트 서버의 목록이며 실제 서비스의 인증 성공 증거가 아니다. 특히 플러그인이 ACTIVE라고 해서 오래된 드라이버로 접속할 수 있다는 뜻은 아니다.

Community MySQL의 mysql_native_password는 8.0.34부터 deprecated, 8.4에서는 기본 비활성화, 9.0.0부터 제거다. deprecated와 비활성화와 제거는 다른 상태다. 8.4에서 일시적으로 활성화할 수 있다는 사실을 장기적인 호환성 전략으로 삼아서는 안 된다. 기존 계정은 서버 기본 인증 방식만 바꾸었다고 자동으로 새 방식으로 변환되지 않는다.

권장 진행 순서는 드라이버의 caching_sha2_password 지원 확인, TLS 신뢰 체인 및 서버 신원 검증, 테스트 계정 전환, 실제 클라이언트 재접속, 운영 계정의 단계적 전환이다. 인증 방식 변경에는 비밀번호 재설정과 비밀정보 배포 절차가 수반될 수 있으므로 해시를 직접 수정하거나 일괄 변경하는 방식은 피한다. 애플리케이션뿐 아니라 복제, 백업, 모니터링, 배포 도구의 계정도 포함한다.

caching_sha2_password는 캐시를 이용한 빠른 인증과 전체 인증 경로를 가진다. 캐시가 비어 있는 첫 접속이나 서버 재시작 후 접속에서만 문제가 나타날 수 있다. 비보안 연결에서의 전체 인증에는 RSA 기반 비밀번호 교환 조건이 추가되므로, 검증 편의를 위해 공개키 취득 옵션을 무조건 켜는 대신 TLS와 신뢰 설정을 우선 정리한다. 시험 서버에서 최초 연결, 반복 연결, 재시작 후 연결을 각각 검증하고, 운영 서버의 인증 캐시를 시험 목적으로 임의 초기화하지 않는다.

6. Upgrade Checker와 실제 애플리케이션 시험을 연결한다

MySQL Shell의 util.checkForServerUpgrade()는 목표 버전을 지정하여 객체·설정의 알려진 비호환성을 검사하는 도구다. 사용 중인 Shell이 그 목표를 지원하는지 먼저 확인한다. 검사 목록은 Shell 버전과 출발·목표 조합에 따라 달라질 수 있으므로 특정 검사명이 모든 버전에 있다고 가정하지 않는다.

실행 절차는 다음과 같다. 아래는 명령 실행 결과가 아니라 운영 절차이며, 이 글에서는 도구를 실행하지 않았다.

  1. 출발 서버 버전과 목표 패치를 고정하고, 그 조합을 지원하는 Shell을 준비한다.
  2. 검사 권한을 갖는 계정으로 원본 또는 검사 목적에 맞는 복제본에 연결한다. 필요한 권한과 부하 영향은 해당 도구 문서로 확인한다.
  3. targetVersion을 명시하고 JSON 형식 등으로 결과를 저장한다. 옵션 파일 검사에 사용하는 configPath는 Shell이 실행되는 위치의 파일임에 유의한다.
  4. 오류, 경고, 수동 확인 사항, 실패하거나 생략된 검사 항목을 구별하여 담당자와 조치 내역을 붙인다.
  5. 보정 후 다시 검사하고, 목표 서버의 복구본에서 애플리케이션 회귀 시험을 수행한다.

검사 도구는 애플리케이션이 나중에 만드는 모든 SQL과 모든 드라이버를 실행하지 않는다. 따라서 정적 검사 통과 후에도 실제 연결 설정으로 대표 트랜잭션, 비정상 입력, 동적 SQL, 정기 배치를 시험해야 한다. 결과 비교에는 반환 행과 순서 계약, 저장된 값, 오류 번호, 경고, affected rows 등 업무에 필요한 관측값을 포함한다. 예약어를 고친 뒤 실행 계획이 달라졌다면 성능 회귀는 별도 검토한다.

7. Aurora MySQL에서는 엔진과 관리형 경계를 추가한다

Aurora에서는 Community 버전 번호만으로 인증 기본값이나 업그레이드 절차를 확정하지 않는다. Aurora 엔진 버전, 대상 파라미터 그룹 계열, 지원 업그레이드 경로와 사전 검사 로그를 함께 확인한다. Community의 설정 파일 옵션을 Aurora에 그대로 적용할 수 있다고 가정해서도 안 된다.

예를 들어 AWS 문서는 Aurora MySQL 2·3에서 마스터 사용자 비밀번호 재설정 시 mysql_native_password를 사용한다고 설명한다. 이는 모든 계정의 플러그인이 같다는 뜻도, Community MySQL 8.4의 기본값이 Aurora 전체에 그대로 적용된다는 뜻도 아니다. 대상 Aurora 릴리스의 계정 생성·변경 동작과 실제 계정 목록으로 확인한다.

TLS 정책도 버전에 따라 다르다. AWS 문서상 Aurora MySQL 3.04.0 이상은 TLS 1.0·1.1을 지원하지 않는다. 인증 플러그인만 점검하고 오래된 런타임의 TLS 지원을 놓치면 접속 실패 원인을 잘못 분류하게 된다. RDS Proxy나 IAM 인증을 사용한다면 직접 접속과 프록시 경유, 토큰 생성과 만료, TLS 설정을 각각 해당 경로의 요구사항에 맞추어 검증한다.

전환 리허설에서는 새 endpoint로 연결 풀을 다시 만들고, 실제 writer·reader 경로에서 신규 연결을 시험한다. 로컬 SQL 예제 성공을 Aurora의 사전 검사 통과, 프록시 인증 성공 또는 실제 업그레이드 성공으로 확대 해석하지 않는다.

8. 전환 승인 체크리스트

  • 서버 기본값과 실제 애플리케이션 세션의 sql_mode

예약어 충돌은 문장의 형태, SQL 모드는 문장의 의미, 인증 플러그인은 문장에 도달하기 위한 연결을 결정한다. 세 층을 분리하여 증거를 모아야 문제가 발생했을 때 서버 시작, 연결, 문법, 데이터 의미 중 어디에서 계약이 깨졌는지 좁힐 수 있다. 이 사전 점검이 완료된 다음에는 실제 업그레이드 리허설에서 성능 회귀와 전환·복구 시간을 검증해야 한다.

참고 문서