-
Z-Score 평균회귀 전략 설계와 OOS 검증
기존에 운용하던 bb_rsi_reversion 전략이 계속 손실을 냈다. 백테스트에서는 괜찮았는데 라이브에서 실패하는 패턴이었다. 원인을 분석하고 z-score 기반으로 전략을 재설계했다. bb_rsi_reversion 실패 원인 세 가지가 문제였다. 1. 타임프레임이 너무 짧았다 (15m) 15분봉은 노이즈가 많다. 볼린저밴드 하단에 닿았다고 반등이 오는 게 아닌 경우가 많았다. 2. 레짐 필터가 없었다 추세장에서도 평균회귀를 시도했다. 강한 하락 추세에서 “저가에 진입”하면 계속 물린다. 평균회귀는 횡보·방어 구간에서만 작동해야 한다. 3. BB 중선 청산 볼린저밴드 중선(20일 이동평균)에서 청산하다... Read More
-
OpenAI web_search_preview로 크롤링 안 되는 사이트 긁기
가격 비교 플랫폼을 만들면서 야후옥션 일본, Amazon JP, 네이버 중고를 크롤링해야 했다. 문제는 이 사이트들이 전통적인 스크래퍼로 긁기가 까다롭다는 점이다. JS 렌더링이 필요하거나, 봇 감지가 빡빡하거나, DOM 구조가 자주 바뀐다. Selenium이나 Playwright를 붙이는 대신 OpenAI Responses API의 web_search_preview 툴을 써봤다. Responses API와 web_search_preview OpenAI Responses API는 Chat Completions와 다르게 툴을 모델이 직접 실행할 수 있다. web_search_preview 툴을 주면 LLM이 웹... Read More
-
FastAPI + SQLite 동시 읽기/쓰기 타임아웃, WAL 모드로 해결
FastAPI 서버에서 검색 요청이 들어올 때 결과를 SQLite에 저장하는 구조였다. 로컬에서는 잘 돌았는데 Railway에 올리고 나서 간헐적으로 요청이 타임아웃으로 죽기 시작했다. 상황 검색 파이프라인이 끝나면 결과를 DB에 flush하는 구조다. def _persist_normalized_offers(self, db: Session, run_id: str, offers: List[Offer]): rows = [] for idx, offer in enumerate(offers[:200], start=1): row = NormalizedOfferRow(run_id=run_id,... Read More
-
라이브 트레이딩 봇 레짐 분류 강화와 포지션별 손절 추가
자동매매 봇을 실제로 돌리다 보면 백테스트에서 잘 보이지 않던 문제들이 라이브에서 드러난다. 최근에 두 가지를 고쳤다. 하나는 레짐 체인 분류가 너무 쉽게 “trend” 체인으로 들어가는 문제였고, 다른 하나는 체인 수준의 손실 한도는 있는데 개별 포지션 단위 손절이 없다는 문제였다. 레짐 체인 구조 봇은 Transformer 모델이 예측한 레짐 확률 (p_up, p_dn, p_chop, p_hv)를 보고 4가지 체인 중 하나를 선택한다. 체인마다 활성 전략과 exposure_multiplier가 다르다. defensive → exposure 0.40 (방어적 전략만, 노출 낮춤) chop →... Read More
-
멀티에이전트 LLM 가격 검색 파이프라인 설계하기
글로벌 최저가 탐색 플랫폼을 만들면서 가장 먼저 부딪힌 문제가 있었다. 단순히 여러 마켓플레이스를 크롤링하면 되는 게 아니었다. 공식몰·중고·해외직구가 섞인 결과에서 “진짜 최저가”를 뽑으려면 각 소스의 성격에 맞게 쿼리를 다르게 짜야 했고, 부품용·약정포함·빈 박스 같은 미끼 가격도 걸러야 했다. 그리고 이 모든 게 45초 안에 끝나야 했다. 결국 역할이 분리된 에이전트를 두고, Orchestrator가 타임아웃 내에 순서대로 실행하는 구조로 설계했다. 전체 구조 사용자 쿼리 ↓ QueryExpander (LLM/휴리스틱 다국어 확장) ↓ SearchOrchestrator ├── [f... Read More
-
Prefect + LangChain으로 영화 시나리오 AI 분석 파이프라인 만들기
영화·드라마 시나리오 원문을 넣으면 캐릭터, 세계관, 스토리, 액션, 비주얼 방향을 LLM이 분석하고, 그 결과로 Kling AI가 프리비즈 영상을 자동으로 만들어주는 파이프라인이다. 파일 파싱부터 영상 생성·병합까지 엔드투엔드로 돌아가도록 Prefect 메달리온 아키텍처로 설계했다. 전체 구조 입력: .pdf / .txt / .html / .docx Bronze → 파싱 + MD5 캐싱 Silver → 5 LLM 에이전트 분석 Gold → 통합 JSON + 리포트 Previs → intent별 샷 리스트 저장 VideoGen → Kling AI 호출 + FFmpeg 병합 출력: outputs/... Read More
-
S&P500 444종목 전략 탐색 파이프라인
라이브 트레이딩 allowlist를 ETF 14쌍에서 S&P500 전체로 확장하면서, 종목별로 전략과 파라미터 조합을 자동으로 선별하는 파이프라인을 만들었다. 핵심은 Train 기간에서 최적화하고 Test 기간에서 독립적으로 검증하는 OOS 분리 구조다. 왜 파이프라인이 필요했나 전략을 전체 기간 데이터로 최적화하면 과최적화가 된다. 백테스트 Sharpe가 2.0이어도 실제 라이브에서 음수가 나오는 경우가 많다. Train/Test 분리가 필수다. 444종목에 4개 전략을 각각 수십~수백 개 파라미터 조합으로 돌리면 수만 번의 백테스트가 필요하다. 수작업으로 할 수 없어서 search_low_corr_... Read More
-
스캔 PDF 처리 - pdfplumber + tesseract OCR fallback 구현
시나리오 분석 파이프라인을 만들면서 PDF 지원이 필요했다. 영화 시나리오가 PDF로 오는 경우가 많다. 문제는 PDF가 두 종류라는 것. 텍스트 레이어가 있는 일반 PDF, 그리고 종이를 스캔한 이미지 PDF. 전자는 텍스트 추출이 간단하지만 후자는 OCR을 써야 한다. pdfplumber 기본 추출 import pdfplumber def _parse_pdf(path: Path) -> str: texts: list[str] = [] with pdfplumber.open(path) as pdf: for page in pdf.pages: page_text... Read More
-
ROC 이중 모멘텀 전략 설계
golden_triangle 전략이 EMA 크로스오버 기반이라면, roc_dual_momentum은 가격 변화율(ROC)을 직접 비교한다. 이동평균의 후행성 없이 모멘텀의 방향과 가속도를 직접 잡는다. ROC 이중 모멘텀이란 ROC(Rate of Change)는 N봉 전 가격 대비 현재 가격의 변화율이다. ROC(n) = (현재가 - n봉 전가) / n봉 전가 이중 모멘텀은 단기 ROC와 장기 ROC를 동시에 본다. fast_roc(10봉): 최근 모멘텀 slow_roc(40봉): 중기 모멘텀 단기 ROC가 장기 ROC보다 크다는 건 모멘텀이 가속 중이라는 뜻이다. 반대면 감속. fast_... Read More
-
Clova OCR API로 영상 크레딧 텍스트 자동 추출
영화나 드라마 엔딩 크레딧에는 모든 스태프가 나온다. 이걸 자동으로 읽어서 DB에 넣을 수 있으면 KOBIS에 없는 스태프 데이터를 보완할 수 있다. 방식: 영상에서 프레임 추출 → OCR로 텍스트 인식 → LLM으로 역할/이름 파싱. OCR 선택 과정 처음엔 Tesseract를 썼다. 무료고 Python 바인딩이 있다. 한국어 텍스트 인식률이 낮았다. 특히 영상 크레딧처럼 배경이 어둡고 폰트가 작은 경우. 인식률이 30~40% 수준. Google Vision API도 테스트했는데 한국어 세로쓰기나 특수 폰트에서 틀리는 경우가 있었다. Clova OCR(Naver)이 한국어 인식률이 가장 높았다. 영상 ... Read More
-
동적 웹 크롤링 Prefect 플로우 + Langfuse 게이트 연동
KOBIS, TMDB, 나무위키에 이어 드라마 스태프 정보가 구조화된 형태로 잘 정리된 미디어 정보 사이트 크롤링 플로우를 추가했다. 클레딧 서비스에 필요한 경력 데이터를 보완하기 위해서다. 이번에는 Langfuse 게이트를 붙여서 LLM이 추출한 데이터 품질을 파이프라인 내에서 검증하도록 했다. 플로우 구조 미디어 정보 페이지 크롤링 (patchright) ↓ Bronze: 원본 HTML 파싱 → 구조화 ↓ Silver: LLM 경력 추출 + Langfuse 게이트 ↓ Gold: DB 적재 기존 KOBIS/TMDB 플로우와 동일한 메달리온 아키텍처를 따른다. patchright로 ... Read More
-
PPO 강화학습으로 게임 봇 만들기 - Stable Baselines3 실전 사용기
강화학습(RL)이 게임 자동화에 어떻게 쓰이는지 직접 실험해보고 싶었다. 카카오 게임봇에서 실행되는 카드 강화 미니게임을 타겟으로 잡았다. 규칙 기반 매크로는 이미 만들어뒀고, RL 에이전트가 더 잘할 수 있는지 비교하고 싶었다. 구조 설계 게임 화면 파싱 → Gym 환경 → PPO 에이전트 학습 → 매크로 실행 화면에서 게임 상태를 읽어서 Gym 환경에 넣고, PPO 에이전트가 액션을 선택하면 매크로로 실행하는 구조다. 데이터 수집 카카오톡 채팅 로그를 export해서 게임 상태를 파싱했다. 채팅 메시지에 강화 결과(성공/실패, 현재 레벨 등)가 텍스트로 나온다. import pandas as pd ... Read More
-
LLM으로 영화 스태프 데이터 Silver ETL 만들기
클레딧의 핵심 데이터는 영화/드라마 스태프 이력이다. 감독, 배우, 촬영감독 등이 어떤 작품에 참여했는지. KOBIS 공식 API에서 스태프 데이터를 내려받으면 이름과 직무가 오는데, 문제는 품질이다. 같은 사람인데 이름 표기가 다르거나(“박준영” vs “박준영 B”), 직무명이 표준화되지 않거나(“촬영” vs “촬영감독” vs “Dir. of Photography”). 이걸 정제하는 Silver ETL을 LLM으로 만들었다. 문제 정의 // KOBIS Raw 스태프 데이터 (Bronze) { "movieCd": "20251234", "directors": [{"peopleNm": "홍길동", "pe... Read More
-
카카오톡 챗봇으로 Notion 게임 리스트 관리하기
Notion에 게임 리스트를 관리하는데, 매번 앱을 열어서 수동으로 추가하는 게 번거로웠다. 카카오톡 채널 봇으로 명령어 하나로 Notion DB에 추가되도록 만들었다. /추가 백투더던 PC 생존 2D 시뮬레이션 이렇게 입력하면 제목, 플랫폼, 태그가 파싱돼서 Notion 데이터베이스에 새 행이 추가된다. 구조 Flask로 카카오톡 스킬 서버를 구현하고, Render에 배포했다. src/ app.py # Flask 진입점 kakao_chatbot.py # 스킬 서버 + Notion API 연동 assets/ game_emoge.svg # 카드 썸네일용 아이콘 카카오톡 ... Read More
-
나무위키 미디어 데이터 크롤링 + KOBIS-TMDB 매칭 자동화
KOBIS와 TMDB만으로는 드라마 스태프 데이터가 부족했다. 특히 OTT 오리지널이나 최신 드라마는 KOBIS에 누락이 많다. 나무위키가 의외로 드라마/영화 스태프 정보가 잘 정리되어 있다. 비공식 데이터라 신뢰성 이슈가 있지만 KOBIS 공식 데이터와 교차 검증하면 쓸 수 있다. ETL 클래스 설계 처음엔 각 소스별로 함수를 만들다가, 코드가 너무 비슷한데 각각 다른 파일에 흩어지는 문제가 생겼다. 공통 인터페이스를 정의하는 베이스 클래스로 리팩터링. from abc import ABC, abstractmethod from dataclasses import dataclass from typing imp... Read More
-
Prefect로 KOBIS/TMDB 영화 데이터 ETL 파이프라인 구축
클레딧 서비스에 필요한 영화/드라마 데이터를 자동으로 수집하고 적재하는 파이프라인이 필요했다. KOBIS(영화진흥위원회)와 TMDB(The Movie Database)를 주 소스로 선택했다. Prefect를 오케스트레이터로 쓰기로 했다. Airflow도 검토했는데 Prefect는 로컬 실행이 간단하고 태스크 단위 재시도 설정이 직관적이었다. 기본 구조 메달리온 아키텍처로 3단계. Raw (API 응답 그대로) → Staging (정제) → Production DB from prefect import flow, task from prefect.tasks import task_input_hash from d... Read More
-
Playwright로 예약 자동화 봇 만들기 - 병렬 처리와 Shadow DOM 대응
예약 자동화 봇을 Playwright로 만들었다. 구체적인 내용은 생략하고, 만들면서 부딪혔던 기술적 문제들만 정리한다. Shadow DOM 문제 최신 React 기반 예약 위젯은 Shadow DOM을 쓰는 경우가 많다. 일반 page.locator로는 Shadow DOM 안쪽 요소에 접근이 안 된다. Playwright는 pierce CSS selector로 Shadow DOM을 뚫을 수 있다. # 일반 locator — Shadow DOM 안쪽 접근 불가 page.locator("button.booking-confirm") # 작동 안 함 # pierce로 Shadow DOM 관통 page.locat... Read More
-
이터널리턴 카카오톡 + 디스코드 챗봇 만들기
이터널리턴(Eternal Return) 게임 커뮤니티용 챗봇을 만들었다. 카카오톡 채널 봇과 디스코드 봇 두 가지를 동시에 지원한다. 주요 기능: 닉네임으로 유저 전적 / 통계 / 랭킹 조회 공식 홈페이지 패치노트 자동 스크래핑 → 디스코드 채널로 전송 카트 명령어, 기타 유틸리티 구조 카카오톡 봇은 Flask 웹훅 서버로 처리하고, 디스코드 봇은 discord.py로 별도 프로세스로 실행한다. . ├── app.py # Flask 웹훅 서버 (카카오톡) ├── discord_bot.py # discord.py 봇 ├── eternal_api.py # 이터널리턴 공식 ... Read More
-
영상 크레딧 OCR 파이프라인 — 히트맵 크롭 + Clova OCR + Bi-Encoder 추천
영상에서 스태프 크레딧을 자동으로 추출하는 파이프라인을 구축했다. 크레딧 구간의 프레임에서 텍스트 영역을 감지하고, OCR로 텍스트를 추출한 뒤, Bi-Encoder로 직군 매칭 추천까지 이어지는 end-to-end 구조다. 전체 파이프라인 영상 프레임 입력 ↓ FilterPipeline (EAST 텍스트 감지 → 히트맵 생성) ↓ HeatmapCropper (히트맵 기반 크레딧 영역 크롭) ↓ OCRProcessor (Clova OCR / Tesseract) ↓ 후처리 (텍스트 정제, 컬럼 클러스터링) ↓ Bi-Encoder (직군 분류 추천) HeatmapCropper... Read More
-
PostgreSQL DISTINCT ON과 ORDER BY 함께 쓸 때 빠지는 함정
경력 목록 API에서 특정 조건으로 조회 시 500 에러가 발생했다. 에러 메시지는 이랬다. ProgrammingError: SELECT DISTINCT ON expressions must match initial ORDER BY expressions DISTINCT ON을 쓰면서 ORDER BY가 맞지 않아서 생긴 에러. 처음 보면 뭔 소린지 몰랐다. 왜 DISTINCT ON을 썼나 사용자의 경력에서 같은 작품에 여러 직무로 참여한 경우, 작품별로 하나씩만 보여줘야 했다. Career.objects.order_by('media_id', '-created_at').distinct('media_id') ... Read More
-
Android 15 16KB 페이지 크기 지원 - React Native 앱 대응기
Google이 Android 15부터 일부 기기에서 16KB 페이지 크기를 지원하기 시작했고, 2025년 이후로는 Play Store 신규 앱이 16KB 정렬을 지원해야 한다는 요구사항을 공지했다. 클레딧(Cleddit) React Native 앱에서 이 대응 작업을 했다. 뭐가 문제인가 기존 Android는 4KB 페이지 크기를 기준으로 동작했다. 16KB 페이지 크기 기기에서는 네이티브 라이브러리(.so 파일)가 16KB 경계에 정렬되어 있어야 한다. 그렇지 않으면 앱이 실행되지 않는다. 직접 작성한 C/C++ 코드가 없어도, 의존하는 네이티브 라이브러리 중 하나라도 정렬이 안 돼 있으면 문제가 된다. ... Read More
-
React Native BottomSheet 크로스플랫폼 이슈 정리
앱 여러 화면에서 BottomSheet를 썼는데, iOS에서는 자연스러운 게 Android에서 어색하거나 그 반대인 경우가 계속 생겼다. 전체 리팩터링을 하면서 정리했다. 라이브러리는 @gorhom/bottom-sheet를 썼다. snap points 문제 기존 코드에서 snap points를 퍼센트로 고정해놨다. // 기존 - 단순 고정값 const snapPoints = ['25%', '50%', '90%']; 문제는 콘텐츠 높이에 따라 25%가 너무 낮거나 너무 높은 케이스가 생겼다. 댓글이 1개인데 50% 높이가 되거나, 댓글이 50개인데 90%가 부족하거나. 동적으로 콘텐츠 높이에 맞추되, 최... Read More
-
NICE 본인인증 React Native WebView 연동 삽질기
클레딧 앱에 본인인증을 붙였다. NICE에서 제공하는 패스(PASS) 인증 방식. 공식 문서가 있긴 한데, React Native WebView에서 쓸 때의 레퍼런스가 거의 없어서 처음부터 삽질이었다. 인증 흐름 앱 → Django 서버: 인증 토큰 요청 Django → NICE API: 암호화 토큰 발급 Django → 앱: 인증 URL + 암호화된 파라미터 반환 앱: WebView로 NICE 인증 페이지 오픈 사용자: PASS 앱 인증 완료 NICE → Django 콜백: 인증 결과 (암호화됨) Django: 복호화 후 앱에 결과 전달 Django 암호화 처리 NICE는 A... Read More
-
Firestore로 실시간 채팅 구현 - React Native에서 쓸 때 주의할 것들
클레딧 앱에 DM 기능을 붙였다. 기술 선택은 Firestore. Django 서버에 채팅 테이블을 만드는 것보다 실시간 동기화가 기본으로 제공되기 때문. Firestore 데이터 구조 chats/ {chatRoomId}/ messages/ {messageId}/ text: string sender_id: string created_at: Timestamp is_read: boolean participants: [userId1, userId2] last_message: string last_message_at: ... Read More
-
React Native 메모리 누수 잡기 - FlatList, React Query, 타이머
앱 사용 중 스크롤이 점점 버벅거린다는 피드백이 있었다. 오래 쓸수록 심해졌다. 메모리 누수 세 군데에서 찾았다. 1. BannerSection 타이머 누적 배너 자동 슬라이드 타이머가 화면 전환 시 제거되지 않았다. 탭을 왔다갔다 할수록 타이머가 쌓였다. // 문제 코드 useEffect(() => { const timer = setInterval(slideNext, 3000); // 반환값 없음 - cleanup 없음 }, []); React Navigation에서 탭 화면은 탭 전환해도 unmount되지 않는다. useEffect의 cleanup이 호출 안 된다. // 수정 - use... Read More
-
배너에 DB 실시간 통계 붙이기 - 클라이언트에서 계산하던 걸 API로 옮긴 이야기
홈 화면 배너에 “등록 배우 X명”, “등록 작품 Y편” 같은 수치를 보여주는 기능이 있었다. 초기에는 React Native에서 API로 유저 수, 작품 수를 따로 가져와서 계산했다. API가 늘어나면서 홈 화면 진입 시 요청이 5~6개가 됐다. 통계 집계 API 한 번의 요청으로 배너에 필요한 수치를 모두 반환하는 API를 만들었다. class DBStatsAPIView(APIView): def get(self, request): from django.db.models import Count stats = { 'user_count': User.o... Read More
-
DRF 피드 API N+1 쿼리 전부 잡기
배너 섹션과 피드를 합친 홈 화면 API 응답이 느렸다. 측정해보니 쿼리가 40개 넘게 나가고 있었다. 문제 파악 # TimedViewSetMixin으로 측정 [PERF] GET /api/v2/feeds/ | 580ms total | 47 queries | 420ms SQL 47개 쿼리. 피드 20개 목록 요청에. 각 쿼리 로그를 보면 패턴이 보였다. SELECT * FROM feeds_feed WHERE id = X (반복 20회) SELECT * FROM users_user WHERE id = X (반복 20회) SELECT * FROM careers_career WHERE user_id = X ... Read More
-
TimesFM + FinBERT로 AI 자동매매 시스템 만들기
자동매매 시스템을 한번 만들어보고 싶었다. 규칙 기반 전략만으로는 시장 변화에 대응하기 어렵고, AI 모델을 붙이면 어느 정도 보완이 될 것 같았다. 실제 주문 연동보다는 파이프라인 설계와 AI 모델 실험에 집중했다. Backtrader로 백테스팅을 돌려서 전략을 검증하는 구조까지 만드는 게 목표였다. 전체 구조 가격 데이터 수집 (yfinance/CCXT) ↓ TimesFM / LSTM 가격 예측 ↓ FinBERT 뉴스 감성 분석 ↓ 신호 결합 → 매매 판단 ↓ Backtrader 백테스팅 가격 예측 — TimesFM Google이 공개한 TimesFM은 시계열 데이터에 특... Read More
-
Django REST API v2 전환과 쿼리 성능 최적화
클레딧 API가 v1에서 v2로 전환되는 시점에 성능 최적화를 같이 진행했다. 기존 Media 모델에 메타 정보가 너무 많이 쌓여 있었다. 시즌, 에피소드, 제작사 정보까지 한 테이블에. v2에서는 MediaMetaInfo를 별도 모델로 분리했다. 모델 구조 변경 # v1 - 하나의 모델에 다 때려넣기 class Media(models.Model): title = models.CharField(max_length=200) year = models.IntegerField(null=True) genre = models.CharField(max_length=100, null=True) ... Read More
-
Django ViewSet에 쿼리 실행 시간 측정 붙이기
API 응답이 느리다는 제보를 받았다. 얼마나 느린지 수치가 없으면 어디서 시작해야 할지 모른다. QuerySet 실행 시간을 로그로 남기는 미들웨어를 붙이는 방법도 있지만, 특정 ViewSet 메서드 단위로 시간을 찍고 싶었다. 미들웨어는 요청 전체 시간이라 쿼리가 많은 뷰에서 어떤 쿼리가 병목인지 파악하기 어렵다. 접근 방법 connection.queries를 활용했다. Django DEBUG=True 환경에서는 실행된 모든 SQL과 시간이 기록된다. from django.db import connection, reset_queries import time class TimedViewSetMixin: ... Read More
-
EAST 텍스트 감지 모델로 영상 엔딩 크레딧 구간 자동 감지하기
영화·드라마 엔딩 크레딧에서 스태프 정보를 뽑아야 했다. 크레딧이 언제 시작하는지를 먼저 찾아야 하는데, 사람이 직접 타임코드를 찍는 방식은 콘텐츠가 늘어날수록 현실적이지 않았다. OTT 플랫폼(넷플릭스, 웨이브, 티빙, 디즈니+ 등)마다 플레이어가 다르고, 크레딧 구간을 자동으로 감지하는 방법이 필요했다. 핵심 아이디어 엔딩 크레딧은 텍스트가 가득한 구간이다. 일반 영상 씬과 비교하면 프레임 안에 텍스트 박스 개수가 훨씬 많다. EAST(Efficient and Accurate Scene Text Detector) 모델로 프레임당 텍스트 영역 수를 세면 크레딧 구간을 찾을 수 있다고 판단했다. EAST 모... Read More
-
C(Pro C)로 VAN 대용량 배치 처리와 TCP 소켓 프로세스 개발하기
KIS정보통신은 VAN(Value Added Network) 사업자다. 카드 결제 승인 데이터가 가맹점 → VAN → 카드사 → 정산 흐름으로 이동하는데, 이 중간 처리를 담당한다. 여기서 하루 1,000만 건이 넘는 거래내역 배치 프로세스와, 실시간 승인서버와 정보계(Oracle) 간 원장 동기화 TCP 소켓 프로세스를 맡았다. 기술 스택이 C(Pro C)인 이유 금융권 레거시 시스템은 아직 C 기반이 많다. 특히 AIX(IBM Unix), Linux 서버에서 Oracle DB와 직접 통신하는 Pro C(Oracle Precompiler for C)를 쓴다. Pro C는 C 코드 안에 SQL을 직접 작성하... Read More
-
Git-Hub 사용법
GitHub 사용법 GitHub는 소스 코드 관리를 위한 웹 기반의 호스팅 서비스로, Git을 사용하는 프로젝트를 지원합니다. 이 포스트에서는 GitHub의 기본적인 사용법을 단계별로 설명하겠습니다. 목차 GitHub 계정 만들기 저장소 만들기 로컬 저장소와 연결하기 커밋하고 푸시하기 풀 리퀘스트 보내기 기타 유용한 명령어 GitHub 계정 만들기 GitHub 웹사이트로 이동합니다. Sign up 버튼을 클릭합니다. 필요한 정보를 입력하고 계정을 만듭니다. 저장소 만들기 로그인 후, 오른쪽 상단의 + 버튼을 클릭하고 New repository를 선택합니다. ... Read More
-
Docker로 Apache Airflow 로컬 환경 구성하기
Apache Airflow를 로컬에서 돌리기 위한 Docker Compose 구성을 정리한다. 공식 문서에서 제공하는 docker-compose.yaml 기반이고, CeleryExecutor로 Worker를 여러 개 띄울 수 있는 구성이다. 사전 준비 Docker 설치 Docker Compose v1.29.1 이상 구성 요소 CeleryExecutor 구성에서 돌아가는 서비스들: 서비스 역할 postgres Airflow 메타데이터 DB redis Celery 브로커 (... Read More
-
Markdown & Kramdown 문법
Markdown 문법 폰트 크기, 굵기, 기울기 H1 H2 H3 H4 H5 ## H1 ### H2 #### H3 ##### H4 ###### H5 기울여서 굵게 강조하고 기울여서 혼용하여 기울여서 사용할 수 있습니다 *기울여서* **굵게** ***강조하고 기울여서*** **혼용하여 _기울여서_ 사용할 수 있습니다** 단락 나누기 - – - -- --- BlockQuote Second line Third line Fourth line Fifth line ... Read More
-
Docker와 github.io 활용하여 기술 블로그 만들기
GitHub Pages와 Jekyll로 기술 블로그를 구축했다. 로컬 개발은 Docker로 Ruby/Jekyll 환경 없이 돌리고, main 브랜치에 push하면 GitHub Actions가 자동 빌드해서 배포하는 구조다. 왜 GitHub Pages + Jekyll인가 Velog, Tistory 등 기존 플랫폼 대신 GitHub Pages를 선택한 이유는 하나다. 포스트 전체가 Git 히스토리로 관리되고, 자유롭게 커스터마이징할 수 있다. Jekyll은 Markdown 파일을 정적 HTML로 변환해준다. 서버 없이 GitHub Pages에 올리면 무료로 호스팅된다. 테마 선택 — jekyll-theme-ya... Read More
-
Java Spring에서 외부업체 REST API Web Service 코어 설계하기
서울애널리티카에서 TG삼보컴퓨터와 외부 파트너사(Amway, Epson, Ilyang Logis 등) 간 데이터 연동을 담당했다. 기존 방식은 파트너사가 TG삼보의 DB에 직접 접근하는 방식이었다. 보안상 문제가 있고 인터페이스 관리가 어려워서 REST API로 전환하는 프로젝트를 맡았다. 기존 방식의 문제 파트너사 담당자가 DB 접근 계정을 알고 있음 어떤 쿼리를 실행하는지 모니터링 불가 파트너사별 접근 권한 제어 불가능 DB 스키마가 바뀌면 파트너사 코드도 전부 바꿔야 함 API 코어 설계 모든 파트너사 요청이 공통 인터셉터를 거치도록 했다. @Component public clas... Read More
-
PHP 5.4 레거시 프로젝트 Docker로 이전한 경험
클라이언트가 PHP 5.4로 만들어진 사이트를 유지보수해달라고 했다. 2023년에 PHP 5.4. 로컬에 PHP 5.4를 설치하려면 Homebrew에서 지원도 안 하고, 수동으로 소스 컴파일해야 한다. macOS 최신 버전에서는 의존성 충돌도 있다. Docker로 해결했다. 환경 구성 docker-compose.yml에 nginx + PHP-FPM + MySQL을 묶었다. version: '3.8' services: nginx: image: nginx:alpine ports: - "8080:80" volumes: - ./src:/var/www/html ... Read More
-
FastAPI AI 서버 보일러플레이트 구성하기
AI 모델 서빙 서버를 반복해서 만들다 보면 매번 같은 설정을 처음부터 하게 된다. Poetry 패키지 관리, Docker 구성, pre-commit 코드 품질, 버전 관리까지 한 번에 잡아둔 FastAPI 보일러플레이트를 만들었다. 전체 구조 src/ main.py # FastAPI 앱, 미들웨어, 라우트 interface.py # 요청/응답 Pydantic 모델 util.py # CamelCase 응답 유틸 Dockerfile.pip # pip 기반 Docker 이미지 Dockerfile.poetry # poetry 기반 Docker 이미지 docker-comp... Read More
-
Spring Boot 다중 역할 CMS 설계 — Admin/Inspector/Worker 인증 분리
서울애널리티카에서 촬영 스태프 프로젝트 관리 CMS를 맡았다. 관리자(Admin), 감리(Inspector), 작업자(Worker) 세 가지 역할이 있고, 역할마다 접근 가능한 메뉴와 기능이 달랐다. Spring Boot + Spring Security로 역할별 인증을 분리하고, JPA(Repository) + MyBatis(Mapper) 혼용 구조로 설계했다. 다중 역할 인증 구조 세 역할이 같은 로그인 폼을 쓰지만, 인증 처리는 각각 별도 Provider를 쓴다. HTTP 요청 ↓ CustomAuthenticationFilter (공통 필터) ↓ AuthenticationManager ... Read More
-
Node.js + Python으로 멀티플랫폼 소셜 미디어 크롤러 만든 경험
서울애널리티카에서 소셜 미디어 데이터 수집 파이프라인을 처음부터 구축하는 일을 맡았다. 요구사항은 단순했다. 인스타그램, 네이버 뉴스/카페, 트위터, 뽐뿌, 보배드림에서 키워드 기반으로 게시물을 수집하고 대시보드에 보여줄 것. 문제는 각 플랫폼마다 수집 방식이 완전히 달랐다. 구조 결정 Node.js를 메인 서버로 쓰고, 처리가 복잡한 Instagram 부분은 Python으로 분리했다. src/ models/ sns/ # Instagram, Twitter community/ # 뽐뿌, 보배드림 portal/ # 네이버 뉴스, 카페 routes/ ... Read More
-
Flutter PCR 의료기기 진단 앱 개발 - BLE UART 통신과 Ct 값 알고리즘 이식
서울애널리티카에서 위즈바이오의 PCR 의료기기 진단 앱을 맡았다. 대상 기기는 Cleo One — COVID-19를 포함한 다중 채널 PCR 진단기다. 앱은 BLE로 기기와 통신해서 PCR 형광 데이터를 실시간으로 수집하고, Ct(Cycle Threshold) 값을 산출해서 양성/음성을 판정한다. 기존 C#으로 작성된 Ct 값 알고리즘을 Dart로 이식하는 게 핵심 과제였다. App Store / Play Store 출시까지 담당했고, 이후 단일 기기(Single)에서 다중 기기 동시 테스트(Multi) 버전으로 확장했다. BLE 통신 구조 — Nordic UART Cleo One은 Nordic Semicon... Read More
-
PHP 웹 서버 온프레미스 → GCP Docker 이전
서울애널리티카에서 인하대학교 PHP 웹 서비스의 서버 이전을 맡았다. 기존 온프레미스 서버에서 회사 GCP 서버로 옮기고, Docker 기반으로 재구성하는 작업이었다. 기존 환경 온프레미스 Linux 서버에서 Apache + PHP 5.x 직접 구동 파일이 서버에 직접 배포된 방식 (FTP 접근) DB는 동일 서버에 MySQL 이전 목표는 GCP VM에 Docker 기반으로 올려서 이전 가능성과 관리 편의성을 높이는 것이었다. Docker 구성 PHP 5.x 레거시라 기존 버전을 유지해야 했다. Docker 덕분에 호스트 환경과 분리할 수 있었다. # PHP 5.6 + Apache FROM... Read More
-
PHP 레거시 CMS 절차지향 → MVC2 패턴으로 리팩토링
에스아이알소프트의 CMS 코어 코드는 2010년대 초에 만들어진 레거시였다. 단일 PHP 파일에 DB 쿼리, 비즈니스 로직, HTML이 전부 섞여 있는 구조였다. // 기존 구조 예시 <?php $conn = mysql_connect(...); // mysql_* 함수 사용 중 $result = mysql_query("SELECT * FROM posts WHERE id=" . $_GET['id']); $row = mysql_fetch_array($result); // 바로 HTML 출력... ?> <html> <body> <h1><?= $row['tit... Read More
-
Git API로 PHP 프로젝트 FTP/SFTP 자동 배포 구현하기
에스아이알소프트 CMS는 여러 고객사 서버에 올라가 있었다. 업데이트가 생기면 개발자가 직접 FTP로 파일을 하나씩 올려야 했다. 실수로 파일을 빠뜨리거나 이전 버전을 덮어씌우는 사고가 간간이 있었다. CI/CD 도구 없이 Git API를 활용해서 자동 배포를 구현하기로 했다. 기본 아이디어 GitHub API로 최신 릴리스 태그를 읽어서, 현재 설치된 버전보다 높으면 자동으로 업데이트 파일을 내려받아 배포하는 방식이다. 고객사 서버에서 스케줄러 실행 → GitHub API로 최신 릴리스 버전 확인 → 현재 버전과 비교 → 버전이 높으면 변경된 파일 목록 조회 → FTP/SFTP... Read More
-
PHP CMS에 inicis 간편인증 모듈 연동하기
에스아이알소프트에서 운영하던 PHP CMS에 inicis 간편인증 모듈을 붙이는 작업을 맡았다. 간편인증은 공인인증서 없이 SMS나 생체인식으로 본인확인을 하는 방식이다. inicis 연동 구조 inicis는 클라이언트 → inicis 서버 → 콜백 방식으로 동작한다. 사용자 브라우저 → inicis 인증 팝업 열기 → inicis 서버에서 인증 처리 → 인증 결과를 콜백 URL로 POST → 우리 서버에서 결과 검증 PHP로 inicis에서 제공하는 SDK를 연동해야 했다. SDK가 PHP 5.x 기준이라 현재 환경에서 deprecated 함수를 일부 교체해야 했다. 가장 ... Read More
-
TypeScript + GraphQL로 B2B 경비 정산 서비스 백엔드 설계하기
B2B 경비 정산 서비스의 백엔드와 모바일 앱을 풀스택으로 개발했다. 기업의 임직원 경비 신청 → 승인 → 정산 흐름을 처리하는 서비스다. 백엔드는 Node.js + TypeScript, API 레이어는 GraphQL(graphql-yoga), 데이터는 TypeORM + MySQL로 구성했다. 모바일 앱은 React Native + Expo + Apollo Client. 왜 GraphQL인가 REST API로 설계했다면 경비 신청 상세, 프로젝트 정보, 승인자 목록을 각각 다른 엔드포인트로 호출해야 했다. GraphQL로 클라이언트가 필요한 데이터를 한 쿼리로 가져오게 설계했다. // 경비 신청 스키마 예시... Read More
-
CentOS 5 → CentOS 7 서버 이전 삽질기
한경ITS에서 마지막으로 맡은 작업이 서버 이전이었다. 운영 중인 서버가 CentOS 5였다. CentOS 5는 2017년에 이미 EOL이었는데 계속 쓰고 있었다. 보안 업데이트가 전혀 안 되는 상태였다. 신규 CentOS 7 서버를 구축하고 기존 PHP/Apache 서비스를 전부 이전하는 작업이었다. CentOS 5와 7의 차이 처음에 별거 아니라고 생각했는데 생각보다 차이가 컸다. init vs systemd CentOS 5는 SysVinit 기반이라 서비스 관리가 service 명령어였다. CentOS 7은 systemd라서 systemctl을 써야 한다. # CentOS 5 service httpd... Read More
-
PHP + jQuery로 사내 인트라넷 리뉴얼하기
첫 직장인 한경ITS에서 맡은 첫 번째 프로젝트가 사내 인트라넷 리뉴얼이었다. 기존 그룹웨어 시스템이 오래돼서 UI도 낡고 기능도 부족한 상태였다. PHP, JavaScript(jQuery), MySQL 스택이었다. 백엔드 API부터 프론트엔드까지 풀스택으로 혼자 담당했다. 기존 시스템 문제 인터페이스가 2010년대 초반 수준으로 노후화 기능이 게시판, 결재 정도만 있고 부서별 필요 기능이 누락 코드 구조가 파일 단위로 로직이 흩어진 절차적 방식 리뉴얼 방향 UI는 전면 재설계. jQuery로 동적 인터랙션을 추가했다. // 기존: 파일 하나에 DB 쿼리와 HTML이 뒤섞인 구조 // 리뉴... Read More
