현재 버전

초보 디자인패턴 리팩터링 성능 조언

패러다임, 디자인 패턴, 리팩터링의 중요성 그리고 성능에 대해


어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라, 못 보신 분들께 굳이 찾아볼 것을 권할 만큼 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 당연하게 들릴 수 있는 말이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 착실하게 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위해 넘어야 하는 장애물을 만나게 될 것입니다. 그리고 이러한 벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 객기에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때, 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때, 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입하며, 또 다른 개발자는 계약(contract) 기반 프로그래밍의 가능성을 고민합니다.

또한 누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때, 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민하는 것입니다.

이렇듯 그 단계에 도달하기 전에는 이해하거나 심지어 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들이 스스로 틀에 갇히는 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 이야기에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.


수정 이력

2019-03-18 12:56:53 에 아래 내용에서 변경 됨 #17

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라, 못 보신 분들께 굳이 찾아볼 것을 권할 만큼 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 당연하게 들릴 수 있는 말이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위해 넘어야 하는 장애물을 만나게 될 것입니다. 그리고 이러한 벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때, 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때, 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입하며, 또 다른 개발자는 계약(contract) 기반 프로그래밍의 가능성을 고민합니다.

또한 누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때, 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민하는 것입니다.

이렇듯 그 단계에 도달하기 전에는 이해하거나 심지어 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들이 스스로 틀에 갇히는 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 이야기에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:42:14 에 아래 내용에서 변경 됨 #16

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라, 못 보신 분들께 굳이 찾아볼 것을 권할 만큼 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 당연하게 들릴 수 있는 말이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위해 넘어야 하는 장애물을 만나게 될 것입니다. 그리고 이러한 벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때, 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때, 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입하며, 또 다른 개발자는 계약(contract) 기반 프로그래밍의 가능성을 고민합니다.

또한 누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때, 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민하는 것입니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들이 스스로 틀에 갇히는 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 이야기에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:28:22 에 아래 내용에서 변경 됨 #15

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라, 못 보신 분들께 굳이 찾아볼 것을 권할 만큼 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 당연하게 들릴 수 있는 말이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위해 넘어야 하는 장애물을 만나게 될 것입니다. 그리고 이러한 벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때, 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때, 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입하며, 또 다른 개발자는 계약(contract) 기반 프로그래밍의 가능성을 고민합니다.

