[프로젝트2] 인센티브 토큰 커뮤니티

Donghun Seol·2023년 1월 13일
1

프로젝트 소개

배포된 클라이언트 : https://d3t5y0jgzx6lw2.cloudfront.net/
깃헙 리포 : https://github.com/codestates-beb/beb-07-second-EWE

개발시 문제점과 해결방안

배포

클라우드 프론트 캐시 무효화

  1. 문제점
  • https로 캐싱한 클라이언트의 캐시가 24시간 지속되어 CI/CD 가 정상적으로 작동하지 않았음.
  • 매번 수동으로 invalidation을 해줘야해서 넘나 귀찮음, 내가 없으면 클라이언트가 제대로 안돌아감
  1. 해결방안
  • 넘나 귀찮아서 caching정책을 disable로 바뀌었다.(이러면 cloud front를 쓰는 의미가 많이 없어진다.)
  • action script에 권한을 설정하고 aws cli로 cache invalidation을 자동화 했다.

api 서버 https 적용

  1. 문제점
  • cors 쿠키를 로그인할때 전달해줘야되는데, samesite:none 정책을 적용하려면 https가 필요했다.
  • 이 프로젝트때문에 인증서와 도메인을 구매하는건 오버
  1. 해결방안

container CI/CD 파이프라인 구축

  1. 문제점
  • 서버 배포를 컨테이너형식으로 하다보니 기존의 액션스크립트가 작동이 안되었다.
  • 신규 커밋이 올라올때 마다. 아래의 과정을 반복해야해서 너무 귀찮고 힘들었다.
   1. 로컬에서 원격지의 코드를 당겨온다.
   2. 도커 이미지를 로컬에서 빌드한다.
   3. 도커 이미지를 허브에 푸쉬
   4. aws 콘솔에서 푸쉬된 이미지를 받아와서 서버를 재시작한다.
  • 테스트를 거치지 않은 코드가 서버에 올라와서 다시 커밋될때까지 1시간정도 다운타임 발생
  1. 해결방안
  • 각각의 항목별로 단계적으로 개선하였다.
  • 액션스크립트로 리포에서 이미지를 빌드하고 푸쉬.(서버 배포는 수동)
  • 도커 이미지에 커밋해시로 태그를 달아서 버저닝 구축 (아직도 서버는 수동 ㅠㅠ)
  • aws cli 연동과 jq활용으로 액션스크립트 내에서 입력 데이터를 재구성해서 컨테이너 배포 자동화 (프로젝트 마감 전날밤에 성공 ㅠㅠ)

api 서버

조금만 코드 규모가 커지니 타입스크립트의 필요성을 절실히 느낀다.

Cors 쿠키 전달

  1. 문제점
  • 서버와 클라이언트가 cross origin에서 작동해서 서버에서 로그인에 사용하는 쿠키를 클라이언트 브라우저에서 거부하는 상황 발생
  • https를 사용하지 않고 해결해보려고 노력했지만, 결국 sameSite:none을 적용해야하고, 이를 위해서는 secure:true 옵션을 쿠키에 줘야하므로 api 서버를 https 프로토콜로 제공해주어야 했다.
  1. 해결방안
  • aws lightsail container를 활용해서 해결
  • cloudfront로도 해결 가능한지 궁금하다.

의존성 주입

  1. 문제점
  • 의존성을 .env파일로 관리하다보니 환경변수가 20개정도로 늘어났다.
  • 환경변수 관리도 어렵고, 팀원간 전달해주기도 어려웠다.
  • 테스트코드 작성이 어렵다.
  1. 해결방안
  • 개선하지 못했다
  • 다음 프로젝트에서는 의존성주입이 가능한 유연하고 테스트가능한 컴포넌트를 만들기 위해 노력해 보겠다.

스마트컨트랙트

truffle migration initial setup

  1. 문제점
  • 컨트랙트 배포할때마다 수동으로 NFT를 발행해줘야해서 넘나 귀찮았다.
  1. 해결방안
  • 공식문서를 참고해서 아래와 같이 migration을 작성해줬다. 간단한 validation 코드도 마지막에 추가했다. (이제 보니 코드가 참 못나서 창피하다 ㅠㅠ)
