디자인 패턴
이란 프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 규약 형태로 만들어 놓은 것이다.
싱글톤 패턴
은 하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다. 하나의 인스턴스를 만들어 놓고 해당 인스턴스를 다른 모듈들이 공유하며 사용한다.
의존성 주입을 통해 모듈 간의 결합을 조금 더 느슨하게 만들 수 있다.
의존성 주입
은 메인 모듈이 직접 다른 하위 모듈에 대한 의존성을 주기보다는
중간에 의존성 주입자가 이 부분을 가로채 메인 모듈이 간접적으로 의존성을 주입하는 방식이다.
이를 통해 메인 모듈은 하위 모듈에 대한 의존성이 떨어지게 된다.
의존성 주입 원칙
: 상위 모듈은 하위 모듈에서 어떠한 것도 가져오지 않아야 한다. 또한, 둘 다 추상화에 의존해야 하며, 이때 추상화는 세부 사항에 의존하지 말아야 한다.
팩토리 패턴
은 객체를 사용하는 코드에서 객체를 직접 생성하지 않고, 객체를 생성하는 부분을 떼어내 추상화한 팩토리 객체를 사용하는 패턴이다.
상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정한다.
하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정한다.
CoffeeFactory 밑에 Coffee 클래스를 놓고, 해당 클래스를 상속하는 Latte, Espresso 클래스를 기반으로 구현한 코드이다.
enum CoffeeType {
LATTE,
ESPRESSO
}
abstract class Coffee {
protected String name;
public String getName() {
return name;
}
}
class Latte extends Coffee {
public Latte() {
name = "latte";
}
}
class Espresso extends Coffee {
public Espresso() {
name = "espresso";
}
}
class CoffeeFactory {
public static Coffee createCoffee(CoffeeType type) {
switch(type) {
case LATTE:
return new Latte();
case ESPRESSO:
return new Espresso();
default:
throw new IllegalArgumentException("Invalid coffee type: " + type);
}
}
}
public class Main {
public static void main(String[] args) {
Coffee coffee = CoffeeFactory.createCoffee(CoffeeType.LATTE);
System.out.println(coffee.getName());
}
}
전략 패턴
은 정책 패턴이라고도 하며, 객체의 행위를 바꾸고 싶은 경우 '직접' 수정하지 않고 전략이라고 부르는 '캡슐화한 알고리즘'을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴이다.
어떤 아이템을 살 때 결제 방식의 전략만 바꿔서 두 가지 방식(LUNACard, KAKAOCard)으로 결제하는 것을 구현한 코드이다.
interface PaymentStrategy {
public void pay(int amount);
}
class KAKAOCardStrategy implements PaymentStrategy {
private String name;
private String cardNumber;
private String cvv;
private String dateOfExpiry;
public KAKAOCardStrategy(String nm, String ccNum, String cvv, String expiryDate) {
this.name = nm;
this.cardNumber = ccNum;
this.cvv = cvv;
this.date = expiryDate;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using KAKAOCard.");
}
}
class LUNACardStrategy implements PaymentStrategy {
private String emailId;
private String password;
public LUNACardStrategy(String email, String pwd) {
this.emailId = email;
this.password = pwd;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using LUNACard.");
}
}
class Item {
...
}
class ShoppingCart {
...
}
public class Main {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
...
// pay by LUNACard
cart.pay(new LUNACardStrategy("email", "password"));
// pay by KAKAOCard
cart.pay(new KAKAOCardStrategy("name", "cardNum", "cvv", "date"));
}
}
옵저버 패턴
은 주체(객체의 상태 변화를 보고 있는 관찰자)가 어떤 객체의 상태 변화가 있을 때마다 메서드 등을 통해 옵저버(객체의 상태 변화에 따라 전달되는 메서드 등을 기반으로 추가 변화 사항이 생기는 객체)들에게 변화를 알려주는 디자인 패턴이다.
객체와 주체가 분리될 수도 있고, 합쳐질 수도 있다. 합쳐진 옵저버 패턴에서는 주체와 객체를 따로 두지 않고 상태가 변경되는 객체를 기반으로 구축하기도 한다.
interface Subject {
public void register(Observer obj);
public void unregister(Observer obj);
public void notifyObservers();
public Object getUpdate(Observer obj);
}
interface Observer {
public void update();
}
class Topic implements Subject {
private List<Observer> observers;
private String message;
public Topic() {
this.observers = new ArrayList<>();
this.message = "";
}
@Override
public void register(Observer obj) {
if (!observers.contains(obj)) observers.add(obj);
}
@Override
public void unregister(Observer obj) {
observers.remove(obj);
}
@Override
public void notifyObservers() {
this.observers.forEach(Observer::update);
}
@Override
public Object getUpdate(Observer obj) {
return this.message;
}
public void postMessage(String msg) {
System.out.println("Message sended to Topic: " + msg);
this.message = msg;
notifyObservers();
}
}
class TopicSubscriber implements Observer {
private String name;
private Subject topic;
public TopicSubscriber(String name, Subject topic) {
this.name = name;
this.topic = topic;
}
@Override
public void update() {
String msg = (String) topic.getUpdate(this);
System.out.println(name + ":: got message >> " + msg);
}
}
public class Main {
public static void main(String[] args) {
Topic topic = new Topic();
Observer a = new TopicSubscriber("a", topic);
Observer b = new TopicSubscriber("b", topic);
Observer c = new TopicSubscriber("c", topic);
topic.register(a);
topic.register(b);
topic.register(c);
topic.postMessage("amumu is op champion!!");
}
}
/*
Message sended to Topic: amumu is op champion!!
a:: got message >> amumu is op champion!!
b:: got message >> amumu is op champion!!
c:: got message >> amumu is op champion!!
*/
topic(주체이자 객체)을 기반으로 구현한 옵저버 패턴이다.
프록시 패턴
은 대상 객체에 접근하기 전, 그 접근에 대한 흐름을 가로채 해당 접근을 필터링하거나 수정하는 등의 역할을 하는 계층이 있는 디자인 패턴이다.
객체의 속성, 변환 등을 보완하여 보안, 데이터 검증, 캐싱, 로깅에 사용한다.
어떠한 대상의 기본적인 동작의 작업을 가로챌 수 있는 객체이다.
프록시 서버
는 서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램이다.
비동기 이벤트 기반의 구조와 다수의 연결을 효과적으로 처리 가능한 웹서버이다.
전세계적으로 분산된 서버가 있고 이를 통해 어떠한 시스템의 콘텐츠 전달을 빠르게 할 수 있는 CDN 서비스이다.
(CDN - Content Delivery Network: 각 사용자가 인터넷에 접속하는 곳과 가까운 곳에서 콘텐츠를 캐싱 또는 배포하는 서버 네트워크)
웹 서버 앞단에 프록시 서버로 두어 DDOS 공격 방어나 HTTPS 구축에 사용된다.
CORS 에러를 해결하기 위해 프론트엔드에서 프록시 서버를 만들기도 한다.
프론트엔드에서 127.0.0.1:3000으로 테스팅을 하는데, 백엔드 서버는 127.0.0.1:8080이라면 포트 번호가 다르기 때문에 CORS 에러가 발생한다.
이때 프론트엔드 서버 앞단에 프록시 서버를 둬서 오리진을 127.0.0.1:8080으로 바꾸는 것이다.
이터레이터 패턴
은 이터레이터를 사용하여 컬렉션의 요소들에 접근하는 디자인 패턴이다.
이를 통해 자료형의 구조와는 상관 없이 이터레이터라는 하나의 인터페이스로 순회가 가능하다.
노출모듈 패턴
은 즉시 실행 함수를 통해 private, public 같은 접근 제어자를 만드는 패턴이다.
자바스크립트는 private나 public 같은 접근 제어자가 존재하지 않고 전역 범위에서 스크립트가 실행되기 때문에 노출모듈 패턴을 통해 private와 public 접근 제어자를 구현하기도 한다.
MVC 패턴
은 모델, 뷰, 컨트롤러로 이루어진 디자인 패턴이다.
애플리케이션의 구성 요소를 세 가지 역할로 구분하여 개발 프로세스에서 각각의 구성 요소에만 집중해서 개발할 수 있다.
애플리케이션의 데이터인 데이터베이스, 상수, 변수 등을 뜻한다.
사용자 인터페이스 요소를 나타낸다. 즉, 사용자가 볼 수 있는 화면이다.
모델이 가지고 있는 정보를 따로 저장하지 않아야 하며 단순히 화면에 표시하는 정보만 가지고 있어야 한다.
또한, 변경이 일어나면 컨트롤러에 이를 전달해야 한다.
모델과 뷰를 잇는 다리 역할을 하며, 이벤트 등 메인 로직을 담당한다.
또한, 모델과 뷰의 생명주기도 관리하며, 모델이나 뷰의 변경 통지를 받으면 이를 해석하여 각각의 구성 요소에 해당 내용에 대해 알려준다.
스프링: Spring의 Web MVC는 웹서비스를 구축하는 데 편리한 기능을 제공한다. @RequestParam, @RequestHeader, @PathVariable 등의 애너테이션을 기반으로 사용자의 요청값을 쉽게 분석할 수 있고, 그것이 유효한지 알 수 있다.
MVP 패턴
은 MVC 패턴으로부터 파생되었으며 P는 프레젠터이다.
모델이 뷰를 직접 컨트롤할 수 없다.
뷰와 프레젠터는 일대일 관계이기 때문에 MVC 패턴보다 더 강한 결합을 가진다.
MVVM 패턴
은 MVC의 C가 뷰모델로 바뀐 패턴이다.
뷰모델은 뷰를 더 추상화한 계층이다.
MVVM 패턴은 MVC 패턴과 다르게 커맨드(여러 요소에 대한 처리를 하나의 액션으로 처리할 수 있게 하는 기법)와 데이터 바인딩(화면에 보이는 데이터와 웹 브라우저의 메모리 데이터를 일치시키는 기법)을 가진다.
뷰와 뷰모델 사이의 양방향 데이터 바인딩을 지원하며 UI를 별도의 코드 수정 없이 재사용할 수 있고 단위 테스팅하기 쉽다.
Vue.js: 반응형이 특징인 프론트엔드 프레임워크로, 함수를 사용하지 않고 값 대입만으로 변수가 변경되며 양방향 바인딩, html을 토대로 컴포넌트를 구축할 수 있다.
어댑터 패턴
은 어댑터를 사용하여 호환되지 않는 인터페이스를 호환되도록 하는 패턴이다.
향후 인터페이스가 변경되더라도, 변경된 내용이 어댑터 안에 캡슐화되므로 수정할 필요가 없다.
상속을 통해 부모 클래스의 기능을 확장할 때 사용하는 패턴이다.
부모 클래스에서 변하지 않는 기능을 구현해두고 자식 클래스에서 확장할 기능을 구현한다.