또한 누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때, 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합는 것입니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들이 스스로 틀에 갇히는 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 이야기에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:27:55 에 아래 내용에서 변경 됨 #14

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라, 못 보신 분들께 굳이 찾아볼 것을 권할 만큼 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 당연하게 들릴 수 있는 말이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위해 넘어야 하는 장애물을 만나게 될 것입니다. 그리고 이러한 벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입하며, 또 다른 개발자는 계약(contract) 기반 프로그래밍의 가능성을 고민합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들이 스스로 틀에 갇히는 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 이야기에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:27:00 에 아래 내용에서 변경 됨 #13

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라, 못 보신 분들께 굳이 찾아볼 것을 권할 만큼 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 당연하게 들릴 수 있는 말이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위해 넘어야 하는 장애물을 만나게 될 것입니다. 그리고 이러한 벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들이 스스로 틀에 갇히는 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 이야기에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:20:02 에 아래 내용에서 변경 됨 #12

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라, 못 보신 분들께 굳이 찾아볼 것을 권할 만큼 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 당연하게 들릴 수 있는 말이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들이 스스로 틀에 갇히는 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 이야기에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:18:19 에 아래 내용에서 변경 됨 #11

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라, 못 보신 분들께 굳이 찾아볼 것을 권할 만큼 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들이 스스로 틀에 갇히는 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 이야기에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:10:29 에 아래 내용에서 변경 됨 #10

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들이 스스로 틀에 갇히는 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 이야기에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:05:32 에 아래 내용에서 변경 됨 #9

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야하는 것입니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들의 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 사고에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:03:46 에 아래 내용에서 변경 됨 #8

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥 윗쪽으로도 얼마나 높고 넓은 공간이 있는지 경험한 적이 없기에 부릴 수 있는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야할 수도 있습니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들의 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 사고에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:01:31 에 아래 내용에서 변경 됨 #7

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥에서 출발한 사람들이 얼마나 높이 올라가고 있는지 본적이 없기에 부리는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야할 수도 있습니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 것이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들의 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 사고에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 10:00:15 에 아래 내용에서 변경 됨 #6

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥에서 출발한 사람들이 얼마나 높이 올라가고 있는지 본적이 없기에 부리는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제공합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야할 수도 있습니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 태도이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들의 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 사고에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 09:56:15 에 아래 내용에서 변경 됨 #5

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 만들어 놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥에서 출발한 사람들이 얼마나 높이 올라가고 있는지 본적이 없기에 부리는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제시합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야할 수도 있습니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 태도이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들의 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 사고에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 09:54:23 에 아래 내용에서 변경 됨 #4

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있을 것 같습니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면, 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨나게 되는 것이 아닌가 싶습니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 상당한 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면 어떨까요? 바로 이런 분들에 대한 우려가 제가 이 주제에 대해 항상 강한 주장을 하게 되는 이유입니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 쳐놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥에서 출발한 사람들이 얼마나 높이 올라가고 있는지 본적이 없기에 부리는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제시합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야할 수도 있습니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 태도이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들의 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 사고에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 09:53:05 에 아래 내용에서 변경 됨 #3

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되며, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉, 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 주된 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있는 듯 합니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨난 것이 아닌가 짐작합니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면? 바로 이런 분들에 대한 걱정 때문에 제가 이 주제에 대해 항상 강한 주장을 하게 되는 듯 합니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 쳐놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥에서 출발한 사람들이 얼마나 높이 올라가고 있는지 본적이 없기에 부리는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제시합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야할 수도 있습니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 태도이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들의 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 사고에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 09:50:33 에 아래 내용에서 변경 됨 #2

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 이러한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고, 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되는데, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있는 듯 합니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨난 것이 아닌가 짐작합니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면? 바로 이런 분들에 대한 걱정 때문에 제가 이 주제에 대해 항상 강한 주장을 하게 되는 듯 합니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 쳐놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥에서 출발한 사람들이 얼마나 높이 올라가고 있는지 본적이 없기에 부리는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제시합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야할 수도 있습니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 태도이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들의 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 사고에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.

2019-03-17 09:48:36 에 아래 내용에서 변경 됨 #1

어제 게시판에서 몇몇 분들과 저수준 지식의 중요성 등에 대해 꽤 격앙된 토론을 했습니다. 주제 자체는 사실 하마님 말씀대로 이 곳에서도 주기적으로 등장하는 고수준 언어에 대한 이런 저런 오해에 관한 내용이라 별로 대단한 내용은 아니었다고 생각합니다.

하지만 토론을 통해 그런 오해가 얼마나 뿌리 깊게 남아있는지 재확인할 수 있었고, 토론 과정에서 VM 언어는 성능 때문에 사용하면 안된다던지 디자인 패턴, 리팩터링 등은 '가짜 지식'이라는 등 꽤나 극단적인 주장까지 오갔기 때문에 혹시 경험이 부족한 개발자 분들이 잘못된 생각을 하게 될 까 우려해서 별도의 글로 관련 내용을 정리해보려 합니다.

이 글은 해당 토론을 이어 가거나 토론에 참여했던 어느 분이든 직접적으로 '저격'할 의도로 쓴 글이 아님을 먼저 밝히고 싶습니다. 사실 위에서 언급한 주장은 과격하긴 해도 이 곳에서 처음 접한 내용은 아닙니다. 저수준에 대한 맹신과 고수준에 대한 멸시, 마이크로 최적화에 대한 집착, 패러다임이나 리팩터링 수준의 소프트웨어 공학에 대한 몰이해 등은 영어권 인터넷에도 이전에는 흔히 볼 수 있었고, 요즘엔 거의 사라졌지만 여전히 게임 개발자 커뮤니티 등에서는 심심찮게 등장하는 주제이기도 합니다.

