infrablockchain-docs
ko
ko
  • 인프라블록체인
    • 배우기
      • 아키텍처
        • 아키텍처
        • 네트워크 참여자
        • 파라체인
          • 시스템 파라체인
      • 프로토콜
        • 시스템 토큰
        • 트랜잭션 수수료
        • Proof of Transaction
      • Substrate
        • 배우기
          • 기초 지식
            • 암호학
            • 블록체인 기본 개념
            • 합의
            • 네트워크와 노드
            • 트랜잭션과 블록 기본 사항
            • 트랜잭션 수명주기
            • 오프체인 작업
            • 라이트 클라이언트
            • Substrate를 위한 Rust
            • 라이브러리 소개
            • 아키텍처와 Rust 라이브러리
            • 파일 구조
            • 계정, 주소 및 키
            • 트랜잭션 형식
            • 난수 생성
          • 프레임
            • FRAME 팔레트
            • FRAME 매크로
            • 커스텀 팔레트
            • 팔레트 커플링
            • Origin
            • 이벤트와 에러
            • 런타임 스토리지 구조
            • 상태 전이와 스토리지
            • SCALE 인코딩
            • 트랜잭션, Weight 및 수수료
            • 런타임 API
            • 런타임 업그레이드
            • 런타임 개발
          • 계정 데이터 구조
          • 주소 형식
          • 용어집
          • cli
            • 아카이브
            • 메모리 프로파일러
            • 노드 템플릿
            • 사이드카
            • srtool
            • 서브키
            • subxt
            • try-runtime
            • tx-wrapper
          • 런타임 개발
            • 기본
              • Genesis 상태 구성하기
              • 런타임 상수 구성
              • 체인 스펙을 커스텀하기
              • 팔레트 가져오기
              • 도우미 함수 사용하기
            • 합의 모델
              • 작업 증명을 사용하는 체인 구성
              • 하이브리드 노드 생성하기
            • 오프체인 워커
              • 오프체인 HTTP 요청하기
              • 오프체인 인덱싱
              • 오프체인 로컬 스토리지
            • 팔레트 설계
              • 크라우드펀딩 구성하기
              • 스토리지 구조체 (struct) 생성하기
              • 잠금 가능한 통화 구현
              • 무작위성 적용하기
              • 느슨한 팔레트 결합 사용하기
              • 타이트한 팔레트 결합 사용하기
            • 파라체인 개발
              • HRMP 채널 추가하기
              • 로컬 파라체인 노드 추가하기
              • 릴레이 체인에 연결하기
              • 솔로 체인을 변환하기
              • 론칭 준비
              • 콜레이터 선택
              • 파라체인 업그레이드
            • 스토리지 마이그레이션
              • 기본 저장소 마이그레이션
              • 스토리지 마이그레이션 트리거
            • 테스트
              • 기본 테스트 설정하기
              • 전송 함수 테스트하기
            • 도구
              • 체인을 위한 txwrapper 생성
              • REST 엔드포인트를 사용하여 체인 데이터 가져오기
              • try-runtime 사용하기
              • Wasm 바이너리 검증하기
            • 가중치
              • 벤치마크 추가
              • 수수료 계산하기
              • 조건부 가중치 사용
              • 사용자 정의 가중치 사용하기
        • 빌드하기
          • 제작할 것을 결정하세요
          • 빌드 프로세스
          • 결정론적 런타임 빌드
          • 체인 스펙
          • Genesis 구성
          • 애플리케이션 개발
          • RPC
          • 문제 해결
        • 튜토리얼
          • 설치하기
            • 개발자 도구
            • 리눅스 개발 환경
            • macOS 개발 환경
            • Rust 툴체인
            • Rust 문제 해결 방법
            • Windows 개발 환경
          • 빠른 시작
            • 코드 탐색하기
            • 런타임 수정하기
            • 노드 시작하기
            • Substrate 한눈에 보기
          • 블록체인 구축
            • 신뢰할 수 있는 노드 추가
            • 특정 노드 승인
            • 로컬 블록체인 구축하기
            • 네트워크 시뮬레이션
            • 실행 중인 네트워크 업그레이드
          • 애플리케이션 로직 구축
            • 런타임에 팔레트 추가하기
            • 오프체인 워커 추가
            • 사용자 정의 팔레트 게시
            • 함수 호출의 출처 지정하기
            • 사용자 정의 팔레트에서 매크로 사용하기
          • 유용한 도구들
            • EVM 계정에 접근하기
            • 이더리움 통합
            • 사이드카 엔드포인트 탐색하기
            • 경량 클라이언트 노드 통합
          • 스마트 컨트랙트
            • 스마트 컨트랙트
            • 토큰 계약 작성하기
            • 스마트 컨트랙트 개발하기
            • 첫 번째 계약 준비하기
            • 스마트 컨트랙트 문제 해결
            • 값 저장을 위한 맵 사용
      • XCM
        • XCM
        • XCM 형식
    • 서비스 체인
      • 인프라DID
      • 인프라EVM
      • URAuth(Universal Resource Auth)
    • 데브 옵스
      • 체인 빌드
      • 배포
      • 모니터링
    • 튜토리얼
      • 기초
        • 시스템 토큰 관리 프로세스
        • 시스템 토큰을 트랜잭션 수수료로 사용해보기
        • 트랜잭션에 투표 포함 시키기
        • 밸리데이터 보상 받기
      • 구축하기
        • 인프라릴레이체인 구축하기
        • 파라체인 구축하기
        • 메시지 전달 채널 열기
        • XCM을 이용하여 토큰 전송하기
        • Asynchronous Backing 적용하기
      • 테스트
        • 벤치마크
        • 런타임 확인
        • 디버그
        • 테스트 네트워크에서 파라체인 시뮬레이션하기
        • 단위 테스트
      • 서비스체인
        • 인프라DID
          • 구축하기
          • 공개키 추가하기
          • 서비스 엔드포인트 등록하기
          • DID 생성하기
        • 인프라EVM
          • 구축하기
          • EVM에 자금 입금 및 인출하기
          • ERC20 토큰 컨트랙트 배포하기
          • ERC721 토큰 컨트랙트 배포하기
          • ERC1155 토큰 컨트랙트 배포하기
  • 뉴날 데이터 마켓
