오늘은 조직생활과 관련하여 많은 사람들이 겪고 있는 직장 상사와의 갈등에 대해 생각해보고자 한다.

많은 사람들이 직장생활의 가장 큰 어려움으로 상사와의 갈등을 꼽고 있다. 과연 세상의 모든 상사들은 다 나쁜 사람인가? 라는 생각이 든다.

본 글은 어디까지나 사회생활에 대한 필자의 주관적인 생각만을 적어보는 것으로 본 글이 직장 내 상사와의 갈등에 대한 일반적인 해안은 결코 되지 못하는 것을 인지하고 있음을 먼저 밝히고 글을 시작한다.

직장 상사를 대하는 태도 대한 일반적인 관념은 어떠한가 부터 생각해 보자.

흔히 직장상사를 이야기 할때 ‘모신다’ 혹은 ‘사수’, ‘배운다’ 등의 단어가 일반적이다.

사실 이런 단어는 벌써부터 직장 상사와 나의 관계가 상/하 관계 혹은 갑/을 관계, 선임자/후임자의 수직적인 관계라고 화자 스스로가 생각하는 경우 사용된다.

따라서 후임자는 직장 상사의 말에 최대한 맞추려고 하고, 직장 상사가 나를 한 없이 착하고, 성실한 그런 ‘직원’ 이자 ‘후임자’로 보여지기를 원한다.

하지만, 이런 태도는 과연 현명한 처신일까?

과연 직장 상사는 ‘후임자’, ‘하급자’ 를 필요로 하고 훌륭한 ‘을’이 필요한 것일까?

이에 대한 필자의 생각은 ‘아니다’ 이다.

기업은 더 이상 학교이자 가정이 아니며 ‘하급자’, ‘직원’ 보다는 비즈니스 파트너 가 필요한 것이다.

착하고 말잘듣는 직원보다는, 좀 쌀쌀 맞더라도 잘난 와 그래도 잘 합을 맞출 수 있는 파트너 가 되어 주기를 원한다.

선임자가 상대방을 후임자 가 아닌 파트너 로 인정하게 되는 순간 선임자는 선임으로써의 버팀목 이 되어주어야 한다는 책임감을 덜어내고 인간 대 인간으로써 함께 협력하여 일을 해 나갈 수 있기 때문이다.

사실 남자라면 누구나 공감하겠지만 수직문화의 끝을 보여주는 군대에서는 이런 장면들을 많이 보아왔을 것이다.

항상 착하기만 하고 저 친구 정말 고생하는구나 하는 후임과 동기들은 어디까지나 보호해야할 대상이며 나의 아랫사람으로 많은 경우 힘든일은 혼자 다하면서도 정작 인정은 받지 못하는 많은 병사들이 있는 반면, 좀 뺀질거리더라도 눈치 빠르고 소신있는 병사들은 전우 로써 인정을 받는 경우는 아주 흔한 일이다.

인생을 살아가면서 언제나 우리는 조직의 한 가운데에 놓여있게 된다.

필자를 비롯한 이 글을 읽는 독자들도 당돌하고 일 좀 하는 비즈니스 파트너 로써 상급자의 도움이 필요없는 믿을만한 파트너로 거듭나 훌륭한 사회의 조직원으로 거듭난다면 더할나위 없이 좋은 일일 것이다.


설치하기

Go 는 패키지 매니저가 없으며 VCS에서 직접 받아와서 라이브러리를 사용합니다.

먼저, 다음 페이지에서 window 용 Go 를 설치해줍니다.

공식 다운로드 사이트

설치가 완료 되었다면. 아래와 같은 구조로 디렉터리를 만들어 줍니다.

1
2
3
4
Go/
bin/
pkg/
src/

main.go 만들기

main.go

1
2
3
4
5
6
7
8
9
10
11
package main //namespace declaration 하는 부분으로 기능과 논리들을 그룹화시켜준다.

import "fmt" // format의 약자로 standard lib에 있다.

// 여기서 main 은 keyword 로써 해당 함수가 라이브러리가 아닌 어플리케이션으로 동작함을 나타낸다.
// go 어플리케이션이 여기서 시작된다.
// func는 function을 선언하는 keyword 이다.
func main() {
fmt.Println("Hello, World") //여기서 println의 앞의 P가 대문자 인데, 이는 외부 라이브러리에서 가져왔음을 의미한다.
// 같은 원리로 함수를 선언할때 대문자로 시작하면 외부에서 접근이 가능한 것이다.
}

실행하기

1
go run main.go

빌드하기

1
2
3
4
5
6
go build # 실행가능한 binary 파일을 만들어 준다.
이 경우 파일명이 아니라 폴더 명으로 파일이 생성된다.
./go-study

윈도우용 빌드
GOOS=windows go build -o main.exe main.go

슬라이스

슬라이스는 배열의 값을 가리킵니다(point). 그리고 배열의 길이를 가지고 있습니다.

[]T 는 타입 T 를 가지는 요소의 슬라이스(slice) 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
p := []int{2, 3, 5, 7, 11, 13}
fmt.Println("p ==", p)

for i := 0; i < len(p); i++ {
fmt.Printf("p[%d] == %d\n",
i, p[i])
}
}

레인지

for 반복문에서 range 를 사용하면 슬라이스나 맵을 순회(iterates)할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}

_ 를 이용해서 인덱스(index)나 값(value)를 무시할 수 있습니다.

만약 인덱스만 필요하다면 “ , value ” 부분을 다 제거하면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << uint(i)
}
for _, value := range pow {
fmt.Printf("%d\n", value)
}
}

맵은 값에 키를 지정합니다.

맵은 반드시 사용하기 전에 make 를 명시해야합니다. (주의: new 가 아닙니다)

make 를 수행하지 않은 nil 에는 값을 할당할 수 없습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

type Vertex struct {
Lat, Long float64
}

var m map[string]Vertex

func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"])
}

맵 리터럴 (Map literals)
맵 리터럴은 구조체 리터럴과 비슷하지만 key 를 반드시 지정해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

type Vertex struct {
Lat, Long float64
}

