작은 의존성 지도: 스파게티 코드에서 명확한 계획으로
가벼운 의존성 스케치를 통해, 복잡하고 혼란스러운 코드베이스를 거대한 도구나 모든 파일을 읽지 않고도 이해 가능하고 탐색하기 쉬운 시스템으로 바꾸는 방법.
소개
당신이 작성하지 않은 코드베이스를 연다.
수십, 많게는 수백 개의 파일이 있다. 함수는 다른 함수를 호출하고, 모듈은 다른 모듈을 import 하고, 서비스는 서로 통신한다. 당신이 원하는 건 그냥 버그 하나 고치거나 기능 하나 추가하는 것뿐인데, 코드는 온통 뒤엉킨 스파게티처럼 느껴진다 — 어디가 어디인지 전혀 감이 안 온다.
스크롤을 내리고, 검색하고, 스택 트레이스를 따라가고, 정의로 점프해 본다. 한 시간쯤 지나면 코드 자체는 많이 읽었는데, 그래도 이게 어떻게 하나의 시스템으로 맞물려 돌아가는지는 잘 모르겠다.
이럴 때 필요한 것이 바로 작은 의존성 지도(tiny dependency map) 다.
모든 걸 머릿속에만 담아 두려고 애쓰는 대신, 구조를 바깥으로 꺼낸다. 각 조각이 서로 어떻게 의존하는지 눈에 보이게 스케치하는 것이다. 예쁘게 그릴 필요도, 완벽할 필요도, 거창한 도구를 쓸 필요도 없다. 오히려 가벼울수록 좋다.
작은 의존성 지도는 본질적으로 고전적인 펜과 종이 스케치의 현대판이다. 단, 오늘날처럼 크고 복잡한 코드베이스에 맞게 조정된 버전이다.
왜 의존성 지도가 무작정 스크롤보다 나은가
대부분의 코드베이스는 한 줄 한 줄 모두 이해하기에는 너무 크다. 실제로 처음에 필요한 건 다음과 같다.
- 주요 구성 요소가 무엇인지 (서비스, 모듈, 패키지, 기능 등)
- 서로 어떻게 통신하는지 (함수 호출, import, 이벤트, 큐, API 등)
- 내가 관심 있는 부분이 이 그림의 어디에 있는지
의존성 지도는 모든 파일을 읽지 않고도 이런 조감도(bird’s-eye view) 를 제공한다.
의존성을 시각화하면, 이런 상태에서…
“결제 플로우에 버그가 있다는 건 알겠는데, 관련 로직이 다 어디 흩어져 있는지 전혀 모르겠다.”
…이렇게 바뀐다:
“요청 흐름은
Gateway → PaymentService → BillingAdapter → ProviderAPI로 간다. 버그는 아마PaymentService와BillingAdapter사이 어딘가에 있을 가능성이 크다.”
각 부분이 어떻게 연결되는지 한눈에 보이면, 읽어야 할 올바른 코드를 찾는 일이 훨씬 쉬워진다. 더 이상 방황하는 게 아니라, 길을 찾아가는 상태가 된다.
왜 여전히 펜과 종이가 통하는가
도구 얘기를 하기 전에, 이런 질문을 해볼 만하다. 왜 많은 시니어 엔지니어들은 본능적으로 화이트보드나 노트를 먼저 찾을까?
마찰이 적은(low-friction) 도구는 다음과 같기 때문이다.
- 설정, 스타일, 메뉴 같은 것들로 주의를 분산시키지 않고
- 지저분하게 생각할 수 있게 해 준다 — 지우고, 다시 그리고, 박스를 옮기고, 화살표를 추가할 수 있다
- 생각 속도를 따라갈 만큼 빠르다
지저분한 코드베이스를 이해하려 할 때는, 사용하는 도구가 주는 인지적 비용이 매우 중요하다. 다이어그램 툴이나 IDE 플러그인이 도움 주는 것보다 당신을 더 느리게 만든다면, 결국 그 도구는 안 쓰게 될 것이다.
이게 바로 작은 의존성 지도의 핵심 아이디어다.
코드 조각들이 서로 어떻게 의존하는지 나타내는, 작고 빠르며 마찰이 적은 표현 방식.
예쁠 필요 없다. 대신 다음만 만족하면 된다.
- 빠르게 만들 수 있고
- 쉽게 고칠 수 있으며
- 지금 내가 답을 구하고 싶은 질문에만 집중할 것
작은 의존성 지도: 현대 코드베이스를 위한 현대식 스케치
작은 의존성 지도는 노트에 그리는 스케치의 디지털 버전이다. 거창한 아키텍처 다이어그램이 아니다. UML도 아니다. 누군가를 감탄시키기 위한 것도 아니다.
다음과 같은 질문에 답해 주는, 작고 살아 있는 스케치다.
- “이 서비스는 어떤 모듈들을 호출하나?”
- “이 함수의 업스트림/다운스트림 의존성은 뭐지?”
- “이 클래스를 수정하면, 누구를 깨뜨릴 수 있지?”
예를 들어 이렇게 할 수 있다.
- 박스 5–10개 정도를 그린다 (모듈, 서비스, 패키지 등)
- 호출, import, 이벤트 흐름에 화살표를 그린다
- 몇몇 간선에 메모를 단다: “heavy query”, “자주 에러 발생”, “TODO: 리팩터링 후보”
이 작은 지도는 다음과 같은 상황에서 당신이 사용하는 작업용 모델이다.
- 까다로운 버그를 디버깅할 때
- 레거시 코드를 풀어헤칠 때
- 여러 컴포넌트를 걸친 기능을 설계할 때
- 리팩터링 계획을 세울 때
일부러 작게 유지한다. 이 지도의 가치는 완전함이 아니라 명료함에 있기 때문이다.
작은 의존성 지도가 스파게티 코드를 다루는 법
스파게티 코드는 단지 보기 안 좋은 스타일 문제가 아니다. 그건 숨겨진 구조의 증상이다 — 관계는 존재하지만, 암묵적이고 흩어져 있어서 보이지 않을 뿐이다.
의존성 지도를 그리면, 다음과 같은 일이 일어난다.
-
구조를 명시적으로 만든다
import, 함수 호출, 이벤트 구독처럼 암묵적인 관계들을 눈에 보이는 화살표로 바꾼다. -
우발적인 복잡성을 드러낸다
- 왜 이 서비스는 관련 없는 세 개의 모듈에 다 의존하고 있지?
- 이 “helper”는 왜 DB, 캐시, HTTP까지 다 알고 있지? 이런 악취(code smell)들이 그림으로 보면 확 눈에 들어온다.
-
리팩터링 기회를 찾는다
모듈을 분리하거나, 책임을 나누거나, 인터페이스를 도입할 수 있는 경계(seam)가 보인다. -
인지 부하를 줄인다
전체 시스템을 작업 메모리에 다 올려 두는 대신, 종이 위로 내려놓는다. 그러면 뇌는 변경과 영향 범위를 생각하는 데 에너지를 쓸 수 있다.
즉, 지도를 그린다고 코드가 자동으로 깨끗해지는 건 아니지만, 혼돈을 명확한 계획으로 바꿔 준다. 이제는 무엇을, 어떤 순서로 바꿔야 할지 말할 수 있게 된다.
대충 그린 스케치에서 도구 기반 명료함으로
손으로 그린 스케치와 툴 가운데 하나만 꼭 골라야 하는 건 아니다. 실제로 좋은 워크플로우는 보통 이렇게 흘러간다.
-
처음엔 거칠고 제약 없이 시작한다
- 공책, 화이트보드, 간단한 마크다운 파일을 꺼낸다.
- 코드 읽으면서 발견되는 대로 박스와 화살표를 적어 나간다.
- 정확함을 목표로 하지 말고, 쓸 만한 정신 모델을 만드는 데 집중한다.
-
작은 의존성 지도로 다듬는다
- 범위를 정한다: “결제 플로우만”, “리포팅 파이프라인만”.
- 동료에게 보여줄 수 있을 정도로만 대충 정리한다.
- 그 지도를 바탕으로 다음에 어디 코드를 읽을지 결정한다.
-
필요한 부분에 정교한 도구를 얹는다
여기서 CodeViz 같은 도구가 힘을 발휘한다.- 자동으로 import 그래프, call graph, 모듈 간 의존성을 보여준다.
- 스케치에서 빠뜨렸거나 단순화했던 부분을 채워 준다.
- 코드가 바뀌어도 지도를 계속 최신 상태로 유지해 준다.
핵심은 순서다.
먼저 자유롭게 생각하고, 그다음에 도구로 이해를 깊게 하고 검증한다.
처음부터 복잡한 시각화 도구로 뛰어들면, 디테일에 압도되기 쉽다. 거친 스케치로 시작하면, 내가 뭘 알고 싶은지 목적이 선명해진다. 그런 다음 도구는 그 목적을 돕는 역할을 하지, 방향을 휘두르지는 못한다.
맥락 더하기: 지도 주변의 로그, 세션, 쿼리
정적인 의존성은 각 부분이 서로 어떻게 통신할 수 있는지를 알려준다. 하지만 실제 시스템은 동작, 데이터, 런타임 조건에도 크게 의존한다.
작은 의존성 지도를 확장하는 강력한 방법은 여기에 맥락 데이터를 덧입히는 것이다.
-
에러 로그
간선에 메모를 단다: “여기서 500 자주 발생”, “널 포인터가 이 호출에서 시작됨”. -
사용자 세션
특정 유저 여정에 대한 흐름을 그린다:UI → API → Service → DB. -
데이터베이스 쿼리
어떤 모듈이나 함수가 비용이 큰 쿼리, 조인, 트랜잭션을 발생시키는지 표시한다. -
퍼포먼스 지표
노드나 간선에 레이턴시, 에러율, 처리량 같은 정보를 태그한다.
CodeViz 같은 도구는 이런 작업을 빠르고 직관적으로 하도록 돕는 걸 목표로 한다. 코드의 한 부분을 보고 곧바로 관련 로그, 쿼리, 트레이스를 끌어올 수 있게 해 준다. 그러면 머릿속 모델은 더 이상 “누가 누구를 호출하는가”에만 머물지 않고, 이렇게 바뀐다.
“누가 누구를 호출하는지, 얼마나 자주, 얼마나 비싸게, 얼마나 안정적으로 호출하는지.”
이렇게 풍부해진 지도를 바탕으로 다음과 같은 결정을 훨씬 잘 내릴 수 있다.
- 어디를 최적화할지
- 어디에 테스트나 모니터링을 더할지
- 어디를 단순화하거나 디커플링할지
오늘 바로 써 볼 수 있는 간단한 워크플로우
다음은 다음 작업에서 작은 의존성 지도를 써 볼 수 있는, 구체적이고 마찰이 적은 방법이다.
-
자를 범위를 정의한다
시스템 전체를 그리지 말고, 질문을 좁게 정한다.- “이 API 요청은 어디까지 흘러가나?”
- “이 리포트는 어떻게 생성되나?”
- “
User엔티티를 건드리는 건 뭐가 있지?”
-
모든 걸 읽기 전에 먼저 스케치한다
종이든 텍스트 문서든 하나 열고:- 가장 위에 엔트리포인트를 적는다 (예:
POST /payments). - 코드를 읽으며 발견하는 대로 박스를 추가한다.
- 눈에 보이거나, 그럴 것 같다고 추측되는 의존마다 화살표를 그린다.
- 가장 위에 엔트리포인트를 적는다 (예:
-
IDE와 도구로 검증한다
- “정의로 이동”, 검색, 또는 CodeViz 같은 도구로 실제 호출/ import 관계를 확인한다.
- 잘못된 화살표는 지우고, 빠진 화살표를 추가한다.
-
동작 정보를 덧붙인다
- 대표적인 로그 라인, 샘플 요청, 예시 쿼리를 붙인다.
- 에러나 성능 문제가 발생하는 지점을 표시한다.
-
계획을 결정한다
이제 구조가 명시적으로 드러났으니 다음 질문에 답해 본다.- “최소한으로 건드려야 할 코드는 어디까지지?”
- “이걸 바꾸면 어디가 깨질 수 있지?”
- “여기에 더 단순한 설계가 숨어 있는 건 아닐까?”
-
지도를 작고 일회용에 가깝게 유지한다
이게 꼭 영구 문서가 될 필요는 없다. 어떤 지도는 문서화해서 남길 가치가 있지만, 어떤 지도는 그 순간 생각을 정리해 줬다는 것만으로 소임을 다한 것이다.
마무리
거대한 코드베이스를 “완전히 이해”할 필요는 없다. 진짜 필요한 건 지금 내 문제에 relevant한 조각을, 알맞은 추상화 수준에서 이해하는 것이다.
작은 의존성 지도는 그걸 가능하게 해 준다. 이 지도는:
- 숨어 있는 관계를 눈에 보이는 화살표로 바꾸고
- 구조를 바깥으로 꺼내 인지 부하를 줄여 주며
- 혼란스러운 스파게티 코드를 탐색 가능한 계획으로 바꾸어 주고
- CodeViz 같은 도구와 결합해 의존성, 로그, 쿼리를 하나의 일관된 뷰로 엮어 준다.
작게 시작하라. 다음 버그나 기능 작업에서, 그냥 스크롤하고 검색만 하려는 충동을 잠시 참아 보라. 대신 잠깐 멈추고, 종이든 노트든 가벼운 도구든 하나 열어 빠르게 의존성 지도를 그려 본다. 그리고 그다음에 더 발전된 도구로 그 스케치를 보완하고 검증하라.
그러면 파일 속에서 길을 잃는 시간은 줄고, 코드가 실제로 어떻게 맞물려 돌아가는지에 대한 명확하고 시각적인 이해를 바탕으로, 더 자신 있고 정밀한 변경을 할 수 있게 될 것이다.