-/Pandas

Pandas: 원본 데이터와 정제 데이터의 스키마 설계

106. 2026. 9. 3. 17:31

원본 테이블 raw_daily_price

데이터 품질을 판단하기 전에 수집한 데이터를 최대한 그대로 저장하는 공간

  • 문자열 위주로 저장
  • 제약조건 최소화
  • 수집시간·출처 기록
52000
N/A
-
 

예를 들어 close 값이 위처럼 섞여 있어도 일단 그대로 저장한다.

왜냐하면 원본 저장 단계의 목적은 “이 값이 올바른가?”를 판단하는 것이 아니라 “수집한 것을 잃지 않고 보관하는 것”이기 때문이다.


왜 모든 컬럼을 문자열로 받을까?

close VARCHAR(30)
date  VARCHAR(20)

 

원본 데이터를 문자열로 저장하면 예상하지 못한 형식의 값이 들어와도 데이터를 잃지 않고 보관할 수 있다.

 

52000
N/A
-
1,250
 

예를 들어 숫자가 들어올 거라고 예상했는데 위의 값이 들어온다고 가정해보자.

INT NOT NULL로 만들어두면 N/A 같은 값 때문에 저장 자체가 실패한다.

하지만 VARCHAR라면 전부 저장할 수 있다.


왜 원본을 꼭 저장해야 할까?

가장 중요한 이유는 정제 로직이 틀릴 수도 있기 때문이다.

"1,250" → 125
 

예를 들어 정제 과정에서 실수로 잘못 변환했다고 해보자.

원본이 없다면 무엇이 잘못됐는지 확인하거나 다시 처리하기 어렵다.

반대로 원본이 남아 있다면 데이터를 다시 처리할 수 있다.

원본 데이터
   ↓
정제 로직 수정
   ↓
다시 정제
 

제약조건이 강한 테이블에 원본을 바로 넣으면?

정제되지 않은 데이터를 강한 타입과 제약조건이 있는 테이블에 바로 넣으면 저장 실패가 발생할 수 있다.

CREATE TABLE demo_strict(
    code VARCHAR(20),
    date DATE,
    close INT NOT NULL
);
 

여기서는 close가 반드시 정수여야 한다.

그런데 원본 데이터에 N/A나 -가 있으면 저장할 수 없다.

try:
    ...
except:
    conn.rollback()

 

이런 경우에는 try-except 로 실패를 처리할 수 있다.

 

즉, 수집 단계부터 강한 제약을 걸면 비정상 데이터 하나 때문에 원본 자체를 잃을 수 있다.


rollback()

rollback은 현재 트랜잭션에서 실행한 변경사항을 취소하고 이전 상태로 되돌리는 것이다.

conn.rollback()
 
 

예를 들어 INSERT 중 문제가 발생했다면 아래처럼 진행이 된다.

INSERT 시도
   ↓
에러 발생
   ↓
rollback()
   ↓
이번 변경 취소
 

 


정제 테이블 daily_price

원본을 안전하게 저장한 뒤에는 정제 과정을 거쳐 실제 분석이나 서비스에서 사용할 테이블로 옮긴다.

이 테이블에서는 데이터의 의미에 맞게 타입과 제약조건을 설정한다.

code        VARCHAR(20) NOT NULL
date        DATE NOT NULL
close       BIGINT
volume      BIGINT
changeRate  DECIMAL(6, 2)
 

 

raw_daily_price
      ↓
   데이터 정제
      ↓
daily_price
 

원본 테이블 vs 정제 테이블

 

원본 테이블 = 택배가 도착한 창고
정제 테이블 = 검사하고 정리해서 진열한 상품
 

 


금액에 float를 사용하지 않는 이유

정수 단위의 금액은 오차가 생길 수 있는 FLOAT보다 BIGINT 같은 정수형이 적합하다.
close BIGINT
 

주가처럼 정수 단위의 금액이라면 BIGINT 같은 정수형을 사용할 수 있다.

float는 이진 부동소수점 방식이라 특정 소수를 정확하게 표현하지 못할 수 있다.

0.1 + 0.2
 

