반응형

배경


useEffect 를 이용해 한번만 함수를 처리하게 하려고 했다.
하지만 왜인지 모르게 자꾸 렌더링이 두번씩 되어 함수가 두번 실행이 되었다.

 

해결

https://youngble.tistory.com/175

 

[React] useEffect 2번 실행되는 이유

다음과 같이 useEffect를 사용하여 jQuery메서드 on 으로 해당 요소를 이벤트 등록을 해주었다. 그런데 해당 태그를 눌러 이벤트가 발생하면 2번이 실행돼서 왜이러지? 찾아보니깐 useEffect부분에서 두

youngble.tistory.com

다음 글을 보면 index.js 의 React.StrictMode 때문이라고 한다.

 

import React from 'react';

function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode>
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>
  );
}

 

개발모드에서만 작동되는 StrictMode 는 다음과 같은 효과를 준다고한다.

  1. 안전하지 않은 생명주기를 사용하는 컴포넌트 발견
  2. 레거시 문자열 ref 사용에 대한 경고
  3. 권장되지 않는 findDOMNode 사용에 대한 경고
  4. 예상치 못한 부작용 검사
  5. 레거시 context API 검사
  6. Ensuring reusable state

https://ko.legacy.reactjs.org/docs/strict-mode.html

 

Strict 모드 – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

문서에 들어가면 더 자세히 볼 수 있다.

 

결론은 index.js React.StrictMode 를 제거하면 한 번만 호출 된다.
반응형

'Error' 카테고리의 다른 글

[Pyinstaller] How to generate an executable on Linux for Windows?  (2) 2023.06.02
반응형

Layout.tsx

import React, {FC} from "react";
import {Stack, Flex, Box, Text, Button} from '@chakra-ui/react';
import { Link } from "react-router-dom";

const Layout: FC = ({children}) =>{
    return( 
    
    <Stack h="100vh">
        <Flex bg="purple.200" p={4} justifyContent="space-around" alignItems="center">
            <Box>
                <Text fontWeight="bold">h662-Animals</Text>
            </Box>
            <Link to="/">
                <Button size="sm" colorScheme="blue">
                    Main
                </Button>
            </Link>
            <Link to="my-animal">
                <Button size="sm" colorScheme="red">
                    My Animal
                </Button>
            </Link>
            <Link to="sale-animal">
                <Button size="sm" colorScheme="green">
                    Sale Animal
                </Button>
            </Link>
        </Flex>
        <Flex direction="column" h="full" justifyContent="center" alignItems="center">
            {children}
        </Flex>
    </Stack>
    );
}

export default Layout

 

MyAnimalCard.tsx

 

import React , {FC, useState, ChangeEvent} from "react";
import {Box, Text, InputGroup, Input, InputRightAddon, Button} from "@chakra-ui/react"
import AnimalCard from "./AnimalCard";
import { saleAnimalTokenContract, web3 } from "../contracts";

export interface IMyanimalCard{
    animalTokenId : string;
    animalType : string;
    animalPrice : string;
}

interface MyAnimalCardProps extends IMyanimalCard {
    saleStatus:boolean;
    account:string;
}

const MyAnimalCard: FC<MyAnimalCardProps> = ({animalTokenId, animalType, animalPrice, saleStatus, account}) =>{
    const [sellPrice, setSellPrice] = useState<string>("");
    const [myAnimalPrice, setMyAnimalPrice] = useState<string>(animalPrice)

    const onChangeSellPrice = (e: ChangeEvent<HTMLInputElement>) =>{
        setSellPrice(e.target.value);
    }

    const onClickSell = async () =>{
        try{
            if(!account || !saleStatus) return;

            const response = await saleAnimalTokenContract.methods
            .setForSaleAnimalToken(animalTokenId, web3.utils.toWei(sellPrice, "ether"))
            .send({from:account});

            if(response.status) {
                setMyAnimalPrice(web3.utils.toWei(sellPrice, "ether"));
                console.log(response)
            }
        }catch(err){
            console.log(err);
        }
    }

    return(
        <Box textAlign="center" w={150}>
            <AnimalCard animalType={animalType} /><Box  mt={2}>
                {animalPrice === "0" ? (
                <>
                    <InputGroup>
                        <Input type='number' value={sellPrice} onChange={onChangeSellPrice}/>
                        <InputRightAddon children="Matic" />
                    </InputGroup>
                    <Button size="sm" colorScheme="green" mt={2} onClick={onClickSell}>
                        Sell
                    </Button>
                </>) : <Text d="inline-block">{web3.utils.fromWei(myAnimalPrice)} Matic</Text> }
            </Box>
        </Box>
    )
}

