주제를 Go로 선정한 이유는 사내 개발 세미나에서 그 동안 다양한 주제를 다 뤘었지만 의외로 프로그래밍 랭귀지에 대해서는 언급한 적이 없었던 것 같아서 선정하 게 되었습니다. 요즘 개인적으로 토플을 공부하고 있는데, 외국어를 공부하면서 느끼는 점이 있습니다. 언어가 의사를 전달하는 도구이기 때문에 구사하는 언어가 제공하는 프레임 에 맞춰서 사고체계도 바뀐다는 점입니다. 프로그래밍도 마찬가지인 것 같습니다. 인터프리터 언어를 할 때와 컴파일 언 어를 사용할 때가 생각하는 방식이 많이 다르니까요 그런 점에서 Go는 많은 생각할 거리들을 제공하는 언어입니다. Go라는 새로운 도구를 익혀보는 시간이 되었으면 좋겠습니다. 4
되는 B언어를 만든 Ken Thompson, 켄 톰슨과 함께 UTF-8을 설계한 Rob Pike, V8 엔진 개발자였던 Robert Griesemer 슬라이드에 실린 켄 톰슨에 대한 인터뷰를 보면, “C++이 싫어서 언어를 설계해 봤는데 어떤 이유에서든지 불필요한 스펙이 발생하지 않도록 작성했다.” 라고 합니다. 구글에서 남는시간 프로젝트로 시작한 건데 이 엄청난 사람들은 1년의 시간 동안 파트타임으로 하면서 언어 스펙부터 컴 파일러까지 모두 완성을 시켰습니다. Google HQ에 재직 중이신 분이 Go가 그냥 흥미위주로 만들어진 언어가 아 니라 구글 내에 실제로 많은 Production project에서 Go를 사용한다는 이야기를 해주셔서 처음 이 언어에 관심을 가지게 되었습니다. 5
효율성과, 컴파일 언어의 안정성을 하나로 묶으려고 시도한 프로젝트입니다. 이를 통해서 네트워크와 멀티코어 컴퓨팅을 쉽게 지원하고, 빌드하는 시간이 빠르길 원했습니다. 그래서 동시성과 가비지 콜렉션을 지원하고 의존성에 대한 스펙을 엄격하게 해야했습니다. 이런 것들은 라이브러리로 해결하기 힘들었기 때문에 언어를 만든 것입니다. Go는 탄생한지 5년 정도 되었고, 오픈소스임을 강조하고 있습니다. 구글의 사 유재산처럼 보이면 많은 개발자들이 참여하지 않을 것을 우려한 것 같습니다. 정적 타입을 가진 컴파일 언어지만 타입 추론을 하기 때문에 코드에서 선언 할 땐 타입을 생략하는 경우가 많이 있습니다. 클래스나 상속의 개념은 없습니다. 관련된 내용은 다시 설명드리겠습니다. 언어의 성능이야기 하는 것은 마치 “이 곳은 너무 평화롭군. Emacs는 최악이 다. Vi가 좋다고 생각한다” 라고 이야기하는 경우와 같지만, 기계어로 컴파일 되어 나오는 특성상 인터프리터 언어보다 빠를 수 밖에 없 습니다. 사실 Go는 굉장히 빌드가 빠르고 설계 단계에서도 그런 점을 많이 고려했다 고 하는데 의존성 관리와 체크도 상당히 간편하게 되어있어서 C언어로 코딩하면서 느낄 수 있는 좌절들을 상당히 줄여주고 코드도 짧게 해 주면서 고성능을 얻을 수 있다는 정도로 생각해주시면 될 것 같습니다. 6
다. Java처럼 가비지 콜렉션을 지원합니다. 런타임 리플렉션을 통해서 컴파일 속 도를 높였습니다. UTF-8을 설계한 사람이 두명이나 있어서 영향을 받았는지 모든 입출력은 기 본적으로 유니코드입니다. 튜토리얼도 Hello World가 아니라 “Hello 세계” 라는 한자로 쓰여서 유니코드 사용을 강조하고 있습니다. Node.js의 NPM이나 bower, docker처럼 요즘 추세인 Remote Package Management를 지원합니다. 원하는 라이브러리나 공개된 타인의 패키지가 있으면 “go get ~” 을 입력하 면 바로 받아올 수 있습니다. 7
쓰레드 생성의 부하를 줄이기 위해서 Goroutine이라는 경량 쓰레드를 지원하 는데, 그냥 새 쓰레드로 실행시키고 싶은 함수 앞에 “go”라는 디렉티브만 입력해주 면 그 함수는 새로운 쓰레드에서 돌게 됩니다. 사실 우리가 생각하는 OS의 쓰레드는 아닙니다. 사실 런타임의 Thread는 Worker Pool 형태로 관리됩니다. Go의 경량 쓰레드는 OS에서 제공하는 쓰레드가 아니라 coroutine처럼 독립 적으로 실행되는 함수들로 구성되어 있습니다. (coroutine은 caller-callee의 종속관계가 아니라 caller와 계속 대화를 주고받 는 방식으로 짜여진 함수입니다.) 실행중인 Goroutine이 Block되면 자동적으로 런타임 쓰레드에서 해당 Goroutine을 빼고 다른 Goroutine으로 교체합니다. 결국 개발자 인위적인 Thread나 Process의 생성은 없는 것입니다. Context-switching으로 인한 오버헤드가 굉장히 적고, 대신 Stack의 사이즈를 동적으로 관리합니다. Go는 Stack을 작게 만들기 위해서 분할 스택을 사용합니다. (분할 스택이란 heap 영역에 각각 독립된 stack처럼 동작하는 자료구조를 올 리는 것입니다. 구현은 List 같은 것이 될 수 있습니다. 기존의 stack이 동일한 주소공간을 공유하기 때문에 발생하는 문제들을 해결 합니다.) 그래서 신규 고루틴은 겨우 몇 kb만 할당받고, 공간이 부족하면 자동으로 공 8
생성할 수 있 습니다. 이런 특징 때문에 Goroutine을 호출해서는 Stackoverflow가 발생하지 않습니 다. Stack에 쌓는게 아니기 때문이죠. 힙이 꽉찼을 때 메모리 오류가 나는 것입니다. 채널은 동시성 모델중 CSP(Communicating Sequential Processes)이라는 것 에서 가져온 개념입니다. 채널은 값을 주고 받는 파이프인데, 송수신시 상대방이 준비 될 때까지 Block 되기 때문에 채널을 사용하는 고루틴 코드에 명시적인 락을 걸지 않고도 동기화 될 수 있 도록 합니다. Go는 객체지향 패러다임의 일부분만 차용하고 있고, 클래스와 상속같은 것은 지원하지 않습니다. 메소드 리시버라는 것을 함수 선언에 넣으면 그 함수는 리시버가 가르키는 타입에 부착된 함수가 됩니다. 타입은 구조체가 될 수도 있고, 재정의한 Premitive 타입이 될 수도 있습니다. Go에서 인터페이스는 메소드의 집합입니다. 어떤 인터페이스를 사용하겠다 고 따로 명시하는 것은 없고, 인터페이스 내의 함수를 구현하면 그냥 암시적으로 계약을 만족했다라고 추 론합니다. 8
대신 레퍼런스를 전달하기 위한 용도로만 사용이 가능합니다. 포인터 연산을 두지 않은 이유는 개발자 과실로 잘못된 주소를 얻어 오는 것이 가능하기 때문도 있고, 가비지 콜렉터의 구현을 단순화하기 위한 목적도 있다고 합니다. 컴파일러 기술이 발전하여 포인터 연산을 이용하지 않더라도 Array 인덱스를 활용한 반복이 충분히 효율적이기 때문에 때문에 괜찮다고 설명하고 있습니 다. Go는 기본적으로 값에 의한 전달을 합니다. 17
인터페이스에 기술된 메소드와 동일한 스펙을 만족하면 인터페이스를 암시적 으로 충족시켰다고 인지합니다. Float64를 반환하는 Abs() 함수가 있으면 되는데, MyFloat는 만족하고 Vertex의 포인터를 받는 Vertex의 Abs 함수도 만족하지 만 그냥 Vertex 값은 못받기 때문에 세번째는 오류가 납니다. 26
지정되어있다면 지정되지 않은 모든 요청은 디폴트 함수를 타게 됩 니다. Handle은 처리할 URL과 함수를 지정합니다. Handle되기 위해서는 ServeHTTP라는 이름의 위와같은 스펙을 만족하는 함 수가 타입에 부착되어 있어야 합니다. 그럼 인터페이스들의 정의는 패키지 문서에서 볼 수 있습니다. Go는 template이라는 패키지도 지원하는데 Go의 기본 템플릿 문법이 Mustache와 매우 닮아있어서 학습이 쉽습니다. 간단한 웹 어플리케이션 이라면 그냥 이런식으로 구현하면 되지만, 복잡한 REST가 들어간다면 Framework을 사용하기를 추천합니다. 30
벤치마크와 유사한 성능을 보여줬 습니다. Go는 13000 TPS 정도를 보였고 Tomcat+Spring 보다 2배 정도 성능이 좋았 습니다. netty의 성능이 상당히 좋다고 알려져 있어서 성능이 가장 중요한 API 서버를 개발한다면 netty를 사용하는게 좋을 수 있습니다. 처음에도 말씀드렸듯이 Go가 빠르긴 하지만 성능만 놓고 보면 아직 컴파일 러를 계속 개선중이라 다른 언어가 더 뛰어날 수 있습니다. Go의 장점은 문법이 자유롭고 짧은 코드로 많은 일을 할 수 있고 동시성처리 개발이 매우 간편하다는 점에 있습니다. 이런 점 때문에 크롤러나 배치성 작업을 위한 프로그램 개발에도 잘 Fit한다 고 볼 수 있습니다. Node.js는 멀티 프로세스나 멀티 쓰레드에 적합하지 않아서 Go나 Python으 로 그런 프로그램을 짜는게 좋을 수 있습니다. 34