본문 바로가기
etc/블록체인 뉴딜일자리사업

KAS와 node.js를 이용한 블록체인 지갑 기능 구현하기 2 - 로그인, 회원가입 기능

by vellahw 2023. 6. 28.

 

 

이전 글에서 지갑 기능 구현에 필요한 함수들을 작성했다. 이제 서버와 연결하여 작동 시켜보자!

 

KAS(Klaytn API Service)와 노드js를 사용한 블록체인 지갑 기능 구현하기

💭 교육 36일차 팀플을 위한.. 토큰 발행 실습을 했다. KAS API를 사용했고 카이카스 지갑을 연동했다. require로 모듈들을 가져와 손쉽게 사용할 수 있는게 너무 신기하다. 노드 만세 ^^** 회원가입

tavi.tistory.com


 

✔️ 서버 구축을 위한 index.js 작성

// express 노드
const express = require('express')
const app = express()

// 포트 번호 설정
const port = 3000

// view 파일들의 기본 경로 설정
app.set('views', __dirname+'/views')
//view 엔진 설정
app.set('view engine', 'ejs')

//post 방식으로 들어오는 데이터를 json 형태로 변환
app.use(express.urlencoded({extended:false}))

//dotenv 설정
require('dotenv').config()

const server = app.listen(port, ()=>console.log(port, 'Server Start!!!'))

서버 구축과 뷰 엔진 설정 등 index.js에 기본 설정들을 넣어줬다.

 

👉 라우팅 사용

앞으로 index.js에 모든 기능들을 때려박아 작성하지 않을 것이다!

각 기능별로 파일을 나눠 코드를 작성하고 폴더로 관리할 것이다.

 

// 메인 페이지 설정
const main = require('./routes/main.js')()
app.use('/', main)

// 모듈화 (라우팅)
const token = require('./routes/token.js')() // 빈괄호=함수 호출
app.use('/token', token) // /token: 주소

index.js에 위 코드를 추가했다.

변수 main은 routes 폴더의 main.js를 로드할 것이고, app은 '/', 즉 기본 경로에 대한 요청을 main.js로(변수 main) 처리할 것이다.

변수 token 또한 마찬가지이다.

 

main.js에서는 회원 관련 기능들을 작성할 것이고, token.js에서는 토큰 발행, 거래 등의 토큰 관련 기능을 작성할 것이다.

설정을 해주었으니 해당 폴더와 파일들을 생성해주었다.

 

 

모듈화 하는 김에 kas 관련 파일들도 token이라는 폴더에 넣어줬다.

 

💡이 과정에서!!

kip7.js 파일의 create_token() 함수 안의 fs.writeFileSync에 들어가는 kip7.json 파일의 경로에 token 폴더를 추가해주어야 한다.

이렇게

다른 함수들도 require로 kip7.json을 불러오는데 다른 함수들은 경로를 바꿔주면 오히려 오류가 남.. 그냥 두기

 

 

 

✔️ 로그인, 회원가입 페이지 만들기

main.js를 작성해보자

 

const express = require('express')
const router = express.Router()

module.exports = ()=>{
    // 기본 경로 localhost:3000/
    router.get('/', (req, res)=>
        res.render('login.ejs')
    )

    // 회원가입 페이지
    router.get('/signup', (req, res)=>
        res.render('signup.ejs')
    )
    
    return router
}

우선 로그인 페이지(기본 경로)와 회원가입 페이지는 단순히 ejs를 렌더해주게 작성했다.

 

맨 위를 보면 express.Router()를 사용했고, module.exports를 선언하고 함수들.. 라우터들?을 정의해주었다.

그 이유는 index.js에서 main.js는 모듈화하여 관리하겠다고 설정해주었다.

Express JS 문서를 보니 express 모듈을 로드하고, express.Router 클래스를 사용할건데 해당 클래스를 사용하면 모듈화 가능한 라우터를 만들 수 있다..는 것 같은데

index.js에서 main.js 갖다 쓰겠다고 require 해줬으니 외부에서 갖다 쓸 수 있게 exports, 내보내기 한다고 이해하면 쉬울 듯

 

🔻 login.ejs 와 signup.ejs는 이렇게 작성해주었다.

더보기

