Minbook
EN
프로토타입에서 상용화로 — 바뀐 것들 목록

프로토타입에서 상용화로 — 바뀐 것들 목록

MJ · · 14 분 소요

해커톤 MVP에서 상용 SaaS로 전환하며 적용한 10가지 핵심 변경 사항 정리. 보안 강화, JWT 인증, KO/EN 다국어 지원, 결제 자동화, 에러 모니터링, DB 정규화 등 '돈을 받을 수 있는 서비스'로 만들기 위한 우선순위별 작업 내역 분석.

개요

해커톤에서 3일 만에 만든 WICHI MVP는 “작동은 하지만 돈을 받기엔 부족한” 상태였습니다. 핵심 분석 기능은 돌아갔고, 가입-결제-분석-리포트 흐름도 연결되어 있었습니다. 하지만 실제로 결제를 받는 서비스로 전환하려면 그 사이를 메워야 할 항목들이 상당히 많았습니다.

이 글은 프로토타입에서 상용 서비스로 전환하면서 변경된 항목들을 카테고리별로 정리합니다. 각 항목마다 프로토타입 상태, 변경 내용, 변경 이유를 기록합니다. 구체적인 구현 코드나 비용 수치는 포함하지 않습니다. 무엇을 바꿨고, 왜 바꿨는지에 집중합니다.

변경 카테고리 전체 목록

#카테고리프로토타입 상태상용화 후 상태우선순위
1보안 강화기본 입력 제한만 존재다층 검증 + 동시 실행 제한P0
2인증 시스템기본 세션 기반JWT + 토큰 갱신P0
3다국어 지원 (i18n)영문 단일KO/EN 이중 언어P0
4샘플 시스템없음locale별 샘플 리포트P1
5결제 연동반자동webhook 자동화 + 크레딧 시스템P0
6에러 처리 및 모니터링console.log 수준구조화된 에러 + GA4P1
7DB 스키마최소 테이블 구조정규화 + 인덱싱P1
8프론트엔드 호스팅노코드 빌더Vercel 정적 호스팅P0
9DNS / SSL / 커스텀 도메인빌더 기본 도메인커스텀 도메인 + SSLP0
10SEO / OG 이미지없음Search Console + meta + OGP1
graph LR
    subgraph "프로토타입"
        A[FE: 노코드 빌더] --> B[BE: 기본 API]
        B --> C[DB: 최소 스키마]
        B --> D[LLM API]
    end

    subgraph "상용화"
        E[FE: Vercel + React] --> F[BE: FastAPI + JWT + Rate Limit]
        F --> G[DB: 정규화 + 인덱스]
        F --> H[LLM API]
        F --> I[결제 Webhook]
        F --> J[GA4 이벤트]
        E --> K[i18n KO/EN]
        E --> L[샘플 시스템]
        E --> M[SEO + OG]
    end

1. 보안 강화

프로토타입 상태

프론트엔드에서 입력 길이만 제한하고 있었습니다. 백엔드에는 별도의 검증 로직이 없었고, 요청 본문을 그대로 처리했습니다. 악의적 입력을 가정한 방어 코드는 존재하지 않았습니다. 해커톤 MVP에서는 이것으로 충분했습니다. 사용자가 심사위원 몇 명이고, 악의적 사용 동기가 없기 때문입니다.

변경 사항

보안 강화를 3개 계층으로 구분해서 적용했습니다.

계층 1: 프론트엔드 UX 검증

  • 입력 형식 검증 (URL 패턴, 이메일 형식 등)
  • 입력 길이 제한 (최소/최대)
  • 특수문자 및 허용되지 않은 문자열 입력 시 즉시 안내 메시지
  • 제출 버튼 비활성화 조건 명확화

프론트엔드 검증은 보안이 아니라 UX입니다. 사용자에게 “왜 이 입력이 안 되는지”를 설명하는 역할이고, 실제 방어는 백엔드에서 이루어집니다.