그래서 이번 글은 특정 개인이 아닌 그런 주장들이 경험이 부족한 개발자들에게 잘못된 인식을 심어주는 것을 경계하기 위한 목적으로 이해해주시면 감사하겠습니다.

우선 성능에 대한 '미신'은 자바 언어 초창기에는 매우 흔하게 볼 수 있었고 지금도 많은 초보 개발자들이 빠지기 쉬운 함정이기도 합니다. 세부적인 내용은 달라도 큰 줄기는 대략 이렇습니다:

어떤 언어 A는 B 보다 50% 빠르다. 50%는 엄청난 차이이고, 성능이 곧 돈이나 사용자 경험에 직결되는 마당에 50%나 느린 언어를 쓰는 건 바보 짓이다.

이는 얼핏 보면 당연하게 들릴 수 있는 이야기이기 때문에 성능이나 최적화에 대한 경험이 부족한 개발자들이 쉽게 빠지는 함정이라고 생각합니다. 자바 언어 초창기에는 비슷한 논리로 많은 공격을 당했지만 자바는 한 때 미션 크리티컬한 비즈니스 시스템 시장을 장악할 수 있었고, 요즘에는 자바보다도 성능이 훨씬 느린 언어들이 그 시장에 진입하고 있습니다. 그렇다면 위의 주장이 어떻게 잘못된 것일까요?

이에 대한 답은 기술적인 설명 보다는 쉬운 비유를 드는 것이 더 효과적인 방법이라고 생각합니다. 여기서는 빠른 언어 A는 F1 레이싱카, 느린 언어 B는 평범한 SUV라고 하겠습니다. 그리고 편이를 위해 고속도로의 속도 제한 같은 건 없다고 가정하겠습니다.

F1 경주차와 SUV가 트랙에서 경주를 한다면 보나마다 경주차가 압승할 것입니다. 문제는 일반적인 시스템이라는 환경은 자동차 경주 트랙이 아니라 명절에 꽉막힌 고속도로와 비슷하다는 것입니다.

즉, F1 경주차가 레이싱 트랙에서 SUV보다 두 세 배 빨리 달릴 수 있을지는 몰라도, 명절에 서울에서 부산까지 SUV로 8시간이 걸리는 길을 경주차로 달리면 4시간에 주파할 수 없다는 것은 굳이 이유를 설명하지 않아도 이해하실 것으로 믿습니다.

서울에서 부산까지 걸리는 시간은 시스템에 비유하면 요청 이후 응답까지 걸리는 시간입니다. 그리고 그 시간이 8시간이 걸렸다면 대부분의 시간은 실제 도로를 달리는 게 아니라 정체가 풀리기를 기다리는데 소모하는데, 시스템에서 이는 보통 네트워크나 데이터베이스 등과 관련된 입출력 관련 오버헤드에 해당합니다.

이는 성능을 무엇보다 중요하게 여기는 게임에도 적용되는데, 이 경우는 주로 렌더링 파이프 라인이나 물리 계산 등이 '정체 구간'에 해당합니다.

즉 일반적인 시스템은 응답/반응 시간 대부분을 잡아먹는 '정체 구간'이 있기 때문에 언어의 단순 실행 성능(raw speed)은 소요 시간을 단축 시키는데 유의미한 차이를 보이지 않는 경우가 많다는 것이 바로 최근 20년 간 VM이나 가상화 기술이 대세로 떠오를 수 있었던 이유입니다.

또한 이를 통해 왜 서버 시스템의 사양을 무한정 높이는 것보다 클러스터 등을 활용해서 '스케일 아웃(scale out)'하는 것이 훨씬 효율적인 최적화의 방식인지 또한 부분적으로 이해할 수 있을 것입니다.

