#4.4 Refactoring part One
- 지금 만드는 프로그램은 대부분 비활성화 상태이다.
- 프로그램을 실행하려고 하면 안 될 때가 있을 것이다. 컴파일이 제대로 안되는 것이다.
- 위 프로그램에서도 chain이 사용되고 있지 않다.
- Go는 선언한 것들을 전부 쓰지 않는 것을 좋아하지 않는다.
func GetBlockchain() *blockchain {
if b == nil {
b = &blockchain{}
}
return b
}
- sync란 패키지에 대해 알아보자.
- Go 언어에서 go routine을 생성하기 쉽다.
- 프로그램이 병렬적으로 실행이 가능하다는 뜻이다.
- sync패키지는 동기적으로 처리해야 하는 부분을 제대로 처리하게 도와준다.
- 병렬로 실행하고 있는 프로그램이 몇개이던 간에, thread가 몇개이던, Goroutine이 몇개이던간에 이 코드를 한번만 실행시키고 싶다.
- 이걸 위해서 sync 패키지를 사용할 것이다.
- 구체적으로 sync의 Once를 사용할 것이다.
func GetBlockchain() *blockchain {
if b == nil {
once.Do(func() {
b = &blockchain{}
})
}
return b
}
- Once는 Do라는 function을 가지고 있다.
- Do는 정확히 단 한 번만 호출되도록 해주는 function이다.
- once.Do를 호출해서 한 번만 실행시킬 function을 넘겨줄 것이다.
- 이전과 똑같이 동작할 것이다.
- 몇 천개의 Goroutine을 실행해도, 프로그램을 병렬적으로 처리해도 초기화 단계를 단 한번만 실행하도록 했다.
- 이 blockchain이 첫 번째 block으로 초기화되야 한다는 걸 알고 있다. Genesis block 말이다. 그것이 모든 것의 시작점이다.
- Genesis block을 생성할 것이다.
- 즉, application을 처음 시작하고 최초로 blockchain의 block들을 보기 요청한다면, blockchain을 초기화 할 것이다.
func createBlock(data string) *block {
}
func GetBlockchain() *blockchain {
if b == nil {
once.Do(func() {
b = &blockchain{}
b.blocks = append(b.blocks,)
})
}
return b
}
- 이곳에서 첫 block인 Genesis block을 생성할 것이다.
- 그리고 이걸 create Block이라는 function으로 정의할 것이다.
- createBlock은 string타입의 data를 인자로 받아서 생성될 block으로의 pointer를 반환해 줄 것이다.
type blockchain struct {
blocks []*block
}
- blockchain은 block들의 slice다.
- 여기에 block의 pointer들을 집어 넣을 것이다.
- 이 blocks는 pointer의 slice가 된다.
- blockchain이 매우 길어질 수 있기 때문에 block들을 복사하고 싶지 않다.
func GetBlockchain() *blockchain {
if b == nil {
once.Do(func() {
b = &blockchain{}
b.blocks = append(b.blocks, createBlock("Genesis Block"))
})
}
return b
}
- createBlock에서 반환된 결과를 여기에 추가할 것이다.
- Genesis Block을 추가하여 blockchain을 초기화한다.
- 비어있는 blockchain을 먼저 생성하고, blockchain 내부의 blocks에 하나의 block을 추가하는 것이다.
- 첫 번째 block은 Genesis Block이다.
- Genesis Block은 createBlock fucntion을 통해 생성된다.
func createBlock(data string) *block {
newBlock := block{data, "", }
}
- block 생성자를 호출해주고, data를 사용해서 block을 생성해야 한다.
- 이 block에는 hash도 필요하지만 아직 없으니 비워둔다.
- 마지막으로 이전 hash 값이 필요하다.
func getLastHash() string {
totalBlocks := len(GetBlockchain().blocks)
if totalBlocks == 0 {
return ""
}
return GetBlockchain().blocks[totalBlocks - 1].hash
}
- 이전 hash값을 불러오는 function을 만들 것이다.
- getLastHash라는 functnion을 만든다.
- string을 반환시키고, if-else를 구현한다.
- 가지고 있는 block들의 길이이다.
- GetBlockchain function을 사용한다.
- 만약 totalBlocks가 0이라면, 아무것도 반환하지 않을 것이다. 왜냐하면 마지막 hash값이 없기 때문이다.
- 만약 그렇지 않다면, GetBlockchain으로 마지막 block의 hash값을 얻어낸다.
func createBlock(data string) *block {
newBlock := block{data, "", getLastHash()}
}
- 이 block은 엉망이다. 왜냐하면 아직 hash 값이 없다.
- receiver function을 만들어야 한다.
- 왜냐하면 data를 변경해야만 하기 때문이다.
func (b *block) calclulateHash() {
hash := sha256.Sum256([]byte(b.data + b.prevHash))
b.hash = fmt.Sprintf("%x", hash)
}
- hash값을 계산할 calculateHash던 getHash던 원하는대로 만들어보자.
- data값와 prevHash값을 가져와서 hash fuctnion에 넣고 string으로 변환시킬 것이다.
- 더 모듈화된 방법으로 구현할 것이다.
- byte의 slice, 그리고 여기에 b.data와 b.prevHash를 더한 값을 넘겨주면 byte의 배열을 반환해 줄 것이다.
- 이걸 16진수로 포맷할 것이다.
func createBlock(data string) *block {
newBlock := block{data, "", getLastHash()}
newBlock.calclulateHash()
return &newBlock
}
- createBlock으로 돌아와서, newBlock의 calculateHash를 호출한다.
- 여기서 block을 생성한다.
- newBlock을 반환해준다.
- Genesis block을 생성하는 blockchain이 있고, createBlock은 data가 들어있는 block을 생성해 줄 것이다.
- block을 생성하기 위해 유저에게서 받아온 data가 필요하고, hash는 아직 존재하지 않는다. 그러나 이전 hash값은 가져올 수 있다.
- getLastHash function에서는 blockchain의 길이를 알아내서 현재 blockchain의 길이가 0이라면 아무것도 반환하지 않고, 그렇지 않다면, blockchain의 마지막 block의 hash 값을 반환할 것이다.
- blockchain의 마지막 block의 hash 값을 반환할 것이다.
- 그리고 newBlock의 hash값을 receiver function에서 계산해주고, 이 function은 pointer를 인자 값으로 받아 온다.
- block의 복사본이 아니고, 실제 값을 가지고 온다. 실제 block을 가지고 오기 때문에 값을 변경해 줄 수 있다.
- calculateHash function에서 hash 값을 계산하고 계산한 hash값을 block에 넘겨준다.
- 아직은 blockchain에서 곧바로 block을 더해주는 함수는 없다.
- 그저 createBlock으로 생성할 뿐이다.
- 아직 blockchain에 block을 추가할 방법을 모른다.
- blockchain 내부의 block들을 드러내는 function도 만들지 않았다.
출처 : 노마드코더, 노마드코인 강의
댓글 영역