# Django
이번 시간에는 정규화 이후 Feature단계에 관련하여 학습하였다. 정규화된 데이터를 분석이나 AI가 바로 활용할 수 있도록 데이터를 가공하는 단계라고 이해하였는데, 여기서 Medallion Architecture의 개념을 배우게 되었다.
* Medallion Architecture란 data lake에서 데이터를 원본 → 정제 → 활용 가능한 형태로 가공해나가는 설계 방식이다.
Bronze(raw데이터) → Silver(정제·표준화) → Gold(분석·AI 활용 계층)
과정에서 데이터가 신뢰하고 사용할 수 있는 상태인지 검사하는 품질 규칙(데이터 품질)에 관해서 학습하였다.
주요 기준은 다음과 같다.
| 완전성 (Completeness) | 필수값이 빠지지 않았는 |
| 유효성 (Validity) | 값이 허용 범위와 조건을 지키는가 |
| 유일성 (Uniqueness) | 중복되어서는 안 되는 값이 중복되지 않았는가 |
| 참조 무결성 (Referential Integrity) | 테이블 간 관계가 정상적인가 |
| 일관성 (Consistency) | 서로 관련된 값들이 모순되지 않는가 |
| 정확성 (Accuracy) | 값이 실제 사실과 맞는가 |
| 시점 품질 (Timeliness) | 데이터가 필요한 시점에 최신 상태인가 |
위 사항들을 굳이 Medallion Architecture에 적용한다면 데이터를 정제·표준화하는 Silver layer에 적용해야겠다고 생각했다.
이후 이번 단위기간 내에서 가장 어려웠던 개념인 Feature 개념으로 넘어갔다.
교육과정명에도 명시되어있듯이 실질적으로 AI가 활용할 수 있는 데이터, AI-ready 데이터를 가공하는 과정은 비즈니스나 목적을 명확하고 가지고 접근해야하는 만큼, 도메인 지식이나 활용성이 뛰어나야함을 느꼈다.
Medallion Architecture에서 Gold layer가 Feature와 동일한 개념이구나 라고 생각했지만, 학습을 하다보니 Feature는 Gold layer가 아닌 Silver 데이터를 이용하여 Gold layer에 들어갈 분석·AI용 데이터셋을 만들어내는 엔지니어링 과정인것도 깨달았다.
Feature 엔지니어링에는 다음과 같은 방식이 있다.
| 파생 | 기존 컬럼으로 새로운 값 계산 |
| 비율 | 두 값의 관계 계산 |
| 구간화 | 연속적인 값을 범주로 변경 |
| Flag | 특정 조건 만족 여부 |
| 집계 | 여러 행을 하나의 값으로 요약 |
| 기간 | 두 날짜 사이의 차이 |
| 빈도 | 특정 이벤트 발생 횟수 |
| 통계 | 평균·최대·최소·표준편차 등 |
이번 시간은 지난번 수행했던 가상의 온라인 서점 내 legacy 데이터를 표준화, 정규화 이후 Feature 가공을 수행하였는데,
Feature 데이터를 만든 기준은 다음과 같다.
* Feature의 한 행 기준
- grain = member_id + as_of_date
- 한 행은 한 회원이 특정 기준일 직전 30일 동안 보인 구매 행동의 요약
* 원천 데이터
- Member, Book, Category, Order, OrderItem
* 시간 범위
- as_of_date = 30일 <= order_datetime < as_of_date
- 30일 전 시작 시점은 포함
- as_of_date 당일과 이후 데이터는 제외
- 미래 주문이 섞이는 학습 누수 방지
* 주문 상태 필터
- PAID, SHIPPING, DONE
- 그 밖의 주문 상태는 제외
* Feature별 계산 기준
- order_cound_30d = 조건을 통과한 최근 30일 주문의 개수
- quantity_sum_30d = 주문 상품의 quantity 합계
- spend_sum_30d = quantity x unit_price의 합계
- preferred_category_code_30d = 구매 수량 합계가 가장 큰 카테고리
- last_order_days_ago = 가장 최근 주문일부터 기준일까지 지난 일수
* 구매 이력이 없는 회원
- order_count_30d = 0
- quantity_sum_30d = 0
- spend_sum_30d = 0.00
- preferred_category_code_30d = NULL
- last_order_days_ago = NULL
* 생성 및 재현 기준
- 매일 오전 1시 새로운 as_of_date 파티션 생성
- 같은 입력과 같은 as_of_date로 실행하면 같은 결과가 나와야 함
- 정규화된 정본 데이터에서 전체 재생성 가능
- 계산 코드 버전과 원본 버전을 기록
- Feature projection은 직접 수정하지 않음
- 날짜 파티션 기준은 as_of_date
정리하자면 가상의 온라인 bookstore의 Feature 데이터는 3NF까지 완료된 주문 데이터를 정본으로 삼고, 회원과 기준인을 grain으로 하여 기준일 직전 30일의 허용된 주문만 집계, 미래 누수를 차단하고 동일 입력에서 동일 결과가 재현되도록 만든 데이터이다.
위 내용을 토대로 Feature 데이터를 가공하고, Django dashboard로 만들어 가시화를 시켜보았다.