다시 말하면 명절에 도로가 꽉 막혀서 서울에서 부산까지 8시간이 걸린다면, 문제를 해결하기 위한 방법은 자동차의 속도를 높이는 것이 아니라 차선을 확장하는 것이라는 간단한 원리입니다.

정리하면, 비교적 느린 자바나 C#, 혹은 파이선 같은 언어들이 대세가 될 수 있었던 것은 실제 구동 환경에서 언어 자체의 실행 속도가 전체 성능에 미치는 영향이 미미한 경우가 많기 때문이고, 어차피 경주차가 제 속도를 못낸다면 비싼 유지비용이나 불편한 좌석 등을 감수하고 SUV 대신 운전할 이유가 없다는 것과 동일한 이유로 C와 같은 언어보다 여러 분야에서 각광 받게 된 것입니다.

반면 분야의 특성상 그런 큰 정체 구간이 존재하지 않는 경우도 있는데, 그런 이유로 아직 디바이스 드라이버 개발이나 임베디드 분야 등은 속도나 메모리 최적화를 위해 C를 주력으로 사용하는 것입니다.

성능에 대한 이야기는 이 정도로 마무리하고 이제는 저수준 지식에 대한 맹신, 혹은 고수준 지식에 대한 멸시에 대한 이야기를 해볼까 합니다.

성능에 대한 오해와 마찬가지로 이러한 편견 역시 꽤나 뿌리가 깊습니다. 그리고 성능에 대한 부분은 실제 시스템으로 증명이 되지만 고수준 언어를 잘 다루는 사람들이 어떤 식의 고민을 하고 개발을 하는지는 그보다는 쉽게 접하기 어렵기 때문에 이런 편견은 조금 더 타파하기 어려운 것 같습니다.

또한 저수준 지식은 대학에서 여전히 중요한 커리큘럼으로 가르치지만 상대적으로 고수준 분야의 기술 추세가 커리큘럼에 반영되는데는 상당한 시간이 필요합니다. 해외 대학들은 입문 언어를 C에서 자바로, 그리고 다시 파이선으로 바꾸는 추세이지만 아마도 국내 대학들은 아직 C나 C++을 고수하고 있는 듯 합니다.

여기에 당장 포인터나 하드웨어 특성 등을 고려하지 않아도 되는 고수준 언어의 낮은 진입장벽이 합쳐지면 대략 하드웨어나 운영체제와 같은 저수준 지식이 프로그래밍의 '근본' 지식이고 자바나 파이선 코딩 같은 '응용' 분야는 상대적으로 쉽기 때문에 시간만 있으면 언제나 배울 수 있다는 식의 편견, 혹은 묘한 자부심이 생겨난 것이 아닌가 짐작합니다.

대학의 소프트웨어 관련 학과의 목적은 자바나 파이선 전문가를 양성하는 것이 아니기 때문에, 당연히 컴퓨터의 구조와 같은 저수준 주제로 부터 객체지향이나 함수형 언어 같은 고수준 주제까지 소프트웨어에 관한 폭넓은 주제를 커리큘럼으로 다루고 있습니다.

그래서 만일 본인이 막 그런 대학에 입학한 전공자이거나, 이미 본인의 분야에서 충분히 전문성을 확보하고 다른 분야의 교양 지식을 쌓고 싶은 개발자라면 그런 포괄적 커리큘럼을 공부하는 것은 도움이 될 것입니다.

하지만 만일 본인이 학원으로 프로그래밍에 입문해서 무작정 자바 같은 언어로 실무에 투입되었는데 더 나은 개발자가 되는 방법을 모르겠다면? 바로 이런 분들에 대한 걱정 때문에 제가 이 주제에 대해 항상 강한 주장을 하게 되는 듯 합니다.

