Simple Library App - Repository

BookRepository를 왜 인터페이스로 분리를 했을까?

image

BookRepository를 인터페이스로 분리하는 이유는 다양한 구현체를 통해 다양한 전략 패턴을 활용하기 위함이다. 이렇게 분리함으로써 코드의 유연성과 확장성을 높일 수 있다. 예를 들어, 메모리 기반 저장소, 데이터베이스 기반 저장소, 파일 기반 저장소 등의 서로 다른 구현체를 만들어 필요에 따라 선택하여 사용할 수 있다. 이로 인해 특정 구현체에 종속되지 않고, 변경이 용이할 수 있다. 이와 같이 인터페이스를 사용하면 새로운 기능을 추가하거나 기존 기능을 수정할 때도 기존 코드를 건드리지 않고도 다양한 방식으로 구현할 수 있다. 또한 BookServiceBookRepository에 의존하고 있다. 이는 BookService가 데이터 저장 및 조회를 위해 BookRepository를 사용한다는 것을 의미한다. BookService는 책을 추가, 수정, 삭제, 조회하는 비즈니스 로직을 구현하고, 이 과정에서 실제 데이터 작업은 BookRepository가 담당한다. 생성자 주입을 통해 런타임에 다양한 Repository의 구현체를 주입받는다. BookService의 생성자에서 BookRepository를 인터페이스를 매개변수로 받아 주입받는 방식이다. 런타임시 BookService의 생성자에서 구현체를 주입받게 된다. 런타임에 다양한 전략을 주입받는 것은 Spring의 의존성 주입 기능을 활용하는 것이다. 애플리케이션 실행 시점에 어떤 BookRepository 구현체가 BookService에 주입될지를 결정하게 된다. 예를 들어, BookRepositoryV0, BookRepositoryV1, BookRepositoryV2와 같은 다양한 구현체가 있을 수 있다. BookService는 주입받은 BookRepository를 사용하여 비즈니스 로직을 수행한다. 책을 추가하는 기능은 bookRepositorysave 메서드를 호출하여 책을 저장하는 방식으로 구현될 수 있다. 이때, 실제 데이터 저장 방식은 주입된 BookRepository의 구현체에 따라 달라진다. BookService는 구체적인 BookRepository의 구현에 대해 알 필요가 없고, 오로지 BookRepository 인터페이스만 알고 있으면 된다. 이는 다양한 데이터 저장 방식을 손쉽게 변경하거나 추가할 수 있는 기반이 된다.

다양한 정책기반의 구현체

위에 내용을 조금 간추려 보자면 BookRepository와 같은 인터페이스를 사용할 때 다양한 정책을 구현할 수 있다는 점이다. 예를 들어, 책 관리 시스템에서 BookRepository 인터페이스를 구현하는 여러 정책을 생각해볼 수 있다. BookRepository와 같은 인터페이스를 활용하면 다양한 정책을 구현할 수 있는 유연성을 제공한다. 예를 들어, 책 관리 시스템에서 BookRepository 인터페이스를 구현하는 여러 정책을 고려할 수 있다. 각 비즈니스 요구 사항에 맞춰 서로 다른 구현체를 만들어 제공할 수 있으며, InMemoryBookRepository, FileBookRepository, DatabaseBookRepository 등 다양한 구현체를 통해 서로 다른 저장소를 사용할 수 있다.

다양한 정책기반의 구현체를 어떻게 넣어줄 것인데?

image

AppConfig는 Spring 애플리케이션에서 의존성 주입을 관리하는 중요한 구성 요소이다. 이 클래스는 Spring 컨테이너에 빈을 등록하고 특정 구현체를 주입하는 역할을 한다. Spring의 의존성 주입은 런타임 시점에 어떤 구현체가 사용될지를 결정한다. 즉, 주입되는 시점에 실제로 어떤 구현체가 사용될지를 선택하게 된다. 이는 코드에서 인터페이스에만 의존하게 하고, 런타임에 Spring이 관리하는 빈을 기반으로 필요한 의존성을 주입하는 방식을 의미한다. 컴파일 시점에서는 인터페이스만 확인되므로 어떤 구현체가 사용될지를 알 수 없다. 예를 들어, 여러 개의 BookService 인터페이스 구현체가 있을 때, 컴파일 시점에는 이들 중 어떤 것이 선택될지를 알 수 없다. 이때는 인터페이스에만 의존하게 되어 유연성을 높인다. Spring에서는 @Autowired 어노테이션을 사용하여 해당 인터페이스의 구현체를 자동으로 주입받을 수 있다. 예를 들어, BookRepository 인터페이스의 여러 구현체 (BookRepositoryV0, BookRepositoryV1, BookRepositoryV2)가 있을 경우, Spring은 컨테이너에 등록된 빈 중에서 적절한 구현체를 찾아 bookRepository에 주입한다. 결론적으로, AppConfig는 Spring에서 의존성을 관리하는 핵심 요소이다. 런타임에 어떤 구현체가 주입될지를 결정하는 데 필수적이다. @Autowired를 통해 필요한 의존성을 자동으로 주입받아, 컴파일 시점에서는 인터페이스에만 의존하고 런타임 시점에 구현체가 결정되는 방식이다.

Repository Save Logic

image image

새로운 책을 추가하려는 경우, 메서드는 book.getId() == null 조건을 검사한다. 만약 책의 ID가 null이라면, 이는 새로운 책을 추가한다. 이때 makeBook(book) 메서드를 호출하여 새로운 책 객체를 생성한다. 이 메서드는 책 객체의 ID를 설정하고 필요한 다른 속성을 초기화하는 역할을 수행한다. 이후, 생성된 새 책 객체는 bookStore라는 저장소에 추가된다. 새로 추가된 책 객체를 반환하여 호출자에게 추가된 책의 정보를 제공한다. 반면, 책의 ID가 null이 아닐 경우, 기존 책을 수정하거나 업데이트를 할 수 있다. 이 경우 메서드는 먼저 주어진 ID가 bookStore에 존재하는지를 확인한다. bookStore.containsKey(book.getId()) 조건을 통해 ID의 유효성을 검증하며, 만약 존재하지 않는다면 IllegalArgumentException을 발생시킨다. 이 예외는 주어진 ID가 유효하지 않음을 나타내며, 호출자는 잘못된 ID로 인해 발생할 수 있는 오류를 방지할 수 있다. 만약 ID가 유효하다면, bookStore에 해당 ID로 책을 업데이트한다. 예외처리에 있어서 IllegalArgumentException은 “잘못된 인수 예외”라는 뜻이다. 이 예외는 메서드에 전달된 인수가 잘못되었거나 유효하지 않을 때 발생한다. 즉, 메서드가 요구하는 조건을 만족하지 않는 인수(예: 범위를 초과하거나 null 값 등)가 전달되었을 때 발생하는 예외를 말한다. 예를 들어, 어떤 메서드가 음수를 허용하지 않는데 음수가 전달되거나 위 코드와 같이 호출자가 잘못된 ID를 호출하여 ID값이 유효하지가 않다면 IllegalArgumentException을 던져서 “잘못된 인수”임을 알리고 적절한 오류 처리를 할 수 있다.

Reference

나의 뇌 + 공식 문서가 참 잘 나와있다.

https://docs.spring.io/spring-framework/reference/core/beans/classpath-scanning.html#beans-scanning-filters



© 2022. by taewoo

Powered by taewoo