
새 책으로 새 스터디를 시작했다.
이번 1회차에서는 <1장> 디자인 패턴과 프로그래밍 패러다임에 대해 공부해보자.
디자인 패턴이란 프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 '규약' 형태로 만들어 놓은 것을 의미한다.
문제를 잘 해결하려고 만들어놓은 코딩 설계 공식이라고도 할 수 있다.
패턴들을 하나씩 알아보자.
싱글톤 패턴은 하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다.
하나의 클래스를 기반으로 여러 개의 개별적인 인스턴스를 만들 수 있지만, 그렇게 하지 않고 하나의 클래스를 기반으로 단 하나의 인스턴스를 만들어 이를 기반으로 로직을 만드는 데 쓰이며, 보통 데이터베이스 연결 모듈에 많이 사용한다.
자바스크립트에서는 리터럴 {} 또는 new Object로 객체를 생성하게 되면 다른 어떤 객체와도 같지 않기 때문에 이 자체만으로 싱글톤 패턴을 구현할 수 있다.
const obj = {
a: 27
}
const obj2 = {
a: 27
}
console.log(obj === obj2)
// false
코드에서 볼 수 있듯이 obj와 obj2는 다른 인스턴스를 가진다.
이 또한 new Object라는 클래스에서 나온 단 하나의 인스턴스니 어느정도 싱글톤 패턴이라 볼 수 있지만, 실제 싱글톤 패턴은 보통 다음과 같다.
class Singleton{
constructor() {
if (!Singleton.instance) {
Singleton.instance = this
}
return Singleton.instance
}
getInstance() {
return this
}
}
const a = new Singleton()
const b = new Singleton()
consol.log(a === b) // true
이 코드를 파이썬에 적용시켜 보자면,
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
a = Singleton()
b = Singleton()
print(a is b) # True
이렇게 바꿀 수 있다.
파이썬은 문법이 간단하고 자바처럼 무조건 클래스로 감싸야 하는 언어가 아니기 때문에 많이 쓰지는 않지만 쓰기는 한다.
싱글톤 패턴 같은 경우네는 설정 클래스나 DB 연결 같은 곳에서 자주 쓴다.
싱글톤 패턴 자체가 데이터베이스 연결 모듈에 많이 쓰이는데, 자바스크립트 코드부터 살펴보자.
const URL = 'mongodb://localhost:27017//hiriceapp'
const createConnection = url => ({"url" : url})
class DB {
constructor(url) {
if (!DB.instance) {
DB.instance = createConnection(url)
}
return DB.instance
}
connect() {
return this.instance
}
}
const a = new DB(URL)
const b = new DB(URL)
console.log(a === b) // true
이 코드도 파이썬 코드로 바꿔보자.
URL = 'mongodb://localhost:27017//hiriceapp'
# DB 연결을 만드는 함수
def create_connection(url):
return {'url': url}
class DB:
_instance = None
def __new__(cls, url):
if cls._instance is None:
cls._instance = create_connection(url)
return cls._instance
def connect(self):
return self._instance
a = DB(URL)
b = DB(URL)
print(a is b) # True
자바스크립트 예시 코드와 비교해 바꿔보았지만, 실제 쓰는 것과는 조금 차이가 있다.
실제로는 pymongo를 이용해 연결을 만들고,
이미 __new__에서 연결을 리턴하기 때문에 굳이 한 번더 연결하는 메서드를 쓸 필요는 없다.
좀 더 실제 싱글톤 예제를 보자면 이렇게 바꿀 수 있다.
from pymongo import MongoClient
class MongoDBConnection:
_instance = None
def __new__(cls, url):
if cls._instance is None:
print("[MongoDB] 새로운 연결 생성 중...")
cls._instance = super().__new__(cls)
cls._instance.client = MongoClient(url)
cls._instance.db = cls._instance.client.get_database()
return cls._instance
def get_db(self):
return self.db
URL = 'mongodb://localhost:27017/hiriceapp'
a = MongoDBConnection(URL)
b = MongoDBConnection(URL)
print(a is b) # True, 같은 객체
print(a.get_db() is b.get_db()) # True, 같은 DB 연결
MySQL에서 쓰는 싱글톤 패턴도 알아보자.
import pymysql
class MySQLConnection:
_instance = None
def __new__(cls, host, user, password, database):
if cls._instance is None:
print("[MySQL] 새로운 연결 생성 중...")
cls._instance = super().__new__(cls)
cls._instance.connection = pymysql.connect(
host=host,
user=user,
password=password,
database=database,
cursorclass=pymysql.cursors.DictCursor # 결과를 딕셔너리 형태로
)
return cls._instance
def get_connection(self):
return self.connection
# 예제 사용
a = MySQLConnection('localhost', 'root', '1234', 'mydb')
b = MySQLConnection('localhost', 'root', '1234', 'mydb')
print(a is b) # True
print(a.get_connection() is b.get_connection()) # True
싱글톤 패턴은 TDD를 할 때 걸림돌이 된다.
TDD는 Test Driven Development로, 테스트 주도 개발이다.
코드를 짜기 전에 먼저 테스트부터 만드는 개발 방법이라고 할 수 있다.
TDD를 할 때 단위 테스트를 주로 하는데, 단위 테스트는 테스트가 서로 독립적이어야 하며 테스트를 어느 순서로든 설명할 수 있어야 한다.
하지만 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로 각 테스트마다 독립적인 인스턴스를 만들기가 어렵다.
또, 싱글톤 패턴은 모듈 간의 결합을 강하게 만들 수 있다는 단점이 있다.
이때 의존성 주입(DI, Dependency Injection)을 통해 모듈 간의 결합을 조금 더 느슨하게 만들어 해결할 수 있다.
의존성이란 종속성이라고도 하며 A가 B에 의존성이 있다는 것은 B의 변경 사항에 대해 A 또한 변해야 된다는 것을 의미한다.

