2026년 01월 30일

🎯 Java 인터페이스 완벽 가이드

Java Spring Boot
Cover Image

🎯 Java 인터페이스 완벽 가이드

"구현이 아닌 인터페이스에 의존하라" - 객체지향 설계의 핵심 원칙


📋 목차

  1. ArrayList vs LinkedList: 왜 두 가지일까?
  2. Java Interface의 본질
  3. List Interface 완전 정복
  4. 인터페이스 기반 프로그래밍

1️⃣ ArrayList vs LinkedList: 왜 두 가지일까?

🤔 흔한 고민

JCF(Java Collections Framework)를 사용하다 보면 이런 의문이 듭니다:

💡 핵심 답변

두 구현체는 각각의 장단점이 명확합니다:

상황최적의 선택
🔍 랜덤 접근이 많은 경우ArrayList
빈번한 삽입/삭제LinkedList
💾 메모리 효율 중시상황에 따라 다름

✨ 핵심 포인트: 어느 것이 더 좋을지는 수행하는 동작에 달려 있습니다!


2️⃣ Java Interface의 본질

📖 Interface란?

자바 interface는 메서드 집합의 명세입니다.
이 interface를 구현하는 클래스는 명세된 모든 메서드를 반드시 제공해야 합니다.

🔍 실전 예제: Comparable Interface

public interface Comparable<T> {
    public int compareTo(T o);
}

✅ Interface 구현 조건

  1. 타입 파라미터 T를 명시해야 합니다
  2. T 타입의 객체를 인자로 받고 int를 반환하는 compareTo() 메서드를 제공해야 합니다

💼 실제 구현 사례: Integer 클래스

public final class Integer extends Number implements Comparable<Integer> {

    public int compareTo(Integer anotherInteger) {
        int thisVal = this.value;
        int anotherVal = anotherInteger.value;

        // 삼항 연산자를 활용한 비교 로직
        return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
    }

    // ... 다른 메서드들
}

🔑 이 코드의 핵심

🛡️ 컴파일러의 역할

클래스가 interface를 구현한다고 선언하면, 컴파일러는 자동으로 검증합니다:

💡 Tip: compareTo() 메서드 구현 시 삼항 연산자(ternary operator) ? :를 자주 사용합니다!


3️⃣ List Interface 완전 정복

🎨 List Interface의 구조

JCF는 다음과 같은 구조를 제공합니다:

List (Interface)
    ├── ArrayList (구현 클래스)
    └── LinkedList (구현 클래스)

📝 List Interface의 역할

List interface는 **"List가 된다는 의미"**를 정의합니다.

구현 클래스는 다음을 제공해야 합니다:

🔄 상호 교환 가능성

ArrayListLinkedList는 동일한 interface를 구현하므로 완벽하게 상호교환 가능합니다!

// 이 메서드는 ArrayList든 LinkedList든 잘 동작합니다
public void processData(List<String> data) {
    data.add("new item");
    String first = data.get(0);
    data.remove(first);
}

💻 실전 예제: ListClientExample

public class ListClientExample {
    // ⭐ 핵심: 구체적인 구현 클래스가 아닌 Interface 타입으로 선언
    private List list;

    // 생성자에서만 구체적인 구현체를 지정
    public ListClientExample() {
        list = new LinkedList();  // 여기만 바꾸면 ArrayList로 전환 가능!
    }

    // 반환 타입도 Interface로!
    private List getList() {
        return list;
    }

    public static void main(String[] args) {
        ListClientExample lce = new ListClientExample();
        List list = lce.getList();
        System.out.println(list);
    }
}

🎯 이 코드의 설계 포인트

  1. 인스턴스 변수: List interface 타입으로 선언
  2. 반환 타입: getList() 메서드도 List 타입 반환
  3. 구체 클래스 사용: 오직 생성자에서만!

🔄 유연한 구현체 교체

ArrayList로 바꾸고 싶다면? 생성자 한 줄만 수정하면 됩니다:

// Before
list = new LinkedList();

// After
list = new ArrayList();  // 이것만 바꾸면 끝!

4️⃣ 인터페이스 기반 프로그래밍

🎓 Interface-Based Programming이란?

인터페이스 기반 프로그래밍(Interface-Based Programming)은 다음을 의미합니다:

코드는 구체적인 구현 클래스가 아닌 인터페이스에만 의존해야 한다

✅ 올바른 방식

// ✅ GOOD: Interface에 의존
List<String> names = new ArrayList<>();

// ✅ GOOD: 메서드 파라미터도 Interface
public void processNames(List<String> names) {
    // ...
}

❌ 피해야 할 방식

// ❌ BAD: 구체적인 구현 클래스에 의존
ArrayList<String> names = new ArrayList<>();

// ❌ BAD: 메서드 파라미터도 구현 클래스
public void processNames(ArrayList<String> names) {
    // LinkedList를 전달할 수 없게 됨!
}

🎁 인터페이스 프로그래밍의 이점

1️⃣ 유연성: 구현체 교체가 쉬움

// 요구사항 변경 시 한 곳만 수정
// list = new LinkedList<>();  // 기존
list = new ArrayList<>();      // 변경

2️⃣ 재사용성: 다양한 구현체에서 동작

// 같은 코드가 모든 List 구현체에서 작동
public int getSize(List<?> items) {
    return items.size();
}

3️⃣ 테스트 용이성: Mock 객체 사용 가능

// 테스트 시 가짜 구현체로 대체 가능
List<String> mockList = mock(List.class);

⚠️ 중요한 트레이드오프

대상변경의 영향
🔄 구현 클래스 변경✅ 클라이언트 코드에 영향 없음
인터페이스 변경❌ 모든 클라이언트 코드 수정 필요

📌 그래서: 라이브러리 개발자들은 꼭 필요한 경우가 아니면 인터페이스를 절대 변경하지 않습니다!


📚 핵심 정리

🎯 반드시 기억할 3가지

  1. 인터페이스에 의존하라

    • 구체 클래스가 아닌 List, Set, Map 같은 interface 사용
  2. 생성자에서만 구체화하라

    • new ArrayList<>(), new LinkedList<>() 등은 생성 시점에만
  3. 변경에 유연한 코드를 작성하라

    • 구현체 교체가 쉬운 구조 유지

💼 실무 적용 팁

// ✅ 추천하는 패턴
public class OrderService {
    private final List<Order> orders;        // Interface로 선언

    public OrderService() {
        this.orders = new ArrayList<>();     // 생성자에서만 구체화
    }

    public List<Order> getOrders() {         // 반환도 Interface
        return orders;
    }

    public void addOrder(Order order) {
        orders.add(order);
    }
}

🚀 다음 단계


"좋은 코드는 변경에 유연하다. 인터페이스는 그 유연성의 핵심이다." 🎯

← 목록으로 돌아가기