-
Python으로 최종 사용자 제품을 만드는 것은 부족하지 않을까?카테고리 없음 2020. 11. 7. 10:01
"There is only one way to do it"은 당시 Perl을 경쟁 상대로 생각했기 때문이었을 것이다. Perl을 좋아하는 나로서도 사실 Perl의 syntax가 제멋대로인 것은 좀 보기 좋지 않다. 100이면 100, 각기 다른 스타일로 Perl 코딩을 하기 때문에 다른 사람 코드를 하나 보려면 처음 펼쳐보는 순간 눈 앞이 좀 막막하다, 이건 마치 다른 programming language로 적혀진 것 같다. 그 사람 코드에 익숙해지기 위해 일단 시간이 필요하다.
Object-Oriented Programming (OOP) 외에 Python이 그 절대적인 중요성을 깨우쳐 준 것이 하나 있다면, readability일 것이다. 어찌보면, 컴퓨터공학 관련된 사람이 아닌 이상 C 같이, "{", "}", "!", "*" 같은 특수 문자가 많이 들어가는 언어에 대해선 거부감이 있을 것은 이미 예상하였을 테지만 그들을 소홀히 대해 온 것은 잘못이었을 것이다. 따라서 비전공자라 하더라도 술술 적어내려갈 수 있는 Python이 대히트를 친 것은 정해진 사실이었을 것이다.
게다가, 또 하나, interpreter임에도 Just-In-Time (JIT) 컴파일 방식을(한줄 한줄 읽을 때마다 조금씩 컴파일을 함, 해당 줄을 다시 실행하는 경우 재컴파일 불필요) 택했기 때문에 실행 속도도 무지(?) 빠른 것도 장점. (하지만, Perl이나, 좀 태생이 다르긴 하지만 Lua에 비해 정말 그리 빠른 지는 음... news.ycombinator.com/item?id=8626131)
해서, Python으로 linux daemon을 개발한 적이 있다. 본래 C++로 만들려고 했으나 3개월 project였는데, 필요한 소스 코드가 있으면 찾아다 포팅하는 것이 시간이 오래 걸릴 것 같아서... Python으로 결정했다. 예를 들어, multi-thread로 동작할 때 그들 사이의 synchronization은 queue로 하게 되는데, C++은 github 같은 곳을 process-queue 관련 코드를 찾아보아야 하고, 또 붙인다고 바로 붙여지는 것이 아니다. 소스 코드의 전반적인 내용을 대충 이해해야 가능한데 이는 또 시간이 걸린다. 하지만, Python은 필요한 것이 대부분 라이브러리에 이미 있고 document를 읽어보고 그냥 불러쓰면 된다. "Battery included"의 소중함을 깨닫게 되는 순간이다.
그렇게 해서 완성한 daemon은 대체로 잘 동작했다. 염려했던 속도도 나쁘진 않았다. 문제는 exception handling이었는데, 어딘가에서 exception이 생기면 Python은 call stack을 뿌리고는 죽어버린다. 물론 exception이 발생하는 것은 어딘가 잘못이 있음을 뜻하기 때문에 이를 미리 수정하든지 아니면 exception handling을 해 주어야 하는 것은 당연하다. 하지만, 정작 문제는 Python은 exception을 너무 많이 발생시킨다는 것이다. Python은 exception handling 처리가 잘 최적화되어 있어 exception에 많이 의존적인 것은 알겠지만, exception을 굳이 쓰지 않아도 될 상황조차 exception을 내뱉는다.
예를 들어, list.remove(element)에서 element가 list에 존재하지 않는다면? 대부분의 language에선 이 경우는 아무 변경 없이 그냥 조용히 넘어갈 것이다. 하지만, Python은 exception을 뿌린다. if element in list:를 추가해 주어야 한다.
더 문제인 것은, 이런 exception이 document에서 해당 함수 본문을 직접 읽어야지 알 수가 있고, 따로 정리해서 보여주진 않는다. 결국... 내가 만든 코드에서 불러쓰는 라이브러리 함수를 찾고 발생할 수 있는 exception을 본문에서 일일이 찾아서 혹시 exception이 발생하지는 않을런지 몽땅 "코드 리뷰"를 했다 (다른 Python 개발자도 이렇게 하는지... 아니면 이를 자동으로 찾아주는 tool이 있는지도 모르겠다). 하지만, 지금도 혹시라도 내가 잡지 못한 곳에서 exception이 발생하고 있지는 않을지 불안하다.
참고로, Rust 같으면, exception handling이 따로 존재하지 않는다 (https://doc.rust-lang.org/book/ch09-00-error-handling.html). 가능한 에러는 함수의 결과값으로 항상 나온다. 즉, 에러가 가능한 함수는 항상 그 return type이 Result<T, E>이다, T는 정상적인 리턴값, E는 에러값. 물론 E를 무시해서 programming하면 runtime 시에 panic이 생기지만, return type이 그냥 T가 아닌 Result<T, E>이기 때문에 에러를 무시하기가 좀처럼 힘들다. 요지는, 함수의 type signature에서 에러가 발생하는 함수인지 아닌지 표가 난다는 것이다.
지금 다니고 있는 회사에서도 Python을 많이 쓰고 있는데, 그건 어디까지나 제품을 테스트하기 위한 코드로서 개발자들만 실행할 뿐이지 최종 사용자에게 배포되지는 않는다.