export default MyAnimalCard;

함수 설명

  1. onChangeSellPrice = (e: ChangeEvent<HTMLInputElement>)
    • Input 박스 내의 요소가 바뀔때마다 price 를 바꿔준다.
  2. onClickSell
    • 토큰을 판매 등록하는 함수

SaleAnimalCard.tsx

import React, {FC, useState, useEffect} from "react";
import AnimalCard from "./AnimalCard";
import {Box, Text, Button} from "@chakra-ui/react"
import { mintAnimalTokenContract, saleAnimalTokenContract, web3 } from "../contracts";

interface SaleAnimalCardProps{
    animalType:string;
    animalPrice:string;
    animalTokenId: string;
    account:string;
    getOnSaleAnimalTokens: () => Promise<void>;
}

const SaleAnimalCard:FC<SaleAnimalCardProps> =({animalType,animalPrice,animalTokenId, account,getOnSaleAnimalTokens}) =>{
    const [isBuyable, setIsBuyable] = useState<boolean>(false);

    const getAnimalTokenOwner = async () => {
        try{
            const response = await mintAnimalTokenContract.methods.ownerOf(animalTokenId).call();
            
            console.log(response)
            console.log(account);
            setIsBuyable(response.toLocaleLowerCase() === account.toLocaleLowerCase())
        } catch(err){
            console.log(err);
        }   
    } 

    const onClickBuy = async () =>{
        try{
            if(!account) return;
            const response = await saleAnimalTokenContract.methods.purchaseAnimalToken(animalTokenId).send({from: account, value:animalPrice});

            if(response.status){
                getOnSaleAnimalTokens();
            }
        } catch(err){
            console.log(err)
        }
    }

    useEffect(() => {
      
        getAnimalTokenOwner();
    }, [])
    

    return(
        <Box textAlign ="center" w={100}>
            <AnimalCard animalType={animalType} />
            <Box>
                <Text d="inline-block">
                    {web3.utils.fromWei(animalPrice)} Matic
                </Text>
                <Button size="sm" colorScheme="green" m={2} disabled={isBuyable} onClick={onClickBuy}>Buy</Button>
            </Box>
        </Box>
    )
}

export default SaleAnimalCard
반응형
반응형
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "MintAnimalToken.sol";

contract SaleAnimalToken {
    MintAnimalToken public mintAnimalTokenAddress;
    //디플로이 주소값을 담는다.

    constructor (address _mintAnimalTokenAddress) {
        mintAnimalTokenAddress = MintAnimalToken(_mintAnimalTokenAddress);
    }

    mapping(uint256 => uint256) public animalTokenPrices;
    //가격관리 맵핑

    uint256[] public onSaleAnimalTokenArray;
    //FE 에서 사용할 배열. 판매중이 토큰 확인용

    function setForSaleAnimalToken(uint256 _animalTokenId, uint256 _price) public {
        //판매 등록 함수

        address animalTokenOwner = mintAnimalTokenAddress.ownerOf(_animalTokenId);

        require(animalTokenOwner == msg.sender, "Caller is not animal token owner");
        //함수 실행 사람이 토큰의 주인이 맞는지
        require(_price > 0 , "Price is zero or lower");

        require(animalTokenPrices[_animalTokenId] == 0, "This animal token is already on sale.");

        require(mintAnimalTokenAddress.isApprovedForAll(animalTokenOwner, address(this)), "Animal token owner did not approve token.");
        //스마트컨트랙트 판매권한 여부를 확인
        
        animalTokenPrices[_animalTokenId] = _price;

        onSaleAnimalTokenArray.push(_animalTokenId);
    }
}

 

