6. Frontend Code Overview

  1. 개요

  2. src/App.js

  3. src/pages

  4. 학습 내용

1) 개요

이 장에서는 프론트엔드를 구축하겠습니다. 본 튜토리얼의 주목적은 컨트랙트와 프론트엔드 코드를 연결하는 방법을 알아보는 것입니다. 따라서 간략히 리액트 코드에 대해 설명하고 Klaytn에 배포된 컨트랙트와 상호작용하는 API 함수를 중점적으로 다루겠습니다.

|-- src
    |-- klaytn
      |-- caver.js
      |-- KlaystagramContract.js
    |-- redux
      |-- auth.js
      |-- photos.js
    |-- pages
      |-- AuthPage.js
      |-- FeedPage.js
    |-- components
      |-- UploadPhoto.js
      |-- Feed.js
      |-- TransferOwnership.js
      |-- ...
    |-- App.js

src/klaytn: Klaytn과의 상호작용을 지원하는 파일을 담고 있습니다.

  • src/klaytn/caver.js: 환경설정에 따라 caver-js 인스턴스화합니다.

    참고) caver-js는 Klaytn 노드와 연결하여 해당 노드나 Klaytn에 배포된 컨트랙트와의 상호작용을 지원해주는 RPC 라이브러리입니다.

  • src/klaytn/Klaystagram.js: caver-js API를 사용하여 컨트랙트 인스턴스를 작성합니다. 이 인스턴스를 통해 컨트랙트와 상호작용할 수 있습니다.

src/redux: 컨트랙트와 상호작용하여 결과 데이터를 추적하는 API 함수를 작성합니다.

  • redux/actions/auth.js

  • redux/actions/photos.js

src/pages: Klaystagram 애플리케이션을 구성하는 두 개의 페이지 파일을 담고 있습니다.

  • src/pages/AuthPage.js: 회원가입 및 로그인 양식이 있습니다. 회원가입 시 개인키를 생성하여 애플리케이션에 로그인할 수 있습니다.

  • src/pages/FeedPage.js: 스마트 컨트랙트로부터 사진을 읽어와 사용자에게 보여주고 업로드 기능을 제공합니다.

src/components: 페이지를 구성하는 컴포넌트 파일들을 담고 있습니다.

  • src/components/Feed.js: 컨트랙트로부터 데이터를 읽어와 사진을 보여줍니다.

  • src/components/UploadPhoto.js: 컨트랙트에 트랜잭션을 전송하여 사진을 업로드합니다.

  • src/components/TransferOwnership.js: 트랜잭션을 전송하여 사진의 소유권을 이전합니다.

src/App.js: 본 튜토리얼의 루트 컴포넌트 파일입니다.

2) App.js

'App.js' 전체 컴포넌트 중 루트 컴포넌트입니다. 이 컴포넌트는 사용자의 로그인 상태에 따라 두 페이지를 렌더링합니다. 각 페이지에는 컨트랙트와 상호작용하는 함수가 있습니다. 블록체인에 트랜잭션을 전송하려면 caver에 지갑 인스턴스를 추가해야 합니다. 자 이제 코드를 간략히 살펴볼게요.

cf. caver-js(or cav in the code) is a library for interacting with Klaytn blockchain. 이 내용은 다음 장 - 7-1 스마트 컨트랙트를 프론트엔드에 연결에서 자세히 다룰 것입니다.

// src/App.js

import React, { Component } from 'react'
import { connect } from 'react-redux'
import AuthPage from 'pages/AuthPage'
import FeedPage from 'pages/FeedPage'
import Nav from 'components/Nav'
import Footer from 'components/Footer'
import Modal from 'components/Modal'
import Toast from 'components/Toast'

import * as authActions from 'redux/actions/auth'

import './App.scss'

class App extends Component {
  constructor(props) {
    super(props)
    /**
     * 1. `isLoggedIn` 상태 초기화
     * 참고) sessionStorage는 브라우저 탭이 닫힐 때까지 데이터를 저장하는 인터넷 브라우저의 기능입니다.
     */
    const walletFromSession = sessionStorage.getItem('walletInstance')
    const { integrateWallet, removeWallet } = this.props

    if (walletFromSession) {
      try {
        /**
         * 2-1. 지갑 연동
         * 'walletInstance' 값이 있는 경우
         * intergrateWallet 메서드가 caver 지갑과 리덕스 스토어에 인스턴스를 추가합니다.
         * 참고) redux/actions/auth.js -> integrateWallet()
         */
        integrateWallet(JSON.parse(walletFromSession).privateKey)
      } catch (e) {
        /**
         * 2-2. 지갑 제거
         * sessionStorage의 값이 유효하지 않은 지갑 인스턴스인 경우
         * removeWallet 메서드가 caver 지갑과 리덕스 스토어에서 인스턴스를 제거합니다.
         * 참고) redux/actions/auth.js -> removeWallet()
         */
        removeWallet()
      }
    }
  }
  /**
   * 3. 페이지 렌더링
   * 리덕스가 walletInstance이 세션 스토리지에 존재하는지 여부에 따라 isLoggedIn의 상태를 true 또는 false로 초기화합니다.
   */
  render() {
    const { isLoggedIn } = this.props
    return (
      <div className="App">
        <Modal />
        <Toast />
        {isLoggedIn && <Nav />}
        {isLoggedIn ? <FeedPage /> : <AuthPage />}
        <Footer />
      </div>
    )
  }
}

