Go언어 에러처리

프로그래밍 2016. 11. 2. 10:11 Posted by UnHa Kim

Go언어를 쓰면서 느끼는 가장 큰 불편은 '에러처리'이다.

try-catch 같은 예외처리 기능이 없으며, if문에서 일일이 에러발생 여부를 확인해야 한다.


 

if 반환값, 에러 := F실행할_함수(인수1, 인수2....); 에러 != nil {

<여기에서 에러처리....>

}

 

이 방식은 다음과 같은 문제점이 있다.

 

1. 실행할_함수()는 if문에 파묻혀서 잘 보이지 않는다.

2. 소스코드가 지저분해진다. 소스코드 중간중간에 if문이 끼어들어서 흐름을 끊어놓는다.

 

그럼에도 불구하고 장점도 있다.

1. 예상하지 못한 동작이 발생하는 것이 예방된다. -> 오작동이 치명적인 경우에는 장점이 된다.

2. 각 에러마다 정확한 개별 대응이 쉽다.

 

이런 장점에도 불구하고, if문을 사용하는 에러처리 패턴이 불편하게 느껴지는 것은 사실이다.

defer, recover, panic을 조합하면 예외처리 기능처럼 에러를 뭉뚱그려서 한 번에 처리할 수 있다.

 

try-catch문은 예외가 발생하면 실행이 중단되고, catch문 내용이 실행된다.

go언어에서는 panic이 발생하면 실행이 중단되고, defer문으로 등록한 함수가 실행된다.

 

실행 흐름은 다음과 같으며 소스 코드에서 번호를 매겨놨다.

 

1. 에러발생

2. 패닉발생

3. defer

4. recover

5. 에러처리

 

func F함수_정의(인수A, 인수B,...) {

defer func() {    // 3함수 실행이 종료될 때 자동으로 실행된다. 패닉이 발생해도 실행된다.

if r := recover(); r != nil {    // 4recover함수를 실행해서 패닉된 경우 복구한다.

<여기에서 뭉뚱그려서 에러처리>

}

}()

 

.....

반환값, 에러 := F실행할_함수(인수1, 인수2....)

if 에러 != nil {    // 1 : 에러가 발생하면 패닉을 발생시켜서 함수의 실행을 종료하도록 한다.

panic(에러)    // 2  :  패닉이 발생하면 실행이 중단되고,  defer로 함수로 넘어간다.

}

....

}

 

언뜻 보면 소스코드가 오히려 더 복잡해진 느낌이다.

그러나, 다음 2개의 도우미 함수를 사용해서 반복되는 부분을 제거할 수 있게 된다.


 

func F에러2패닉(에러 error) {

if 에러 != nil {    // 에러가 발생하면 패닉 되도록 한다.

panic(에러)

}

}

 

func F패닉_처리(함수 func(r interface{}) {

if r := recover(); r != nil {    // 패닉이 발생한 경우 패닉을 복구하고 지정된 함수를 실행한다.

함수(r)

}

}

 

도우미 함수를 이용해서 소스코드를 간단하게 수정하면 다음처럼 된다.

func F함수_정의(인수A, 인수B,...) {

defer F패닉_처리(func(r interface{}) {

<여기에서 뭉뚱그려서 에러처리>

})

 

.....

반환값1, 에러 := F실행할_함수_1(인수1, 인수2....)

F에러2패닉(에러)    // 에러는 패닉으로 전환되고, defer에 등록된 패닉처리 함수가 실행됨.

 

// 여기까지만 보면 크게 좋은 점을 모르겠으나, 

// 처리해야 할 에러가 많아지면 이러한 방식의 장점이 드러난다.

....

반환값2, 에러 := F실행할_함수_2(인수1, 인수2....)

F에러2패닉(에러)

....

반환값3, 에러 := F실행할_함수_3(인수1, 인수2....)

F에러2패닉(에러)}

...

}

 

타 언어의 try-catch 방식의 예외처리문보아야 여전히 불편하긴 하지만,

if문을 사용해서 모든 에러를 일일이 확인하는 기존의 방식보다는 훨씬 간편하다.

 

GHTS에서는 lib.S예외처리{}.S실행() 으로 되어 있으며

S예외처리의 필드값을 변경하여 실행 옵션을 선택할 수 있다.

 

'프로그래밍' 카테고리의 다른 글

한국투자증권 REST API  (0) 2022.07.07
WebAssembly는 이식성 높은 바이트코드였구나.  (0) 2022.06.11
Go언어 안정성 문제.  (0) 2022.05.27
Go언어 소개  (0) 2014.11.04

댓글을 달아 주세요

ZeroMQ를 이용한 분산형 구조.

GHTS 2015. 3. 10. 16:01 Posted by UnHa Kim