알고리즘

  • MintAnimalToken 장부를 먼저 Deploy 해야한다.
  • Deploy 된 장부의 주소값을 인자로 등록한다.
  • setForSaleAnimalToken 함수
    • 등록할 토큰의 ID 를 가진 주인의 주소를 불러온다.
    • 함수를 실행하는 사람이 msg.sender 가 주인이 맞는지 확인한다.
    • 가격이 0 초과인지 확인한다.
    • 판매등록이 되지 않은 상품인지 확인한다.
    • 해당 장부가 판매등록이 허가가 된 장부인지 확인한다.
    • 배열에 판매등록을 한다.

 

function purchaseAnimalToken(uint256 _animalTokenId) public payable {
        //payble 을 붙여야 matic 이 왔다갔다하는 함수를 실행할 수 있다.

        uint256 price = animalTokenPrices[_animalTokenId];
        address animalTokenOwner = mintAnimalTokenAddress.ownerOf(_animalTokenId);

        require(price > 0, "Animal Token not sale.");
        require(price <= msg.value, "Caller sent lower than price.");
        //함수를 실행할때 보내는 MATIC 의 양이 같거나 큰지 확인
        require(animalTokenOwner != msg.sender, "Caller is animal Token owner");

        payable(animalTokenOwner).transfer(msg.value);
        //가격만큼의 양이 돈의 주인으로 보내진다.

        mintAnimalTokenAddress.safeTransferFrom(animalTokenOwner, msg.sender, _animalTokenId);

        animalTokenPrices[_animalTokenId] = 0;

        for(uint256 i = 0; i<onSaleAnimalTokenArray.length;i++){
            if(animalTokenPrices[onSaleAnimalTokenArray[i]] == 0){
                onSaleAnimalTokenArray[i] = onSaleAnimalTokenArray[onSaleAnimalTokenArray.length -1];
                onSaleAnimalTokenArray.pop();
                //맨뒤랑 바꿔서 맨뒤 삭제
            }
        }
        
        
    }
    //읽기 전용 FE 함수
    //판매중인 리스트 확인용
    function getOnSaleAnimalTokenArrayLength() view public returns (uint256){
            return onSaleAnimalTokenArray.length;
    }

 

알고리즘

  • payable 을 사용해야 구입 판매 함수를 사용할 수 있다.
  • 구매할 토큰의 ID 와 토큰 주인의 주소를 불러온다.
  • 토큰의 판매여부를 확인
  • 구매자가 보낸 금액이 토큰 가격보다 크거나 같은지 확인
  • 구매자가 판매자와 동일한지 확인
  • payable 함수를 이용해 msg.value 에 저장된 금액을 판매자로 송금
  • 판매된 토큰을 배열에서 제거

후기

  • Solidity 언어를 많이 다룰줄 알았는데 아닌것같다. 그래도 대충 하는 법을 익힌듯 해서 얼른 만들어버리고 다른 강의 들어야겠다.

 

전체 알고리즘

MintAnimal Token Deploy
mint 주소를 이용해 Sale 장부 실행
Animal 민팅
Sale 장부 허가
판매 등록후 확인
다른 계정으로 구매후 구매확인

반응형

'BlockChain > Solidity' 카테고리의 다른 글

[DP/NFT] 4. 리액트 세팅  (0) 2022.09.05
[DP/NFT] 2. Minting contract 작성  (0) 2022.09.05
[DP/NFT] 1. Install Solidity & Metamask  (0) 2022.08.25

+ Recent posts