as_of_date의 기준일이 2026-08-12이기 때문에 2026-08-13 이상의 데이터는 반영되지 않는다.
실습을 통해 Feature 데이터를 만들 때는 "Sliver의 어떤 컬럼을 근거로 어떠한 비즈니스(서비스) 목적을 가지고 Feature를 만들었는가?"가 중요하다고 느꼈다.
# 2차 프로젝트
2주간의 짧은 단위 기간이 종료되어서, 이번에도 어김없이 단위 프로젝트 기간이 찾아왔다. 프로젝트 기간은 이틀로 매우 짧았지만, 제공될 데이터셋의 극히 일부를 사전에 공개하여 팀별 대략적인 방향성을 확립하는데 어느정도 도움이 되었다.
이번 프로젝트의 목표는 주어진 legacy데이터에 대해 Medallion Architectre를 설계해 수집,정제(표준화, 정규화),적재 과정을 거치고 각 팀별 business 목표에 따라 Feature데이터를 Gold layer에 추가하여 구현하는 것이다.
이번 단위기간에서 배웠던 모든 개념과 기능들을 일괄적으로 적용시켜 팀만의 결과물을 도출해내어야 하는 것인데, 나는 적재된 데이터를 business 목표에 맞게 꺼내어 dashboard로 구현하는 역할을 맡았다.
이번 회고에서는 프로젝트 진행 과정을 나열하고, 중간중간 문제가 되었던 부분들을 review하는 방향성으로 나아가려고 한다.
1. Gold layer 목표 수립
프로젝트 초기에는 우리 팀이 구현해야할 business 목표와 Silver layer 과정에서 legacy 데이터를 어떠한 기준에 맞게 표준화 및 정규화를 진행할 것인지, 또한 Feature 기준은 어떻게 정할것인지 토론하였다. 팀의 목표는 "Standardization / Normalization의 명확한 기준을 제시하고, 이를 대상(회사)에 적용시켜 Data 유지관리에 대한 Solution을 제공하고, Gold layer를 통해 AI가 정확한 데이터셋에 대한 방향성을 제시해주는 것" 이었다.
이를 기반으로 BRD(Business Requirments Document)와 PRD(Product Requirments Document)를 작성하여 계획을 세분화하였고, Bronze / Silver / dashboard / Gold 큰 4개의 layer에서 각 역할분담을 실시하였다.
1-1 명확한 목표 수립
계획단계의 중요성은 1차 프로젝트때 이미 경험을 해봐서 익히 알고 있었다. 하지만 알면서도 어려운것이 계획수립 단계인것같다. 누구나 이해하기 쉽도록 명확, 간결하게 문서들이 작성되야 하고 모든 팀원들이 이를 인지하여 자신이 맡은 역할을 수행하여 팀의 방향성이 일관적으로 나아갈 수 있는데, 이번 프로젝트에서도 계획수립 단계에서 많은 변동사항이 있어서 진행 중 많은 혼선이 있엇던것 같다. 아직은 개인적으로도 어떠한 문서가 어떤 형태로 작성되어야하는지, 어느 시점에 작성되어야 하는지 인지하는 능력이 부족했던 것 같다.
2. Bronze layer
Medallion Architecture의 가장 첫 시작 부분인 Bronze layer는 주어진 legacy 데이터를 API호출, raw 데이터 적재 및 백업을 진행하여 Silver layer 이전 Data lake를 구상하는 것이었다.
* Data lake란 ? 다양한 형태의 데이터를 원본 또는 원본에 가까운 형태로 대규모 저장하는 중앙 데이터 저장소
Bronze layer의 형태는 다음과 같다.

