스프링이란?
Spring은 무엇인가?
- Java 기반의 애플리케이션 프레임워크
- 스프링 프레임워크
- 핵심 기술 : 스프링 DI 컨테이너, AOP, 이벤트, 기타
- 웹 기술 : 스프링 MVC, 스프링 WebFlux
- 데이터 접근 기술 : 트랜잭션, JDBC, ORM, XML
- 기술 통합 : 캐시, 이메일, 원격접근, 스케줄링
- 테스트 : 스프링 기반 테스트
- 스프링 부트
- 스프링을 편리하게 사용할 수 있도록 지원
- 단독으로 실행할 수 있는 스프링 애플리케이션을 쉽게 생성 가능
- 웹 서버를 내장하고 있음
- 손쉬운 빌드 구성을 위한 starter 종속성
- 스프링과 외부 라이브러리 자동 구성
- 메트릭, 상태 확인, 외부 구성과 같은 프로덕션 준비 기능
- 스프링을 사용하는 이유?
- 자바 언어 기반의 프레임 워크로 객체 지향 언어의 장점을 잘 살려낸다.
- 스프링을 통해 좋은 객체 지향 애플리케이션을 개발할 수 있도록 해준다.
좋은 객체 지향 프로그래밍
스프링은 통해 좋은 객체 지향 애플리케이션을 개발한다는데, 그렇다면 좋은 객체 지향 프로그래밍이란 무엇인가?
- 컴퓨터 프로그램을 객체라는 여러개의 독립된 단위들의 모임으로 생각할 수 있고, 각 객체는 메시지를 서로 주고 받으며 데이터를 처리함.
- 객체 지향 프로그래밍은 유연하고 변경에 용이하기 때문에 대규모 소프트웨어 개발에 사용됨
- 객체를 쉽게 갈아 끼울 수 있음
- 다형성을 통해 역할과 구현을 분리하여, 역할에만 의존하도록 하여 구현 부분을 자유롭게 갈아끼울 수 있음
- 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있음
public class UserService {
private UserRepository userRepository = new MemoryUserRepository;
private UserRepository userRepository = new JpaUserReposiotory;
}
유저 서비스는 유저 레포지토리의 구현에 의존하지 않고, 역할만에 의존함.
즉, 구현한 클래스가 아닌 역할을 지정한 인터페이스 만으로 서비스를 할 수 있음.
public interface UserRepository {
void save();
User findById(Long id);
}
public class MemoryUserRepository implement UserRepository {
@Override
void save() {
...
}
@Override
User findById(Long id) {
...
}
}
public class JpaUserRepository implement UserRepository {
...
}
구현 클래스는 명세된 역할을 구현하기만 하면 되며, 자유롭게 바꾸어 낄 수 있음.
- 역할과 구현을 분리하여 다형성을 가능하게 하였고, 이는 스프링에서 IoC(제어의 역전), DI(의존관계 주입)을 편리하게 사용할 수 있도록 지원함
SOLID 원칙
SRP(Single Responsibility Principle) - 단일 책임 원칙
- 하나의 클래스는 하나의 책임만 가져야 한다. 하나의 클래스는 하나의 기능만을 담당하여 하나의 책임을 수행하는 데에 집중해야 함.
- 책임의 크기는 어떻게 정하는가? 정하기 애매하다...
- 변경을 기준으로 책임을 부여하면 효과적임. 변경이 있을 때 파급 효과가 적다면 SRP를 잘 따랐다고 할 수 있다.
- 장점
- 한 책임의 변경으로부터 다른 책임의 변경으로의 연쇄작용에서 자유로울 수 있음
- 코드의 가독성 향상
- 유지보수 용이
- 어떻게 지킬 수 있는가?
- 클래스명에 책임을 분명하게 나타낼 수 있도록 작성한다.
- 책임을 분리할 때, 결합도와 응집도를 고려하여 분리한다. 응집도는 높게, 결합도는 낮게 설계한다.
OCP(Open Closed Principle) - 개방 폐쇄 원칙
- 소프트웨어 요소는 "확장"에는 열려 있으나, "변경"에는 닫혀 있어야 함
- 다형성을 활용하여 기능과 구현을 분리하였지만, 구현을 바꾸기 위해서는 클라이언트의 코드를 변경해야 함. 즉, 확장을 하기 위해서 클라이언트 코드를 변경해야 해 OCP 원칙을 위반하는 경우가 발생한다.
- 이는 객체를 생성하고, 연관관계를 맺어주는 별도의 설정자가 필요하게 됨. 스프링의 컴포넌트 스캔과 Autowired가 이를 해결해 줌.(Dependency Injection)
LSP(Liskov Substitution Principle) - 리스코프 치환 원칙
- 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 함.
- 구현 클래스는 기능 인터페이스에서 정한 규칙을 모두 지켜야 함.
ISP(Interface Segregation Principle) - 인터페이스 분리 원칙
- 인터페이스를 명확하게 분리해야지, 특정 인터페이스를 변경해도 다른 인터페이스에 영향을 주지 않음
- 보다 더 쉽게 대체할 수 있게 됨.
DIP(Dependency Inversion Principle) - 의존관계 역전 원칙
- 프로그램을 작성할 때, 구체화가 아닌 추상화에 의존하도록 함. 즉, 구현 클래스가 아닌 인터페이스에 의존하도록 하지만, 실제로는 구현 클래스를 선언해주어야 함.
- 이러한 문제를 해결해주기 위해서 의존관계 주입을 해주는 컨테이너를 통해 구현 클래스에 의존하지 않는 클래스를 만들 수 있도록 함.