1. 좋은 코드는 유지보수성이 좋아야 한다
좋은 코드의 조건은 무엇일까요? 우리는 유지보수성이라고 정의합니다.
개발이 비즈니스의 병목이 되지 않기 위해서입니다. 더 나아가 개발이 비즈니스의 성공 공식이 되려면, 요구사항이 바뀔 때마다 코드를 빠르고 안전하게 수정할 수 있어야 합니다.
2. 유지보수성 = 변경 용이성 + 낮은 인지부하
유지보수성이 좋은 코드는 두 가지 특징이 있습니다:
1.
변경이 쉽고
2.
코드를 이해하기 위한 인지부하가 적다
이 두 가지 목표는 좋은 추상화를 통해 달성할 수 있습니다.
3. 추상화란? What과 How의 분리
추상화란, 코드의 목적(What)과 세부 구현(How)을 분리하여:
•
사용하는 측에는 필요한 정보만 노출하고
•
How를 변경하더라도 외부로 변경이 불필요하게 전파되지 않도록 하는 것입니다
// 추상화 없음: What과 How 섞임
function ProductPage() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products') // How: REST API
.then(res => res.json())
.then(setProducts);
}, []);
return products.map(p => <div>{p.name}</div>); // How: map
}
// 추상화 있음: What과 How 분리
function ProductPage() {
const products = useProducts(); // What: 제품 가져오기
return <ProductList items={products} />; // What: 목록 표시
}
TypeScript
복사
4. 추상화와 변경 용이성: 점착성(Viscosity)
4-1. 점착성이란?
변경이 쉬운 코드란 점착성이 낮은 코드입니다.
점착성이란, 코드를 변경할 때 느끼는 "끈적끈적함", 즉 변경이 얼마나 번거롭고 어려운가를 나타내는 개념입니다.
// 높은 점착성: 10개 컴포넌트 모두 수정해야 함
function ProductPage() {
fetch('/api/products')... // API 바꾸려면 여기도
}
function CartPage() {
fetch('/api/products')... // 여기도
}
// ... 8개 더
// 너무 귀찮아서 "나중에" → 기술 부채 쌓임
TypeScript
복사
// 낮은 점착성: 한 곳만 수정
function ProductPage() {
const products = useProducts(); // API 바꾸려면
}
// useProducts 내부만 수정하면 됨
TypeScript
복사
4-2. 좋은 추상화는 점착성이 낮다
목적(What)은 비교적 잘 바뀌지 않고, 세부 구현(How)은 목적에 비해 자주 바뀝니다.
What과 How가 명확히 분리되면:
•
How 변경 (자주 발생): 영향 범위가 작음 (해당 추상화 내부만)
•
What 변경 (가끔 발생): 변경 범위가 명확함 (어떤 추상화를 수정할지 명확)
따라서 변경 시 느끼는 마찰이 줄어들고 변경 용이성이 높아집니다.
// What과 How 분리된 코드
function ProductPage() {
const products = useProducts(); // What 1
const filter = useFilter(); // What 2
return <ProductList items={products} filter={filter} />; // What 3
}
TypeScript
복사
아래와 같은 코드 변경 시나리오를 상상해볼 수 있습니다. 두 케이스 모두 추상화를 통해 What은 건드리지 않고, 추상화 벽 내부의 How만 수정할 수 있게 되었습니다.
•
API 엔드포인트 변경:
◦
useProducts 내부에서 /api/products → /api/v2/products만 수정
•
제품 검색 필터 추가:
◦
useFilter 내부만 수정
5. 추상화와 인지부하: 글처럼 읽히는 코드
글처럼 읽히는 코드란 요구사항과 코드가 1:1로 매칭되어 인지적으로 편안하게 느껴지는 코드입니다. 구현을 위한 세부사항(How)은 숨겨지고, 어떤 목적을 위해 작성된 코드인지(What)가 잘 드러나 있기 때문입니다.
What을 중심으로 작성되어 있으며, 세부 구현이 시선을 뺏지 않아 코드가 마치 요구사항 문서처럼 읽힙니다.
요구사항: "사용자는 제품 목록을 볼 수 있다"
const products = useProducts(); // "제품 목록을"
return <ProductList items={products} />; // "볼 수 있다"
JavaScript
복사
코드가 글처럼 읽혔을 때의 이점은 코드의 동작을 이해하기 위해 실행 순서를 따라가는 등 별도의 노력을 기울이지 않아도 된다는 점입니다. 덕분에 코드를 읽을 때의 인지부하가 낮아집니다.
// 실행 추적 필요 (인지부하 높음)
useEffect(() => {
fetch('/api/products')
.then(res => res.json())
.then(data => {
setProducts(data);
setLoading(false);
});
}, []);
// "fetch 호출하고... then에서... setProducts 하고..."
// 머릿속으로 실행 순서 추적 필요
// 실행 추적 불필요 (인지부하 낮음)
const products = useProducts();
// "제품 가져오네" -> 끝. 실행 추적 안 해도 됨
TypeScript
복사
6. 해법: Wishful Thinking
위와 같은 점착성이 낮고 글처럼 읽히는, 추상화가 잘 된 코드를 작성하려면 어떻게 해야 할까요?
SICP에서 언급된 Wishful Thinking을 활용할 수 있습니다.
7. Wishful Thinking이란?
Wishful Thinking이란 "이런 게 있으면 좋겠다(What)"를 먼저 상상하고, "어떻게 만들지(How)"는 나중에 생각하는 사고방식입니다. Wishful Thinking의 사고방식을 올바르게 따라가기만 해도 자연스럽게 추상화 된 코드가 만들어지게 됩니다.
// 1단계: What 먼저 상상
const products = useProducts(); // 아직 없어도 OK
<ProductList items={products} /> // 아직 없어도 OK
// 2단계: How 나중에 구현
function useProducts() {
return useQuery(...)
}
TypeScript
복사
8. Wishful Thinking을 잘 하려면?
Wishful Thinking을 잘 하려면 두 가지 능력이 필요합니다:
1.
분해/조립: 대상의 내부가 어떻게 구성되어 있는지에 대한 명시적 지식
•
예: "제품 페이지" → "List + 데이터 페칭 + 필터"
2.
일반화: 대상의 부수적인 부분을 덜어내고 본질을 추려내는 환원적 사고
•
예: 여러 단계 → "제품을 가져온다"
9. 핵심 통찰: 능력의 전이
이 능력은 일반적으로 사람들이 가지고 있는 일반 추론 능력과 언어 능력에 기반합니다.
따라서 이 능력을 프로그래밍 분야로 전이시키기 위한 '언어 능력 훈련'의 관점에서 바라보는 게 효과적입니다.
일상에서 이미 하고 있습니다:
•
커피 주문: "아메리카노 주세요" (How 생략)
•
택시 호출: "강남역으로" (경로 계산은 기사님이)
프로그래밍도 같은 능력:
•
useProducts() (내부 구현 생략)
•
<ProductList /> (렌더링 로직 생략)
Prat(2020) 연구가 밝혔듯, 프로그래밍 학습은 수학보다 언어 학습에 가깝습니다.
우리의 교육 전략:
•
•
