728x90
안녕하세요, 오늘은 제가 BNB 테스트넷에서 작동하는 DApp 프로젝트 BNB.Fun을 개발하며 겪은 과정을 공유하려고 합니다. 이 프로젝트는 토큰 발행부터 프리세일(구매/환불) 기능까지 구현하는 것을 목표로 했고, 여러 시행착오 끝에 성공적으로 완성했습니다. 그 여정을 정리해 보겠습니다.
 
지난번까지 개발한 내용은 React(프론트앤드) <ㅡ> 백앤드(컨트랙트) 연동하여
웹 사이트(localhost)에서 토큰을 발행하고 웹사이트에 발행한 토큰들이 웹에 표시되는것까지
구현 완료했습니다.
 

Step 1: 컨트랙트 코드(Solidity)

먼저, TokenFactory.sol, CustomToken.sol, Presale.sol 세 개의 컨트랙트를 작성하고 배포했습니다.
  • TokenFactory:
    • createToken(name, symbol): 0.01 BNB를 받고 CustomTokenPresale 인스턴스를 생성.
    • withdraw(): 컨트랙트에 쌓인 BNB를 소유자에게 전송가능하도록 구현 
    • 배포 주소: 0x7E5b012c635d9bB9c19aAD3E37F059C4410E3AA1. (bsc testnet)
     
  • CustomToken:
    • ERC-20 기반 토큰, 초기 공급량은 Presale에 할당.
    • 예: 0x173ACc12fd867f2bee4bECB1e05a2e2fB106f1Ee.
  • Presale:
    • buyTokens(): BNB로 토큰 구매.
    • refund(): 투자한 BNB 환불 및 토큰 반환.
    • 예: 0x790fc6bB52B34488D9b6a9630e7DcC516d47c8a6.

 

Step 2: 프론트엔드 구현

 
React 프로젝트를 생성하고, 주요 컴포넌트를 구현했습니다.
  • App.js:
    • MetaMask 연결 및 tokens 상태 관리.
    • 로컬 스토리지로 tokens 유지 (새로고침 시 초기화 방지).
  • CreateTokenModal.js:
    • 토큰 이름, 심볼 입력 후 TokenFactory.createToken 호출.
    • 발행된 tokenAddresspresaleAddresstokens에 추가.
  • PresalePage.js:
    • URL 파라미터(tokenAddress)로 토큰 식별.
    • tokens에서 presaleAddress 찾아 프리세일 데이터 로드.
    • "Buy Now"와 "Refund" 버튼으로 구매/환불 기능 제공.

 

 


Step 3: 디버깅과 해결 과정

개발 중 여러 문제를 만났고, 하나씩 해결해 나갔습니다.
  1. "Transaction execution reverted":
    • 원인1 : 가스 한도 부족 또는 잔액 문제 / 
    • 해결: gasLimit: 3000000으로 증가, 계정에 0.1 BNB 충전.
    • 원인2. :  CustomTokenPresale 주소를 필요로 하는데, PresaleCustomToken 주소를 필요로 함 → 순서 문제로 인해 CustomToken의 초기 공급량이 잘못된 주소(address(0))로 민팅되거나, 이후 Presale에서 토큰을 다룰 때 오류 발생.
    • 해결: Presale을 먼저 만들어 presaleAddress를 확보한 뒤, 이를 CustomToken에 전달 → CustomToken이 올바른 Presale 주소에 민팅하고, Presale은 이후 업데이트로 토큰을 인식.
      *이 문제로 진짜 몇일동안 뭐가 문제인지 고민함...(백업하고 구현하고 문제발생되고 다시 백업하고 구현하고 문제 생기고.. 반복)
  2. "Presale address not found in local token list":
    • 원인: PresalePagetokens에서 데이터를 찾지 못함.
    • 해결: URL을 최신 토큰 주소로 수정, tokens 상태를 로컬 스토리지로 유지.
  3. "limit exceeded" (eth_getLogs):
    • 원인: TokenFactory 이벤트 조회 시 블록 범위 초과.
    • 해결: eth_getLogs 대신 tokens 상태만 사용.

Step 4: 최종 테스트

최신 토큰 발행 후 프리세일 페이지 접속
  • URL: http://localhost:3000/presale/0x173ACc12fd867f2bee4bECB1e05a2e2fB106f1Ee.
  • 결과:
    • "Presale Details"에 현재 비율, 모금액 표시.
    • "Buy Now"로 0.01 BNB 구매 성공.
    • "Refund"로 환불 성공.
    •  
 
 
 

주요코드(Solidity)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "./customToken.sol";
import "./Presale.sol";

contract TokenFactory is Ownable {
    uint256 public constant CREATE_FEE = 0.01 ether;

    event TokenCreated(address tokenAddress, address presaleAddress);

    constructor() Ownable(msg.sender) {}

    function createToken(string memory name, string memory symbol) external payable returns (address, address) {
        require(msg.value == CREATE_FEE, "Must send exactly 0.01 BNB");

        // Presale 먼저 생성
        Presale newPresale = new Presale(IERC20(address(0)));
        address presaleAddress = address(newPresale);

        // CustomToken 생성 및 Presale로 토큰 전송
        CustomToken newToken = new CustomToken(name, symbol, presaleAddress);
        address tokenAddress = address(newToken);

        // Presale에 토큰 설정 및 소유권 이전
        newPresale.updateToken(IERC20(tokenAddress));
        newPresale.transferOwnership(msg.sender);

        emit TokenCreated(tokenAddress, presaleAddress);
        return (tokenAddress, presaleAddress);
    }

    function withdraw() external onlyOwner {
        payable(owner()).transfer(address(this).balance);
    }
}

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

interface IPancakeSwapRouter {
    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);
}

