1. 논문 정보
서지 정보
제목: Forensic Recovery of SQL Server Database: Practical Approach
저자: HOYONG CHOI, SANGJIN LEE, DOOWON JEONG
학술지: IEEE Access. Volume 9, 2021, 페이지 14564-14575
발행 연도: 2021.
DOI: 10.1109/ACCESS.2021.302505
url: https://ieeexplore.ieee.org/document/9328241
키워드
데이터베이스, 포렌식, SQL 서버, MSSQL, 서버.
2. Study
2-1. 연구 배경 및 목적
수사 과정에서 데이터베이스 포렌식이 점점 중요해지는 가운데, 특히 삭제된 데이터를 복구하는 것은 데이터베이스 변조 및 안티 포렌식을 방지하는 데 핵심적인 기술이다. 이전 연구는 삭제된 데이터를 복구하려 트랜잭션 로그나 저널 로그를 복구하는 데 초점을 두었다. 그러나 주기적으로 로그를 삭제하도록 설정하거나, 중요한 증거가 담긴 로그가 새로운 로그로 덮어씌워진 경우 기존의 방법을 사용할 수 없다는 새로운 문제점이 대두되고 있다.
과거 SQLite나 EDB 같은 작은 규모의 데이터베이스를 복구하는 연구는 있었지만, 대용량 데이터베이스에서 삭제된 데이터를 복구하거나 데이터 파일 구조를 설명하는 연구는 존재하지 않았다. 하여 이 연구에서 가장 널리 사용되는 대규모 데이터베이스 중 하나인 MSSQL을 조사한다. 해당 연구는 MSSQL 저장 엔진을 중점으로 분석해, MSSQL 데이터파일의 내부 구조와 저장 매커니즘을 파악하고, 저장 엔진을 기반으로 한 MSSQL 데이터 복구 알고리즘을 소개한다.
이를 바탕으로 테이블과 기록을 복구하는 방법을 제시한다. low level에서 데이터에 접근하므로 다양한 MSSQL의 버전에 호환된다. 특히 LLOB(대용량 객체) 등 멀티미디어 데이터를 포함한 모든 데이터 유형을 복구한다.
2-2. 기존 모델 분석
데이터베이스 포렌식 분야에서는 일반적인 절차를 제시하는 DBMS 독립적 모델과, 특정 데이터베이스에 맞춘 DBMS 종속적 모델이 연구되어 왔다. 독립적 모델의 경우, Khanuja와 Adane은 일반적인 디지털 포렌식 절차(식별–수집–분석–해석–보고–보존)에 기반한 프레임워크를 제안했고, Flores와 Jhumka는 데이터베이스 감사 기록을 활용해 증거의 연계성(chain of custody)을 충족하는 모델을 제시하였다. 이외에도 다수의 연구에서 데이터 모델 정리, 아티팩트 수집, 단계적 분석 절차를 포함한 모델이 제안되었다. 그러나 DBMS의 복잡성과 다차원적 특성 때문에 이러한 보편적 모델은 실제 현장에서 적용하기 어렵다는 한계가 있다. 이에 따라 MSSQL, Oracle, MySQL, SQLite와 같은 특정 DBMS의 구조에 맞춘 종속적 분석 방법이 연구되었으며, 특히 MSSQL의 경우 로그 파일이나 DBMS 제공 함수에 의존하는 방식이 주를 이루었다.
용어
- DBMS 독립적 모델
특정 데이터베이스 제품(MSSQL, Oracle, MySQL 등)에 구애받지 않고, 공통된 디지털 포렌식 절차를 적용하는 일반화된 분석 모델. 주로 디지털 포렌식의 표준 절차인 식별(identification) – 수집(collection) – 분석(analysis) – 해석(validation & interpretation) – 보고(reporting) – 보존(preservation) 단계로 이루어진다. 범용적으로 적용 가능하며, DBMS가 어떤 종류이든 일정 수준의 포렌식 절차를 보장할 수 있지만, DBMS의 내부 구조가 매우 복잡하고 서로 다르기 때문에, 실제 현장에서 데이터를 복구하거나 삭제 기록을 찾아내는 데에는 구체성이 부족하다.
-> 큰 틀의 포렌식 절차
- DBMS 종속적 모델
특정 DBMS(MSSQL, Oracle, MySQL, SQLite 등)의 내부 구조, 로그 시스템, 저장 메커니즘에 특화된 포렌식 분석 모델. DBMS마다 다른 데이터 파일 구조(.mdf, .ndf, .ldf 등)와 저장 규칙, 무결성 검증 방식(Checksum, TornPageDetection 등)을 깊이 분석하여 해당 DBMS 전용 복구·탐지 방법을 제시한다. 실제 데이터 복구나 증거 확보에서 높은 정확도와 실용성을 보장. 예를 들어 MSSQL에서는 로그 기반 분석 대신 엔진 기반 분석으로 삭제 레코드를 직접 복구 가능하다. 그러나 특정 DBMS에 종속되므로 다른 DBMS에는 그대로 적용하기 어렵고, 새로운 DBMS가 등장하면 별도의 연구가 필요하다.
-> 특정 DBMS에 특화된 분석 프레임워크
- 종속적 분석 방법
종속적 모델을 구현하는 구체적 방법론으로, 특정 DBMS의 파일 구조와 메커니즘을 해독하고 맞춤형 알고리즘을 적용하는 방식.
2-3. 연구 방법 (1) MSSQL 내부 구조 분석
페이지 구조
MSSQL의 데이터 파일은 8KB 크기의 페이지 단위로 구성된다.