Powered by GitBook
On this page
  • Mapping 초기화
  • 스마트 컨트랙트에 맵 추가
  • 값 삽입, 업데이트 또는 제거
  • 다음 단계로 넘어가기
  1. 인프라블록체인
  2. 배우기
  3. Substrate
  4. 튜토리얼
  5. 스마트 컨트랙트

값 저장을 위한 맵 사용

맵을 사용하여 데이터를 키-값 쌍으로 저장하세요.

Previous스마트 컨트랙트 문제 해결NextXCM

Last updated 1 year ago

에서는 하나의 숫자 값을 저장하고 검색하는 스마트 컨트랙트를 개발했습니다.

이 튜토리얼에서는 스마트 컨트랙트의 기능을 확장하여 각 사용자마다 하나의 숫자를 관리하는 방법을 설명합니다. 이를 위해 타입을 사용합니다.

ink! 언어는 맵 타입을 제공하여 데이터를 키-값 쌍으로 저장할 수 있게 합니다. 예를 들어, 다음 코드는 사용자를 숫자에 매핑하는 방법을 보여줍니다:

// `Mapping` 타입을 가져옵니다
use ink::storage::Mapping;

#[ink(storage)]
pub struct MyContract {
  // AccountId를 u32에 매핑하는 맵을 저장합니다
  my_map: Mapping<AccountId, u32>,
}

Mapping 데이터 타입을 사용하면 각 키에 대해 고유한 저장 값 인스턴스를 저장할 수 있습니다.

이 튜토리얼에서는 각 AccountId가 하나의 my_map에 매핑되는 키를 나타냅니다.

각 사용자는 자신의 AccountId와 연결된 값을 저장, 증가, 검색할 수 있습니다.

Mapping 초기화

첫 번째 단계는 AccountId와 저장된 값 사이의 매핑을 초기화하는 것입니다.

  • 매핑 키와 해당 키에 매핑된 값을 지정하세요.

다음 예제는 Mapping을 초기화하고 값을 검색하는 방법을 보여줍니다:

#![cfg_attr(not(feature = "std"), no_std)]

#[ink::contract]
mod mycontract {
    use ink::storage::Mapping;

    #[ink(storage)]
    pub struct MyContract {
        // AccountId를 u32에 매핑하는 맵을 저장합니다
        my_map: Mapping<AccountId, u32>,
    }

    impl MyContract {
        #[ink(constructor)]
        pub fn new(count: u32) -> Self {
            let mut my_map = Mapping::default();
            let caller = Self::env().caller();
            my_map.insert(&caller, &count);

            Self { my_map }
        }

