개발 꼰대

알고리즘 실력과 개발 능력의 관계

the_jjong 2022. 1. 16. 19:42

이건.. 고이고 고인 주제이고 마땅한 정답이 없어서 식상한 면이 있지만 명백한 오답을 가지고 있는 사람들에게 정의의 철퇴를 내리기 위해 키보드를 든다. (당연히 농담이다)

 

이 주제가 여전히 정답을 찾지 못하고 있는 가장 큰 이유는 "개발을 잘 하는 것"에 대한 정의가 너무나 모호하단 데에 있다. 프로그래밍을 잘 한다는 뜻인가? 아니면 기술적인 문제에 대한 해결 능력이 좋다는 뜻인가? 복잡하지만 정교한 알고리즘적 해결책을 만들어낼 수 있다는 뜻인가? 아니면 오히려 문제를 훨씬 간결하게 바꾸어 생각할 줄 아는 능력을 말하는 것일까? 그리고 이러한 능력들이 과연 하나의 (또는 여러 개의) 정량적인 지표로 표시될 수 있을까?

 

그럼에도 여전히 수많은 테크 기업들이 알고리즘 문제를 코딩 인터뷰로 사용하는 이유는 코딩 테스트가 그나마 정량적으로 표현 가능한 성적이고, 1일 미만의 짧은 시간에 평가할 수 있으며, 어느정도 "개발을 잘 하는 것"과 양의 상관관계를 가진다고 판단되기 때문일 것이다.

 

알고리즘(PS; Problem Solving)을 잘 한다는 것

나는 아주 자기중심적이게도 내 알고리즘 실력을 기준으로 알고리즘을 잘하고 못하고를 평가한다. 내 실력이 딱 중간이고, 나보다 잘 하는 사람은 알고리즘을 잘 하는 사람들, 나보다 못 하는 사람들은 다소 아쉬운 사람들이다. 내 실력으로 감히 평가할 수 없는 사람들이 천상계 사람들이라 할 수 있겠다.

 

나는 대학교 3학년 때 ACM-ICPC 대전 리저널에서 15위 정도의 성적을 거두었다. 문제 난이도를 쉬움, 중간, 어려움으로 나눈다면 중간 난이도까지는 거의 다 풀었고, 어려운 문제들은 손도 못 댔었다. 반대로 최상위권 팀들은 어려운 문제들도 대부분 풀어낸 팀들에 해당한다.

 

다른 것보다 PS에서 필요하거나, PS 경험으로부터 크게 향상되는 능력을 생각해보면 다음과 같은 것들이 있을 것 같다.

  • 패턴을 파악하는 능력. 적은 갯수의 (few shot) 예시, 또는 정해진 규칙으로부터 유도되는 결과를 상상하고 그 과정에서의 공통된 패턴을 파악해야 한다. 그러한 패턴은 4선 문제 (4개의 선으로 9개의 점을 연결하는 문제) 처럼 사고의 틀에서 벗어나야 보이는 경우가 많다.
  • 엄밀하고 합리적인 사고 방식. 어려운 문제들의 경우 직접 도움 정리 또는 불변식을 찾고 이를 증명하는 과정을 거쳐야 하는 경우가 많다. 이는 수학 문제를 풀 때의 느낌과도 비슷하다. 이거 맞을 거 같은데? 와 같은 어림짐작으로는 정답을 유도할 수 없는 경우가 대부분이다. 증명을 한다는 것은 동시에 내 가정에 대한 반례를 찾아야 하는데, 이 반례를 찾기 위한 프로그램을 직접 작성할 수도 있다.
  • 문제를 단계적으로 단순화시키는 능력. 부분문제로 분리하는 것이 (divide and conquer) 애초에 유명한 알고리즘 갈래이기도 하고, 잘 만들어진 문제는 겉으로는 어렵고 복잡하지만 전혀 다른 종류의 간단한 해결책이 있을 수 있다.
  • 프로그래밍적 상상력과 직관. 문제 풀이 중에 오답이 나온 경우, 해당 오답이 나오게 된 입력을 알려주지 않기 때문에 직접 여러가지 입력을 가정하면서 눈디버깅을 해야 한다. 즉 코드롤 보고 머릿속으로 프로그램을 돌리는 것은 아주 자연스러운 일이다.

이러한 능력들은 사실 어떠한 종류의 문제 해결에서든지 필요한 능력이다. 먼저 문제를 정의하기 위해서는 가능한 넓은 시야에서 요구사항들의 공통된 패턴을 파악해야 하고, 정의된 문제를 단계적으로 단순화시키고 부분문제로 분리하고, 그 과정에서 사용된 가정에 문제가 없는지 엄밀하게 검증해야 시스템의 구조적인 문제를 피할 수 있다. 이렇게 만들어진 시스템이 동작 과정에 문제가 생긴 경우, 어디서 문제가 생겼는지 파악하기 위해서는 주어진 단서로부터 오류가 생기는 과정을 유추해야 한다.

 