고등학교 때 배운 지식이 교양은 될지라도 직접적인 직업 활동에 도움을 주는 내용은 제한적입니다. 즉, 기초 수학이 필요한 분야에 취업을 한 사람이 고등학교 수준의 수학 과정을 복습하는 것은 의미가 있어도 그 사람이 세계사, 국어 등 모든 고교 교과 과정을 다시 공부하지 않으면 절대 해당 분야에서 성공할 수 없다는 식의 주장은 성립하지 않습니다.

소프트웨어 관련 학과의 전공 지식도 이와 비슷합니다. 즉, 실무에서 어떤 분야를 택하느냐에 따라 대학에서 배운 어떤 내용은 필수 지식이 되지만 다른 지식은 '알면 좋은' 수준의 교양 지식이 되는 것입니다. 그리고 만일 본인이 선택한 분야가 자바나 C#, 또는 프론트엔드 개발자라면 컴퓨터 구조나 운영체제 같은 분야는 반드시 알아야 하는 지식이 아니라 알면 좋은 교양 지식으로 분류할 수 있습니다.

물론 교양지식도 중요합니다. 세상에 알아서 나쁜 것은 없습니다. 단, 필수 지식을 충분히 갖추지 않고 교양 지식에 집착하는 것은 스스로 커리어를 망치는 지름길일 뿐입니다.

학원에서 속성으로 자바를 배우고 막 실무에서 프레임워크를 이용해서 페이지를 찍어내는 식의 업무를 하는 개발자라면 아마도 더 나은 개발자가 되기 위한 벽을 느끼게 될 것입니다. 그리고 그 '벽'은 현재 서있는 자리 아랫쪽, 그리고 윗쪽으로 모두 존재합니다.

아래에 있는 벽을 뚫고 내려간다면 C++로 구현된 JVM을 만나게 될 것이고, 더 파고 내려가면 운영체제에 다다를 것이며, 거기서 더 파고 내려가면 궁극적으로는 하드웨어에 도달하게 됩니다.

하지만 자바나 C# 개발자에게 있어 그 방향의 벽은 애초에 뚫지 말라고 쳐놓은 것입니다. 이는 막 학원을 수료한 자바 입문자에게나 20년 경력의 고수에게나 똑같이 적용되는 규칙입니다. 그런 벽이 존재하는 이유는 그 아랫 방향의 복잡성은 가상머신이 책임질테니 개발자는 남는 여력으로 보다 높이 올라가라는 의도입니다.

그리고 그런 벽의 존재에 대해 그 아래 있는 개발자들에 대해 열등감을 가질 이유도 전혀 없습니다. 그 중에는 바닥 아래로 못 내려오는 개발자들을 뭔가 근본이 부족한 것으로 멸시하고 자신들은 마음만 먹으면 그걸 뚫고 올라가 하늘 끝까지 갈 수 있다고 착각하는 부류가 있지만, 그건 그 바닥에서 출발한 사람들이 얼마나 높이 올라가고 있는지 본적이 없기에 부리는 만용에 불과합니다. 그런 사람들이 아래에서 올라올 동안 이미 그 보다 높은 바닥에서 시작한 놀고 있는 것이 아니라면 당연히 같은 노력을 들이면 더 높이 올라갈 수 있을 따름입니다.

애초에 자바나 닷넷 같은 기술을 만든 이유는 저수준 지식을 공부할 능력이 없는 개발자들을 차별하거나 배려하기 위함이 아니라, 기술이 발전해서 소프트웨어가 다루는 분야가 하드웨어로 부터 아득하게 먼곳까지 확장하게 되어 더 이상 같은 개발자가 바닥부터 그런 경계의 끝까지 모두 능숙하게 다루는 것이 사실상 불가능해졌기 때문입니다.

그래서, 스스로 자바나 C#와 같은 언어를 주력으로 삼은 개발자라면 자신이 서 있는 바닥 아래에 무엇이 있는지를 공부하는 것은 교양 수준의 지식이 됩니다. 그 바닥을 뚫고 내려가고 싶다면 공부를 더하는 것이 아니라 더 저수준을 다루는 분야로 전직을 하는 것이 올바른 접근입니다.

