당니의 개발자 스토리

객체 지향 설계와 스프링 본문

스프링/스프링 핵심 원리 - 기본편

객체 지향 설계와 스프링

clainy 2024. 1. 16. 11:58

이번 시간에는 객체지향 설계스프링에 대해서 알아보겠습니다.

다시 스프링으로 돌아왔습니다. 스프링 이야기에 객체지향 이야기가 왜 이렇게 많이 나오는가 하면,

사실 스프링은 다음 설명할 기술들로 다형성이랑 OCP, DIP가능하게 지원을 해주는 기술입니다.

DI는 뭐냐면, Dependency Injection이라고 하고 보통 의존관계 주입 또는 의존성 주입이라고 합니다.

그리고 DI 컨테이너라는 걸 제공한다는데, 이건 뭐냐면 자바의 객체들을 어떤 컨테이너 안에 넣어놓고, 이 안에서 의존관계를 서로 연결해주고 주입해주는 기능들제공해주는 거예요.

이것들을 활용하면, 클라이언트 코드의 변경 없이 기능을 확장할 수 있는 거예요. 그래서 쉽게 부품을 교체하듯이 개발을 할 수가 있습니다.

다시 스프링이 없던 시절로 돌아가볼게요.

옛날에 어떤 개발자가 좋은 객체 지향 개발을 해보려고 OCP, DIP 원칙을 지키면서 개발을 해봤어요. 그랬더니 원래 개발하던 코드보다 OCP, DIP 원칙을 지키려고 했을 때, 만들 게 너무 많은 거에요. 배보다 배꼽이 더 큰거죠. 그래서 프레임워크를 만들어버립니다.

순수하게 Java 로 OCP, DIP 원칙들을 다 지키면서 개발을 하다보면, 결국 Spring Framework, 더 정확히는 스프링 DI 컨테이너를 만들게 돼요. 이 DI 개념은 사실 말로 설명해도 잘 이해가 안 돼요. 코드로 직접 짜봐야 필요성을 알게 됩니다.

자 그러면 스프링이 왜 만들어졌는지 코드로 이해하는 시간을 다음 강의부터 가지도록 하겠습니다.


지금까지 공부한 내용을 쭉 다 정리해볼게요.

모든 설계의 역할과 구현을 분리해야 됩니다. 자동차와 공연의 예시를 떠올려 보세요. 그리고 애플리케이션 설계도, 마치 공연을 설계하듯이 배역만 만들어 두고, 배우는 언제든지 유연하게 교체하고 변경할 수 있도록 만드는 것이 좋은 객체지향 설계에요.

이거를 가능하게 하려면, 다형성 뿐만 아니라 아까 말한 OCP, DIP 등을 다 지켜야 됩니다.

그렇게 하려면 뭔가가 더 필요한데, 그게 바로 스프링 컨테이너가 되는 거죠.

이상적으로는 모든 설계에 인터페이스를 부여하는 게 좋아요. 조금 이상적일 수 있지만 완전히 궁극의 그림인 정말 인터페이스를 먼저 다 만들어놓고, 그 다음에 구현체를 개발하는 방식으로 개발하면, 어떤 장점이 있냐면 예를 들어서 기술적으로 어떤 데이터베이스를 사용할지 아직 정해지지 않은 거예요. 그래도 일단 개발을 해야된단 말이에요. 그게 인터페이스만 정해두면 가능합니다.

우리가 입문 강의에서 메모리 리포지토리를 만들고나서, db가 정해지면 jdbc나 jpa 리포지토리로 변경했었는데, 이런 식으로 인터페이스를 먼저 만들어 놓으면, 하부 구현 기술들에 대한 선택을 최대한 뒤로 미룰 수 있는 장점이 있어요.

그리고 또 예를 들어서 할인 정책 같은 게 있는데, 구체적으로 아직 정해지지 않았단 말이에요. 그렇다고 개발을 못하면 안 되잖아요. 그러면 할인 정책을 간단하게 만들어놓고, 즉 간단한 인터페이스를 만들고, 그거에 대한 간단한 구현체를 하나 만들어서 일단 개발을 시작하는 거예요.

예를 들어서 0원 할인으로 해놓고 개발을 진행하는 거예요. 그 다음에 나중에 기획에서 정리가 되면, 기능을 확장하면 되겠죠.

자 그래서 인터페이스를 먼저 설계하고, 구현을 나중에 하게 되면 구현 기술이 바뀌더라도 나머지를 변경할 필요가 없어져서 변경의 범위가 되게 작고 유연해진다는 장점이 있습니다.


자 그런데 물론 실무적인 어떤 고민이 있어요.

뭐가 있냐면, 이상적으로는 인터페이스를 도입하는게 좋지만 인터페이스를 도입을 하면, 즉 무분별하게 남발하면 추상화라는 비용이 발생해요. 이건 뭐냐면 이 비용이 성능에 대한 걸 말하는 게 아니라, 일단 단순하게는 인터페이스라는 클래스 만들어야 되고 구현 클래스 만들어야 되잖아요. 이건 그냥 하면 돼요.

그런데 뭐가 고민이냐면, 추상화가 돼버리면 개발자가 코드를 한 번 더 열어봐야 돼요. 이게 메모리 멤버 리포지토리를 쓸지, JDBC 멤버 리포지토리를 쓸지, 런타임에 여러 개 중에 하나가 선택이 될 수도 있거든요. 코드를 열면 인터페이스만 보이잖아요. 그럼 구현 클래스가 바로 안보여서 한번 또 들어가야 돼요.

이런 식으로 코드가 추상화 됨으로써 오는 장점만 있는게 아니라, 단점도 있어요. 그래서 항상 이 장점이 단점을 넘어설 때 선택을 해야 되거든요. 그래서 사실 추천하는 방법은 '이건 미래에 기능을 확장할 가능성이 없다' 그러면, 그냥 구체 클래스, 즉 추상화 없이 구체 클래스를 바로 쓰는 거예요.

그리고 향후에 꼭 필요하면, 리팩토링 해서 인터페이스를 구현해도 돼요.

근데 뭔가 딱 보고 '아 이거는 미래에 확장 가능성이 있다'라고 하면 처음부터 인터페이스를 도입하는 게 좋겠죠.


다음 강의부터는 이제 스프링 핵심 원리와 관련돼서 전부 다 코드, 그림으로 설명해드릴 거에요.

그래서 스프링 프레임워크를 왜 만들게 됐는지를 쭉 하나의 예제코드로 쭉 하면서 '아, 이러이러해서 컨테이너라는 걸 만들어야 되는구나', '이러이러해서 OCP랑 DIP를 지키려면 이런 거를 만들어야 되는구나' 라는 걸 같이 코드로 직접 만들어보면서 이해하는 시간을 가지도록 하겠습니다.