PS와 실제 개발에서 보이는 문제와의 차이점은 문제의 요구 조건과 이를 해결하기 위해 걸리는 시간의 스케일이 다르다는 점일 것이다. 하지만 큰 문제를 정의할 때도 narrow down을 해야 하고, 문제를 해결할 때에도 subproblem을 정의하는 식으로 점점 작은 단위의 문제들로 바꾸어 나가기 때문에 결국 작은 문제를 풀 때와 거치는 과정과 아주 유사할 뿐만 아니라 작은 단위의 문제를 아주 잘 해결할 수 있는 능력 역시 중요하다.

 

개발을 잘 한다는 것

내가 주니어 엔지니어일 때 가장 많이 고민했던 부분이 바로 어떻게 하면 더 좋은 개발자로 성장할 수 있을지, 시니어 엔지니어의 역할이 무엇이고 그걸 잘 수행하기 위해서는 무슨 능력치가 필요한지였다. 초기 커리어에 스타트업을 다닐 때는 이에 대해 마땅한 대답을 주시는 분이 없었지만, 대기업으로 이직하고 나니 글쎄 그냥 잘 정리된 문서가 있지 않은가.

 

우리 회사는 레벨 제도를 운영하고 있다. 무슨 말이냐면 대리, 과장 같은 직급이 레벨로 표현된다. 대졸 신입의 경우 L3, 박사 졸업생의 경우 L4로 시작하여 L5는 시니어 엔지니어, L6은 스태프 엔지니어, L7은 시니어 스태프 엔지니어, L8은 프린시펄 엔지니어와 같은 식이다. (L9 이상은 극천상계, 예를 들어 알파고를 만든 데이빗 실버 박사님 정도).

 

  • L3은 비록 신입 주니어지만, 이때부터 프로그래밍 능력은 이미 아주 높은 수준일 것을 요구한다. 여기서 말하는 프로그래밍 능력은 새로운 커다란 시스템을 설계하는 프로그래밍을 말하는 것은 아니고, 기존의 코드베이스에서 best practice를 따르는 수준 높은 코드를 작성하는 능력을 말한다. 수준 높은 코드란 만약에 같은 기능을 시니어 엔지니어가 만들려고 했을 때도 거의 동일한 코드를 작성했을 것이라는 뜻이다.
  • L4의 경우 업무의 자주성을 핵심적으로 생각한다. 문제의 크기는 작더라도 직접 문제를 파악하고 정의하고, 이에 대한 좋은 기술적인 해결책을 팀원들과 토론을 통해 정하고 만들어내는 것이 필요하다. 프로그래밍 능력은 이미 충분한 상황에서 기술적인 문제를 해결하는 방식에 대해 촛점이 맞추어진다.
  • L5부터는 시니어로서 리더십으로서의 역할이 중요해진다. 합리적인 문제 해결 능력은 이미 L4에서 탑재된 상태라고 간주한다. 팀 레벨에서의 기술적 의사 결정을 내리는 역할을 맡기에 문제 해결에 앞서서 최대한 많은 맥락을 고려해야 한다. 이러한 맥락은 가시적이지 않은 영역을 포함한다: 기술 부채를 줄이고, 시스템의 복잡도를 줄이고, 문제가 될 구조적 문제를 예방하고, 등등.
  • L6에서는 문제 해결에 앞서 어떤 문제가 중요한 문제고 풀어야 하는 문제인지 파악하고 결정해야 한다. 내가 종종 쓰는 말로 메타문제를 푸는 것이고, 팀 레벨에서의 전략과 우선순위를 세우는 역할이다. 점점 더 모호한 영역에 발을 들이게 되고, 정답이 없지만 최선의 선택을 내려야 한다.
  • L7 이상부터는 문제 해결과 의사 결정을 위한 맥락을 얼마나 더 깊이 이해하고 있는지를 중요하게 생각한다. 결국 더 좋은 답을 찾기 위해서는 더 많은 도메인 지식과 기술적 경험이 필요하기에 L7 이상은 다른 팀원들보다 이 점에 있어서 더 많은 경력을 가지고 있고, 자신만의 지혜를 바탕으로 기술적 토론에서 정답에 가까운 방향을 제시할 수 있어야 한다.

위에 나열된 각 레벨의 역할과 필요한 능력들은 개발 도메인에 무관하게 적용되고, 심지어 개발이 아닌 다른 직군에서도 약간의 변형을 거치면 그대로 사용할 수 있을 것이라고 생각한다. 특히 레벨이 높아질수록 개발을 잘 한다는 것은 그냥 일을 잘 하는 방법에 대한 것이고, 그 방법은 꼭 개발적인 방법일 필요도 없다.

 