여기서 Manifest의 개념이 등장하는데, Manifest란 어떠한 데이터를 수집·저장했는지 기록해 놓은 것 이며, raw데이터에 대한 메타정보를 기록한다.
우리 팀은 Python으로 run_id를 API로 호출 할 때마다 부여 될 수 있도록 하여, raw데이터와 추후 정제과정을 거친 데이터 정합성을 유지할 수 있도록 구현하였다. (추후 정제 이후 DB적재 전 raw data와 정제 데이터의 run_id를 대조하여 적제하도록 설계하였다.)
수집한 raw 데이터는 .csv로 데이터를 사용가능 할 수 있게 저장하였고, 메타데이터는 manifest로 따로 적재하여 Silver layer 이전 단계의 준비가 완료되었다.
2-1 ETL과 ELT
Bronze layer에서 궁금했던 점은, raw 데이터를 수집하고 .csv로 변환하니까 Bronze layer 관점에서는 ETL인건가? 그렇다면 Silver layer 관점에서는 Bronze 데이터 → 정제 → 적재니까 ELT인건가? 각 관점마다 ETL과 ELT가 구분되냐였다.
정답은 아니다. ETL/ELT 구분 기준은 Transform이 최종 적재되기 전이냐 후냐에 따라 달라진다.
우리의 전체 Medallion Architecture를 보면 Bronze(수집) → Silver(정제) → Gold(Feature 가공) 이므로 전체적으로 보면 데이터 수집, 정제 후 DB에 적재되는 ELT 방식이라고 할 수 있다.
3. Silver layer
수집된 raw 데이터를 바탕으로 표준화 및 정규화를 실시하여 DB적재하는 구간으로, 사실상 이번 프로젝트의 핵심이라고 할 수 있다. 왜냐하면 표준화 이전 데이터의 column을 확인하고 어떠한 기준을 가지고 데이터를 분류하냐에 따라서 추후 정제율이 다르게 산출될 수 있기 때문이다. 우리팀은 기본적으로 표준화 가능한 데이터(시간값이 ISO 8601 기준으로 맞지 않거나, TOP, L1, 최상위 든 일률적으로 통합 가능한 AREA 등)은 표준화 후 accepted로 진행하고, NULL값이나 데이터가 없는경우, 도메인 규칙으로 판단이 어려운 경우는 rejected하여 rejectedDB(MongoDB)에 따로 적재하기로 하였다. (BRD를 데이터셋에 대한 방향성을 제공해주는 솔루션으로 정의했기 때문)
이후 표준화가 accepted된 데이터는 정규화 과정으로 이동하여, 다시 한번 기준에 의해 정규화 과정을 거친다. 최종적으로 정규화가 완료된 데이터들을 acceptedDB(MySQL), rejected 데이터들은 rejectedDB(MongoDB)에 다른 collection을 추가하여 적재하기로 하였다.
이를 도식화하면 다음과 같다.

