오늘은 기존 개인 투 두 프로젝트에서 Service를 인터페이스화 시키고 Service 구현체를 만드는 리팩토링을 진행하였다.
public interface CardService {
// ...
}
@Service
public class CardServiceImpl implements CardService {
// ...
}
의문점
굳이 인터페이스를 써야하나? 라는 의문점을 가진 것은 아니다.
Spring에서 의존성 관리를 위해서 @Service 어노테이션을 갖고있는 클래스의 Bean을 빈팩토리에 넣어서 가지고 있는데, 내가 CardService인터페이스를 Controller에서 주입을 받는다고 하면
Spring 은 자동으로 인터페이스의 구현체 Bean을 찾아서 주입을 해준다.
여기서 들었던 의문이다.
@Service
public class CardServiceImpl1 implements CardService {
// ...
}
@Service
public class CardServiceImpl2 implements CardService {
// ...
}
@Service
public class CardServiceImpl3 implements CardService {
// ...
}
구현체가 여러개면 Bean을 어떻게 주입해줄 건데?
너가 어떻게 알고 주입을 해줄 건데?
@RestController
public class CardController {
private final CardService cardService;
public CardController(CardService cardService) { // 에러 발생!
this.cardService = cardService;
}
그러면 Impl 클래스에서만 인터페이스를 주입 받고 Controller에서는 ServiceImpl을 주입 받으면 되는 거 아니야??
@RestController
public class CardController {
private final CardServiceImpl1 cardService;
public CardController(CardServiceImpl1 cardService) {
this.cardService = cardService;
}
그냥 딱 이렇게 받아주면 되는 거 아닌가??
라고 생각했다.
인터페이스의 목적
인터페이스를 왜 써야하냐? 라는 의문을 가진 건 아니라고 위에서 말했지만 설명에 들어가기 전에, 인터페이스의 목적을 먼저 정리해야겠다.
인터페이스는 약속이다.
인터페이스는 통일성 유지다.
인터페이스는 규약이다.
내가 JpaRepository를 쓴다고 하면 많은 종류의 DB를 이용할 수 있다. 이 이유는 인터페이스 덕분이다.
DB 드라이버가 JpaRepository를 구현해서 자신의 DB와 맞도록 쿼리문을 작성해주는 것이다.
이게 인터페이스 덕분에 많은 종류의 DB를 이용할 수 있는 것이다.
어? 무궁무진하네?
그래서 어떻게 주입을 받을 수 있는데?
많은 블로그에서는 @Qualifier와 @Primary를 얘기한다.
@Qualifier
@Service
@Qualifier("cardService1")
public class CardServiceImpl1 implements CardService {
// ...
}
@Service
@Qualifier("cardService2")
public class CardServiceImpl2 implements CardService {
// ...
}
@Service
@Qualifier("cardService3")
public class CardServiceImpl3 implements CardService {
// ...
}
@RestController
public class CardController {
private final CardService cardService;
public CardController(@Qualifier("cardService1") CardService cardService) {
this.cardService = cardService; // CardServiceImpl1 의 Bean을 주입
}
인터페이스의 수많은 구현체중 어떤 구현체의 Bean을 받을 것인지 명확하게 짚어주는 것이다.
이러면 CardServiceImpl1의 Bean을 Controller에서 주입 받는다.
어? 버전 관리가 가능하겠네?
@Primary
@Service
public class CardServiceImpl1 implements CardService {
// ...
}
@Service
@Primary
public class CardServiceImpl2 implements CardService {
// ...
}
@Service
public class CardServiceImpl3 implements CardService {
// ...
}
@RestController
public class CardController {
private final CardService cardService;
public CardController(CardService cardService) {
this.cardService = cardService; // CardServiceImpl2 의 Bean을 주입
}
수많은 구현체 중 Primary가 붙은 클래스의 Bean을 자동으로 주입하라고 명확하게 표현하는 것이다.
뭐 비슷하네?
근데 여기서 더 생각이 많이 나는 게 많다
예를 들어..
어?? 회원 등급 별로 기능을 분리를 할 수 있겠네???
어?? Gradle이나 이런 버전관리에서도 이런 유사한 방식을 쓰겠네???
@Controller
public class CardController {
// 예시 코드
private final CardService cardService;
public CardController(CardServiceImpl1 cardService1, CardServiceImpl2 cardService2) {
if () {}
}
}
이런 느낌으로 뭘 주입 해줄지도 정해줄 수 있겠네?
어???????????????
AOP를 앞단에 넣고 여기서 등급이라던지 이런 분리 로직을 만들고 뭘 주입 받을 건지 고를 수 있게 할 수 있겠네??????
어??????? 생각보다 엄청 무궁무진하네???
라고 생각하며 배운 하루다 🤤🤤
'TIL' 카테고리의 다른 글
TIL 2023-12-14 정적쿼리 동적쿼리 그리고 QueryDSL 약간 (0) | 2023.12.14 |
---|---|
TIL 2023-12-13 AOP Logging 구현 (0) | 2023.12.13 |
TIL 2023-12-11 명시적 삭제 컬럼 (0) | 2023.12.11 |
TIL 2023-12-08 getWriter() has already been called for this response (0) | 2023.12.08 |
TIL 2023-12-07 Refresh Token 쓰는 이유 및 DB로 Redis를 쓰는 이유 (0) | 2023.12.07 |