데이터를 묻는 것을 멈춰라! ‘Tell, Don't Ask’ 원칙을 마스터하라

발행: (2025년 12월 19일 오전 09:57 GMT+9)
4 분 소요
원문: Dev.to

Source: Dev.to

소개

객체지향 프로그래밍(OOP)에서 가장 흔한 실수 중 하나는 객체를 단순한 데이터 구조처럼 다루는 것입니다. 우리는 종종 객체에서 데이터를 꺼내어 로직을 수행한 뒤 결과를 다시 넣습니다. 이 글에서는 Tell, Don’t Ask (TDA) 원칙을 설명하고 간단한 ShopAccount 예제를 통해 코드가 어떻게 개선되는지 보여줍니다.

Tell, Don’t Ask 원칙

  • 묻지 말라: 객체에게 데이터를 요청해 직접 로직을 수행하지 마세요.
  • 지시하라: 객체에게 무엇을 해야 할지 알려주고, 객체가 스스로 상태를 관리하도록 하세요.

TDA를 따르면 캡슐화응집도가 향상됩니다.

안티패턴 예시

Shop 클래스가 Account에 속하는 로직을 관리하여 캡슐화를 위반하고 있습니다.

package com.mateandgit.programming.chapter1_2.problem;

public class Shop {

    public void sell(Account account, Product product) {
        // ❌ BAD: Asking for data (getMoney)
        long price = product.getPrice();
        long mileage = account.getMoney();

        // ❌ BAD: The Shop decides if the account has enough money
        if (mileage >= price) {
            // ❌ BAD: Modifying the account's state externally
            account.setMoney(mileage - price);
            System.out.println(product.getName() + " purchased.");
        } else {
            System.out.println("Insufficient balance.");
        }
    }
}

문제점

  • 캡슐화 위반ShopAccount의 내부 세부 사항(예: money 필드 및 잔액 계산 방식)을 알고 있습니다.
  • 로직 중복 – 돈을 차감해야 하는 다른 클래스는 if (balance >= price) 검사를 중복해서 구현해야 합니다.
  • 유지 보수 어려움 – 검증 규칙(예: “최소 잔액은 100이어야 함”)을 변경하려면 Account에 접근하는 모든 클래스에서 업데이트가 필요합니다.

리팩터링된 해결책

로직을 Account 클래스로 옮깁니다. 이제 ShopAccount에 명령만 내립니다.

package com.mateandgit.programming.chapter1_2.solution;

public class Shop {

    public void sell(Account account, Product product) {
        // ✅ GOOD: Ask a question (canAfford)
        if (account.canAfford(product.getPrice())) {

            // ✅ GOOD: Tell the object to do something (withdraw)
            account.withdraw(product.getPrice());
            System.out.println(product.getName() + " purchased.");
        } else {
            System.out.println("Insufficient balance.");
        }
    }
}

Account 클래스 안에는 canAfford()withdraw() 같은 메서드가 있어 로직을 캡슐화합니다.

리팩터링의 장점

  • 향상된 캡슐화Shop은 인출 방식에 대해 알 필요가 없으며, 단지 메시지를 보냅니다.
  • 재사용 가능한 로직 – 검증 규칙(canAfford)이 Account에 집중됩니다.
  • 가독성 – 코드를 읽으면 “계정이 충분히 있으면 인출한다.”와 같은 문장이 됩니다.
  • 높은 응집도와 낮은 결합도 – 객체가 자신의 상태를 관리하므로 코드가 더 깔끔하고 유지보수가 쉬워집니다.

Tell, Don’t Ask 적용하기

  • 하지 말 것: getSomething() → calculate → setSomething().
  • 할 것: 객체에 메서드를 만들고 지시하여 동작을 수행하게 합니다.

결과: 높은 응집도, 낮은 결합도, 그리고 향상된 캡슐화.

Back to Blog

관련 글

더 보기 »

SOLID 재검토 — 포스트 패턴 관점

원칙이 그 뒤에 있는 힘보다 덜 중요한 이유 SOLID는 체크리스트가 아니다. 그것은 더 깊은 힘들의 역사적 압축이다. 이것은 시리즈의 5부이다.