        // 호출자의 AccountId에 연결된 숫자를 가져옵니다 (존재하는 경우)
        #[ink(message)]
        pub fn get(&self) -> u32 {
            let caller = Self::env().caller();
            self.my_map.get(&caller).unwrap_or_default()
        }
    }
}

계약 호출자 식별

앞의 예제에서 Self::env().caller() 함수 호출을 볼 수 있습니다.

이 함수는 계약 로직 전체에서 사용할 수 있으며 항상 계약 호출자를 반환합니다.

계약 호출자는 원래 호출자와 다릅니다.

사용자가 계약에 접근하여 다른 계약을 호출하는 경우, 두 번째 계약의 Self::env().caller()는 첫 번째 계약의 주소가 됩니다.

계약 호출자 사용

계약 호출자를 사용할 수 있는 많은 시나리오가 있습니다.

예를 들어, Self::env().caller()를 사용하여 사용자가 자신의 값에만 액세스할 수 있는 액세스 제어 계층을 만들 수 있습니다.

또한 Self::env().caller()를 사용하여 계약 배포 중에 계약 소유자를 저장할 수 있습니다.

예를 들어:

#![cfg_attr(not(feature = "std"), no_std)]

#[ink::contract]
mod my_contract {

    #[ink(storage)]
    pub struct MyContract {
        // 계약 소유자를 저장합니다
        owner: AccountId,
    }

    impl MyContract {
        #[ink(constructor)]
        pub fn new() -> Self {
            Self {
                owner: Self::env().caller(),
            }
        }
        /* --snip-- */
    }
}

owner 식별자를 사용하여 계약 호출자를 저장했으므로 나중에 현재 계약 호출자가 계약의 소유자인지 확인하는 함수를 작성할 수 있습니다.

스마트 컨트랙트에 맵 추가

이제 incrementer 계약에 저장 맵을 추가할 준비가 되었습니다.