각 페이지는 페이지 헤더(약 96byte 크기의 메타데이터), 데이터 행(실제 레코드가 저장되는 영역), 행 오프셋 배열(각 행의 시작 위치를 가리키는 인덱스)로 이루어진다. 페이지 헤더에는 페이지 타입, 슬롯 수, 페이지 ID, 파일 ID, 체크섬 등 복구에 필수적인 메타데이터가 기록된다. 구체적으로 다음과 같다.
- Type: 페이지 종류. 어떤 유형의 데이터가 저장되어 있는지 확인 가능. ex) 1=데이터 페이지, 3/4=LOB 페이지.
- SlotCnt: 슬롯 수. 현재 페이지에 저장된 레코드(=슬롯)의 개수.
- Page Object ID: 페이지가 어떤 객체(table, index) 등에 속하는지 알려주는 식별자
- Page ID: 데이터 파일 내 페이지의 순번
- File ID: 해당 페이지가 속한 물리적 파일(.mdf, .ndf 등)을 식별
- Cheksum/TornBits: 무결성 검증을 위한 값 -> 손상 여부를 판별.
이러한 정보들은 특정 페이지에 어떤 테이블 데이터가 저장되었는지를 파악하는 단서가 된다.

페이지 헤더의 Page Object ID를 통해 이 페이지가 어떤 테이블에 속하는지 식별하고, System Table과 매칭해 Object ID와 테이블 이름 및 구조를 연결한다. Row offset Array로 해당 테이블의 레코드들이 페이지 어디에 저장되어 있는지, 삭제로 인해 빈 공간이 있는지 확인한다. 이러한 과정을 통해 이 페이지가 어느 테이블의 데이터인지, 여기에 몇 개의 레코드가 저장/삭제되었는지 알 수 있다.
레코드 저장 방식
레코드는 크기에 따라 In-row Data, Row-overflow Data, LOB Data, MAX-length Data로 저장된다.

In-row는 고정 길이 데이터와 가변 길이 데이터를 순차적으로 저장하는 구조를 가진다. 레코드가 너무 커지면 여러 페이지에 걸쳐 저장되며, 이 경우 포인터를 통해 LOB 페이지(텍스트·이미지 등 대용량 데이터 저장용)와 연결된다.
- In-row Data: 레코드 크기가 8,060바이트 이하일 때, 레코드 전체가 한 페이지(8KB)에 그대로 저장되는 방식. 가장 단순한 저장 방식, 삭제 복구 시 구조 해석이 비교적 쉬움
- Row-overflow Data: 레코드 전체 크기가 8,060바이트를 초과하지만, 각 컬럼 데이터는 여전히 8,060바이트 이내일 때 사용. 일부 컬럼 데이터가 overflow 페이지로 이동되고, 원래 페이지에는 그 위치를 가리키는 포인터만 저장됨.레코드가 여러 페이지에 분산 저장되므로, 복구 시 페이지 간 연결 정보를 따라가야 함
- LOB Data: 길이 제한이 없는 대용량 객체 데이터(텍스트, 이미지, 바이너리 등)를 저장할 때 사용. 데이터가 여러 페이지(Type 3, Type 4)로 나뉘어 저장됨. B-tree 구조로 관리되어 각 조각을 연결. 대용량 멀티미디어 데이터까지 복구 가능하다는 점에서 포렌식적으로 매우 중요

