프로젝트 개요
- 진행 기간
- 2026-09-30 ~ 2026-10-24
- 인원
- 1명 (풀스택 1명)
- 나의 역할
- 1. 서비스 기획 및 도메인 설계 2. 자체 SVG 기반 아키텍처 · ERD 양방향 다이어그램 엔진 구현 3. Tiptap 하이브리드 블록 에디터 개발 4. Spring Boot 3 기반 DDD 백엔드 및 인증/미디어 파이프라인 구축 5. Cloudflare Edge + Oracle Cloud(Docker) 배포 자동화
기술 스택
프론트엔드
- TypeScript
- Next.js
- React
백엔드
- Java
- Spring Boot
- JPA
DB
- PostgreSQL
- Redis
DevOps
- Docker Compose
- GitHub Actions
인프라
- Cloudflare
- Oracle Cloud
배경 및 목적
1) 왜 개발 블로그와 포트폴리오의 설계도는 늘 ‘멈춰 있는 이미지’여야 할까요?
개발자가 프로젝트를 정리하거나 기술 아티클을 작성할 때 가장 많은 시간을 쏟는 핵심 산출물은 시스템 아키텍처 구성도와 데이터베이스 ERD·테이블 명세서입니다. 그러나 기존의 블로그 및 포트폴리오 플랫폼에서는 Draw.io, ERDCloud, Figma 등 외부 도구를 열어 다이어그램을 그린 뒤 PNG 스크린샷으로 캡처해 본문에 붙여넣는 방식을 반복해야 했습니다.
잦은 수정과 버전 불일치의 피로감: 컬럼 하나가 바뀌거나 인프라 노드가 하나만 추가되어도 외부 툴을 다시 열어 수정하고 이미지를 재캡처·재업로드해야 합니다.
독자의 정보 탐색 단절: 글을 읽는 독자는 해상도가 깨진 정적 이미지 속에서 작은 글씨를 확대해 보아야 하며, 특정 테이블과 연결된 외래키(FK) 참조 관계나 컬럼의 상세 제약 조건(
PK,UK,NOT NULL, 기본값, 설명)을 클릭해서 탐색할 수 없습니다.시각적 ERD와 테이블 정의서의 괴리: 다이어그램과 명세서 표를 별개로 작성하다 보니 두 문서 간 정합성이 쉽게 무너집니다.
2) Pebble의 해결책: 다이어그램 자체가 살아있는 문서 블록이 되다
Pebble은 이러한 기록과 열람의 단절을 해결하기 위해 탄생했습니다. 외부 도구 없이 에디터 내부에서 바로 클라우드·컨테이너 아키텍처 캔버스와 데이터베이스 ERD·명세서 양방향 엔진을 네이티브 구조화 블록(ARCHITECTURE, DB_SCHEMA)으로 조합할 수 있도록 설계했습니다. 작성자는 코드와 설계를 한 흐름으로 기록하고, 독자는 발행된 화면에서 직접 다이어그램을 확대(Fullscreen)·하이라이트·탭 전환하며 입체적으로 프로젝트를 탐색할 수 있습니다.
핵심 기능
1) 양방향 동기화 데이터베이스 ERD & 테이블 명세서 엔진 (DB_SCHEMA)
ERD 캔버스 ↔ 명세서 탭 실시간 동기화: 하나의 스키마 데이터 모델을 기반으로 시각적 ERD 뷰와 표준 테이블 명세서(정의서) 뷰를 탭으로 즉시 전환하며, 어느 쪽에서 수정하든 양쪽 상태가 실시간으로 동기화됩니다.
스마트 포트 드래그 연결 & 자동 FK 생성: PK 컬럼의 좌·우 연결 포트에서 다른 테이블이나 컬럼으로 선을 드래그하면 참조 대상의 데이터 타입에 맞춰 외래키(FK) 컬럼과 참조 제약이 자동으로 생성·연결됩니다.
직교(Orthogonal) 다중 채널 자동 라우팅 & 꺾임(Bend) 핸들 조작: 테이블 카드를 침범하지 않는 최단 직각 경로를 실시간 계산하며, 동일 복도를 지나는 다중 외래키 선들을
14px간격의 다중 채널로 자동 분산하고 수동 꺾임 핸들 조작까지 지원합니다.IE(새발 표기법) & Chen 표기법 전환 및 마스터-디테일 탐색:
1:1,1:N, 필수(|)·선택(o) 카디널리티 표기법을 즉시 전환할 수 있으며, 발행 화면의 ERD 탭에서 특정 테이블 헤더를 클릭하면 해당 테이블의 상세 컬럼 명세서만 하단에 깔끔하게 펼쳐집니다.
2) 클라우드·컨테이너 계층형 시스템 아키텍처 빌더 (ARCHITECTURE)
인프라 그룹(Group) 바운딩 & 기술 스택 카탈로그: Cloudflare, AWS, Oracle Cloud, Docker 컨테이너 등 논리적·물리적 인프라 경계를 그룹 박스로 선언하고, 내부에 애플리케이션·DB·캐시·스토리지 노드를 배치해 계층 구조를 직관적으로 시각화합니다.
4방향 포트 & 경유점(Waypoint) 라우팅: 노드 간 단방향·양방향 트래픽 화살표와 프로토콜 라벨(예:
HTTPS / SSR,Lua 원자적 RTR,HMAC 서명 조회)을 부여하고 경유점을 자유롭게 조정할 수 있습니다.
3) Tiptap 기반 하이브리드 블록 에디터 & IndexedDB 로컬 초안 보호
구조화 블록 파이프라인: 리치 텍스트(
HTML)와 코드(CODE), 표 명세(TABLE), 아키텍처(ARCHITECTURE), 데이터베이스(DB_SCHEMA) 블록을 단일 문서 트리로 결합하고, 발행 및 미리보기 화면에서 동일한 렌더러(PostBody)로 픽셀 단위까지 일관되게 표현합니다.로컬 초안 자동 보호: 네트워크 순단이나 브라우저 종료 시에도 작성 중인 프로젝트 문서와 다이어그램 상태를 브라우저
IndexedDB에 보존하여 작업 유실을 방지합니다.
4)비공개 이미지 CDN 파이프라인 & 토큰 동시성 제어
HMAC 서명 기반 이미지 서빙 (Cloudflare R2 & Worker): 업로드된 이미지를 WebP로 변환해 비공개 R2 버킷에 저장하고, 백엔드가 발급한 15분 만료 HMAC 서명을 Cloudflare Worker가 검증하여 허가된 요청에만 이미지를 캐싱·반환합니다.
Redis Lua 스크립트를 통한 Refresh Token 동시성 제어: 브라우저 다중 탭 환경에서 여러 API 요청이 동시에 토큰 갱신을 시도할 때 발생하는 레이스 컨디션을 방지하기 위해, 토큰 검증과 상태 갱신을 단일 Lua 스크립트로 원자적(Atomic)으로 처리합니다.
아키텍처
구성 요소 및 연결 목록 (10)
경계 그룹
- 클라이언트 및 인증 · CLIENT & AUTH
- 그룹 · CLOUDFLARE EDGE
- Backend & Data Infrastructure · ORACLE CLOUD
구성 요소
- 클라이언트 브라우저
- 클라이언트
- OAuth2.0 · 클라이언트 및 인증 · CLIENT & AUTH
- Web Worker · 그룹 · CLOUDFLARE EDGE
- PostgreSQL 16 · Backend & Data Infrastructure · ORACLE CLOUD
- Redis · Backend & Data Infrastructure · ORACLE CLOUD
- Pebble API Server · Backend & Data Infrastructure · ORACLE CLOUD
- Cloudflare R2 · 그룹 · CLOUDFLARE EDGE
- Signed Media Worker · 그룹 · CLOUDFLARE EDGE
- Caddy Gateway · Backend & Data Infrastructure · ORACLE CLOUD
연결
- 클라이언트 브라우저 → Web Worker · HTTPS / SSR
- Pebble API Server → Cloudflare R2 · S3 WebP 업로드
- Pebble API Server → OAuth2.0 · 14세 이상 인증
- 클라이언트 → 클라이언트 브라우저
- 클라이언트 브라우저 → Signed Media Worker · HMAC 서명 이미지 요청
- Signed Media Worker → Cloudflare R2 · 비공개 R2 버킷 바인딩
- Caddy Gateway → Pebble API Server · X-Real-IP 전달
- Web Worker → Caddy Gateway
- Pebble API Server → PostgreSQL 16 · JPA / JSONB
- Pebble API Server → Redis · Lua 원자적 RTR
아키텍처 설계 핵심 의도
Edge SSR & CSR 하이브리드 프론트엔드 (
Cloudflare Edge + Next.js 16)공개 게시글·프로젝트 상세·개인 블로그 화면은 OpenNext 어댑터를 통해 Cloudflare Workers 엣지에서 SSR로 렌더링하여 초기 응답 속도와 SEO를 확보하고, 고도의 포인터·키보드 상호작용이 필요한 아키텍처·ERD 편집기는 클라이언트 상태 머신으로 분리했습니다.
DDD 4계층 백엔드 & 엄격한 책임 경계 (
Oracle Cloud ARM64 + Spring Boot 3 / Java 21)auth,member,post,project,board,comment,media,admin도메인별로Presentation→Application→Domain→Infrastructure4계층을 엄격히 분리했습니다.앞단의 Caddy Reverse Proxy가 TLS 종료와 공식 Cloudflare IPv4 CIDR 검증을 수행하고 신뢰된 단일
X-Real-IP만 내부 Docker 네트워크의 API 컨테이너로 전달합니다.
비공개 스토리지 + 서명된 엣지 미디어 CDN (
Cloudflare R2 + Signed Media Worker)업로드된 이미지는 서버에서 안전성을 검증하고 WebP(원본·썸네일)로 변환한 뒤 비공개 R2 버킷에 저장합니다.
API가 콘텐츠 공개 여부를 판정한 뒤 15분 만료 HMAC-SHA256 서명 URL을 발급하면, 전용
Signed Media Worker(images.pebble-log.com)가 엣지에서 서명을 검증하고 캐시(HIT/MISS)를 서빙하여 비공개 글의 이미지 무단 노출을 원천 차단합니다.
메모리 Access Token + Redis Lua 원자적 Refresh Rotation (
Redis 7)XSS 탈취 방어를 위해 Access Token은 브라우저 메모리에만 보관하고, Refresh Token은
HttpOnly · Secure · SameSite=Lax쿠키로 전달합니다.토큰 갱신 시 Redis Lua 스크립트를 통해 검증·교체·재사용 감지(Family 폐기)를 단일 원자 연산으로 처리합니다.
데이터베이스 설계
관계 목록 (11) — 클릭하여 관계선 종류·기호 편집
핵심 성과 및 트러블슈팅
1) 다중 외래키(FK) 직교 라우팅 선 겹침 및 꺾임 핸들(Bend Handle) 간섭 해결
문제 상황: 하나의 부모 테이블(
member.id)을 여러 자식 테이블(board,project,post등)이 동시에 참조할 때, 직교(Orthogonal) 연결선들이 같은 경로로 포개져 구분이 불가능해지거나 카드를 가로지르는 문제가 발생했습니다. 또한 평행하게 달리는 인접 선들의 투명 클릭 영역(stroke-width: 14px)이 서로의 꺾임 핸들을 덮어버려 마우스를 가져가도 핸들이 깜빡이며 사라지는 현상이 있었습니다.해결 및 성과:
동적 포트 슬롯 정렬(Dynamic Slot Sorting): 연결된 상대 테이블의 Y좌표 순으로 출발·도착 포트의 슬롯 인덱스를 동적으로 재할당하여 출발점부터 선 교차(Crossing)가 발생하지 않도록 개선했습니다.
복도(Corridor) 다중 채널 탐색 알고리즘: 테이블 사이 빈 복도의 중앙선에서 바깥쪽으로
±14px씩 확장되는 후보 채널을 탐색하여, 이미 점유된 선분과 겹치지 않는 독립 채널을 자동 배정했습니다.SVG 최상위 핸들 레이어 분리 & 호버 디바운스: SVG 페인터 모델에서 모든 관계선의 경로를 먼저 렌더링한 뒤, 꺾임 핸들만 별도의 최상위 그룹(
bendHandlesLayer)에 모아서 렌더링하고350ms호버 유예 타이머를 적용해 인접 선 간섭을 100% 제거했습니다.
2) 한국어 IME 조합(Composition) 중 ERD 인라인 입력 유실 및 커서 튐 해결
문제 상황: ERD 테이블 카드 위에서 한글로 테이블명이나 컬럼 논리명을 빠르게 타이핑할 때, 자모 조합 도중 상위 스키마 상태 리렌더링이 개입하면서 한 글자에서 입력이 막히거나 초성·중성이 분리되는 버그가 발생했습니다.
해결 및 성과:
브라우저의
compositionstart~compositionend주기를 추적하는 전용 제어 입력 래퍼(TableTextInput)를 설계했습니다.한글 조합 중(
isComposing = true)에는 로컬 드래프트 버퍼만 갱신하여 부모 캔버스의 불필요한 리렌더링을 차단하고, 조합 완료 및 포커스 해제(blur) 시점에 정규화된 값을 상위 스키마에 원자적으로 반영해 한글·영문 모두 끊김 없는 인라인 편집 UX를 완성했습니다.
3) 다중 탭·동시 API 요청 환경에서의 Refresh Token Rotation 경합 방어
문제 상황: Access Token이 만료된 시점에 페이지 내 여러 컴포넌트(또는 다중 브라우저 탭)가 동시에 인증 API를 호출하면, 중복 Refresh 요청이 발생해 먼저 도착한 요청이 토큰을 교체한 직후 두 번째 요청이 ‘이미 사용된 토큰(재사용 공격)’으로 오인되어 정상 사용자의 전체 세션이 강제 만료되는 문제가 있었습니다.
해결 및 성과:
프론트엔드: 단일 탭 내에서는 In-flight Refresh Promise를 공유(Deduplication)하고, 탭 간에는 브라우저
Web Locks API와BroadcastChannel을 결합해 오직 하나의 탭만 토큰 갱신을 수행한 뒤 새 상태를 전파하도록 구현했습니다.백엔드: Redis Lua 스크립트(
user-refresh-transition.lua)를 통해 토큰 검증·상태 전이·새 토큰 발급을 단일 원자 연산으로 묶어 레이스 컨디션을 원천 차단했습니다.
4) 편집기 미리보기와 실제 발행 화면 간의 스타일·렌더링 격차 해소
문제 상황: 프로젝트 작성 화면의 ‘미리보기’와 실제 발행 후 상세 페이지의 컨테이너 폭, 아키텍처·ERD 다이어그램 헤더 비율, 테이블 명세서 가로 스크롤 동작이 미묘하게 달라 작성자가 발행 결과를 예측하기 어려웠습니다.
해결 및 성과:
미리보기 컨테이너에 실제 상세 페이지와 동일한
.detail-shell카드 레이아웃과 여백 규칙을 적용하고, 아키텍처와 데이터베이스 상단 툴바 높이(42px)·타이포그래피(12px)·확대 모달 인터랙션을 단일 디자인 토큰으로 통일하여 WYSIWYG(보는 그대로 발행되는) 경험을 완성했습니다.
프로젝트 회고
1) 배운 점과 기술적 성장
외부 그래픽 라이브러리에 의존하지 않고 직접 구현한 SVG 다이어그램 엔진: React Flow나 Mermaid 같은 기성 라이브러리를 쓰면 초기 구현은 빠르지만, Pebble만의 양방향 ERD-명세서 동기화나 세밀한 직교 라우팅·카디널리티 표기법을 구현하는 데 한계가 뚜렷했습니다. 밑바닥부터 순수 SVG와 기하학 라우팅·포인터 캡처 상태 머신을 직접 설계하며 복잡한 인터랙티브 UI 아키텍처에 대한 깊은 통찰을 얻었습니다.
프론트엔드와 백엔드를 관통하는 방어적 이중 검증: 사용자가 캔버스에서 조작하는 JSON 다이어그램 데이터와 리치 텍스트 블록을 다루면서, 클라이언트 파서와 백엔드 도메인 검증기 양쪽에서 동일한 불변식(크기 상한, 참조 무결성, 유니코드 서러게이트 검증, 허용 프로토콜 검증)을 강제하는 계약 중심 개발(Contract-Driven Development)의 힘을 체감했습니다.
보안과 개인정보 보호를 기능 설계의 출발점으로 삼는 태도: 만 14세 연령 검증 시 생년월일 원문 미저장 원칙, 비회원 댓글의 원문 IP DB 미보관 및 Redis 해시 기반 레이트 리밋, 비공개 R2와 15분 HMAC 서명 Worker 분리 등 실제 상용 서비스 수준의 보안·프라이버시 아키텍처를 끝까지 타협 없이 구현해 냈습니다.
2) 아쉬운 점 및 향후 발전 계획
SQL DDL 내보내기 / 가져오기 자동화: 현재 구축된 정교한
DB_SCHEMA모델을 기반으로, 작성한 ERD를 PostgreSQL / MySQLCREATE TABLEDDL로 즉시 추출하거나 기존 SQL 스크립트를 붙여넣어 ERD 카드를 자동 생성하는 파서를 추가할 계획입니다.아키텍처 클라우드 템플릿 프리셋: 자주 쓰이는 웹 서비스 아키텍처(예:
Cloudflare + Next.js + Spring Boot + RDS)를 원클릭 템플릿으로 불러와 빠르게 커스터마이징할 수 있는 프리셋 라이브러리를 확장할 예정입니다.
프로젝트 링크
로그인 상태를 확인하고 있어요.
댓글
로그인 상태를 확인하고 있어요.