로딩 중이에요... 🐣
02 데이터가 저장되고 가공되는 흐름 | ✅ 저자: 이유정(박사)
데이터 파이프라인 단계별 설명:
🔹 1단계: DL (Data Lake) – 데이터 레이크
데이터 레이크는 엑셀, 로그, 크롤링 결과, 이미지, 센서 정보 등 다양한 원시(raw) 데이터를 있는 그대로 저장하는 공간입니다.
정제되지 않은 데이터라도 모두 저장할 수 있어, 이후 분석이나 가공을 위한 출발점 역할을 합니다.
✅ 원시 데이터 저장소
- CSV, JSON, 크롤링 결과, 공공데이터 API 등에서 수집한 원시 데이터
- 저장 위치:
raw_data/restaurant_raw_data.csv
,json_files/naver_blog_reviews.json
등
내용 예시:
{
"가게명": "브런치카페 강남점",
"주소": "서울 강남구 테헤란로...",
"리뷰": "진짜 맛있어요!",
"별점": "⭐⭐⭐⭐",
"카테고리": "카페, 양식",
"전화번호": "+82-02-1234-5678",
"운영시간": "09:00~21:00",
"위도": "37.123456789012",
"경도": "127.123456789012"
}
✔ 이 데이터는 아직 불완전합니다.
✔ 필드명 불일치, 단위 불명확, 주소 파싱 필요 등 → 정제 필요
🔹 2단계: DW (Data Warehouse) – 데이터 웨어하우스
데이터 웨어하우스는 데이터 레이크에 저장된 원시 데이터를 깨끗하게 정리하고 표 형태로 구조화하여 저장하는 공간입니다.
주로 BI 도구, 리포트 작성, 분석 작업에 활용됩니다.
즉, 분석하기 쉬운 형태로 가공된 데이터를 저장하는 곳이에요.
✅ 원시 데이터를 정제·표준화한 구조화 테이블
- 주소 파싱 →
Region
테이블 기준으로 나누기 - 별점 변환:
⭐⭐⭐⭐
→ 숫자4
- 운영시간 분리 →
start_time
,end_time
,last_order_time
- 카테고리 정리 →
RestaurantCategory
,CuisineType
분리 - 중복 가게 제거, 결측치 채움, 전화번호 포맷 정규화 등
중간 결과: cleaned_restaurant_data.csv
→ 이 파일을 기반으로 다음 단계로 이동
{
"name": "브런치카페 강남점",
"branch_name": "강남점",
"address": "서울 강남구 테헤란로...",
"feature": "저렴한 가격, 빠른 제공",
"rating": 4.2,
"category": "분식",
"start_time": "09:00",
"end_time": "21:00",
"region": "서울 강남구 역삼동",
...
}
🔹 3단계: DM (Data Mart) – 데이터 마트
데이터 마트는 웨어하우스에 있는 데이터를 부서별(예: 마케팅팀, 영업팀) 또는 목적별로 잘라서 보관하는 작은 데이터 저장소입니다.
필요한 데이터만 골라 빠르게 분석하거나 사용할 수 있도록 설계되어 있어요.
✅ 특정 목적(뷰/부서별)을 위한 소규모 테이블 이 단계에서는 DW의 정제된 데이터를 아래 목적별로 분리 저장
상점명 | 목적 | 설명 |
---|---|---|
dm_top_rated_restaurants |
추천용 | 평점 4.5 이상 + 리뷰 10개 이상 맛집 리스트 |
dm_new_restaurants |
홍보용 | 최근 한 달 이내 등록된 레스토랑 |
dm_popular_tags |
검색 분석 | 가장 자주 사용된 태그별 집계 |
dm_region_based_stats |
리포트 | 지역별 레스토랑 수, 평균 평점, 리뷰 수 등 통계 |
Django에는 직접 모델로 만들지 않고, View나 임시 테이블로 활용하거나 통계 API에서 사용 |
🔹 4단계: Service DB – 서비스 데이터베이스
서비스 DB는 실제 웹사이트나 모바일 앱에서 사용되는 데이터베이스입니다.
예를 들어, 사용자가 로그인하거나 상품을 추천받을 때, 바로 이 DB의 정보를 활용하게 됩니다.
즉, 실시간으로 고객에게 서비스를 제공하는 데 직접 쓰이는 DB입니다.
✅ Django 모델 기반 실서비스용 데이터베이스
이 부분은 지금 이미 구축한 models.py가 해당됩니다!
각 모델은 다음처럼 구성되어 있고, Admin / API / 사용자 인터페이스에서 직접 사용됩니다:
모델 | 역할 |
---|---|
Restaurant |
핵심 맛집 정보 저장 (이름, 주소, 위도/경도, 시간 등) |
Region |
주소 파싱 결과로 지역 분류 저장 (시도, 구, 동) |
RestaurantCategory / CuisineType |
음식 분류 저장 (한식, 일식, 카페 등) |
Tag |
#혼밥 , #데이트 같은 사용자/시스템 태그 |
RestaurantMenu |
메뉴 정보 저장 |
RestaurantImage |
대표 이미지 및 기타 이미지 저장 |
Review |
사용자 후기 저장 |
ReviewImage |
후기 이미지 저장 |
SocialChannel |
블로그, 인스타그램 등 채널 구분용 |
Article |
관리자 작성 칼럼 (추천 콘텐츠 등) |
정제된 CSV는 다음과 같은 구조라고 가정하면:
name,branch_name,address,feature,latitude,longitude,phone,rating,
rating_count,start_time,end_time,last_order_time,category_name,
cuisine_type,region_sido,region_sigungu,region_eupmyeondong,
tags
김밥천국,강남점,서울 강남구 테헤란로,저렴한 가격,37.1234,127.1234,+82-2-1234-5678,4.2,15,09:00,21:00,20:30,분식,한식,서울,강남구,역삼동,
#혼밥,#가성비
Step 1. CSV 읽기
import pandas as pd
df = pd.read_csv("csv_files/cleaned_restaurant_data.csv")
Step 2. 각 모델에 맞게 데이터 분리 & 저장
from restaurant.models import (
Restaurant, Region, RestaurantCategory, CuisineType, Tag
)
from django.utils.dateparse import parse_time
for i, row in df.iterrows():
# 1. 지역 처리 (Region)
region, _ = Region.objects.get_or_create(
sido=row["region_sido"],
sigungu=row["region_sigungu"],
eupmyeondong=row["region_eupmyeondong"],
)
# 2. 음식 종류 처리 (CuisineType)
cuisine_type, _ = CuisineType.objects.get_or_create(name=row["cuisine_type"])
# 3. 카테고리 처리 (RestaurantCategory)
category, _ = RestaurantCategory.objects.get_or_create(
name=row["category_name"],
defaults={"cuisine_type": cuisine_type},
)
# 4. 레스토랑 저장 (Restaurant)
restaurant = Restaurant.objects.create(
name=row["name"],
branch_name=row["branch_name"],
address=row["address"],
feature=row["feature"],
latitude=row["latitude"],
longitude=row["longitude"],
phone=row["phone"],
rating=row["rating"],
rating_count=row["rating_count"],
start_time=parse_time(str(row["start_time"])),
end_time=parse_time(str(row["end_time"])),
last_order_time=parse_time(str(row["last_order_time"])),
category=category,
region=region,
)
# 5. 태그 저장 및 연결 (ManyToManyField)
tags = str(row["tags"]).split(",")
for tag_name in tags:
tag_name = tag_name.strip().lstrip("#")
if tag_name:
tag, _ = Tag.objects.get_or_create(name=tag_name)
restaurant.tags.add(tag)
데이터는 수집(DL) → 정제(DW) → 분류(DM) → 서비스(Service DB) 단계로 점점 깨끗하고 목적에 맞게 정돈되어 실제 서비스에 활용됩니다.
자주 사용되는 Pandas 메서드
read_csv()
CSV 파일을 읽어서 DataFrame으로 불러오기
import pandas as pd
df = pd.read_csv("data.csv")
to_csv()
DataFrame을 CSV 파일로 저장
df.to_csv("output.csv", index=False)
head()
처음 5줄(기본값) 보기
df.head() # df.head(10) → 처음 10줄 보기
tail()
마지막 5줄 보기
df.tail() # df.tail(3) → 마지막 3줄 보기
describe()
숫자형 열의 요약 통계 (평균, 표준편차, 최대/최소 등)
df.describe()
info()
열 이름, 결측치 여부, 데이터 타입 등 기본 정보
df.info()
mean()
열 평균 계산
df["age"].mean()
median()
열의 중앙값
df["salary"].median()
max()
최대값 찾기
df["score"].max()
min()
최소값 찾기
df["score"].min()
drop()
열 또는 행 삭제
# 열 삭제
df.drop("email", axis=1, inplace=True)
# 행 삭제 (index 기준)
df.drop(0, axis=0)
fillna()
결측치 채우기
df["age"].fillna(30, inplace=True) # 결측치를 30으로
isnull()
결측치 여부 확인
df.isnull() # True/False로 표시
df["age"].isnull().sum() # age 열의 결측치 개수
groupby()
열 기준으로 그룹화 후 계산
df.groupby("gender")["salary"].mean()
merge()
두 DataFrame 병합 (SQL의 JOIN과 유사)
df1.merge(df2, on="user_id", how="inner")
concat()
위 또는 옆으로 데이터 연결
pd.concat([df1, df2], axis=0) # 위아래 연결
pd.concat([df1, df2], axis=1) # 좌우 연결
value_counts()
고유값 개수 세기
df["gender"].value_counts()
sort_values()
열 기준 정렬
df.sort_values(by="score", ascending=False)
pivot_table()
피벗 테이블 (엑셀처럼 요약 테이블)
df.pivot_table(index="department", values="salary", aggfunc="mean")
apply()
함수 적용
# 각 이름 앞에 "Mr./Ms." 붙이기
df["name"].apply(lambda x: "Mr. " + x)