- MAX-length Data: varchar(MAX), nvarchar(MAX), varbinary(MAX) 같은 최신 대체 타입. 8,060바이트 이하일 경우 → In-row Data처럼 한 페이지에 저장. 8,060바이트 초과 시 → LOB Data처럼 별도의 페이지에 분산 저장. 하나의 데이터 타입이 상황에 따라 In-row 또는 LOB로 저장되므로, 복구 시 데이터 크기와 저장 포맷을 정확히 판별해야 함.
무결성 검증 방식
페이지 무결성 검증은 Checksum과 TornPageDetection 두 방식이 있으며, 후자는 NTFS의 Fixup Array와 유사하게 바이트 단위 교체를 통해 손상 여부를 검증한다. 이 특성을 고려하지 않으면 삭제 데이터 해석 과정에서 오류가 발생할 수 있다.
- Checksum: 8KB의 페이지 전체에 대해 체크섬 값을 계산해 페이지 헤더에 기록한다. 페이지를 읽을 때 다시 체크섬을 계산해 이전의 저장된 값과 비교하고, 일치하지 않으면 손상된 것으로 판단한다. 비트 하나라도 바뀌면 탐지 가능하기에 오류를 쉽게 검출할 수 있다. 단, 페이지의 손상 여부만 판별할 뿐 손상 원인을 밝힐 수 없고 내용 복구 역시 불가능하다.
- TornPageDetection: 페이지를 디스크에 기록할 때 전원이 꺼지는 등 장애가 발생하면, 페이지 일부 단위만 쓰이고 나머지가 불완전하게 기록될 수 있다. 이를 방지하기 위해 특정 바이트의 하위 2비트를 TornBits 값으로 치환하여 저장한다. 페이지를 다시 불러올 때, TornBits와 교체된 비트를 비교해 손상 여부를 판단한다. 디스트 입출력 오류로 일부 바이트가 꺠지더라도 TornBits와의 대조를 통해 손상 여부를 감지할 수 있는 구조이다. NTFS 파일 시스템은 섹터 손상을 막기 위해 페이지 끝부분에 교정용 값을 넣어두는데, MSSQL의 TornPageDetection은 이 개념을 차용한 것이다.
- 발생 가능한 오류에 대해서
TornPageDetection 방식으로 저장된 데이터인데 Checksum방식으로 해석하면 데이터 자체가 손상되었다고 판단할 수 있다. 데이터가 살아 있는데 복구 툴이 이를 해석하지 못해 삭제된 레코드를 복구하지 못하는 오류가 발생할 수 있다. 반대로, TornBits를 무시할 경우, 일부 바이트가 교체된 상태 그대로 해석되므로 레코드 데이터가 잘못 해석되거나 깨진 문자열, 손상된 숫자값으로 인식되므로 결국 원본 레코드를 정확히 복구할 수 없게 된다.
증거로 활용할 수 있는 삭제 데이터가 있음에도 불구하고 손상된 데이터로 간주해 누락되는 경우가 생길 수 있다. 예를 들어, 본 연구에서도 Stella Repair 툴이 checksum만 고려하고 TornPageDetection을 고려하지 않아 데이터셋 A'와 B'의 레코드를 아예 복구하지 못한 사례를 언급하였다.