var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}

func main() {
fmt.Println(m)
}

다양한 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
m := make(map[string]int)

m["Answer"] = 42
fmt.Println("The value:", m["Answer"])

m["Answer"] = 48
fmt.Println("The value:", m["Answer"])

delete(m, "Answer")
fmt.Println("The value:", m["Answer"])

v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)
}

switch

다른 일반적인 언어를 아는 분이라면 switch 에 대해서 잘 알 것입니다.

다른 언어와 다른점은 case의 코드 실행을 마치면 알아서 break를 한다는 점입니다.

( fallthrough 로 끝나는 case는 스스로 break를 하지 않습니다 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"runtime"
)

func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}

메소드

고에는 클래스가 없습니다. 하지만 메소드를 구조체(struct)에 붙일 수 있습니다.

메소드 리시버(method receiver) 는 func 키워드와 메소드의 이름 사이에 인자로 들어갑니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"math"
)

type Vertex struct {
X, Y float64
}

func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
v := &Vertex{3, 4}
fmt.Println(v.Abs())
}

인터페이스

인터페이스는 메소드의 집합으로 정의됩니다.

그 메소드들의 구현되어 있는 타입의 값은 모두 인터페이스 타입의 값이 될 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
"fmt"
"math"
)

type Abser interface {
Abs() float64
}

func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}

a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
a = v // a Vertex, does NOT
// implement Abser

fmt.Println(a.Abs())
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}

type Vertex struct {
X, Y float64
}

func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)

살면서 누구나 한번쯤은 돌이킬 수 없는 잘못된 결정으로 인한 실수를 진실을 왜곡함으로써 해결하고 싶은 강한 욕망에 휩싸이는 순간이 있다.
가장 사소한 예로, 학창시절 선생님이 교실에서 어떤 잘못을 저지른 사람에게 자백을 요구하는 훈계를 듣는 경우는 사실 누구나 한번쯤은 있었던 경험일 것이다. 나는 항상 그 상황에서 진짜 잘못을 저지른 사람은 어떤 마음일까 하는 생각을 하고 했다. 그 상황이 나에게는 그저 귀찮은 일인 반면 그 당사자에게는 얼마나 큰 마음의 짐이 될까를 생각해보면 생각만으로도 암담한 일이 아닐 수 없었다.

인간은 누구나 실수를 하기에 많은 사람들이 이런 욕망에 휩싸일 수 있고, 두가지 선택지만을 가진다. 진실을 밝히는가 혹은 진실을 숨기는가.

많은 경우에 전자의 선택지는 너무도 큰 희생이 필요할 수 있다. 내가 일생동안 살아온 나의 신념을 배반하는 감당할 수 없는 실수로 내 인생을 송두리 째 바꾸어 놓을 것만 같고, 다시는 회생할 수 없을 것 같은 암담한 미래가 그려진다. 반면, 두번째 선택지는 너무도 달콤하다. 나의 실수를 없던 것으로 하는것. 그것은 마치 시간을 되돌리는 것처럼 단 한번만 진실을 감추면 모든 문제가 해결 될 것만 같다.

사실 이런 이야기는 영화나 책속에나 나올법 한 일이라고 생각할 수 있지만, 사실 사회의 많은 곳들에서 흔히 일어나는 일중 하나이다. 대부분의 부패한 정치인 혹은 기업가, 범죄자들도 많은 경우 선량하고 옳바른 선택을 할 수 있었던 시절이 있었을 것이라 생각하고 또 누군가는 과거의 단 한번의 잘못된 처신을 평생을 후회하며 열심히 살아가고 있을지도 모른다.

이 영화는 그런 감당할수 없을 것 같은 실수에 대처하는 한 잘나가던 경찰의 이야기를 다룬다.
평생을 신념에 입각해서 살았고, 훌륭한 경찰로 살았지만, 단 한번의 선택으로 최악의 상황으로 치닫게 되며, 그러한 심적 부담과 ‘백야’ 라는 극중 설정으로 인해 불면증을 않아 점점 더 판단력을 잃어간다.
이러한 설정은 현실의 상황을 너무도 잘 반영한다. 진실을 은닉한 것에 대한 엄청난 마음의 부담은 그 사람에게 제대로된 판단을 할 힘을 앗아가고 결국 거짓의 구렁텅이로 빠져버리게 되는 것이다.

결국 영화는 우리가 살면서 겪을 이런 상황들에 대한 가장 쉽고 유일한 선택지를 제시하고 있다.

그것은 바로 “모든 진실을 밝히고 실수를 감내하는 것” 그리고 “최악의 상황에서도 옳바르게 행동할 판단력을 잃지 않는것”이다.

그 어떤 순간에서도 한치앞의 달콤한 유혹을 뿌리치고 진실로 삶을대하는 것이 남은 인생을 위한 최고의 선택지이며, 실수를 할 수 밖에 없는 인간으로써 모든 결정적인 실수에 대한 책임을 감내하고 살아야 하는 것은 인간의 숙명이기에 최악의 상황에서도 판단력을 잃지않고 올바른 선택을 할 수 있는 용기와 힘을 가지도록 노력해야 할 것이다.

블록체인의 탄생

현대 사회에서 기술의 발전의 가장 큰 흐름은 바로 atom worldbit world 로 옮기려 한다는 것이다.
모든 실제 세계의 정보와 행동 양식 더 넘어서는 시장 그 자체에 이르기 까지 모든 atom world 즉, 실제 세계의 모든 것을 컴퓨터 사이의 네트워크로 구성되는 bit world 에 밀어 넣으려는 다양한 시도들이 계속되었다.

흔히, Database 즉, 데이터를 넣기위한 저장공간은 실제로 현대사회에서 모든 권리를 대변하고 있다.
은행의 돈, 국가에서 개인의 신분 등 모든 중요한 정보들은 database 내에 존재하고 실제가 어떻게 되었든 database 안의 내용이 곧 진리인 사회속에서 우리는 살아가고 있다. 심지어는 database에 명확하게 들어가지 못하는 많은 정보들은 그 가치를 잃어가는 사회이기도 하다.

