Search

[3장] 함수

기억하고싶은 내용과 궁금증 및 견해

많은 개발자들이 말하는 적절한 추상화 뭘까?

개발자들이 자주 언급하지만 막연하게 이해하는 추상화 수준을 실전에서 어떻게 판단하고 적용할 수 있는지 구체적으로 알아보자.
tl:dr 함수 이름 + 추상화 수준 일관성 + 의미적 응집도

추상화 수준이란?

추상화 수준(Level of Abstraction)은 코드가 "무엇을 하는가(What)"와 "어떻게 하는가(How)" 사이에서 어느 위치에 있는지를 나타낸다.
높음 (What) ──────────낮음 (How) 비즈니스 로직 구현 세부사항
높은 수준: 비즈니스 로직, 도메인 개념
중간 수준: 알고리즘 구조, 제어 흐름
낮은 수준: 구현 세부사항, 기술적 디테일

함수는 추상화 수준을 한단계만 내려가야 한다.

함수 내 모든 문장은 추상화 수준이 동일해야 한다. 그리고 그 추상화 수준은 함수 이름이 의미하는 작업 보다 한단계만 낮아야 한다.
public String render() throws Exception{ StringBuffer html = new StringBuffer("<hr); if(size>0) html.append(" size=\"").append(size+1).append("\""); html.append(">"); return html.toString(); }
Java
복사
추상화 수준이 최소 두개 있다.
1.
수평선에 크기가 있다.
2.
HR태그 자체의 문법 이다. 위 코드는 FitNess모듈 HruleWidget에서 가져왔다. 이 모듈으 ㄴ네 개 이상 연이은 대시를 감지해 HR태그로 변환한다. 대시 수가 많을 수록 크기는 커진다.
public String render() throws Exception{ HtmlTag hr = new HtmlTag("hr") if(size>0){ hr.addAttribute("size",""+(size+1)) } return hr.html() }
Java
복사
추상화 레벨의 구분 기준
특징
높은 추상화(High-level)
무엇을 하는지, 목표/의도 중심
processOrder(), printOwing()
중간 추상화(Mid-level)
핵심 로직, 알고리즘, 데이터 처리
calculateOutstanding(), validateOrder()
낮은 추상화(Low-level)
구체적 구현, 세부 동작
DB 저장, 콘솔 출력, 파일 쓰기, API 호출

함수는 한가지를 해야한다.

오해 : 여러 함수를 호출하면 여러 가지 일?
원칙: “한 함수에 하나의 일만 해야 한다” → 한 추상화 수준에서 하나의 역할 → 각 함수 자신이 처리하는 레벨이 한 단계여야 한다
중요: 여기서 ‘일’ = 추상화 수준의 단위
한 줄 한 줄의 세부 구현이 아니라
함수가 하는 목적/의도가 하나인지 보는 것
function printOwing(invoice) { printBanner(); const outstanding = calculateOutstanding(invoice); printDetails(invoice, outstanding); }
Java
복사
printOwing 함수의 목적은 청구서를 출력하는것이다. 내부적으로 호출하는 함수들은 모두 이 목적을 이루기 위한 세부 단계이다.

TO 문단 테스트

