디자인 패턴
라이브러리와 프레임워크의 기본이 되는 디자인 패턴
프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 '규약' 형태로 만들어 놓은 것
GoF(Gang of Fout, 디자인 패턴을 구체화하고 체계화한 사람들)이 세가지로 분류한 디자인 패턴.
- 생성(Creational) 패턴
- 객체 생성에 관련된 패턴
- 객체의 생성과 조합을 캡슐화해 특정 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 크게 받지 않도록 유연성을 제공한다.
- 구조(Structural) 패턴
- 클래스나 객체를 조합해 더 큰 구조를 만드는 패턴
- 예를 들어 서로 다른 인터페이스를 지닌 2개의 객체를 묶어 단일 인터페이스를 제공하거나 객체들을 서로 묶어 새로운 기능을 제공하는 패턴이다.
- 행위(Behavioral)
- 객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴
- 한 객체가 혼자 수행할 수 없는 작업을 여러 개의 객체로 어떻게 분배하는지, 또 그렇게 하면서도 객체 사이의 결합도를 최소화하는 것에 중점을 둔다.
라이브러리 vs 프레임워크
라이브러리 : 사용자가 전체적인 흐름을 만들때 라이브러리를 가져다 쓴다
프레임워크 : 전체적인 흐름은 스스로 쥐고 있으며 사용자가 그 안에 코드 넣는다
제어의 역전(IoC) : 사용자의 코드를 프레임워크가 호출해서 사용
싱글톤 패턴
싱글톤 패턴은 하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다.
생성자가 여러번 호출되어도 하나의 인스턴스만 리턴한다.
JAVA에서는 정적멤버(static)과 singleInstanceHolder를 이용하여 Singleton 인스턴스를 생성한다.
class Singleton {
private static class singleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return singleInstanceHolder.INSTANCE;
}
}
public class HelloWorld{
public static void main(String []args){
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
if (a == b){
System.out.println(true);
}
}
}
- final 키워드를 통해서 read only 즉, 다시 값이 할당되지 않는다.
- 중첩클래스 Holder로 만들었기 때문에 싱글톤 클래스가 로드될 때 클래스가 메모리에 로드되지 않고
어떠한 모듈에서 getInstance()메서드가 호출할 때 싱글톤 객체를 최초로 생성 및 리턴한다.
장점
-고정된 메모리 영역을 사용하기 때문에 메모리 자원을 아낄 수 있다.
-데이터 공유가 쉽다.
-하나의 인스턴스를 만들어 놓고 다른 모듈들이 해당 인스턴스를 사용하기 때문에 인스턴스를 생성할때 비용이 준다는 장점이 있다. 그래서 I/O Bound 작업에 많이 사용된다.
Network, DB, File System과의 연결과 같은 작업을 I/O Bound 라고 한다.
단점
-구현해야할 코드가 많다.
-의존성이 높아진다는 단점이 있다. OCP 원칙(개방 폐쇄 원칙)에 위반된다.
-TDD할 때 걸림돌이 된다. 싱글톤 패턴에서는 순서가 Fix 되기 때문에 단위 테스트 하기 어렵다.
※ static
- class level에서 관리
- 함수 같은 경우 객체 생성 안해도 호출 가능
- 클래스당 1개 생성 -> 따라서 같은 클래스에 있으면 같은 값을 봄
- 클래스 로딩 시에 생성 -> 그래서 객체 생성 전에 사용가능
- static 영역에 할당
의존성 주입
싱글톤 패턴의 높은 결합도는 의존성 주입을 통해 해결할 수 있다.
의존성 : 파라미터나 리턴 값 또는 지역변수 등으로 다른 객체를 참조하는 것. A가 B에 의존성이 있다는 것은 B의 변경사항에 대해 A도 변해야 한다는 것이다.
의존성 주입은 메인 모듈이 직접 하위모듈에 대해 의존성이 있기 보다 의존성 주입자를 통해 간접적으로 의존성을 주입하는 것이다. 즉, 의존 관계를 외부에서 결정하는 것
장점
- 모듈들을 쉽게 교체할 수 있는 구조가 되어 테스팅하기 쉽고 마이그레이션하기도 수월
단점
-클래스 수가 늘어나 복잡성이 증가될 수 있으며 약간의 런타임 페널티가 생긴다.
팩토리 패턴
객체 생성 부분을 떼어낸 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 뼈대를 결정하고 하위 클래스에서 구체적인 내용을 결정하는 패턴이다.
전략 패턴(정책 패턴)
객체의 행위를 바꾸고 싶은 경우 '직접' 수정하지 않고 '캡슐화한 알고리즘'을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴
ex) 결제할때 네이버페이, 카카오페이 등 다양한 방법으로 결제
Template Pattern vs Strategy Pattern
Template Pattern
- 중복 없애기 + 형식 맞추기
- 기본적인 구조를 정의. 내용이 구현되어 있는것도 있고 껍데기만 있는것도 있다.
- 상속해서 구현해야 한다. 따라서, 부모 클래스가 바뀌면 자식 클래스도 바뀐다. => OCP 위반
- 객체를 바꿔끼면 된다
Strategy Pattern
- 인터페이스 구현
- 바꿔끼면 된다.
※ 상속 vs 구현
상속(Extends)
- 자식 클래스가 부모 클래스의 메서드 등을 상속 받아 자식 클래스에서 추가 및 확장
- 재사용성, 중복성 최소화
구현
- 부모 인터페이스를 자식 클래스에서 재정의하여 구현
- 반드시 부모 클래스의 메서드를 재정의하여 구현
- Interface => 함수 껍데기만 있다.(return type, 함수명, argument)
옵저버 패턴
- 어떤 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴.
- 여러 객체에 대해 동일한 데이터 제어
- 주체 : 관찰자, 옵저버 : '추가 변화 사항'이 생기는 객체들을 의미
- 느슨한 결합
- 서로 사용작용하지만 서로에 대해 잘 모름
- 옵저버는 언제든 추가, 제거 가능
- 새로운 형식의 옵저보도 주제 변경 필요 없음
- 서로 독립적, 재사용 가능
- 서로 영향을 주지 않음
프록시 패턴
대상 객체에 접근하기 전 그 접근에 대한 흐름을 가로채 대상 객체 앞단의 인터페이스 역할을 하는 디자인 패턴.
어떤 일을 대신해서 한다고 생각하면 된다.
객체의 속성, 변환 등을 보완하며 보안, 데이터 검증, 캐싱, 로깅에 사용
프록시 서버
-웹 서버를 대신하여 응답. 자체 저장디스크를 갖고 있어 최근 호출된 객체의 사본을 저장 및 보존.
- 만약 요청하는 컨텐츠가 자신(프록시 서버)에게 있을 경우, 바로 클라이언트에게 컨텐츠를 제공
- 요청하는 컨텐츠가 자신에게 없을 경우 오리지널 서버에게 컨텐츠를 요청하고 이를 프록시 서버에 저장한 후 클라이언트에게 제공하는 형태로 동작
-로드 밸런싱 : 라운드 로빈 등을 이용하여 중간에서 조율 ⇒ 부하 분산
-보안 : 서버에 직접 접근 못하게 한다.
+)로드밸런서와 프록시란 용어는 업계 내에서 대강 동의어로 사용된다. 모든 프록시가 로드밸런서인 건 아니지만, 대부분의 프록시는 기본적으로 부하분산 기능을 수행한다.
cf) CDN: Content Delivery Network, 콘텐츠 전송 네트워크는 지리적 제약 없이 전 세계 사용자에게 빠르게 안전하게 콘텐츠를 전송할 수 있는 전송 기술. 서버-클라이언트 사이의 물리적인 거리를 줄여 콘텐츠 로딩 시간을 최소화함. 각 지역에 캐시 서버를 두어 캐시 서버가 콘텐츠를 전달한다.
nginx
비동기 이벤트 기반의 구조와 다수의 연결을 효과적으로 처리 가능한 웹 서버
프록시 서버로 둬서 실제 포트를 숨길 수 있고 정적 자원을 gzip으로 압축하거나, 메인서버 앞단에서 로깅할 수 있음
CloudFlare
웹 서버 앞단에 프록시 서버로 두어 DDOS 공격방어나 HTTPS 구축에 쓰임
CORS와 프론트 엔드의 프록시 서버
CORS를 해결하기 위해 프론트엔드에서 프록시 서버를 만든다.
cors(Cross-Origin Resource Sharing)
웹페이지에서 ajax를 동일서버에 요청하는 건 괜찮지만 외부서버에 요청하는 순간 동일 origin이 아니기때문에 이 요청을 거부하는 보안관련 이슈가 생긴다.
cross-origin이란 다음 중 한 가지라도 다른 경우를 말한다.
- 프로토콜 - http와 https는 프로토콜이 다르다.
- 도메인 - domain.com과 other-domain.com은 다르다.
- 포트 번호 - 8080포트와 3000포트는 다르다.
MVC 패턴
모델(Model), 뷰(View), 컨트롤러(Controller)로 이루어진 패턴
모델 : 데이터 관리 및 비지니스 로직 처리(DAO, DTO, Service 등)
뷰 : 비지니스 로직의 처리 결과를 통해 유저 인터페이스가 표현되는 구조(html, jsp, rest api 같은경우 jso)
컨트롤러 : 사용자의 요청처리. Model과 View 중개
동작
- 사용자의 Action들은 Controller에 들어온다
- Controller는 사용자의 Action를 확인하고, Model을 업데이트한다
- Controller는 Model을 나타내줄 View를 선택한다
- View는 Model을 이용하여 화면을 나타낸다
장점
- 각각의 구성요소에만 집중해 개발 가능
- 재사용성과 확장성이 용이
단점
- 애플리케이션이 복잡해질수록 모데로가 뷰의 관계가 복잡해짐
Spring MVC
DispatcherServlet : 클라이언트에게 요청을 받아 응답까지의 MVC 처리과정을 통제한다.
HandlerMapping : 클라이언트의 요청 URL을 어떤 Controller가 처리할지 결정한다.
HandlerAdapter : HandlerMapping에서 결정된 핸들러 정보로 해당 메소드를 직접 호출해주는 역할을 한다.
ViewResolver : Controller의 처리 결과(데이터)를 생성할 view를 결정한다.
1. 디스패처 서블릿이 사용자의 요청을 다 받는다.
2. Handler Mapping을 통해 이 요청을 처리할 Controller 를 정한다. -> @RequestMapping을 참고해서 Controller 할당
3. 디스패처 서블릿은 핸들러 어댑터에게 요청의 전달을 맡긴다.
4. 핸들러 어댑터는 해당 컨트롤러에 요청을 전달한다.
5. Controller는 비지니스 로직을 처리한 후에 반환할 뷰의 이름을 반환한다.
6. 디스패처 서블릿은 뷰 리졸버를 통해 반환할 뷰를 찾는다.
7. 디스패처 서블릿은 컨트롤러에서 뷰에 전달할 데이터를 추가한다.
8. 데이터가 추가된 뷰를 반환한다.
MVP 패턴
Model + View + Presenter
프레젠터 : view에 요청한 정보로 model을 가공하여 view로 보낸다.
Presenter는 View와 Model의 인스턴스를 가지고 있어 둘을 연결하는 접착제 역할을 한다. Presenter와 View는 1:1 관계
장점 : View와 Model의 의존성이 없다
단점 : 어플리케이션이 복잡해 질 수록 View와 Presenter 사이의 의존성이 강해진다.
MVVM 패턴
Model + View + View Model
View Model : View를 표현하기 위해 만든 View를 위한 Model. View를 더 추상화한 계층
View와 View Model 사이의 양방향 데이터 바인딩을 지원한다.
MVC 패턴과 달리 Command와 Data Binding을 가지고 있다.
커멘드 : 여러가지 요소에 대한 처리를 하나의 액션으로 처리할 수 있게 하는 기법
데이터 바인딩 : 화면에 보이는 데이터와 웹 브라우저의 메모리 데이터를 일치시키는 기법으로 뷰모델을 변경하면 뷰가 변경
장점 : UI를 별도의 코드 수정없이 재사용할 수 있고 단위 테스팅 하기 쉽다.
단점 : View Model의 설계가 어렵다