하지만, 이렇게 모든 현실 세계의 데이터를 database라는 꽉막힌 틀이 쑤셔넣는 것은 상당한 노력을 필요로 하는 일이고, 심지어 이러한 데이터베이스는 단지 하나의 컴퓨터에 존재하는 bit 덩어리에 불과하기 때문에 사람들이 서로가 소통해서 문제를 해결하듯이 database와 database 사이에서 원활하게 정보를 교류하여 문제를 빠르게 처리하는 것은 매우 어렵다.

각 database는 서로 다른 형태로 존재하고 때문에 우리는 유사한 정보를 수도없이 많이 컴퓨터에게 가르쳐 주어야 한다.
흔한 예로 우리는 수많은 사이트에 수도없이 많이 같은 주소를 입력하고, 정부에서 민원을 처리하는 경우 각 부서를 옮겨다니면서 수십번씩 이름과 개인 정보를 입력하는 수고를 들여서 일을 처리해야 한다. 이는 너무나도 비효율적인 시스템이 아닐 수 없다.

시간이 지나고 우리는 이렇게 유연하지 못한 database의 홍수 속에서 어떻게 각각의 database를 통합하여 보다 유연하고 효율적인 데이터의 흐름을 만들어 낼 수 있을지 고민하게 되고 3가지의 해안을 제시하게 되었다.

1.Diverse peers model
첫번째 방법은 단순히 컴퓨터와 컴퓨터를 연결하는 것이다.
흔히 오늘날에는 결제를 하기 위해 쇼핑몰과 통신하고, 쇼핑몰은 은행과 은행은 다시 국세청과 정보를 교환해야 한다.
이렇게 하나의 단순 작업을 처리하기 위해서 각 peer들이 계속해서 통신을 해야하고 매일매일 소프트웨어를 업그레이드해야 할 뿐만 아니라 버그에도 매우 취약하다.

또 하나의 심각한 문제는 무엇이 진짜인지를 알 수 없다는 것이다.
진리는 존재하지 않고 각 peer 들 내부의 database의 수치값만이 존재하기 때문에 정보가 일관되지 않은 경우가 빈번하게 일어난다.
가령 내가 쇼핑몰에서 물건을 주문과 결제를했는데 쇼핑몰 내의 database에서 내가 결제한 정보가 없다고 선언해 버린다면 우리는 대처할 수 있는 방법이 사실상 거의 없다고 보아도 무관하다.
또 A 업체와 B 업체가 거래를 하려고 하는데 서로가 가진 재고량과 주문, 발송량이 일치하지 않는 경우 심각한 문제를 초래하게 된다.

2.Hub and Spoke
두번째 방법은 이렇게 어려움 프로세스를 맡아줄 중앙 허브를 두는 것이다.
많은 peer들이 직접 서로가 1:1 로 수십번 정보의 일관성을 흐리지 않고 정보를 교환하는 것은 매우 어렵기 때문에 이 모든 일을 다 처리해줄 마법같은 서비스들이 등장하게 되었다. 가령 결제사라던가 전자인증정보 시스템, VISA 처럼 수많은 peer들과 올바로 통신하고 데이터의 일관성을 보장해주는 중앙기관이 생기고 나머지 모든 peer는 이러한 중앙기관에 의존해 업무를 처리하는 것이다.

사실상 이러한 시스템은 자연적 독점기업이라고 보아도 무방하다. 이렇게 수많은 peer 들이 정보를 교환하는데 이 중앙 기업은 technical cost 라는 명목으로 막대한 부를 축적하고 당신의 신용과 정보에 대한 완벽한 통제권을 가지고 있다. 이는 전혀 민주적이지 못하며 VISA 사의 경우 전 세계 거래의 1% 라는 천문학적인 수입을 올릴 수 있는 시스템이 완성되는 것이다.

3.Protocol - if you can find them
프로토콜은 궁극의 유니콘 같은 존재이다. 최적의 프로토콜을 찾으면 이러한 database의 완벽한 통합을 할 수 있겠지만, 현실 세계에서는 아무도 그런 프로토콜을 가지고 있지 못하다.
HTTP, HTML, SMTP 등 많은 standard 들이 생겨나게 되었지만 아직 많은 한계들이 보인다.
가령 slack 이나 Gchat의 경우 각 플랫폼 내의 사용자들이 서로간에 소통할 수 있도록 기존의 프로토콜사용하여 훌륭하게 동작시키고 있지만, 각 기업들 사이에 데이터를 교환하는 것은 불가능하다. 왜냐하면 각 기업이 어떤 프로토콜을 사용하는지를 공개하지 않기 때문이다. 때문에, 모든 서비스에 접근하여 중재역할을 하기 위해서는 각 게이트웨이를 해킹하는 수밖에는 없다.

블록체인은 위의 세가지 해안이 극복하지 못하는 많은 부분을 해결해 줄 해안으로 떠올랐고 그것이 블록체인의 시작이다.

참고자료 - Programmable Blockchains in Context: Ethereum’s Future by vinay gupta

블록체인이란 무엇인가요?

블록체인이란 수많은 컴퓨터들을 연결하여 모든 컴퓨터가 동일한 체인 형태의 정보 리스트를 가지도록 하는 분산 컴퓨팅 기반의 데이터 위변조 기술이다.
이 때 저장하고자 하는 데이터는 작은 정보들의 집합인 블록으로 구성되고 각 블록들이 서로 연결고리를 가지게 하여 누구도 중간 블록을 수정할 수 없고, 변경의 결과를 열람할 수 있게된다.

이러한 블록체인의 가장 큰 특징은 바로 분산 노드의 운영자가 임의로 데이터를 조작하지 못한다는 것이다.
가령 기존의 데이터베이스의 경우 데이터베이스의 관리자가 손쉽게 데이터베이스를 조작할 수 있었고, 악한 마음을 가진다면 이를 기정사실화 하여 각종 보안 문제들에 휘말리게 된다. 하지만 블록체인의 경우 각 블록들이 수정되면 전체 연결고리가 깨어지게 되어 근본적으로 기존의 데이터를 수정하지 못하도록 고안되었기에 데이터베이스 관리자가 모든 권한을 가졌던 기존의 중앙집권적 권력체계가 무너지게 된다고 볼 수 있기에 보다 민주적인 형태의 데이터 저장 기술로 칭송받고 있다.

