Go 언어로 블록체인 직접 만들기: 개념부터 구현까지 쉽게 파헤치기
혹시 "블록체인"이라는 단어를 들으면 뭔가 복잡하고 어렵게 느껴지시나요?
마치 최첨단 기술 용어 같아서 일상과는 동떨어진 이야기처럼 생각될 수도 있을 텐데요.
하지만 블록체인의 핵심 아이디어는 생각보다 훨씬 쉽고, 우리 생활 곳곳에 적용될 수 있는 매력적인 개념입니다.
특히 데이터를 안전하게 관리하고 공유해야 하는 시스템에서 그 중요성이 점점 더 커지고 있죠.
블록체인은 비트코인과 같은 암호화폐의 기반 기술로 잘 알려져 있지만, 사실 그 활용 범위는 무궁무진합니다.
정보를 투명하게 기록하고, 누구도 함부로 변경할 수 없게 만드는 블록체인의 특성 덕분에 보안, 신뢰, 효율성을 높일 수 있는 다양한 분야에서 주목받고 있습니다.
이번 글에서는 블록체인의 기본 원리를 쉽게 설명하고, Go 프로그래밍 언어를 사용해서 우리만의 아주 기본적인 블록체인을 직접 만들어보는 과정을 차근차근 안내해 드릴게요.
코드를 직접 작성하고 실행하면서 블록체인의 작동 방식을 더 깊이 이해하는 시간을 가져볼까요?
블록체인, 도대체 뭘까요? 레고 블록 쌓듯이 이해해봐요
블록체인을 한 문장으로 요약하면, '서로 연결된 블록들의 분산된 디지털 장부'라고 할 수 있습니다.
여기서 핵심은 '블록', '연결', 그리고 '분산'이라는 세 가지 단어인데요.
- 블록(Block): 블록은 데이터 덩어리라고 생각하시면 쉬워요. 여기에는 실제로 기록하고 싶은 정보(예: 거래 내역, 계약 내용 등)와 함께, 이 블록이 어디에 연결되어 있는지 알려주는 특별한 정보가 담겨 있습니다. 마치 레고 블록처럼 데이터를 담고 있는 기본 단위인 셈이죠.
- 연결(Chain): 각 블록은 이전 블록에 대한 '지문'을 가지고 있습니다. 이 지문을 통해 블록들은 마치 사슬처럼 순서대로 연결되죠. 이전 블록의 정보가 조금이라도 바뀌면 그 지문도 변하기 때문에, 중간에 있는 블록을 몰래 바꾸려고 해도 전체 사슬이 망가져 버려서 쉽게 알아챌 수 있습니다. 이렇게 연결되어 있기 때문에 데이터의 순서와 무결성이 보장되는 것이죠.
- 분산(Distributed): 블록체인은 특정 중앙 기관 한 곳에만 저장되는 것이 아니라, 네트워크에 참여하는 여러 컴퓨터에 똑같이 복사되어 저장됩니다. 그래서 어느 한 곳이 고장 나더라도 다른 곳에 있는 정보를 이용할 수 있어서 시스템이 안정적이고, 데이터를 조작하기도 훨씬 어렵습니다. 여러 사람이 함께 공유하는 공공 장부와 비슷하다고 생각할 수 있습니다.
쇠사슬로 연결된 모습은 블록체인의 연결된 특성을 시각적으로 잘 보여줍니다.
각 고리가 하나의 블록이라고 생각하면 됩니다.
왜 블록체인이 주목받는 걸까요? 세 가지 핵심 매력
블록체인이 여러 분야에서 각광받는 이유는 크게 세 가지로 요약할 수 있습니다. 바로 신뢰성, 투명성, 그리고 보안성입니다.
- 신뢰성(Trust): 블록체인은 중앙에서 관리하는 주체가 없어도 참가자들 사이에 신뢰를 구축할 수 있게 해줍니다. 데이터가 여러 곳에 분산되어 있고, 모든 변경 사항이 기록되며, 임의로 수정하기 어렵기 때문입니다. 서로를 완전히 믿을 수 없는 상황에서도 데이터의 정확성을 확보할 수 있다는 것이 큰 장점이죠. 예를 들어, 여러 회사가 협력하는 공급망 관리 시스템에서 각 회사는 블록체인에 기록된 정보를 신뢰하고 업무를 진행할 수 있습니다.
- 투명성(Transparency): 블록체인에 기록된 데이터는 일반적으로 네트워크 참여자들에게 투명하게 공개됩니다. 물론, 어떤 데이터를 공개할지는 설계에 따라 달라질 수 있지만, 중요한 것은 일단 기록된 정보는 누구나 확인하기 쉽다는 것입니다. 이러한 투명성은 정보의 흐름을 명확하게 만들고, 불필요한 의혹을 줄이며, 책임 소재를 분명히 하는 데 도움을 줍니다. 예를 들어, 식품 이력 추적 시스템에 블록체인을 적용하면 소비자들이 해당 식품이 어떻게 생산되고 유통되었는지 투명하게 확인할 수 있습니다.
- 보안성(Security): 블록체인은 암호화 기술을 사용하여 데이터를 보호하고, 분산된 저장 방식으로 시스템의 안정성을 높입니다. 특정 블록을 변경하려면 네트워크 대부분의 참여자들의 동의가 필요하기 때문에, 해킹이나 데이터 위변조가 매우 어렵습니다. 이러한 강력한 보안성은 개인 정보, 금융 정보 등 중요한 데이터를 안전하게 관리해야 하는 환경에서 매우 유용합니다.
암호화폐는 블록체인을 어떻게 활용할까요? 떼려야 뗄 수 없는 관계
비트코인과 같은 암호화폐는 블록체인 기술이 실제로 어떻게 활용될 수 있는지 보여주는 대표적인 예입니다.
암호화폐 시스템에서 블록체인은 모든 거래 내역을 기록하는 공공 장부 역할을 합니다.
이 장부는 분산되어 저장되므로 어느 한 곳에서 거래 기록을 조작하는 것이 불가능하며, 모든 거래는 암호화되어 안전하게 관리됩니다.
암호화폐 네트워크에서 새로운 거래가 발생하면, 이 거래는 여러 단계를 거쳐 블록체인에 새로운 블록으로 추가됩니다.
이 과정에서 네트워크 참여자들은 거래의 유효성을 검증하고, 합의 알고리즘을 통해 블록을 체인에 연결합니다.
이러한 과정을 통해 암호화폐는 중앙 은행과 같은 중개 기관 없이도 안전하게 거래될 수 있으며, 거래 기록의 위변조 가능성을 극히 낮출 수 있습니다.
최근에는 이더리움과 같은 플랫폼에서 '스마트 계약'이라는 기능을 통해 블록체인의 활용 범위를 더욱 넓히고 있습니다.
스마트 계약은 특정 조건이 충족되면 자동으로 실행되는 컴퓨터 프로그램으로, 블록체인 상에 기록되어 투명하고 안전하게 계약을 이행할 수 있도록 돕습니다.
누가 이 멋진 기술을 만들었을까요? 베일에 싸인 사토시 나카모토
블록체인 기술은 2008년 '사토시 나카모토(Satoshi Nakamoto)'라는 가명을 사용하는 인물 또는 그룹에 의해 처음 세상에 알려졌습니다.
사토시는 비트코인이라는 디지털 통화를 제안하면서, 그 기반 기술로 블록체인의 핵심 개념을 제시했습니다.
사토시 나카모토는 2008년 10월에 발표한 백서 "Bitcoin: A Peer-to-Peer Electronic Cash System"에서 블록체인의 기본적인 아이디어를 상세하게 설명했습니다.
이 백서는 중앙 기관의 개입 없이 온라인에서 안전하게 돈을 주고받을 수 있는 시스템을 구축하기 위한 방안을 제시했으며, 블록체인은 이러한 시스템을 가능하게 하는 핵심 요소였습니다.
사토시 나카모토의 정체는 아직까지도 완전히 밝혀지지 않았지만, 그가 제시한 아이디어는 이후 수많은 혁신적인 기술과 서비스의 탄생에 큰 영향을 미쳤습니다.
Go 언어로 나만의 작은 블록체인 만들기, 첫걸음
이제 블록체인의 기본 개념을 어느 정도 이해했으니, 실제로 Go 언어를 사용해서 아주 기본적인 형태의 블록체인을 만들어보는 과정을 시작해 볼까요?
직접 코드를 작성하고 실행하면서 블록체인이 어떻게 동작하는지 좀 더 실감 나게 느껴볼 수 있을 거예요.
가장 먼저, 프로젝트를 위한 새로운 디렉토리를 만들고 Go 모듈을 초기화해야 합니다.
터미널을 열고 다음 명령들을 순서대로 실행하세요.
mkdir my-simple-blockchain
cd my-simple-blockchain
go mod init my-simple-blockchain
이렇게 하면 my-simple-blockchain
이라는 새로운 폴더가 만들어지고, 그 안에 Go 프로젝트를 관리하기 위한 설정 파일이 생성됩니다.
블록의 모습을 정의합니다: 데이터, 이전 정보, 그리고 지문
블록체인의 가장 기본적인 구성 요소는 바로 '블록'입니다.
우리 블록은 데이터를 담고 있고, 이전 블록과의 연결 정보를 가지고 있으며, 자신의 내용을 요약하는 '지문' 역할을 하는 해시값을 포함하고 있습니다.
Go 언어로 block.go
파일을 만들어서 블록의 구조를 정의해 봅시다.
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"time"
)
// Block은 블록체인을 구성하는 기본적인 데이터 구조입니다.
type Block struct {
Index int // 블록의 순서
Timestamp string // 블록이 생성된 시간
Data string // 블록에 저장될 실제 데이터 (예: 거래 내용)
PreviousHash string // 이전 블록의 해시값 (연결 고리 역할)
Hash string // 현재 블록의 해시값 (블록 내용의 고유한 지문)
}
// calculateHash 함수는 블록의 내용을 기반으로 SHA-256 해시를 생성합니다.
func (b *Block) calculateHash() string {
record := fmt.Sprintf("%d%s%s%s", b.Index, b.Timestamp, b.Data, b.PreviousHash)
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hex.EncodeToString(hashed)
}
// createBlock 함수는 새로운 블록을 생성합니다.
func createBlock(prevBlock *Block, data string) *Block {
newBlock := &Block{
Index: prevBlock.Index + 1,
Timestamp: time.Now().String(),
Data: data,
PreviousHash: prevBlock.Hash,
}
newBlock.Hash = newBlock.calculateHash()
return newBlock
}
// createGenesisBlock 함수는 블록체인의 첫 번째 블록인 제네시스 블록을 생성합니다.
func createGenesisBlock() *Block {
return &Block{Index: 0, Timestamp: time.Now().String(), Data: "Genesis Block", PreviousHash: "", Hash: calculateInitialHash()}
}
// calculateInitialHash 함수는 제네시스 블록의 초기 해시값을 생성합니다.
func calculateInitialHash() string {
record := "0" + time.Now().String() + "Genesis Block" + ""
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hex.EncodeToString(hashed)
}
위 코드에서는 Block
이라는 구조체를 정의했습니다. 각 필드의 역할은 주석에 자세히 설명되어 있습니다.
중요한 부분은 calculateHash
함수인데, 이 함수는 블록의 Index
, Timestamp
, Data
, PreviousHash
를 조합하여 SHA-256 알고리즘으로 해시값을 생성합니다.
이 해시값은 블록의 내용을 대표하는 고유한 문자열이 됩니다. createBlock
함수는 이전 블록을 받아서 새로운 블록을 생성하고, 이전 블록의 해시를 PreviousHash
에 저장하여 연결 고리를 만듭니다.
createGenesisBlock
함수는 블록체인의 첫 번째 블록인 '제네시스 블록'을 특별히 생성합니다. 제네시스 블록은 이전 블록이 없으므로 PreviousHash
가 비어 있습니다.
블록들을 연결하는 블록체인 구조 만들기
이제 생성된 블록들을 관리하고 연결할 블록체인 구조를 만들어야 합니다.
blockchain.go
파일을 생성하고 다음과 같이 코드를 작성하세요.
package main
import "fmt"
// Blockchain은 블록들의 연결 리스트입니다.
type Blockchain struct {
Blocks []*Block
}
// createBlockchain 함수는 새로운 블록체인을 생성하고 제네시스 블록을 추가합니다.
func createBlockchain() *Blockchain {
blockchain := &Blockchain{}
genesisBlock := createGenesisBlock()
blockchain.Blocks = append(blockchain.Blocks, genesisBlock)
return blockchain
}
// addBlock 함수는 블록체인에 새로운 데이터를 기반으로 블록을 추가합니다.
func (bc *Blockchain) addBlock(data string) {
prevBlock := bc.Blocks[len(bc.Blocks)-1]
newBlock := createBlock(prevBlock, data)
bc.Blocks = append(bc.Blocks, newBlock)
}
// isChainValid 함수는 블록체인의 무결성을 검증합니다.
func (bc *Blockchain) isChainValid() bool {
if len(bc.Blocks) <= 1 {
return true // 제네시스 블록만 있는 경우는 유효하다고 가정합니다.
}
for i := 1; i < len(bc.Blocks); i++ {
currentBlock := bc.Blocks[i]
prevBlock := bc.Blocks[i-1]
if currentBlock.Hash != currentBlock.calculateHash() {
fmt.Printf("Error: current block's hash is invalid at index %d\n", i)
return false
}
if currentBlock.PreviousHash != prevBlock.Hash {
fmt.Printf("Error: previous hash mismatch at index %d\n", i)
return false
}
}
return true
}
// printChain 함수는 블록체인의 모든 블록 정보를 출력합니다.
func (bc *Blockchain) printChain() {
for _, block := range bc.Blocks {
fmt.Printf("Index: %d\n", block.Index)
fmt.Printf("Timestamp: %s\n", block.Timestamp)
fmt.Printf("Data: %s\n", block.Data)
fmt.Printf("Previous Hash: %s\n", block.PreviousHash)
fmt.Printf("Hash: %s\n", block.Hash)
fmt.Println("--------------------")
}
}
여기서는 Blockchain
이라는 구조체를 정의했습니다. 이 구조체는 Block
들을 슬라이스로 관리합니다.
createBlockchain
함수는 새로운 블록체인을 생성하고, 가장 먼저 제네시스 블록을 추가합니다.
addBlock
함수는 새로운 데이터를 받아서 블록체인의 마지막 블록을 이전 블록으로 삼아 새로운 블록을 생성하고, 블록체인에 추가합니다.
isChainValid
함수는 블록체인의 각 블록을 순회하면서 해시값의 유효성과 이전 해시와의 연결이 제대로 되어 있는지 검사하여 블록체인의 무결성을 확인합니다.
printChain
함수는 블록체인에 저장된 모든 블록의 정보를 보기 좋게 출력합니다.
블록체인을 사용해보는 메인 함수 작성
이제 main.go
파일을 만들어서 실제로 블록체인을 생성하고, 데이터를 추가하고, 검증해보는 코드를 작성해 봅시다.
package main
import "fmt"
func main() {
// 새로운 블록체인 생성
myBlockchain := createBlockchain()
// 블록체인에 몇 가지 데이터 추가
fmt.Println("Adding more blocks to the blockchain...")
myBlockchain.addBlock("Send 10 BTC to Alice")
myBlockchain.addBlock("Pay Bob for services")
myBlockchain.addBlock("Record temperature: 25 degrees")
// 블록체인의 내용 출력
fmt.Println("\nPrinting the blockchain:")
myBlockchain.printChain()
// 블록체인의 유효성 검증
fmt.Println("\nIs blockchain valid?", myBlockchain.isChainValid())
// 블록체인을 일부러 변조해보고 유효성을 검증해보기
fmt.Println("\nAttempting to tamper with the blockchain...")
if len(myBlockchain.Blocks) > 1 {
myBlockchain.Blocks[1].Data = "Tampered data!"
fmt.Println("Modified the data of the second block")
}
fmt.Println("Is blockchain valid after tampering?", myBlockchain.isChainValid())
// 변조된 블록의 해시를 다시 계산해서 체인을 복구해보기
if len(myBlockchain.Blocks) > 1 {
myBlockchain.Blocks[1].Hash = myBlockchain.Blocks[1].calculateHash()
fmt.Println("Recalculated the hash of the second block")
}
fmt.Println("Is blockchain valid after recalculating hash?", myBlockchain.isChainValid())
}
main
함수에서는 먼저 createBlockchain
함수를 호출하여 새로운 블록체인을 생성합니다.
그 다음 addBlock
함수를 여러 번 호출하여 블록체인에 데이터를 추가합니다.
printChain
함수를 사용해서 블록체인의 내용을 출력하고, isChainValid
함수로 블록체인이 유효한지 확인합니다.
그런 다음 일부러 두 번째 블록의 데이터를 변경하고 다시 유효성을 검증해서 블록체인이 변경을 감지하는 것을 확인합니다.
마지막으로, 변조된 블록의 해시를 다시 계산하면 블록 자체는 유효하게 보이지만, 그 다음 블록의 PreviousHash
가 일치하지 않게 되어 여전히 체인 전체로는 유효하지 않다는 것을 보여줍니다.
(완벽하게 복구하려면 그 다음 블록부터 모두 다시 계산해야 합니다.)
이제 터미널에서 go run main.go block.go blockchain.go
명령을 실행하면, 블록체인이 생성되고 데이터가 추가되는 과정, 그리고 유효성이 검증되는 결과를 확인할 수 있습니다.
➜ my-simple-blockchain go run main.go block.go blockchain.go
Adding more blocks to the blockchain...
Printing the blockchain:
Index: 0
Timestamp: 2025-02-15 19:54:42.339646 +0900 KST m=+0.000125001
Data: Genesis Block
Previous Hash:
Hash: 5b9c1a76990c1158fd38c23b85e8632a58500fdb931e46e3e84fe38ded1e4c3a
--------------------
Index: 1
Timestamp: 2025-02-15 19:54:42.33998 +0900 KST m=+0.000459459
Data: Send 10 BTC to Alice
Previous Hash: 5b9c1a76990c1158fd38c23b85e8632a58500fdb931e46e3e84fe38ded1e4c3a
Hash: ac2129785ffd0a76fee94a3cf41131b72ebc29eee8e313d99fe80d94650ec1b4
--------------------
Index: 2
Timestamp: 2025-02-15 19:54:42.339989 +0900 KST m=+0.000467959
Data: Pay Bob for services
Previous Hash: ac2129785ffd0a76fee94a3cf41131b72ebc29eee8e313d99fe80d94650ec1b4
Hash: 6036585b075cd69e59c312a65426123a3b112e82b5944d08b571b7ff784b71f7
--------------------
Index: 3
Timestamp: 2025-02-15 19:54:42.339991 +0900 KST m=+0.000470084
Data: Record temperature: 25 degrees
Previous Hash: 6036585b075cd69e59c312a65426123a3b112e82b5944d08b571b7ff784b71f7
Hash: d2430f8dc17a86469acd7813001036d0366e21c17c004459ab041a255aeffab6
--------------------
Is blockchain valid? true
Attempting to tamper with the blockchain...
Modified the data of the second block
Error: current block's hash is invalid at index 1
Is blockchain valid after tampering? false
Recalculated the hash of the second block
Error: previous hash mismatch at index 2
Is blockchain valid after recalculating hash? false
이것으로 Go 언어를 사용한 아주 기본적인 블록체인 만들기를 완료했습니다.
이 예제는 블록체인의 핵심 개념인 블록, 연결, 유효성 검증을 이해하는 데 도움이 될 것입니다.
실제 블록체인은 이보다 훨씬 복잡하지만, 이 기본적인 구조를 이해하는 것이 중요합니다.
'Go' 카테고리의 다른 글
Go 언어에서 다양한 형태의 for 루프를 제공하는 이유: 간결함 속에 담긴 힘 (1) | 2025.02.15 |
---|---|
Steam(스팀)이 Go(고) 런타임을 망가뜨린다고? 게임 개발, 이제 Go로도 가능한가? (0) | 2025.02.09 |
sqlx와 PostgreSQL 타임존 완벽 정복! (0) | 2025.01.12 |
예상치 못한 untyped int의 동작, 왜 이런 일이? (0) | 2025.01.12 |
Go에서 구조체 패킹(structure packing) 이해하기 (0) | 2024.12.28 |