계층 2: 백엔드 allowlist 검증

  • 허용된 값만 통과시키는 allowlist 기반 검증
  • enum 타입 파라미터는 정의된 값 목록과 비교
  • 문자열 파라미터는 패턴 매칭 후 통과 여부 결정
  • 허용되지 않은 값은 요청 자체를 거부 (400 응답)

denylist(차단 목록) 대신 allowlist(허용 목록)를 선택한 이유가 있습니다. denylist는 “알려진 나쁜 입력”만 차단하므로 새로운 공격 패턴에 취약합니다. allowlist는 “알려진 좋은 입력”만 통과시키므로 기본적으로 안전합니다.

계층 3: 금칙 패턴 필터링

  • 프롬프트 인젝션 시도 탐지 및 차단
  • 시스템 프롬프트 탈취 시도 차단
  • 의도하지 않은 특수 문자열 조합 필터링
  • 금칙어 목록은 별도 설정 파일로 관리 (코드에 하드코딩하지 않음)

추가 방어: 동시 실행 제한

  • 사용자당 동시 분석 요청 수 제한
  • 제한 초과 시 대기열 안내 또는 요청 거부
  • 크레딧 차감은 분석 성공 후에만 확정 (실패 시 복구)
계층위치역할차단 시점
1프론트엔드UX 안내입력 시점
2백엔드 진입부허용값 검증요청 수신 시
3백엔드 처리부금칙 패턴 탐지처리 직전
추가백엔드동시 실행 제한실행 시점
graph TD
    A[사용자 입력] --> B{FE 형식 검증}
    B -->|통과| C[API 요청]
    B -->|실패| D[입력 오류 안내]
    C --> E{BE allowlist 검증}
    E -->|통과| F{금칙 패턴 필터}
    E -->|실패| G[400 Bad Request]
    F -->|통과| H{동시 실행 제한 확인}
    F -->|실패| I[403 Forbidden]
    H -->|여유| J[분석 실행]
    H -->|초과| K[429 Too Many Requests]
    J --> L{분석 성공?}
    L -->|성공| M[크레딧 차감 확정]
    L -->|실패| N[크레딧 복구]

변경 이유

결제가 붙는 순간 악의적 사용의 동기가 생깁니다. 무료 MVP에서는 누군가 이상한 입력을 넣어도 손해가 크지 않습니다. 유료 서비스에서는 크레딧 부정 사용, API 남용, 프롬프트 인젝션이 실제 비용으로 연결됩니다. LLM API 호출은 건당 비용이 발생하므로, 악의적 사용이 곧 운영 비용 증가입니다.

보안은 “나중에 하면 되는 것”이 아니라 “결제 기능이 붙는 순간 필수가 되는 것”이다.


2. 인증 시스템

프로토타입 상태

기본적인 세션 기반 인증을 사용하고 있었습니다. 로그인 후 세션이 유지되는 방식이었고, 토큰 만료나 갱신에 대한 고려가 없었습니다. CORS 설정도 개발 편의를 위해 전체 허용(*) 상태였습니다.

변경 사항

JWT 기반 인증으로 전환

  • 액세스 토큰과 리프레시 토큰 분리
  • 액세스 토큰에 짧은 만료 시간 설정
  • 리프레시 토큰으로 자동 갱신
  • 토큰 페이로드에 사용자 역할 및 권한 정보 포함

CORS 설정 강화

  • 전체 허용(*) 제거
  • 허용 도메인을 명시적으로 지정 (프로덕션 도메인, 프리뷰 도메인)
  • 허용 메서드와 헤더도 명시적으로 제한
  • Credentials 옵션 활성화

Rate Limiting 적용

  • IP 기반 + 사용자 기반 이중 제한
  • 엔드포인트별 차등 제한 (인증 관련 엔드포인트는 더 엄격)
  • 제한 초과 시 429 응답과 함께 재시도 가능 시점 안내
항목프로토타입상용화
인증 방식세션 기반JWT (액세스 + 리프레시)
토큰 만료고려 안 함단기 만료 + 자동 갱신
CORS* (전체 허용)도메인 명시 지정
Rate Limit없음IP + 사용자 이중 제한
권한 관리없음토큰 페이로드에 역할 포함

변경 이유