contract Presale is Ownable, ReentrancyGuard {
    IERC20 public token;
    uint256 public constant INITIAL_RATE = 10_000_000 * 10**18; // 1 BNB = 1,000만 토큰
    uint256 public constant PRICE_INCREASE = 10; // 10% 증가
    uint256 public constant INCREASE_INTERVAL = 1 hours; // 1시간마다
    uint256 public constant TARGET_BNB = 5 ether; // 5 BNB 목표
    uint256 public constant LIQUIDITY_TOKENS = 50_000_000 * 10**18; // 5천만 토큰

    uint256 public startTime;
    uint256 public totalBNB;
    bool public liquidityAdded;

    mapping(address => uint256) public contributions; // 투자 BNB
    mapping(address => uint256) public tokenBalances; // 구매 토큰

    IPancakeSwapRouter public pancakeSwapRouter;
    address public constant PANCAKESWAP_ROUTER = 0xD99D1c33F9fC3444f8101754aBC46c52416550D1; // BSC Testnet 라우터

    event TokensPurchased(address buyer, uint256 bnbAmount, uint256 tokenAmount);
    event Refunded(address user, uint256 bnbAmount);
    event LiquidityAdded(uint256 tokenAmount, uint256 bnbAmount);

    constructor(IERC20 _token) Ownable(msg.sender) {
        token = _token;
        pancakeSwapRouter = IPancakeSwapRouter(PANCAKESWAP_ROUTER);
        startTime = block.timestamp;
    }

    function getCurrentRate() public view returns (uint256) {
        uint256 intervals = (block.timestamp - startTime) / INCREASE_INTERVAL;
        return INITIAL_RATE - (INITIAL_RATE * PRICE_INCREASE * intervals) / 100; // 토큰 수 감소
    }

    function buyTokens() external payable nonReentrant {
        require(!liquidityAdded, "Presale ended");
        require(msg.value > 0, "Must send BNB");

        uint256 rate = getCurrentRate();
        uint256 tokenAmount = (msg.value * rate) / 1 ether;
        require(token.balanceOf(address(this)) >= tokenAmount, "Not enough tokens");

        contributions[msg.sender] += msg.value;
        tokenBalances[msg.sender] += tokenAmount;
        totalBNB += msg.value;

        token.transfer(msg.sender, tokenAmount);
        emit TokensPurchased(msg.sender, msg.value, tokenAmount);

        if (totalBNB >= TARGET_BNB) {
            addLiquidity();
        }
    }

    function refund() external nonReentrant {
        require(!liquidityAdded, "Presale ended");
        uint256 bnbAmount = contributions[msg.sender];
        require(bnbAmount > 0, "No contribution");

        uint256 refundAmount = (bnbAmount * 90) / 100;
        uint256 tokenAmount = tokenBalances[msg.sender];

        contributions[msg.sender] = 0;
        tokenBalances[msg.sender] = 0;
        totalBNB -= bnbAmount;

        require(token.transferFrom(msg.sender, address(this), tokenAmount), "Token transfer failed");
        payable(msg.sender).transfer(refundAmount);
        emit Refunded(msg.sender, refundAmount);
    }

    function addLiquidity() internal {
        require(!liquidityAdded, "Liquidity already added");
        liquidityAdded = true;

        token.approve(address(pancakeSwapRouter), LIQUIDITY_TOKENS);
        pancakeSwapRouter.addLiquidityETH{value: TARGET_BNB}(
            address(token),
            LIQUIDITY_TOKENS,
            LIQUIDITY_TOKENS * 90 / 100, // 10% 슬리피지 허용
            TARGET_BNB * 90 / 100,
            address(this),
            block.timestamp + 15 minutes
        );

        emit LiquidityAdded(LIQUIDITY_TOKENS, TARGET_BNB);

        uint256 remainingTokens = token.balanceOf(address(this));
        if (remainingTokens > 0) {
            token.transfer(owner(), remainingTokens);
        }
    }

    function withdrawBNB() external onlyOwner {
        require(liquidityAdded, "Presale not ended");
        payable(owner()).transfer(address(this).balance);
    }

    function updateToken(IERC20 _token) external onlyOwner {
        require(address(token) == address(0), "Token already set");
        token = _token;
    }
}
 
 
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract CustomToken is ERC20, Ownable {
    constructor(
        string memory name,
        string memory symbol,
        address presaleAddress
    ) ERC20(name, symbol) Ownable(msg.sender) {
        uint256 initialSupply = 100_000_000 * 10**18; // 1억 토큰
        _mint(address(this), initialSupply);
        if (presaleAddress != address(0)) {
            _transfer(address(this), presaleAddress, initialSupply);
        }
    }
}
 
 

 
<마무리>
 
BSC 테스트넷의 JSON-RPC 제한 문제를 해결하면서 많은 걸 배웠습니다. 이 DApp은 토큰 발행과 프리세일을 간단히 경험할 수 있는 좋은 시작점이 될 거라 생각합니다.
다음 목표는 차트 데이터를 실시간으로 반영하고, 프리세일이 5BNB(테스트목적으로 0.5BNB)가 프리세일로 모금이 되면 팬케이크 스왑에 자동으로 유동성이 공급되는것을 구현 및 테스트 할 예정입니다. 
 
 
<잡담>
.env파일에 자신의 메타마스크 개인key를 입력해놓는데, 실수로 github에 .env도 같이올렷다가 1만원(bnb) 털렸습니다 ㅎㅎ 신기해서 
 
다른지갑에 5000원(bnb)입금하고 깃허브에 올렸더니 10초만에 털렸습니다. 아무튼 개인key는 절대 어디에 올리지 마세요

 

728x90
728x90
잡담
최종 프로젝트 형태는 Pump.fun과 비슷한 Dapp을 배포하는것입니다! 저만의 차별성이 있기도하고 아이디어가 번뜩 떠올라서 구현해보려고합니다. 100개 프로젝트 만들다보면 한개쯤 대박 터지겠죠? 1달에 1개 프로젝트를 만들면 8년안에 pump.fun같은 대박서비스가 개발될수도.... 지금 저는 군대(육군장교)인데 진짜 주간에는 
작업하고, 훈련계획짜고, 용사들 챙기고 훈련뛰고나면 개발하기가 힘들지만... 다시 1일1코딩 도전합니다.!
 
 
 
 
 
 
 

프로젝트 개요

BNB Chain Testnet에서 스마트 컨트랙트를 배포하고, 프론트엔드와 연동해서 토큰 생성 및 프리세일 기능을 구현한 과정을 공유하려고 합니다. Hardhat, React, Ethers.js를 사용했으며, 단계별로 자세히 정리해봤습니다.

 
이 프로젝트(실습)는 유저가 0.01 BNB(test net bnb)를 지불하면 새로운 ERC-20 토큰을 생성하고, 생성된 토큰을 프리세일 컨트랙트로 전송한 뒤, 프리세일에서 유저가 토큰을 구매할 수 있는 DApp을 만드는 것을 목표로 했습니다. 주요 구성 요소는 다음과 같습니다:
  • TokenFactory 컨트랙트: 새로운 토큰을 생성하고 프리세일 컨트랙트로 전송.
  • Presale 컨트랙트: 프리세일 진행 및 유동성 풀 추가.
  • 프론트엔드: React로 구현, 유저가 토큰 생성 및 프리세일에 참여 가능.        * ui는 Tailwind Css사용해서 업+Pump.fun과 비슷하게 구현할겁니다! 

개발 환경

  • 블록체인 네트워크: BNB Chain Testnet
  • 스마트 컨트랙트 개발: Hardhat, Solidity
  • 프론트엔드: React, Ethers.js
  • 지갑: MetaMask

구현 단계