(70.0/0은 포렌식 도구가 데이터셋 A의 삭제된 기록 중 70%을 복구하지만, A'에서는 0%을 복구한다는 것을 의미한다.)
시스템 테이블 정밀 분석
삭제된 레코드 복구에는 MSSQL 내부의 시스템 테이블 다섯 가지가 핵심적 역할을 한다.
1. syscolpars
- 데이터베이스 내 모든 테이블(시스템 테이블 + 사용자 테이블)의 컬럼 정보를 저장.
- 컬럼 이름(name), 데이터 타입(int, carchar, nvarchar), 컬럼의 크기(length), 컬럼 순서(order), NULL 허용 여부, 시간/숫자 타입의 세부 매개변수 등 컬럼 정보 저장
- 삭제된 레코드를 다시 해석할 때 이 자리에 어떤 타입의 데이터가 와야하는지 알려준다.
- sysschobjs
- 데이터베이스에 존재하는 모든 테이블의 객체 정보를 저장.
- 테이블 이름(name), 테이블 유형(system/user), 컬럼 개수, Table Object ID 등 테이블 기본 정보 저장
- 페이지 헤더에 기록된 Page Object ID와 sysschobjs의 Table Object ID를 매칭 →
“이 페이지는 어떤 테이블에 속한다” 라고 특정 가능. 테이블 이름까지 확인할 수 있어, 증거 수집 시 어떤 테이블의 데이터인지 식별할 수 있음.
- sysiscols
- 각 테이블의 인덱스 컬럼 정보를 저장.
- 인덱스 대상 컬럼의 위치, 클러스터형 인덱스(Primary Key) 여부, 인덱스 설정으로 인해 실제 저장 순서가 바뀌는 경우의 순서 정보 등
- 레코드를 복구할 때, 컬럼이 정의된 순서대로 저장되지 않을 수 있다. 예를 들어 Primary Key를 지정하면, 해당 컬럼이 앞쪽으로 배치되면 기존 저장 순서와 달라진다. sysiscols는 이를 방지하는 역할을 한다.
- sysrowsets
- 테이블별 Partition ID를 관리.
- rowsetid(Partition ID), idmajor(Table Object ID)
- Partition ID와 Allocation Unit ID 매핑
- sysschobjs에서 얻은 Table Object ID와 sysrowsets의 idmajor를 매칭해 Partition ID를 찾는다. Partition ID는 곧 sysallocunits와 연결되어, 페이지가 실제 어디에 할당되었는지를 추적하는 열쇠 역할을 한다.
- sysallocunits
- Partition ID → Allocation Unit ID → Page Object ID로 이어지는 매핑을 담당.
- ownerid(Partition ID), auid(Allocation Unit ID)
- 특정 테이블의 Partition ID를 sysrowsets에서 찾은 뒤, sysallocunits에서 Allocation Unit ID로 변환한다. 이때 Allocation Unit ID는 다시 Page Object ID 계산식에 사용된다. 최종적으로, 테이블이 실제 어떤 페이지에 저장되어 있는지를 정확히 찾아낼 수 있다.
이들 테이블을 종합적으로 분석함으로써 사용자 테이블의 구조와 위치를 파악할 수 있다.
2-4. 연구 방법 (2) 제안된 복구 알고리즘
4단계 절차 ① 페이지 스캔
데이터 파일 전체를 페이지 단위로 스캔하여 Page ID와 Page Object ID를 식별한다. 이를 통해 어떤 페이지가 특정 테이블과 연관되어 있는지를 추적할 수 있다.
4단계 절차 ② 시스템 테이블 정보 수집
syscolpars를 분석하여 시스템 테이블의 스키마와 컬럼 구조를 식별한다. MSSQL 버전에 따라 스키마 구조는 조금씩 다르지만, 복구에 필요한 핵심 컬럼은 공통적으로 유지되므로 버전에 무관하게 적용할 수 있다.
4단계 절차 ③ 사용자 테이블 정보 수집
sysschobjs와 syscolpars를 연결하여 사용자 테이블의 컬럼 이름·데이터 타입·순서를 파악한다. sysiscols를 통해 인덱스 여부와 컬럼 저장 순서를 확인하고, sysrowsets와 sysallocunits를 활용해 실제 데이터가 저장된 페이지의 위치 정보를 확보한다.
1. Table information in sysschobjs
- 대상 사용자 테이블의 신원을 확정한다. sysschobjs에서 테이블 이름, 테이블 유형(시스템/사용자), Table Object ID, 컬럼 수 등을 얻는다. 이 Object ID가 이후 모든 매핑의 축이 된다. 즉 이 페이지가 어느 table의 것인가를 판별하는 기준값.
- Column information in syscolpars
- 컬럼 type, 길이, 저장 순서, null 허용 여부 등을 확정한다.
- 이를 바탕으로 CREATE TABLE query를 사용하여 복구된 기록을 저장할 임시 테이블을 생성한다.
- Clustered index column information in sysiscols
- 클러스터형 인덱스 때문에 달라지는 실제 저장 순서를 반영한다.
- mssql에서 처리 속도를 높이려 인덱싱이 발전하고 있다. 인덱싱 사용 여부를 반드시 확인해야 한다. 아닐 경우 오류 발생O. ex) 인덱싱의 전형적인 기능인 primary key를 세팅하면 순서가 뒤바뀐다.
- Location information in sysrowsets and sysallocunits
- (1)~(3)의 과정을 거치며 해당 테이블이 어떤 컬럼 구조를 가지고 있는지 파악 완료. 그러나 데이터 파일에서 해당 테이블의 레코드가 실제로 어느 페이지에 저장되어 있는지 모르면 복구를 시작할 수 없다. 따라서 테이블 → Partition ID → Allocation Unit ID → Page Object ID → 페이지 위치(오프셋) 순서로 진행되는 추적 과정이 필요하다. 구체적으로는 다음과 같다.
- Sysrowsets 테이블에 rowsetid라는 값이 있는데, 이 값이 Partition ID. 이때 idmajor 값과 앞서 확보한 Table Object ID를 비교해서 해당 테이블의 Partition ID를 얻는다.
- sysallocunits 테이블은 Partition ID와 Allocation Unit ID를 연결하는 기능을 한다. 따라서 Allocation Unit ID를 얻을 수 있음.
- 이렇게 얻어낸 Partition ID와 Allocation Unit ID로 다음의 계산을 진행한다.

