Next.js + Supabase Docker 셀프 호스팅 구축기

저사양 클라우드 인스턴스의 성능 한계를 극복하기 위해 고성능 미니 PC 기반의 홈 서버와 Docker 기반 Supabase 풀스택 환경을 구축합니다. 내부망 통신을 통한 지연 시간 단축과 전력 효율 최적화를 통해 비용 대비 극대화된 인프라 성능을 구현합니다.

ByteGaze
12/17/2025
9분 소요

처음엔 저도 당연히 클라우드가 정답인 줄 알았습니다.

Vultr 계정에 잔액이 좀 남아있길래, 가장 저렴한 $6짜리 VPS 인스턴스를 하나 띄워 Next.js를 올리고 DB는 Supabase Cloud(Free, 서울 리전)를 연결했습니다. "같은 서울이니까 빠르겠지?"라고 생각했죠.

하지만 현실은 미묘하게 달랐습니다:
  1. CPU 병목: $6짜리 VPS의 빈약한 vCPU는 Next.js의 SSR 렌더링만으로도 숨이 찼습니다. 여기에 외부 HTTPS 통신(DB 연결) 오버헤드까지 더해지니 반응이 굼떴습니다.

  2. 데이터센터 간 지연: Vultr와 Supabase(AWS)가 물리적으로는 같은 서울에 있지만, 서로 다른 통신망을 타기 때문에 "외부 인터넷"을 거쳐야 합니다.

"아, 답답해서 못 쓰겠다!"

결국 눈길을 돌린 건 책상 위에서 조용히 먼지만 쌓여가던 미니 PC, TOPFEEL DeskOne T1이었습니다.

나의 새로운 홈 서버: TOPFEEL DeskOne T1

이 녀석의 스펙을 기존에 쓰던 $6짜리 Vultr 인스턴스와 비교해보니, 이건 "다윗과 골리앗" 싸움도 아닌 "개미와 티라노사우루스" 수준의 체급 차이가 났습니다.

구분Vultr Cloud (High Frequency)TOPFEEL DeskOne T1 (Home)비고
CPU1 vCPU (Intel High Frequency)AMD Ryzen 7 8745HS (8C/16T)1코어 vs 16스레드
RAM1 GB32 GB DDR5대역폭과 용량 모두 압살
Storage32 GB NVMe2TB NVMe (PCIe 4.0)용량 64배 차이
Cost$6.00/월 (약 8,500원)전기요금 4,000원~5,000원/월성능은 수십 배, 비용은 저렴

💡 참고: Vultr의 vhf-1c-1gb 플랜은 고성능(High Frequency) 제품군이라 $6부터 시작하며 스토리지도 32GB를 제공합니다. 하지만 8코어 16스레드를 가진 라이젠 8745HS 앞에서는 초라해질 수밖에 없습니다.

⚡ 퍼포먼스: "같은 서울"이어도 로컬이 더 빠른 이유

Supabase 서울 리전을 쓰면 핑(Ping)은 3~5ms 내외로 매우 빠릅니다. 하지만 "빠른 것""즉각적인 것(Instant)"의 차이는 큽니다.

클라우드 환경에서는 쿼리 하나를 날릴 때마다 Next.js(Vultr)인터넷망Supabase(AWS) 과정을 거치며 SSL 핸드쉐이크네트워크 홉이 발생합니다. 하지만 셀프 호스팅은 이 모든 과정이 생략됩니다.

비교 항목Supabase Cloud (서울)Self-Hosted (로컬)차이점
통신 경로Vultr ↔ Public Internet ↔ AWSDocker Bridge Network외부망 vs 내부망
지연 시간 (RTT)2ms ~ 10ms (가변적)< 0.1ms (마이크로초)비교 불가
안정성인터넷망 회선 상태 영향 받음네트워크 변수 거의 없음

💡 핵심: 단순히 핑(Ping) 차이뿐만이 아닙니다. Docker 내부망(Loopback)을 통한 통신은 네트워크 패킷 처리 오버헤드가 거의 없어, CPU 부하를 획기적으로 줄여줍니다. 저사양/고사양을 막론하고 "체감 빠릿함"이 다를 수밖에 없습니다.

💡 팩트 체크: 전기요금 폭탄? 직접 계산해봤습니다