세션 기반 인증은 단일 서버 환경에서는 단순하지만, 프론트엔드와 백엔드가 다른 도메인에서 운영되는 SaaS 구조에서는 한계가 있습니다. JWT는 stateless하므로 서버 확장에도 유리하고, 프론트엔드에서 토큰을 직접 관리할 수 있어 SPA 구조에 적합합니다. CORS와 Rate Limiting은 API를 외부에 노출하는 서비스에서 기본 방어선입니다.


3. 다국어 지원 (i18n)

프로토타입 상태

영문 단일 언어였습니다. 모든 UI 텍스트가 컴포넌트 코드에 직접 작성되어 있었습니다. 버튼 텍스트, 안내 메시지, 에러 메시지가 영어 문자열로 하드코딩된 상태였습니다.

변경 사항

프론트엔드 변경

  • UI 텍스트를 코드에서 분리하여 locale별 JSON 파일로 관리
  • 언어 전환 UI 추가 (헤더에 KO/EN 토글)
  • 브라우저 locale 감지 후 자동 언어 설정
  • 수동 전환 시 선택 언어를 로컬 스토리지에 저장
  • 날짜, 숫자 포맷도 locale에 맞게 변환
  • 링크, 라우팅 경로에 locale prefix 적용

백엔드 변경

  • 분석 리포트 생성 시 요청 locale에 따라 프롬프트 분기
  • LLM에 전달하는 시스템 프롬프트에 응답 언어 지시 포함
  • API 응답의 에러 메시지도 locale별 분기
  • 이메일 알림 템플릿 locale 분리

번역 관리

  • 번역 키 네이밍 규칙 수립 (예: page.analysis.button.start)
  • 누락 키 발생 시 fallback 언어(영어) 표시
  • 번역 파일 업데이트 체크리스트 운영
변경 위치프로토타입상용화
UI 텍스트코드에 하드코딩locale별 JSON 분리
리포트 본문영문 고정요청 locale에 따라 분기
에러 메시지영문 고정locale별 분기
날짜/숫자미국식 고정locale 기반 포맷
라우팅단일locale prefix
감지없음브라우저 locale 자동 감지

변경 이유

WICHI는 글로벌 SaaS입니다. 한국 시장 진입과 글로벌 확장을 동시에 하려면 i18n은 선택이 아니라 필수입니다.

i18n을 나중에 넣으면 전체 UI 컴포넌트를 다시 뜯어야 합니다. 하드코딩된 문자열을 하나씩 찾아서 번역 키로 교체하는 작업은, 컴포넌트 수가 늘어난 뒤에 할수록 비용이 기하급수적으로 증가합니다. 상용화 시점에 구조를 잡는 것이 가장 효율적이었습니다.

i18n은 “번역”이 아니라 “구조”의 문제다. 텍스트를 코드에서 분리하는 것이 핵심이고, 실제 번역은 그 이후의 작업이다.

특히 백엔드 i18n은 프론트엔드보다 고려 사항이 많습니다. LLM 기반 서비스에서는 분석 결과물 자체의 언어가 바뀌어야 하므로, 프롬프트 레벨에서의 언어 지시가 필요합니다. 단순히 UI를 번역하는 것과는 다른 차원의 작업입니다.


4. 샘플 시스템

프로토타입 상태

샘플 데이터가 없었습니다. 가입 후 크레딧을 소비해야만 분석 결과를 볼 수 있었습니다. “이 서비스가 어떤 결과를 주는지”를 확인하려면 결제가 선행되어야 하는 구조였습니다.

변경 사항

샘플 리포트 시스템 구축

  • 비로그인 상태에서 접근 가능한 샘플 리포트 제공
  • 영문 샘플: 글로벌 DTC 브랜드 기반
  • 한국어 샘플: 국내 대기업 브랜드 기반
  • 샘플 리포트는 실제 분석 결과와 동일한 UI로 렌더링

전환 유도 설계

  • 샘플 리포트 끝에 가입 유도 게이트 배치
  • 전체 리포트의 일정 비율까지만 무료 열람 가능
  • 가입 게이트 문구도 locale에 따라 분기
  • 샘플에서 가입으로의 전환 이벤트 GA4 추적