반면, 윗쪽에 존재하는 벽은 노력에 따라 얼마든지 뚫고 올라갈 수 있고 또 고수준 언어를 다루는 개발자라면 다음 단계로 가기 위해 당연히 그렇게 해야하는 것입니다.

구체적으로, 앞서 언급한 막 학원을 수료한 흔한 SI 환경의 자바 개발자라면 그러한 윗쪽 방향의 벽은 예컨대 스프링 프레임워크를 사용은 할 수 있지만 구조를 이해하거나 그런 프레임워크를 직접 설계할 수는 없다거나 하는 고민일 것입니다.

여기서 중요한 것은 그런 종류의 벽을 깨기 위한 최적의 도구는 바로 패러다임, 리팩터링, 디자인 패턴과 같은 지식이라는 점입니다. 알고리즘 같은 주제가 꽤 유용한 주제일 수는 있어도 이 단계에서 그런 벽에 막혀있는 개발자들이 지나치게 집착하는 것을 경계하는 글을 많이 썼던 것도 바로 그런 이유입니다.

알고리즘 역시 그런 개발자의 위치에선 바닥 아랫쪽에서 주요 무기로 사용할 수 있는 지식이고, 그 윗쪽으로 올라오면 유용성이 급격하게 떨어집니다. 여전히 실무에선 가끔씩 도움을 줄 수 있는 정도인지는 몰라도 적어도 그 윗쪽의 벽, 즉 자바나 C# 개발자가 다음 단계로 올라가기 위해 돌파해야하는 벽을 깨는데는 사실상 거의 도움을 주지 못한다고 보아야 합니다.

반면 앞서 언급한 바대로 패러다임, 리팩터링, 디자인 패턴 등과 같은 주제는 바로 윗단계의 벽을 부수기 위한 최적의 도구입니다. 예를들어 스프링 프레임워크의 구조를 이해하고 싶다면 필요한 것은 소스를 내려받아 한 줄 한 줄 분석하는 것이 아니라, API 수준에서 클래스의 계층구조나 메서드의 시그네쳐를 보고 동작이나 의도를 이해하는 것입니다.

그리고 패러다임이나 디자인 패턴은 그런 식의 보다 추상적인 이해를 가능하게 하는 '기본 문법'에 가깝습니다. 예컨대 스프링에서 'AbstractSingletonProxyFactoryBean'이란 클래스를 접한다면, 디자인 패턴을 이해하는 개발자라면 소스를 열어서 한 줄 한 줄 조건문과 메서드 호출을 따라가지 않아도 클래스 이름의 '싱글턴', '프록시', '팩토리' 같은 단어만 가지고도 대체적인 역할과 메서드 구성, 사용 방법 등을 짐작할 수 있습니다.

패러다임, 리팩터링, 디자인 패턴 등의 지식은 그런식으로 언어의 기본 문법을 가지도 더 복잡한 구조를 쌓아 올리는 원리를 제시하며, 그 과정에서 접할 수 있는 흔한 문제를 풀 수 있는 가이드라인을 제시합니다. 물론 그런 해법이 항상 정답은 아닐 수 있겠지만, 오랜 기간 여러 능력있는 개발자들이 쌓아올린 경험에서 비롯된 그런 가이드라인이, 그런 걸 깡그리 무시하고 언어 문법만 가지고 혼자서 도닦듯 수련해서 '발견'한 패턴보다 정답에 가까울 확률은 매우 높습니다.

모든 것을 떠나서, 그런 지식들은 고수준 개발자가 다음 단계로 나가기 위해 익혀야하는 지식을 만들고 전파하는 사람들이 공유하는 기본 문법입니다. 그래서 적어도 인터넷 기술 블로그에 언급된 '커플링'이 무엇이고 왜 나쁜지, 또는 API 문서에서 찾은 '팩토리'가 무슨 역할을 하는지 같은 상식은 최소한 이해할 수는 있어야 그 다음 단계로 발전할 수 있는 길이 열리는 것입니다.