그림처럼 메인 모듈이 직접 다른 하위 모듈에 대한 의존성을 주기보다는 중간에 의존성 주입자가 이 부분을 가로채 메인 모듈이 간접적으로 의존성을 주입하는 방식이다.
이를 통해 메인 모듈(상위 모듈)은 하위 모듈에 대한 의존성이 떨어지게 된다.
이를 디커플링이 된다고도 한다.
팩토리 패턴은 객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴이다.
쉽게 말해서 객체를 직접 만들지 않고, 공장에게 대신 만들어 달라고 시키는 방식이다.
자바스크립트에서 팩토리 패턴을 구현한다면 간단하게 new Object()로 구현할 수 있다.
const num = new Object(42)
const str = new Object('abc')
num.constructor.name; // Number
str.constructor.name; // String
숫자를 전달하거나 문자열을 전달함에 따라 다른 타입의 객체를 생성하는 것을 볼 수 있다.
전달받은 값에 따라 다른 객체를 생성하며 인스턴스의 타입 등을 정한다.
커피 팩토리를 기반으로 라떼 등을 생산하는 코드를 구축해보자.
class CoffeeFactory {
static createCoffee(type){
const factory = factoryList[type]
return factory.createCoffee()
}
}
class Latte {
constructor() {
this.name = "latte"
}
}
class Espresso {
constructor() {
this.name = "Espresso"
}
}
class LatteFactory extands CoffeFactory{
static createCoffee() {
return new Latte()
}
}
class EspressoFactory extends CoffeFactory{
static createCoffee() {
return new Espresso()
}
}
const factoryList = { LatteFactory, EspressoFactory }
const main = () => {
// 라떼 커피를 주문한다
const coffee = CoffeeFactory.createCoffee("LatteFactory")
// 커피 이름을 부른다
console.log(coffee.name) // latte
}
main()
CoffeeFactory라는 상위 클래스가 중요한 뼈대를 결정하고 하위 클래스인 LatteFactory가 구체적인 내용을 결정하고 있다.
참고로 이는 의존성 주입이라고도 볼 수 있다.
또, CoffeeFactory 클래스를 보면 static 키워드를 통해 createCoffee() 메서드를 정적 메서드로 선언한 것을 볼 수 있는데, 이렇게 정적 메서드로 정의하면 클래스를 기반으로 객체를 만들지 않고 호출이 가능하며, 해당 메서드에 대한 메모리 할당을 한 번만 할 수 있는 장점이 있다.
이 코드를 파이썬으로 바꿔보자.
class Coffee:
def __init__(self, name):
self.name = name
class Latte(Coffee):
def __init__(self):
super().__init__("latte")
class Espresso(Coffee):
def __init__(self):
super().__init__("espresso")
class CoffeeFactory:
@staticmethod
def create_coffee(type_):
factory = factory_list[type_]
return factory.create_coffee()
class LatteFactory:
@staticmethod
def create_coffee():
return Latte()
class EspressoFactory:
@staticmethod
def create_coffee():
return Espresso()
# 공장 리스트 (문자열로 타입을 넘기면 대응되는 팩토리 클래스 반환)
factory_list = {
"LatteFactory": LatteFactory,
"EspressoFactory": EspressoFactory
}
def main():
# 라떼 커피 주문
coffee = CoffeeFactory.create_coffee("LatteFactory")
# 커피 이름 출력
print(coffee.name) # latte
main()
파이썬에서는 클래스 상속 없이 팩토리 클래스만 만들어서 직접 호출하는 식으로 구현해준다.
create_coffee는 각 공장에서 만든 객체만 리턴하도록 만들어준다.
메인 로직은 main() 함수로 분리해서 깔끔하게 실행한다.
상수의 집합을 정의할 때 사용되는 타입이다. 미리 정해진 값들의 모임이라고 할 수 있다.
예를 들어 월, 일, 색상 등의 상수 값 같은 것을 이름 붙여서 사용할 수 있게 해주는 것.
자바에서는 Enum이 다른 언어보다 더 활발히 활용되며, 상수뿐만 아니라 메서드를 집어넣어 관리할 수도 있다.
자바에서는 Enum을 진짜 객체처럼 다루기 때문이다.
파이썬에서는 enum 모듈을 통해 Enum을 사용할 수 있다.
간단한 프로그램이나 스크립트에서는 잘 사용하지 않지만,
상태값, 선택지, 고정된 값 목록이 필요할 땐 Enum을 쓰는게 좋다.
전략 패턴은 정책 패턴(policy pattern)이라고도 하며, 객체의 행위를 바꾸고 싶은 경우 직접 수정하지 않고 전략이라고 부르는 캡슐화한 알고리즘을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴이다.
어떤 행동을 바꾸고 싶을 때 기존 코드를 고치지 않고
행동만 갈아끼울 수 있도록 만드는 설계 방법이다.
파이썬 예시 코드를 살펴보자.
쇼핑 카트에 아이템을 담아 LUNACard 또는 KAKAOCard 라는 두 개의 전략으로 결제하는 코드이다.
from abc import ABC, abstractmethod
# 전략 인터페이스
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount):
pass
# 카카오 카드 결제 전략
class KAKAOPaymentStrategy(PaymentStrategy):
def __init__(self, name, card_number, cvv, expiry_date):
self.name = name
self.card_number = card_number
self.cvv = cvv
self.expiry_date = expiry_date
def pay(self, amount):
print(f"{amount} paid using KAKAOCARD.")
# 루나 카드 결제 전략
class LUNAPaymentStrategy(PaymentStrategy):
def __init__(self, email, password):
self.email = email
self.password = password
def pay(self, amount):
print(f"{amount} paid using LUNACARD.")
# 아이템 클래스
class Item:
def __init__(self, name, price):
self.name = name
self.price = price
def get_name(self):
return self.name
def get_price(self):
return self.price
# 쇼핑카트 클래스
class ShoppingCart:
def __init__(self):
self.items = []
def add_item(self, item):
self.items.append(item)
def remove_item(self, item):
self.items.remove(item)
def calculate_total(self):
return sum(item.get_price() for item in self.items)
def pay(self, payment_method: PaymentStrategy):
amount = self.calculate_total()
payment_method.pay(amount)
# 메인 실행
def main():
cart = ShoppingCart()
item_a = Item("kundolA", 100)
item_b = Item("kundolB", 300)
cart.add_item(item_a)
cart.add_item(item_b)
# LUNA 카드로 결제
cart.pay(LUNAPaymentStrategy("kundol@example.com", "pukubababo"))
# KAKAO 카드로 결제
cart.pay(KAKAOPaymentStrategy("Ju hongchul", "123456789", "123", "12/01"))
main()
모든 결제 전략은 pay(amount)라는 메서드를 가져야 한다.
이걸 지키게 함으로써 동작을 통일시켜주는 틀을 제공하는 것.
실제 결제 방식들. 각자 pay() 메서드 안에 다른 방식으로 동작하도록 정의돼 있다. 이 부분이 “전략을 캡슐화”한 부분이다.
여기서 중요한 건 ShoppingCart 클래스는 어떻게 결제되는지 모른다는 것이다.
그냥 pay()를 부르고 넘겨진 전략 객체 (KAKAOPaymentStrategy, LUNAPaymentStrategy)한테 일을 맡긴다.
즉, 전략을 바꿔 끼우기만 하면 동작이 바뀌는 구조이다.
옵저버 패턴은 주체가 어떤 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서듣 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴이다.
유튜버가 영상을 업로드 하면 구독자들한테 알림이 가게 된다.
유튜버는 주체, 구독자들은 옵저버이다.
또, 옵저버 패턴은 주로 이벤트 기반 시스템에 사용하며 MVC(Model-View-Controller) 패턴에도 사용된다.
MVC 구조에서도 모델의 상태가 바뀌면 뷰가 자동으로 그걸 감지하고 화면을 바꿔주는 구조에 사용된다.

