스프링 개발 시작하기
단축키!!
Ctrl Alt ← | 이전화면 |
---|---|
Alt F12 | 터미널 |
Alt insert | Generator |
Alt + Enter | 지역변수 생성 |
main | 메인함수 |
Ctrl + P | 파라매터의 위치 |
Ctrl + W | 단어 단위로 커서 선택 |
Alt 1 | 프로젝트으로 이동 |
Ctrl + Alt + Insert | 새 프로젝트 파일 생성 |
BufferedReader
버퍼를 사용하여 읽는 함수
키보드의 입력이 있을 때마다 한 문자씩 버퍼로 전송한다. 버퍼가 가득 차거나 혹은 개행 문자가 나타나면 버퍼의 내용을 한 번에 프로그램에 전달
속도 빠름
InputStreamReader
바이트 단위로 읽어 들이는 InputStream을 통해 입력을 받은 뒤, 문자 단위로 데이터를 변환시키는 중개자 역할을 하는 클래스
오브젝트와 의존관계
오브젝트
- 오브젝트: 프로젝트를 실행했을 때 움직이는 것
- 클래스: 오브젝트 생성을 위한 것 (청사진 같은 것)
- 인스턴스: 앞에 나온 추상적인 것(클래스)의 실체화
클래스를 통해 객체의 구조와 행동을 정의하고, 인스턴스를 통해 이러한 정의를 실제로 구현함
오브젝트 = 클래스의 인스턴스
자바에서 배열도오브젝트
📌 클래스는 객체의 설계도로, 객체가 가져야 할 상태(속성)와 행동(메소드)을 정의
반면, 인스턴스는 클래스에 의해 생성된 실체로, 클래스의 정의를 바탕으로 실제 메모리 상에 할당된 객체를 의미
의존관계
클래스 레벨
- Client → Supplier(의존한다) ⇒ client class가 제대로 의존하려면 supplier class가 필요
- Supplier 변경시 Client 영향 받음
오브젝트 레벨
- 클래스 레벨 의존관계와 다를 수 있음
관심사의 분리 (SoC)
관심사가 다르면 분리해야한다.
관심사는 변경으로 설명할 수 있다. 추후 확장, 수정할 때 어떻게 달라질 지 고민하기.
방법: 메서드 추출
하지만 나중에 확장성을 고려했을 때(다른 API를 사용하고 싶다 등) 완전한 해결책이 될 수 없음.
방법2: 상속 이용
하지만 복잡한 상속 구조를 활용하거나 기능이 많아졌을 때 불편할 수 있음
방법3: 클래스의 분리
하지만 이용하는 PaymentService에서 다른 Provider를 사용해야 할 때 메서드 이름, 클래스 이름이 다르기에 계속 수정해야 되기에 불편 —
방법4: 인터페이스 도입
PaymentService는 인터페이스에만 의존한다.
하지만 실제로는 Provider인 Class 레벨에도 의존한다. —
방법5: 관계설정 책임의 분리
런타임에 의존해서 사용해야 할 클래스를 설정하는 책임을 분리해줘야함
의존관계를 설정하는 클래스를 따로 만들
📌 객체지향 디자인 패턴 (상속을 통한 예시)
템플릿 메서드 패턴
어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴
팩토리 메서드 패턴
객체를 생성할 때 어떤 클래스의 인스턴스를 만들 지 서브 클래스에서 결정하는 패턴
인스턴스 생성을 서브 클래스에게 위임
오브젝트 팩토리
오브젝트 팩토리에 의존 관계를 설정하는 부분을 위임함
원칙과 패턴
개방 폐쇄 원칙(OCP)
클래스나 모듈은 확장에는 열려있어야 하고 변경에는 닫혀 있어야 한다. (어떤 면에서 열리고, 어떤 면에선 닫혀 있다)
== 어떤 클래스는 이 클래스의 기능을 확장할 때 코드가 변경되면 안된다.
높은 응집도와 낮은 결합도
높은 응집도 = 하나의 모듈이 하나의 책임, 관심사에 집중되어있다는 뜻 (변화가 일어날 때 해당 모듈에서 변하는 부분이 크다 -> 변화시 비용이 적게 듦)
낮은 결합도 = 느슨하게 연결된 형태를 유지해야함 (-> 변화시 비용이 적게 듦)
응집도 = 모듈 내부의 요소들이 연관된 정도 / 결합도 = 다른 모듈과의 의존성 정도
전략패턴
자신의 기능 context에서, 필요에 따라서 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있는 디자인 패턴
public class Sort { public static void main(String[] args) { List<String> scores = Arrays.asList("asdf", "qwerqwer", "zxcdv", "awer"); Collections.sort(scores, (o1, o2) -> o1.length() - o2.length()); scores.forEach(System.out::println); } }
제어의 역전
제어권 이전을 통한 제어관계 역전
제어권이 누구에게 있었고, 어디로 이동했는지 보면 알 수 있음
ex) 제어권을 이전: PaymentService -> Client -> ObjectFactory
스프링 컨테이너와 의존관계 주입
이제 스프링을 사용해보자!
How? – ObjectFactory를 BeanFactory(쓰프링 IoC/DI 컨테이너)로 변경하자!
Bean(PaymentService)와 Bean(WebApiExRateProvider)가 의존관계를 맺는다.
이러한 의존관계 정보와 Bean 클래스, 즉 구성정보를 ObjectFactory 를 거쳐서 BeanFactory에서 만들어서 client에서 사용하도록 한다.
스프링의 정체성은 IoC/DI 컨테이너다
구성 정보를 만드는 2가지 방법
- ObjectFactory에서 명시적으로 인스턴스 생성 뒤 사용
- 사용할 인스턴스에 @Component 어노테이션을 붙인 뒤, 컴포넌트 스캔을 통해 구성 정보를 동적으로 생성
싱글톤 레지스트리
어떻게 단 하나의 오브젝트만 가져와서 사용할 것인가??
-> 싱글톤 오브젝트를 사용해서 사용하도록 함
싱글톤 레지스트리는 직접 싱글톤 형태의 오브젝트를 만들고 관리한다.
매번 요청을 수행할 때 오브젝트를 만들면 과부하가 오기 때문에 단 한 개의 오브젝트만을 사용하여 사용하는 것이다.
따라서 동일한 ObjectFactory
클래스에서 다른 인스턴스를 생성해도 동일하다고 뜬다. 즉 1과 2의 동일한 오브젝트를 사용하는 것이다.
@Configuration
@ComponentScan
public class ObjectFactory {
@Bean
public PaymentService paymentService() {
return new PaymentService(exRateProvider());
}
// 1
@Bean
public ExRateProvider exRateProvider() {
return new WebApiExRateProvider();
}
@Bean
public OrderService orderService() {
return new OrderService(orderProvider());
}
// 2
@Bean
public OrderProvider orderProvider() {
return new OrderProvider();
}
}
DI와 디자인 패턴
오브젝트 합성을 이용하는 디자인 패턴을 적용할 때 스프링의 의존관계 주입(DI) 적용
환율 정보가 필요할 때마다 매번 외부 API를 호출하는 건 별로다!
-> 환율 정보 캐시를 도입하자
How? - 데코레이터 디자인 패턴 사용 (오브젝트에 부가적인 기능/책임을 동적으로 부여한다.)
의존성 역전 원칙 (DIP - Dependency Inversion Principle)
추상화에 의존해야지, 구체화에 의존하면 안 된다
- 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다. 둘 모두 추상화에 의존해야 한다.
- 추상화는 구체적인 사항에 의존해서는 안된다. 구체적인 사항은 추상화에 의존해야 한다.
Policy Layer(정책을 관리하는 중요 모듈, 상위 모듈, 잘 변하지 않음) - Mechanism Layer(환율 정보를 가져오는 모듈, 하위 모듈, 기술적인 부분 담당)
인터페이스 소유권의 역전이 필요하다 - Seperated Interface 패턴