1. 스마트 컨트랙트 작성
1.1 TokenFactory 컨트랙트
TokenFactory 컨트랙트는 유저가 0.01 BNB를 지불하면 새로운 ERC-20 토큰을 생성하고, 발행된 토큰을 Presale 컨트랙트로 전송합니다.
contracts/TokenFactory.sol:solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract TokenFactory is Ownable {
    uint256 public constant CREATE_FEE = 0.01 ether;
    address public presaleContract;

    event TokenCreated(address tokenAddress, string name, string symbol);

    constructor(address _presaleContract) Ownable(msg.sender) {
        require(_presaleContract != address(0), "Presale contract address cannot be zero");
        presaleContract = _presaleContract;
    }

    function createToken(string memory name, string memory symbol) external payable {
        require(msg.value == CREATE_FEE, "Must send 0.01 BNB to create token");
        require(bytes(name).length > 0, "Token name cannot be empty");
        require(bytes(symbol).length > 0, "Token symbol cannot be empty");

        CustomToken newToken = new CustomToken(name, symbol, 100_000_000 * 10**18);
        require(newToken.transfer(presaleContract, 100_000_000 * 10**18), "Token transfer failed");

        emit TokenCreated(address(newToken), name, symbol);
    }

    function withdraw() external onlyOwner {
        payable(owner()).transfer(address(this).balance);
    }
}

contract CustomToken is ERC20 {
    constructor(string memory name, string memory symbol, uint256 initialSupply) 
        ERC20(name, symbol) 
    {
        _mint(msg.sender, initialSupply);
    }
}
1.2 Presale 컨트랙트
Presale 컨트랙트는 유저가 BNB를 보내서 토큰을 구매할 수 있도록 하고, 목표 BNB(5 BNB)에 도달하면 PancakeSwap에 유동성을 추가합니다.
contracts/Presale.sol (일부 생략):solidity
 
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract Presale is Ownable {
    IERC20 public token;
    uint256 public constant INITIAL_RATE = 10_000_000;
    uint256 public constant TARGET_BNB = 5 ether;

    uint256 public totalBNB;
    mapping(address => uint256) public contributions;
    mapping(address => uint256) public tokenBalances;

    constructor(IERC20 _token) Ownable(msg.sender) {
        token = _token;
    }

    function buyTokens() external payable {
        uint256 tokenAmount = msg.value * INITIAL_RATE;
        require(token.balanceOf(address(this)) >= tokenAmount, "Not enough tokens");

        contributions[msg.sender] += msg.value;
        tokenBalances[msg.sender] += tokenAmount;
        totalBNB += msg.value;
    }
}
2. 스마트 컨트랙트 배포
Hardhat을 사용해 BNB Chain Testnet에 컨트랙트를 배포했습니다.
scripts/deploy.js:javascript
 
const hre = require("hardhat");

