Skip to content
Go back

Web3 신뢰 인프라 #5 — IPFS, 블록체인 바깥의 데이터는 어디에 두나

4편까지 지갑 → 앵커링 → DID/VC → 보안을 다뤘다. 여기까지 만들고 나니 한 가지가 빠져 있었다 — 원본 데이터를 어디에 저장할 것인가. 블록체인에는 해시(32바이트)만 기록했고, 원본은 브라우저 메모리에만 존재했다. 새로고침하면 증거가 사라진다.

온체인 저장의 한계

블록체인에 원본을 직접 올리면 안 되나? 해봤다. 스마트 컨트랙트의 SSTORE1 명령어로 32바이트 저장 시 22,100 gas가 소모된다.

데이터 크기슬롯 수ETH 환산 (0.5 gwei)
32바이트 (해시)10.000011 ETH
1KB320.00035 ETH
1MB32,7680.36 ETH
1GB33,554,432370.8 ETH

검사 보고서 PDF 하나가 수 MB인데 0.36 ETH씩 낼 수는 없다. 프라이빗 체인이라 가스비가 없더라도, 모든 노드가 전체 데이터를 복제하므로 디스크가 빠르게 차고 동기화가 느려진다. 결론은 명확했다 — 블록체인에는 해시만, 원본은 오프체인에.

IPFS를 선택한 이유

오프체인 후보로 DB, S3, IPFS를 비교했다.

DBS3IPFS
관리자가 내용 수정가능가능불가능
같은 주소에 다른 내용가능가능불가능
블록체인 연동 궁합약함약함강함

DB와 S3는 관리자가 원본을 바꿔치기할 수 있다. 해시가 안 맞으면 “조작됐다”는 알 수 있지만 “원래 뭐였는데?”는 복구가 안 된다(백업을 조작하면 끝이다). IPFS는 파일 내용을 해싱해서 CID(Content Identifier)2를 만들기 때문에 내용이 1비트라도 바뀌면 CID가 달라진다. 구조적으로 바꿔치기가 불가능하다.

CID — 내용이 곧 주소

HTTP는 “어디에 있나”(URL)로 파일을 찾는다. IPFS는 “무엇인가”(CID)로 찾는다.

HTTP:  https://server.com/report.pdf  ← 서버가 내용을 바꿔도 URL 동일
IPFS:  ipfs://QmXyz...               ← 내용이 바뀌면 CID 자체가 바뀜

IPFS에 파일을 올리면 256KB 단위로 청크를 나누고, 각 청크를 해싱해서 Merkle DAG3로 묶는다. 루트 해시가 CID다. 같은 내용이면 항상 같은 CID가 나오고, 다른 내용이면 반드시 다른 CID가 나온다.

Pinning — 안 하면 사라진다

IPFS 노드에 파일을 올리면(ipfs add) 자동으로 Pin이 걸린다. Pin은 “이 파일은 삭제하지 마”라는 표시다. 노드의 로컬 디스크에 Pin 목록이 파일로 관리된다.

~/.ipfs/
├── blocks/          ← 실제 파일 청크
│   ├── QmAAA.data   ← 배터리 검사 보고서 (Pin ✅)
│   └── QmCCC.data   ← 다른 노드에서 조회한 캐시 (Pin ❌)
└── pins/
    └── pinset       ← Pin 목록: QmAAA만 등록

Pin이 없는 파일은 GC(Garbage Collection) 때 삭제된다. 다른 노드에서 조회해서 캐시된 파일은 Pin이 안 걸려 있으므로 시간이 지나면 사라진다.

Pinata로 PoC 연동

직접 IPFS 노드를 운영하는 대신 Pinning Service4를 썼다. Pinata5에 API 키를 발급받고 SDK에 연동했다.

