마스터링 Sui DeepBook: 실전 DeFi DEX 시리즈 (3)
Source: Dev.to
Note 계약 이해하기
아래는 이번 튜토리얼에서 사용할 노트 계약의 코드입니다. 이 노트 계약은 사용자가 노트를 생성하고 삭제할 수 있게 하는 아주 간단한 개념 증명(Proof‑of‑Concept)입니다. 코드의 각 부분을 이해할 수 있도록 자세한 주석을 추가했습니다.
Note 모듈
새 파일 sources/notes.move를 만들고 다음 코드를 추가하세요:
#[lint_allow(self_transfer)]
module dacade_zklogin::notes {
use sui::tx_context::{TxContext, Self};
use sui::transfer::Self;
use sui::object::{Self, UID};
use std::string::String;
/// Notes(공유 객체)의 ID를 저장하기 위한 구조체
struct Notes has key {
id: UID
}
/// 개별 Note를 나타내는 구조체
struct Note has key, store {
id: UID,
title: String,
body: String
}
/// Notes 공유 객체를 생성하는 모듈 초기화 함수
#[allow(unused_function)]
fun init(ctx: &mut TxContext) {
let notes = Notes {
id: sui::object::new(ctx),
};
// 모든 사용자가 접근할 수 있도록 Notes 객체를 공유
transfer::share_object(notes)
}
/// 새로운 노트를 생성하는 함수
/// @param title: 노트의 제목
/// @param body: 노트의 본문
/// @param ctx: 트랜잭션 컨텍스트
public fun create_note(title: String, body: String, ctx: &mut TxContext) {
let note = Note {
id: object::new(ctx),
title,
body
};
// 트랜잭션 발신자에게 노트를 전송
transfer::transfer(note, tx_context::sender(ctx))
}
/// 노트를 삭제하는 함수
/// @param note: 삭제할 노트 객체
/// @param _ctx: 트랜잭션 컨텍스트
public fun delete_note(note: Note, _ctx: &mut TxContext) {
// Note 구조체를 풀어 필드에 접근
let Note { id, title: _, body: _ } = note;
// 객체 ID를 삭제하여 노트를 실제로 삭제
sui::object::delete(id)
}
}
코드 상세 분석
이 계약은 매우 직관적입니다.
구조체 – 두 개의 구조체가 있습니다: Notes와 Note.
Notes–init에서 생성·공유되는 공유 객체입니다. 이 간단한 버전에서는 UID만 보관합니다.Note– 개별 노트를 나타냅니다.key와store능력을 가지고 있어 주소가 소유하고 전송할 수 있습니다.id,title,body를 저장합니다.
init 함수 – 패키지가 배포될 때 한 번 실행됩니다. Notes 구조체를 만들고 공유하여 모든 사람이 접근할 수 있게 합니다.
create_note 함수 – 누구나 새로운 노트를 만들 수 있습니다. 제목과 본문을 받아 Note 객체를 생성하고, 이를 발신자(tx_context::sender(ctx))에게 전송합니다. 호출자가 노트의 소유자가 됩니다.
delete_note 함수 – 노트 소유자가 해당 노트를 삭제할 수 있습니다. Note 객체를 값으로 받아(소모하고) 풀어 UID를 삭제함으로써 노트를 제거합니다.
포괄적인 테스트 작성
스마트 계약 개발에서는 테스트가 필수입니다. 노트를 올바르게 생성하고 삭제할 수 있는지 검증하는 테스트 모듈을 작성해 보겠습니다.
새 파일 sources/notes_tests.move를 만들거나 기존 테스트 파일에 아래 코드를 추가하세요:
#[test_only]
module dacade_zklogin::notes_tests {
use dacade_zklogin::notes::{Self, Note};
use sui::test_scenario;
use std::string::{Self};
#[test]
fun test_create_and_delete_note() {
let user = @0xA;
// `user`를 발신자로 하는 테스트 시나리오 시작
let scenario = test_scenario::begin(user);
// 1. 노트 생성
{
let ctx = test_scenario::ctx(&mut scenario);
let title = string::utf8(b"My First Note");
let body = string::utf8(b"This is the body of my note.");
notes::create_note(title, body, ctx);
};
// 2. 노트 생성 및 소유권 검증
test_scenario::next_tx(&mut scenario, user);
{
// Take the Note object from the sender's inventory
let note = test_scenario::take_from_sender(&scenario);
// If we successfully took it, the note was created and transferred to the user.
// Now, let's delete it.
// 3. Delete the note
let ctx = test_scenario::ctx(&mut scenario);
notes::delete_note(note, ctx);
};
// 4. Verify note deletion
test_scenario::next_tx(&mut scenario, user);
{
// Check that the user no longer has the note.
// `has_most_recent_for_sender` returns false if the object is no longer accessible/owned.
assert!(!test_scenario::has_most_recent_for_sender(&scenario), 0);
};
test_scenario::end(scenario);
}
}
테스트 실행
테스트를 실행하려면 contract 디렉터리에서 터미널을 열고 다음 명령을 실행하세요:
sui move test
테스트가 성공했음을 나타내는 출력이 표시됩니다:
Running Move unit tests
[ PASS ] dacade_zklogin::notes_tests::test_create_and_delete_note
Test result: OK. Total tests: 1; passed: 1; failed: 0
“Note” 계약 튜토리얼 끝.
계약 배포
이제 계약을 테스트했으니 Sui Devnet에 배포할 차례입니다.
Devnet으로 전환
Sui 클라이언트가 devnet 환경에 연결되어 있는지 확인하세요.
sui client switch --env devnet
배포
배포 명령을 실행합니다. 트랜잭션이 실행될 충분한 가스를 확보하기 위해 가스 예산을 설정합니다.
sui client publish --gas-budget 100000000
Note: 아직 주소에 자금을 충전하지 않았다면
sui client faucet명령을 실행해 테스트용 SUI를 받아오세요.
중요한 정보 저장
패키지를 배포하면 Sui CLI가 트랜잭션 요약을 출력합니다. 이 출력에는 나중에 프론트엔드 애플리케이션을 스마트 계약에 연결하기 위해 반드시 저장해야 하는 중요한 정보가 들어 있습니다.
출력에서 Object Changes 섹션을 찾아 다음 ID들을 식별하고 저장하세요:
- Package ID – 배포된 코드의 ID (
Published Objects아래에 표시). - Shared Object ID –
init함수에서 생성된Notes객체의 ID (Created Objects아래에Owner: Shared로 표시).
예시 출력 분석
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x81833f94d4019d03fdc0d63abdf179950740f513bf434ae182e1031e0568a2b7 │
│ │ Owner: Shared( 5 ) │
│ │ ObjectType: 0xfea5f1839d675fa03d5b02208adf2e6167336b4646399cb2248b9a7347a48998::notes::Notes │
│ └── │
│ ... │
│ Published Objects: │
│ ┌── │
│ │ PackageID: 0xfea5f183d675fa03d5b02208adf2e6167336b4646399cb2248b9a7347a48998 │
│ │ Version: 1 │
│ │ Digest: 93isZkd3bCKDqeMUL1wUhWnxYvJT4KLc72U6buFAPS4F │
│ │ Modules: notes │
│ └── │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
저장할 내용
- Package ID:
0xfea5...8998(Published Objects섹션에서) - Notes Shared Object ID:
0x8183...a2b7(Created Objects섹션에서 Owner가Shared인 경우)
Tip: 지금 바로 프론트엔드 프로젝트에 .env 파일이나 constants.ts 파일을 만들어 위 값을 저장해 두세요. UI에서 계약과 상호작용하려면 이 값들이 필요합니다.
축하합니다! 여기까지 오셨다면 스스로에게 충분히 칭찬해 주세요—이제 공식적으로 블록체인‑프로 배지를 획득하셨습니다. 😄
첫 번째 Sui Move 스마트 계약을 작성하고, 테스트하고, 배포까지 성공했습니다. 꽤 훌륭하지 않나요?
다음 파트에서는 이 개념들을 실제로 적용해 DeepBook DEX 프론트엔드의 핵심 컴포넌트를 구축할 것입니다. 이론과 실습이 만나면서 이야기가 본격적으로 흥미로워집니다.