본문 바로가기

스터디

쪼아요 쪼아요~ 웹소설 쪼아요~

반응형

https://titlejoayo.com/

 

웹소설쪼아요 - 데이터 기반 제목 최적화

 

titlejoayo.com

 

트리컬은 하지않지만 쪼아요 송을 듣고 중독되버려 잠을 조금 포기하고 진행한 사이드 프로젝트 후기 시작합니다.

 

 

 

 

Oh-My-OpenCode로 웹서비스 런칭까지

'웹소설쪼아요' 개발부터 배포까지, AI 에이전트와 함께한 개발기

2026년 1월 · 아티펙트

완성된 서비스

titlejoayo.com — 웹소설 제목 최적화 AI 서비스

86.6%
모델 정확도 (R²)
44개
지원 장르 조합
36개
감정 키워드
TL;DR

Oh-My-OpenCode의 ultrawork 키워드로 FastAPI 백엔드 + React 프론트엔드 + Ridge Regression ML 모델까지 개발. Cloudflare DNS + Render 배포로 서비스 런칭 완료.

만든 것: 웹소설쪼아요

웹소설 작가들을 위한 AI 기반 제목 최적화 서비스다. 제목을 입력하면 예상 조회수, 등급, 감정 키워드 분석 결과를 보여주고, 프롤로그나 키워드를 넣으면 최적화된 제목 5개를 추천해준다.

핵심 기능은 세 가지다:

  • 제목 평가 — Ridge Regression 모델로 예상 조회수 예측, 0-100점 점수 및 A+~D 등급 부여
  • 제목 추천 — 프롤로그/키워드 기반 AI 제목 생성, 최대 50개 후보 중 상위 5개 추천
  • 감정 키워드 분석 — '역전', '귀환', '최강' 등 36개 고성능 키워드 영향도 분석
Python 3.10+ FastAPI React 18 TypeScript TailwindCSS scikit-learn SQLAlchemy Render Cloudflare
webnovel-joayo/ ├── backend/ # FastAPI 백엔드 │ ├── app/ │ │ ├── api/ # /evaluate, /recommend, /usage │ │ ├── services/ # ml_service, title_analyzer │ │ ├── models/ # Pydantic + SQLAlchemy │ │ └── main.py │ └── requirements.txt ├── frontend/ # Vite + React + TS │ ├── src/ │ │ ├── api/ # axios client │ │ ├── components/ # UI 컴포넌트 │ │ └── pages/ # 페이지 컴포넌트 │ └── package.json ├── database/ # init.sql └── docker-compose.yml

개발 과정

1 로컬 개발 — 여기까지는 쉽다

Oh-My-OpenCode 설치 후 프로젝트 폴더에서 OpenCode를 실행했다.

# OpenCode 설치
curl -fsSL https://opencode.ai/install | bash

# Oh-My-OpenCode 플러그인 설치
bunx oh-my-opencode install

# 프로젝트 시작
opencode

그리고 이렇게 입력했다:

ulw 웹소설 제목 최적화 서비스 만들어줘.
- 백엔드: FastAPI + SQLAlchemy
- 프론트: React + TypeScript + Tailwind
- ML: 제목 텍스트로 조회수 예측 (Ridge Regression)
- 기능: 제목 평가, 제목 추천, 사용량 제한

ulw(ultrawork) 키워드 하나로 Sisyphus 에이전트가 작업을 분석하고, 백엔드/프론트엔드/ML 모델을 병렬로 개발하기 시작했다. Gemini가 UI를 짜는 동안 Claude가 FastAPI 엔드포인트를 만들고, GPT가 전체 아키텍처를 검토하는 식이다.

백엔드 로컬 실행

cd backend
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env      # 환경변수 설정
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

프론트엔드 로컬 실행

cd frontend
npm install
npm run dev -- --host --port 5173
💡 Tip

API 문서는 http://localhost:8000/docs에서 Swagger UI로 바로 확인 가능하다. FastAPI의 자동 문서화 기능 덕분에 별도 작업 없이 테스트할 수 있다.

로컬에서 돌리는 것까지는 정말 쉬웠다. Oh-My-OpenCode가 AGENTS.md 파일을 자동으로 생성해서 프로젝트 컨벤션을 관리해주고, 에이전트들이 이 컨텍스트를 공유하며 일관된 코드를 짜줬다.

2 Cloudflare 설정 — DNS 관리

도메인 titlejoayo.com을 Cloudflare에서 관리하기로 했다. 무료 플랜으로도 DNS, SSL, CDN을 전부 쓸 수 있다.

