티스토리 뷰

반응형

 

시작하며: 뜨거운 감자, 람다(Lambda)

https://www.youtube.com/watch?v=knASr7oQNTk

 

 

평소 즐겨보는 개발 유튜브 채널인 '포프TV'에 매우 도발적인 제목의 영상이 올라왔다. "람다 함수, 그냥 쓰지 마세요. 회사에 해가 됩니다."

Java, C#, Python 등 현대적인 언어에서 함수형 프로그래밍의 핵심 요소로 자리 잡은 람다. 간결하고 우아한 코드를 작성하게 해주는 강력한 도구로 칭송받는 이 기능을 쓰지 말라니, 심지어 회사에 해가 된다니. 개발자로서 그냥 지나칠 수 없는 주제였다. 영상을 끝까지 본 후, 포프 님의 주장에 대한 요약과 현업 개발자로서의 내 생각을 정리해보고자 한다.

포프가 말하는 '람다를 쓰지 말아야 할 이유'

영상에서 포프 님은 람다 함수를 남용했을 때 발생하는 여러 문제점을 지적한다. 크게 세 가지로 요약할 수 있다.

1. 성능 저하의 가능성

람다는 공짜가 아니다. Java를 기준으로, 컴파일러는 람다를 처리하기 위해 내부적으로 이름 없는 클래스(Anonymous Class)와 함수를 생성한다. 이는 곧 함수 호출에 따른 오버헤드, 메모리 점프에 의한 캐시 미스 등의 비용이 발생할 수 있음을 의미한다. 만약 인라인으로 처리해도 충분했을 간단한 코드를 굳이 람다로 만들었다면, 이는 불필요한 성능 저하를 유발하는 '섣부른 최적화'의 역효과를 낳을 수 있다.

2. 재사용성의 부재와 모듈화 저해

어떤 로직이 이름 있는 함수 대신 람다 표현식 내부에 숨겨져 있다면, 다른 곳에서 비슷한 로직이 필요할 때 이를 찾아 재사용하기가 매우 어렵다. 결국 비슷한 코드를 복사-붙여넣기 하게 될 가능성이 높아진다.

반면, 명확한 이름과 역할을 가진 함수로 분리되어 있다면, 그 함수는 자연스럽게 재사용 가능한 모듈이 된다. 람다의 남용은 코드의 모듈화를 방해하고, 장기적으로 유지보수 비용을 증가시킨다.

3. 코드 리뷰를 회피하는 심리적 수단

이 부분이 가장 흥미로웠다. 포프 님은 일부 개발자들이 함수를 설계하고, 그 의도를 명확히 드러내는 이름을 짓는 과정에서 오는 스트레스와 코드 리뷰의 압박을 피하기 위해 의도적으로 람다를 사용한다고 지적한다.

함수를 만든다는 것은, 그 함수의 역할, 이름, 파라미터, 반환 값 등 명확한 '인터페이스'를 설계하는 행위다. 이 과정에서 동료 개발자들의 날카로운 지적을 받다 보면 감정적인 소모가 커질 수 있다. 하지만 복잡한 로직을 이름 없는 람다 안에 숨겨버리면, 이러한 설계의 책임을 회피하고 코드 리뷰를 더 쉽게 통과할 수 있다는 것이다. 결국 '간편함'이라는 가면 뒤에 코드 품질의 저하를 숨기는 셈이다.

현업 개발자로서의 나의 생각과 예제

영상을 보며 포프 님의 주장에 많은 부분 고개를 끄덕였다. 특히 '코드 리뷰 회피 수단'이라는 심리적 분석은 정곡을 찌르는 듯했다. 하지만 현대 프로그래밍에서 람다를 완전히 금지하는 것은 현실적으로 어렵고, 때로는 바람직하지도 않다고 생각한다. Java의 Stream API나 C#의 LINQ처럼, 람다는 데이터 컬렉션을 다룰 때 코드의 가독성과 흐름을 매우 유연하게 만들어주기 때문이다.

중요한 것은 '언제 람다를 쓰고, 언제 함수로 분리할 것인가'에 대한 명확한 기준을 갖는 것이다.

나쁜 예: 복잡한 로직을 담은 람다

예를 들어, 특정 조건에 맞는 사용자 목록에서 VIP 사용자에게만 특별 할인율을 적용하여 이메일 주소를 추출하는 코드가 있다고 가정해보자. (Java 예시)

List<String> vipEmails = users.stream()
    .filter(u -> u.isActive() && u.getPurchaseAmount() > 1000) // 조건 1
    .map(u -> { // 조건 2
        if (u.getGrade() == UserGrade.VIP) {
            u.applyDiscount(0.15); // 특별 할인 적용
            return u.getEmail().toLowerCase();
        }
        return u.getEmail().toLowerCase();
    })
    .collect(Collectors.toList());

위 코드는 스트림 API의 흐름 안에서 모든 것을 처리하려다 보니 filtermap 내부의 람다식이 복잡해졌다. 특히 map 내부는 여러 줄의 로직과 부수 효과(side effect)까지 포함하고 있어 의도를 파악하기 어렵고 재사용도 불가능하다.

좋은 예: 람다는 위임, 로직은 함수로

포프 님의 조언에 따라 이 코드를 리팩토링 해보자. 복잡한 로직을 명확한 이름의 private 함수로 분리하는 것이다.

List<String> vipEmails = users.stream()
    .filter(this::isEligibleForEvent)
    .map(this::processAndGetEmail)
    .collect(Collectors.toList());

private boolean isEligibleForEvent(User user) {
    return user.isActive() && user.getPurchaseAmount() > 1000;
}

private String processAndGetEmail(User user) {
    if (user.getGrade() == UserGrade.VIP) {
        user.applyDiscount(0.15); // 특별 할인 적용
    }
    return user.getEmail().toLowerCase();
}

어떤가? 스트림 연산은 isEligibleForEventprocessAndGetEmail이라는 명확한 단계를 서술하는 이야기처럼 읽힌다. 각 함수의 역할이 이름만으로 명확히 드러나며, 내부 로직은 해당 함수 안에 캡슐화되어 있다. 이제 isEligibleForEvent 로직은 다른 곳에서도 얼마든지 재사용할 수 있다.

 

결론: 람다는 '수단'이지 '목표'가 아니다

포프TV 영상을 통해 람다의 편리함 이면에 숨겨진 비용을 다시 한번 생각하게 되었다. 람다는 그 자체로 좋은 것도, 나쁜 것도 아닌 강력한 '도구'일 뿐이다.

나만의 원칙을 세워본다. "람다의 바디(body)가 한 줄을 넘어가거나, 여러 가지 일을 동시에 처리하려는 경향이 보이면 즉시 함수로 분리하자."

결국 좋은 코드란 '짧은 코드'가 아니라 '읽기 쉽고, 재사용하기 쉽고, 유지보수하기 쉬운 코드'다. 람다의 간결함이라는 달콤한 유혹에 빠져 더 중요한 가치를 놓치고 있지는 않은지, 우리 모두가 코드를 작성하는 매 순간 고민해봐야 할 문제다.

 

 

영상 출처

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/10   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함