페이지를 불러오는 중

Folio 01 · 개인 프로젝트

TablePeek

가볍게, 브라우저에서 바로 쓰는 데이터베이스 클라이언트

기간
2026.10.17 — 현재
역할
데이터베이스 클라이언트 · 제품 개발
테마
50종
플랫폼
macOS · Windows

가볍고 디자인이 깔끔하면서, 어느 운영체제에서나 설치 부담 없이 바로 쓸 수 있는 데이터베이스 도구가 필요해서 만든 프로젝트. 접근이 가장 쉬운 웹에서 찾아봤지만, 디자인이 마음에 드는 도구는 설치가 필요했고 설치 없이 쓰는 도구는 조작이 직관적이지 않았음. 그래서 내 컴퓨터에서 Rust 엔진을 띄우고 화면은 브라우저로 쓰는 구조로 새로 만듦. SQLite·PostgreSQL·MySQL/MariaDB·CockroachDB·SQL Server를 같은 화면에서 조회·편집하고, 가져오기·내보내기는 백그라운드 작업으로 처리. 필터·외래 키 탐색·글꼴·스크롤바·설정까지 데스크톱 앱처럼 다듬는 중.

Rust · axum · Arrow IPC · WebSocket · React · CodeMirror 6 · Canvas · MCP

화면

SQLite 데모 데이터베이스의 orders 표를 값의 타입별 색상으로 보여 주는 TablePeek 데이터 표 화면
데이터 표 — 문자열·숫자·JSON·날짜를 타입별 색상으로 구분.
셀에 담긴 JSON 값을 트리 구조로 펼쳐 보여 주는 TablePeek Quick Look 패널
Quick Look — 셀 값을 JSON 트리와 인코딩·HEX로 확인.
테마 목록과 타입 색상 미리보기가 함께 있는 TablePeek 화면 설정
화면 설정 — 테마와 타입 색상을 미리 보며 조정. (10.20 화면. 지금은 영역별로 나뉜 설정 화면.)

01 왜 새로 만들었나: 웹 화면과 로컬 엔진 2026.10.17 — 10.20

필요했던 도구의 조건은 가볍고, 디자인이 깔끔하고, 운영체제와 상관없이 바로 쓸 수 있는 것. 접근이 가장 쉬운 웹에서 찾았지만, 디자인이 마음에 드는 도구는 설치가 필요했고 설치 없이 쓰는 도구는 조작이 직관적이지 않았음. 두 조건을 함께 만족하는 도구가 없어 직접 만들기로 결정.

요구사항

가벼움
무거운 데스크톱 앱 없이, 엔진 하나를 띄우고 브라우저로 사용
어디서나
macOS와 Windows에서 같은 화면, 같은 사용법
바로 쓰는 화면
처음 써도 헤매지 않는 조작과 깔끔한 디자인

구현

  1. 화면은 웹, 접속은 로컬 엔진 설계 판단

    • 운영체제와 상관없이 같은 화면을 쓰려고 화면을 웹으로 구성
    • 브라우저만으로는 데이터베이스에 직접 접속할 수 없어, 내 컴퓨터에서 실행하는 Rust 엔진이 접속과 쿼리 실행을 담당
    • 엔진은 127.0.0.1에서만 열리고 실행할 때마다 새 접근 토큰을 발급해, 웹 화면이지만 외부 서버를 거치지 않음
  2. 엔진과 서버의 역할 분리

    • tablepeek-engine이 드라이버·세션·쿼리 실행·취소·Arrow 직렬화를 담당
    • tablepeek-server(axum)가 루프백 리스너·인증·WebSocket 스트리밍·SSH 터널·접속 프로필을 담당
    • 결과는 Arrow IPC로 받아 보이는 칸만 그리는 캔버스 표에 표시해, 큰 테이블도 구간별로 가볍게 표시
  3. 값을 읽기 쉽게

    • 문자열·정수·실수·큰 정수·불리언·JSON·날짜·NULL을 타입별 색상으로 구분
    • 셀을 Quick Look으로 열어 JSON 트리, 인코딩·HEX 보기, NULL 저장 지원
    • AI 클라이언트용 MCP 서버를 읽기 전용 조회 도구로 제공

