2022년 이후로 팀스파르타에 튜터로 합류하면서 거의 3년 넘게 튜터일을 하고 있습니다. 이글을 보는 학생들에게 진심을 담아 얘기하고자 다음 내용 전문을 글로서 남깁니다.
안녕하세요, 여러분. 튜터 임정입니다. 수강생 한 분 한 분과 나눈 경험의 크기가 저마다 다르기에, 혹여나 소외되는 분이 없도록 이렇게 전체 공지로 마음을 전하는 점 너그러운 이해 부탁드립니다. 그동안 튜터로서 지식을 나누었지만, 사실 저는 대단한 사람이 아닙니다. 그저 2019년부터 시작된 데이터의 흐름에 조금 "먼저" 올라타 고민해 온 사람일 뿐입니다. 지금까지는 기술적인 내용을 주로 전달했다면, 오늘은 캠프 이후 여러분이 갖추었으면 하는 마음가짐 에 대해 이야기해 보려 합니다.
Attitude is Everything: 태도가 전부입니다
신입 시절부터 제가 가슴에 새긴 말입니다. 일은 결국 사람과 하는 것입니다. 동료, 상사, 혹은 비즈니스 파트너와 함께하는 과정에서 크고 작은 충돌과 실수는 반드시 발생합니다. 이때 자신의 과오를 솔직하게 인정하고 소통하는 태도를 가지세요. 실력을 쌓기에 앞서, 태도는 그 사람의 인격을 비추는 거울임을 기억하시길 바랍니다.
2. 최신 기술보다 '적정 기술'을 고민하세요 현장, 특히 제조업 같은 분야는 도메인 지식과 하드웨어가 우선입니다. 화려한 머신러닝 모델보다, 데이터를 명확히 보여주는 엑셀 한 줄의 결과값이 비즈니스 임팩트가 훨씬 클 때가 많습니다. 여러분은 높은 수준의 지식을 습득했지만, 이를 현장에 바로 적용하기는 쉽지 않을 것입니다. "이 문제를 해결하기 위해 어떤 도구가 가장 적절한가?"를 고민하고, 그 근거를 제시할 수 있는 힘을 기르는 것이 주니어 시기에 가장 중요한 배움입니다.
3. 자신만의 뚜렷한 '길'을 닦으세요 데이터 분석과 QA/QC를 희망하는 지원자는 넘쳐납니다. 하지만 모두가 채용되지 못하는 이유는 노동 시장의 공급이 많기 때문이기도 하지만, '나만의 이유'가 부족하기 때문이기도 합니다. 단순히 기술을 익히는 것을 넘어, 왜 이 산업에 관심을 두게 되었는지, 왜 이 일을 평생의 업으로 삼고 싶은지 치열하게 고민해 보세요. 그 근거가 스스로 정리되는 순간, 여러분은 사회의 일원으로서 당당히 설 준비가 된 것입니다.
4. 일은 반드시 '결과물'로 증명하세요 기획과 과정이 아무리 멋져도, 회사는 결국 문서와 자료로 말합니다. 기록되지 않은 일은 하지 않은 것과 같습니다. 사람의 기억은 흐릿하고 왜곡되기 쉽습니다. 타인이 아닌 '미래의 나'를 위해서라도 반드시 글로 남기세요. 블로그, 포트폴리오, 소셜 미디어 무엇이든 좋습니다. 기록의 힘을 믿으세요.
5. 스스로 '유효기간'을 설정하세요 부트캠프에서 배운 지식은 영원하지 않습니다. 딱 6개월, 길어도 1년 안에 승부를 보겠다는 마음으로 몰입하세요. 대신 그 목표를 이력서 통과, 면접 경험, 공모전 참여 등 세부 단계로 나누어 보세요. 작은 성취를 이룰 때마다 스스로를 칭찬하며 긍정적인 루프(Positive Loop)를 만들어야 지치지 않고 나아갈 수 있습니다.
우리의 인연은 여기서 끝이 아닙니다. 저는 여러분이 꼭 데이터 업계의 동료로 다시 만나길 진심으로 바랍니다. 좋은 소식이든, 고민 상담이든 언제든 편하게 연락해 주세요 마지막으로, 여러분이 원하는 목표를 달성했을 때 제가 꼭 해드리고 싶었던 말을 미리 전하며 글을 마칩니다.
railway에 호스팅된 n8n 서버의 postgres 디비는 다양한 정보를 가지고 있습니다. 이번 글에서는 로컬 노트북 DBevaer에서 postrgres 를 연결하는 방법을 작성합니다.
1. 기본 개념
Postgres는 n8n의 데이터베이스로 self-hosting을 하는 경우 외부에 접속 권한이 열려있지 않음
이를 해결하기 위해서 Public Netwokring 활성화 필요
Database 접속은 TCP 적용 필요 & SSL 설정 필요
2. 연결 정보 획득
Postgres 정보 클릭 - Setting - Networking - Public Networking 활성화
TCP 활성화
참고: Public HTTP Domain은 웹 서비스용 도메인이기 때문에 DB 접속 도메인은 따로 있음
Variable 탭에서 기본 정보 획득
POSTGRES_DB: DB정보, 일반적으로 railway
POSTGRES_USER: 유저 정보, 일반적으로 railway
POSTGRES_PASSWORD: 비밀번호
2. DBeaver 설정
Connection Settings - Main
Host: 위의 TCP 도메인
PORT: TCP 도메인의 설정된 PORT
Database: railway
Username: railway
Password: POSTGRES_PASSWORD 값
Connection Settings - Driver properties
ssl: true
sslmode: require
3. 접속 완료 및 조회
주요 데이터베이스별 정보(Gemini 생성)
1. execution_entity (가장 중요)
역할: 워크플로우가 한 번 실행될 때마다 모든 기록이 저장되는 곳입니다.
저장 내용: 실행된 JSON 데이터 전체, 실행 성공/실패 여부, 걸린 시간.
특징: 용량을 제일 많이 차지합니다. DB가 무거워졌다면 99% 이 테이블 때문입니다.
2. workflow_entity
역할: 우리가 화면에서 만든 워크플로우 '설계도'가 저장되는 곳입니다.
저장 내용: 워크플로우 이름, 노드들의 배치 정보, 노드 안의 설정값(JSON 형태).
특징: 이 테이블을 백업하면 워크플로우 전체를 복구할 수 있습니다.
3. credentials_entity
역할: 구글 시트, 슬랙, 노션 등 외부 서비스 연결을 위한 인증 정보입니다.
저장 내용: API Key, OAuth 토큰, 비밀번호.
특징: 보안을 위해 평문이 아니라 암호화된 문자열로 저장됩니다.
4. user
역할: n8n에 로그인하는 사용자 정보입니다.
저장 내용: 이메일 주소, 암호화된 비밀번호, 이름.
5. tag_entity
역할: 워크플로우 관리를 위해 붙이는 '태그' 정보입니다.
저장 내용: 태그 이름 (예: Production, Test 등).
특정 사용자의 워크플로우 생성 히스토리 조회
SELECT
u.id as user_id,
u.email,
w.id as workflow_id,
w.name as workflow_name,
p.name as project_name,
sw.role as workflow_role
FROM "user" u
JOIN project_relation pr ON u.id = pr."userId"
JOIN project p ON pr."projectId" = p.id
JOIN shared_workflow sw ON p.id = sw."projectId"
JOIN workflow_entity w ON sw."workflowId" = w.id
WHERE u.email = '이메일'
;
배민의 '요즘 우아한 AI 개발' 이후 나온 책의 시리즈입니다. 골든 래빗 출판사에서 이어서 만든 책으로, 둘 다 대중을 위한 서비스를 하는 기업이라는 관점에서 몰입도 잘되고 재밌게 읽었습니다. 몇 가지 유의미했던 내용을 간단하게 나열해봅니다.
독자를 고려한 쉬운 설명
으레 AI 책들은 복잡하고 어려운 도입문으로 독자의 몰입을 방해하곤 합니다. 반면 이 책은 초반부터 PM의 활용 사례로 시작해 누구나 쉽게 읽을 수 있도록 구성했으며, 중반에는 PM/운영 + 개발자의 협업 사례를, 후반에는 개발자 중심의 심화 내용을 다루는 식으로 점진적으로 깊이 있게 이해할 수 있도록 만들었습니다.
하지만 놓치지 않은 깊이 있는 설명
당근 서비스를 이따금 사용하기에 PM의 AI 서비스 도입 사례는 빠르게 넘어갈 수 있었습니다. 반면 저는 n8n 툴에 관심이 있어서 "Part 2: 06 작은 팀, LLM으로 큰 업무 효율 내기" 부분을 집중해서 읽었는데, n8n-mcp를 이용한 온콜 서비스 문제 해결 사례를 직접 코드 레벨로 확인할 수 있는 점이 좋았습니다. 여기에 더해 기존에는 PM이 개발자를 통해서만 해결할 수 있었던 서비스 문제를 자동화를 통해 스스로 에러를 확인하고 처리할 수 있게 된 효율화 과정이 인상 깊었습니다
이런 좋은 구성을 쓴 저자가 누구일까?
제목에는 '당근 팀 지음'이라고 되어 있지만, 사실 이런 책은 주도적으로 집필한 사람이 있기 마련입니다. 소개에 등장하는 OOO 님이 작성하신 것 같아 찾아보니 AI Agent Lead이시더군요. 놀라웠던 점은 AI Agent를 단순히 사용하는 게 아니라 당근 내부에서 플랫폼을 직접 만들고 있다는 점이었습니다. 굉장히 도전적인 일인데, 플랫폼을 만들고 활용한 사례를 책으로 정리하면서 회사 전체를 아우르는 LLM 활용 서비스까지 이렇게 잘 요약해냈다는 것에 부러움과 경외감을 느끼게 됩니다.
종합적으로 이 책은 현시점 IT기업의 AI 활용 방향을 정말 낱낱이 알려주는 책이라고 할 수 있으며, 소비재 IT 기업들에게는 필독서가 되지 않을까 싶습니다. 한번 읽어보시길 추천합니다.
+ 추가 내용
책 작성하신 분을 좀더 덕질(?)을 해보니 2024년 당근 테크 밋업에서 발표를 하셨더라구요. 더 놀랐던 것은 MLE 로써 기획, 기술 적용(ML + LLM, 엔지니어링), 발표, 비즈니스 임팩트까지 깔끔하게 정리하시는 한편 회사 내의 컨셉(당근 보안관)이라는 설정도 너무 잘하셔서 정말 경외감이 들었습니다. 나중에 더더 좋은 기업 가실 것 같아서 기대되고 발표하실 때마다 찾아볼 것 같습니다.
최근 Airflow 이 3.0으로 판올림 되었습니다. Web UI도 세부 탭으로 변경되고 유틸적인 부분이 더 늘어났습니다. docker를 이용하여 airlfow 3 설치도 해보고 실습 예제도 구성해볼겸 다음날 온도데이터를 supabase(PostgresSQL) 에 저장하는 예제를 작성해보겠습니다. airflow, 기상 API, discord, supabase 연결을 각각 테스트 해보고 종합적인 파이프라인도 정리해보겠습니다.
1. 파이프라인 소개
3일간 단기 특보를 이용하여 구로구의 내일 정오 날씨를 알려주는 앱을 만든다고 생각해봅시다. 이를 반영하기 위하여 이 파이프라인은 매일 오후 11시에 실행되어 그다음날 구로구의 정오 온도(12시)를 저장하고 알림을 주며, 데이터에 저장합니다.
전체 Schema
본 글에서는 (1) 기상 API 데이터 처리 (2) Airflow 설치 및 실행 (3) Supabase 저장 (4) Discord 알림 각 단계를 간단하게 세팅하는 것을 3단원에서 소개합니다. 마지막으로 최종 파이프라인을 위한 환경세팅과 코드를 4단원에서 기술합니다. 데이터는 기상청 API 허브의 예특보 - 4. 동네예보 - 3. 단기예보 조회를 활용할 예정입니다. 3일간의 기상 상황에 대한 정보로 특정 위치(x,y)좌표를 기준으로 기온, 풍속 하늘 상태 등에 대한 정보를 전달해 줍니다.
#터미널
mkdir airflow3 && cd airflow3
#docker-compose.yaml 로드
#docker로 airflow 설정시 정보가 담김
curl -LfO 'https://airflow.apache.org/docs/apache-airflow/3.1.0/docker-compose.yaml'
#기본 디렉토리 설정 및 uid 설정
mkdir -p ./dags ./logs ./plugins ./config
echo -e "AIRFLOW_UID=$(id -u)" > .env
#airflow.cfg 설정파일 초기화
docker compose run airflow-cli airflow config list
#데이터베이스 초기화
docker compose up airflow-init
#도커 실행
docker compose up
#터미널 새로 열어 서비스 확인
docker ps
docker ps 출력 내용
[선택] Docker Desktop 에서 contanier 확인
[Airflow3 UI 변경 점]
이제 바로 Dags 탭이 나오지 않고 Home 화면이 보이며, Dag 성공,실패 유무가 먼저 확인 가능. 각 컨테이너(MetaDatabase, Scheduler, Triggerer, Dag Processor)의 health check 가 상단에 나옵니다. 그 밖에도 Browse - Xcom 에서 Xcom 데이터가 보이며, Admin에서 환경변수(Variable)설정, 연결(Connections) 관리가 가능합니다.
개인적으로 Xcom은 WebUI로 보는 것보다 DBeaver 같이 어플리케이션으로 조회하는 것이 사용성 측면에서는 더 좋았습니다. 이 경우 airflow.cfg 파일에서 MetaDatabase의 포트를 외부(5432)로 열어줘야 연결 가능합니다.
#도커 컴포즈 내리기
docker compose down
# airflow.cfg파일변경
postgres:
# ... 기존 설정
ports:
- "5432:5432" # 이 줄 추가
# 도커 재시작
docker compose up
from dotenv import load_dotenv
import os
from supabase import create_client
import pandas as pd
load_dotenv()
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_KEY = os.getenv("SUPABASE_KEY")
# print(SUPABASE_URL,'\n', SUPABASE_KEY)
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
type(supabase)
def get_data():
result = supabase.table("products").select('*').execute()
return result
get_data()
def insert_data():
data = {"product_no":4, 'name': "치킨", 'price': 20.00}
result = supabase.table("products").insert(data).execute()
print(result.data)
insert_data()
삽입 성공
4. Python -> Discord: Webhook 설정 테스트
4.1. Airflow 환경 변수 설정
Airflow 에서 환경변수 설정에는 3가지 방법이 있습니다.
Airflow 프로젝트 루트에 .env 파일 생성
docker-compose.yaml 에 작성
Web UI - Admin - Variable 에서 설정
3번이 제일 쉽고 편합니다.
Variable 탭
이렇게 설정한 환경변수는 Variable.get 메소드를 이용해 불러올 수 있습니다.
[여기서 팁: 전역변수 설정하면 안되나요?] : 파이썬 스크립트에서 전역변수로 사용하면 될 것 같지만 Dags 하부의 task 는 Airflow 아키텍쳐에서 각각 다른 worker에게 할당될 수 있습니다. 따라서 전역변수로 설정하기보다 환경변수 설정을 해야합니다.
환경 변수 불러오기 예제 Dags
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime, timedelta
from airflow.models import Variable
def print_variable():
my_value = Variable.get("SUPABASE_URL", default_var="url 로드 안됌!")
print(f"Supabase URL 값은 : {my_value}")
with DAG(
dag_id="get_variable",
start_date=datetime(2025, 10, 23),
schedule=None,
catchup=False
) as dag:
t1 = PythonOperator(
task_id="print_variable_task",
python_callable=print_variable
)
4.2. Supabase 테이블 생성
Supabase 내에서 온도 정보를 담을 테이블을 선언해줍니다.
-- SQL 코드 시작
-- weather_forecast 테이블 생성
CREATE TABLE weather_forecast (
id BIGSERIAL PRIMARY KEY,
forecast_date VARCHAR(8) NOT NULL,
forecast_time VARCHAR(4) NOT NULL,
temperature DECIMAL(5,2) NOT NULL,
location VARCHAR(50) NOT NULL,
nx INTEGER NOT NULL,
ny INTEGER NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CONSTRAINT unique_forecast UNIQUE (forecast_date, forecast_time, location)
);
-- 인덱스 생성 (검색 성능 향상)
CREATE INDEX idx_forecast_date ON weather_forecast(forecast_date);
CREATE INDEX idx_location ON weather_forecast(location);
CREATE INDEX idx_created_at ON weather_forecast(created_at);
-- 코멘트 추가
COMMENT ON TABLE weather_forecast IS '날씨 예보 데이터';
COMMENT ON COLUMN weather_forecast.forecast_date IS '예보 날짜 (YYYYMMDD)';
COMMENT ON COLUMN weather_forecast.forecast_time IS '예보 시간 (HHMM)';
COMMENT ON COLUMN weather_forecast.temperature IS '기온 (섭씨)';
COMMENT ON COLUMN weather_forecast.location IS '위치명';
COMMENT ON COLUMN weather_forecast.nx IS '격자 X 좌표';
COMMENT ON COLUMN weather_forecast.ny IS '격자 Y 좌표';
COMMENT ON COLUMN weather_forecast.created_at IS '데이터 생성 시간';
4.3. Airflow 내 Supabase 모듈 설치
Supabase는 airflow에 등록되어있지 않는 패키지 이기 때문에 Dockerfile과 docker-compose.yaml 파일 수정이 필요합니다.
requirments.txt 작성
Python -> Supabase 실행한 가상환경에서 pip freeze > requirements.txt 실행
n8n은 노코드로 자동화를 구축할 수 있는 툴로 페어소스 정책을 따르는 오픈소스 툴입니다. Make와 비슷하지만 셀프호스팅 등 개발자를 위한 운영이 가능하며 비용도 절약할 수 있다는 장점이 있습니다. n8n을 공부하는 김에 간단하게 만들어본 장보기 지원 AI Agent 개발 후기와 Product 관점에서의 후기를 남겨봅니다.
1. 개요
한 2년전부터 꾸준히 농수산물 공공데이터(kamis)를 실습 데이터로 사용하고 있습니다. api에 대한 기본기를 소개하기도 좋고, 일상 생활의 데이터라는 점에서 몰입도가 좋아서 선호합니다. 하지만 대시보드로서의 Product를 꾸준히 개발하진 못했는데, 저의 게으름이 한 몫했습니다. 이번에 n8n 이라는 도구를 알게 되면서 AI agent를 손쉽게 구축할 수 있다고 들어서 실습 겸 해보았습니다.
2. 개발기
2.1. 문제 정의: 신혼 주부의 콜드스타트 문제
결혼 후 처음 장보기를 시작했을 때의 일입니다. 마트에서 애호박 1개가 1,500원에 팔고 있었고, 저는 고민없이 구매했습니다. 그런데 몇 개월 후, 같은 애호박이 700원에 팔리는 것을 보고 깨달았습니다. 농산물 가격은 계절, 날씨, 공급량에 따라 크게 변동하는데, 초보 주부는 이런 시장 정보가 없다면 '비싼 타이밍'에 구매할 수밖에 없었던 것입니다. 정보 비대칭의 문제였습니다.
"관심있는 품목의 집계 정보만 있어도 좋을텐데.."라는 현실의 문제를 인지했습니다. 다시 생각해보니 "아게 바로 주부의 콜드스타트 문제구나!" 라고 생각 했고, 농수산식품유통공사(aT)가 운영하는 kamis API를 알게 된 것도 이때 였습니다. 하지만, 당시에는 서비스화 하기에는 코딩 지식도 부족했고, AI 도구도 지금처럼 발달하지 않아 막연한 아이디어로만 남겨두었습니다.
2.2. 해결방안: No-code n8n으로 AI Agent 구현
지난번 데이터 파이프라인을 위한 치트시트를 공유한 것 처럼, n8n은 이런 간단한 데이터 파이프라인을 구축할 수 있습니다. 특히, Chat Trigger와 AI Agent 노드의 조합으로 챗봇을 만들 수 도 있습니다. 저는 지난 숙원 사업을 어제 반나절 정도에 완성하여, 요즘 제철 과일, 채소, 육류 제품을 쉽게 조회하고 제철 품목을 제안하는 보고서를 생성하는 AI Agent "FarmData"를 만들었습니다.
🤖 AI Agent란? AI Agent란 특정 목표를 달성하기 위해 스스로 의사결정을 하고 다양한 Tool을 이용하여 행동할 수 있는 인공지능 시스템을 뜻합니다. "뇌"에 해당하는 Large Language Model, "기억"에 해당하는 Memory(DB), "손과 발"에 해당하는 Tool(Google Sheest, 웹 검색) 3가지 구성 요소가 기본 뼈대입니다. 기존 LLM은 워크플로우가 선형적이고 예측 가능한 반면 (A-> B -> C) 반면 AI Agent는 목표를 달성하기 위한 비선형적 워크플로우를 구성(A -> B -> A -> C)하여 더 복잡하고 다양한 업무를 수행할 수 있습니다.
저는 이 개념을 손쉽게 n8n으로 No-code로 구현하였습니다. 채팅 인터페이스로 Interactive하게 사용할 수 있는 것은 물론, 실제로 노드들의 상호작용을 하거나 중간 Inout/output 내용을 확인 할 수 도 있어 과정을 이해하기도 쉬웠습니다. 또한 데이터 분석을 위해서 로깅 기능을 추가했습니다.
챗봇 구현 화면
3. 운영기
3.1. 로그 분석
한달동안 쌓인 로그들
링크드인에 포스팅한 뒤로 약 한달이 지났습니다. 포스팅 노출은 18,112회이며 그 이후로 98개의 세션이 실행되었습니다.
첫 포스팅 8월 29일 이후로 지속적으로 세션이 감소하는 자연스러운 모습이 보입니다만, 일부 링크드인의 알고리즘 때문인지 튀는 값들이 조금 보이는 형태입니다. 반등을 한 건 9월 3일(배포 후 일주일), 9월 8일(일요일) 정도에 상승하는 모습을 보이나, 세션의 모수가 작아서 링크드인의 알고리즘 때문인지는 판단하기 어려운 부분이 있습니다.
시간대는 꽤 정직한 모습을 보이는데, 출근 시간인 7시-9시, 저녁시간인 21~22시에 높은 모습을 보입니다. 의외인 점은 퇴근 시간대 많이 세션이 잡히지 않았는데, 퇴근하고는 자기계발 위주의 SNS는 보기 싫은게 아닌가 🤣 라는 생각이 드네요
3.2. 운영의 문제점
데이터가 조회되지 않는 문제
기본 프로덕트는 엄청 간단하게 만들어서 헛점이 많았습니다. 먼저 사용자가 챗봇으로 요청할 때마다 농수산물 품목에 대한 조회가 재현성이 없다는걸 확인했습니다. 아무래도 직거래 시장에서 발생한 데이터를 조회하다 보니 제철이 아닌 과일인 경우 조회하지 못하는 문제가 발생했습니다. 사실 이걸 보완하기 위해서 프롬프트로 "만약 조회 결과가 없다면 이전의 결과를 조회해"라는 식으로 넣었지만 아무래도 조건문이 아닌 프롬프트로 만들다보니 재현성이 떨어졌습니다.
API 안정성 문제
지난 실행에 대한 히스토리 확인
공공 API다 보니 이따금 HTTP 요청이 제대로 수립되지 않은 문제가 있었습니다. 게다가 위 질문에 대한 리포트를 만드는데 있어서 HTTP 요청이 여러번 실행되는 경우도 있었는데, 반복되는 요청에서 역시 실패하는 경우도 발생했습니다. 사실 이 문제는 사전에 기본적인 데이터를 Database에 저장해놓고 거기에 쿼리를 요청하는게 구조적으로 맞는 것 같습니다. 그 외에 필요한 결과가 있으면 그때 API를 요청하는게 맞는 방법이라는걸 깨달았습니다.
사전 테스트
사실 위 내용을 방지할 수 있는 방법은 사전 테스트를 실행해보는 것입니다. n8n에서는 Evaluations이라는 탭을 제공해주는데 이걸 한번 테스트해봤다면 이런 문제를 방지할 수 있었을 것 같습니다.
n8n Evaluation 가이드
4. 나가며
AI Agent를 쉽게 만들 수 있다는 점에서는 입문으로 n8n만한게 없는 것 같습니다. PoC로 사용하면 매우 좋을 것 같고, 비개발자들도 쉽게 접근할 수 있다는 점에서 매력적인 툴인 것 같습니다. 보다 더 수준 높은 Agent 구축을 위해서는 Langchain/LangGraph 프레임워크를 사용하면 되겠다는 생각이 들었습니다. 반면, PoC를 만들긴 쉽지만 완성도 있는 프로덕트를 만드는 것은 역시 설계/평가 관점에서 고려할 점이 있다는 걸 깨달은 프로젝트 였습니다. 또, n8n을 만드는 과정에서 데이터 파이프라인을 위한 cheatsheets를 만들었는데 처음으로 github star를 30개 넘게 받아서 기분이 좋았네요. 앞으로는 지속적으로 유즈케이스를 만들어서 github를 채워볼 생각입니다.
가끔 게임 분야에 관심있는 분들이 데이터분석 포트폴리오로 만들만 한 주제를 추천해달라고 하면 저는 riot API를 추천합니다. 대중성있고 리그오브레전드,룬테라, TFT 등 API도 다양하게 많이 제공해주기 때문입니다. 이번 글은 롤 API를 가져오는 핵심 Endpoint를 정리해보겠습니다.
Product 등록입니다. 왼쪽은 Prouduction 레벨의 API 이며, 요청횟수가 더 많거나 더 많은 정보를 줍니다만, 승인 기간이 느리고 절차가 필요합니다. 우측 Personal API Key를 발급 받으시면 됩니다.
Personal 키의 경우 매번 갱신해야합니다. 또한 요청 횟수가 1초에 20건, 2분에 100건으로 제한되어있기 때문에 꾸준한 데이터 수집을 위해서는 반드시 배치형식의 데이터 수집을 고려해야합니다.
2. 핵심 API 3가지
보통 riot api라고하면 리그오브레전드 게임 내의 게임 정보에 대한 데이터를 생각합니다. 해당 데이터는 match - timeline api에 정보가 있습니다. 이 정보를 가져오려면 matchi id를 가져와야하고 또 이 match id를 구하려면 유저 id(puuid)가 필요합니다. 다음은 이 3가지 api를 cascading 하게 가져오기 위한 각 api의 구조입니다.
이번에는 나만의 서비스를 Claude code를 이용한 바이브 코딩한 절차와 교훈에 대해서 작성해봅니다. 늘 나만의 서비스를 하나씩 만들고 싶었는데 좋은 핑계(?)가 생겨서 하루 이틀 반짝해서 만들어봤습니다. 이게 되네?라고 느끼는 프로젝트였고 웹개발을 직접 핸즈온으로 배우고 구현하는 재미가 있어서 좋은 경험이였습니다. 이 글을 읽고 덜 시행착오를 겪으시고 결과를 만드시길 바랍니다.
1. 문제 정의
커리어의 목표를 위해서 방송통신대에 통계.데이터학과에 25년 2학기에 입학했습니다! 자연스럽게 3학년 편입이라 전공과목을 4학기만에 수강해야하는 상황입니다. 더해서 추후 CS 대학원을 목표로 하고 있어서 컴퓨터과 과목도 넣게되었습니다. 그러다 보니 한학기에 5과목씩 전공을 듣게 되었네요. 많이 듣는건 좋은데 일도 해야하고, 방통대 특성상 자유롭게 들을 수 있는 인강 형태라 미루다가 강의 폭탄 맞을 것 같아서 대책이 필요 했습니다.
한 강좌당 15개가 존재하며 데드라인은 3개월 남짓이다!
제가 해결하고 싶은 문제는 꾸준히 수업을 들을 수 있는 시스템을 만들자에 집중했습니다.
2. 해결 방법 1 - 노션으로 대시보드 만들기
제일 좋은 방법은 수업이 같거나 비슷한 학우들을 모아서 서로의 진척 사항을 공유하는 것입니다. 그래서 노션의 데이터베이스 기능을 이용하려했습니다. 요구사항은 다음과 같습니다.
랜딩 페이지는 학생마다 수강률에 대한 친적사항이 프로그레스바로 보여줄 것
수강기록 페이지는 손쉽게 접근할 수 있는 체크박스형태로 만들 것
그래서 다음과 같은 템플릿을 만들었습니다.
전체 과목의 진척사항 확인개별 강의의 수강 체크
노션에서는 "관계형"이라는 컬럼이 존재합니다. 두 테이블을 관계로 묶고, 수강현황판의 한행은 수강기록 N개의 강의와 1:N 관계이기 때문에 수강기록 데이터베이스 페이지 하나가 수강현황판을 참조하는 식으로 구성했습니다. 또한, 수강현황판에서 수강완료는 "롤업"이라는 컬럼으로 수강기록의 체크박스의 수를 기록하는 형식입니다. 마지막으로 수강 현황판의 "진행률"은 같은 행의 수강완료 / 전체 강의 수의 비율을 시각적으로 나타내었습니다.
반나절정도 노션 데이터베이스를 레퍼런스를 찾고 만들었는데, 생각보다 이 만드는 과정에 "노션식 관계형"에 대한 이해가 없으면 많이 허덕이는 부분입니다. 그래서 동영상으로 소개 영상을 만들었습니다. 맨 밑에 링크 남겨놓겠습니다.
하지만 노션은 신규등록자 입장에서 되게 불편했습니다. 가이드 영상을 고사하더라도, 나의 새로운 과목을 추가할때 15개씩 수강기록을 남겨야했고(자동화를 통해 1~15회차가 자동으로 만들어지지만) 체크박스 하나씩 클릭하자고 수강기록을 만드는 것도 썩 맘에 들지 않았습니다. 그래서 그냥 웹서비스를 하나 만들자라는 결론에 도달했습니다.
3. KNOU Tracker 사이트
4가지 세부 페이지를 구상했습니다.
대시보드: 수업 진행 현황을 볼 수 있는 랜딩페이지
개인 현황: 수업 진행률을 체크할 개인 페이지
신규 등록: 신규 진입자를 위한 등록 페이지
관리
이렇게 하여 신규사용자가 스스로 자신을 등록하고 기록을하며 전체 진척사항을 서로 공유하여 동기부여를 얻을 수 있는 서비스가 만들어졌습니다.
4. 시행착오들
4.1. PRD 문서 작성
개발쪽에서는 PRD라는 문서가 있다는 걸 알게되었습니다. Claude에게 PRD를 만들기 위한 방법으로 질의응답으로 나에게 질문을 해달라고 부탁했고 개발하기전 LLM에게 해당 사항을 읽고 구현하라고 시켰습니다. 나중에 README.md 에 작성하기에도 좋으니 사전에 설정해놓으면 사전 준비작업이 빨라집니다.
README.md 일부
4.2. 스택 선택
Claude code를 요즘 간단한 개발할 때 잘 쓰고 있었습니다. 그래서 다양한 조합을 Claude에게 물어봤습니다.
Python Only(streamlit)
Vanillia Javascript
React,Vue 등 최신 프론트 스택
사실 웹 개발, 특히 프론트쪽은 잘 모르기에 유지보수에 대한 걱정으로 Streamlit을 추구했습니다.
하지만 반나절 정도 해보니 내가 원하는 디자인이 계속 나오지 않았습니다. 특히 좌측 한바닥을 차치하는 세부 페이지 드롭다운, 핵심 페이지의 공간 비효율성이 마음에 들지 않았습니다. 그래서 그냥 빠르게 접었습니다. 3번은 투머치였고, 그냥 Vanilla Js로 하기로 결정하고 만들었습니다. 또한 Github Pages로 배포를, 데이터베이스는 요즘 유명하다는 Superbase를 사용했습니다.
4.2. Claude Code 사용
Claude Code Pro 요금제를 사용하고 있어서 웹개발을 계속 시키면 거의 2시간 만에 토큰이 모두 소모됩니다. 시간이 지나면 다시 한도가 돌아가는 시스템인데 Claude-monitor 를 이용해 중간에 개발하다가 끊키는 일이 없도록 했습니다. 한 3번정도 iteration 끝에 웹페이지를 만든 것 같습니다. Claude-monitor 링크는 하단에 넣어놓겠습니다.
여기서보면 정확히는 Token과 Message 한도가 있는데, Token은 100%가 도달하면 얄짤 없이 더이상 프롬프팅이 불가능한 반면 Message는 100%가 도달 해도 어느정도 명령을 수행해줍니다. 초반에는 Limit이 걸리면 그냥 손놓고 있었는데 요즘 Gemini CLI이나 Codex CLI 라는 대체 툴도 많아서, 손놓고 놀지말고 도구를 바꿔서 사용하면 연속성있게 사용할 수 있을 것 같습ㄴ다.
4.3. 추후 개선할 기능들
빠른 결과를 추구하다보니 비 필수적이 기능들을 아직 구현하지 못했습니다. 차차 구현해볼 생각입니다.
크롬 확장 브라우저를 통한 자동 연동 -> 사실 이게 젤 하고 싶습니다. 근데 지식이 전무해서 찾아봐야하는...
오픈 서비스로 만들기 위한 로그인 기능
사용자의 분석을 위한 로그시스템
버그 리포팅
5. 후기
처음만 해도 이정도의 퀄리티가 하루 반나절만에 나오리라고 생각하지 못했습니다. 간단한 개발은 확실히 접근성이 좋아진 것 같습니다. 하지만 그럼에도 개발을 위한 몇가지 사전 지식이 필요하다는걸 깨달았습니다.
명확한 요구사항
PRD같은 문서의 존재를 아는 것도 중요하지만 내가 정확히 어떤 프로덕트를 만들고 싶은지 소구하는 것이 중요했습니다. 모호한 목표는 모호한 결과물을 만들기 마련이니까요.
백엔드에 대한 기본적인 지식
배포시에 데이터베이스를 만들어야한다는 개념은 아는데 이걸 Superbase에서 어떻게 연결해야하는지는 스스로 공부해야하고, 찾아낼 줄 알아야합니다. 이번에 LocalStorage가 브라우저에 내장될 수 있다는 걸 처음 알았습니다.
웹개발에 대한 기본지식
직접 github 블로그를 만들어서 알았던 부분이여서 완전히 모르는 상태로하진 않았지만 index.html을 비롯한 html에 대한 이해, 웹브라우저에서 개발자도구 - 콘솔의 오류를 체크하는 부분을 몰랐다면 시행착오를 많이 겪었을 것 같습니다. 물론 Claude Code에게 물어보면 방법을 알려주긴 하지만 전혀 개발 생태계를 모른다면 이런 점이 병목이 될 것 같습니다.
CLI AI Assitant는 신이다.
저는 연동하는 방법을 찾지 못해서 Github 배포도, Superbase MCP 설정도 못하고 일일히 손으로 했지만 앞으로는 이런것들이 기본 기능으로 들어오면서 정말 손쉽게 결과물을 만들어낼 것 같습니다. 결국 기술은 발전할 것이니 빠르게 Proto Typing하고 기획하는것 그리고 이런 프로세스를 지속 가능한 시스템으로 내재화하는 것이 중요하겠구나 깨달은 프로젝트 였습니다.
과적합: 에포크를 과도하게 늘리면 학습데이터셋에서는 높은 정확도를 보이지만 새로운 데이터셋에서 일반화 되지 못할 수 있음
Catastophic Forgetting: 특정한 작업을 위한 미세조정 과정에서 처음에 습득한 광범위한 지식을 잃을 위험
데이터 유출: 훈련 및 검증 데이터 세트가 중복됨
RAG
정의: 검색기반 모델과 생성 모델의 장점을 결합, 입력 쿼리를 기반으로 대규모 데이터베이스 또는 지식 기반을 검색하여 관련 정보를 검색
동적 지식 통합: 외부 소스의 실시간 정보를 통합하여 최신 지식이나 특정 지식이 필요한 작업에 적합
문맥적 연관성: 검색된 문서에서 추가적인 맥락을 제공하여 생성 모델의 응답을 향상
다재다능: 더 광범위한 쿼리를 처리할 수 있음
2. Langchain
LLM 기반 어플리케이션 개발을 간소화하고 유연하게 만드는 핵심적인 역할
2.1 Runnable
Chain을 구성하는 모든 구성요소를 연결하고 실행하기 위한 표준 인터페이스
표준화된 메소드: invoke, batch, stream 표준 호출 메소드를 제공
모든 구성요소가 공통된 메서드를 가지게 하는 것
2.2 LCEL
LangChainExpressionLanguage Runnable을 체인으로 구성하여 유닉스 파이프 연산자처럼 선언적인 방식 제공
Prompt | model | output_parser 형식으로 구성
프롬프트 엔지니어링 기법 지원
역할 기반 메시지: ChatPromptTemplate.from_messages 메소드를 사용하여 Human, AI, System 와 같은 역할 기반 메시지를 명시적으로 정의
퓨삿 프롬프팅: FewShotChatMessagePromptTemplate 모델 동작에 영향에 미치는 예시를 사전에 제공할 수 있음
출처: Prompt Engineering vs. RAG vs. Finetuning: What’s the Difference?
StringOutputParser
언어모델의 출력값을 다루기 쉬운 문자열로 변환하기 위해 사용함
LLM이나 챗봇 모델은 단순히 텍스트만 변환하는 것이 아니라 AIMessage, ChatMessage와 같은 복잡한 객체 형태로 출력하기 때문에 순수한 텍스트 내용을 추출하기 위한 방법
3. 기타
3.1. Pydantic 사용하는 이유
파이썬은 동적언어여서 자료형이 사전에 선언되지 않음
타입 힌트는 힌트일 뿐이며 데이터 유효성 검사를 위해 Pydantic를 사용하여 출력 자료형을 구체하하는것
3.2. RAG을 사용해야하는 이유
지식의 단절, 특정 시점까지만 LLM이 학습하기 때문
context window의 한계, 기억 메모리가 무한하지 않음
벡터 DB는 기존 DB가 정확히 요청한 단어만 찾는것과 달리, 유사한 단어를 찾아 응답결과를 좀더 풍성하게 해줌
벡터(색인)과 원본데이터(원자료)를 모두 저장하여 유사한 색인을 찾아 결과를 반환하는 형태로 구현됨
3.3. 메모리를 기억하기 위한 방법
이 부분 확실하지 않음. 애초에 LLM은 stateless 하기 떄문에 정보를 저장하는게 중요.chat_message 메소드에 이전 대화 기록을 AI message, Human Message등을 저장하고 멀티턴으로 구현하는 방법도 있으나 context limt 때문에 적절하지 않을 수 있음. 검색한 결과 다음 메서드가 있는 것을 확인