샘플 선정 기준

  • 해당 locale의 사용자가 인지도가 높은 브랜드 선택
  • 분석 결과가 풍부하게 나오는 브랜드 선택 (빈약한 결과는 역효과)
  • 주기적으로 샘플 데이터 갱신 (오래된 데이터는 신뢰도 저하)
항목프로토타입상용화
결과물 미리보기불가샘플 리포트로 가능
비로그인 접근불가샘플 한정 가능
locale별 콘텐츠해당 없음각 locale에 맞는 브랜드
전환 유도없음가입 게이트 + GA4 이벤트

변경 이유

“돈을 내기 전에 결과물을 확인하고 싶다”는 자연스러운 심리입니다. 특히 SaaS에서 “분석 리포트”라는 결과물은 텍스트 기반이므로, 결과물의 품질 편차가 클 수 있다는 인식이 있습니다. 샘플 없이 결제부터 요구하면 전환율이 나올 수 없습니다.

locale별 브랜드를 샘플로 제공한 이유도 명확합니다. 한국 사용자에게 미국 브랜드 샘플을 보여주면 “이게 우리 시장에도 적용되나?”라는 의문이 남습니다. 각 시장의 사용자가 직접 아는 브랜드의 분석 결과를 보여줘야 결과물의 가치를 체감할 수 있습니다.

샘플은 “데모”가 아니라 “증거”다. 사용자에게 “이 서비스가 돈을 낼 만한 결과를 제공한다”는 것을 입증하는 장치다.


5. 결제 연동

프로토타입 상태

결제 페이지는 존재했지만 webhook 처리가 불완전했습니다. 결제 성공 이벤트를 수신하는 코드는 있었으나, 에지 케이스(중복 이벤트, 실패 후 재시도, 환불 등)에 대한 처리가 없었습니다. 크레딧 부여는 수동으로 이루어졌습니다.

변경 사항

Webhook 자동화

  • 결제 플랫폼에서 발송하는 webhook 이벤트 수신 및 서명 검증
  • 이벤트 타입별 핸들러 분기 (결제 완료, 환불, 구독 변경 등)
  • 멱등성(idempotency) 보장 — 동일 이벤트를 중복 수신해도 한 번만 처리
  • webhook 수신 실패 시 재시도 대응 (재시도 시에도 중복 처리 방지)
  • 수신된 모든 webhook 이벤트 로깅 (디버깅 및 감사 추적용)

크레딧 시스템 설계

  • 결제 완료 → 크레딧 충전 → 분석 실행 시 차감의 3단계 흐름
  • 크레딧 잔액 조회 API
  • 크레딧 차감은 분석 시작 시 예약, 완료 시 확정
  • 분석 실패 시 예약된 크레딧 자동 복구
  • 크레딧 사용 이력 조회 (충전, 차감, 복구 내역)

주문 상태 관리

  • 주문 상태 머신: 결제 대기 → 결제 완료 → 크레딧 부여 → (환불 시) 크레딧 회수
  • 각 상태 전이에 타임스탬프 기록
  • 비정상 상태 전이 발생 시 알림
stateDiagram-v2
    [*] --> 결제대기: 결제 페이지 진입
    결제대기 --> 결제완료: webhook 수신 (결제 성공)
    결제대기 --> 결제실패: webhook 수신 (결제 실패)
    결제완료 --> 크레딧부여: 크레딧 충전 처리
    크레딧부여 --> [*]
    결제완료 --> 환불요청: 사용자 환불 요청
    환불요청 --> 크레딧회수: 잔여 크레딧 확인 후 회수
    크레딧회수 --> 환불완료: 환불 처리 완료
    환불완료 --> [*]
    결제실패 --> [*]

방어 로직

  • 결제 금액과 상품 정보의 서버사이드 재검증 (클라이언트 전달값 신뢰하지 않음)
  • 동일 사용자의 짧은 간격 중복 결제 탐지 및 차단
  • 크레딧 잔액이 마이너스가 되는 것을 방지하는 제약 조건