이러한 분산 시스템을 운영함에 있어 가장 큰 문제 중 하나는 비잔틴 장군 문제(Byzantine General Problems) 이다.
여기서는 적군의 도시를 공격하려는 비잔티움 제국군의 여러 부대가 지리적으로 떨어진 상태에서 각 부대의 지휘관들이 (중간에 잡힐지도 모르는) 전령을 통해 교신하면서 공격 계획을 함께 세우는 상황을 가정하고 있습니다. 이 부대의 지휘관 중 일부에는 배신자가 섞여있을 수 있고, 배신자는 규칙을 충실히 따르는 충직한 지휘관들과 달리 규칙에 얽매이지 않고 마음대로 행동할 수 있다. 이 때 배신자의 존재에도 불구하고 충직한 지휘관들이 동일한 공격 계획을 세우기 위해서는 충직한 지휘관들의 수가 얼마나 있어야 하며, 이 지휘관들이 어떤 규칙을 따라 교신해야 하는지에 대한 문제가 비잔티움 장군 문제이다.

블록체인 기술은 이러한 비잔틴 장군 문제를 최초로 해결하였으며 비잔틴 장군 문제에 대한 내성이 있다는 의미로 비잔틴 장애 허용(BFT) 시스템 중의 하나이다.

블록체인 플랫폼 이더리움 백서에 대한 설명

현대 가상화폐의 근간이 되는 블록체인 기술의 핵심은 시간의 흐름에 따른 각 노드의 상태를 저장하는 UTXO(Unspent Transaction Output) 를 모두 저장하는 것이다.
각 트랜잭션은 k개의 input을 가지고 각 입력은 UTXO에 대한 레퍼런스를 가지며 특정 노드의 상태는 이러한 UTXO의 리스트로 구성된다.

블록은 10분 간격으로 생성되며, 각 블록은 timestamp, nonce, hash of previous block, transaction list 로 구성되며 블록을 생성하기 위한 과정을 채굴(mining) 이라고 하며 다음의 단계에 따라 진행된다.

  1. 이전 블록의 해시값 검사
  2. 블록의 timestamp가 이전 블록의 것보다 크고 2시간이 경과하지 않은지 검사
  3. POW
  4. 상태집합 S 의 S[0] 에 이전 블록의 마지막 상태값을 저장
  5. 0 ~ n-1 의 모든 i에 대해 S[i+1] = APPLY(S[i], TX[i]) 를 수행
  6. 마지막 블록에 상태값 S[n] 를 등록

위 과정을 완료한 뒤에 해당 블록의 헤더를 해싱하고 그 해시를 풀기 위한 nonce를 무작위값을 계속 대입하면서 구함. = > mining

블록의 구조

블록은 위에서 언급했듯이 timestamp, nonce, hash of previous block, transaction list 로 구성되는데, 이 중에서 transaction list는 블록 데이터 부분에 저장되고 나머지는 블록 헤더에 저장이된다. 또, 블록 헤더에는 블록데이터에 대한 해시값이 저장되는데, 이 해시값은 머클트리 형태로 저장된 트랜잭션 리스트의 root hash의 값으로 하나의 트랜잭션이라도 조작되면 이 값이 바뀌게 되어 블록이 변질되었음을 알 수 있게 된다.

여기서 머클트리 란, 가령 n 개의 트랜잭션이 있다면 높이가 log(n)인 이진트리 형태로 구성되며, 각 부모는 자식의 해시를 가지게 되어 하나의 트랜잭션만 바뀌어도 수많은 노드의 해시값이 바뀌게 되는 트리이다.

이러한 블록들은 level DB 에 bloack hash 를 키로 블록 데이터를 value로 저장됩니다.
이러한 db의 종류는 block_hash db, block_number db, transaction db 등이 있습니다.

보안의 원리

블록체인은 어떠한 원리로 보안을 유지하는가?

먼저 다음과 같은 경우를 통해 블록체인을 훼손하는 경우를 생각해 보자.
먼저 가장 마지막 블록번호가 270번 블록이라고 하자.

  1. 사용자가 특정 상인에게 100BTC 를 입금한다.
  2. 상인이 사용자에게 디지털 재화를 전송한다(순식간에 전송 가능하다고 전재한다.)
  3. 사용자가 스스로에게 100BTC를 입금한다.

이 경우 블록체인은 3의 트랜잭션을 보 정상적인 state에 있지 않은 utxo라 판단하게 되고 사용자의 블록체인은 따로 분기되게 된다.
만약 그 동안 1시간이 경과하였다면 하지만 그와 동시에 다른 사용자들은 계속해서 블록을 생성하여 271~275까지 많은양의 블록을 생성한 상태이고 그들이 가리키는 해시값은 사용자의 270번 블록이 아닌 블록체인의 270번 블록을 가리키게 되고 사용자의 노드는 기존 블록체인에서 분리되게 된다.

이더리움

이더리움의 기본 단위는 account 이며, EOA(External Owned Account) 와 Contract 두 종류의 account 가 있습니다.

EOA 는 메세지를 보내는 주체이며 message에 의해 contract가 조작됩니다.
여기서 message는 가상의 객체이며 송/수신자 정보와 데이터, eth 양 등의 정보를 담고 있습니다.ㄴ

Contract가 Call 을 수행할때 생성됩니다.

블록체인의 비잔틴 장군 문제 해결

블록체인에서는 모든 정보를 블록이라는 정보 뭉치의 연결로 보고 각 블록에는 암호화된 해시값이 있어 다음 블록을 가리키고 있는 형태로 구성되어 있습니다.
또한 다음 블록이 생성되기 위해서는 참여하는 노드의 절반이상이 해당 노드가 올바른 노드라고 판단해야 다음 블록이 연결될 수 있기 때문에, 악의적인 목적을 가진 노드가 몇 개 존재한다고 하더라도 안정적으로 동작을 함을 말합니다.