+) 페이지 오프셋은 offset=PageID * 8192 공식을 따른다. 페이지 헤더에 File ID와 Page ID가 들어 있고, 이 offset을 구하면 실제 record에서 해당 페이지 데이터를 읽을 수 있다.
정리
테이블 ID → Partition → Allocation Unit → Page Object → Page/File → 물리적 오프셋 순서로 좁혀가며 최종적으로 테이블 레코드가 들어 있는 페이지의 물리적 위치를 알아낸다.
용어
- Partition ID: 테이블 데이터를 쪼개 저장할 때 생기는 단위 ID. sysrowsets를 통해 얻는다.
- Allocation Unit ID: 파티션을 실제로 페이지 단위로 할당할 때 쓰는 ID. sysallocunits 과정을 통해 얻는다.
- Offset: 실제 파일에서 페이지가 시작되는 위치. Page ID * 8192로 계산.
4단계 절차 ④ 삭제 레코드 복구
마지막 단계에서는 데이터 페이지의 Unallocated Area(삭제 후 비워진 영역)를 탐지하고, 고정 길이/가변 길이 데이터를 구분해 레코드를 재구성한다. 알고리즘은 레코드의 컬럼 순서와 데이터 타입을 참조하여 데이터를 해석하고, 최종적으로 INSERT 쿼리 형태로 복구된 레코드를 출력한다.
1. Accesing data page : 대상 페이지 집합을 엶.
- 사용자 테이블 정보 수집 단계에서 계산한 위치 정보를 통해 확인됨.
- 찾는 테이블의 데이터가 들어 있는 페이지를 열면 페이지 헤더, 데이터 행, row offset array 세 부분이 있음. 이를 확인하는 과정.
- Identifying unallocated area : Row Offset 기반으로 삭제 영역 정확히 찾는 단계.
- 삭제된 데이터는 완전히 지워지지 않고 unallocated area, 즉 빈칸으로 남는다. 이를 찾는 과정.
- 페이지 맨 끝에는 row offset array이 있어서 각 record의 시작 위치를 적어둔다. 일종의 좌표 리스트 개념. 이 좌표와 record의 길이를 비교해서 삭제된 흔적, 빈칸을 찾는다. 본 연구에서는 빈칸이 생기는 경우로 다음 세 가지 케이스를 제시함. 페이지 헤더 아래(첫 레코드 위쪽), 레코드와 레코드 사이, 마지막 레코드와 raw offset array 사이

- Reconstructing deleted records : INSERT query로 재구성.
- 이제 빈칸에서 삭제된 record를 다시 재구성함.
- 복구 알고리즘 : 빈칸에 있는 데이터 조각을 읽음 -> 고정 길이 컬럼은 컬럼 규칙대로 잘라내고, 가변 길이 컬럼일 경우 offset 배열을보고 시작과 끝 위치를 알아낸 뒤 그 부분을 잘라냄 -> 다 모아서 본래 record를 조립 -> 최종적으로 INSERT INTO table VALUES(); 등의 query를 작성한다.

