SOLID 원칙
- 단일 책임 원칙 (SRP)
- 하나의 클래스는 하나의 책임만 가져야 한다.
- 클래스는 한 가지 기능에만 집중해야 하며, 그 외 기능들은 담당하지 않아야 함
- 3 Layered Architecture 과 유사하다.
- 개방 폐쇄 원칙 (OCP)
- 새로운 기능을 추가할 때 기존 코드를 수정하지 않고, 확장할 수 있도록 설계해야한다.
- 다형성을 활용하여 해결
- 인터페이스를 상속받은 새로운 클래스를 만들어 새로운 기능을 구현한다.
- 리스코프 치환 원칙 (LSP)
- 자식 클래스는 언제나 부모 클래스를 대체할 수 있어야한다.
- 부모 클래스를 사용하는 곳에서 자식 클래스를 사용해도 프로그램 동작에 문제가 없어야 함
- 인터페이스를 구현한 구현체를 믿고 사용할 수 있다. (신뢰성이 높음)
- 인터페이스 분리 원칙 (ISP)
- 하나의 큰 인터페이스보다 여러 개의 작은 인터페이스로 분리해야 한다.
- 인터페이스가 명확해짐
- 의존 관계 역전 원칙 (DIP)
- 인터페이스나 추상 클래스에 의존하도록 설계해야한다.
- 모듈간의 결합도를 낮추고, 유연성과 확장성을 높일 수 있다.
- 서로간의 변경 사항에 독립적이어서 변경에 유연하다.
- 객체 지향의 핵심은 다형성에 있다. 하지만 다형성만으론 개방 폐쇄 원칙 (OCP)와 의존 관계 역전 원칙 (DIP)를 지킬 수 없다.
IOC (제어의 역전, Inversion Of Control)
- 객체의 생성과 관리 권한을 Spring 컨테이너가 담당하는 것을 말하며, Spring에서는 컨테이너가 객체의 생성, 주입, 소멸을 관리한다.
- IOC의 역할
- IOC의 개념
- 객체의 생성 및 생명주기 관리를 Spring 컨테이너가 담당한다.
- 객체간의 결합도를 낮춰 유연한 코드가 된다.
DI (의존성 주입, Dependency Injection)
- Spring이 객체 간의 의존성을 자동으로 주입해주는 것
@ComponentScan
- Spring이 특정 패키지 내에서
@Component,@Controller,@Service,@Repository같은 어노테이션이 붙은 클래스를 자동으로 검색하고 이를 Bean으로 등록하는 기능
- ComponentScan 동작 순서
- Spring Application이 실행되면
@ComponentScan이 지정된 패키지를 탐색- 해당 패키지에서 위의 어노테이션이 붙은 클래스를 찾음
- 찾은 클래스를 Spring 컨테이너의 Bean으로 등록
- 등록된 Bean은 DI와 같은 방식으로 다른 Bean과 연결
Spring Bean은 자동 등록과 수동 등록 두가지가 존재하며, 위의 방식은 자동 등록의 방식이다.
Bean의 수동 등록
@Configuration이 있는 클래스를 Bean으로 등록하고 해당 클래스를 파싱해서@Bean이 있는 메서드를 찾아 Bean을 생성한다.
@Qualifier, @Primary (수동 등록)
@Qualifier@Component
@Qualifier("firstService")
public class MyServiceImplV1 implements MyService { ... }
@Component
@Qualifier("secondService")
public class MyServiceImplV2 implements MyService { ... }
@Component
public class ConflictApp {
private final MyService myService;
// 생성자 주입에 구분자 추가
public ConflictApp(@Qualifier("firstService") MyService myService) {
this.myService = myService;
}
}
@Primary@Component
public class MyServiceImplV1 implements MyService { ... }
@Component
@Primary
public class MyServiceImplV2 implements MyService { ... }
@Component
public class ConflictApp {
private final MyService myService;
public ConflictApp(MyService myService) {
this.myService = myService;
}
}
실제 적용 사례
DB가 두 개 존재하는 경우 (메인 MySQL, 보조 Oracle)
@Primary를 사용하면 된다.@Qualifier 로 Oracle을 사용하도록 만들 수 있다.@Qualifier의 우선순위가 더 높다.수동 등록 VS 자동 등록
- 자동 Bean 등록을 사용하는 이유
- 다양한 어노테이션으로 쉽게 등록할 수 있다.
- Spring Boot는 ComponentScan 방식을 기본으로 사용한다.
- 간단하면서 OCP, DIP를 준수하며 개발할 수 있다.
- 수동 Bean 등록을 사용하는 경우
- 외부 라이브러리나 객체를 Spring Bean으로 등록할 때
- DB 연결과 같이 비즈니스 로직을 지원하는 기술들에 사용
- 같은 타입의 Bean 여러개 중 하나를 명시적으로 선택해야 될 때
@Primary,@Qualifier
Validation (검증)
- 시스템이 미리 정의한 사양에 부합하는지 검증하는 것
- Validation을 사용하는 이유
- 주문서 작성 페이지에서 잘못된 입력값으로 인해 서버에 오류가 발생한다면?
- 서버의 오류로 인해 작성 페이지에서 오류 페이지로 이동한다면?
- 오류 페이지로 이동되어 작성중인 폼이 모두 리셋된다면?
- 위와 같은 상황이 나오지 않기 위해 사용
- Validation 역할
- 검증을 통해 적절한 메시지를 클라이언트에게 보여줘야 한다.
- 검증 오류로 인해 정상적인 동작을 하지 못하는 상황이 없어야 한다.
- 사용자가 입력한 데이터는 유지되어야 한다.
Binding Result
- Spring에서 기본적으로 제공되는 Validation 오류를 보관하는 객체
- 주로 사용자 입력 폼을 검증할 때 많이 쓰이고, Field Error와 Object Error를 보관한다.
Validator
@Valid와@Validated의 차이점
@Valid는 Java 표준이고,@Validated는 Spring에서 제공하는 어노테이션이다.@Validated를 통해 Group Validation 혹은 Controller 이외 계층에서 Validation이 가능하다.