MongoDB에 두개의 collection을 두고 rejected를 적재하는 이유는, 하나의 DB내에서 rejected에 대한 관리 및 조회가 쉬워지고, 추후 dashboard 연결 간 하나의 DB만 연결하면 되는 접근성 또한 용이해지기 때문이다.
또한 표준화 → 검증, 정규화 → 검증 구조처럼, 각 단계별 체크포인트를 두어 사용할만한 데이터가 정확하게 정제되는지 명확하게 확인할 수 있도록 하였는데, 장,단점이 있다는 것을 확인하였다.
장점으로는 다음과 같은 사항이 있다.
| 오류 발생 위치 추적 | 표준화에서 실패했는지 정규화에서 실패했는지 바로 알 수 있음 |
| 불량 데이터 전파 방지 | 표준화 실패 데이터가 정규화 단계까지 넘어가지 않음 |
| 검증 규칙 분리 | 단계별로 서로 다른 품질 규칙을 적용할 수 있음 |
| 재처리 용이 | Reject 데이터를 보관하면 수정 후 해당 단계부터 재처리 가능 |
| 품질 지표 생성 | 단계별 Accepted/Rejected 비율을 측정할 수 있음 |
| Lineage/감사 추적 | run_id, 실패 단계, 실패 사유 등을 남기기 쉬움 |
각 체크포인트별 지표를 확인할 수 있기에, 데이터가 정확하게 정제되고 있는지, accepted는 몇개고 rejected는 몇개인지 추후 만든 dashboard에서 해당 지표들을 추가하고 시각화하여 한 눈에 알아볼 수 있도록 구현할 수 있다는 것도 장점이었다.
단점으로서는 "복잡성과 운영 비용 증가"이다.
변환 → 검증 → Rejected 적재 각 단계마다 검증 코드, Reject 스키마, 로그, 재처리 정책 등을 관리해야한다.
이러한 이유로 각 Gate 설계 시 검증 목적을 명확히 분리하고 Reject 사유와 재처리 정책까지 설계해야 의미가 있다는 것을 깨달았다.
3-1 통합 트랜잭션 운영
우리는 silver layer 안에서 raw데이터 수집 → 표준화 (accepted / rejected) → 정규화 (accepted / rejected) -> 매니페스트 조회 -> 일치하면 각 DB 적재까지 하나의 트랜잭션으로 관리하기로 하였다.
이는 정규화 accepted 데이터도 manifest와 대조하여 값이 다르면, 데이터의 품질을 보장할 수 없다는 의미로 처음과정으로 roll back되도록 설계했었는데, 이 설계 부분도 궁금한 사항들을 짚어가며 조금 더 학습해보았다.
* 현재 방식의 장점
→ 불완전한 Batch가 최종 Silver 데이터로 적재되는 것을 막을 수 있다.
| 데이터 일관성 | 검증이 끝난 Batch만 적재 가능 |
| 데이터 유실 방지 | RAW = Aceepted + Rejected 검증 가능 |
| 잘못된 데이터 공개 방지 | 중간 실패 결과가 Silver에 노출되지 않음 |
| 추적성 | run_id 단위로 실행 상태 관리 가능 |
| 재현성 | 특정 Batch 전체를 다시 처리하기 쉬움 |
| 품질 보장 | Manifest가 마지막 Quality Gate 역할 |
이는 "검증이 모두 통과한 뒤 최종 적재한다"라는 패턴으로, 패턴 자체는 일반적인 방향이 맞다.
다만, "일반적인 하나의 DB 트랜잭션"이라고 보기는 어려운데, 최종 적재 대상이 단일 DB가 아닌 MySQL, MongoDB처럼 서로 다른 DB이기 때문이다.
왜냐하면 최종 정규화 accepted이후 manifest 검증 →DB적재 단계에서 manifest가 success 되었더라도, MySQL이나 MongoDB에 insert하는 과정에서 모종의 이유로 fail이 발생할 수 있기 때문이다.
< INSERT ERROR 예시 >
Manifest 검증 SUCCESS
MySQL INSERT SUCCESS
MongoDB INSERT FAIL
이 상황에서는 트랜잭션의 ACID중 원자성(Atomicity)이 반영되지 않는다. 그래서 두 DB의 Commit을 완전히 동시에 보장하려면 분산 트랜잭션 (Distributed Transaction)을 통해 관리해야 하는 등 복잡한 문제가 야기된다.
따라서 여러 DB 저장소가 섞여 있으면 일반적으로 구현 난이도가 높아, 실무에서는 각 DB 로컬 트랜잭션 + 상태관리 + 재시도/보상처리로 해결하는 경우가 많다고 한다.
또다른 단점은 "트랜잭션 범위가 너무 커질 수 있다."이다.
이번 프로젝트에서 처리한 데이터는 대략 2만7천여건으로, 대규모 데이터라고 불리기에는 다소 규모가 작은 감이 없지않아 있다.
RAW가 100만건 이상, 1000만건 이상 등 데이터의 규모가 커질수록 변환 → 검증, 변환 → 검증의 과정이 길어질수록 LOCK, 메모리/Undo log, 장애 시 롤백 비용, DB 연결 점유, 재처리 시간 등이 커질 수 있다.
따라서 MySQL과 MongoDB등을 포함한 전체를 하나의 긴 ACID DB 트랜잭션으로 묶는 것은 권장되지 않고, run_id + Manifest + 상태관리 +개별 DB 트랜잭션 + 재시도/멱등성으로 설계하는 것이 더 안정적일 수 있겠다.
4. Dashboard
MySQL과 MongoDB에 적재된 데이터를 Django를 활용해 실시간 데이터 처리 현황을 dashboard로 구현하기로 하였다.
dashboard를 구현하기 전에 먼저 중요하게 생각했던 것은 서로 DB 종류가 다르고, 다른 ip에 저장된 각 DB를 어떻게 호출하여 dashboard 내 데이터로 가져오는가, dashboard로 노출하고 싶은 데이터의 종류는 무엇인가였다.
우리 팀은 업무분장에 따라 최종 accepted 데이터 (MySQL)은 팀원1의 PC에, 표준화/정규화 과정에서 rejected 데이터 (MongoDB)는 팀원2의 PC에 저장되어 있었는데, 이는 .env 환경변수로 두고 실시간 PING 체크, UNABLE시 dashboard에 warning sign으로 현재 connection 상태를 확인하는 방향으로 설계하였다.
또한 dashboard에는 실시간 데이터 처리 현황에서 중요한 legacy 데이터 전체 수집율, 표준화 accepted/rejected 비율, 정규화 accepted/rejected 비율, 전체 원천 데이터 중 accepted/rejected 비율 등 어떠한 정보가 필요한지 구상하는 단계에서 많은 시간이 소요되었다. 이는 많은 패널을 사용하여 한 눈에 시각화하여 보여주고싶다는 개인적인 욕심이 반영되었다.
dashboard는 Django를 통해 4가지 layer로 구분하여 구현하였다.



