
이번 주는 웹/JSON/API 데이터를 수집해 MariaDB·DuckDB에 저장하고, Grafana·Metabase·Elastic Stack으로 시각화 및 모니터링을 경험했으며, 데이터 웨어하우스 구조(ODS·Fact·Mart)와 RAG 기반 벡터DB 활용 이론도 함께 학습하였다.
즉, 요즘은 상황에 맞춰 Playwright나 NiFi 같은 최신 도구들을 더 많이 활용한다는 점이 특징
데이터를 저장할 때는 데이터의 성격에 맞는 저장소를 선택해야 한다
데이터 웨어하우스에서는 여러 층위의 테이블 구조가 존재한다
특징:
이번 주에 실습하면서 겪었던 어려움이나 궁금증의 해결 과정을 기록하였습니다
다음은 CSV를 duckDB로 불러오는 실습을 수행하면서 겪었던 어려움입니다
데이터 수집용 가상환경을 venv로 새로 만든 뒤 아래와 같이 csv를 먼저 판다스로 먼저 불러온 뒤 duckDB를 이용해 데이터를 추출하는 실습을 하였는데 실행 중에 알 수 없는 에러가 떴었습니다.
import duckdb
import os
import pandas as pd
# duckdb로 csv파일을 읽으면 에러가 발생하므로 pandas에서 먼저 읽어서 duckdb에 import,
print('a')
duck_con = duckdb.connect("c:/data/duck_smb.db")
print('b')
duck_con.execute("CREATE TABLE IF NOT EXISTS tb_smb_file AS SELECT * FROM read_csv('c:/data/smb_data/sejong.csv');")
duck_con.execute("DELETE FROM tb_smb_file;")
csv_path = 'c:/data/smb_data'
file_list = os.listdir( csv_path ) # 경로 내에 파일을 모두 불러옴
csv_file_list = [file for file in file_list if file.endswith('.csv')] # csv 확장자를 가진 파일로 새로운 리스트 생성
for csv_file_name in csv_file_list:
csv_file_full = f'{csv_path}/{csv_file_name}'
print(f'csv_file_name = {csv_file_full}')
csv_df = pd.read_csv( csv_file_full )
duck_con.execute("insert into tb_smb_file SELECT * FROM csv_df;")
print("csv loading complete")
duck_con.sql("SELECT addr1, addr2, addr3, cate3_nm, cnt FROM ( SELECT addr1, addr2, addr3, cate3_nm, cnt, RANK() OVER (PARTITION BY addr3 ORDER BY cnt DESC) AS t_rank FROM ( SELECT 시도명 as addr1, 시군구명 as addr2, 행정동명 as addr3, 상권업종소분류명 as cate3_nm, COUNT(상권업종소분류명) AS cnt FROM tb_smb_file GROUP BY addr1, addr2, addr3, cate3_nm ORDER BY cnt DESC ) temp_rank ) temp_rank2 WHERE t_rank=1 ORDER BY cnt DESC;").show()
어디서 에러가 발생한 지 명확하게 판단하기 위해 각 줄마다 print 문을 이용하여 어디서 에러가 나는지 체크를 해보았었는데 바로 첫번째 줄, duckDB를 연결하는 부분에서 문제가 발생했다는 것을 알 수 있었습니다.
a
PS C:\data>
이런식으로 터미널창에 출력이 되었는데 에러 메세지 자체가 보이지 않아 어떤 문제점인지 발견을 못하던중 다른 가상환경에서는 잘 동작하는 것을 보고 가상환경 충돌이 원인일 수 있겠다는 생각을 하였습니다.
실제로 먼저 만든 conda 환경안에 venv를 만드는 실수를 하여 가상환경 충돌로 인한 문제임을 venv환경을 삭제하고 다시 실행해봄으로써 알게 되었습니다.
이번 실습을 계기로 가상환경을 아예 분리해서 독립적으로 관리를 하는 것이 좋다는 사실을 배우게 되었습니다.
다음은 웹크롤링을 통해 데이터수집 실습을 수행하면서 새롭게 배운 점입니다
이번 데이터 수집 실습에서 URL과 데이터를 분리해서 관리하는 이유를 처음으로 명확히 이해할 수 있었다.
그동안은 단순히 데이터만 모으면 된다고 생각했는데, 강사님께서 설명해주신 내용을 통해 아래와 같은 장점을 새롭게 알게 되었다.
추가로 URL을 단순히 리스트로만 두는 게 아니라, 각 URL의 수집 상태(status)를 DB 컬럼으로 관리하는 방식도 배웠다.
# status : 1 수집처리대기
cur.execute(ready_update_sql, ('1', ready_seq_no))
conn.commit()
# status : 2 수집중
cur.execute(ready_update_sql, ('2', ready_seq_no))
conn.commit()
# status : 5 실패
cur.execute(ready_update_sql, ('5', ready_seq_no))
conn.commit()
# status : 9 수집완료
cur.execute(ready_update_sql, ('9', ready_seq_no))
conn.commit()
이와 같은 방식으로 크롤링 파이프라인이 어느 단계까지 진행됐는지 한눈에 파악할 수 있었다.