2-1. 도메인 추가

  1. Cloudflare Dashboard 접속
  2. Add a Site → 도메인 입력 (titlejoayo.com)
  3. Free Plan 선택
  4. 기존 DNS 레코드 자동 스캔 → 확인 후 Continue

2-2. 네임서버 변경

도메인 등록 업체(가비아, 후이즈 등)에서 네임서버를 Cloudflare가 제공하는 값으로 변경한다:

# Cloudflare에서 제공하는 네임서버 (예시)
ns1.cloudflare.com
ns2.cloudflare.com
⚠️ 주의

네임서버 변경은 전파되는 데 최대 24시간이 걸릴 수 있다. 보통 1-2시간 내로 완료되지만, 조급해하지 말 것.

2-3. SSL/TLS 설정

  1. Cloudflare Dashboard → SSL/TLS
  2. 암호화 모드: Full (strict) 선택
  3. Edge Certificates → Always Use HTTPS 활성화

2-4. DNS 레코드 (Render 연결용)

아직 Render 배포 전이라면 일단 넘어가고, Step 3 이후에 돌아와서 설정한다.

3 Render 배포 — 백엔드 & 프론트엔드

Render는 무료 플랜으로 웹 서비스를 배포할 수 있어서 사이드 프로젝트에 최적이다. 다만 무료 플랜은 15분간 요청이 없으면 슬립 모드로 들어가서 첫 요청이 느리다는 점은 감안해야 한다.

배포 아키텍처
사용자
Cloudflare
DNS + CDN + SSL
Render
Frontend (Static)
Render
Backend (Web Service)

3-1. GitHub 연결

  1. Render Dashboard 접속
  2. New + → GitHub 저장소 연결
  3. webnovel-joayo 저장소 선택

3-2. 백엔드 배포 (Web Service)

  1. New +Web Service
  2. 저장소 선택 후 설정:
    • Name: webnovel-backend
    • Region: Singapore (Asia 권장)
    • Branch: main
    • Root Directory: backend
    • Runtime: Python 3
    • Build Command: pip install -r requirements.txt
    • Start Command: uvicorn app.main:app --host 0.0.0.0 --port $PORT
  3. Environment Variables 설정:
    DATABASE_URL=sqlite:///./app.db
    SECRET_KEY=your-secret-key-here
    ENVIRONMENT=production
    CORS_ORIGINS=https://titlejoayo.com
    LOG_LEVEL=INFO
  4. Create Web Service 클릭
💡 Tip

Render가 자동으로 *.onrender.com 도메인을 부여한다. 예: webnovel-backend.onrender.com. 이 URL을 프론트엔드 환경변수에 사용한다.

3-3. 프론트엔드 배포 (Static Site)

  1. New +Static Site
  2. 설정:
    • Name: webnovel-frontend
    • Branch: main
    • Root Directory: frontend
    • Build Command: npm install && npm run build
    • Publish Directory: dist
  3. Environment Variables 설정:
    VITE_API_BASE_URL=https://webnovel-backend.onrender.com
  4. Create Static Site 클릭
⚠️ 주의: React Router SPA 설정