async function main() {
  const [deployer] = await hre.ethers.getSigners();
  console.log("Deploying contracts with the account:", deployer.address);

  const CustomToken = await hre.ethers.getContractFactory("CustomToken");
  const testToken = await CustomToken.deploy("Test Token", "TST", ethers.parseEther("100000000"));
  await testToken.waitForDeployment();
  console.log("Test Token deployed to:", await testToken.getAddress());

  const Presale = await hre.ethers.getContractFactory("Presale");
  const presale = await Presale.deploy(await testToken.getAddress());
  await presale.waitForDeployment();
  console.log("Presale deployed to:", await presale.getAddress());

  const TokenFactory = await hre.ethers.getContractFactory("TokenFactory");
  const tokenFactory = await TokenFactory.deploy(await presale.getAddress());
  await tokenFactory.waitForDeployment();
  console.log("TokenFactory deployed to:", await tokenFactory.getAddress());
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
배포 명령어:bash
npx hardhat run scripts/deploy.js --network bscTestnet
배포 결과:(테스트넷)
  • Test Token: 0x8721C944f7Eb09F81FA12527ee6f7eea4A121696
  • Presale: 0xDD95883d286150DB9781242239C2197E22305A80
  • TokenFactory: 0xd9d2231675C43a4751AE5d262Ee54bc565C50d4f
3. 프론트엔드 구현
React와 Ethers.js를 사용해 프론트엔드를 구현했습니다. 유저가 토큰을 생성할 수 있는 모달을 만들었어요.
3.1 토큰 생성 모달
frontend/src/components/CreateTokenModal.js (간략화된 버전):
javascript
 
import React, { useState } from "react";
import { ethers } from "ethers";
import TokenFactoryABI from "../abis/TokenFactory.json";

const TOKEN_FACTORY_ADDRESS = "0xd9d2231675C43a4751AE5d262Ee54bc565C50d4f";

const CreateTokenModal = ({ onClose, onCreate, signer }) => {
  const [name, setName] = useState("");
  const [symbol, setSymbol] = useState("");
  const [error, setError] = useState("");

  const handleSubmit = async () => {
    if (!signer) {
      setError("Please connect your wallet first.");
      return;
    }

    try {
      const tokenFactory = new ethers.Contract(
        TOKEN_FACTORY_ADDRESS,
        TokenFactoryABI,
        signer
      );
      const tx = await tokenFactory.createToken(name, symbol, {
        value: ethers.parseEther("0.01"),
        gasLimit: 1000000,
      });
      await tx.wait();
      onCreate({ name, symbol });
      onClose();
    } catch (err) {
      setError("Transaction failed: " + err.message);
    }
  };

  return (
    <div className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center">
      <div className="bg-gray-800 p-6 rounded-lg w-full max-w-md shadow-xl">
        <h2 className="text-xl font-semibold mb-4">Create New Token</h2>
        {error && <div className="mb-4 p-2 bg-red-600 text-white rounded">{error}</div>}
        <div className="mb-4">
          <label className="block mb-1 text-gray-300">Token Name</label>
          <input
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
            className="w-full p-2 bg-gray-700 rounded border border-gray-600"
            placeholder="My Token"
          />
        </div>
        <div className="mb-4">
          <label className="block mb-1 text-gray-300">Token Symbol</label>
          <input
            type="text"
            value={symbol}
            onChange={(e) => setSymbol(e.target.value)}
            className="w-full p-2 bg-gray-700 rounded border border-gray-600"
            placeholder="MTK"
          />
        </div>
        <div className="flex justify-end space-x-3">
          <button onClick={onClose} className="bg-gray-600 px-4 py-2 rounded-lg">
            Cancel
          </button>
          <button onClick={handleSubmit} className="bg-blue-600 px-4 py-2 rounded-lg">
            Create (0.01 BNB)
          </button>
        </div>
      </div>
    </div>
  );
};

export default CreateTokenModal;
3.2 테스트
프론트엔드를 실행하고, MetaMask를 연결한 뒤 토큰 생성을 테스트했습니다.
명령어:
bash
cd frontend
npm start
  • "Create Token" 버튼 클릭 → 이름: "MyToken", 심볼: "MTK" 입력.
  • 0.01 BNB 지불 → 트랜잭션 성공 → 토큰 생성 완료.
4. 토큰 소유자 확인
TokenFactory에서 생성된 토큰은 Presale 컨트랙트로 전송됩니다. 따라서 현재 1억 토큰은 Presale 컨트랙트(0xDD95883d286150DB9781242239C2197E22305A80)가 소유하고 있습니다.
마무리
오늘은 BNB Chain Testnet에서 토큰 생성 및 프리세일 DApp의 기본 기능을 구현해봤습니다. 몇 가지 에러(예: 트랜잭션 리버트, 가스 한도 문제)를 해결하면서 많은 것을 배웠어요. 다음 단계로는 프리세일 페이지에서 유저가 토큰을 구매할 수 있도록 기능을 추가할 예정입니다.
728x90
728x90

블록체인이란?


 

블록체인이란 제 3자 없이도 데이터의 정합성을 수학적으로 담보할 수 있는(위·변조가 불가능한) 분산화된 공개 거래장부입니다. 분산 데이터베이스 기술로 구성되어 있으며, 합의 알고리즘에 따라 다수의 참여자 투표를 거처 의사결정을 진행합니다. 또한 해시값으로 구성된 블록들이 체인으로 연결되어 있으므로 데이터를 위·변조 하는 것은 거의 불가능에 가깝습니다.

 

블록체인의 특징


탈중앙화
  • 블록체인의 기본적인 특징으로, 중앙화 되어 있는 집단이 없고 '제 3자 신뢰 기관'이 존재하지 않습니다.
  • 블록체인은 다수의 참가자가 데이터를 저장하고, 증명하기 때문에 중앙 관리자가 존재하지 않습니다.
  • 중앙 관리자 대신에 '스마트 컨트랙트' 라는 전자계약을 통해 개인 간 거래가 가능합니다.
확장성
  • 공개된 소스로 인해 네트워크 참여자는 누구나 구축하고 연결 또는 확장할 수 있습니다.
  • 네트워크를 통해서 데이터 공유가 필요한 시스템 간의 확장이 쉽고, API를 통한 기존 시스템과의 확대도 고려할 수 있습니다.
투명성
  • 블록체인에 연결된 모든 거래 기록은 공개적으로 접근할 수 있습니다.
  • 거래 양성화 및 보안, 감독, 규제 비용 절감이 가능합니다.
무결성
  • 데이터가 블록에 담기고 그 블록이 체인에 연결되면 데이터를 변경하는 것은 사실상 불가능합니다.
  • 블록체인에 연결된 블록들은 암호학적으로 이전 블록과 특정한 관계를 맺고 있어, 변경하고자 하는 블록 이전의 모든 블록을 변경해야만 하기 때문입니다.

블록체인 핵심 구성 요소

1. 분산 원장

분산 원장은 블록체인에서 발생하는 모든 내역이 서로 공유되어 저장되는 데이터베이스입니다. 기존 데이터베이스에서는 데이터를 등록한 후 수정, 삭제할 수 있었지만, 블록체인에서는 한번 기록된 내역은 삭제할 수 없습니다.

 
2. 스마트 컨트랙트

스마트 컨트랙트는 제 3의 기관 없이 코드로만 계약이 이루어질 수 있도록 하는 기술입니다. 기존 비트코인에서는 입출금 내역(UTXO: Unspent Transaction Outputs)만으로 동작하였기 때문에 단순 거래만 가능할 뿐 계약이 불가능했습니다. 하지만 스마트 컨트랙트가 등장하면서 거래뿐만 아니라 계약을 기반으로 다양한 코드가 생성됨에 따라 블록체인 생태계가 확장될 수 있었습니다.

 
3. PKI

PKI (Public Key Infrastructure)는 보통 높은 수준의 신원 인증 시스템이라고 생각합니다. 하지만, 인증 기관 (Certificate Authority)에 따른 중앙집권적 특성으로 인해 종종 데이터 소유권 및 신뢰성에 대한 문제가 발생합니다. 그 결과, 많은 사람은 인증 기관을 신뢰할 수밖에 없습니다.

블록체인에서는 인증 기관이 존재하지 않습니다. 프라이빗 키는 사용자가 보관하고, 퍼블릭 키는 블록체인에 저장함으로써 거래할 때사용자가 소유하고 있는 프라이빗 키로 서명하고 블록체인에서는 해당 서명이 유효한지 판단합니다.

 

1 블록체인 (Blockchain) 데이터를 블록 단위로 묶어 체인 형태로 연결한 분산 원장 기술입니다. 각 블록은 이전 블록과 암호학적으로 연결되어 데이터의 무결성과 투명성을 보장합니다.
2 퍼블릭 블록체인 (Public Blockchain) 누구나 참여할 수 있는 공개형 블록체인으로, 탈중앙화된 네트워크에서 모든 거래 내역이 투명하게 공개됩니다.
3 프라이빗 블록체인 (Private Blockchain) 특정 조직이나 그룹 내에서만 접근 가능한 폐쇄형 블록체인으로, 접근 권한이 제한되어 있습니다.
4 컨소시엄 블록체인 (Consortium Blockchain) 미리 선정된 노드에 의해 제어되는 반중앙화된 블록체인으로, 퍼블릭과 프라이빗 블록체인의 중간 형태입니다.
5 블록 (Block) 유효한 거래 정보를 포함하는 데이터 묶음으로, 블록체인의 기본 구성 단위입니다.
6 머클 트리 (Merkle Tree) 블록에 포함된 거래의 해시 값을 트리 형태로 구성한 구조로, 데이터의 무결성을 효율적으로 검증할 수 있습니다.
7 머클 루트 (Merkle Root) 머클 트리의 최상위 노드로, 블록에 포함된 모든 거래의 요약본을 나타냅니다.
8 제네시스 블록 (Genesis Block) 블록체인 네트워크에서 최초로 생성된 블록으로, 블록 높이 0에 해당합니다.
9 블록 높이 (Block Height) 제네시스 블록으로부터 현재 블록까지의 순서를 나타내는 지표입니다.
10 블록 헤더 (Block Header) 블록의 메타데이터를 포함하는 부분으로, 버전, 이전 블록 해시, 머클 루트, 타임스탬프, 난이도 목표, 논스 등이 포함됩니다.
11 블록 보상 (Block Reward) 새로운 블록을 생성한 채굴자에게 지급되는 보상으로, 새로운 코인 발행과 거래 수수료로 구성됩니다.
12 블록 타임 (Block Time) 새로운 블록이 생성되는 데 걸리는 평균 시간을 의미합니다. 예를 들어, 비트코인의 블록 타임은 약 10분입니다.
13 블록 탐색기 (Block Explorer) 블록체인 네트워크의 블록, 거래, 주소 등의 정보를 조회할 수 있는 웹 서비스입니다.
14 비트코인 (Bitcoin) 2009년에 사토시 나카모토에 의해 개발된 최초의 분산형 암호화폐입니다.
15 비트코인 코어 (Bitcoin Core) 비트코인 네트워크를 구성하고 거래를 검증하는 공식 소프트웨어입니다.
16 스테이블 코인 (Stablecoin) 법정 화폐나 실물 자산에 연동되어 가격 변동성을 최소화한 암호화폐입니다.
17 이더리움 (Ethereum) 스마트 컨트랙트 기능을 지원하는 블록체인 플랫폼으로, 다양한 분산형 애플리케이션(DApp)을 개발할 수 있습니다.
18 디앱 (DApp) 탈중앙화된 애플리케이션으로, 블록체인 상에서 스마트 컨트랙트를 통해 작동합니다.
19 스마트 컨트랙트 (Smart Contract) 블록체인 상에서 자동으로 실행되는 계약으로, 사전에 정의된 조건이 충족되면 계약이 자동으로 이행됩니다.
20 해시 함수 (Hash Function) 임의의 입력 데이터를 고정된 크기의 해시 값으로 변환하는 함수로, 데이터의 무결성 검증에 사용됩니다.
21 해시레이트 (Hash Rate) 초당 수행되는 해시 계산의 수를 나타내며, 채굴 성능을 평가하는 지표입니다.
22 채굴 (Mining) 블록체인 네트워크에서 거래를 검증하고 새로운 블록을 생성하는 과정으로, 채굴자는 이에 대한 보상을 받습니다.
23 작업 증명 (Proof of Work, PoW) 채굴자가 복잡한 수학 문제를 해결해야만 새로운 블록을 생성할 수 있는 합의 알고리즘입니다.
24 지분 증명 (Proof of Stake, PoS) 코인 보유량에 비례하여 블록 생성 권한을 부여하는 합의 알고리즘입니다.
25 위임 지분 증명 (Delegated Proof of Stake, DPoS) 코인 보유자가 대표자를 선출하고, 선출된 대표자가 블록을 생성하는 합의 알고리즘입니다.
26 합의 알고리즘 (Consensus Algorithm) 분산 네트워크에서 거래의 유효성을 검증하고 블록을 생성하기 위해 노드 간의 합의를 이끄는 프로토콜입니다.
27 노드 (Node) 블록체인 네트워크에 참여하는 개별 컴퓨터나 장치를 의미합니다.
28 풀 노드 (Full Node) 블록체인의 전체 데이터를 저장하고 네트워크의 규칙을 검증하는 노드입니다.
29 라이트 노드 (Light Node) 전체 블록체인을 다운로드하지 않고 필요한 정보만을 조회하는 노드입니다.
30 트랜잭션 (Transaction) 블록체인 네트워크 내에서 자산이나 데이터를 전송하는 작업을 의미합니다.
31 트랜잭션 수수료 (Transaction Fee) 트랜잭션을 처리하기 위해 지불하는 수수료로, 채굴자에게 지급됩니다.
32 가스 (Gas) 이더리움 네트워크에서 트랜잭션을 처리하거나 스마트 컨트랙트를 실행할 때 필요한 수수료 단위입니다.
33 가스 가격 (Gas Price) 가스 단위당 지불하는 가격으로, 트랜잭션의 우선순위에 영향을 미칩니다.
34 가스 한도 (Gas Limit) 트랜잭션이나 블록에서 소비할 수 있는 최대 가스량을 설정하는 값입니다.

 

728x90
728x90

문제 설명
수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.

마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.

제한사항
마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
completion의 길이는 participant의 길이보다 1 작습니다.
참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
참가자 중에는 동명이인이 있을 수 있습니다.

 

def solution(participant, completion):
    hashDict = {}
    print(type(hashDict))
    sumHash = 0
    # 결론 : 참가자목록을 {해시값:"사람이름"}으로 정의하고 참가자목록의 해시값 총합을 더해놓음
    #        참가자_총_해시값 - 완주자_총_해시값 = 미완주자의_해시값
    #        참가자목록[미완주자_해시값] = 미완주자 이름이 나오게됨 
    
    # 1. Hash : Participant의 dictionary 만들기
    # 2. Participant의 sum(hash) 구하기
    
    for part in participant:
        hashDict[hash(part)] = part
        sumHash += hash(part)

    print(hashDict)
    
    # 3. completion의 sum(hash) 빼기
    for comp in completion:
        sumHash -= hash(comp)
    
    # 4. 남은 값이 완주하지 못한 선수의 hash 값이 된다
    return hashDict[sumHash]
728x90

'알고리즘' 카테고리의 다른 글

알고리즘) 퀵 정렬(Quick Sort) 개념과 C구현  (0) 2021.05.19
728x90