그리고 개발 분야의 어려움 중 하나는 자신이 일정 수준에 도달하기 이전엔 그 다음 단계에 있는 문제나 고민들이 보이지 않는다는 점입니다. 예를들어 경력이 얼마 안되는 개발자들은 자바 같은 언어를 두고 '쉽다'라고 착각을 하는 경우가 많습니다. 또한 언어는 어차피 반복문, 조건문, 함수 호출 같은 문법만 알면 다 거기서 거리라는 식으로 피상적 이해를 하는 경우도 흔하게 볼 수 있습니다.

하지만 어느 수준의 개발자는 어차피 반복문이 문법만 다르지 뭐 별거 있냐는 식의 우물안 개구리 식의 사고의 틀에 갇혀 있을 때 누군가는 반복문의 명령형적 특성에 주목하고 이를 선언적 파이프라인으로 대체하는 고민을 하며, 그런 고민의 결과가 람다 같은 스펙으로 반영이 됩니다.

어떤 개발자는 입력값이야 에러 안나게 꼼꼼하게 널체크 해주면 끝이라고 생각할 때 누군가는 아예 '널'이라는 개념을 사용하지 않는 옵셔널(optional)이라는 새로운 방식을 고안하고 언어에 도입합니다.

누군가 리눅스 좀 만지고 서버 몇 번 구성해봤다고 서버 쪽은 알만큼 안다라고 자만할 때 누군가는 가상화를 도입하고 마이크로서비스와 서버리스 아키텍쳐를 고민합니다.

이렇듯 그 단계에 도달하기 전에 이해하거나 존재 자체도 인식하기 힘든 고민의 영역은 수도 없이 많습니다. 그렇기 때문에 그런 지식을 얻고 자신의 능력을 다음 단계로 끌어올리기 위해선 끊임없이 기술 동향을 파악하고, 관련된 토론이나 스펙을 찾아보며, 때에 따라선 타입이론 같은 이론적 기반을 공부해야할 수도 있습니다.

그런 노력을 다 무시하고 분야와 무관한 어떤 '근본 지식' 같은 게 있어서 그와 관련된 매우 좁은 분야의 공부에 매진하다 보면 나머지 분야는 도를 깨치듯 자연히 통달하게 된다는 태도는 개발자로서 꽤 편협하고 오만한 태도이며, 이제 막 입문해서 아직 방향을 잡지 못하는 개발자에게 권하기엔 매우 위험한 접근이기도 합니다.

더구나 그런 분야가 직접적인 도움이 되기 힘든 고수준 분야의 개발자라면, 무턱대고 그런 쪽으로 공부 방향을 잡아 정작 자신에게 훨씬 중요한 분야의 노력을 게을리할 경우 영원히 남이 만든 프레임워크의 영역을 벗어나지 못할 수도 있습니다.

일부 저수준 개발자들이 그런 편협한 생각으로 고수준을 멸시하고 그런 분야에선 어떤 고민을 어떤 깊이로 하는지 이해 못하는 거야 그 사람들의 문제이니 무시하면 그만이지만, 고수준 개발자가 그런 사고에 혹해서 방향을 잘못 잡으면 커리어를 망치게 됩니다. 그리고 이 것이 이 주제로 토론이 있을 때마다 제가 반복적으로 강한 주장을 제시하고 장문의 글을 쓰게 되는 이유입니다.

정리하면, 모든 공부는 다 유익한 것이지만, 어떤 공부를 먼저하고 얼마나 비중을 둘 것인지는 분야에 따라 다르다는 것이 핵심입니다. 특히 비전공자로 출발해서 회사 업무에 치이는 와중에 짬을 내서 공부를 하는 입장이라면 더욱 그 시간을 본인의 실력을 향상 시키는데 직접 연관이 있는 방향으로 집중하는 것이 중요할 것입니다.