| Presentation | 사용자의 요청을 받고 응답을 만들어주는 계층 | urls.py views.py |
| Repository | 데이터베이스 접근을 전담하는 계층 | mysql_repository.py mongodb_repository.py gold_repository.py |
| Service | 업무 규칙과 데이터 로직을 담당하는 계층 | main_services.py mysql_services.py mongodb_services.py gold_services.py |
| template | 전달받은 데이터를 HTML화면으로 표현하는 계층 | main-dashboard.html mysql-dashboard.html mongodb-dashboard.html gold-dashboard.html |
HTML 구성은 java script의 library인 Three.js를 사용하여 화면 중앙 3d 파이프라인 아키텍처를 구상하고, 나머지 패널들은 언급했던 각종 지표들을 노출시키는 방향으로 구현하였다.
완성본은 다음과 같다.

dashboard는 데이터 적재를 담당한 팀원들이 현재 DB에 존재하는 데이터와 dashboard 내 데이터가 서로 정합성을 이루는지 확인하기 위해 local network망으로 server를 오픈하여 전체 팀원이 실시간으로 확인할 수 있도록 조치하였다.
또한 우측 상단 데이터가 실시간으로 반영되는 LIVE 여부, 현재 KST, WARNING SIGN등을 추가하여 MySQL, MongoDB의 connection이 불안정하면 critical alarm을 띄울 수 있게끔 구현하였다.
5. Gold layer
이번 프로젝트의 Gold Feature 기준은 초기에 언급했던 것처럼 특정 회사의 데이터셋에 대한 기준점을 제공하는 솔루션 이었지만, 만족할만한 성과 및 구성으로 나오지 않아서 이 부분은 to-do로 남겨 팀원들과 다시 진행하도록 합의하였다.
6. Github 운용관리
여지없이 이번에도 Github 운용은 쉽지 않았다. 1차 프로젝트때에 비하면 cli 사용법이나 전반적인 사용법에 대해 많이 익숙해졌으나, 구체적인 사용법들은 아직 많은 연습이 필요했다.
Github 운용방식에 대해 논의할 때, 팀원중 한명이 Linux의 방식을 벤치마킹 해보자는 의견을 내었다.
일단 Issue, pull request 사용부분에 대해 적극적으로 벤치마킹 해보았다.
Isuue 탭을 적극적으로 사용해보는 것은 이번이 처음이었는데, 관련 내용을 정리해보았다.
| Issue | 프로젝트에서 해야 할 작업, 버그, 개선사항, 논의사항 등을 등록하고 추적하는 기능 |
| Pull request | 내 브렌치에서 만든 변경사항을 다른 브렌치에 병합할 때 요청하는 기능 |
- Isuue는 작업의 시작점, Pull request는 코드 반영 과정이다.
- 일반적으로 commit / push만 하는 것은 왜 이 코드를 변경했는지 추적이 어려워지지만, Issue와 pull request를 사용하면 "작업 이력"이 생성되어 여러 명이 작업 흐름을 파악하는데 도움을 준다.
- 우리 팀도 이번 프로젝트에서 Issue / Pull request를 적극적으로 활용하면서 github 관리를 이어나갔다.