버전이 4.x.x일때는 잘 작동되던 코드가 truffle 버전이 5.x.x 되고나서 나타나는 에러이다.

 

해결방법 1. require -> import 로 바꾸면 해결된다.

 

해결방법 2. Truffle 다운그레이드

npm uninstall -g truffle

npm install -g truffle@4.x.x

 

 

It is esm only in 5.x
You can still use a dynamic import to import it in node at least afaik, but should probably just stick to 4.x until you can migrate to esm (feature set shouldn't be much different)

 

https://github.com/chaijs/chai/issues/1561

 

ERR_REQUIRE_ESM in v5 · Issue #1561 · chaijs/chai

Hi there, I don't have all the details, as I don't have access to my workstation right now, but our CI is failing with this error for all our projects using chai when trying an upgrade to chai@5. T...

github.com

 

728x90
728x90

Solidity Docs를 보면서 공부하는데 생소한 문법들이 많이보인다. 아래 2가지정도를 위주로 공부하였다

  • 함수제어자(접근제어,상태제어,사용자 정의제어,payable제어자)
  • 예외처리

 


함수제어자(function modifier) 

위 test함수 뒤에 있는

external , view , customModifier payable 은 모두 함수제어자이다.

 

1.접근제어자 (visibility modifier)

해당 함수가 언제,어디서 호출될 수 있는지 제어한다.

 

접근제어자는 함수를 정의할때 (param) 뒤에 쓰이기도 하고 변수 앞에 쓰이기도 하는데

보통은 (param)뒤에 쓴다.

이 접근제어자는 함수나 변수에 대한 접근을 제한하는 역할을 한다.

접근제어자는 기본적으로 public , private, internal, external 등이 있다. 

 

  • public : 변수나 함수가 어디에서든 접근 가능
  • private : 오직 private이 정의된 Contract 에서만 사용이 가능(private로 정의된 Contract에 get , set함수를 만들어야 접근가능 )
  • internal : 오직 internal이 정의된 contract 안에서, 상속받은 자식 smart contract 에서 접근 가능하다. private과 유사하나 상속받은 자식 contract에서 접근이 가능하다는것이 추가되었다고 보면 된다.
  • external : 오직밖에서만 접근이 가능하다. public과 유사하나 , external이 정의된 contract내에서는 사용이 불가능 

 

2. 상태 제어자(state modifier)

블록체인과 상호작용 하는 방법에 대한 것이다.

 

  • view, constant : 해당 함수를 실행해도 어떤 데이터도 저장/변경되지 않음
  • pure : 해당함수가 어떤 데이터도 블록체인에 저장하지 않을뿐만아니라, 블록체인으로부터 어떤 데이터도 읽지않는다.

 

3. 사용자 정의 제어자

직접 원하는 논리대로 제어자를 정의하여 함수에 적용할 수 있다.

 

// 메시지를 보낸 사람이 owner와 같은지 확인하는 제어자 정의

modifier onlyOwner () {
	require(msg.sender == owner);
  	_; // _;와 항상 함께 쓰인다.
}

 

require 는 예외처리이다. 예외처리는 아래와 같다. 

  • require : 조건이 false 에러를 발생하고 gas를 환불해줌
  • revert : 조건없이 에러를 발생시키고 gas를 환불
  • assert : gas를 다소비한 후 조건이 false일때 에러를 발생 

4. payable 제어자 

함수 호출을 통해 이더리움을 받을 수있는 특별한 함수 유형을 정의한다.

함수가 실행되는 동시에 컨트랙트 계정(CA)에 돈을 지불할 수 있다. payable을 작성하지 않은 함수에서는 이더를 보낼 수 없다.

 

contract OnlineStore {
  function buySomething() external payable {
    require(msg.value == 0.001 ether);
    transferThing(msg.sender);
  }
}

 

위 코드를 정리해 보면 

1. OnlineStore라는 Contract를 정의하고

2. buySomething()라는 함수를 정의하고 함수제어자로 external(external로 정의된 함수 밖에서만 접근가능)이고 

payable라는 함수제어자가 있다.

3. buySomething()는 msg.value(이 함수를 호출할 떄 보낸 wei(이더)의 양을 나타낸다) 가 0.001이더가 아닐 경우 종료한다. 참일경우(value == 0.001eth) 이더를 전송한다.


 

msg 는 솔리디티에서 제공하는 전역변수로서 현재 함수호출에 대한 정보를 담고있다. 

msg.sender 현재 함수를 호출한 주소를 나타냅니다.
msg.value 이더리움 네트워크에서, 이 함수를 호출할 때 보낸 wei의 양을 나타냅니다.
msg.data 함수를 호출할 때 보낸 데이터를 나타냅니다.
msg.gas 함수를 호출할 때 사용할 수 있는 가스의 양을 나타냅니다. (v0.4.21 이후로는 더 이상 사용되지 않습니다.)
msg.sig 함수를 호출할 때 사용한 함수 서명을 나타냅니다.
728x90
728x90

 

Solidity는 이더리움 가상머신을 대상으로 설계된 언어이며 Smart Contrat  을 구현하기 위한 언어이다.

Solidity을 이해하려면 Smart Contrat 이 무엇인지 먼저 알아야되고, Smart Contrat  을 이해하기 위해서는 블록체인이 뭔지 알아야된다. (추가로 EVM 까지...)

 

도움받은 자료(블록체인과 스마트컨트랙을 이해하는데 도움이 됨. 본인은 사실 코인투자를 해왔기에 이미 두루뭉실하게 알고있었음) 

1.  https://www.dbpia.co.kr/journal/articleDetail?nodeId=NODE10531916

 

블록체인 기술과 활용 연구 | DBpia

이새봄, 박아름, 송재민 | 한국컴퓨터정보학회논문지 | 2021.2

www.dbpia.co.kr

2. https://goldsystem.tistory.com/347

 

스마트컨트랙 제대로 이해하기

정의 스마트 계약 또는 스마트 컨트랙이란 블록체인 기반으로 금융거래, 부동산 계약, 공증 등 다양한 형태의 계약을 체결하고 이행하는 것을 말합니다. 블록체인 2.0이라고도 합니다. Hashgraph에

goldsystem.tistory.com


Solidity 언어 특징

  • 절차적 프로그래밍 언어(C++ , 자바스크립트, 자바)
  • 정적타입 (자바와 비슷) : 컴파일 시에 타입이 결정, 오류가 있다면 컴파일 에러 발생
  • 객체 지향언어
  • 확장자 : .sol 

예제 코드 

실습환경 : https://remix.ethereum.org/ 

Compiler : 0.8.24

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

SPDX-LIcense-Identifier

스마트 컨트랙트에 대한 신뢰를 높이고, 저작권 문제를 해소하기 위해 코드 최상단에 꼭 SPDX라이센스를 주석으로

명시해야된다. 

https://spdx.org/licenses/

 

SPDX License List | Software Package Data Exchange (SPDX)

SPDX License List The SPDX License List is an integral part of the SPDX Specification. The SPDX License List itself is a list of commonly found licenses and exceptions used in free and open or collaborative software, data, hardware, or documentation. The S

spdx.org

 

SPDX(Software Packgae Data Exchange)는 소프트웨어 명세서에 대한 개방형 표준

SPDX는 소프트웨어와 관련된 구성요소,라이센스,저작권,보안 참조 및 기타 메타데이터의 표현을 허용하며

원래목적은 라이센스 준수를 개선하는 것이었고 이후 공급망 투명성과 같은 추가 사용사례를 촉진하기 위해 확장됨


Pragma

컴파일러의 특정 기능을 활성화 하는데 사용됨

위 예제코드에서는 solidity 버전을 0.4.16 ~ 0.9.0을 사용한다고 설정 


Contrat

객체지향언어(Java)에서 class와 유사 위 예제코드에서는 Contrat SimpleStorage를 만들고, 이 Contract 안에는

storedData라는 상태변수(storage에 저장되어있는 데이터)와  set과 get의 함수들이 정의되어있다.


상태변수(State Variables)

Contract 내부에서 선언된 변수 중 function 외부에서 선언된 변수를 상태변수(state Variables)라고 함

contract storage에 저장됨 -> 모든 function이 접근가능, function이 변경한 값은 계속해서 저장됨,,

 

1. Storage : 블록체인에 기록되는 영구적으로 값이 유지되는 데이터 영역

2. Memory : 프로그램이 동작되는 동안에만 값이 저장되고, 종료되는 값이 사라짐


값형 데이터 타입 (Value types)

  • Boolean
    true(default)/false
  • 정수(int, uint)
    signed integers: 부호 있음
    unsigned integer: 부호 없음
    8bit ~ 256bit까지 지원 (예: int8: -128 ~ 127, uint16: ~65,535)
  • Byte
    데이터를 바이너리 형태로 저장하기 위해 byte타입을 지원함.
    bytes1 ~ bytes32까지 정해져 있음.
    크기가 정해져 있기 때문에 fixed-sized byte array라고도 불림.
    정해진 바이트 크기와 다르면 에러가 남.
  • cf.) bytes 타입: dynamically sized byte array - 참조형(reference) 타입임
  • 주소(Address)
    20byte 크기의 이더리움 어드레스
    balance property를 갖고 있어 이더 잔액을 확인할 수 있음.
    이더를 옮기는 transfer, send 메소드 지원(address.transfer(amount), address.send(amount))
  • 열거형 (Enumerations)
    내부적으로는 정수 값으로 구현됨.

 

