인터페이스에 대한 생각 정리

1. 인터페이스란 무엇인가?

인터페이스(Interface)는 자바의 핵심 개념 중 하나로, 클래스가 구현해야 하는 메서드의 명세를 정의하는 추상적인 타입이다. 인터페이스는 다음과 같은 특징을 갖는다.

  • 메서드의 시그니처만 정의하고, 구체적인 구현은 제공하지 않는다.
    • 메서드의 시그니처 : 메서드의 이름과 그 메서드가 받을 파라미터의 종류 및 개수를 의미
  • 다중 구현(multiple inheritance)을 가능하게 한다.
    • 다중 구현 : 한 클래스가 두 개 이상의 부모 클래스를 상속받는 개념
  • 구현 클래스의 계약(Contract) 역할을 수행하여 코드의 유연성과 확장성을 높인다.
    • 계약 : 특정 규칙을 지켜야 한다는 약속을 의미
    • 인터페이스를 구현한 클래스는 반드시 메서드들을 구체적으로 구현해야 한다는 규칙(계약)을 지켜야 한다.

2. 인터페이스의 주요 목적

인터페이스는 여러 측면에서 중요한 역할을 한다.

  • 설계 원칙 준수: SOLID 원칙 중 ISP(Interface Segregation Principle, 인터페이스 분리 원칙) 및 **DIP(Dependency Inversion Principle, 의존 역전 원칙)**을 준수하는 데 필수적이다.
    • ISP : 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않도록 해야한다는 원칙
      • 필요한 기능만을 제공하는 작은 인터페이스로 분리하라는 의미
      • 큰 인터페이스는 다양한 기능을 제공하지만, 이를 구현해야 하는 클래스가 필요 없는 메서드까지 강제로 구현해야하는 상황이 발생 → 클래스가 불필요한 코드까지 작성하게 하여, 유지 보수성을 떨어뜨리고 확장성을 제한한다.
    • DIP : 고수준 모듈은 저수준 모듈에 의존해서는 안 되고, 둘 다 추상화에 의존해야 한다는 원칙 [추상화는 세부 사항에 의존해서는 안 되고, 세부 사항은 추상화에 의존해야 한다.]
      • 상위 모듈(고수준 모듈)이 하위 모듈(저수준 모듈)에 직접 의존하면 두 모듈 간 결합도가 높아져서 유연성과 확장성이 떨어지게 된다. 이 문제를 해결하기 위해, 의존 관계를 추상화(인터페이스 또는 추상 클래스)하여 결합도를 낮추는 방법이 DIP이다.
      • DIP 적용하면 테스트 코드 작성도 용이해진다. → 실제 구현이 아닌 인터페이스를 기반으로 의존성을 주입하면, 테스트 시 Mock 객체를 활용할 수 있어 독립적인 단위 테스트가 가능해진다.
  • 유지보수성 향상: 구체적인 구현을 숨기고 계약을 정의함으로써, 코드의 변경이 인터페이스에 의존하는 다른 부분에 영향을 최소화한다.
  • 다형성(Polymorphism) 활용: 인터페이스를 통해 다양한 구현을 하나의 타입으로 다룰 수 있다.
    • 역할(Role)과 책임(Responsibility) 분리 : 클래스의 역할(무엇을 하는가?)과 구현(어떻게 하는가?)을 분리할 수 있으며, 결합도를 낮추고 유연한 설계를 가능하게 한다.

3. 기본적인 사용 예시

public interface PaymentGateway {
    void processPayment(double amount);
}

public class CreditCardPayment implements PaymentGateway {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing credit card payment: " + amount);
    }
}

public class PayPalPayment implements PaymentGateway {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing PayPal payment: " + amount);
    }
}

이러한 방식으로 인터페이스를 활용하면 결합도를 낮추고, 새로운 결제 방식을 추가할 때 기존 코드를 수정하지 않고 확장할 수 있다.