함수가 한 가지 일만 하는지 확인하는 방법:
TO [함수이름]하려면, [단계1]하고, [단계2]하고, [단계3]한다.
printOwing 예시:
"청구서를 출력하려면, 배너를 출력하고, 미지급금을 계산하고, 세부사항을 출력한다"
자연스럽죠? 세 단계 모두 청구서 출력이라는 한 가지 목적을 위한 의미 있는 단계들입니다.
반대 예시:
function handleOrder(order) { validateOrder(order); saveOrder(order); sendConfirmationEmail(order); // 갑자기 관련 없는 일들 cleanupOldLogs(); rebuildSearchIndex(); generateMonthlyReport(); }
Java
복사
TO 문단으로 읽으면:
"주문을 처리하려면, 주문을 검증하고, 저장하고, 확인 이메일을 보내고, 오래된 로그를 정리하고, 검색 인덱스를 재구성하고, 월간 보고서를 생성한다"
마지막 세 개는 주문 처리와 관련 없습니다. 이건 여러 가지 일을 하는 함수이다.

1. 추상화 수준 체크리스트

함수를 작성하거나 리뷰할 때:
모든 문장이 비슷한 추상화 수준인가?
갑자기 세부 구현이 튀어나오지 않는가?
고수준 개념과 저수준 구현이 섞여있지 않은가?

2. TO 문단으로 검증

TO 문단으로 읽었을 때 자연스러운가?
모든 단계가 함수명의 목적과 관련있는가?
뜬금없는 단계가 없는가?

3. 의미적 응집도 확인

모든 단계가 하나의 책임을 위한 것인가?
이 함수를 다른 방식으로 의미있게 나눌 수 없는가?

실전 적용 예시

Case 1: 사용자 등록

AS-IS: 추상화 수준 섞임

function registerUser(userData) { // 높은 수준 if (!validateEmail(userData.email)) { throw new Error('Invalid email'); } // 낮은 수준 - DB 세부사항 노출 const conn = db.connect('localhost', 'user', 'pass'); const query = 'INSERT INTO users (email, name) VALUES (?, ?)'; conn.execute(query, [userData.email, userData.name]); conn.commit(); // 높은 수준 sendWelcomeEmail(userData.email); }
Java
복사

TO-BE: 일관된 추상화

function registerUser(userData) { validateUserData(userData); saveUser(userData); sendWelcomeEmail(userData.email); } function saveUser(userData) { const userRepository = new UserRepository(); userRepository.save(userData); }
Java
복사

Case 2: 보고서 생성

AS-IS

function generateSalesReport(month) { const sales = fetchSalesData(month); const report = createReport(sales); // 보고서와 관련 없는 작업들 cleanupTempFiles(); updateDashboard(); notifyAllAdmins(); return report; }
Java
복사

TO-BE

function generateSalesReport(month) { const sales = fetchSalesData(month); const report = createReport(sales); return report; } // 별도 함수로 분리 function performMonthEndTasks(month) { generateSalesReport(month); cleanupTempFiles(); updateDashboard(); notifyAllAdmins(); }
Java
복사

Q1. 함수가 너무 많이 쪼개지지 않나요?

A: 추상화 수준을 맞추다 보면 함수가 많아집니다. 하지만:
각 함수는 이해하기 쉬워집니다
테스트하기 쉬워집니다
재사용 가능성이 높아집니다
3줄짜리 함수도 괜찮습니다. 중요한 건 명확성입니다.

Q2. 어디까지 추상화해야 하나요?

A: 정답은 없지만, 가이드라인은:
더 이상 의미 있게 나눌 수 없을 때까지
함수 이름으로 내부 구현을 충분히 설명할 수 있을 때까지
같은 수준의 추상화를 유지할 수 있을 때까지

Q3. 성능 때문에 함수를 인라인해야 할 때는?

A:
1.
먼저 명확성을 위해 추상화하세요
2.
성능 문제가 실제로 측정되면 그때 최적화하세요
3.
대부분의 경우 현대 컴파일러/인터프리터가 최적화합니다

핵심 정리

좋은 추상화의 특징

한 함수 내 모든 문장이 같은 추상화 수준
TO 문단으로 읽었을 때 자연스러움
하나의 명확한 책임만 가짐
함수 이름이 내부 구현을 적절히 숨김

실천 방법

1.
함수를 작성한 후 TO 문단으로 읽어보기
2.
추상화 수준이 섞였다면 중간 함수 추출
3.
관련 없는 작업은 별도 함수로 분리
4.
함수 이름이 너무 포괄적이면 주의 (doStuff, manager 같은 이름 피하기)

마치며

"적절한 추상화"는 결국 코드를 읽는 사람의 인지 부하를 줄이는 것이다. 한 번에 한 가지 수준에서 생각할 수 있게 만들어, 복잡한 시스템도 이해하기 쉽게 만든다.
꾸준히 TO 문단 테스트와 추상화 수준체크를 습관화해 자연스럽고 깔끔한 코드를 작성하길 스스로에게 기대 해본다.