2-5. 실험 및 결과
데이터셋 및 비교 도구
MSSQL 2017 환경에서 자체 제작한 데이터셋을 활용.
데이터셋 A/A′: 25만 개 레코드, 약 200MB, In-row 데이터만 포함
데이터셋 B/B′: 5만 개 레코드, 13.8GB, LOB 데이터 포함
여기서 A/B는 Checksum, A′/B′는 TornPageDetection을 적용.
비교 대상은 ApexSQL Recover, Stellar Repair, SysTools SQL Recovery 등의 복구 툴.
평가 지표
복구된 데이터의 무결성을 보장하기 위해 원본 데이터와 해시값을 비교하였다. 해시값이 일치할 경우에만 성공으로 간주하였고, 부분 복구는 실패로 처리하였다.
결과
단순 삭제(1천~5천 레코드 삭제) 상황에서는 제안 기법이 100% 복구에 성공했으며, Stellar Repair는 일부 데이터를 복구하지 못했다.
삭제 후 재삽입 상황에서는 삽입 레코드 수가 많을수록 복구율이 낮아졌지만, 제안 기법은 ApexSQL보다 안정적인 성능을 보였다.
TornPageDetection이 적용된 데이터셋에서는 Stellar Repair가 전혀 복구하지 못한 반면, 제안 기법은 완전 복구에 성공하였다.
특히 LOB 데이터 복구에서 SysTools가 일부 끝부분 데이터를 누락한 반면, 제안 기법은 모든 데이터를 완전 복구하였다.
기여
본 연구는 MSSQL의 내부 구조를 체계적으로 해석하고, 삭제 레코드를 완전하게 복구할 수 있는 버전 독립적 알고리즘을 제시했다. 또한 오픈소스 도구로 구현을 공개하여 학계와 실무에서 활용할 수 있도록 기여했다. 이는 기존 상용 툴의 한계를 극복한 중요한 성과로, 디지털 포렌식 현장에서 법적 증거 확보에 실질적인 도움을 줄 수 있다.
3. 마치며
3-1. 의의 및 한계
본 연구는 MSSQL 데이터베이스에서 삭제된 레코드를 복구하기 위해 내부 구조를 정밀히 분석하고, 버전에 독립적인 알고리즘을 제안하였다는 점에서 학문적·실무적 의의를 가진다. 특히 시스템 테이블과 페이지 구조를 기반으로 한 복구 절차를 통해, 기존 상용 툴들이 복구하지 못하던 LOB 데이터나 TornPageDetection 환경에서도 안정적으로 데이터를 복구할 수 있음을 실험으로 입증하였다.
다만 한계도 존재한다.
- 제안된 방법은 MSSQL에만 적용 가능하여 Oracle, MySQL, PostgreSQL 등 다른 DBMS에는 직접 적용하기 어렵다.
- GUI 기반 사용자 친화적 환경이나 대규모 데이터셋에서의 성능 비교가 이루어지지 않았다.
- 삽입·삭제가 반복되는 복잡한 시나리오에서는 overwriting으로 인해 복구율이 낮아지는 문제가 확인되었다.
3-2. 향후 연구
향후 연구에서는 다음과 같은 방향으로 발전이 필요하다.
- MSSQL을 넘어 Oracle, MySQL, PostgreSQL 등 다양한 DBMS의 내부 구조를 해석하고, 동일한 엔진 기반 복구 기법을 확장한다.
- 복구 속도와 효율성을 개선하기 위한 최적화 연구, GUI 기반 도구 개발을 통해 실제 수사·감사 현장에서 비전문가도 사용 가능하도록 활용성을 높여야 한다.
- 오버라이팅이 발생한 데이터나 부분적으로 손상된 페이지에서도 가능한 한 많은 fragment를 복원할 수 있는 고도화된 알고리즘이 필요하다.
3-3. 관련 프로젝트를 한다면?
MSSQL파일을 열어서 어떤 페이지에 삭제된 부분이 있는지 시각적으로 표시하는 기능을 가진 간단한 GUI 툴을 만들 수 있을 것 같다. 더불어, 삭제되지 않은 레코드를 컬럼 단위로 파싱해 JSON, CSV 등으로 내보내는, 논문 알고리즘1의 단순화 버전 실습을 하고 싶다. (삭제된 레코드를 그대로 구현하는 실습은 당장 혼자 진행하기 어려워서.. 삭제되지 않은 레코드를 대상으로, 논문이 제시한 알고리즘 1이 하는 일을 아주 단순화해서(고정/가변 컬럼 분리 -> offset 기준 자름 -> 타입 디코딩 -> row 재구성) 실습해본다.)