// @core/anchor/src/ipfs/pinata.ts (핵심)
export async function uploadToIPFS(
  data: string | Record<string, unknown>,
  jwt: string,
): Promise<IPFSUploadResult> {
  const res = await fetch("https://api.pinata.cloud/pinning/pinJSONToIPFS", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${jwt}`,
    },
    body: JSON.stringify({
      pinataContent: typeof data === "string" ? { raw: data } : data,
    }),
  });
  const result = await res.json();
  return { cid: result.IpfsHash, url: `https://gateway.pinata.cloud/ipfs/${result.IpfsHash}` };
}

업로드 후 CID를 SHA-256으로 해싱해서 블록체인에 앵커링한다. 검증 시에는 블록체인에서 해시를 조회하고, CID로 IPFS에서 원본을 다운로드해서 대조한다.

이미지 파일도 올릴 수 있게 uploadFileToIPFS(FormData + pinFileToIPFS 엔드포인트)를 추가했다. 블록체인에는 CID 해시 32바이트만 올라가므로 원본 크기와 무관하게 가스비가 동일하다.

퍼블릭 vs 프라이빗

Pinata는 퍼블릭 IPFS 네트워크에 파일을 올린다. CID만 알면 전 세계 누구나 원본을 조회할 수 있다. 배터리 검사 데이터나 수거기사 개인정보를 여기에 그대로 올리면 안 된다.

프로덕션에서는 프라이빗 IPFS로 전환해야 한다. swarm key6라는 비밀 키를 생성해서 모든 노드에 배포하면, 같은 키를 가진 노드끼리만 연결된다. 외부 노드는 접근 자체가 차단된다.

단일 노드가 죽으면 데이터가 유실되므로 IPFS Cluster를 쓴다. Cluster는 Pin 목록을 모든 노드에 동기화하여 자동 복제한다. 참여 노드가 3~10대 수준이면 전체 복제(replication = 노드 수)가 현실적이다.

일반 IPFS:   노드 A에 업로드 → A만 보관 → A 죽으면 유실
IPFS Cluster: 노드 A에 업로드 → A, B, C 전부 Pin → 어디가 죽어도 남아있음

VC + IPFS — 전부 묶이는 구조

VC 클레임에 CID를 포함하면 자격 증명 + 원본 참조 + 무결성 증명이 하나로 엮인다.

{
  "credentialSubject": {
    "batteryId": "BAT-001",
    "grade": "A",
    "inspectionReport": "QmXyz...",
    "batteryPhoto": "QmAbc..."
  }
}

검증자가 VP를 받으면: VC 서명 검증 → Trust Registry에서 발급자 신뢰 확인 → CID로 IPFS에서 원본 조회 → 앵커링 대조. 1~4편에서 만든 기능이 전부 여기서 맞물린다.

트러블 슈팅

  1. Pinata JWT를 vite define으로 주입하면 빌드에 노출된다 — 4편에서 다뤘던 PK 노출과 같은 문제. Pinata JWT는 공개 API 키 성격이라 괜찮지만, 프로덕션에서는 API 서버를 거쳐야 한다. 브라우저에서 IPFS/블록체인 노드에 직접 접근하는 구조는 PoC에서만 허용된다.
  2. CID로 조회가 안 될 때 — Pinata Gateway에 전파되기까지 1~2초 딜레이가 있다. 업로드 직후 바로 검증하면 404가 나올 수 있다.

회고

블록체인은 “무엇이 일어났나”를 기록하는 곳이지, “원본 데이터”를 저장하는 곳이 아니다. 역할 분리를 구조적으로 강제하는 게 IPFS + CID의 가치다.

PoC에서는 Pinata(퍼블릭)로 연동 흐름을 검증했다. SDK 인터페이스(uploadToIPFS, fetchFromIPFS)를 분리해뒀기 때문에 프라이빗 IPFS로 전환할 때 엔드포인트 URL만 바꾸면 된다. 함수 시그니처는 그대로다.


시리즈 전체: #1 지갑 SDK · #2 앵커링 · #3 DID/VC · #4 보안 · #5 IPFS

Footnotes

  1. SSTORE — EVM에서 스토리지에 값을 쓰는 명령어. 새 슬롯 기록 시 22,100 gas 소모 (EIP-2200 + EIP-2929 기준).

  2. CID (Content Identifier) — IPFS에서 파일 내용을 해싱하여 생성하는 고유 식별자. 내용이 바뀌면 CID도 바뀐다.

  3. Merkle DAG — Merkle Tree의 확장. 파일을 청크로 나누고 해시 트리로 연결하는 구조. 동일 청크 중복 제거 가능.

  4. Pinning Service — IPFS 노드를 직접 운영하지 않고 외부 서비스가 파일을 Pin(영구 보관)해주는 SaaS.

  5. Pinata — 가장 대중적인 IPFS Pinning Service. 무료 1GB, JWT 인증 API 제공.

  6. swarm key — 프라이빗 IPFS 네트워크의 접근 키. 같은 키를 가진 노드끼리만 P2P 연결이 허용된다.


Share this post on:

Comments


Previous Post
Confluence 일일 스크럼 페이지 자동 생성기 만들기
Next Post
Web3 신뢰 인프라 #4 — 보안, PK는 어디에 있어야 하는가