디자인패턴: 4. 전략 패턴 (Strategy Pattern)
다른 말로는 정책 패턴이라고도 하며 알고리즘 부분을 캡슐화하여 교체가 용이하게 만드는 패턴이다. 예를 들어 네이버페이, 카카오페이 등 다양한 방법으로 결제가 가능한 기능을 구현할 경우 결제 방식의 '전략'만 바꿔서 두 가지 방식으로 결제하도록 하는 패턴이다.
먼저 이해를 돕기 위해 전략패턴을 사용하지 않는 결제 시스템을 만들어보자.
function payment (type, amount) {
let result = '';
if (type === 'kakaopay') {
result = amount + '원을 카카오페이를 통해 결제 하였습니다';
} else if (type === 'naverpay') {
result = amount + '원을 네이버페이를 통해 결제 하였습니다';
} else if ( type === 'toss') {
result = amount + '원을 토스를 통해 결제 하였습니다';
} else {
result = 'Type을 제대로 지정해주세요';
}
return result;
}
console.log(payment('naverpay', 10000)); //10000원을 네이버페이를 통해 결제 하였습니다
이런식의 코드를 짰다고 하면, 결제방식이 늘어날때마다 else if 를 추가 하는 방식으로 메서드 payment를 수정해야 할것이다. 그렇다면 이번엔 전략 패턴을 사용한 결제 코드를 만들어보자.
class Pay {
constructor(type) {
this.type = type;
}
payment (amount) {
this.type.payment(amount);
}
}
class KaKaoPay {
constructor() {}
payment(amount) {
const result = amount + '원을 카카오페이를 통해 결제 하였습니다';
return result;
}
}
class NaverPay {
constructor() {}
payment(amount) {
const result = amount + '원을 네이버페이를 통해 결제 하였습니다';
return result;
}
}
class Toss {
constructor() {}
payment(amount) {
const result = amount + '원을 토스를 통해 결제 하였습니다';
return result;
}
}
const pay = new Pay(new NaverPay());
console.log(pay.type.payment(10000)); //10000원을 네이버페이를 통해 결제 하였습니다
이렇게 전략패턴을 이용하면 다른 결제방식이 추가 되었다 하더라도 클래스 Pay를 수정하지 않고 새로운 클래스를 추가하는 것 만으로도 대응이 가능하다. 추가나 수정이 손 쉽게 가능하다는 것 말고도 재사용성이 높아지기 때문에 비용이 절감된다. 또한 각 결제 수단이 캡슐화 되어 있기 때문에 테스트도 용이하다. 하지만 클래스의 수가 늘어나기 때문에 코드가 복잡해질수 있는 단점이 있다.
< passport의 전략 패턴 >
passport 는 전략패턴을 사용한 라이브러리이다.
Passport.js
Simple, unobtrusive authentication for Node.js
www.passportjs.org
Node.js에서 인증 모듈을 구현할 때 쓰는 미들웨어 라이브러리로, 여러 가지 '전략'을 기반으로 인증할 수 있게 한다.
미들웨어(Middleware) 란? 서로 다른 애플리케이션이 서로 통신하는 데 사용되는 소프트웨어이다. 미들웨어는 단일 시스템에 원활하게 통합할 수 있도록 다양한 기술, 도구, 데이터베이스 간에 다리 역할을 한다. 그런 다음 이 단일 시스템은 사용자에게 통합된 서비스를 제공한다. 예를 들어 Windows 프런트엔드 애플리케이션은 Linux 백엔드 서버에서 데이터를 송수신하지만, 애플리케이션 사용자는 그 차이를 인식하지 못한다. - https://aws.amazon.com/ko/what-is/middleware/에서 발췌
- LocalStrategy : ID와 비밀번호를 기반으로 인증
- OAuth : 페이스북, 네이버 등 다른 서비스를 기반으로 인증
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy( // '전략'을 use 메서드의 매개변수로 넣어서 로직을 수행한다
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
위의 코드에서는 LocalStrategy '전략'으로 인증을 진행하고 있다.