블랙박스를 현명하게 다루는 법—언제는 그대로 쓰고 언제는 내부를 열어야 하는지—를 다루며, GPU와 네트워크 플로우 사례를 통해 기반 다지기와 지속 학습 전략을 제시합니다.
목차 열기
우리는 한 번 읽어서는 도저히 이해가 안 될 만큼 복잡한 정보가 어디에나 널린 시대를 살고 있습니다. 초급 수준을 넘어 편안한 영역 밖으로 배우려 시도해 본 경험이 있다면, 이런 상황을 분명 겪어봤을 겁니다:
“뭔가 이해가 잘 안 되긴 하는데, 일단 이 라이브러리/프레임워크/알고리즘/개념을 완전히 이해하지 않고도 쓸 수는 있겠지. 당분간은 블랙박스로 취급하면 되겠다.”
대부분의 경우 이런 접근은 매우 많은 것들에 대해 전혀 문제없습니다. 현대 기술에서는 정보의 토끼굴이 사실상 끝이 없고, 간단한 구글 검색을 하려고 트랜지스터의 동작 원리부터 배울 사람은 거의 없겠지요.
하지만 아주 가끔, 어떤 개념을 남에게 자신 있게 설명할 수 있을 정도로 깊이 이해하고 싶을 때가 있습니다. 제 경험상 이런 일은 다음과 같은 상황에서 벌어집니다:
갑자기, 어디서나 써먹던 다소 게으른 블랙박스 접근이 통하지 않습니다. 이 복잡한 개념의 내부 작동을 이해하고 싶고, 지금 당장 이해할 필요가 있지만, 어디서 시작해야 할지 모르겠습니다. 문서는 너무 상위 수준이거나 거의 존재하지 않고, 논문은 가능한 가장 난해한 방식으로 쓰여 있으며, 코드는 타입 힌트도 주석도 없는 절대적인 파이썬 난장판입니다. 어쩌겠습니까?
운이 좋아 위의 세 가지 자료 중 하나라도 실제로 도움이 된다면 다행이지만, 대부분은 꽤 막막할 겁니다. 이쯤에서 “난 이걸 이해하기엔 너무 멍청하구나” 하고 인정하고 다른 걸로 넘어가야 할까요?
안타깝게도, 지금은 실제로 곤란한 상황이 맞습니다. 하지만 미래에 같은 상황에 다시 빠지지 않도록 스스로를 도울 방법은 있습니다. 그렇다고 세그먼트 트리를 쓰려고 군론을 이해할 필요까지는 없으니까요.
절대 아닙니다. 바닥부터 모든 걸 이해해야 했다면 새로운 걸 배우는 데 영겁의 시간이 걸릴 겁니다. 학습의 일부 복잡한 부분을 블랙박스로 취급하는 건 자연스럽고, 어떤 부분은 블랙박스로 둬도 되는지, 어떤 부분은 깊이 이해해야 하는지를 구분할 수 있는 능력은 매우 중요합니다. 보통 어떤 개념을 더 깊이 배울수록 참고 자료를 더 많이 접하게 되므로, 두 가지를 더 쉽게 구분하게 됩니다. 예시를 몇 개 보죠(무슨 말인지 이미 알겠다면 건너뛰어도 됩니다).
많은 머신러닝 연구자와 실무자는 자기 분야의 여러 알고리즘과 기법에 정통하며, 이는 보통 GPU에서 실행되는 원시 명령으로 구현됩니다. 저는 진짜 100% 확신하는데, 당신도 삶에서 한 번쯤은 최신 연구에 관한 온갖 전문 용어를 늘어놓는, 머신러닝 하위 분야의 자칭 전문가를 만났을 겁니다. 그런데 막상 그 아이디어를 코드로 구현하는 모습을 보면, 정작 본인도 완전히 이해하지 못하는 경우가 많죠. 물론 진짜 자신이 무엇을 하는지 아는 전문가들도 많습니다. 다만 머신러닝의 hype 때문에, 충분히 이해하지 못한 채 파도를 타는 사람도 그 못지않게, 어쩌면 더 많습니다.
이런 ‘블랙박스’는 대개 알고리즘 뒤의 수학이나, 알고리즘이 실행되는 하드웨어—즉 GPU—에서 비롯됩니다. 수학 쪽은 중요하다고 자주 강조되므로, 많은 실무자들이 스스로 이해했는지 의식하는 편입니다. 반면 GPU 쪽은 자주 간과됩니다. 머신러닝에 대해 온갖 전문 용어를 말하는 사람들 중, 실제로 자신의 고수준 파이썬 코드를 GPU 하드웨어 아키텍처를 고려해 최대 효율로 도는 CUDA 커널로 번역할 수 있는 사람이 얼마나 될까요?
엔비디아가 GTC 2025에서 밝히길, 많지 않습니다(<10%). 실제로 FlashAttention 같은 여러 SOTA(최첨단) 커널 라이브러리를 살펴보면, 저자들이 백워드(역전파) 커널을 효율적으로 구현하는 게 엄청난 고통이라든가, 그런 수준의 성능 커널을 쓸 수 있는 사람이 전 세계에 100명도 안 된다 같은 코멘트를 남긴 걸 쉽게 찾을 수 있습니다. 물론 GPU의 동작 원리를 이해하는 것과 SOTA 성능 커널을 쓰는 것은 수준 차이가 큽니다. 하지만 요지는 이겁니다: 대부분은 하드웨어를 블랙박스로 취급하고, PyTorch나 TensorFlow 같은 고수준 라이브러리에 의존한다는 사실이죠.
어느 정도는 불가피합니다. 엔비디아의 하드웨어 아키텍처에는 완전히 이해하려면 공개되지 않은 독점 정보가 포함되어 있으니까요. 그래도 일반적으로 GPU가 어떻게 동작하는지 배울 수 있는 것들은 많고, 머신러닝의 엔지니어링 측면으로 더 들어가고 싶다면 매우 유용한 역량입니다.
제 생각에, 이는 오픈소스 연구 코드의 품질 저하로 이어지곤 합니다. 일부 연구자들이 엔지니어링을 깊이 고려하지 않은 고수준 파이썬 코드를 남발하는 데 익숙해져, 정상적인 엔지니어링 조직의 코드 리뷰를 통과하기 힘든 코드를 만들어내곤 하니까요. 뭐, 약간의 잡담이었고 요지는 분명합니다. 많은 사람이 굳이 이해하려 하지 않는 블랙박스의 대표적인 예라는 것 말이죠. 그리고 여기서는 GPU를 대부분 블랙박스로 둬도 크게 문제 없습니다. 파이토치가 그냥 너무 잘 해주거든요.
컴피티티브 프로그래밍에서 자주 요구되는 더 복잡한 개념 중 하나가 네트워크 플로우와 그 알고리즘 계열입니다. 흔히 쓰이는 알고리즘은 대략 다음과 같습니다(복잡도 순으로):
Ford–Fulkerson 방법
Edmonds–Karp 알고리즘
Dinic 알고리즘
Push–Relabel 알고리즘
네트워크 플로우 자체의 개념이 이 주제를 막 배우기 시작한 많은 프로그래머에겐 이미 낯설고, Edmonds–Karp까지의 기초를 이해하는 데도 어려움을 겪습니다. 다른 주제들에서 흔히 그렇듯, 알고리즘을 약간 변형해 고급 문제를 풀려면 내부 동작을 완전히 이해해야 한다고 기대하게 됩니다. 이는 Segment Tree Beats, 제곱근 분해, Sprague–Grundy 정리 등 많은 알고리즘에서도 자주 벌어지는 일입니다.
하지만 Dinic 알고리즘부터는 구현 복잡도와 수학적 복잡도가 함께 올라, 수학적 배경이 부족한 프로그래머는 해당 시간 복잡도가 왜 성립하는지 완전히 이해하기 어려워집니다. Dinic 알고리즘이 특정 그래프 변형에서 놀랄 만큼 빠르게 동작하는 경우가 여럿 있는데, 주어진 시간 제한 안에 풀려면 입력 그래프를 그런 특수 변형으로 ‘조정’해야 할 때가 있습니다. _왜 그렇게 빨라지는지 이해하는 것_은 기본 알고리즘을 이해하는 것보다 훨씬 어려울 때가 많아, 많은 프로그래머가 이 지점에서 완전한 이해를 포기하고 문제 풀이용 블랙박스로 취급하곤 합니다.
이 관행이 어느 정도 받아들여지는 이유는 두 가지입니다:
따라서 이 경우 Dinic 이상을 블랙박스로 두는 것은 흔히 받아들여지는 실천이며, 팀 대회에서 해당 알고리즘의 라이브러리 구현을 그대로 복붙해도 누구도 문제 삼지 않을 겁니다.
좋습니다. 이제 다음을 받아들였죠:
그렇다면, 실제로 블랙박스를 열어야 하는 상황에 대비하는 법은 무엇일까요? 평소에 적극적으로 열지 않을 거라면, 때가 왔을 때 어떻게 그런 복잡한 개념을 이해할 수 있도록 준비할 수 있을까요?
대부분의 경우 답은 아주 간단합니다: 기초를 다듬으세요.
제 경험을 포함해 솔직히 말하자면, 제로에서 탄탄한 기초를 ‘한 번에’ 구축하는 건 거의 불가능합니다. 어떤 과목이든 완전 초보에서 시작해 초심자에게 권하는 모든 기본 개념을 자신 있게 설명할 수 있게 되는 일은 거의 없어요. 이유는 간단합니다. 당신은 자신이 모르는 것을 모릅니다. 자신이 모르는 바를 자각하는 데는 처음 생각보다 훨씬 많은 경험/지식이 필요하고, 종종 과목을 더 파고들수록 기초 지식의 빈틈을 발견하게 됩니다. 아주 자연스러운 과정이며, 좌절할 필요가 없습니다.
그러나 바로 그렇기 때문에 ‘불필요한’ 블랙박스가 많이 생기기도 합니다. 가장 이상적인 시나리오에선 모든 기본 개념에 절대적인 전문가이고, 그래서 새로운 걸 배우면서도 모든 걸 즉석에서 이해할 수 있겠죠. 이 과정이 삐걱거릴 때, 이상적으로는 자신의 기초 역량에서 어떤 연결 고리가 빠졌는지 식별할 수 있어야 합니다.
수식이 왜 그렇게 성립하는지 완전히 이해하지 못할 수도 있습니다. 선형대수의 약점이 마침내 따라잡았음을 깨닫고, 기본을 다시 배울 때가 왔다는 걸 인지하게 될 겁니다.
어떤 코드가 실전에서 왜 그렇게 효율적인지 완전히 이해하지 못할 수도 있습니다. 예전에 더 단순한 자료구조의 고급 활용을 대충 훑고 넘어갔다는 걸 떠올리며, 먼저 그 단순한 자료구조들에 대한 이해를 다져야 한다고 느낄 겁니다.
이처럼 번쩍이는 자각이 없다면, 문제는 예상보다 더 심각할 수 있습니다. 즉, 해당 문제에 필요한 기초 지식이 너무 부족해 어디서 시작해야 할지도 모르는 상태일 수 있습니다! 흔한 원인은 필요한 직관이 당신이 한 번도 다뤄본 적 없는, 겉보기엔 무관한 분야에서 비롯되는 경우입니다.
당장 열고 싶지 않은 블랙박스를 만났다면, 언젠가 반드시 열어야 하는 가상의 상황을 위해 준비만은 해 두세요. 지금 완전히 이해하는 걸 막는 빠진 고리를 찾아내고, 기록해 두었다가 시간이 날 때 따라가 보세요. 수학적 직관 부족 때문에 블랙박스가 자꾸 생긴다면 수학을 배우러 가야 할 때일지 모릅니다. 대학에서 핵심 CS 과목을 하나 건너뛰었다면, 이제 스스로 배울 시간입니다. 타입 없는 파이썬만 쓰느라 읽기/유지보수가 끔찍한 코드를 써 왔다면, Rust나 C++ 같은 강한 타입 언어를 배울 때일지도 모릅니다.
다행히, 새로운 주제를 배우는 안내가 필요할 때 LLM은 그 어느 때보다 도움이 됩니다. 제 생각에 이런 AI 도구를 일을 ‘완전히 대신’하게 하기보다 보조 용도로 쓰는 건, 자신 없던 방향에서 역량을 키우는 데 매우 유익합니다. 약점을 식별하기 시작하면, 학습은 정말 끝이 없습니다. 모르는 게 드러날수록, 얼마나 역량이 부족한지 더 절감하게 됩니다. 솔직히 말해, 때로는 ‘임포스터 증후군’을 겪지 않는 사람들이야말로 자신의 약점을 외면하는 건 아닐까 싶을 정도예요.
기회는 과거보다 드물어 보이고 기준은 그 어느 때보다 높지만, 우리에게 주어진 정보의 양도 전례 없이 많습니다. 소프트웨어 엔지니어가 혼자서 이룰 수 있는 일의 폭은 LLM 이전 시대와 비교해도 믿기 어려울 만큼 커졌습니다. 저품질의 것들에 휘말리지 않는 법을 배우되, 그렇다고 활용 가능한 도구를 멀리하지는 마세요. 세상은 그 어느 때보다 빠르게 움직이고 있고, 따라잡는 유일한 방법은 계속 배우는 것입니다.