🔻 login.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>로그인</title>
    <style>
        body {
            padding: 15px;
            text-align: center;
        }

        input {
            border: 1px solid darkgray;
            border-radius: 20px;
            height: 30px;
            color: darkgray;
            margin-bottom: 10px;
            padding-left: 10px;
            width: 180px;
            font-size: 11px;
            outline: none;
        }

        .btn {
            width: 193px;
            color: white;
            background-color: black;
            border: none;
            cursor: pointer;
            font-weight: bold;
            margin-bottom: 5px;
        }

        button {
            width: 193px;
            height: 30px;
            color: white;
            background-color: darkgray;
            cursor: pointer;
            font-weight: bold;
            font-size: 11px;
            border-radius: 20px;
            border: none;
        }
    </style>
</head>
<body>
    <h3>로그인</h3>
    <form action="/signin" method="post">
        <input type="text" name="_phone" placeholder="Phone"><br>
        <input type="password" name="_pass" placeholder="Password"><br>
        <input type="submit" value="로그인" class="btn">
    </form>
    <button type="button" onclick="location.href='/signup'">회원가입</button>
</body>
</html>

 

🔻 signup.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>회원가입</title>
    <style>
        body {
            padding: 15px;
            text-align: center;
        }

        input {
            border: 1px solid darkgray;
            border-radius: 20px;
            height: 30px;
            color: darkgray;
            margin-bottom: 10px;
            padding-left: 10px;
            width: 180px;
            font-size: 11px;
            outline: none;
        }
        .btn {
            width: 193px;
            color: white;
            background-color: black;
            border: none;
            cursor: pointer;
            font-weight: bold;
            margin-bottom: 5px;
        }
    </style>
</head>
<body>
    <h3>회원가입</h3>
    <form action="/signup" method="post">
        <input type="text" name="_phone" placeholder="Phone"><br>
        <input type="password" name="_pass" placeholder="Password"><br>
        <input type="submit" value="가입하기" class="btn">
    </form>
</body>
</html>



localhost:3000/

서버를 구동하고 기본경로로 접속했을 때 보여지는 로그인 화면이다.

 

localhost:3000/signup

로그인 페이지에서 회원가입 버튼을 누르면 회원가입 페이지로 이동한다.

이제 회원가입 기능을 만들어보자!

 

 

 

✔️ 회원가입 + 지갑 생성

회원가입이 되면 유저 정보를 DB에 저장해줄 것이기 때문에 user라는 테이블을 만들고 핸드폰 번호, 비밀번호, 지갑 컬럼을 생성해주었다.

지갑 컬럼을 생성한 이유는 회원가입 시 해당 회원에게 지갑을 생성해줄 것이기 때문이다.

🔻 테이블 생성 쿼리

더보기

CREATE TABLE user (

    phone varchar(45) primary key,

    password varchar(45) not null,

    wallet varchar(45) not null

);

 

 

// DB에 저장하기 위한 Mysql 로드
const mysql = require('mysql2')

const connection = mysql.createConnection(
    {
        host : process.env.host,
        port : process.env.port,
        user : process.env.user,
        password: process.env.password,
        database: process.env.database
    }
)

// kip7.js 로드
const token = require('../token/kip7.js')

index.js 상단에 코드를 추가한다.

mysql2 모듈을 로드하고 서버 설정을 해주었다. (npm install mysql2 필요)

지갑 생성에 필요한 함수를 사용하기 위해 kip7.js 파일도 로드해주었다.

 

env 파일에는 이렇게 작성함.

로컬환경에서 작업할 때 윈도우의 경우 host를 localhost로 적어주면 된다. 패스워드도 맥은 별도 설정이 없었다면 적어주지 않아도 되지만 윈도우는 적어주어야한다!

 

 

// 유저가 입력한 정보를 mysql db에 삽입
router.post('/signup', async (req, res)=>{
    // 유저가 입력한 정보를 변수에 대입, 확인
    const input_phone = req.body._phone
    const input_pass = req.body._pass
    console.log('-> 회원가입 정보: ', input_phone, input_pass)
    const wallet = await token.create_wallet() // 지갑 주소
    console.log('-> 지갑 생성 됐을까욤: ', wallet)
        
    // db에 삽입
    const sql = `
        insert into
        user
        values(?, ?, ?)
    ` 
    const values = [ input_phone, input_pass, wallet ]

    // 쿼리 연결
    connection.query(
        sql,
        values,
        (err, result)=>{
            if(err){
                console.log(err)
                res.send(err)
            } else {
                console.log(result)
                res.redirect('/') // 성공하면 메인페이지로 리다이렉트
            }
        }
    )
})