남긴 것

  • 다섯 종류의 데이터베이스를 같은 표·편집기·스키마 화면으로 다루는 기본 구조 완성
  • cargo와 Node.js로 빌드해 바로 실행하는 경로 마련

02 접속 관리와 비밀번호 보관 2026.10.21 — 10.23

여러 환경의 데이터베이스를 오가려면 접속 정보를 모아 두고 다른 컴퓨터로 안전하게 옮길 수 있어야 함. 접속 홈, 내보내기 파일 암호화, 비밀번호 보관 방식을 정리.

요구사항

접속 정리
접속을 그룹으로 묶고 LOCAL·DEV·STAGE·PROD 배지로 구분
안전한 이동
접속 목록을 파일로 내보내고 다른 컴퓨터에서 가져오기
비밀번호 보관
설정 파일에 비밀번호를 남기지 않을 것

구현

  1. 암호화된 접속 내보내기: 사람이 기억하는 비밀번호에서 출발하는 파일 설계 판단

    • 위협 모델부터 정함. 이 파일은 메신저와 USB로 옮겨지고 다운로드 폴더에 몇 달 남는다. 공격자는 파일을 통째로 들고 가서 자기 장비로 비밀번호를 무한히 시도할 수 있다고 가정. 그래서 지켜야 할 것은 전송 구간이 아니라 "오프라인 추측을 비싸게 만드는 일"
    • 키 유도는 Argon2id. PBKDF2와 scrypt를 두고 고민했는데, PBKDF2는 반복 횟수만 올릴 수 있어 GPU에서 수천 배 빠르게 돌고, scrypt는 메모리를 쓰지만 파라미터 하나에 시간과 메모리가 묶여 따로 조절할 수 없음. Argon2id는 메모리(64 MiB)·반복(3)·병렬도(1)를 각각 정할 수 있고, 메모리 접근이 비밀번호에 의존하는 d 모드와 의존하지 않는 i 모드를 섞어 사이드 채널과 GPU 양쪽을 막는 절충안이라 골랐음. 64 MiB는 노트북에서 한 번 여는 데 0.3초 안쪽, 공격자가 GPU 한 장에서 동시에 돌릴 수 있는 시도 수를 메모리 용량으로 묶는 선
    • 암호화는 AES-256-GCM. CBC처럼 암호화와 인증을 따로 조립하면 패딩 오라클 같은 실수가 끼어들 자리가 생겨서, 둘이 한 몸인 AEAD만 후보로 두었음. ChaCha20-Poly1305도 좋지만 Apple Silicon과 x86 모두 AES 하드웨어 명령을 갖추고 있어 AES-GCM이 더 빠르고, 키 크기는 256비트로 두어 Argon2 출력 32바이트를 그대로 키로 사용. 논스 12바이트는 파일마다 새로 뽑고, 파일 하나에 암호문이 하나뿐이라 논스 재사용 걱정이 구조적으로 없음
    • 봉투(envelope)도 인증함. 포맷 이름, 버전, KDF 이름과 세 파라미터, 솔트, 암호 이름, 논스를 줄바꿈으로 이어 붙여 GCM의 associated data로 넣었음. 누가 헤더의 메모리 비용을 1 KiB로 고쳐 놓아도 복호화가 "틀린 비밀번호"와 같은 실패로 끝나기 때문에, 파라미터를 낮춰 추측을 쉽게 만드는 공격이 성립하지 않음
    • 여는 쪽의 비용에 상한을 둠. 파일이 요구하는 메모리는 256 MiB, 반복은 16, 병렬도는 8을 넘으면 열지 않음. 악의적인 파일 하나로 사용자의 메모리를 다 먹게 하는 것도 공격이기 때문. 반대로 파라미터는 파일 안에 적어 두어, 나중에 기본값을 올려도 옛 파일은 그대로 열림
    • 비밀번호는 최소 8자. 더 길게 강제하면 사람들이 포스트잇에 적는다. 대신 KDF 비용이 짧은 비밀번호의 부족분을 메우는 방향으로 설계
    • 저장된 접속 비밀번호는 "포함"에 체크했을 때만 서버가 키체인에서 읽어 암호문 안에 넣음. 평문 JSON에는 이름·호스트·사용자도 남기지 않고 전부 암호문 안으로. 봉투만 보고 알 수 있는 것은 "TablePeek 접속 파일이고 이렇게 열면 된다"뿐
    • 가져오기는 모든 항목을 검증한 뒤 전부 저장하거나 하나도 저장하지 않음. 반쯤 들어온 접속 목록은 없는 것보다 나쁨
  2. 접속 홈과 작업 화면

    • 접속을 그룹으로 정리하는 시작 화면, 연결 탭 색상과 환경 배지
    • 쿼리 기록·안전 모드·패널 상태를 접속별로 유지
    • 내장 테마 50종과 Base16·VS Code 테마 가져오기