변경 이유

MVP에서는 “결제가 되긴 한다” 수준이면 충분했습니다. 상용 서비스에서는 다릅니다. 결제 후 크레딧이 즉시 반영되지 않으면 사용자는 “돈만 빠져나가고 서비스를 못 쓰는” 상황을 경험합니다. 중복 차감이 발생하면 신뢰를 잃습니다.

Webhook 기반 자동화와 트랜잭션 안전성은 유료 서비스의 기본 요건입니다. 특히 크레딧 차감의 예약-확정 패턴은, 분석이 LLM API 호출에 의존하므로 실패 가능성이 항상 존재한다는 점에서 필수적이었습니다.

결제 시스템에서 “대부분의 경우 잘 작동한다”는 충분하지 않다. 엣지 케이스 하나가 사용자의 돈과 직결된다.


6. 에러 처리 및 모니터링

프로토타입 상태

에러 처리는 console.log 수준이었습니다. 에러가 발생하면 브라우저 콘솔에 찍히거나, 백엔드 로그에 스택 트레이스가 남는 정도였습니다. 사용자에게 보여주는 에러 메시지는 “Something went wrong” 수준의 범용 메시지였습니다. 사용자 행동 데이터 수집은 하지 않았습니다.

변경 사항

프론트엔드 에러 처리

  • 에러 바운더리(Error Boundary) 적용 — 컴포넌트 에러가 전체 앱을 크래시시키지 않도록 격리
  • API 요청 실패 시 사용자 친화적 메시지 표시 (locale별 분기)
  • 네트워크 에러와 서버 에러 구분 안내
  • 재시도 가능한 에러에는 재시도 버튼 제공

백엔드 에러 처리

  • 구조화된 에러 응답 포맷 통일 (code, message, detail 필드)
  • HTTP 상태 코드 정확한 사용 (이전에는 대부분 500을 반환)
  • 에러 레벨 분류: 사용자 입력 오류(4xx) vs 서버 오류(5xx) 명확 구분
  • LLM API 호출 실패 시 재시도 로직 (지수 백오프)
  • 재시도 횟수 소진 시 분석 실패 처리 + 크레딧 복구

모니터링 — GA4 연동

  • 주요 전환 이벤트 정의 및 추적:
이벤트시점목적
sign_up회원가입 완료가입 전환율 측정
login로그인재방문 패턴 파악
view_sample샘플 리포트 조회샘플의 효과 측정
begin_checkout결제 페이지 진입결제 퍼널 시작점
purchase결제 완료결제 전환율 측정
begin_analysis분석 시작크레딧 사용 패턴
analysis_complete분석 완료분석 성공률
analysis_failed분석 실패실패율 모니터링

로깅 구조화

  • 요청 ID 기반 추적 — 하나의 요청이 여러 서비스를 거쳐도 추적 가능
  • 로그 레벨 체계화 (DEBUG, INFO, WARNING, ERROR, CRITICAL)
  • 민감 정보(토큰, 비밀번호, API 키)는 로그에서 마스킹

변경 이유

데이터 없이는 개선할 수 없습니다. 어디서 이탈하는지, 결제 전환율이 얼마인지, 분석을 시작한 사용자 중 완료까지 가는 비율이 얼마인지를 알아야 다음 우선순위를 정할 수 있습니다.

에러 처리도 마찬가지입니다. “Something went wrong”이라는 메시지는 사용자에게도, 개발자에게도 쓸모가 없습니다. 사용자에게는 “무엇이 잘못되었고, 어떻게 해야 하는지”를 알려줘야 하고, 개발자에게는 “어디서, 왜 실패했는지”가 구조화된 형태로 기록되어야 합니다.


7. DB 스키마 진화

프로토타입 상태

최소한의 테이블만 존재했습니다. 사용자 테이블, 분석 결과 테이블 정도였고, 정규화가 이루어지지 않은 상태였습니다. 인덱스는 기본 키 외에 없었고, 쿼리 성능에 대한 고려가 없었습니다.

변경 사항