회원가입 처리 url인 post 요청의 /signup 주소를 만든다.

유저가 입력한 휴대폰번호, 패스워드를 변수로 담아준다. (form으로 보내준 데이터는 request의 body 안에 있음)

그리고 회원가입을 하면서 지갑을 생성해줄 것이기 때문에 kip7.js의 create_wallet 함수를 불러온다. 지갑이 생성되어야 다음 작업으로 넘어가 DB에 저장되어야 하기 때문에 aysc await를 걸어주었다.

 

테이블에 데이터를 입력하는 insert 쿼리문을 작성했고, 물음표 안에 들어갈 값들은 사용자가 입력한 핸드폰 번호, 비밀번호, 새로 생성된 지갑주소이다.

쿼리문과 값을 연결하여 동작했을시, 에러가 발생하면 단순히 에러 메세지를 보여주도록 작성했고, 정상적으로 작동했다면 메인페이지(로그인 페이지)로 리다이렉트 한다.

 

회원가입을 했을 때 로그인 페이지로 리다이렉트되고, 콘솔에 직접 입력한 핸드폰 번호, 비밀번호와 생성된 지갑(kip7.js에서 create_wallet 함수가 돌 때 찍히도록 작성함) 정보와 생성된 지갑의 주소가 찍히면 정상적으로 처리된 것이다.

 

DB에서 조회했을 때도 정상적으로 뜬다면 성공!

 

 

 

✔️ 로그인 기능

회원가입이 되었으니 로그인 기능을 만들어보자.

index.js에 아래와 같이 post 요청의 /singin 주소를 작성한다.

 

// 로그인
router.post('/signin', (req, res)=>{
    // 유저가 보낸 데이터를 변수에 대입, 삽입
    const input_phone = req.body._phone
    const input_pass = req.body._pass
    console.log('-> 로그인 정보: ', input_phone, input_pass)

    // 로그인 확인 == Mysql 데이터와 비교
    const sql = `
        select *
        from user
        where
        phone = ?
        and
        password = ?
    `
    const values = [input_phone, input_pass]
    
    connection.query(
        sql,
        values,
        (err, result)=>{
            if(err){
                console.log("에러발생: ", err)
                res.send(err)
            } else {
                // 로그인이 성공하는 조건? == (result.length !=0)
                // 데이터는 배열 안의 json 형태:  [{}]
                if(result.length != 0) {
                    // 세션에 데이터 저장
                    // Request 안에 Session 안에 logined 라는 키로 result 저장
                    req.session.logined = result[0] 
                }
                res.redirect('/') // login 페이지로 리다이렉트
            }
        }
    )
})

유저가 로그인 폼에 입력한 핸드폰 번호와 패스워드를 변수로 담아준다.

로그인이 되었다는 뜻은 데이터베이스에 로그인 하려는 회원의 정보가 저장되어있다는 뜻이다. 따라서 로그인하려는 회원의 휴대폰 번호와 비밀번호를 대입하여 데이터를 찾아주면 되는 것! select 문을 작성해 sql 변수에 저장해준다.

물음표 안에 들어갈 값은 사용자가 입력한 input 태그의 휴대폰 번호, 패스워드가 된다.

 

로그인에 성공하면 result에 성공값이 담기는데, result는 배열 안의 JSON 형태로 데이터가 담긴다. ( [{ }] )

따라서 로그인에 성공했다는 뜻은 result 배열의 길이가 0이 아니란 뜻이므로, if 조건문을 이용해 작성해준다.

 

💡 session 사용

여기서 우린 로그인 정보를 세션에 저장해서 특정 시간 동안 로그인이 풀리지 않도록 관리해줄 것이다.

request의 session에 접근하여 logined 라는 키로 result 값을 저장해줄 것이다.

result는 JSON이 배열로 감싸져 있는 형태이기 때문에 배열(대괄호)을 없앤다는 의미로 0번째 인덱스를 가져왔다. (로그인 성공 데이터의 값은 무조건 하나이기 때문에 0번째 인덱스를 가져온 것임)

 

session을 사용하기 위해선 모듈을 로드하고 별도의 설정이 필요하다.

index.js에 아래와 같이 추가한다.

// express-session 모듈 로드
const session = require('express-session')
// session 설정
app.use(
    session(
        {
            secret : process.env.secret, // 암호화 키값 
            resave : false,
            saveUninitialized: false,
            cookie : {
                maxAge : 60000 // 쿠키 수명 설정 - 1분 (1000당 1초)
            }
        }
    )
)