까다로웠던 지점

"암호화했다"와 "안전하다" 사이의 거리

첫 구현은 비밀번호를 SHA-256으로 한 번 해시해 키로 쓰고 AES-CBC로 감쌌음. 동작은 했지만, 파일을 들고 간 공격자가 흔한 비밀번호 사전을 초당 수십억 번 대입할 수 있다는 점과, CBC는 암호문이 바뀌어도 모르고 지나간다는 점을 셀프 리뷰에서 걸러냄.

해결KDF를 Argon2id로, 암호를 AES-256-GCM으로 바꾸고 봉투 전체를 associated data로 묶음. 테스트는 "같은 입력을 두 번 내보내면 솔트와 논스가 달라 파일이 다르다", "헤더의 파라미터를 한 글자라도 고치면 열리지 않는다", "버전이 다른 파일과 다른 프로그램의 파일을 구분해 말한다", "메모리 비용을 64 GiB로 적은 파일은 열려고 시도조차 하지 않는다"를 고정.

비밀번호 저장을 뺐다가 되돌림

내보내기 파일의 비밀번호와 접속 비밀번호를 같은 것으로 잘못 보고, 비밀번호 저장 기능을 한 번 제거. 같은 날 원인을 확인하고 되돌림.

해결접속 비밀번호는 운영체제 키체인에 저장하는 것을 기본으로 하고, 원하면 접속할 때마다 입력하도록 선택. store.json에는 비밀번호를 남기지 않음.

남긴 것

  • 접속 정보는 파일에, 비밀번호는 키체인에 나눠 보관하는 구조 확정
  • 접속 목록을 다른 컴퓨터로 옮길 때도 비밀번호가 평문으로 드러나지 않는 경로 마련

03 가져오기·내보내기와 백그라운드 작업 2026.10.26

가져오기, SQL 덤프 복원, 내보내기를 백그라운드 작업으로 돌리면서 화면의 조회와 같은 데이터베이스 세션을 함께 쓰게 됨. 작업과 화면이 서로를 취소하지 않도록 세션을 쓰는 순서를 정리.

요구사항

가져오기
CSV·JSON·SQL 덤프를 미리 보고 인코딩을 골라 가져오기
내보내기
CSV·JSON·XML·SQL INSERT로 내보내고, 백업을 다시 불러올 수 있을 것
동시 작업
가져오는 중에도 표를 열어 볼 수 있을 것

구현

  1. 세션 하나를 나눠 쓰는 순서 설계 판단

    • 접속마다 데이터베이스 세션이 하나라 화면 조회와 백그라운드 작업이 같은 세션을 사용
    • 문장을 하나씩 통과시키는 관문을 접속마다 두고, 열린 트랜잭션이 화면 것인지 작업 것인지 매 문장 뒤에 판정
    • 작업이 연 트랜잭션은 화면 조회가 취소하지 못하도록 서버에서 보호
  2. 값을 바꾸지 않는 가져오기·내보내기

    • SQL 내보내기와 백업을 외래 키 순서(부모 테이블 먼저)로 작성
    • JSON의 큰 정수와 유효숫자가 많은 소수를 원문 그대로 유지
    • CSV 구분자·따옴표·인코딩(UTF-8, EUC-KR 등)을 고르고 한 트랜잭션으로 가져오기

까다로웠던 지점

화면 조회와 백그라운드 작업의 상호 취소

