2023-09-01 20:15:37

 

전략(Strategy) 패턴이란?

전략 패턴이란 객체 지향 디자인 패턴 중 하나로 알고리즘을 정의하고 각각을 캡슐화하여 그들을 상호 교체 가능하게 만드는 패턴입니다.

객체가 할 수 있는 행위를 각각 정의하여 입력 받은 타입에 대한 처리를 각각의 파일에서 동적으로 수행합니다.

 

주요 구성 요소

  • 전략 인터페이스(Strategy Interface) : 모든 구체적인 전략이 구현해야하는 인터페이스 입니다.
interface TravelStrategy {
  travel(): string;
}
  • 구체적인 전략(Concrete Strategies) : 전략 인터페이스를 구현하는 여러 클래스들은 실제 로직이나 알고리즘을 정의합니다.
class WalkingStrategy implements TravelStrategy {
  travel(): string {
    return "걸어가는 중!";
  }
}

class BikeStrategy implements TravelStrategy {
  travel(): string {
    return "자전거 타고 가는 중!";
  }
}

class BusStrategy implements TravelStrategy {
  travel(): string {
    return "버스 타고 가는 중!";
  }
}
  • 컨텍스트(Context) : 전략 객체를 구성하며 전략을 통해 정의된 알고리즘을 실행합니다.
class Traveler {
  private strategy: TravelStrategy;

  constructor(strategy: TravelStrategy) {
    this.strategy = strategy;
  }

  setTravelStrategy(strategy: TravelStrategy) {
    this.strategy = strategy;
  }

  startJourney(): string {
    return this.strategy.travel();
  }
}

const dongbok = new Traveler(new WalkingStrategy());
console.log(john.startJourney());  // "걸어가는 중!"

dongbok.setTravelStrategy(new BikeStrategy());
console.log(john.startJourney());  // "자전거 타고 가는 중!!"

dongbok.setTravelStrategy(new BusStrategy());
console.log(john.startJourney());  // "버스 타고 가는 중!"

 

전략 패턴을 활용한 로그인 구현

  • 실제 로그인 로직은 없으며 전략 패턴을 실습하고자 임의 객체를 만들어 실습
  • interface/login.request.ts : 유저 아이디와 패스워드를 담을 객체 구조 정의
  • service/user.service.ts : 로그인 클래스 정의
  • strategy/Authenticator.ts : 요청을 받았을때 요청에 대한 성공 실패 여부를 검증 해주는 객체 구조 정의
  • strategy/email.strategy.ts : email 형식으로 로그인을 요청했을때 
  • strategy/google.strategy.ts : google 로그인 요청했을때
  • strategy/kakao.strategy.ts : kakao 로그인 요청했을때
  • strategy/strategy.ts : email, google, kakao 로그인 로직을 하나로 묶음
  • user.controller.ts : 사용자가 로그인 요청을 하면 서비스를 수행할 클래스 정의
  • index.ts : 위에서 정의된 전략 패턴을 생성자로 받아온 뒤 로그인 작업 수행

 

프로젝트 구조

user/interface/login.request.ts

// 유저 정보 객체 구조 정의
export interface UserParams {
  email: string;
  password: string;
}

user/service/user.service.ts

import Strategy from "../strategy/strategy";
import { UserParams } from "../interfaces/login.request";
import { AuthenticatonResponse } from "../strategy/Authenticator";

// 유저 서비스 로직 클래스 정의
class UserService {
  // 전략패턴 유저 로그인 서비스 로직 객체
  // 이메일, 카카오, 구글 세가지 로그인 로직을 사용
  constructor(private readonly strategy: Strategy) {}

  async login(
    type: string,
    credentials: UserParams
  ): Promise<AuthenticatonResponse> {
    const result = await this.strategy.login(type, credentials);
    return result;
  }
}

export default UserService;

user/strategy/Authenticator.ts

import { UserParams } from "../interfaces/login.request";

// 응답 받았을때 객체의 구조 정의
export interface AuthenticatonResponse {
  success: boolean;
  message?: string; // 옵셔널 체이닝을 사용하여 없어도 사용가능
}

// 검증 객체 구조 정의
export interface Authenticator {
  // 로그인 검증 함수 구조 선언
  authentcate(credentials: UserParams): Promise<AuthenticatonResponse>;
}

