Will find a way

[TS] 전략패턴 본문

Language/Typescript

[TS] 전략패턴

Jaka_Park 2024. 5. 2. 22:46

전략 패턴

공통된 기능을 인터페이스 중심으로 기능을 들어내서 의존성을 주입한다.

클래스로 생성 객체를 기능으로 받아서 사용하는 패턴 클래스를 공통된 기능 단위로 작성한다.

상속을 받을 경우에는 공통된 기능을 조건문으로 다 처리를 해야하는데

기능의 의존성을 공유하지 않고 하나의 기능만 관리하게 공통된 기능단위로 클래스를 작성한다.

(확장성 + 유지보수성 증가)

 

// interface
// 유저가 전달할 데이터 객체 구조 정의

interface IUserParams{
  email : string;
  password : string;
}

// 유저가 로그인을 요청하고 응답을 받았을 때
// 유니온 : 타입을 두가지 이상 포함 시킬 수 있다.
interface IAuthReps{
  success : boolean;
  // message 키가 있어도 되고 없어도 된다.
  message? : string | Error;
}

// 검증 객체 구조 정의
interface IAuth {
  // 로그인 검증을 하는 함수를 공통으로 주고
  authenticate(credentials : IUserParams) : IAuthReps;
}

// 전략 패턴 객체 구조 정의
interface IStrategy {
  // key 문자열로 작성
  // 포함시킬 기능이 생길 때마다 키를 추가하면서 값을 넣어줄 것
  [key : string] : IAuth;
}

////////////// strategy /////////////////
class Strategy {
  private strategy : IStrategy = {};
  
  // 서비스 로직을 객체에 추가할 함수
  // 의존성 주입을 할 부분
  setStrategy(key : string, authenticate : IAuth){
    // key 값을 받아서 key 추가하면서 로직 추가
    this.strategy[key] = authenticate;
  }
  
  login(type : string, credentials : IUserParams) : IAuthReps {
    const result = this.strategy[type].authenticate(credentials);
    return result;
  }
}

//////////// auth /////////////////
// 이메일 로그인 검증 클래스 정의
class EmailAuth implements IAuth {
  authenticate(credentials : IUserParams) : IAuthReps {
    // credentials === 유저가 입력한 값
    // 조건문을 credentials 값으로 작성
    // 이메일 로직 부분 작성
    console.log("email 로그인 성공");
    // 성공 객체 반환
    return { success : true, message : "이메일 로그인 성공" }
  }
}

// 구글 로그인 검증 클래스 정의
class Google implements IAuth {
  authenticate(credentials : IUserParams) : IAuthReps{
    // 구글 로그인 로직 부분 작성
    console.log("google 로그인 실패");
    // Error : 객체를 만들어 준다.
    // 생성자에서 매개변수로 전달한 문자열을 가지고 객체를 생성한다.
    return { success : false, message : new Error("구글 로그인에서 에러 발생") };
  }
}

// 카카오 로그인 검증 클래스 정의
class KakaoAuth implements IAuth{
  authenticate(credentials : IUserParams): IAuthReps{
    // 카카오 로그인 로직 부분 작성
    console.log("kakao 로그인 성공");
    return { success : true };
  }
}

////////// servie ////////////
class UserService{
  // 서비스들 기능들을 구조로 정의할 클래스
  // readonly 값을 읽기만 가능하고 수정할 수 없다.
  // 생성자 매개변수로 private readonly 매개변수명까지 넣고 생성자를 호출하게 되면
  // 키값은 strategy 전달한 매개변수로 객체에 키가 추가된다.
  constructor(private readonly strategy : Strategy){}
  login(type : string, credentials : IUserParams) : IAuthReps{
    const result = this.stategy.login(type, credentials);
    return result;
  }
}

///////// controller ///////////
// 유저 서비스 로직 실행 클래스 정의
class UserController {
  constructor(private readonly UserService : UserService){}
  
  signin (type : string) {
    const loginParams : IUserParams = {
      email : "jaka@naver.com",
      password : "123456"
    }
    const result = this.UserService.login(type, loginParams);
    console.log(result);
  }
}

// 전략 패턴 객체 생성
 const strategy = new Strategy();

// 로그인 로직을 추가
strategy.setStrategy("email", new EmailAuth());

// 카카오 로그인 로직 추가
strategy.setStrategy("kakao", new KakaoAuth());

// 구글 로그인 로직 추가
strategy.setStrategy("google", new GoogleAuth());

// 완성된 전략 패턴의 객체를 userservice 객체에 전달해서 생성
const userService = new UserService(strategy);

// 유저가 로그인 로직을 실행할 클래스 생성 userController
const userController = new UserController(userService);

// 유저가 로그인을 시도했다.
userController.signin("kakao");
userController.signin("google");
userController.signin("email");