추상 클래스 vs 인터페이스
추상 클래스와 인터페이스
"무엇을 할 수 있는가"라는 계약을 구현과 분리해서 정의하는 두 도구를 익힌다. 이 레슨을 끝내면 추상 클래스와 인터페이스를 상황에 맞게 골라 쓰고, 전략 패턴과 템플릿 메서드 패턴으로 교체 가능한 설계를 만들 수 있다.
1. 왜 배우는가
결제 시스템을 만든다고 하자. 처음에는 카드 결제만 있었는데, 몇 달 뒤 계좌이체가 추가되고, 다시 간편결제(카카오페이, 네이버페이)가 붙는다. 결제 방식이 추가될 때마다 주문 처리 코드를 고쳐야 한다면 그 시스템은 곧 손댈 수 없는 상태가 된다. 주문 처리 코드는 "결제한다"라는 계약만 알고, 실제로 어떤 방식으로 결제하는지는 몰라야 한다. 이 계약을 정의하는 도구가 인터페이스와 추상 클래스다.
Spring, JPA, JDBC 같은 라이브러리를 쓸 때 우리는 거의 항상 인터페이스를 다룬다. List, Map, Runnable, Comparator, DataSource, Repository — 전부 인터페이스다.
라이브러리가 인터페이스로 계약을 공개하고 구현체를 숨기기 때문에, 우리는 구현체가 ArrayList에서 LinkedList로, H2에서 PostgreSQL로 바뀌어도 코드를 고치지 않는다. 이 원리를 이해하면 라이브러리 문서가 읽히기 시작하고, 내 코드도 같은 방식으로 설계할 수 있다.
테스트 관점에서도 중요하다. 실제 결제 API를 호출하는 코드를 단위 테스트할 수는 없다. 인터페이스로 분리해 두면 테스트에서는 "항상 성공하는 가짜 결제 게이트웨이"를 끼워 넣을 수 있다. 인터페이스 없이는 테스트 가능한 코드를 쓰기가 매우 어렵다.