참조

.https://velog.io/@mae-zung/Solidity-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95

 

.https://docs.soliditylang.org/en/v0.8.24/introduction-to-smart-contracts.html#simple-smart-contract

 

.https://goldsystem.tistory.com/347

 

728x90
728x90

이글은 완전 기초적인 CRUD 구현방법에 대해 설명해드린느 글입니다.

 


CRUD란?

📥 Create : 데이터 생성
👀 Read : 데이터 불러오기
✍️ Update : 데이터 수정 / 갱신
📤 Delete : 데이터 삭제하기

 

 

 

<1.시작>

#1.장고설치 
pip install django
#2. 장고 프로젝트만들기
django-admin startproject config .
#3. 데이터베이스DB 생성
python manage.py migrate
#4. 앱생성
python manage.py startapp myapp

 

 

<2.프로젝트 구성>

>

 

3. 소스코드

#myproject/urls.py

from django.contrib import admin
from django.urls import path , include

#사용자가 사이트에 접속했을떄 요청을 어떻게 누가 처리할건지 지정하는 
#라우팅을 해주는 파일 

# https://127.0.0.1/
# https://127.0.0.1/app/
# https://127.0.0.1/create/
# https://127.0.0.1/read/1/
urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('myapp.urls'))
]
#myapp/urls.py