예를 들어 개념적으로 0.1 + 0.2가 정확히 0.3으로 표현되지 않는 문제가 생길 수 있다.

따라서 정확한 금액 계산이 필요할 때는 FLOAT를 무조건 사용하는 것은 피하는 것이 좋다.

다만, 금액 자체가 정수라면 BIGINT가 자연스럽다.


비율에 DECIMAL

changeRate DECIMAL(6, 2)
 

DECIMAL은 소수점 자릿수를 정확하게 관리할 수 있는 자료형이다.

DECIMAL(6, 2)

전체 최대 6자리
소수점 아래 2자리
 

예를 들어 1234.56같은 값을 저장할 수 있습니다.

비율이나 금액처럼 정확한 소수 계산이 중요한 데이터에 적합하다.


날짜는 DATE

date DATE NOT NULL
 

날짜를 문자열로 저장할 수도 있지만  DATE로 저장해야 날짜 비교·정렬·계산을 제대로 활용할 수 있다.

날짜 비교
기간 검색
정렬
YEAR(), MONTH() 등 날짜 함수
 

코드는 왜 VARCHAR?

code VARCHAR(20)
 

종목 코드처럼 숫자로 보이는 값이라도 실제로는 계산하기 위한 숫자가 아니라 식별자다.

예를 들어 001234라는 숫자를 저장하면 1234가 되어 앞의 0이 사라질 수 있다.

따라서 계산하지 않는 식별번호는 앞자리 0을 보존하기 위해 VARCHAR(문자열)로 저장하는 것이 적합하다.


UNIQUE KEY

UNIQUE KEY uk_code_date (code, date)

 

UNIQUE(code, date)는 같은 종목의 같은 날짜 데이터가 중복 저장되는 것을 막는다.

G0001 / 2026-09-01
G0001 / 2026-09-01   ← 중복 X
 

 

code 하나만 보면 여러 날짜가 있기 때문에 중복될 수 있고, date 하나만 봐도 여러 종목이 있으므로 중복될 수 있다.

code + date
 

하지만 code + date 조합을 사용함으로써 데이터가 중복 저장되는 것을 막는다.


collected_at과 updated_at

collected_at은 수집 시점, updated_at은 데이터가 마지막으로 변경된 시점을 기록한다.

collected_at DATETIME DEFAULT CURRENT_TIMESTAMP
 

원본에는 collected_at을 두어 언제 수집했는지 기록한다.

updated_at DATETIME
DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
 

정제 테이블에는 updated_at을 사용하여 처음 저장된 시간뿐 아니라 데이터가 수정된 시간도 자동으로 기록할 수 있다.


전체 흐름

원본은 일단 받아두고, 판단은 정제 단계에서 한다.

외부 데이터 수집
      ↓
raw_daily_price
일단 원본 그대로 저장
      ↓
검증 / 정제 / 형변환
      ↓
daily_price
서비스에 사용할 깨끗한 데이터 저장

 

  • 원본 테이블 : 수집 데이터를 잃지 않도록 최소한의 제약으로 그대로 저장한다.
  • 문자열 저장 : 예상하지 못한 값도 받을 수 있도록 원본 단계에서는 문자열을 주로 사용한다.
  • 원본 보존 : 정제 로직이 잘못되어도 원본으로 돌아가 다시 처리할 수 있다.
  • 정제 테이블 : 검증이 끝난 데이터를 실제 의미에 맞는 자료형으로 저장한다.
  • rollback() : 문제가 생긴 트랜잭션의 변경사항을 취소한다.
  • BIGINT : 정수 단위의 큰 숫자나 금액을 정확하게 저장한다.
  • DECIMAL : 정확한 소수 자릿수가 필요한 비율 등에 사용한다.
  • DATE : 날짜 비교·정렬·계산을 위해 날짜 전용 타입을 사용한다.
  • VARCHAR 코드 : 계산용 숫자가 아닌 식별자는 앞자리 0 보존을 위해 문자열로 저장한다.
  • UNIQUE(code, date) : 동일 종목·동일 날짜 데이터의 중복 저장을 막는다.