테이블 추가 및 정규화

  • 크레딧 트랜잭션 테이블 신규 (충전, 차감, 복구 이력)
  • 주문 테이블 신규 (결제 상태 머신)
  • 샘플 리포트 테이블 신규
  • 사용자 설정 테이블 분리 (locale, 알림 설정 등)
  • 분석 결과 테이블 구조 변경 (locale 컬럼 추가, 메타데이터 정규화)

인덱싱

  • 빈번한 조회 패턴에 맞는 인덱스 추가
  • 크레딧 잔액 조회 최적화 (사용자 ID + 생성일 복합 인덱스)
  • 분석 결과 목록 조회 최적화 (사용자 ID + 상태 + 생성일)
  • 불필요한 전체 테이블 스캔 제거

데이터 무결성

  • 외래 키 제약 조건 추가
  • 크레딧 잔액 마이너스 방지 CHECK 제약
  • 상태 전이에 대한 트리거 또는 애플리케이션 레벨 검증
  • 소프트 삭제(soft delete) 패턴 적용 (실수로 데이터 유실 방지)
항목프로토타입상용화
테이블 수2–3개7개 이상
인덱스PK만조회 패턴 기반 복합 인덱스
외래 키없음모든 관계에 적용
데이터 무결성애플리케이션 레벨만DB 레벨 제약 + 앱 레벨 검증
삭제 방식하드 삭제소프트 삭제

변경 이유

프로토타입에서는 “데이터가 저장되면 된다”가 목표였습니다. 상용 서비스에서는 “데이터가 정확하고, 빠르게 조회되고, 실수로 유실되지 않아야 한다”가 목표입니다.

특히 크레딧 시스템이 추가되면서 트랜잭션 정합성이 중요해졌습니다. 크레딧 차감과 분석 실행이 원자적으로 처리되지 않으면, 크레딧은 차감되었는데 분석은 실패하는 상황이 발생합니다. DB 레벨의 제약 조건은 이런 상황을 방어하는 마지막 안전장치입니다.


8. 프론트엔드 호스팅

프로토타입 상태

노코드 빌더 기반 호스팅을 사용했습니다. 빠르게 UI를 만들 수 있어서 해커톤 기간에 유용했지만, 상용화 단계에서는 커스터마이징과 비용 통제에 한계가 있었습니다.

변경 사항

  • 노코드 빌더에서 코드 기반 프론트엔드(React + Vite)로 완전 이전
  • Vercel 기반 정적 호스팅으로 전환
  • Git push 기반 자동 배포 파이프라인 구축
  • PR마다 Preview Deploy 자동 생성
  • 이전 배포 버전으로 즉시 롤백 가능
  • 환경변수를 Production / Preview / Development로 분리 관리
  • 빌더 전용 의존성 (빌더 플러그인, 태거 등) 완전 제거
항목노코드 빌더Vercel
배포 트리거에디터에서 수동git push → 자동
PR 프리뷰불가PR당 Preview Deploy
롤백수동 복원즉시 롤백
환경변수제한적환경별 분리
코드 제어부분적완전
월 비용유료 구독무료 티어

변경 이유

노코드 빌더의 월 구독 비용 대비 Vercel 무료 티어가 경제적이었고, 프론트엔드 코드를 직접 제어해야 i18n, SEO, 커스텀 컴포넌트 등 상용화에 필요한 기능을 자유롭게 구현할 수 있었습니다. 프로토타이핑 도구와 프로덕션 호스팅은 역할이 다릅니다.


9. DNS / SSL / 커스텀 도메인

프로토타입 상태

노코드 빌더가 제공하는 기본 서브도메인을 사용했습니다. SSL은 빌더가 자동 제공. 커스텀 도메인은 연결되어 있지 않았습니다.

변경 사항

커스텀 도메인 설정

  • 프로덕션 도메인 구매 및 등록
  • Cloudflare DNS에서 네임서버 레코드 설정
  • 프론트엔드(Vercel)와 백엔드(Railway) 각각에 커스텀 도메인 연결
  • www와 루트 도메인 리다이렉트 설정

