24일 차: 솔라나에서는 모든 것이 계정이다

발행: (2026년 5월 23일 AM 10:09 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

Solana에는 계정만 존재합니다. 하나의 모델이죠. 모든 것이 계정입니다 — 지갑, 배포된 프로그램, 토큰 민트, 사용자의 토큰 잔액까지. 이 모든 것은 키가 32바이트 주소이고 값이 계정 데이터인 동일한 평면 키‑값 저장소에 존재합니다.
간단해 보이지만, 실제로는 많은 함의를 가진 꽤 우아한 설계 결정입니다.

제가 이해하게 된 사고 모델은 다음과 같습니다: Solana를 파일 시스템처럼 생각해 보세요.

metadata:

  • owner
  • permissions
  • size

contents:

  • 실제 데이터

프로그램 계정은 실행 파일이고, 데이터 계정은 그 프로그램이 읽고 쓰는 문서입니다. 그리고 System Program은? 바로 OS 커널과 같습니다 — 새로운 파일을 만들고 소유권을 이전하는 일을 담당합니다.

계정이 무엇을 나타내든, 항상 동일한 다섯 개의 필드를 가지고 있습니다:

  • lamports — SOL 잔액. 1 SOL = 1,000,000,000 lamports.
  • data — 원시 바이트 배열. 모든 상태가 여기 저장됩니다.
  • owner — 이 계정을 제어하고 데이터를 수정할 수 있는 프로그램.
  • executable — 불리언 값. true이면 이 계정에 배포된 프로그램이 들어 있습니다.
  • rent_epoch — 사용되지 않음. 최신 계정에서는 u64::MAX 로 설정돼 있습니다.

소유권 규칙이 핵심 보안 원칙입니다: 오직 소유자 프로그램만 계정 데이터를 수정하거나 lamports를 차감할 수 있습니다. 누구든지 쓰기 가능한 계정에 lamports를 입금할 수 있습니다. 단순하지만 강력합니다.

Web2 개발자에게 가장 놀라운 점은 Solana 프로그램은 무상태(stateless)라는 것입니다.
프로그램의 실행 바이트코드는 하나의 계정에 존재하고, 프로그램이 필요로 하는 모든 데이터는 완전히 별개의 계정에 존재합니다. 프로그램은 런타임에 그 계정들을 읽고 씁니다. 이는 웹 서버(프로그램)와 데이터베이스(데이터 계정)가 별개인 것과 같은 차이입니다.

이를 구체적으로 보여드리기 위해, 저는 Solana 메인넷에서 가장 기본적인 계정 중 하나인 Wrapped SOL 민트 계정을 가져왔습니다. 아래는 @solana/kit을 사용해 원시 데이터를 가져온 방법입니다:

import { createSolanaRpc, address, getBase64Encoder, getBase16Decoder } from "@solana/kit";
import { getMintDecoder } from "@solana-program/token";

const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
const mintAddress = address("So11111111111111111111111111111111111111112");

const { value: accountInfo } = await rpc
  .getAccountInfo(mintAddress, { encoding: "base64" })
  .send();

const dataBytes = getBase64Encoder().encode(accountInfo.data[0]);

계정 데이터는 base64 형태로 반환됩니다. 이를 원시 바이트로 디코딩한 뒤, 두 가지 디코드 경로를 통해 확인했습니다 — Token Program 코덱을 이용한 방법과 DataView를 사용한 수동 바이트 레벨 접근법입니다:

// Codec approach
const mint = getMintDecoder().decode(dataBytes);

// Manual byte-level approach
const view = new DataView(dataBytes.buffer, dataBytes.byteOffset, dataBytes.byteLength);
const supply = view.getBigUint64(36, true);  // bytes 36–43, little-endian
const decimals = view.getUint8(44);          // byte 44

두 접근법 모두 동일한 결과를 보여줍니다 — 터미널에 출력된 내용은 다음과 같습니다:

  • 공급량(supply)은 0 (wSOL은 필요 시 발행됩니다)
  • 소수점 자리수(decimals)는 9
  • 민트와 동결 권한(authorities)은 모두 null — 즉, 누구도 추가로 민트를 만들거나 전송을 동결할 수 없습니다.
  • 이 계정은 완전히 탈중앙화되어 있습니다.

마지막으로 한 가지 더: 모든 계정은 데이터 크기에 비례하는 최소 lamport 잔액을 유지해야 합니다. 이는 버 validator 상태가 버려진 계정으로 팽창하는 것을 방지합니다. 데이터가 전혀 없는 계정의 경우 최소 잔액은 약 0.00089 SOL 정도입니다. Solana CLI를 사용하면 정확한 금액을 계산할 수 있습니다:

solana rent <ACCOUNT_ADDRESS>

계정이 이 임계값 이하로 떨어지면 자동으로 삭제(purge)됩니다. 따라서 프로그램에서 계정을 생성할 때는 해당 계정이 rent‑exempt 최소 금액을 초과하도록 자금을 충당해야 합니다.

Solana의 계정 모델은 PDAs, 토큰 계정, 프로그램 파생 상태 등 모든 것의 기반입니다. 모든 상태가 계정에 존재하고, 프로그램은 무상태이며, 소유권 = 쓰기 권한이라는 점을 내면화하면 생태계 전체가 훨씬 이해하기 쉬워집니다.

이 글은 제 100 Days of Solana 시리즈의 일부입니다. 제 여정을 따라가며 처음부터 프로그램을 배포하는 과정을 확인해 보세요. Github Repo

0 조회
Back to Blog

관련 글

더 보기 »

내 스킬

프로젝트를 위한 AI 지시문을 만들고, 설치하고, 관리하세요 — 코딩이 필요 없습니다. CREATE 이름을 정하고, 카테고리를 선택하고, 원하는 것을 설명하세요 — 마법사가 자동으로 구성합니다.