4. 실무에서 인터페이스를 활용하는 방식

  1. 의존성 주입(DI, Dependency Injection)과 스프링 빈 관리
    • 인터페이스를 활용하면 런타임에 다양한 구현체를 주입할 수 있어 유지보수성이 향상된다.
    @Service
    public class PaymentService {
        private final PaymentGateway paymentGateway;
    
        public PaymentService(PaymentGateway paymentGateway) {
            this.paymentGateway = paymentGateway;
        }
    
        public void executePayment(double amount) {
            paymentGateway.processPayment(amount);
        }
    }
    
  2. 전략 패턴(Strategy Pattern) 적용
    • 전략 패턴 : 행동을 캡슐화하여, 실행 중에 동적으로 변경할 수 있도록 하는 디자인 패턴 즉, 실행 중에 특정 동작을 유연하게 변경할 수 있게 만들어 준다. 
      • 핵심 개념
        • 동일한 문제를 해결하는 여러 개의 알고리즘(전략)을 인터페이스로 추상화
        • 실행 중에 적절한 전략을 선택하여 사용할 수 있음
        • OCP(개방-폐쇄 원칙) 준수: 새로운 전략을 추가해도 기존 코드를 수정할 필요 없음
        • DIP(의존 역전 원칙) 준수: 특정 구현체가 아닌 인터페이스에 의존
  3. API 설계 시 인터페이스 중심 개발(Interface-Based Development, IBD)
    • 인터페이스 중심 개발 : 비즈니스 로직을 인터페이스로 추상화하여 구현체를 유연하게 변경할 수 있도록 유도하는 개발 방식
    • REST API 설계 시 서비스 레이어에 인터페이스를 두어 구현체를 쉽게 변경할 수 있도록 해준다.
      • 주요 개념
        • 구현체(Service Implementation)와 분리된 인터페이스를 통해 느슨한 결합(Loose Coupling)을 유지
        • 특정 기술(JPA, MyBatis, MongoDB 등)에 의존하지 않고 쉽게 교체 가능
        • Mock 객체를 활용한 단위 테스트 용이
        • SOLID 원칙 준수 (특히 DIP(의존 역전 원칙), OCP(개방-폐쇄 원칙) 적용

5. 인터페이스의 단점은 무엇인가?

  • 코드 중복 가능성: 여러 구현체에서 동일한 로직이 필요할 경우, 인터페이스에서는 메서드 구현이 불가능하므로 중복된 코드가 발생할 수 있다.
  • 변경 시 영향도가 클 수 있음: 인터페이스 변경은 이를 구현한 모든 클래스에 영향을 미치므로 신중한 설계가 필요하다.

6. 인터페이스와 람다 표현식은 어떤 관계가 있는가?

자바 8부터 인터페이스는 람다 표현식과 함께 활용될 수 있게 되었다.

특히, 함수형 인터페이스(Functional Interface)는 하나의 추상 메서드만 가지는 인터페이스로, @FunctionalInterface 어노테이션을 활용할 수 있다.

@FunctionalInterface
public interface Calculator {
    int calculate(int a, int b);
}

public class LambdaExample {
    public static void main(String[] args) {
        Calculator add = (a, b) -> a + b;
        System.out.println(add.calculate(5, 3));
    }
}

람다 표현식을 활용하면 코드가 간결해지고 유지보수성이 높아진다.

7. 인터페이스를 너무 많이 사용하면 발생하는 문제는?

인터페이스를 과도하게 사용하면 복잡성이 증가할 수 있다.

인터페이스가 많아지면 구현 클래스와의 관계가 복잡해지고, 관리 비용이 증가할 수 있다.

따라서 적절한 수준에서 인터페이스를 설계하고 유지하는 것이 중요하다.

결론: 인터페이스의 중요성 강조

마지막으로, 인터페이스를 단순한 문법 요소가 아니라 객체 지향 프로그래밍(OOP)과 SOLID 원칙을 준수하는 핵심 도구로 설명하며 마무리하려고 한다.

"인터페이스는 단순히 추상 메서드를 선언하는 것이 아니라, 설계의 유연성과 확장성을 극대화하는 수단이다. 실무에서는 이를 기반으로 느슨한 결합(loose coupling)을 유지하며, 다양한 패턴과 원칙을 적용하여 유지보수성이 높은 코드를 작성하는 것이 중요하다고 생각한다."

'Java' 카테고리의 다른 글

인터페이스와 추상 클래스 비교  (0) 2025.02.03
JVM 내부 구조  (0) 2024.02.23