incrementer 계약에 저장 맵을 추가하려면 다음 단계를 수행하세요:

  1. 필요한 경우 컴퓨터에서 터미널 셸을 엽니다.

  2. incrementer 프로젝트 폴더에 있는지 확인하세요.

  3. 텍스트 편집기에서 lib.rs 파일을 엽니다.

  4. Mapping 타입을 가져옵니다.

    #[ink::contract
    mod incrementer {
        use ink::storage::Mapping;
  5. AccountId에서 i32 데이터 타입으로 저장되는 my_map에 매핑 키를 추가합니다.

    pub struct Incrementer {
        value: i32,
        my_map: Mapping<AccountId, i32>,
    }
  6. new 생성자에서 새로운 Mapping을 생성하고 이를 계약에 초기화합니다.

    #[ink(constructor)]
    pub fn new(init_value: i32) -> Self {
        let mut my_map = Mapping::default();
        let caller = Self::env().caller();
        my_map.insert(&caller, &0);
    
        Self {
            value: init_value,
            my_map,
        }
    }
  7. default 생성자에 기존의 기본 value와 함께 새로운 기본 Mapping을 추가합니다.

    #[ink(constructor)]
    pub fn default() -> Self {
    Self {
            value: 0,
            my_map: Mapping::default(),
        }
    }
  8. Mapping API의 get() 메서드를 사용하여 my_map을 읽고 my_map을 계약 호출자에 대해 반환하는 get_mine() 함수를 추가합니다.

    #[ink(message)]
    pub fn get_mine(&self) -> i32 {
         let caller = self.env().caller();
         self.my_map.get(&caller).unwrap_or_default()
    }
  9. 초기 계정을 확인하는 새로운 테스트를 추가합니다.

     #[ink::test]
    fn my_map_works() {
        let contract = Incrementer::new(11);
        assert_eq!(contract.get(), 11);
        assert_eq!(contract.get_mine(), 0);
    }
  10. 변경 사항을 저장하고 파일을 닫습니다.

  11. 다음 명령을 실행하여 test 하위 명령과 nightly 도구 체인을 사용하여 작업을 테스트합니다.

    cargo test

    명령은 다음과 유사한 출력을 표시하여 테스트가 성공적으로 완료되었음을 나타냅니다.

    running 3 tests
    test incrementer::tests::default_works ... ok
    test incrementer::tests::it_works ... ok
    test incrementer::tests::my_map_works ... ok
    
    test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

값 삽입, 업데이트 또는 제거

Incrementer 계약의 마지막 단계는 사용자가 자신의 값을 업데이트할 수 있도록 하는 것입니다.

스마트 컨트랙트에서 이 기능을 제공하기 위해 Mapping API를 호출할 수 있습니다.

Mapping은 저장 항목에 직접 액세스할 수 있습니다.

예를 들어, Mapping::insert()를 호출하여 기존 키에 대한 이전 값이 저장 항목에서 대체될 수 있습니다.

또한 Mapping::get()을 사용하여 저장소에서 값을 읽은 다음 Mapping::insert()로 값을 업데이트할 수 있습니다.

주어진 키에 기존 값이 없는 경우 Mapping::get()은 None을 반환합니다.

Mapping API는 저장소에 직접 액세스할 수 있으므로 Mapping::remove() 메서드를 사용하여 주어진 키의 값을 저장소에서 제거할 수 있습니다.

계약에 삽입 및 제거 기능을 추가하려면 다음 단계를 수행하세요:

  1. 필요한 경우 컴퓨터에서 터미널 셸을 엽니다.

  2. incrementer 프로젝트 폴더에 있는지 확인하세요.

  3. 텍스트 편집기에서 lib.rs 파일을 엽니다.

  4. inc_mine() 함수를 추가합니다. 이 함수는 계약 호출자가 my_map 저장 항목을 가져오고 증가된 value를 매핑에 삽입합니다.

    #[ink(message)]
    pub fn inc_mine(&mut self, by: i32) {
        let caller = self.env().caller();
        let my_value = self.get_mine();
        self.my_map.insert(caller, &(my_value + by));
    }
  5. remove_mine() 함수를 추가합니다. 이 함수는 계약 호출자가 my_map 저장 항목을 저장소에서 제거할 수 있도록 합니다.

    #[ink(message)]
    pub fn remove_mine(&self) {
        let caller = self.env().caller();
        self.my_map.remove(&caller)
    }
  6. inc_mine() 함수가 예상대로 작동하는지 확인하는 새로운 테스트를 추가합니다.

    #[ink::test]
    fn inc_mine_works() {
        let mut contract = Incrementer::new(11);
        assert_eq!(contract.get_mine(), 0);
        contract.inc_mine(5);
        assert_eq!(contract.get_mine(), 5);
        contract.inc_mine(5);
        assert_eq!(contract.get_mine(), 10);
    }
  7. remove_mine() 함수가 예상대로 작동하는지 확인하는 새로운 테스트를 추가합니다.

    #[ink::test]
    fn remove_mine_works() {
        let mut contract = Incrementer::new(11);
        assert_eq!(contract.get_mine(), 0);
        contract.inc_mine(5);
        assert_eq!(contract.get_mine(), 5);
        contract.remove_mine();
        assert_eq!(contract.get_mine(), 0);
    }
  8. test 하위 명령을 사용하여 작업을 확인합니다.

    cargo test

    명령은 다음과 유사한 출력을 표시하여 테스트가 성공적으로 완료되었음을 나타냅니다.

    running 5 tests
    test incrementer::tests::default_works ... ok
    test incrementer::tests::it_works ... ok
    test incrementer::tests::remove_mine_works ... ok
    test incrementer::tests::inc_mine_works ... ok
    test incrementer::tests::my_map_works ... ok
    
    test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

다음 단계로 넘어가기

이 튜토리얼에서는 ink::storage::Mapping 타입과 Mapping API를 스마트 컨트랙트에서 사용하는 방법을 배웠습니다. 예를 들어, 이 튜토리얼에서는 다음과 같은 내용을 설명했습니다:

  • 키-값 쌍을 저장하기 위해 매핑을 초기화하는 방법.

  • 스마트 컨트랙트에서 계약 호출자를 식별하고 사용하는 방법.

  • 스마트 컨트랙트를 사용하여 사용자가 맵에 저장된 값을 삽입하고 제거하는 함수를 추가하는 방법.

다음 주제에서 스마트 컨트랙트 개발에 대해 더 자세히 알아볼 수 있습니다:

이 튜토리얼의 최종 코드 예제는 의 자산에서 찾을 수 있습니다.

스마트 컨트랙트 개발
Mapping
smart-contracts
ERC20 토큰 계약 빌드
스마트 컨트랙트 문제 해결