# 느낀점
이번 프로젝트에서 가장 아쉬웠던 것은 개인 건강관리 실패에 의한 컨디션 저하였다. 지난주에 갑작스런 개인건강 이슈로 약 4일동안 입원하면서 정규수업도 나가지 못하고 병실에서 강사님께서 남겨주신 동영상을 보고 복습했었는데, 개인 병동도 아니라 노트북 소리가 혹여 다른 사람들에게 민폐가 되진 않을까 생각이되어 제대로 집중도 못했었던것 같다.
또한 갑작스런 입원으로 팀원들과 예정된 정규 회의를 계속 연장하게 되었는데, 프로젝트에 누구보다 진심인 팀원들에게 민폐를 끼친 것 같아 매우 죄송하게 생각한다.
그래도 우리 팀은 팀장을 중심으로 밤낮없이 열심히 준비했다고 생각한다.
한 가지 아쉬웠던 사항이 있다면 팀의 절대 목표가 명확하지 않았다는 점이다. 앞서 언급했듯 전체가 A를 보고 가는 것이 아닌 A-01, A-02로 나가게 된 일들이 몇 번 있었는데, 이 또한 간결하고 명확하고 정확한 소통이 중요하다라는 것을 다시한번 느꼈다.
개인적인 아쉬움으로는 초반 dashboard의 방향성을 구상할 때 "데이터의 흐름"에 집중하는 것이 아닌 "목표한 비즈니스를 편하게 보여줄 수 있는 산출물"로 생각했었어야 됐다고 생각이 든다. 이번 프로젝트는 단순하게 데이터 파이프라인을 구상하는 것이 아니라 비즈니스 모델을 직접 구상하고 그 내용을 브라우저로 노출시키는 거라고 생각했기 때문이다. (이 점을 너무 늦게 알아버렸다.)
아무쪼록 이번 2차 프로젝트는 나의 의견도 제시하고, 회의 내용도 어느정도는 이해하는 것으로 보아 1차 프로젝트때보다는 발전했구나 라는 개인적인 생각이 들었다. 앞으로도 여유를 잃고(?) 학습하여 차주에 있을 kafka & spark도 열심히 배워보아야겠다.