express-session 모듈을 로드한다.

app.use 안에 session을 사용하겠다는 설정을 해준다.

 

  • secret: 세션 암호화 키 값 (암호화 키값은 .env 파일로 관리해야한다. .env에 secret이란 키로 아무값이나 입력해줌.)
  • resave: 요청중 세션이 수정되지 않은 경우에도 세션을 세션 저장소에 재저장 하는 옵션. 매 request마다 세션을 재저장하는 것! 기본값이 false임
  • saveUninitialized: request가 들어오면 해당 request에서 새로 생성된 session에 아무런 작업이 이루어지지 않은 상황에서도 세션을 저장소에 저장하도록 한다. 아무 내용 없는 세션이 저장될 수 있다는 뜻인데, false가 그것을 막아줌.
  • cookie 에는 여러 옵션이 존재하는데 위에서 사용한 maxAge는 쿠키의 수명을 설정한 것이다. 1000당 1초이므로 1분간 저장된다.

 

로그인에 성공하면 '/' 경로로 리다이렉트 하도록 작성했는데, 기존 '/' 경로는 로그인 페이지였다.

이제 로그인을 했다면 로그인 페이지가 아닌 메인 페이지로 리다이렉트되게 할 것이다.

 

// 기본 경로 localhost:3000/
router.get('/', (req, res)=>{
    // Session logined에 데이터가 존재하지 않는다면 login.ejs 보여줌
    if(!req.session.logined){
        res.render('login.ejs')
    } else {
        res.redirect('/main') // 세션에 로그인 있다면 main 보여줌
    }
})

// localhost:3000/main
router.get('/main', (req,res)=>{
    if(!req.session.logined){
        res.redirect('/')
    }else{
        console.log("로그인 되었습니다. 저장된 session: ", req.session)
        res.render('main.ejs')
    }
})

 

처음에 작성했던 get 방식 요청의 '/' 주소의 동작 함수를 수정해주자.

 

여깅

req의 session 안에 logined가 없다면 로그인이 되지 않았단 뜻으로, login.ejs를 렌더링해준다.

그렇지 않다면 로그인 되었단 뜻이니 '/main' 주소로 리다이렉트 시켜준다.

 

이어서, '/main' 주소를 만들어주지 않았으니 get 방식 요청으로 만들어주자.

똑같이 로그인 여부로 조건문을 작성하는데 로그인이 되지 않았다면 '/' 주소로 리다이렉트 즉, 로그인 페이지로 이동하게 되고,

그렇지 않다면 main.ejs를 렌더링 해준다. 콘솔에는 session 정보를 찍히게 했다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>메인페이지</title>
    <style>
         body {
            padding: 15px;
            text-align: center;
        }

        input {
            border: 1px solid darkgray;
            border-radius: 20px;
            height: 30px;
            color: darkgray;
            margin-bottom: 10px;
            padding-left: 10px;
            width: 180px;
            font-size: 11px;
            outline: none;
        }

        .charge {
            width: 193px;
            background-color: black;
            border: none;
            color: #fff;
            font-weight: bold;
        }

        button {
            background-color: #fff;
            color: black;
            font-weight: bold;
            border: none;
            font-size: 11px;
            cursor: pointer;
        }

        button:hover {
            background-color: #fff;
            color: darkgray;
            font-weight: bold;
            border: none;
            font-size: 11px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h3>Main Page</h3>
    <h3>토큰 충전하기</h3>
    <form action="/token/charge" method="get">
        <!-- 토큰의 충전량을 입력 -->
        <input type="number" name="_amount" placeholder="Amount"><br>
        <input type="submit" value="충전" class="charge">
    </form>
    <button type="button" onclick="location.href='/token/info'">마이페이지</button>
</body>
</html>

main.ejs는 이렇게 작성했다.

메인 페이지는 토큰을 충전할 수 있는 입력폼과 마이페이지로 이동할 수 있는 버튼을 제공한다.  

이제 회원가입한 유저 정보로 로그인해보자!

 

콘솔
리다이렉트 페이지 localhost:3000/main

로그인에 성공하면 콘솔이 찍히고, 메인페이지로 이동하게 된다.

 

토큰 충전과 발행, 유저 정보 기능은 다음 글에서 작성하도록 하겠다!

 

 

댓글