새 조회가 같은 세션에서 실행 중인 문장을 취소하는 구조라, 가져오기 중에 표를 클릭하면 가져오기 일부가 끊기고 PostgreSQL에서는 트랜잭션이 중단됨. 내보내기도 화면 조회와 서로를 취소.

해결접속마다 문장 순서를 정하는 관문과 트랜잭션 소유 판정을 추가해 작업과 화면이 서로 끊지 않도록 정리.

JSON 큰 숫자의 정밀도 손실

JSON 가져오기가 모든 숫자를 double로 읽어 1234567890123456789 같은 ID가 1234567890123456800으로 바뀐 채 BIGINT 열에 저장됨.

해결double로 읽으면 값이 바뀌는 숫자는 원문 텍스트를 유지하는 값으로 읽어 그대로 저장.

남긴 것

  • 가져오기·복원·내보내기를 화면 작업과 동시에 실행할 수 있는 구조 마련
  • 실패한 가져오기·복원은 멈춘 문장을 오류 위에 표시하고, 되돌릴 수 있는 범위에서는 모두 되돌림

04 매일 쓰는 화면 다듬기 2026.10.27 — 현재

구조가 자리 잡힌 뒤에는 하루 종일 열어 두는 도구로서의 감각을 맞추는 일. 필터, 외래 키 탐색, 접속 홈, 글꼴, 스크롤바, 설정 화면을 데스크톱 클라이언트 수준으로 정리.

요구사항

필터
조건을 하나씩, 또는 체크한 것을 모두 AND·OR로 적용
관계 탐색
외래 키 셀에서 참조하는 행과 테이블을 바로 열기
손에 익은 감각
글꼴·스크롤바·메뉴·설정이 웹 페이지가 아니라 앱처럼 동작

구현

  1. 필터 바 설계 판단

    • 포함·시작·끝·목록·범위 같은 연산자와 SQL 직접 입력을 조건 줄로 쌓고, 한 조건만 적용하거나 체크한 조건을 AND·OR로 함께 적용
    • 마지막 조건을 지우면 필터 바가 닫히고, 정렬·페이지·필터가 바뀌어도 열 너비는 유지
    • 바이너리 값은 HEX로 표시하고 HEX로 고쳐 쓰면 바이트로 저장
  2. 외래 키 Quick Look

    • 외래 키 셀의 Quick Look에 참조하는 행을 표시
    • 참조하는 테이블을 그 키로 필터한 표로 열어, 관계를 따라가며 읽기
    • 오른쪽 패널은 필드 검색을 유지하고 선택된 행이 없으면 그렇다고 알림
  3. 접속 홈과 설정

    • 접속 그룹을 끌어서 정렬하고, 데이터베이스 종류에서 새 접속을 시작하며, 연결 탭은 한 번 클릭으로 닫힘
    • 설정을 일반·테마, SQL 편집기·데이터 표·사이드바, 가져오기·내보내기처럼 화면 영역별로 재구성하고 영역마다 미리보기
    • SQL 편집기·데이터 표·사이드바가 각각 설치된 글꼴 전체에서 글꼴을 고르고, 스크롤바는 앱이 직접 그려 얇게, 포인터 아래서만 한 단계 두껍게

까다로웠던 지점

테스트용 데이터베이스가 비어 있음

엔진 테스트가 읽는 몇 줄만 들어 있어, 실제 서버에서 필터·관계 탐색·가져오기를 손으로 확인할 데이터가 없었음.

해결Docker 테스트 서버 네 종류에 고객·상품·주문·리뷰로 이어진 샘플 shop 스키마(약 28,000행)를 심음. 자기 참조·복합 키·CASCADE·enum·JSON·배열·바이너리·기본 키 없는 테이블·한글과 이모지가 섞인 값까지 포함해, 어색한 경우를 모두 한 곳에서 확인.

남긴 것

  • 필터·관계 탐색·접속 관리가 데스크톱 클라이언트와 같은 흐름으로 동작
  • 네 엔진의 테스트 서버를 엔진별로 띄우고 같은 샘플 데이터로 손으로 확인하는 경로 마련

이미지를 불러오지 못했습니다.