다시 돌아와, 개발을 잘 하는 사람에 대해서 정의를 하려면, 소프트웨어 엔지니어의 종신 레벨(더이상 레벨업을 하지 않아도 되는 레벨)인 L5까지로만 제한하면 다음과 같이 정리할 수 있겠다.

  1. 프로그래밍을 잘 한다
    • 언어의 표현능력에 맞게 기술적인 모델링을 할 수 있고 잘 모듈화된 이해하기 쉬운 코드를 작성한다.
    • 오류를 사전에 예방할 수 있는 구조로 작성한다.
    • 그걸 아주 능숙하고 빠르게 해낸다.
  2. 문제 해결 능력이 뛰어나다
    • 문제를 잘 정의할 수 있다. 잘 정의된 문제일수록 이해하기 쉽고 해결책이 단순하고 관리하기가 용이하다. 잘 정의되지 않은 문제일수록 무엇이 문제인지 불명확하고, 해결책이 임시적이고, 오히려 더 큰 문제를 야기하는 경향이 있다.
    • 문제를 단계적으로 쪼개어 접근할 수 있다.
    • 내가 가진 도구만으로 문제 해결에 부족할 때, 새로운 도구를 만들어내거나 도입할 수 있다.
  3. 최대한 많은 맥락을 고려해서 기술적인 의사 결정을 한다. (즉 많은 맥락을 인지하고 있다)
    • 코드베이스 오너들이 가지는 시야에 대해서: 기술부채, 시스템의 전체적인 복잡도, 등
    • 심지어 비기술적인 영역을 포함해서: 인력과 스케줄, 비즈니스 목표, 상위 로드맵, 등

각 요소는 단계적이라, 프로그래밍을 능숙하게 함이 선행되어야 문제 해결에 대해 논할 수 있고, 합리적인 문제 해결 프로세스를 이해하는 사람에게 더 많은 맥락이 주어져야 더 올바른 의사 결정을 내릴 수 있다.

 

종합

PS 고수들과 개발을 잘 하는 사람들의 공통점은 많다.

  • 어려워 보이는 문제도 흥미를 가지고 접근한다. 풀지 못함을 두려워하지 않는다.
  • 어쨌든 문제를 풀어낸다!
  • 문제를 다른 관점에서 접근하는데 능숙하다. 복잡한 문제에 오히려 단순한 해결책이 없을지 찾아내려고 노력한다.
  • 아이디어를 구현하는 속도가 대단히 빠르다. 단순히 오랜 시간 프로그래밍을 하기도 했지만, 프로그래밍 대회의 경우 더 빠른 시간 안에 풀어야 더 높은 점수를 얻기 때문이다.

반대로 간단한 PS도 어려워하는 사람과 개발을 못하는 사람들의 공통점은 기본적으로 프로그래밍을 제대로 하지 못한다는 것인데, 이는 결국 개발을 잘 하는 사람의 첫 단계에 아직 도달하지 못한 것이다.

  • 주어진 문제는 이해했지만 이 다음에 어떻게 해야할지 손을 대지 못한다. 머릿속이 혼탁하고 정리가 되어 있지 않다. 다음 단계가 무엇인지 알지 못한다.
  • 간단한 아이디어를 구현하는데도 비효율적이고 가독성 낮은 코드를 작성한다. 추상화 과정에서 제대로 기능과 역할을 구분하지 못해 기능이 한데 섞인다.
  • 간단한 문제에도 내가 가지고 있는 도구들을 충분히 이해하지 못했기 때문에 훨씬 더 복잡한 방법으로 해결한다.
  • 비효율적인 습관, 또는 엄밀하지 못한 사고 과정을 통해 오류가 발생하기 쉬운 코드를 작성한다.

"쉬운 PS 문제 못푸는 사람 -> 개발 못하는 사람"은 맞는 것 같다, 그렇다면 "어려운 PS 문제를 쓱쓱 풀어내는 사람 -> 개발 잘하는 사람" 이라는 명제는 어떠한가?

 

당장 PS 잘하는 사람의 조건에서 언급되지 않은, 하지만 개발 잘하는 사람에게 필요한 추가적인 능력치가 있다. PS에서 쓰이던 영역 이상의 프로그래밍 언어 기능에 대한 이해와 활용, 스케일이 더 큰 문제에 대한 경험, 그리고 더 복잡하고 큰 코드베이스에서 시스템을 만들고 운영함에 있어서 영향을 끼치는 요소들에 대한 이해, 등. 그렇다면 PS로 갈고 닦인 문제 해결 능력이 과연 새로운 능력치를 획득하는데에도 도움이 될까? 이 부분이 결국 이 글의 핵심 질문이다.

 