Go언어의 고루틴(goroutine), 채널(channel)은 처음에는 거의 완벽한 동시처리 모델로 보인다.

그러나, Go언어는 같은 메모리 공간에서 여러 고루틴(goroutine)들을 실행하며, 고루틴끼리 서로 완벽하게 메모리를 분리하지는 못한다.

그리고, 채널은 락(Lock)보다 성능이 낮다보니, 프로그래머들은 채널 대신 락을 통해서 메모리를 공유하고자 하는 유혹을 계속 받게 되며, 그로 인해서, 고루틴 간의 채널을 통한 메시지 전송으로 안전한 동시처리를 하겠다는 아이디어는 너무나도 손쉽게 기존의 멀티스레딩 방식의 락을 통해 공유되는 메모리 모델로 되돌아간다.

 

멀티스레드 방식의 공유 메모리 모델이 뭐가 문제인지는 마이크로소프트가 발행한 "멀티스레딩 코드에서 발생하기 쉬운 11가지 문제점" (https://msdn.microsoft.com/en-us/magazine/cc817398.aspx)을 참조하시라.

(여러분이 사용하는 윈도우와 오피스를 개발한 그 마이크로소프트가 맞다.)

MS윈도우에서 실행되는 각종 서버 프로그램을 제작하면서, 성능을 높이기 위해서 멀티스레딩 방식의 공유 메모리 모델을 기반을 채택했던 마이크로소프트 사는 그 과정에서 메모리 공유로 인해서 생기는 문제를 혹독하게 겪고 나서, 위의 글을 배포하기에 이른다.

즉, MS조차도 문제가 많다고 인정한 것이 멀티스레딩의 메모리 공유 모델이다.

 

메모리가 완벽하게 분리되는 모델이 이렇게도 구현하기 힘든 것일까?

아니다. 운영체제 프로세스는 아주 오래 전부터 서로 독립된 메모리 공간을 가진 채 동시에 실행되었다.

(MS-DOS에는 프로세스 간 메모리 분리 기능이 없었고, 도스(DOS)에 기반을 둔 윈도우 3.1, 윈도우 95, 윈도우 98 시절에는 메모리 겹쳐쓰기 문제로 블루스크린과 함께 컴퓨터가 자주 다운되었다.

윈도우NT, 윈도우 2000 내지 윈도우XP부터 프로세스끼리 메모리가 완벽하게 분리되었으며, 이후로는 MS윈도우의 안정성이 눈에 띄게 좋아졌다.)

이렇게 좋은 독립 메모리 프로세스 모델을 놔 두고, 왜 그 골치아픈 메모리 공유형 멀티스레딩 모델이 대세가 된 것인지는 잘 모르겠다. (아마도 성능 차이 때문인듯..)

 

어쨋건 간에 여러 운영체제 프로세스가 서로 분리된 메모리 공간에서 서로 메시지를 주고 받으면서 함께 작동하는 모델... 태고적부터 존재했던 멀티프로세스 모델... 이것이 바로 진정한 액터모델이자 CSP모델이다.

이러한 형태 하에서, 모든 실행단위는 메모리가 완벽하게 분리되며, 당연하게도 멀티스레딩의 메모리 공유로 인한 각종 문제가 원천적으로 봉쇄되며, 예전처럼 순차적 실행을 가정하여  프로그램을 짜더라도, 각 실행단위들은 독립적으로 동시에 실행되면서 멀티코어 CPU의 능력을 모두 활용할 수 있다.

분리된 메모리 공간의 안정성, 손쉬운 순차적 프로그래밍, 멀티코어 CPU의 모든 능력 활용등을 모두 실현할 수 있는 데, 왜 다들 멀티 프로세스 모델을 태고적 구식모델이라고 생각하면서 꺼리는 것일까?

(요즘은 컴퓨터가 너무 좋아져서 멀티 프로세스 모델로 인한 성능 저하가 아직도 문제가 되는 지 의문이다.)

 

어쨋든 간에 프로세스 간에는 메모리가 너무 완벽하게 분리되어 있다보니, 서로 메시지를 주고 받을려면 IPC(Inter-Process Communication : 프로세스 간 통신)이라는 것을 사용해야 한다고 한다.

그런데, 유닉스와 윈도우 간에도 IPC 규격이 다르고, 호환이 안 된다.

요즘은 모든 컴퓨터끼리 인터넷의 TCP/IP으로 정보를 주고받는 게 너무 당연시 하는 세상이다 보니, IPC도 인터넷의  TCP/IP 소켓 방식으로 하는 게 자연스러워 보여졌고, 이것을 중앙집중형 서버에서 맡아서 해 주는 게 메시지큐(현재 국제표준은 AMQP 1.0)이며, 이후  정보교환 기능을 개별 실행단위에게까지 분산(하였지만 간편하게)한 것이 오늘의 주제인 ZeroMQ이다.

그로 인해서, 이제는 동시처리 프로그램을 짜기 손쉬워졌을 뿐만 아니라, 일부 실행단위를 아예 다른 컴퓨터로 이동시켜서 분산형 시스템을 만들기도 쉽게 되었다.

어쩌면 한참 입에 오르내리다가 조용히 사라진 SOAP(서비스 기반 아키텍쳐)의 진정한 후계자는 REST(단순 웹기반 서비스)가 아니라 ZeroMQ일지도 모른다.

어쨋든, 요즘 ZeroMQ가 각광을 받는 게 다들 멀티스레드의 메모리 공유모델에 넌덜머리가 나서이지 않을까 싶다.

 

ZeroMQ(혹은 그 후속작인 nanomsg)을 사용하면 동시처리 프로그램을 안전하고 간편하게 짤 수 있는 것 이외에도 생각하지 못한 장점들이 있다.

 

1. 필요에 따라서 다른 프로그래밍 언어들을 섞어서 쓸 수 있다.

- 주식 API의 예제코드를 그대로 복사해서 붙여넣기 한 후, 획득한 정보를 ZeroMQ를 통해서 다른 모듈에 전송해 주면 된다.

- 매매전략의 통계처리는 간편한 R이나 Python으로 하거나, 그 외 시스템 트레이딩 서적에 나온 예제 코드에 ZeroMQ 메시지 전송기능을 추가해서 그대로 시뮬레이션에 돌려볼 수 있게 된다.

- 처음에는 친숙히고 편한 프로그래밍 언어로 개발하고, 나중에 성능이 문제시 되는 부분만 분리하여 좀 더 어렵지만, 성능이 뛰어난 프로그래밍 언어로 다시 개발해서 교체해 나가면, 장기간 안정적인 발전이 가능해진다.

 

2. 서로 다른 저작권 형태의 소스코드를 섞어서 쓸 수 있다.

- 모두가 공유하는 소스코드는 GPL로 의무적으로 공개하도록 하여서, 소스코드를 공개하는 사람만 착취당하는 '죄수의 딜레마'와 같은 모순을 제거하여서 모두가 협력하여 개선할 수 있도록 유도한다.

- 각자 자기만의 매매전략은 타인에게 공개되는 순간 수익성이 사라지는 특징이 있다.

  (모든 전략은 수용한계(capacity)가 있으며 이를 넘어서면 수익성이 사라진다.)

이렇게 공개되면 안 되는 부분은 상업용 라이센스 모델을 채택하여야 하며, 이러한 비공개 상업용 소스코드와 GPL에 따른 의무적으로 공개해야 하는 소스코드 간에 서로 메시지를 주고 받으면서 하나의 시스템으로 구성하여 배포하되, 각 소스코드는 서로 다른 라이센스를 채택하는 것이 가능해진다.

 

 

즉, 프로그래밍 언어의 제한, 소스코드 라이센트의 제한에서 자유로워진다.

그간 Go언어로 개발해 오다가 독립된 메모리를 보장하는 방법을 고민하다가, 메모리에 안전한 Rust 언어도 만지작 거려보다가 결국은 ZeroMQ에서 답을 찾았다.

이제 슬슬 개발을 다시 시작해야 할 듯 하다.        

'GHTS' 카테고리의 다른 글

소스코드 패키지 통합  (0) 2019.07.27
Xing API 현물 주식 주문 TR 테스트 완료.  (0) 2018.06.25
Xing API에 숨겨진 지뢰 3  (0) 2018.06.07
Xing API에 숨겨진 지뢰 2  (0) 2018.06.07
Xing API에 숨겨진 지뢰 1  (0) 2018.06.07

댓글을 달아 주세요

Go언어 소개

프로그래밍 2014. 11. 4. 20:55 Posted by UnHa Kim

Go언어는 효율성을 추구하는 실용적인 프로그래밍 언어이다.

 

1. 개발 효율성 (혹은 생산성)

2. 실행 효율성 (멀티코어 CPU 활용)

 

이렇게 2가지의 효율성을 추구하는 대신 기능성은 포기했다.

 

go언어 창시자는 C++언어가 너무 복잡해서 go언어를 만들었기에

미니멀리즘의 극한을 추구하는 것으로 유명하다.

(어쩔 때는 그게 좀 과한 듯 하다.

 키워드 숫자를 줄이려고 interface가 가변형 자료형을 겸하는 것은 혼동을 유발하고,

 에러 처리를 if문으로 처리하니 상당히 불편하다.) 

 

개발자 생산성 향상을 위해서는 다음 특징을 가진다.

1. 빠른 컴파일.

2. 단순하지만 필수기능은 다 갖춘 문법.

3. 자동 메모리 관리

4. 유니코드 지원

5. 풍부한 표준 라이브러리 및 수많은 제3자 공개 라이브러리.

6 테스트, 테스트 커버리지, 동시 사용 데이터 충돌(레이스) 감지등 풍부 개발 도구 기본 내장

 

마지막으로...

7. 소스코드 자동 정리 (써보면 입이 떡 벌어진다. 소스 코드 들여쓰기로 분쟁이 생길 소지를 원천 봉쇄.)

 

 

하드웨어의 효율적 사용을 위해서 다음 특징을 가진다.

1. AOT 컴파일. (C, C++의 컴파일 개념. Java나 C#이 사용하는 JIT컴파일의 반대 개념.)

2. C보다 2배 정도 느리고, Java와는 비슷한 상당히 우수한 단일 스레드 실행 속도.

3. 멀티코어 CPU를 안전하고 효율적으로 사용하기 위한 독특한 기능 (goroutine, channel)

 

Go언어에서는 포기한 기능들은 다음과 같다.

1. 상속은 지원하지 않음. 대신 mixin을 지원.

2. 제너릭은 지원하지 않음. 대신, interface{}가 가변형 변수 역할을 함.

(제너릭 기능은 Go언어 창시자들이 미니멀리즘에 기반하여 의도적으로 배제한 것이 아니라,

 Go언어 창시자들이 기술적인 문제를 해결하지 못해서 아직 추가되지 못한 경우임.)

3. 에러 처리 구문이 취약함. 대신, defer문, recover()함수를 사용해서 panic상황에 대처 .

 

이러한 장단점이 혼합된 Go언어를 실제로 사용해 본 느낌과,

1. 기능성이 약간 부족하지만 오히려 간결해서 좋은 면도 있다.

   기능이 풍부하지 않지만 꼭 있어야 하는 기능들은 거의 다 있으며,

   반대로, 쓸데없는 기능이 없기 때문에 남이 개발한 소스 코드를 읽을 때도

   처음보는 기능을 접하는 경우는 거의 없다.

   그래서, 불러다 쓰는 제 3자 라이브러리가 의심스러우면 

    (저작권 문제가 없는 한도 내에서) 거리낌없이 소스 코드를 들여다보고 만지작 거리게 된다.

2. 개발 생산성이 확연하게 높아진다.

3. 실행속도 문제로 걱정할 일이 없다.

 

트위터가 서비스 초기에 루비(Ruby)언어를 사용했으나, 성능 상의 문제로 인해서 서비스 불안정의 어려움을 겪었고, 결국은 상당 부분을 Java와 Scala언어로 다시 개발해야 했다.

드롭박스도 서비스 초기에 파이썬(Python)언어를 사용했으나, 성능 상의 문제로 인해서 백엔드 상당 부분을 Go언어로 대체하였다.

 

트위터와 드롭박스의 공통점은 높은 개발 생산성을 얻기 위해서

루비(Ruby), 파이썬(Python)등 동적언어를 사용하였으나,

성능상의 문제로 인해서 동일한 기능을 실행 성능이 높은 정적 언어로 다시 작성했다는 것이다.

 

Go언어는 개발 생산성도 높고, 실행 성능도 상당히 높은 편이므로,

이러한 고민을 동시에 해결할 수 있으며,

무한 경쟁 시대에 비용 효율성 면에서 강점을 가진다.

(거대 S/W 기업이 된 구글이 자체적인 비용 절감 차원에서 만든 것 같다.)

 

그 대신에 포기해야 하는 것이 기능성인데, 

막상 써보면 기능이 심하게 부족하다고 느껴지지는 않는다.

 

물론, C++, Java, C#등을 사용하던 사람은

1. 변수 선언 순서가 반대임.

2. 에러 처리 구문이 없고 if문으로 처리함.

3. 제너릭이 없음

등의 장애물에 좌절하곤 하지만 

실제로 사용해보면 의외로 그럭저럭 쓸만 하다.

(Java도 1.4까지 Generic이 없었지만 다들 잘 썼다.)

 

Go언어를 가장 잘 설명하는 문구는  '모든 면에서 80%'이다.

파이썬(혹은 루비) 개발 효율성의 80%,

C++ 실행 효율성의 80% (사실은 50%이지만, 동적 언어보다 수백배 빠르므로 체감상 80%)

 

모든 면에서 약간씩 부족하고 아쉽지만,

비용절감의 면에서는 타의 추종을 불허하고,

여러모로 공동작업이 편하다.

'프로그래밍' 카테고리의 다른 글

한국투자증권 REST API  (0) 2022.07.07
WebAssembly는 이식성 높은 바이트코드였구나.  (0) 2022.06.11
Go언어 안정성 문제.  (0) 2022.05.27
Go언어 에러처리  (0) 2016.11.02
TAG go언어

댓글을 달아 주세요