React Router를 쓴다면 Render에서 Rewrite Rule을 추가해야 한다. Redirects/Rewrites 탭에서 /*/index.html (Rewrite) 규칙을 추가하지 않으면 새로고침 시 404가 뜬다.

4 커스텀 도메인 연결

이제 Cloudflare DNS와 Render를 연결해서 titlejoayo.com으로 접속할 수 있게 한다.

4-1. Render에서 커스텀 도메인 추가

  1. Render Dashboard → 프론트엔드 서비스 선택
  2. SettingsCustom Domains
  3. titlejoayo.com 입력 후 Add Custom Domain
  4. Render가 제공하는 CNAME 값 복사 (예: webnovel-frontend.onrender.com)

4-2. Cloudflare DNS 레코드 추가

  1. Cloudflare Dashboard → DNSRecords
  2. Add record 클릭:
    # 루트 도메인
    Type: CNAME
    Name: @
    Target: webnovel-frontend.onrender.com
    Proxy status: Proxied (주황색 구름)
    
    # www 서브도메인 (선택)
    Type: CNAME
    Name: www
    Target: webnovel-frontend.onrender.com
    Proxy status: Proxied

4-3. API 서브도메인 — 여기서 삽질했다

백엔드 API를 api.titlejoayo.com으로 연결하려고 했다. 처음엔 단순하게 Cloudflare에서 CNAME만 추가하면 될 줄 알았다.

# 처음 시도한 설정
Type: CNAME
Name: api
Target: webnovel-backend.onrender.com
Proxy status: DNS only (회색 구름)

결과: 안 됨.

Proxied(주황색 구름)로 바꿔봤다. 안 됨.

커뮤니티에 물어봤다. 답변대로 해봤다. 그래도 안 됨.

SSL 설정 문제인가 싶어서 Full, Flexible 다 바꿔봤다. 여전히 안 됨.

몇 시간 삽질 끝에 찾은 해결책은 허무할 정도로 간단했다:

⚠️ 핵심: Render에서 먼저 도메인을 추가해야 한다

Cloudflare DNS 설정만으로는 안 된다. Render Dashboard에서 해당 서브도메인을 Custom Domain으로 등록하고 Verify까지 완료해야 연결이 된다.

4-4. 해결 방법 (API 서브도메인)

  1. Render Dashboard → 백엔드 서비스 (webnovel-backend) 선택
  2. SettingsCustom Domains
  3. api.titlejoayo.com 입력 → Add Custom Domain
  4. Render가 보여주는 안내에 따라 Cloudflare에서 CNAME 레코드 추가:
    Type: CNAME
    Name: api
    Target: webnovel-backend.onrender.com
    Proxy status: DNS only (회색 구름)
  5. Render Dashboard로 돌아와서 Verify 클릭
  6. 인증 성공 후 SSL 인증서 자동 발급 (1-2분 소요)

Verify 버튼을 누르는 순간 바로 연결됐다. Cloudflare DNS 설정은 동일한데, Render 쪽에서 도메인을 "인식"해야만 트래픽을 받아들이는 구조였던 것이다.

💡 교훈

DNS 레코드 설정은 "이쪽으로 트래픽을 보내라"는 신호일 뿐이다. 받는 쪽(Render)에서 "이 도메인으로 오는 요청을 처리하겠다"고 등록하지 않으면 소용없다. 양쪽 다 설정해야 한다.

4-5. 최종 DNS 설정

# Cloudflare DNS Records (최종)

# 프론트엔드 (루트 도메인)
Type: CNAME
Name: @
Target: webnovel-frontend.onrender.com
Proxy status: Proxied

# 프론트엔드 (www)
Type: CNAME
Name: www
Target: webnovel-frontend.onrender.com
Proxy status: Proxied

# 백엔드 API
Type: CNAME
Name: api
Target: webnovel-backend.onrender.com
Proxy status: DNS only

프론트엔드 환경변수도 업데이트:

VITE_API_BASE_URL=https://api.titlejoayo.com

4-6. SSL 인증서 확인

Render와 Cloudflare 모두 자동으로 SSL 인증서를 발급한다. Render Dashboard에서 각 서비스의 Custom Domains 섹션을 확인하면 인증서 상태가 Valid로 표시되어야 한다.

결론: 생각보다 빨랐다

솔직히 말하면, 로컬 개발은 Oh-My-OpenCode 덕분에 정말 쉬웠다. ulw 키워드 하나로 에이전트들이 알아서 구조를 잡고, 병렬로 코드를 짜줬다. AGENTS.md가 자동 생성되면서 프로젝트 컨벤션이 일관되게 유지됐다.

배포 과정에서 삽질한 부분도 있었다. 특히 API 서브도메인 연결에서 "Cloudflare DNS만 설정하면 되겠지"라는 안일한 생각으로 몇 시간을 날렸다. Render에서 Custom Domain을 추가하고 Verify해야 한다는 걸 알았으면 5분 만에 끝났을 일이다.

무료 스택(Render Free + Cloudflare Free)으로 실서비스를 운영하는 건 한계가 있다. 특히 Render 무료 플랜의 슬립 모드는 첫 방문자 경험을 해친다. 그래도 MVP 검증 단계에서는 충분하다.

다음 단계는 구글 애드센스 연동과 사용자 피드백 기반 모델 개선이다.

서비스 바로가기

titlejoayo.com →

무료로 하루 5회 평가, 3회 추천 이용 가능

 

GitHub - code-yeongyu/oh-my-opencode: The Best Agent Harness. Meet Sisyphus: The Batteries-Included Agent that codes like you.

The Best Agent Harness. Meet Sisyphus: The Batteries-Included Agent that codes like you. - code-yeongyu/oh-my-opencode

github.com

 

 

 

 

절대 render에서 커스텀 도메인을 추가해야한다... cloudflare에서 api 프록시 끄고 only dns하더라도 꼭꼭 여기서 한시간 날렸다....