책에 예시를 들어놓은 자바에서의 옵저버 패턴
import java.util.ArrayList;
import java.util.List;
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 HelloWorld {
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!!
*/
이 코드를 파이썬으로 바꿔보자.
from abc import ABC, abstractmethod
# 옵저버 인터페이스
class Observer(ABC):
@abstractmethod
def update(self):
pass
# 주체 인터페이스
class Subject(ABC):
@abstractmethod
def register(self, observer: Observer):
pass
@abstractmethod
def unregister(self, observer: Observer):
pass
@abstractmethod
def notify_observers(self):
pass
@abstractmethod
def get_update(self, observer: Observer):
pass
# 주체 구현: Topic
class Topic(Subject):
def __init__(self):
self.observers = []
self.message = ""
def register(self, observer):
if observer not in self.observers:
self.observers.append(observer)
def unregister(self, observer):
self.observers.remove(observer)
def notify_observers(self):
for observer in self.observers:
observer.update()
def get_update(self, observer):
return self.message
def post_message(self, msg):
print(f"Message sent to Topic: {msg}")
self.message = msg
self.notify_observers()
# 옵저버 구현: TopicSubscriber
class TopicSubscriber(Observer):
def __init__(self, name, topic: Subject):
self.name = name
self.topic = topic
def update(self):
msg = self.topic.get_update(self)
print(f"{self.name}:: got message >> {msg}")
# 테스트 실행
def main():
topic = Topic()
a = TopicSubscriber("a", topic)
b = TopicSubscriber("b", topic)
c = TopicSubscriber("c", topic)
topic.register(a)
topic.register(b)
topic.register(c)
topic.post_message("amumu is op champion!!")
main()
/*
Message sent 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을 기반으로 옵저버 패턴을 구현했다.
class Topic(Subject)을 주체이자 객체로, Topic.post_message()를 메시지 게시 + 자동 알림, TopicSubscriber → 옵저버 역할 (구독자), register(), unregister(), notify_observers() 다 동일하게 구현해서 변경했다.
자바: 상속과 구현
상속(extends)
상속은 자식 클래스가 부모 클래스의 메서드 등을 상속받아 사용하며 자식 클래스에서 추가 및 확장을 할 수 있는 것을 말한다.
이로 인해 재사용성, 중복성의 최소화가 이루어진다.
구현(implements)
구현은 부모 인터페이스를 자식 클래스에서 재정의하여 구현하는 것을 말하며, 상속과는 달리 반드시 부모 클래스의 메서드를 재정의하여 구현해야 한다.
상속과 구현의 차이
상속은 일반 클래스, abstract 클래스를 기반으로 구현하며, 구현은 인터페이스를 기반으로 구현한다.
파이썬 : 상속과 구현
파이썬에서는 구현이 따로 존재하지 않고, 추상 클래스(ABC) 상속 + @abstractmethod 구현이 그 역할을 한다. 인터페이스와 추상 클래스의 구분이 없었다.
자바는 interface vs abstract class 구분한다.
파이썬은 둘 다 ABC로 처리함 (하나로 통일)
엄격하지 않음 → 유연한 구조 인터페이스처럼 메서드 정의 안 해도 오류 안 나는 경우 있음 (주의해야 함)
| 개념 | 자바 | 파이썬 |
|---|---|---|
| 상속 | extends | class Sub(Super) |
| 구현 | implements interface | class Sub(ABC) + @abstractmethod |
| 추상 클래스 | abstract class | class Sub(ABC) |
| 다중 상속 | interface만 가능 | 클래스/인터페이스 모두 가능 |
| 메서드 구현 강제 | interface는 필수 | @abstractmethod만 필수 |
| 접근 제어자 | public, private 등 명확함 | 암묵적(private 없음, _로 표시만) |
자바 예시를 기반으로 파이썬 코드를 비교해봤지만, 사실 파이썬 코드는 좀 더 간결하다.
파이썬 옵저버 패턴의 예시를 보자.
class Topic:
def __init__(self):
self.subscribers = []
self.message = None
def register(self, callback):
if callback not in self.subscribers:
self.subscribers.append(callback)
def unregister(self, callback):
if callback in self.subscribers:
self.subscribers.remove(callback)
def notify(self):
for subscriber in self.subscribers:
subscriber(self.message)
def post_message(self, message):
print(f"Message sent to Topic: {message}")
self.message = message
self.notify()
# 구독자 함수들
def subscriber_a(message):
print(f"a:: got message >> {message}")
def subscriber_b(message):
print(f"b:: got message >> {message}")
def subscriber_c(message):
print(f"c:: got message >> {message}")
# 사용 예
def main():
topic = Topic()
# 구독자 등록
topic.register(subscriber_a)
topic.register(subscriber_b)
topic.register(subscriber_c)
# 메시지 게시 → 알림 전파
topic.post_message("amumu is op champion!!")
# 구독자 제거 후 다시 메시지 전파
topic.unregister(subscriber_b)
topic.post_message("riven got nerfed")
main()
프록시 객체는 어떠한 대상의 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 작업을 가로챌 수 있는 객체를 뜻하며, 자바스크립트에서 프록시 객체는 두개의 매개변수를 가진다.
자바스크립트의 프록시 객체를 통해 옵저버 패턴을 구현한 예시를 살펴보자.
function createReactiveObject(target, callback) {
const proxy = new Proxy(target, {
set(obj, prop, value){
if(value !== obj[prop]){
const prev = obj[prop]
obj[prop] = value
callback(`${prop}가 [${prev}] >> [${value}] 로 변경되었습니다`)
}
return true
}
})
return proxy
}
const a = {
"형규" : "솔로"
}
const b = createReactiveObject(a, console.log)
b.형규 = "솔로"
b.형규 = "커플"
// 형규가 [솔로] >> [커플] 로 변경되었습니다
프록시 객체의 get() 함수는 속성과 함수에 대한 접근을 가로채며, has() 함수는 in 연산자의 사용을 가로챈다. set() 함수는 속성에 대한 접근을 가로챈다. set() 함수를 통해 속성에 대한 접근을 가로채서 형규라는 속성이 솔로에서 커를로 되는 것을 감시할 수 있다.
파이썬도 프록시처럼 행동하는 객체를 만들 수 있다.
정확히는 내장 메서드(getattr, setattr, getitem, setitem) 등을 오버라이딩해서 프록시처럼 동작하게 만드는 구조가 가능하다.
class ReactiveDict:
def __init__(self, target, callback):
self._data = target
self._callback = callback
def __setitem__(self, key, value):
old = self._data.get(key)
if old != value:
self._data[key] = value
self._callback(f"{key}가 [{old}] >> [{value}] 로 변경되었습니다")
def __getitem__(self, key):
return self._data[key]
def __repr__(self):
return repr(self._data)
# 사용 예
def logger(message):
print(message)
a = {"형규": "솔로"}
b = ReactiveDict(a, logger)
b["형규"] = "솔로" # 변경 없음, 출력 안 됨
b["형규"] = "커플" # 형규가 [솔로] >> [커플] 로 변경되었습니다
파이썬에서는 프록시를 대신해 getitem, setitem로 속성 접근을 감시한다.
파이썬은 동적 속성 접근이 복잡하기 때문에 키 접근 방식을 사용해 b["형규"]로 바꿔준다.
프록시 객체는 기자인 패턴 중 하나인 프록시 패턴이 녹아들어 있는 객체이다.
프록시 패턴은 대상 객체에 접근하기 전 그 접근에 대한 흐름을 가로채 해당 접근을 필터링 하거나 수정하는 등의 역할을 하는 계층이 있는 디자인 패턴이다.

이를 통해 객체의 속성, 변환 등을 보완하며 보안, 데이터 검증, 캐싱, 로깅에 사용한다. 이는 앞서 공부했던 프록시 객체로 쓰이기도 하지만 프록시 서버로도 활용된다.
캐시 안에 정보를 담아두고, 캐시 안에 있는 정보를 요구하는 요청에 대해 다시 저 멀리 있는 원격 서버에 요청하지 않고 캐시 안에 있는 데이터를 활용하는 것을 말한다. 이를 통해 불필요하게 외부와 연결하지 않기 때문에 트래픽을 줄일 수 있다는 장점이 있다.
프록시 서버는 서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램을 말한다.
nginx는 비동기 이벤트 기반의 구조와 다수의 연결을 효과적으로 처리 가능한 웹 서버이며, 주로 Node.js 서버 앞단의 프록시 서버로 활용된다.

CloudFlare는 전 세계적으로 분산된 서버가 있고 이를 통해 어떠한 시스템의 콘텐츠 전달을 빠르게 할 수 있는 CDN 서비스다.
웹 서버 앞단에 프록시 서버로 두어 DDOS 공격 방어나 HTTPS 구축에 쓰인다.

DDOS는 짧은 기간 동안 네트워크에 많은 요청을 보내 네트워크를 마비시켜 웹 사이트의 가용성을 방해하는 사이버 공격 유형이다. CloudFlare는 의심스러운 트래픽, 특히 사용자가 접속하는 것이 아닌 시스템을 통해 오는 트래픽을 자동으로 차단해서 DDOS 공격으로부터 보호한다.
서버에서 HTTPS를 구축할 때 인증서를 기반으로 구축할 수도 있다. 하지만, CloudFlare를 사용하면 별도의 인증서 설치 없이 좀 더 손쉽게 HTTPS를 구축할 수 있다.
CORS는 서버가 웹 브라우저에서 리소스를 로드할 때 다른 오리진을 통해 로드하지 못하게 하는 HTTP 헤더 기반 메커니즘이다.

프런트엔드 개발 시 프런트엔드 서버를 만들어서 백엔드 서버와 통신할 때 주로 CORS 에러를 마주치는데, 이를 해결하기 위해 프런트엔드에서 프록시 서버를 만들기도 한다.
예를 들어 프론트엔드에서는 127.0.0.1:3000으로 테스팅을 하는데 백엔드 서버는 127.0.0.1:2010이라면 포트 번호가 다르기 때문에 CORS 에러가 난다. 이때, 프록시 서버를 둬서 프론트엔드 서버에서 요청되는 오리진을 127.0.0.1:2010으로 바꾸는 것이다.

그림처럼 프런트엔드 서버 앞단에 프록시 서버를 놓아 /api 요청은 users API, /api2 요청은 users API2에 요청할 수 있다. 자연스럽게 CORS 에러 해결은 물론 다양한 API 서버와의 통신도 매끄럽게 할 수 있다.
이터레이터 패턴은 이터레이터를 사용하여 컬렉션의 요소들에 접근하는 디자인 패턴이다. 이를 통해 순회할 수 있는 여러 가지 자료형의 구조와는 상관없이 이터레이터라는 하나의 인터페이스로 순회가 가능하다.
const mp = new Map()
mp.set('a', 1)
mp.set('b', 2)
mp.set('cccc', 3)
const st = new Set()
st.add(1)
st.add(2)
st.add(3)
const a = []
for(let i = 0; i < 10; i++)a.push(i)
for(let aa of a) console.log(aa)
for(let a of mp) console.log(a)
for(let a of st) console.log(a)
/*
a, b, c
[ 'a', 1 ]
[ 'b', 2 ]
[ 'c', 3 ]
1
2
3
*/
자바스크립트에서의 이터레이터 패턴이다.
분명히 다른 자료 구조인 set과 map임에도 똑같은 for a of b라는 이터레이터 프로토콜을 통해 순회하는 것을 볼 수 있다.
이 코드를 파이썬 코드로 변경한다면
# 리스트
a = [i for i in range(10)]
# 딕셔너리 (Map)
mp = {
'a': 1,
'b': 2,
'cccc': 3
}
# 집합 (Set)
st = {1, 2, 3}
# 리스트 순회
for aa in a:
print(aa)
# 딕셔너리 순회 (key-value 쌍)
for pair in mp.items():
print(pair)
# 셋 순회
for item in st:
print(item)
노출모듈 패턴은 즉시 실행 함수를 통해 private, public과 같은 접근 제어자를 만드는 패턴을 말한다. 자바스크립트는 prinvate나 public 같은 접근 제어자가 존재하지 않고 전역 범위에서 스크립트가 실행된다. 그렇기 때문에 노출모듈 패턴을 통해 private와 public 접근 제어자를 구현하기도 한다.
파이썬은 노출 모듈 패턴이 필요 없는 구조다. 대신 모듈, 클래스, 그리고 이름 규칙(_, __)으로 private/public 같은 접근 제어를 자연스럽게 관리할 수 있다.
MVC 패턴은 모델, 뷰, 컨트롤러로 이루어진 디자인 패턴이다.

애플리케이션의 구성 요소를 세 가지 역할로 구분하여 개발 프로세스에서 각각의 구성 요소에만 집중해서 개발할 수 있다. 재사용성과 확장성이 용이하다는 장점이 있고, 애플리케이션이 복잡해질수록 모델과 뷰의 관계가 복잡해지는 단점이 있다.
모델은 애플리케이션의 데이터인 데이터베이스, 상수, 변수 등을 뜻한다.
예를 들어 사각형 모양의 박스 안에 글자가 들어 있다면 그 사각형 모양의 박스 위치 정보, 글자 내용, 글자 위치, 글자 포맷(utf-8 등)에 관한 정보를 모두 가지고 있어야 한다. 뷰에서 데이터를 생성하거나 수정하면 컨트롤러를 통해 모델을 생성하거나 갱신한다.
뷰는 inputbox, chectbox, textarea 등 사용자 인터페이스 요소를 나타낸다. 모델을 기반으로 사용자가 볼 수 있는 화면을 뜻한다. 모델이 가지고 있는 정보를 따로 저장하지 않아야 하며 단순히 사격형 모양 등 화면에 표시하는 정보만 가지고 있어야 한다. 또한, 변경이 일어나면 컨트롤러에 이를 전달해야 한다.
컨트롤러는 하나 이상의 모델과 하나 이상의 뷰를 잇는 다리 역할을 하며 이벤트 등 메인 로직을 담당한다. 또한, 모델과 뷰의 생명주기도 관리하며, 모델이나 뷰의 변경 통지를 받으면 이를 해석하여 각각의 구성 요소에 해당 내용에 대해 알려준다.
MVC 패턴을 이용한 대표적인 프레임워크로는 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크인 스프링이 있다.
스프링의 WEB MVC는 웹 서비스를 구축하는데 편리한 기능들을 많이 제공한다. @RequestRaram, @RequestHeader, @PathVariaable 등의 애너테이션을 기반으로 사용자의 요청 값들을 쉽게 분석할 수 있으며 사용자의 어떠한 요청이 유효한 요청인지를 쉽게 거를 수 있다.
MVP 패턴은 MVC 패턴으로부터 파생되었으며 MVC에서 C에 해당하는 컨트롤러가 프레젠터로 교체된 패턴이다.

뷰와 프레젠터는 일대일 관계이기 때문에 MVC 패턴보다 더 강한 결합을 지닌 디자인 패턴이라고 볼 수 있다.
MVVM 패턴은 MVC의 C에 해당하는 컨트롤러가 뷰모델로 바뀐 패턴이다.

여기서 뷰모델은 뷰를 더 추상화한 계층이며, MVVM 패턴은 MVC 패턴과는 다르게 커맨드와 데이터 바인딩을 가지는 것이 특징이다. 뷰와 뷰모델 사이의 양방향 데이터 바인딩을 지원하며 UI를 별도의 코드 수정 없이 재사용할 수 있고 단위 테스팅하기 쉽다는 장점이 있다.
MVVM 패턴을 가진 대표적인 프레임워크로는 뷰가 있다. 뷰는 반응형이 특징인 프런트엔드 프레임워크다. 예를 들어 watch와 computed등으로 쉽게 반응형적인 값들을 구축할 수 있다.
함수를 사용하지 않고 값 대입만으로도 변수가 변경되며 양방향 바인딩, html을 토대로 컴퍼넌트를 구출할 수 있다는 점이 특징이다.
1.1 디자인 패턴은 여기까지 정리하겠다. 🤟🤟