#장고의 urls.py 파일은 페이지 요청이 발생하면 가장 먼저 호출되는 파일로 
#URL과 뷰 함수 간의 매핑을 정의한다. 뷰 함수는 views.py 파일에 정의된 함수를 말한다.
# 본 프로젝트에서는 글생성, 읽기, 삭제, 수정 이 있기때문에 4개의 path를 추가해주었다.

from django.contrib import admin
from django.urls import path , include
from myapp import views

urlpatterns = [
    path('',views.index),
    path('create/',views.create),
    path('read/<id>/',views.read),
    path('delete/' , views.delete),
    path('update/<id>/',views.update)
    
]

 

#myapp/views.py

from django.shortcuts import render, HttpResponse
import random
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import redirect
nextId = 4
topics = [
{'id':1, 'title':'routing', 'body':'Rounting is ..'},
{'id':2, 'title':'view', 'body':'view is ..'},
{'id':3, 'title':'model', 'body':'model is ..'}
]

def HTMLTemplate(articleTag,id=None):
    
    global topics
    ol = ''
    contextUI =''
    if id != None:
        contextUI = f'''
            <li>
                <form action="/delete/" method="post">
                    <input type="hidden" name="id" value={id}>
                    <input type="submit" value="delete">
                </form>
            </li>

            <li>
                <a href="/update/{id}">update</a>
            </li>

    '''

    for topic in topics:
        ol +=  f'<li><a href = "/read/{topic["id"]}">{topic["title"]}</a></li>'
    
    return (f'''
    <html>
        <body>
        <h1><a href="/" > Django</a></h1>
        <ol>
            {ol}
        </ol>
        {articleTag}

        <ul>
            <li><a href="/create/">create</a></li>
            {contextUI}
            
        </ul>
        
        </body>
        </html>
                                      
    ''')

def index(request):
    article = '''
    <h2> Welcome</h2>
    Hello Django
    '''

    return HttpResponse(HTMLTemplate(article))

def read(request,id):
    global topics
    article =''

    for topic in topics:
        if topic['id'] == int(id):
            article = f'<h2>{topic["title"]}</h2>{topic["body"]}'

    return HttpResponse(HTMLTemplate(article,id))

@csrf_exempt
def create(request):
    global nextId

    print('request.method : ' , request.method)
    if request.method == 'GET':       
        article ='''
            <form action= "/create/" method="post">
                <p> <input type="text" placeholder="title" name="title"> </p>
                <p> <textarea name="body" placeholder="body"> </textarea> </p>
                <p> <input type="submit"></p>
            </form>
        '''
        return HttpResponse(HTMLTemplate(article))
    elif request.method =='POST':
        title = request.POST['title']
        body = request.POST['body']

        newtopic = {"id":nextId , "title":title, "body":body}

        url = '/read/' + str(nextId)
        nextId = nextId + 1
        topics.append(newtopic)
        return redirect(url)
    
@csrf_exempt
def delete(request):
    global topics

    if request.method == 'POST':
        id = request.POST['id']
        newTopics = []
        for topic in topics:
            if topic['id'] != int(id):
                newTopics.append(topic)
        topics = newTopics
        return redirect('/')
    
@csrf_exempt
def update(request,id):
    global topics
    if request.method == 'GET':

        for topic in topics:
            if topic['id'] == int(id):
                selectedTopic ={
                    "title":topic['title'],
                    "body":topic['body']
                }
        article = f'''
            <form action ="/update/{id}/" method="post">
                <p> <input type="text" name="title" placeholder="title"></p>
                <p> <textarea name="body" placeholder="body">{selectedTopic['body']}</textarea></p>
                <p> <input type="submit"></p>
            </form>
        '''
        return HttpResponse(HTMLTemplate(article,id))
    elif request.method =='POST':
        title = request.POST['title']
        body = request.POST['body']
        
        for topic in topics:
            if topic['id'] == int(id):
                topic['title'] = title
                topic['body'] = body
        return redirect(f'/read/{id}')

 

<4. 결과화면 >

 

 

< 5.느낀점 >

Django를 처음 공부하면서 배우는건데 파이썬과 웹작동방식을 조금 알고있으니 빨리 빨리 이해되는거같다.

html css db이용해서 웹페이지 게시판을 처음 구현할때 1주일이 넘는시간이 걸렸던거같은데.... 

 

쨋든 개발을 다하고나서 글쓰기에 <script>alert('test')</script> 스크립트문을 넣었더니 그대로 실행되더라... 보안적인 부분은 아직 장고라는 프레임워크를 배우는단계라 고민만 해보고 다음 단계로 넘어가야겠다. 

728x90

'Python > Django' 카테고리의 다른 글

파이썬 Django 웹 개발 환경준비하기  (0) 2023.09.06
728x90

1. 웹 개발 환경준비

1.장고설치 
pip install django
2. 장고 프로젝트만들기
django-admin startproject config .
3. 데이터베이스DB 생성
python manage.py migrate

2. 프로젝트 구조 