const mapStateToProps = (state) => ({
  isLoggedIn: state.auth.isLoggedIn,
})

const mapDispatchToProps = (dispatch) => ({
  integrateWallet: (privateKey) => dispatch(authActions.integrateWallet(privateKey)),
  removeWallet: () => dispatch(authActions.removeWallet()),
})

export default connect(mapStateToProps, mapDispatchToProps)(App)

참고) walletInstance 세션이 JSON 문자열로 저장되기 때문에 JSON.parse가 필요합니다.

1. Initialize isLoggedIn state To initialize state isLoggedIn, we use constructor life cycle method on App component. 이는 컴포넌트를 마운트하기 전에 브라우저의 sessionStorage 내의 walletInstance 세션을 확인합니다.

2. Inject/Remove wallet If you have never logged in before, walletInstance session may not exist. 로그인했었다면 sessionStorage 내에 walletInstance 세션이 JSON 문자열의 형태로 존재할 것입니다.

  1. 삽입 - 지갑 인스턴스가 sessionStorage에 있으면 해당 지갑 인스턴스를 caver와 리덕스 스토어에 추가하세요.

  2. 제거 - sessionStorage에 있는 지갑 인스턴스가 유효하지 않으면 caver 지갑과 리덕스 스토어에서 제거하세요.

// redux/actions/auth.js

// 1. 지갑 삽입
export const integrateWallet = (privateKey) => (dispatch) => {
  // caver의 privateKeyToAccount API를 이용하여 지갑 인스턴스를 생성합니다.
  const walletInstance = cav.klay.accounts.privateKeyToAccount(privateKey)

  // 트랜잭션을 보내려면 지갑 인스턴스를 caver에 추가합니다.
  cav.klay.accounts.wallet.add(walletInstance)

  // 로그인 상태를 유지하려면 walletInstance를 sessionStorage에 저장합니다.
  sessionStorage.setItem('walletInstance', JSON.stringify(walletInstance))

  // 애플리케이션을 실행하는 동안 walletInstance 정보에 접근하려면 리덕스 스토어에 저장합니다.
  return dispatch({
    type: INTEGRATE_WALLET,
    payload: {
      privateKey,
      address: walletInstance.address,
    },
  })
}

// 2. 지갑 제거
export const removeWallet = () => (dispatch) => {
  cav.klay.accounts.wallet.clear()
  sessionStorage.removeItem('walletInstance')
  return dispatch({
    type: REMOVE_WALLET,
  })
}

cf. For further information about caver's privateKeyToAccount API, see caver.klay.accounts.privateKeyToAccount.

3. 페이지 렌더링 리덕스는 walletInstance가 세션 스토리지에 존재하는지 여부에 따라 isLoggedIn 상태를 true 또는 false로 초기화합니다.

3) src/pages

에서 설명했듯이 src/pages는 두 페이지 파일을 포함합니다. 사용자의 로그인 여부에 따라 두 페이지 중 하나가 애플리케이션에 렌더링됩니다.

  • AuthPage.js: 회원가입 및 로그인 양식이 있습니다. 회원가입 시 개인키를 생성하여 애플리케이션에 로그인할 수 있습니다.

  • FeedPage.js: 컨트랙트로부터 사진 데이터를 읽어와 사용자들에게 보여줍니다. 또한 사용자는 사진을 업로드할 수도 있습니다.

4) What we are going to learn?

블록체인 기반 애플리케이션에서 컨트랙트와 상호작용하는 방법이 두 가지 있습니다.

1) Reading data from contract. 2) Writing data to contract.

Reading data from contract is cost-free. On the otherhand, there is cost for writing data to contract (Sending a transaction). 따라서 데이터를 쓰려면 이 비용을 지불할 KLAY가 있는 Klaytn 계정이 있어야 합니다.

AuthPage에 있는 SignupForm을 통해 Klaytn 계정(개인키)을 생성할 수 있습니다. 이후에 생성한 개인키로 로그인하여 트랜잭션 수수료를 지불할 수 있습니다.

If you want to learn more about the two different login methods (private key / keystore), please refer to the 5.2. Auth Component page.

본 튜토리얼에서는 FeedPage에 중점을 두어 애플리케이션이 어떻게 컨트랙트에서 데이터를 읽고 쓰는지를 알아봅니다.

Last updated