user/strategy/email.strategy.ts

import { UserParams } from "../interfaces/login.request";
import { AuthenticatonResponse, Authenticator } from "./Authenticator";

// email 형식으로 로그인 시도했을때
export class EmailAuthenticator implements Authenticator {
  async authentcate(credentials: UserParams): Promise<AuthenticatonResponse> {
    // email 로그인 로직
    console.log("email login success");
    return { success: true };
  }
}

user/strategy/google.strategy.ts

import { UserParams } from "../interfaces/login.request";
import { AuthenticatonResponse, Authenticator } from "./Authenticator";

// 구글 계정으로 로그인 시도 했을때
export class GoogleAuthenticator implements Authenticator {
  async authentcate(credentials: UserParams): Promise<AuthenticatonResponse> {
    // 구글 로그인 로직 작성);
    return { success: true };
  }
}

user/strategy/kakao.strategy.ts

import { UserParams } from "../interfaces/login.request";
import { AuthenticatonResponse, Authenticator } from "./Authenticator";

// 카카오 계정으로 로그인 시도 했을때
export class KakaoAuthenticator implements Authenticator {
  async authentcate(credentials: UserParams): Promise<AuthenticatonResponse> {
    // 카카오 로그인 로직
    console.log("kakao login success");
    return { success: true };
  }
}

user/strategy/strategy.ts

import { UserParams } from "../interfaces/login.request";
import { AuthenticatonResponse, Authenticator } from "./Authenticator";

// 3개의 로그인 로직을 하나로 묶음
// 전략 패턴 객체 구조 정의
interface IStrategy {
  // key를 문자열로 지정
  [key: string]: Authenticator;
}

// 서비스 로직들을 가질 객체 구조 정의
class Strategy {
  private strategy: IStrategy = {};

  // 서비스 로직을 객체에 추가할 함수
  public set(key: string, authenticate: Authenticator) {
    // key 값을 받고 서비스 로직 추가
    this.strategy[key] = authenticate;
  }
  public async login(
    type: string,
    credentials: UserParams
  ): Promise<AuthenticatonResponse> {
    const result = await this.strategy[type].authentcate(credentials);
    return result;
  }
}

export default Strategy;

user/user.controller.ts

import { UserParams } from "./interfaces/login.request";
import UserService from "./service/user.service";

// 사용자 서비스 로직 클래스 정의
class UserController {
  constructor(private readonly userService: UserService) {}

  // 로그인
  // /login/:type으로 요청이 들어왔다면
  signin(type: string) {
    console.log("실행 순서 1 : UserController.signin");
    // req.body에서 유저의 정보를 받아옴(실습 환경에서는 임시 객체 사용)
    const loginParams: UserParams = {
      email: "db@google.com",
      password: "1234",
    };
    this.userService.login(type, loginParams);
  }
  // 회원가입
  // /signup
  signup() {}
}

export default UserController;

user/index.ts

import Strategy from "./strategy/strategy";
import UserService from "./service/user.service";
import { GoogleAuthenticator } from "./strategy/google.strategy";
import { KakaoAuthenticator } from "./strategy/kakao.strategy";
import { EmailAuthenticator } from "./strategy/email.strategy";
import UserController from "./user.controller";

// 전략 패턴 객체 생성
const strategy = new Strategy(); // { strategy: {},set(), login() }

strategy.set("email", new EmailAuthenticator()); // { strategy: { EmailAuthenticator { authentcate }},set(), login() }

strategy.set("kakao", new KakaoAuthenticator()); // { strategy: { EmailAuthenticator { authentcate }},{ KakaoAuthenticator { authentcate }},set(), login() }

strategy.set("google", new GoogleAuthenticator()); // { strategy: { EmailAuthenticator { authentcate }},{ KakaoAuthenticator { authentcate }},{ GoogleAuthenticator { authentcate }},set(), login() }

// 완성된 객체를 유저 서비스 클래스 생성자의 매개변수로 전달 및 유저 서비스 객체 생성
const userService = new UserService(strategy);

// 유저 로그인 로직 클래스 생성 및 유저 서비스 로직 객체 생성자 매개변수로 전달
const userController = new UserController(userService);

userController.signin("google");
728x90