"성능 좋은 건 알겠는데, 24시간 켜두면 전기요금 폭탄 맞는 거 아냐?"라고 걱정하실 수 있습니다. 그래서 Ryzen 7 8745HS 기준으로 전력 효율 세팅을 적용해 시뮬레이션을 돌려봤습니다.

  • 최적 세팅: 바이오스에서 SmartShift 35W 제한 (전성비 스윗스팟)

  • 실제 소비전력:
    • 풀로드(Full Load): 시스템 전체 약 56W

    • 일상 평균(Idle/Web): 시스템 전체 약 25W

  • 한 달 예상 요금 (24시간 가동 시):
    • 월 사용량: 약 18kWh (25W × 24h × 30일)

    • 예상 비용: 약 3,900원 ~ 5,500원 (주택용 누진세 2~3단계 기준)

결론적으로, 서버를 하루 종일 켜둬도 Vultr 최저가 요금($6, 약 8,500원)의 절반 수준입니다. 성능은 수십 배 좋으면서 유지비는 더 저렴한 셈이죠.


"저 깡패 같은 성능을 놔두고 왜 내가 1GB 램에 갇혀서 고통받아야 하지?"

그래서 결심했습니다. "모든 걸 집으로 가져오자. 성능도, 데이터도, 속도도!"

이번 프로젝트에서는 미니 PC 서버 자원을 활용해 Next.jsSupabase 전체 스택을 Docker로 띄워 운영하기로 결정했습니다.

Docker Host 안에 Next.js App 컨테이너와 Supabase(Kong, Auth, DB, Realtime 등) 컨테이너들이 떠 있는 모습

1. 왜 Supabase Self-Hosting인가?

Supabase는 오픈소스 Firebase 대안으로 아주 훌륭합니다. 특히 docker-compose 하나로 인증(GoTrue), 스토리지, 실시간 구독(Realtime), API 게이트웨이(Kong)까지 한방에 구축할 수 있다는 게 엄청난 매력이었죠.

단, 홈 서버에서 돌릴 때는 미디어 파일 처리배포의 유연성을 고민해야 했습니다.

2. 미디어 서버 분리 vs Supabase Storage

처음에는 Nginx를 통해 로컬 디렉토리에 정적 파일을 직접 저장하고 서빙하려 했습니다. 하지만 이 방식은 확장성도 떨어지고, 무엇보다 Supabase 생태계의 장점을 전혀 활용하지 못하는 구식 방식이었습니다.

❌ (Before) 버려진 로컬 파일시스템 코드

처음 작성했던 코드는 대략 이랬습니다. 디렉토리가 없으면 생성하고, 경로를 계산해서 파일을 쓰고, URL을 반환하는... 아주 귀찮은 작업들이었죠.

// src/utils/local-storage.ts (이제는 안 씁니다)
import fs from 'fs';
import path from 'path';

export async function saveFileLocally(file: Buffer, filename: string) {
  // 1. 저장할 디렉토리 경로 설정 (public 폴더 내 uploads)
  const uploadDir = path.join(process.cwd(), 'public/uploads');

  // 2. 디렉토리가 없으면 생성 (recursive 옵션 필수)
  if (!fs.existsSync(uploadDir)) {
    fs.mkdirSync(uploadDir, { recursive: true });
  }

  // 3. 파일 쓰기
  const filePath = path.join(uploadDir, filename);
  fs.writeFileSync(filePath, file);

  // 4. 접근 가능한 정적 URL 반환
  return `/uploads/$ {filename}`;
}

✅ (After) Supabase Storage 전면 도입

Supabase Storage를 사용하니 로컬 파일시스템을 직접 제어할 필요가 싹 사라졌습니다. 로컬 Docker 환경에서도 S3와 동일한 방식의 객체 스토리지를 사용할 수 있게 된 것이죠.

// src/utils/storage.ts
// 위에서 짰던 복잡한 fs 코드는 다 갖다 버리고 이렇게 깔끔해졌습니다.
export async function uploadToStorage(file: Buffer, path: string) {
  const { data, error } = await supabase.storage
    .from('media') // 버킷 이름
    .upload(path, file, { 
      contentType: 'image/webp',
      upsert: true 
    });
    
  if (error) throw error;
  
  // 저장된 파일의 공개 URL을 바로 받아옵니다.
  const { data: { publicUrl } } = supabase.storage
    .from('media')
    .getPublicUrl(data.path);
    
  return publicUrl;
}

Supabase Studio의 Storage 대시보드 화면

🔗 참고: 더 자세한 설정 방법은 Supabase 공식 셀프 호스팅 가이드를 참고하세요.

3. 마무리 및 예고

이렇게 미니PC에 빵빵한 풀스택 환경을 구축했습니다. 비용은 전기요금 뿐입니다.

하지만, "집에 정전이 나면 어떡하지?"

다음 편에서는 단돈 $6로 무중단 재해 복구(DR) 시스템을 구축한 이야기를 다룹니다.