인터페이스가 왜 필요할까?
사실 진짜 왜 굳이 써야하는지 직접적인 체감이 덜 됐다.
이번 기회에 진짜 필요한 이유를 알아보려 한다.
인터페이스
interface UserRepository {
UserDTO findById(int id);
List<UserDTO> findAll();
}
```
클래스 for 데이터베이스
class UserRepositoryImpl implements UserRepository {
@Override
public UserDTO findById(int id) {
// 데이터베이스에서 조회하는 로직
return new UserDTO();
}
@Override
public List<UserDTO> findAll() {
// 데이터베이스에서 전체 유저 조회
return new ArrayList<>();
}
}
이런 코드가 있을 때 나중에 데이터베이스 말고 Redis를 추가해 Redis에서 데이터를 조회하고 싶은 상황이 생길 수 있다. 혹은 Redis로 전환하는 상황이 생길 수 있다.
그때 인터페이스를 위에 처럼 만들어 놨다면
class RedisUserRepository implements UserRepository {
@Override
public UserDTO findById(int id) {
// Redis에서 조회하는 로직
return new UserDTO();
}
@Override
public List<UserDTO> findAll() {
// Redis에서 전체 유저 조회
return new ArrayList<>();
}
}
이후 UserRepositoryIpl
대신 RedisUserRepository
를 주입하면 된다.
UserRepository userRepository = new RedisUserRepository(); // Redis로 교체
이렇게!!
즉 코드 수정 없이, 구현체만 바꿔서 쉽게 데이터 저장소를 변경할 수 있다.
인터페이스를 이용하면 Mock(가짜) 구현체를 주입해 테스트 할 수 있다. ➔ DB에 의존하지 않고 테스트를 편하게 할 수 있다.
class MockUserRepository implements UserRepository {
@Override
public UserDTO findById(int id) {
// 테스트용 데이터 반환
UserDTO user = new UserDTO();
user.setName("테스트 유저");
return user;
}
@Override
public List<UserDTO> findAll() {
return List.of(new UserDTO());
}
}
public class UserServiceTest {
@Test
public void testFindById() {
UserRepository mockRepo = new MockUserRepository();
UserDTO user = mockRepo.findById(1);
assertEquals("테스트 유저", user.getName());
}
}
이처럼 테스트 환경에서 DB 연결 없이도 간단한 테스트가 가능해진다
Spring Framework 같은 의존성 주입 프레임워크를 사용할 때, 인터페이스를 사용하면 구현체를 유연하게 변경할 수 있다. 예를 들어, 스프링 컨테이너가 실행될 때 UserRepository 인터페이스의 어떤 구현체를 사용할지 쉽게 결정할 수 있다.
예시:
코드 복사
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public UserDTO getUserById(int id) {
return userRepository.findById(id);
}
}
나중에 구현체를 교체할 때도 코드 수정 없이 스프링 설정 파일만 변경하면 된다.
만약 인터페이스가 없다면, UserRepositoryImpl 클래스에 강하게 의존하게 된다. 즉, DB를 Redis로 바꾸거나 다른 저장소로 바꾸기 위해서는 모든 사용되는 곳을 수정해야 한다. 테스트 환경에서도 DB 연결 없이 테스트하려면 매번 코드 수정이 필요하다.
코드가 성장하고 복잡해질수록 인터페이스의 장점이 명확하게 드러나게 되어있다.
만약 작은 프로젝트라 구현체가 하나이고, 확장가능성이 없다면 굳이 사용하지 않아도 된다.
하지만 확장성과 유지보수성을 고려한다면 인터페이스를 도입하는 것이 장기적으로 유리할 것. ➡️ 결론: 지금 당장은 인터페이스가 필요 없어 보여도, 장기적인 유지보수와 확장성을 위해 사용하는 것이 좋.