TECH
QUESTION
자주하는 질문답변 입니다.
Oracle
작성자 | 유건데이타 | 등록일 | 2015-05-19 |
제목 | Tablespace Point-In-Time-Recovery | ||
---|---|---|---|
SCOPE
----- 8~10g Standard Edition 에서는 Tablespace Point-In-Time-Recovery (TSPITR) 기능을 지원하지 않는다. Tablespace Point-In-Time-Recovery (TSPITR) -------------------------------------------------------------------------- [주의] 이 bulletin은 internal only로 만들어졌으면, 실제 time-based recovery가 필요한 경우는 아래 언급된 이 문서는 TSPITR 개념을 파악하고 test하는 문서로 사용하도록 한다. TSPITR은 실제 고객에게 직접 적용하기에는 매우 복잡하고, 실수의 위험이 많아 실제로는 거의 사용하지 않고 있다. 사용자의 실수로 일부 table이 drop되거나 일부 data가 delete되어 그 부분을 다시 이전 상태로 되돌려야 할 필요가 있는 경우가 있다. 이 때, Oracle7에서는 backup을 이용하여 time based recovery를 수행한 후 지워진 data를 export받고, 다시 현재의 database 내에 import하는 방식을 이용하였다. Oracle8에서는 이러한 경우 특정 tablespace만을 과거 시점으로 돌리는 방법을 절차화하여 Tablespace Point-In-Time recovery라고 한다. 기본 내부 mechanism은 Oracle7과 유사하나 export와 import시에 실제 data를 export/import하지 않도록 하여 recovery의 시간을 최소화하고 복구 방법도 view나 parameter등을 이용하여 간편화하고 체계적으로 되도록 하였다. 여기에서는 이 Tablespace Point-In-Time recovery에 대한 개념 및 방법과, 실제 예를 이용한 자세한 사용 방법을 정리하였다. 단, 이 방법은 복구하고자 하는 과거 시점 이전의 cold backup이나 hot backup이 존재하여야 하며, database는 archive log mode인 경우에만 가능하다. 1. 용어 정리 ============= (1) TSPITR Tablespace Point-In-Time Recovery (2) Recovery Set 이전 시점으로 되돌리고자 하는 tablespace에 속한 datafile들 단, SYSTEM tablespace는 recovery set에 포함되지 않는다. (3) Auxiliary Set: - controlfile의 backup - SYSTEM tablespace의 datafiles의 backup - rollback segments를 포함하는 tablespace (일반적으로 RBS)의 datafiles의 backup - temporary tablespace에 포함된 datafiles의 backup (optional) 이후 수행되는 export시에 약간의 temporary영역이 필요한데 temp tablespace가 없는 경우 system tablespace를 사용하게 된다. (4) Clone Database tablespace point-in-time recovery시에 이용되는 current production database의 backup으로 구성되는 복사본. 현재의 database와는 다른 ORACLE_SID를 가지며 아래와 같은 구성요소를 가진다. - recovery set에 포함된 file들의 backup - auxiliary set에 포함된 file들의 backup - controlfile의 backup 2. 제약 사항 ============= - tablespace를 drop한 경우에 대한 복구는 불가능하다. - 지워졌다 같은 이름으로 만들어진 tablespace에 대한 복구는 불가능하다. - tablespace에 잘못 만들어진 datafile을 지우기 위해 사용되어질 수 없다. 만약, 일정 시간인 TSPITR시간 이후에 추가된 file이라면, TSPITR이 완료되어도 그 file은 여전히 database의 일부분이고, 단 내부의 내용은 모두 지워진 상태이다. - clone database에 DML은 불가능하다. 단지 recovery만을 위해 사용되어진다. - TSPITR이 완료된 이후에는 TSPITR시 이용했던 backup은 다시 이용될 수 없다. TSPITR이전의 backup과 archive file을 이용하여 TSPITR이후 시점으로 복구하고자 하면, ORA-1247(database recovery through TSPITR of tablespace" 오류가 발생한다. - optimizer 통계정보를 가지고 있었던 object들에 대해서 통계정보는 복구가 안된다. TSPITR이후에 다시 통계정보를 수집해야 한다. - 다음과 같은 type의 object들은 TSPITR recovery set내에 포함될 수 없다. - replicated master tables - varray columns을 가진 tables - nested tables를 가진 tables - external bfiles를 가진 tables - snapshot logs - snapshot tables - IOTs - sys가 owner인 objects 3. Verification View ===================== TSPITR을 수행하기 위해서는 먼저 $ORACLE_HOME/rdbms/admin directory 에 존재하는 다음 세개의 script를 수행하여야 한다. 단, 이 script는 이미 catproc에 포함되어 있으므로 기본적으로 이미 수행되어 있다. dbmspitr.sql prvtpitr.plb catpitr.sql 이 script내에는 TSPITR에 필요한 여러가지 view 및 package를 생성하는데 그중에서 다음 두 가지에 유의하여야 한다. (1) TS_PITR_CHECK 서로 연관된 objects, 즉 table과 index, master table과 child table 중에서 같은 tablespace내에 포함되어 있지 않는 objects에 대한 정보를 담고 있다. 예를 들어 DEPT table은 USERS tablespace에, DEPT_PK index는 USERS_IDX tablespace에 포함되어 있다면, 이 정보가 ts_pitr_check에 포함되게 되는 것이다. 그러므로 특정 tablespace만 TSPITR을 수행 후 다른 tablespace에도 연관된 object가 있는지 확인할 때 이 view를 이용할 수 있다. TSPITR의 경우 recovery set내에 포함되지 않은 datafile에 이러한 식으로 연관된 object가 존재한다면 recovery가 불가능하게 되어, export전에 다른 tablespace에 포함된 object는 지워야 한다. 즉 위에서 언급한 예의 경우 USERS tablespace만이 recovery set에 포함되어 있다면 DEPT_PK index는 export전에 drop하여야 한다. (2) TS_PITR_OBJECTS_TO_BE_DROPPED 특정 시점(pitr_time)까지 recovery를 하고자 하는 경우 그 시점 이후에 생성된 object를 조회할 수 있는 view이다. TSPITR이 완려되면 recover가 된 tablespace에 포함된 objects중 pitr_time이후에 생성된 objects는 완전히 지워지게 되므로 recovery를 시도하기 이전에 이미 이 view를 확인하여 drop되게 되는 objects가 어떠한 것이 있고, 이후에 필요한 것이라면 TSPITR을 시작하기 전에 미리 export를 받아둘 수도 있다. 4. Point In Time Recovery의 단계 요약 ====================================== (1) prepare/restore 위에 기술한 relationship이 다음의 제약을 부과한다. 1단계: testcase 준비 및 user가 table drop test를 위하여 db를 archive log mode로 변환하고 cold backup을 받고 control file을 backup받는다. 특정 table을 drop하고 시간을 확인한다. 2단계: TSPITR 수행 후 drop될 objects 확인 및 현재 database에 대한 조치 TSPITR이후 생성된 obejcts의 경우 recovery가 완료되면 drop되므로 미리 확인해 두고, 현재 database의 recovery set에 포함된 tablespace에 더이상의 transaction이 수행되지 못하도록 offline시 켜둔다. 그리고 복구하고자 하는 시점이 current redo log내의 정보를 필요로 할 수도 있으므로 current log까지 archive를 받아둔다. 3단계: Clone Database의 구성 clone database구성을 위해 현재 database의 initSID.ora와 configSID.ora file을 clone database용으로 copy해두고 clone database 구성을 위해 필요한 system, rollback, 복구하고자 하는 objects가 포함된 datafile들을 restore한다. configCLONE.ora file에서는 control_files를 backup control file을 가리키도록 하고, initCLONE.ora (혹은 configCLONE.ora) file에서는 다음 parameter를 설정한다. lock_name_space db_file_name_convert log_file_name_convert lock_name_space는 current production database와 clone database가 db_name이 같아도 둘 다 startup이 가능하게 해주어, clone database의 경우 database name은 변경할 필요없이 ORACLE_SID값만 변경하면 되도록 한다. db_file_name_conver나 log_file_name_convert는 반드시 설정해야 하는 것은 아니지만, 설정하면 alter database rename file명령을 대신하여 clone의 controlfile내의 datafile과 redo log file의 이름을 변경시켜 둔다. backup file들의 경우 file이름은 current database와 같으나 restore 하는 directory만 다르므로 db_file_name_convert="oradata", "backup"과 같이 변경되는 directory이름만 지정하면 편리하다. 4단계: recovery set에 포함되지 않은 연관된 objects에 대한 확인 및 조치 TS_PITR_CHECK view를 확인하여 다음 사항을 만족하지 않는 object에 대해서 recovery set내에 있지 않은 object는 지운다. - table/cluster와 해당 index는 모두 recovery set에 포함되어 있어야 한다. - partitioned object의 모든 partition은 모두 recovery set에 포함되어 있어야 한다. - parent-child관계의 table은 함께 recovery set에 포함되어야 한다. - LOB segments, lob indexs와 이것을 reference하는 table은 함께 recovery set에 포함되어야 한다. 5단계: Export 6단계: Import 여기에서 recovery set의 실제 data를 export/import하는 것이 아니어 서 실제 data가 대량인 경우에도 많은 시간이 걸리지 않는다. recovery data는 이미 clone datafile에 있고, production database의 dictionary 정보만 지정된 시간의 것으로 대체되는 것이다. 이것은 recovery set의 모든 objects의 segment header를 읽어서 가능하다. 7 단계: 확인 및 삭제된 object생성 작업 2단계나 4단계에서 drop된 objects에 대해서 필요한 경우 재생성한다. 5. 실제 case 재현 =================== 1단계: testcase 준비 및 user가 table drop ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (1) 데이타베이스를 archive log mode로 변환한다. (2) database의 cold backup을 받는다. (hot backup도 가능하다.) (3) control file을 alter database backup controlfile to 'filename' 방식으로 backup을 받는다. TSPITR를 사용하기 위해서는 반드시 table을 drop하기 전 시점의 alter database backup controlfile to 'filename'을 사용한 backup이 존재해야 한다. cold backup형태의 control file backup이나 create controlfile문장을 이용하여 controlfile을 생성하여 사용할 수 없다. SQL> connect system/manager SQL> alter database backup controlfile to '/mnt3/rctest80/server/eykim/oradata/backcon.ctl'; (4) drop후 복구할 table을 만든다. test와 test2라는 두개의 table을 만들고 이중 test2는 scott user의 default tablespace인 USERS가 아닌 TOOLS에 만든다. SQL> create table test(t number); SQL> create table test2(t number); SQL> create index test_idx on test(t); SQL> create index test2_idx on test2(t) tablespace tools; 각각 다음과 같은 세개의 row를 insert후 확인한다. SQL> select * from test; T ---------- 1 2 3 SQL> select * from test2; T ---------- 100 200 300 두개의 table모두 USERS tablespace에 저장되어 있음을 확인할 수 있다. SQL> select table_name, tablespace_name from user_tables; TABLE_NAME TABLESPACE_NAME ---------------- ------------------------ TEST USERS TEST2 USERS (5) table을drop한다. SQL> !date Mon Nov 22 21:50:31 KST 1999 SQL> drop table test; SQL> drop table test2; (6) 새로운 table test3를 다시 만든다. TSPITR을 이용하여 위의 table test와 test2가 drop되기 이전 시점으로 복구가 되면 이 이후에 만들어진 test3는 없어지게 된다. SQL> !date Mon Nov 22 22:05:19 KST 1999 SQL> create table test3(t number); 2단계: TSPITR 수행 후 drop될 objects 확인 및 현재 database에 대한 조치 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (7) test와test2를 drop하기 이전 시점으로 TSPITR을 시도하는 경우 drop 되게 되는 object를 다음과 같이 확인할 수 있다. 이것을 확인하고 여기에 나타나는 object를 export받은 후 이후에 TSPITR 이 완료된 후 다시 import할 수도 있고, 혹은 복구하고자하는 시점 이후에 생성된 table이므로 drop되는 것이 바람직한 경우일 수도 있다. SQL> connect system/manager SQL> select owner, name, tablespace_name, to_char(creation_time,'yyyy/mm/dd:hh:mi:ss') from sys.ts_pitr_objects_to_be_dropped where tablespace_name in ('USERS') and creation_time > to_date('99/11/22:21:50:31','yy/mm/dd:hh24:mi:ss') order by tablespacE_name, creation_time OWNER NAME TABLESPACE_NAME TO_CHAR(CREATION_TIME,'YYYY/MM/DD:HH:M ----------------------------------------------------------- SCOTT TEST3 USERS 1999/11/22:10:05:42 (8) 복구하고자 하는 tablespace인 USERS를 offline시켜 추가적인 transaction을 막는다. SQL> alter tablespace users offline immediate; (9) current log까지 모두 archive받도록 한다. 이렇게 TSPITR recovery시 모든 log가 archive file로 되어 있어야 복구하고자 하는 시점의 변경사항이 current redo log에 포함된 경우 오류가 발생하지 않게 된다. SQL> alter system archive log current; 3단계: Clone Database의 구성 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (10) backup을 이용하여 새로운 database환경으로 다음과 같이 구성한다. ORACLE_SID환경 변수를 변경하고 initSID.ora와 configSID.ora file을 새로운 SID에 맞게 수정한다. KEY80:/mnt3/rctest80/server/eykim> ORACLE_SID=CLONE;export ORACLE_SID CLONE:/mnt3/rctest80/server/eykim> cd dbs CLONE:/mnt3/rctest80/server/eykim/dbs> cp initKEY80.ora initCLONE.ora CLONE:/mnt3/rctest80/server/eykim/dbs> cp configKEY80.ora configCLONE.ora (11) configCLONE.ora file내에서 control_files부분에 기존의 control file이 아닌 3번 단계에서 받아둔 backup control file을 명시한다. 이때, controlfile이 coldbackup형태의 file copy한 것인 경우에는 recovery시에 ORA-01696, 00000, "controlfile is not a clone controlfile"오류 발생 configCLONE.ora내의 내용을 다음과 같이 수정 control_files = (/mnt3/rctest80/server/eykim/backup/backcon.ctl); (12) recovery set내에 포함된 datafile들, 즉 system, rbs, drop된 object가 포함된 tablespace에 해당하는 datafile들의 backup을 restore한다. 이 예에서는 /mnt3/rctest80/server/eykim/backup directory에 restore하였다. 기존의 datafile이 /mnt3/rctest80/server/eykim/oradata directory 이름을 포함하고 있기 때문에 oradata를 backup으로만 변경하면 backup datafile을 가리킬 수 있게 된다. (13) initCLONE.ora file 내에 다음과 같이 수정 혹은 추가한다. lock_name_space=CLONE은 전체 시스템내에서 유일한 값을 설정하여야 한다. 이 값을 설정함으로써, KEY80과 CLONE database가 같은 db_name (configSID.ora file에 정의)을 가져도 둘 모두 startup이 가능하게 된다. 12번에서 datafile위치가 oradata directory가 backup directory로 바뀌었기 때문에 db_file_name_convert를 아래와 같이 설정함으로써 alter database rename file의 효과를 얻게 된다. ifile = /mnt3/rctest80/server/eykim/dbs/configCLONE.ora lock_name_space=CLONE db_file_name_convert="oradata","backup" log_file_name_convert="oradata","backup" (14) 다음과 같이 clone database를 mount시키고, recovery하고자 하는 file은 online시킨다. SVRMGR> connect internal Connected. SVRMGR> startup nomount pfile=/mnt3/rctest80/server/eykim/dbs/initCLONE.ora SVRMGR> alter database mount clone database; SVRMGR> select name, status from v$datafile; NAME STATUS ---------------------------------------------- ----------- /mnt3/rctest80/server/eykim/backup/system01.dbf SYSOFF /mnt3/rctest80/server/eykim/backup/rbs01.dbf OFFLINE /mnt3/rctest80/server/eykim/backup/temp01.dbf OFFLINE /mnt3/rctest80/server/eykim/backup/tools01.dbf OFFLINE /mnt3/rctest80/server/eykim/backup/users01.dbf OFFLINE SVRMGR> alter database datafile '/mnt3/rctest80/server/eykim/backup/system01.dbf' online; SVRMGR> alter database datafile '/mnt3/rctest80/server/eykim/backup/rbs01.dbf' online; SVRMGR> alter database datafile '/mnt3/rctest80/server/eykim/backup/users01.dbf' online; (15) drop되기 이전 시점으로 recovery를 시도한다. SVRMGR> recover database using backup controlfile until time '1999-11-22:22:05:19'; ORA-00279: change 12290 generated at 11/22/99 20:44:32 needed for ORA-00289: suggestion : /mnt3/rctest80/server/eykim/arch/arc59.arc ORA-00280: change 12290 for thread 1 is in sequence #59 Specify log: { Log applied. Media recovery complete. (16) resetlogs로 open시킨다. SVRMGR> alter database open resetlogs; [NOTE] 만약 10 ~ 15번 사이에 문제가 생겨서 clone database를 포기하고 이전 production 시스템으로 돌아가고자 한다.면 ORACLE_SID를 이전 SID (이 예에서는 KEY80)로 수정 후 다음과 같이 작업한다. SQL> connect system/manager SQL> alter database recover datafile '/mnt3/rctest80/server/eykim/oradata/users01.dbf' SQL> alter tablespace users online; 이렇게 현재의 db상태로 맞춘 후 TSPITR을 다시 시도하고자 하는 경우, 이미 14, 15번이 진행되어 backup datafile에 변형이 있은 경우, 그 backup datafile을 이용하여 계속 작업을 진행할 수는 없다. (17) 다음과 같이 지워진 test와 test2 table이 복구된 것을 확인할 수 있다. 그러나 drop이후 시점에 만들어진 test3은 복구되지 않았다. SVRMGR> select * from scott.test; T ---------- 1 2 3 SVRMGR> select * from scott.test2; T ---------- 100 200 300 SVRMGR> select * from scott.test3; select * from scott.test3 * ORA-00942: table or view does not exist 4단계: recovery set에 포함되지 않은 연관된 objects에 대한 확인 및 조치 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (18) USERS tablespace와 다른 tablesapce에 연관된 object가 존재하는지 다음과 같이 확인한다. 즉, 이 예의 경우 TEST2 table은 USERS tablespace에 TEST2_IDX index는 TOOLS tablespace에 들어있음을 알 수 있다. 앞부분에 기술된 column정의부분은 조회 결과를 보기 좋게 하기 위해 추 가한 것이므로 생략해도 되고 임의의 자리수나 heading을 추가해도 된다. 아래 조회는 실제 TSPITR수행과 관계없이 언제나 같은 tablespace에 연관된 object가 저장되지 않은 것에 대한 정보를 보여준다. 그러므로 이것은 앞의 4단계에서 수행하여도 같은 결과를 나타낸다. sqlplus system/manager SQL> column OBJ1_OWNER heading "owner1" SQL> column OBJ1_OWNER format a6 SQL> column OBJ1_NAME heading "name1" SQL> column OBJ1_NAME format a5 SQL> column OBJ1_SUBNAME heading "subname1" SQL> column OBJ1_SUBNAME format a8 SQL> column OBJ1_TYPE heading "obj1type" SQL> column OBJ1_TYPE format a8 word_wrapped SQL> column TS1_NAME heading "ts1_name" SQL> column TS1_NAME format a8 SQL> column OBJ2_NAME heading "name2" SQL> column OBJ2_NAME format a5 SQL> column OBJ2_SUBNAME heading "subname2" SQL> column OBJ2_SUBNAME format a8 SQL> column OBJ2_TYPE heading "obj2type" SQL> column OBJ2_TYPE format a8 word_wrapped SQL> column OBJ2_OWNER heading "owner2" SQL> column OBJ2_OWNER format a6 SQL> column TS2_NAME heading "ts2_name" SQL> column TS2_NAME format a8 SQL> column CONSTRAINT_NAME heading "cname" SQL> column CONSTRAINT_NAME format a5 SQL> column REASON format a57 word_wrapped SQL> column REASON heading "reason" SQL> select * from sys.ts_pitr_check where (ts1_name in ('USERS') and ts2_name not in ('USERS')) or (ts1_name not in ('USERS') and ts2_name in ('USERS')) owner1 name1 subname1 obj1type ts1_name name2 subname2 obj2type ------ ----- -------- -------- -------- ----- -------- -------- owner2 ts2_name cname --------- ------------------------- reason ---------------------------------------------------------------- SCOTT TEST2 TABLE USERS TEST2_IDX INDEX SCOTT TOOLS Tables and associated indexes not fully contained in the recovery set (19) sys.ts_pitr_check에 나타난 object에 대한 처리를 결정한다. USERS에 포함되어 있지 않은 test2_idx index를 지울지 혹은 recovery set에 포함하여 다시 recovery를 시도할지를 결정하여야 한다. 여기에서는 drop한다. 이렇게 drop하지 않으면 이후에 수행하여야 하는 export시에 오류가 발생한다. SQL> drop index scott.test2_idx; 5 단계: Export ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (20) 다음과 같이 export를 수행한다. 실제 data를 export하는 것은 아니다. 그래서 일반적인 export시에는 각 object마다 몇개의 rows가 export되었다는 메시지가 나타나지만 이 경우에는 export하는 object만 나타난다. CLONE:/mnt3/rctest80/server/eykim> exp sys/manager file=test.dmp point_in_time_recover=y recovery_tablespaces=users About to export Tablespace Point-in-time Recovery objects... For tablespace USERS ... . exporting cluster definitions . exporting table definitions . . exporting table TEST . . exporting table TEST2 . exporting referential integrity constraints . exporting triggers . end point-in-time recovery Export terminated successfully without warnings. (21) CLONE db에서의 작업은 끝났으므로 clone db를 shtudown시킨다. SVRMGR> connect internal SVRMGR> shutdown 6단계: Import ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (22) ORACLE_SID를 바꾸어 원래의 database상태로 변경한다. ORACLE_SID=KEY80;export ORACLE_SID (23) TSPITR의 대상이 되는 USERS tablespace를 recover할 수 있는 offline 상태로 변경한다. KEY80:/mnt3/rctest80/server/eykim> sqlplus system/manager SQL> alter tablespace users offline for recover; (24) users01.dbf를 backup file로 대치한다. KEY80:/mnt3/rctest80/server/eykim> cd backup KEY80:/mnt3/rctest80/server/eykim/backup> cp users01.dbf ../oradata (25) 다음과 같이 import시킨다. 이것도 20번의 export와 마찬가지로 실제 data를 import하지는 않는다. 그러므로 몇 rows가 import되었다는 메시지는 나타나지 않는다. 실제 이미 data는 datafile내에 포함되어 있다. imp sys/manager file=test.dmp point_in_time_recover=y Export file created by EXPORT:V08.00.04 via conventional path About to import Tablespace Point-in-time Recovery objects... . importing SCOTT's objects into SCOTT . . importing table "TEST" . . importing table "TEST2" . importing SYSTEM's objects into SYSTEM . importing SCOTT's objects into SCOTT Import terminated successfully with warnings. 7 단계: 확인 및 삭제된 object생성 작업 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (26) 22번 단계에서 offline시켰던 users tablespace를 online시킨다. sqlplus system/manager SQL> alter tablespace users online; (27) 18번 단계에서 drop한 test2_idx index를 새로 생성한다. SQL> connect scott/tiger Connected. SQL> create index test2_idx on test2(t) tablespace tools; (28) 다음과 같이 원하는 상황으로 복구가 되었는지를 확인한다. SQL> select * from tab; TNAME TABTYPE CLUSTERID --------------------- ------- -------------- TEST TABLE TEST2 TABLE SQL> select * from test; T ---------- 1 2 3 SQL> select * from test2; T ---------- 100 200 300 SQL> select index_name, table_name from user_indexes; INDEX_NAME TABLE_NAME ------------- -------------------------- TEST2_IDX TEST2 TEST_IDX TEST Reference Documents ------------------- |
Comment | |||
---|---|---|---|
등록된 코멘트가 없습니다. |