1. config 폴더 : 프로젝트 설정 파일과 웹 서비스 실행을 위한 파일이 들어 있습니다.

   __init__.py : 파이썬 2.x 버전과 호환을 위해 만들어진 비어있는 파일 

   settings.py : 프로젝트 설정에 관한 내용이 담긴 파일

      BASE_DIR : 프로젝트 루트폴더, 설정 파일이나 py파일 등에서 프로젝트의 루트 폴더를 찾아 그 하위를 탐색함, 이 때         문에 변수를 미리 준비해 두는 값

      SECRET_KEY: 보안을 위해 사용되는값, 세션값의 보호나 비밀번호 변경시 사용되는 보안URL을 만드는데 이용

      DEBUG: 디버그 모드를 설정합니다. True 일경우 다양한 오류 메시지ㅣ를  즉시 확인가능 *배포시에는 False 

      allowed_hosts : 현재 서비스의 호스트를 설정 *개발시에는 비어두고 배포시 '*'나 실제 도메인을 사용함

      INSTALLED_APPS: 장고 웹서비스는 다양한 앱의결합으로 만들어지는데, 현재 프로젝트에서 사용하는 앱의 목록을        기록하고 관리합니다.

      MIDDLEWARE: 장고의 모든 요청응답메시지 사이에 실행되는 특수한 프레임워크

      ROOT_URLCONF:기준이 되는 urls.py파일의 경로를 설정

      TEMPLATES: 장고에서 사용하는 템플릿 시스템에 대한 설정들

      WSGI_APPLICATION: 실행을 위한 WSGI 어플리케이션을 설정함

      DATABASE: DB관련 설정

      AUTH_PASSWORD_VALIDATORS : 비밀번호 검을위한 설정

      LANGUAGE_CODE : 다국어에 관한 설정

 

   urls.py : 특정 기능을 수행하기 위해 접속하는 주소를 url이라고 하고 이를 설정해 주는데, 그 설정을 이 파일에 기록함.

   wsgi.py : 웹 서비스를 실행하기 위한 wsgi관련 내용이 들어있습니다. / 실제로 웹 서버와 장고 애플리케이션 사이 통신 역할을 담당 

 

2. venv 폴더 : 프로젝트 구동에 필요한 가상환경이 들어있는 폴더

3. db.sqlite3 : SQLite3 DB파일

4. manage.py : 장고의 다양한 명령어를 실행하기 위한 파일

 

장고 기본명령어

1. 서버 시작하기

$ python manage.py runserver 8080 기본 웹 서버는 8000 포트를 기본으로 동작한다. 변경하고 싶다면 포트 번호를 적어준다.
$ python manage.py runserver 0.0.0.0:8000 IP를 직접 지정하거나 같은 네트워크 망 안에서 접속이 가능하도록 할 때 왼쪽과 같이 적어준다.
$ python manage.py runserver 0:8000 0은 0.0.0.0의 약어이다. 해당 형태로 동작을 시킬 때는 settings.py에 있는 ALLOWED_HOSTS를 설정해 준다.

서버를 실행 후 종료를 하기 위해서는 [Ctrl+C]를 누르면 된다.

 

 

 

2. 기본 명령어 정리

$ django-admin startproject 프로젝트이름

장고 프로젝트를 만드는 명령이다. 장고를 설치 후에 위와 같이 명령어를 치면 프로 젝트가 생성된다. 아래와 같이 뒤에 .을 붙여주면 새로운 파일에서 프로젝트가 생성되는 것이 아니라 현재 파일에서 프로젝트가 생성된다.

$ django-admin startproject 프로젝트이름 .

 

 

$ python manage.py startapp 앱이름

프로젝트에 기능 단위인 앱을 새로 만들 때 사용한다.

 

 

$ python manage.py makemigrations 어플리케이션의 변경 사항을 추적에 DB에 적용할 내용을 정리한다. 보통 모델의 변경 사항이 있을 때 주로 사용한다.
$ python manage.py migrate 실제 변경사항을 DB에 반영한다.
$ python manage.py showmigrations DB 변경사항 목록과 상태를 출력한다.
$ python manage.py createsuperuser 관리자 계정을 생성한다.
$ python manage.py changepassword 계정의 비밀번호를 변경할 수 있다.

 

$ python manage.py sqlmigrate 실행할 SQL 명령문을 출력한다. 어떤 명령문을 실행할지 확인할 떄 사용하고, 튜닝이 안된 쿼리나 슬로우 쿼리 여부를 확인할 수 있다.
$ python manage.py dumpdata 현재 DB의 내용을 백업할 때 사용한다.
$ python manage.py loaddata 백업 파일에서 DB로 내용을 복구 할 때 사용한다.
$ python manage.py flush DB 테이블은 그대로 두고 테이블의 내용만 전부 삭제한다.
$ python manage.py shell 장소 쉘(shell)을 실행한다. 작성한 모델 등을 불러와 실제로 테스트를 해볼 수 있다.
$ python manage.py dbshell DB에 직접 접근할 수 있는 쉘을 실행한다. 장고 어플리케이션에 문제가 있어 관리자 페이지에 접근할 수 없을 때 보통 shell을 이용해 DB를 수정하는데 SQL 구문을 이용해 직접 수정하고 싶다면 dbshell을 이용할 수 있다.

 

참고 : https://han-py.tistory.com/308 

참고 : 배프의 오지랖 파이썬 웹프로그래밍

 

728x90

'Python > Django' 카테고리의 다른 글

[Django] Django CRUD 간단한 게시판 구현하기  (0) 2023.09.17
728x90

바이비트의 경우 KYC변경을 통한 레퍼럴 변경가입이 가능합니다.

수수료할인같은 경우 90일동안 유지되기때문에 90일마다 계정을 바꾸면서 수수료할인을 꼭 받으셔야 됩니다.

 

언제든지 모르는 사항이 있을땐 실시간 고객센터(Q&A방)에 문의주시면 친절하게 답변드립니다.

카톡(FAQ) 링크 :https://open.kakao.com/o/s1haVQAf

1.  아래 링크를 통해서 새로운 이메일로 바이비트 수수료할인링크 레퍼럴 가입하기

주소: https://partner.bybit.com/b/19476

- 바이빗 수수료 할인 가입 코드 : 19476


2.  '기존에 사용하던 계정'에서 방금 가입한 '새로운 계정'으로 KYC 이전하기

※  잠깐!  KYC를  이전하기 전에 꼭 기존계정에 있는 "모든  잔고"를 꼭  새로운계정(1번에서 가입한계정) 으로  먼저 전송 해주세요.

<KYC 이전방법>

핸드폰으로 바이비트 어플을 실행 시킨 뒤 "기존에 사용하시던 아이디" 로 로그인합니다.

1번 메인화면에서 왼쪽 상단 사람아이콘을 클릭하신 뒤 

2번 화면의 Idntify Vertiaction을 클릭합니다.

3번 화면의 Identify transfer을 클릭합니다.

4번 화면의 Faicial vertification을 클릭합니다.

5번 화면에  바이비트레퍼럴로 방금 가입한 이메일을 입력합니다.

6번 화면에 입력하신 이메일에 전송된 인증번호, OTP를 입력하여 인증을 완료합니다.

 

 

 
 
 
 
 
 
728x90

+ Recent posts