합의 알고리즘

POW

새로운 화폐가 생성되는 과정(조폐)에서, 생성자들(채굴자들)에게 “일을 했다는 것을 증명(proof of work)”하는 것을 강제하여 화폐의 가치와 보안을 보장하는 방식이다.

분산 네트워크에서는 조폐 과정에서 누가 얼마의 새 화폐를 받을지를 결정할 중앙 권력이 없기 때문에 모든 참여자들이 자동적으로 동의할 수 있는 방법이 필요하다. 이때 일방향함수인 해시 함수가 계산(검산)하기는 쉽지만 역을 구하는 것(채굴)은 어렵다는 것에 착안하여, 모든 참여자가 해시 함수를 계산해서 가장 먼저 계산한 사람이 새 화폐를 받아가게 하는 것.

최초로 상업적 성공을 거둔 암호화폐 비트코인의 경우, 블록체인의 다음 블록을 캐기 위한 해시 함수의 입력값에 거래내역을 담은 블록체인의 최신값을 연동시켜서, 송금과 조폐 양 기능과 보안을 동시에 해결하였다.

나무 위키 참조

POS

암호화폐에서 proof-of-stake의 이니셜. 가치 증명, 또는 지분 증명으로 번역된다.

POW와는 근본적으로 다른 조폐 개념으로, 신규 코인을 발행할 때 이미 기존에 코인을 가지고 있던 사람들에게 전체 대비 각각의 소지량의 비율만큼을 지급하는 방식이다. 즉 암호화폐하면 으레 생각하는 채굴이 없다! 단, 현실적으로 액수에 관계없이 코인을 보유한 모든 지갑에 일괄적으로 %를 더하는 것은 불가능하므로, 조폐 과정에 참여하고 싶은 사람들이 일정 액수를 걸고(stake), 렌덤하게 추첨하여 참여자들 중 한명에게 다음 블록 보상(신규 코인)이 돌아가는 방식을 쓴다. 당연히 더 많이 걸수록 당첨 확률이 늘어난다. 참여에 필요한 최소 액수가 적어도 한화 수억원 어치이므로, 일반인들은 마이닝 풀과 유사한 개념인 스테이크 풀에 코인을 위탁해서 추첨에 참여한다.

얼핏 들으면 돈놓고 돈먹는 헤괴한 시스템으로 보일 수 있으나, 코인캡의 등락 없이는 신규 코인이 잔뜩 발행돼도 개당 가격이 내려가므로 각 코인 소유자들의 자산 가치는 동일하게 유지됨을 알 수 있다. 특정 집단이 과반수를 차지할 경우 그들의 지분이 계속 늘어나는 폐해가 벌어질 수 있으나, 채굴의 51%공격과 마찬가지로 당사자들의 이해관계 때문에 자정능력을 가진다. 채굴(POW) 대비 가장 큰 장점은 채굴에 들어가는 막대한 양의 에너지 낭비 없이 조폐와 거래를 운용할 수 있다는 점이다. 단점은 탈중앙화된 신규 코인은 이런 식으로 발행하기가 쉽지 않다는 점.

퀀텀(Qtum) 등에서 POS를 채택하고 있다.

POW와 POS 외에 POI라는 기술도 있다.

나무위키 참조

토큰 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pragma solidity ^0.4.20;

