우리가 어떤 것을 살 때 네이버페이, 카카오페이 등 다양한 방법으로 결제하듯이, 어떤 아이템을 살 때 LUNACard로 사는 것과 KAKAOCard로 사는 것을 구현한 예제이다. 결제 방식의 ‘전략’만 바꿔서 두 가지 방식으로 결제하는 것을 구현
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
// PaymentStrategy는 전략 인터페이스로, 다양한 결제 방법을 제공하기 위한 공통 계약을 정의
// 각 결제 방식은 이 인터페이스를 구현하면서 서로 다른 결제 로직을 제공함
interface PaymentStrategy {
public void pay(int amount);
}
class KAKAOCardStrategy implements PaymentStrategy {
// KAKAOCard를 사용하는 결제 전략
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.dateOfExpiry=expiryDate;
}
@Override
public void pay(int amount) {
System.out.println(amount +" paid using KAKAOCard.");
}
}
class LUNACardStrategy implements PaymentStrategy {
// LUNACard를 사용하는 결제 전략
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 {
private String name;
private int price;
public Item(String name, int cost){
this.name=name;
this.price=cost;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
class ShoppingCart {
List<Item> items;
public ShoppingCart(){
this.items=new ArrayList<Item>();
}
public void addItem(Item item){
this.items.add(item);
}
public void removeItem(Item item){
this.items.remove(item);
}
public int calculateTotal(){
int sum = 0;
for(Item item : items){
sum += item.getPrice();
}
return sum;
}
public void pay(PaymentStrategy paymentMethod){
int amount = calculateTotal();
paymentMethod.pay(amount);
}
}
public class HelloWorld{
public static void main(String []args){
ShoppingCart cart = new ShoppingCart();
Item A = new Item("kundolA",100);
Item B = new Item("kundolB",300);
cart.addItem(A);
cart.addItem(B);
// pay by LUNACard
cart.pay(new LUNACardStrategy("kundol@example.com", "pukubababo"));
// pay by KAKAOBank
cart.pay(new KAKAOCardStrategy("Ju hongchul", "123456789", "123", "12/01"));
}
}
/*
400 paid using LUNACard.
400 paid using KAKAOCard.
*/
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.dateOfExpiry=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.");
}
}
KAKAOCardStrategy와 LUNACardStrategy는 각각 결제 방식에 대한 구체적인 전략(알고리즘)을 제공PaymentStrategy 인터페이스를 구현하면서 자신만의 결제 로직을 가지고 있음.class ShoppingCart {
List<Item> items;
public ShoppingCart(){
this.items=new ArrayList<Item>();
}
public void addItem(Item item){
this.items.add(item);
}
public void removeItem(Item item){
this.items.remove(item);
}
public int calculateTotal(){
int sum = 0;
for(Item item : items){
sum += item.getPrice();
}
return sum;
}
public void pay(PaymentStrategy paymentMethod){
int amount = calculateTotal();
paymentMethod.pay(amount);
}
}
ShoppingCart는 전략을 사용하는 컨텍스트 클래스pay 메서드 를 통해 외부에서 주입된 전략을 사용해 결제를 처리주체가 어떤 객체(subject)의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴
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!!
*/
Topic 클래스는 주체로서, 상태(message)가 변경되면 notifyObservers() 메서드를 통해 등록된 옵저버들에게 변경을 알린다.register()와 unregister() 메서드는 옵저버를 주체에 추가하거나 제거하는 메서드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);
}
}
TopicSubscriber 클래스는 옵저버로서, 주체인 Topic의 상태가 변경될 때 update() 메서드를 통해 상태 변화를 알림 받는다.topic.getUpdate(this)로 주체의 상태를 확인한 후, 그 정보를 사용해 필요한 동작을 수행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!!"); // 상태 변화 및 알림
}
}
Topic)가 상태를 업데이트(postMessage())하면 등록된 모든 옵저버(a, b, c)에게 알림이 가고, 각 옵저버는 상태 변화를 반영해 메시지를 출력프록시 패턴(proxy pattern) 은 대상 객체(subject)에 접근하기 전 그 접근에 대한 흐름을 가로채 해당 접근을 필터링하거나 수정하는 등의 역할을 하는 계층이 있는 디자인 패턴
프록시 서버에서의 캐싱
프록시 서버(proxy server)는 서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램을 가리킴
nginx는 비동기 이벤트 기반의 구조와 다수의 연결을 효과적으로 처리 가능한 웹 서버
주로 Node.js 서버 앞단의 프록시 서버로 활용됨
Node.js 창시자 라이언 달(Ryan Dahl)은 다음과 같이 말했다.
“Node.js의 버퍼 오버플로우 취약점을 예방하기 위해서는 nginx를 프록시 서버로 앞단에 놓고, Node.js를 뒤쪽에 놓는 것이 좋다.”

gzip 압축하거나, 메인 서버 앞단에서의 로깅을 할 수 있음버퍼 오버플로우란?
gzip 압축
gzip 압축을 하면 데이터 전송량을 줄일 수 있지만, 압축을 해제했을 때 서버에서의 CPU 오버헤드도 생각해서 gzip 압축 사용 유무를 결정해야 함CloudFlare는 전 세계적으로 분산된 서버가 있고, 이를 통해 어떠한 시스템의 콘텐츠 전달을 빠르게 할 수 있는 CDN 서비스
💡CDN(Content Delivery Network)
각 사용자가 인터넷에 접속하는 곳과 가까운 곳에서 콘텐츠를 캐싱 또는 배포하는 서버 네트워크를 말함
이를 통해 사용자가 웹 서버로부터 콘텐츠를 다운로드하는 시간을 줄일 수 있다
CloudFlare는 웹 서버 앞단에 프록시 서버로 두어 DDOS 공격 방어나 HTTPS 구축에 쓰임
또한, 서비스를 배포한 이후에 해외에서 무언가 의심스러운 트래픽이 발생하면 이 때문에 많은 클라우드 서비스 비용이 발생할 수도 있는데, 이 때 CloudFlare가 의심스러운 트래픽인지를 먼저 판단해 CAPTCHA 등을 기반으로 이를 일정 부분 막아주는 역할도 함
위 그림처럼 사용자, 크롤러, 공격자가 자신의 웹 사이트에 접속하게 될 텐데, 이 때 CloudFlare를 통해 공격자로부터 보호할 수 있음
DDOS 공격 방어
HTTPS 구축

프록시 서버 이용 전
CORS?
오리진?

프록시 서버 이용 후
위 그림처럼, 프론트 서버 앞단에 프록시 서버를 놓아 /api 요청은 users API, /api2 요청은 users API2 에 요청할 수 있음.
127.0.0.1:3000 으로 테스팅 하는데, BE 서버가 127.0.0.1:12010 라면 포트 번호가 다르기 때문에 CORS 에러가 발생127.0.0.1:12010 으로 바꿈127.0.0.1이란?