// this script migrates EWENFT
const EWENFT = artifacts.require('EWENFT');
const EWEToken = artifacts.require('EWEToken');

module.exports = async function (deployer, network, accounts) {
  await deployer.deploy(EWENFT);
  const contract = await EWENFT.deployed();
  const token = await EWEToken.deployed();
  await contract.setToken(token.address);
  // some predefined mint logics
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  await contract.mintNFT(accounts[0], { from: accounts[0] });
  const tokenId = await contract.getTokenId();
  console.log({ tokenId });
  if (tokenId.toString() !== '10') throw new Error('token id should be 10');
};

truffle 테스트 코드 작성

  1. 문제점
  • 아래와 같은 트러플 테스트 코드에서 문제가 생겼다.
  • truffleAssert에 함수를 인자로 전달할때 제대로 작동하지 않았다.
  1. 해결방안
  • async 함수더라도 해당 콜백의 인자로 전달할때는 await를 넣으면 안된다.
  it('should not be able to transferFrom when sender do not enough token', async () => {
    const account0BalanceInitial = await token.balanceOf(accounts[0]);
    const val = 20;
    const approveResult = await token.approve(accounts[1], val, {
      from: accounts[0],
    });

    const allowanceVal = await token.allowance(accounts[0], accounts[1]);
    assert.strictEqual(allowanceVal.toString(), val.toString());
    await truffleAssert.fails(
      token.transferFrom(accounts[0], accounts[2], 21, {
        from: accounts[1],
      }),
    );

erc721에서 erc20의 함수 호출

  1. 문제점
  • 컨트랙트 내에서 다른 컨트랙트를 호출하는 부분이 구현하기 힘들었다.
  • 코드스테이츠 콘텐츠에 나와있는 함수는 해당 로직에서 제대로 동작하지 않았다.
  1. 해결방안
  • 콘텐츠에 있는 setToken을 변형해서 해결했다.
  • erc20 컨트랙트내의 transferFrom을 erc721내에서 호출하는 로직에 맞게 변형한 transferFromForNFT 메서드를 추가해서 erc721컨트랙트 내에서는 해당 함수를 활용했다.
// 기존함수
  function transferFrom(
    address sender,
    address recipient,
    uint256 amount
  ) external virtual override returns (bool) {
    _transfer(sender, recipient, amount);
    emit Transfer(msg.sender, sender, recipient, amount);
    uint256 currentAllowance = _allowances[sender][msg.sender]; // trouble here
    require(
      currentAllowance >= amount,
      'ERC20: transfer amount exceeds allowance'
    );
    _approve(sender, msg.sender, currentAllowance, currentAllowance - amount); // potential bug
    return true;
  }

// 새로 작성한 함수
  function transferFromForNFT(
    address sender,
    address recipient,
    uint256 amount
  ) external virtual returns (bool) {
    _transfer(sender, recipient, amount);
    emit Transfer(msg.sender, sender, recipient, amount);
    uint256 currentAllowance = _allowances[sender][recipient];  // fixed like this
    require(
      currentAllowance >= amount,
      'ERC20: transfer amount exceeds allowance'
    );
    _approve(sender, recipient, currentAllowance, currentAllowance - amount); // fixed like this
    return true;
  }

클라이언트

  • 잘 몰라용. 내가 안함
  • 근데 내가 해야된다면 생산성을 위해 mui나 react-bootstrap 같은 라이브러리를 사용했을것 같음
profile
I'm going from failure to failure without losing enthusiasm

1개의 댓글

comment-user-thumbnail
2023년 1월 13일

클라이언트 css 라이브러리 사용한다는 것은 동감합니다. 사실 이번에는 팀원이 익숙하지 않다는 이유로 바닐라 css를 사용하기로 했는데 아마 이번처럼 생각보다 규모가 커지는 프로젝트에서는 효율적인 방법이 있다는 것을 공부해 가셨을 거라 생각되어요.

답글 달기