contract MyToken {
event Transfer(address indexed from, address indexed to, uint256 value);
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
string public name;
string public symbol;
uint8 public decimals;
/* Initializes contract with initial supply tokens to the creator of the contract */
function MyToken(uint256 initialSupply, string tokenName, string tokenSymbol, uint8 decimalUnits) public {
balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens
name = tokenName; // Set the name for display purposes
symbol = tokenSymbol; // Set the symbol for display purposes
decimals = decimalUnits; // Amount of decimals for display purposes
}

/* Send coins */
function transfer(address _to, uint256 _value) public {
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
require(balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
balanceOf[msg.sender] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
/* Notify anyone listening that this transfer took place */
emit Transfer(msg.sender, _to, _value);
}
}

먼저 crowd sale을 진행하기 전에 crowd sale에서 사용할 토큰을 생성하고, crowd sale의 shares address에 등록해 준다.

crowd sale contract

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
pragma solidity ^0.4.16;

interface token {
function transfer(address receiver, uint amount) external;
}

contract Crowdsale {
address public beneficiary;
uint public fundingGoal;
uint public amountRaised;
uint public deadline; //마감 기한
uint public price;
token public tokenReward;
mapping(address => uint256) public balanceOf;
bool fundingGoalReached = false;
bool crowdsaleClosed = false;

event GoalReached(address recipient, uint totalAmountRaised);
event FundTransfer(address backer, uint amount, bool isContribution);

/**
* Constructor function
*
* Setup the owner
*/
function Crowdsale(
address ifSuccessfulSendTo, //
uint fundingGoalInEthers, //이더 단위의 모금 목표액
uint durationInMinutes, //분 단위의 모금 기간
uint etherCostOfEachToken, //각 토큰 당 단위 가격(이더 기준)
address addressOfTokenUsedAsReward //모금의 보상으로 주어질 위에서 만든 토큰의 주소
) public {
beneficiary = ifSuccessfulSendTo;
fundingGoal = fundingGoalInEthers * 1 ether;
deadline = now + durationInMinutes * 1 minutes;
price = etherCostOfEachToken * 1 ether;
tokenReward = token(addressOfTokenUsedAsReward);
}

/**
* Fallback function
*
* The function without name is the default function that is called whenever anyone sends funds to a contract
*/
function () payable public {
require(!crowdsaleClosed);
uint amount = msg.value;
balanceOf[msg.sender] += amount;
amountRaised += amount;
tokenReward.transfer(msg.sender, amount / price);
emit FundTransfer(msg.sender, amount, true);
}

modifier afterDeadline() { if (now >= deadline) _; }

/**
* Check if goal was reached
*
* Checks if the goal or time limit has been reached and ends the campaign
*/
function checkGoalReached() afterDeadline public {
if (amountRaised >= fundingGoal){
fundingGoalReached = true;
emit GoalReached(beneficiary, amountRaised);
}
crowdsaleClosed = true;
}


/**
* Withdraw the funds
*
* Checks to see if goal or time limit has been reached, and if so, and the funding goal was reached,
* sends the entire amount to the beneficiary. If goal was not reached, each contributor can withdraw
* the amount they contributed.
*/
function safeWithdrawal() afterDeadline public {
if (!fundingGoalReached) {
uint amount = balanceOf[msg.sender];
balanceOf[msg.sender] = 0;
if (amount > 0) {
if (msg.sender.send(amount)) {
emit FundTransfer(msg.sender, amount, false);
} else {
balanceOf[msg.sender] = amount;
}
}
}

if (fundingGoalReached && beneficiary == msg.sender) {
if (beneficiary.send(amountRaised)) {
emit FundTransfer(beneficiary, amountRaised, false);
} else {
//If we fail to send the funds to beneficiary, unlock funders balance
fundingGoalReached = false;
}
}
}
}

공자께서 말씀하셨다. “백성들을 정치로 인도하고 형벌로 다스리면, 백성들은 형벌을 면하고도 부끄러워함이 없다. 그러나 덕으로 인도하고 예로써 다스리면, 백성들은 부끄러워할 줄도 알고 또한 잘못을 바로잡게 된다.”
-논어 [위정]

현대의 대부분의 정치체제는 제도로 하여금 사람들의 사회적 행동을 이끌어 내고자 노력합니다. 잘못을 한 사람들을 감옥에 가두고 벌금을 메기고, 사회적으로 필요한 질서에 관한 법령을 제정하여 사람들에게 법령을 지키도록 강요합니다. 기업도 마찬가지 입니다. 좋은 실적을 낸 직원에게는 상을 주고 근무 태도가 좋지 않은 직원에게는 불이익을 주는 등 규율을 세우고 이를 지키도록 함으로써 조직을 운영해 나갑니다.

이렇게 훌륭한 제도와 법규로 조직을 개선하려는 노력은 어떻게 보면 규모가 매우 큰 조직을 통제하기에는 유일한 수단으로 보입니다.

공자의 이런 말은 오늘날에 많은 생각할 거리를 줍니다.

조직의 질서와 개인의 변화를 제도와 상벌만으로 다스린다면 개인은 행동의 변화시킬 이유를 찾지 못하고 되려 제도를 회피하고자하는 행동을 촉발할 수 있습니다.
하지만, 국가가 시민에게 덕을 바탕으로 한 시민의식을 심어주고 또, 회사가 개인에게 기업의 비전과 기업 문화를 배양해 준다면 개인은 자발적으로 일의 의미를 가지고 보다 주도적으로 일을 해 나갈 수 있다는 것은 참 실현하기 힘들고 번거로운 일이지만 어쩌면 조직을 이끌어 나가는 핵심이자 리더의 자질이 아닐까 생각합니다.

이 글을 읽는 독자분들도 덕과 예로써 주변 사람들을 대하는 리더가 되기를 기원합니다.

기본 contract 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pragma solidity ^0.4.20;

contract MyToken {
event Transfer(address indexed from, address indexed to, uint256 value);
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
string public name;
string public symbol;
uint8 public decimals;
/* Initializes contract with initial supply tokens to the creator of the contract */
function MyToken(uint256 initialSupply, string tokenName, string tokenSymbol, uint8 decimalUnits) public {
balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens
name = tokenName; // Set the name for display purposes
symbol = tokenSymbol; // Set the symbol for display purposes
decimals = decimalUnits; // Amount of decimals for display purposes
}

/* Send coins */
function transfer(address _to, uint256 _value) public {
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
require(balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
balanceOf[msg.sender] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
/* Notify anyone listening that this transfer took place */
emit Transfer(msg.sender, _to, _value);
}
}

상속을 이용한 보다 심플란 contract 작성하기

is owned를 통해 상속을 하여 owner 변수와 onlyOwner modifier에 접근할 수 있게 되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
pragma solidity ^0.4.20;

contract owned {
address public owner;

function owned() {
owner = msg.sender;
}

modifier onlyOwner {
require(msg.sender == owner);
_;
}

function transferOwnership(address newOwner) onlyOwner {
owner = newOwner;
}
}

contract MyToken is owned{
event Transfer(address indexed from, address indexed to, uint256 value);
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
string public name;
string public symbol;
uint8 public decimals;
/* Initializes contract with initial supply tokens to the creator of the contract */
function MyToken(
uint256 initialSupply,
string tokenName,
uint8 decimalUnits,
string tokenSymbol,
address centralMinter
) {
if(centralMinter != 0 ) owner = centralMinter;
}

/* Send coins */
function transfer(address _to, uint256 _value) public {
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
require(balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
balanceOf[msg.sender] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
/* Notify anyone listening that this transfer took place */
emit Transfer(msg.sender, _to, _value);
}
}

#

향상된 최종 코인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
pragma solidity ^0.4.16;

contract owned {
address public owner;

function owned() public {
owner = msg.sender;
}

modifier onlyOwner {
require(msg.sender == owner);
_;
}

function transferOwnership(address newOwner) onlyOwner public {
owner = newOwner;
}
}

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }

contract TokenERC20 {
// Public variables of the token
string public name;
string public symbol;
uint8 public decimals = 18;
// 18 decimals is the strongly suggested default, avoid changing it
uint256 public totalSupply;

// This creates an array with all balances
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;

// This generates a public event on the blockchain that will notify clients
event Transfer(address indexed from, address indexed to, uint256 value);

// This notifies clients about the amount burnt
event Burn(address indexed from, uint256 value);

/**
* Constrctor function
*
* Initializes contract with initial supply tokens to the creator of the contract
*/
function TokenERC20(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) public {
totalSupply = initialSupply * 10 ** uint256(decimals); // Update total supply with the decimal amount
balanceOf[msg.sender] = totalSupply; // Give the creator all initial tokens
name = tokenName; // Set the name for display purposes
symbol = tokenSymbol; // Set the symbol for display purposes
}

/**
* Internal transfer, only can be called by this contract
*/
function _transfer(address _from, address _to, uint _value) internal {
// Prevent transfer to 0x0 address. Use burn() instead
require(_to != 0x0);
// Check if the sender has enough
require(balanceOf[_from] >= _value);
// Check for overflows
require(balanceOf[_to] + _value > balanceOf[_to]);
// Save this for an assertion in the future
uint previousBalances = balanceOf[_from] + balanceOf[_to];
// Subtract from the sender
balanceOf[_from] -= _value;
// Add the same to the recipient
balanceOf[_to] += _value;
emit Transfer(_from, _to, _value);
// Asserts are used to use static analysis to find bugs in your code. They should never fail
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}

/**
* Transfer tokens
*
* Send `_value` tokens to `_to` from your account
*
* @param _to The address of the recipient
* @param _value the amount to send
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}

/**
* Transfer tokens from other address
*
* Send `_value` tokens to `_to` in behalf of `_from`
*
* @param _from The address of the sender
* @param _to The address of the recipient
* @param _value the amount to send
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]); // Check allowance
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}

/**
* Set allowance for other address
*
* Allows `_spender` to spend no more than `_value` tokens in your behalf
*
* @param _spender The address authorized to spend
* @param _value the max amount they can spend
*/
function approve(address _spender, uint256 _value) public
returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}

/**
* Set allowance for other address and notify
*
* Allows `_spender` to spend no more than `_value` tokens in your behalf, and then ping the contract about it
*
* @param _spender The address authorized to spend
* @param _value the max amount they can spend
* @param _extraData some extra information to send to the approved contract
*/
function approveAndCall(address _spender, uint256 _value, bytes _extraData)
public
returns (bool success) {
tokenRecipient spender = tokenRecipient(_spender);
if (approve(_spender, _value)) {
spender.receiveApproval(msg.sender, _value, this, _extraData);
return true;
}
}

/**
* Destroy tokens
*
* Remove `_value` tokens from the system irreversibly
*
* @param _value the amount of money to burn
*/
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
balanceOf[msg.sender] -= _value; // Subtract from the sender
totalSupply -= _value; // Updates totalSupply
emit Burn(msg.sender, _value);
return true;
}

/**
* Destroy tokens from other account
*
* Remove `_value` tokens from the system irreversibly on behalf of `_from`.
*
* @param _from the address of the sender
* @param _value the amount of money to burn
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
require(balanceOf[_from] >= _value); // Check if the targeted balance is enough
require(_value <= allowance[_from][msg.sender]); // Check allowance
balanceOf[_from] -= _value; // Subtract from the targeted balance
allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance
totalSupply -= _value; // Update totalSupply
emit Burn(_from, _value);
return true;
}
}

/******************************************/
/* ADVANCED TOKEN STARTS HERE */
/******************************************/

contract MyAdvancedToken is owned, TokenERC20 {

uint256 public sellPrice;
uint256 public buyPrice;

mapping (address => bool) public frozenAccount;

/* This generates a public event on the blockchain that will notify clients */
event FrozenFunds(address target, bool frozen);

/* Initializes contract with initial supply tokens to the creator of the contract */
function MyAdvancedToken(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) TokenERC20(initialSupply, tokenName, tokenSymbol) public {}

/* Internal transfer, only can be called by this contract */
function _transfer(address _from, address _to, uint _value) internal {
require (_to != 0x0); // Prevent transfer to 0x0 address. Use burn() instead
require (balanceOf[_from] >= _value); // Check if the sender has enough
require (balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
require(!frozenAccount[_from]); // Check if sender is frozen
require(!frozenAccount[_to]); // Check if recipient is frozen
balanceOf[_from] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
emit Transfer(_from, _to, _value);
}

/// @notice Create `mintedAmount` tokens and send it to `target`
/// @param target Address to receive the tokens
/// @param mintedAmount the amount of tokens it will receive
function mintToken(address target, uint256 mintedAmount) onlyOwner public {
balanceOf[target] += mintedAmount;
totalSupply += mintedAmount;
emit Transfer(0, this, mintedAmount);
emit Transfer(this, target, mintedAmount);
}

/// @notice `freeze? Prevent | Allow` `target` from sending & receiving tokens
/// @param target Address to be frozen
/// @param freeze either to freeze it or not
function freezeAccount(address target, bool freeze) onlyOwner public {
frozenAccount[target] = freeze;
emit FrozenFunds(target, freeze);
}

/// @notice Allow users to buy tokens for `newBuyPrice` eth and sell tokens for `newSellPrice` eth
/// @param newSellPrice Price the users can sell to the contract
/// @param newBuyPrice Price users can buy from the contract
function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}

/// @notice Buy tokens from contract by sending ether
function buy() payable public {
uint amount = msg.value / buyPrice; // calculates the amount
_transfer(this, msg.sender, amount); // makes the transfers
}

/// @notice Sell `amount` tokens to contract
/// @param amount amount of tokens to be sold
function sell(uint256 amount) public {
require(address(this).balance >= amount * sellPrice); // checks if the contract has enough ether to buy
_transfer(msg.sender, this, amount); // makes the transfers
msg.sender.transfer(amount * sellPrice); // sends ether to the seller. It's important to do this last to avoid recursion attacks
}
}

Greeter Contract 만들기

Greeter.sol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
contract Mortal {
/* Define variable owner of the type address */
address owner;

/* This function is executed at initialization and sets the owner of the contract */
function Mortal() { owner = msg.sender; }

/* Function to recover the funds on the contract */
function kill() { if (msg.sender == owner) selfdestruct(owner); }
}

contract Greeter is Mortal {
/* Define variable greeting of the type string */
string greeting;

/* This runs when the contract is executed */
function Greeter(string _greeting) public {
greeting = _greeting;
}

/* Main function */
function greet() constant returns (string) {
return greeting;
}
}

solidity compiler 설치 및 컴파일 하기

solcjs 설치하기

1
npm install -g solc

컴파일 하기

1
solcjs -o target --bin --abi Greeter.sol

js 스크립트 작성

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var greeterFactory = eth.contract(<contents of the file Greeter.abi>)

var greeterCompiled = "0x" + "<contents of the file Greeter.bin>"

var _greeting = "Hello World!"

var greeter = greeterFactory.new(_greeting,{from:eth.accounts[0],data:greeterCompiled,gas:47000000}, function(e, contract){
if(e) {
console.error(e); // If something goes wrong, at least we'll know.
return;
}

if(!contract.address) {
console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");

} else {
console.log("Contract mined! Address: " + contract.address);
console.log(contract);
}
})

geth console 에서 스크립트 실행

1
loadScript("main.js")

채굴 시작 및 greeter 실행

1
miner.start()

채굴이 완료되면 contract에 address가 부여되고 계약이 발행된다.

이제는 블록체인 상에 greeter라는 봇이 생성되었으므로 다음과 같이 자유롭게 호출하여 사용이 가능하다.

1
greeter.greet();

공자께서 말씀하셨다. “남이 자신을 알아주지 못할까 걱정하지 말고 내가 남을 제대로 알지 못함을 걱정해야 한다.”

우리는 사회생활을 하면서 이런 생각을 참 많이도 합니다.

“아 저건 너무한거 아니야?”

“어떻게 저럴 수 있지?”

“배려가 너무 부족해”

이런 많은 상황들을 친구에게 이야기 하면 대부분은 이런식을 대답을 해 주곤 합니다.

“정말 너무하네!”

“그건 아니지!”

이런 태도는 단 한가지의 결론만을 가져다 줍니다.

“그 사람은 이상한 사람이다.”

하지만, 이런식의 사고를 통한다면 이 세상에는 얼마나 많은 이상한 사람들이 있는 것일까요?

우리의 이런 삶은 공자가 본다면 아마도 이렇게 말하지 않을까 싶습니다.

“상대방의 마음을 잘 헤아려 보았는가?”

우리가 나와 다른 사람을 둘러싼 상황에서 상대방을 마음을 제대로 헤아리는 경우는 얼마나 될까요?

이런식의 사고는 사실 단순한 인간 관계를 넘어 국제 외교, 사업 협상 테이블 등 많은 경우에 적용할 수 있는 사고인 것 같아 보입니다.

누군가를 대할 때 냉철한 판단을 할 수 있는 가장 효과적인 방법은 내가 제안한 훌륭한 제안을 상대방이 이해하지 못하는 것을 탓하는 것이 아니라 바로 상대방을 올바로 아는 것이기 때문이지요

누군가가 나를 알아주지 않을 때 내가 혹시 상대방을 제대로 알지 못하는 것은 아닐까라고 생각해 볼 수 있는 하루가 되었으면 합니다.

공자께서 말씀하셨다. “배우고 때때로 그것을 익히면 또한 기쁘지 않은가? 벗이 먼 곳에서 찾아오면 또한 즐겁지 않은가? 남이 알아주지 않아도 성내지 않는다면 또한 군자답지 않은가?”

위 글귀는 논어의 [학이]편에 나오는 글귀입니다.

요즘 세상을 살아가면서 우리는 매일매일 새로운 것을 배워야만 합니다.

기술을 빠르게 발전하고 조금이라도 주춤하면 언젠가는 세상에서 도태될 질 모른다는 두려움을 가지기도 합니다.

이런 요즘 세상에서 우리는 배움에 대한 의심을 품게 됩니다.

배움이란 세상에서 살아남기 위해 또 다른 사람과의 경쟁에서 승리하기 위해 해야만 한다는 무릇 잘못된 생각을 하게 됩니다.

공자는 배움이란 즐거움이라고 말합니다. 우리가 배우고 익힌다는 것은 즐거운 일이고 이를 즐겁게 여기는 것이 군자의 덕목이라고 말합니다. 훌륭한 사람이라면 배움을 즐길줄 알아야 한다는 것이지요.

또한, 공자는 “때때로 그것을 익힌다” 는 말에서 우리가 배운 것을 몸소 활용하고 몸에 익히고 배운것을 항상 실생활에서 적용할 기회가 있을 때마다 수시로 반복하여 익히는 것이 필요하다고 말하며 학문의 실용적 측면을 강조합니다.
저는 특히 이 구절이 와닿았습니다. 20년이 넘는 세월 대학을 졸업하면서 까지 정말 하루하루 배움의 연속이었지만 사실 학교를 다니면서 배운것을 익힐 시간은 많이 없었던 것 같습니다. 시험을 위해, 남들보다 더 좋은 실력을 가지고 있음을 증명하기 위해 공부를 해왔던 제 자신을 돌아보게 되는 문단이지요. 공대생으로써 공학을 배워 새로운 제품 혹은 개선방안을 찾기위해 노력하고, 경제와 철학을 배웠다면 배운 내용을 말미암아 보다 통찰을 가지고 세상을 바라보았어야 하지 않나라는 생각이 듭니다.
이 글도 또한 같은 맥락에서 적게 된 것이지요, 내가 논어를 통해 좋은 글귀를 읽고 배움을 얻었다면 이를 내 삶과 인생에 접목시켜보는 것이 반드시 필요하다라는 좋은 교훈을 얻은 것 같습니다.

위 글의 마지막 문장은 요즘 우리 세상 또 저 자신에게 큰 교훈을 줍니다.

항상 시험을 보면 좋은 학점을 받았다는 사실을 과시하고 싶고, 내가 공부를 열심히 했는데도 낮은 학점을 받으면 나는 더 나은 사람인데 이정도 밖에 인정받지 못했다고 혼자 주제넘는 생각을 하기도 한 옛날을 제 모습을 많이 반성합니다.

이 글을 읽는 독자분들은 배움에 어떤 태도를 지니고 계신가요?

혹 배움을 살아남기 위한 수단으로 남에게 인정받기 위한 수단으로 생각된 적은 없으셨나요?

이 글을 통해 좀 더 넉넉한 마음으로 배움에 임할 수 있는 하루가 되었으면 합니다 ^^

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×