나는 너무 낮지 않은 지적 수준을 가진 사람이 (i.e. 프로그래밍에 어느 정도 재능이 있는 사람이), 오랜 시간동안 (다른 시니어가 잘 정의한) (1) 좋은 문제를 경험하고, (2) 나와 다른 의견을 가진 사람들과 토론하고, (3) 종종 몰입하여 오랜 시간 문제에 대해 고민한다면 누구나 "개발 잘 하는 사람"이 될 수 있다고 생각한다. PS를 잘 하는 사람들은 대부분 똑똑하고 프로그래밍을 잘 하는 사람들이며, 챌린징한 문제에서 흥미와 동기부여를 얻는 사람들일 가능성이 높기 때문에 이들에게 좋은 (개발 잘하는) 동료들과 충분한 시간만 주어진다면 개발 잘 하는 사람들이 될 확률이 아주 높다고 생각한다. 결국 가장 큰 덕목은 어려운 문제를 흥미롭게 대할 수 있는 태도가 아닐까 싶다.

 

(이 외에도 커뮤니케이션과 인간관계와 같은 요소가 중요하게 작용하겠지만 해당 특질들은 글에서 언급한 다른 특질들과 독립적으로 다룰 수 있을 것 같아서 제외했다).

 

1줄 요약: PS 덕후들은 똑똑하고 어려운 문제 푸는걸 좋아하는 사람들인데, 개발을 당연히 잘하지 못할지언정 지금 부족한 경험이 있더라도 시간이 지나면 거의 반드시 가장 이상적인 경험을 쌓고 구루가 될거임.

 

FAQ

낯선 고급 알고리즘을 잘 알고 있다고 더 개발/코딩을 잘 하는 것은 아니다?

 

내가 어려운 영어 단어를 조금 더 안다고 영어를 잘 하는것은 아니지만 이것은 올바른 비교 관점이 아니다. 담화 속에서 수준 높은 단어를 자연스럽게 담아내는 사람은 TEPS 단어 영역에서 고득점을 받기 위해 한국어로 된 단어장을 외운 사람과 전혀 다른 종류의 사람이다. 고급 알고리즘까지 익숙하게 사용할 정도로 많은 알고리즘 문제를 풀어본 경험이 있는 사람들은 평균 하루에 수 시간씩 PS를 취미처럼 해왔던 사람들이고, 그렇지 않았던 사람들과 학부 졸업 수준에서만 비교해보자면 프로그래밍 및 문제 해결 경험이 적어도 수 배 차이가 나지 않을까. 심지어 대부분은 중고등학교때부터 정보올림피아드를 준비해서 프로그래밍을 해왔던 사람들이다.

 

알고리즘 문제를 아주 잘 푸는 사람이 시스템 디자인 문제를 잘 푸는 것은 아니다?

 

서로 영역이 다른건 맞는 것 같다. 그래도 연결고리가 없진 않아서 위에서 언급했던건 (1) 패턴 파악 능력 (2) 단계적 문제 접근 능력 (3) 어려운 문제에 대한 흥미

 

알고리즘 풀이를 하는 사람들의 코드는 빠르게 작성하기에 최적화되어 있어서 가독성이 낮다?

 

그 사람들이 코드를 예쁘게 짜는걸 못해서 안하는거 같은가?

 

그래서 개발 잘 하려면 PS 열심히 풀어야 되나?

 

어느 정도 난이도까지는 도움이 되겠지만, 어려운 PS 문제까지 완벽하게 풀려고 하는건 최적 테크트리가 아닐거다. 결국 우리가 원하는 개발 잘 하는 사람이 되려면 딱 그에 맞춰 필요한 점들을 상기해보자. 물론 알고리즘 관련 도메인 지식이 더 있어서 나쁠건 없음.

 

어차피 알고리즘 일할 때 안쓰지 않냐?

 

아주 고급 알고리즘을 쓸일은 없고, 만약에 그런 코드가 있더라도 그건 해당 분야 박사 학위 있으신 분이 짜면 되는데, 지금 우리가 확인하려던 명제는 "PS잘 -> 개발잘" 이냐는 거였지 "개발잘 -> PS잘" 을 보려던거 아님. 그리고 후자는 틀린 명제임을 거의 확신.

 

반대로 컴퓨터공학 대학교 지식 / 알고리즘 중급 정도의 지식이 아주 쓸모가 없냐? 문제 해결의 아이디어는 결국 내가 과거에 경험했던 형태들로부터 비롯되는데, 이때 깊이 이해하고 있는 경험해본 조각들이 서로 이어지는 영역이 결국 내가 보는 시야이고, 이미 잘 알려진 것들조차 잘 모르는 사람이 이 영역이 넓을 리가 없음. 결국 문제 해결 능력이 아주 기초적일거고, 기술적 의사 결정을 위한 맥락이 얕아서 테크 리드의 역할을 맡기 어려움.