SSL 인증서

  • Vercel 측: 자동 SSL 프로비저닝 (Let’s Encrypt)
  • 백엔드 측: Railway 자동 SSL
  • 인증서 갱신은 양쪽 모두 자동 관리

DNS 레코드 정리

  • A 레코드, CNAME 레코드 정확한 설정
  • 불필요한 레거시 레코드 제거
  • DNS 전파 확인 후 이전 도메인에서 리다이렉트 설정
항목프로토타입상용화
도메인빌더 서브도메인커스텀 도메인
SSL빌더 자동 제공양쪽 자동 프로비저닝
DNS 관리해당 없음Cloudflare
www 리다이렉트해당 없음설정 완료

변경 이유

something.builder-name.app 같은 서브도메인은 프로토타입에서는 문제없지만, 유료 서비스에서는 신뢰도에 영향을 줍니다. 커스텀 도메인은 브랜드 정체성의 기본이고, SSL은 사용자 데이터 보호의 기본입니다. 결제 정보를 다루는 서비스에서 이 둘은 협상의 대상이 아닙니다.


10. SEO / OG 이미지

프로토타입 상태

SEO 대응이 전무했습니다. robots.txt도 없고, sitemap.xml도 없었습니다. 메타 태그는 기본값 그대로였고, Open Graph 이미지는 설정되지 않았습니다. 검색엔진에 등록된 적이 없었습니다.

변경 사항

검색엔진 등록

  • Google Search Console에 도메인 속성 등록
  • DNS TXT 레코드로 소유권 인증
  • sitemap.xml 자동 생성 및 제출
  • robots.txt 설정 (크롤링 허용 경로 명시)

메타 태그 정비

  • 페이지별 title, description 최적화
  • 구조화된 데이터(JSON-LD) 추가 (SoftwareApplication 스키마)
  • canonical URL 설정 (중복 콘텐츠 방지)
  • locale별 hreflang 태그 (다국어 검색 노출)

Open Graph 이미지

  • 서비스 대표 OG 이미지 제작
  • 주요 페이지별 OG 이미지 분기
  • OG 이미지를 프로젝트 내부에 저장 (외부 CDN 의존 제거)
  • Twitter Card 메타 태그도 함께 설정

기술 SEO

  • 페이지 로딩 속도 최적화 (정적 호스팅 전환의 부수 효과)
  • 모바일 반응형 확인
  • Core Web Vitals 기본 수준 확보
graph TD
    A[검색엔진 크롤러] --> B[robots.txt 확인]
    B --> C[sitemap.xml 파싱]
    C --> D[페이지 크롤링]
    D --> E[메타 태그 수집]
    D --> F[구조화 데이터 수집]
    D --> G[hreflang 확인]
    E --> H[검색 결과 표시]
    F --> H
    G --> I[locale별 검색 결과 분기]

    J[소셜 공유] --> K[OG 메타 태그 파싱]
    K --> L[OG 이미지 + 제목 + 설명 표시]

변경 이유

유료 SaaS에 사용자가 유입되려면 검색에서 발견되어야 합니다. 광고 없이 시작하는 초기 SaaS에서 오가닉 검색은 사실상 유일한 무료 유입 채널입니다. SEO가 없으면 아무리 좋은 서비스를 만들어도 존재 자체를 모릅니다.

OG 이미지는 소셜 공유 시 첫인상을 결정합니다. 링크를 공유했을 때 제목과 설명, 이미지가 깔끔하게 나오는 것과 아무것도 나오지 않는 것은 클릭률에 차이를 만듭니다.


우선순위 판단 기준

10개 카테고리를 동시에 진행할 수 없으므로, 우선순위를 정해야 했습니다. 다음 기준으로 P0/P1을 구분했습니다.

P0 (상용화 전 필수 — 결제를 받기 위한 최소 요건)

  • 보안 강화: 결제가 있으면 보안은 선택이 아님
  • 인증 시스템: JWT 없이 API를 보호할 수 없음
  • 결제 연동: 수동 크레딧 부여로는 서비스 운영 불가
  • 프론트엔드 호스팅: 코드 제어권 없이 i18n/SEO 구현 불가
  • DNS/SSL/도메인: 커스텀 도메인 없이 결제 서비스 운영 불가
  • i18n: 한국 시장 진입에 필수, 나중에 넣으면 비용 급증

P1 (상용화 직후 필수 — 성장을 위한 요건)

  • 샘플 시스템: 전환율에 직접 영향
  • 에러 처리/모니터링: 데이터 없이 개선 불가
  • DB 스키마 정비: 데이터 정합성과 성능
  • SEO/OG 이미지: 유입 채널 확보
graph TD
    subgraph "P0: 결제 전 필수"
        A[보안 강화] --> B[인증 시스템]
        B --> C[결제 연동]
        D[FE 호스팅 전환] --> E[DNS/SSL/도메인]
        D --> F[i18n]
    end

    subgraph "P1: 성장 필수"
        G[샘플 시스템]
        H[에러 처리/모니터링]
        I[DB 스키마 정비]
        J[SEO/OG 이미지]
    end

    C --> G
    F --> G
    E --> J
    B --> H
    C --> I
우선순위기준해당 카테고리
P0이것 없이 결제를 받을 수 없다보안, 인증, 결제, 호스팅, DNS/SSL, i18n
P1이것 없이 성장할 수 없다샘플, 모니터링, DB 정비, SEO
P2있으면 좋지만 없어도 런칭 가능(이 글에서 다루지 않는 항목들)

전체 요약

#영역프로토타입상용화우선순위
1보안기본 입력 제한3계층 검증 + 동시 실행 제한 + 트랜잭션 안전P0
2인증세션 기반JWT + CORS + Rate LimitP0
3i18n영문 단일KO/EN 이중 언어 (FE + BE)P0
4샘플없음locale별 샘플 리포트 + 가입 게이트P1
5결제반자동webhook 자동화 + 크레딧 시스템P0
6에러/모니터링console.log구조화 에러 + GA4 8개 이벤트P1
7DB최소 테이블정규화 + 인덱싱 + 무결성 제약P1
8호스팅노코드 빌더Vercel 정적 호스팅P0
9DNS/SSL빌더 서브도메인커스텀 도메인 + 자동 SSLP0
10SEO없음Search Console + sitemap + OG 이미지P1

회고

이 10개 항목은 MVP에서 “빠졌던 것”이 아닙니다. 프로토타입 단계에서는 필요하지 않았던 것들입니다.

프로토타입의 목적은 핵심 가치를 검증하는 것입니다. “이 분석이 유용한가?”라는 질문에 대한 답을 얻는 것이 전부였고, 그 목적에 비추면 MVP는 충분했습니다.

상용화의 목적은 그 가치를 안정적으로, 반복적으로, 신뢰할 수 있게 전달하는 것입니다. 위 10개 항목은 모두 후자에 해당합니다.

프로토타입은 “이게 가능한가?”에 답하고, 상용 서비스는 “이걸 믿을 수 있는가?”에 답한다.

전환 과정에서 관찰한 패턴을 정리합니다.

  • 결제가 붙는 순간 모든 것의 기준이 바뀐다. 보안, 에러 처리, 데이터 무결성 — 무료일 때는 “있으면 좋은 것”이었던 항목들이 유료일 때는 “없으면 안 되는 것”이 됩니다.
  • i18n은 구조의 문제다. 번역이 아니라 텍스트 분리입니다. 컴포넌트 수가 적을 때 잡아야 합니다.
  • 샘플은 마케팅이 아니라 신뢰 장치다. “이 서비스가 무슨 결과를 주는지” 보여주는 것은 판매 전략이 아니라 사용자에 대한 기본 예의입니다.
  • 모니터링 없이 개선은 감에 의존한다. 데이터가 있어야 우선순위를 정할 수 있습니다.
  • DB 제약 조건은 마지막 방어선이다. 애플리케이션 코드에 버그가 있어도, DB 레벨 제약이 데이터 무결성을 지켜줍니다.

이 글에서 다루지 않은 항목들(블로그, 이메일 마케팅, A/B 테스트, 고급 분석 기능 등)은 이후 별도로 정리할 예정입니다.


공유

관련 글