typescript와 class정의

이무헌·2023년 8월 31일
0

typescript

목록 보기
2/2
post-thumbnail

1.interface

typescript에서는 변수,함수의 구조를 정의할 수 있는 interface가 존재한다. 다음과 같은 형태를 띄고 있다.

interface IBlock {
  id: number;
  title: string;
  content: string;
  date: number;
  like: number;
  //   옵션 체이닝으로 선택적으로 바꾼다.
  hit?: number;
}

위 인터페이스를 implements 하여 객체 구조의 타입을 정할 수 있다.

const Block: IBlock = {
  id: 0,
  title: "제목",
  content: "내용",
  date: 0,
  like: 0,
  hit: 0,
};

? optional chaning을 통해 hit는 선택에 따라 선언이 가능해진다.

클래스에서는?


interface Discount {
  // 함수만 선언
  getDiscountPrice(price: number): number;
}

인터페이스에 함수를 선언하여 추상화 시켜준다. '자바' 스크립트
클래스에서 저 인터페이스를 갖고와 보자

// 가격만 수정하는 할인
class FlatDiscount implements Discount {
  private amount: number;
  constructor(amount: number) {
    this.amount = amount;
  }
  getDiscountPrice(price: number): number {
    return price - this.amount;
  }
}

getDiscountPrice를 implement하여 갖고 왔다.
오버라이딩 하여 재정의해서 사용하면 된다.

2.간단한 로그인 시스템 구현하기

위에 쓰인 인터페이스와 클래스를 사용하여 간단한 로그인 시스템을 구현해보자

1.interface

1.회원이 보낸 정보를 정의한 interface

export interface AuthProps {
  email: string;
  password: string;
}

2.정보를 통해 로그인의 성공여부를 반환하는 interface


export interface AuthenticationResponse {
  success: boolean;
  message?: string;
}

3.검증을 처리해주는 함수를 담은 interface(곧 오버라이딩 당할 예정)


 export interface Authenticator {
  // 검증에 대한 요청 처리
  authenticate(credentials: AuthProps): Promise<AuthenticationResponse>;
}

4.로그인 로직, 즉 3번에서 정의한 함수(오버라이딩된)를 여기에서 쓰게 된다.

export interface LoginService {
  // 로그인 로직에 대한 함수 구조
  login(type: string, credentials: AuthProps): Promise<AuthenticationResponse>;
}

5.이메일 보내는 인터페이스

interface IEmailSender {
  sendEmail(email: string): void;
}

sendEmail 즉, 유저의 이메일을 보내주는 역할을 한다. 하지만 이번 코드에는 백엔드가 없고 ,외부 api도 없으니 그냥 명시적으로 넣어놨다.

6.로그인 타입 인터페이스 =>객체화

interface Strategy {
  email: EmailAuthenticator;
  kakao: kakaoAuthenticator;
}

이메일로 로그인 할지, 카카오로 로그인 할지 그 방법을 객체화 시켰다.

2.class

1. 위 6번에 나온 email,kakao의 type인 클래스(각 로그인 방법에 대한 로직)

// 이메일 로그인 로직 클래스
export class EmailAuthenticator implements Authenticator {
  async authenticate(credentials: AuthProps): Promise<AuthenticationResponse> {
    // 로직은 없다 요청과 응답 코드가 들어갈 부분
    console.log("email 로그인");
    return {
      success: true,
    };
  }
}

// 카카오 로그인 로직 클래스
export class kakaoAuthenticator implements Authenticator {
  async authenticate(credentials: AuthProps): Promise<AuthenticationResponse> {
    // 카카오 로그인 로직 들어갈 부분
    console.log("kakao 로그인");
    return { success: true };
  }
}

2.login service를 implements 받는 login 클래스

 export class Login implements LoginService {
  constructor(private readonly strategy: Strategy) {}

  async login(
    type: "email" | "kakao",
    credentials: AuthProps
  ): Promise<AuthenticationResponse> {
    // strategy 로그인 로직이 들어있는 객체
    // 여기에서 어떤 로그인 로직으로 처리할지 type구분해서
    const result = await this.strategy[type].authenticate(credentials);
    return result;
  }
}

type: "email" | "kakao"

만약 type이 어떤 객체의 특정한 key의 value라면 그 key를 타입 부분에 명시해주면 된다.

AuthenticationResponse가 리턴 타입이므로 우린 이 로그인이 클래스에서 성공,실패 여부가 결정됨을 알 수 있다.credentials은AuthProps interface이므로 사용자가 입력한 email,password의 정보임을 알 수 있다.

3.EmailSender , IEmailSender를 implements 받는 클래스

class EmailSender implements IEmailSender {
  sendEmail(email: string): void {}
}

원래 이메일을 보내는 로직을 오버라이딩 해야 하지만 없으므로 그저 class로 사용할 수 있도록 해주었다.

4.로그인과 이메일 처리(sendEMail)등등이 일어나는 종합 클래스

class Auth {
  constructor(
    private readonly authProps: AuthProps,
    private readonly emailSender: EmailSender,
    private readonly loginService: LoginService
  ) {}

  public async login() {
    console.log(this);
    await this.loginService.login("kakao", this.authProps);
  }
  //   이메일 인증 처리 구조
  public register(): void {
    this.emailSender.sendEmail(this.authProps.email);
  }
}

3.동적할당으로 변수 생성및 클래스 메소드 실행

// 유저의 이메일과 패스워드 임시 객체
const authProps: AuthProps = { email: "2coco97@gmail.com", password: "1234" };
const _emailSender = new EmailSender();

// 이메일 로그인 로직 클래스 동적 할당
const _email = new EmailAuthenticator();

// 카카오 로그인 로직 클래스 동적할당
const _kakao = new kakaoAuthenticator();

// 로그인 서비스 로직을 가지고 있는 객체 Strategy 인터페이스 사용
const _strategy: Strategy = {
  email: _email,
  kakao: _kakao,
};

const _loginService = new Login(_strategy);

const auth = new Auth(authProps, _emailSender, _loginService);

auth.login();
//Auth {
 // authProps: { email: '2coco97@gmail.com', password: '1234' },
//  emailSender: EmailSender {},
//  loginService: Login {
//    strategy: { email: EmailAuthenticator {}, kakao: kakaoAuthenticator {} }
//  }
//}
//kakao 로그인

4.전체코드

Authentication.ts

// 로그인 가입 관련된 작업
import { Strategy } from "./auth";
export interface AuthProps {
  email: string;
  password: string;
}

export interface AuthenticationResponse {
  success: boolean;
  message?: string;
}

export interface Authenticator {
  // 검증에 대한 요청 처리
  authenticate(credentials: AuthProps): Promise<AuthenticationResponse>;
}

// 이메일 로그인 로직 클래스
export class EmailAuthenticator implements Authenticator {
  async authenticate(credentials: AuthProps): Promise<AuthenticationResponse> {
    // 로직은 없다 요청과 응답 코드가 들어갈 부분
    console.log("email 로그인");
    return {
      success: true,
    };
  }
}

// 카카오 로그인 로직 클래스
export class kakaoAuthenticator implements Authenticator {
  async authenticate(credentials: AuthProps): Promise<AuthenticationResponse> {
    // 카카오 로그인 로직 들어갈 부분
    console.log("kakao 로그인");
    return { success: true };
  }
}

// 로그인에 대한 서비스를 처리할 클래스 구조
export interface LoginService {
  // 로그인 로직에 대한 함수 구조
  login(type: string, credentials: AuthProps): Promise<AuthenticationResponse>;
}

// 로그인 클래스에 로그인 서비스 구조를 상속 받고
export class Login implements LoginService {
  constructor(private readonly strategy: Strategy) {}

  async login(
    type: "email" | "kakao",
    credentials: AuthProps
  ): Promise<AuthenticationResponse> {
    // strategy 로그인 로직이 들어있는 객체
    // 여기에서 어떤 로그인 로직으로 처리할지 type구분해서
    const result = await this.strategy[type].authenticate(credentials);
    return result;
  }
}

auth.ts



import {
  EmailAuthenticator,
  Login,
  AuthProps,
  kakaoAuthenticator,
  LoginService,
} from "./Authentication";

interface IEmailSender {
  sendEmail(email: string): void;
}

class EmailSender implements IEmailSender {
  sendEmail(email: string): void {}
}

export interface Strategy {
  email: EmailAuthenticator;
  kakao: kakaoAuthenticator;
}

class Auth {
  constructor(
    private readonly authProps: AuthProps,
    private readonly emailSender: EmailSender,
    private readonly loginService: LoginService
  ) {}

  public async login() {
    console.log(this);
    await this.loginService.login("kakao", this.authProps);
  }
  //   이메일 인증 처리 구조
  public register(): void {
    this.emailSender.sendEmail(this.authProps.email);
  }
}

// 유저의 이메일과 패스워드 임시 객체
const authProps: AuthProps = { email: "2coco97@gmail.com", password: "1234" };
const _emailSender = new EmailSender();

// 이메일 로그인 로직 클래스 동적 할당
const _email = new EmailAuthenticator();

// 카카오 로그인 로직 클래스 동적할당
const _kakao = new kakaoAuthenticator();

// 로그인 서비스 로직을 가지고 있는 객체 Strategy 인터페이스 사용
const _strategy: Strategy = {
  email: _email,
  kakao: _kakao,
};

const _loginService = new Login(_strategy);

const auth = new Auth(authProps, _emailSender, _loginService);

auth.login();

5.느낀점

복잡하고 힘들었다.... java를 못 본지 어언1년이고 그 동안 함수지향형으로 코딩을 해서 객체지향을 보는 눈이 퇴화한것 같다 ㅜㅜ 개발자로 성장하려면 객체지향은 필수다. 더 공부해서 로그인 로직 정도는 정밀하게 설계할 수 있게 성장할 것이다.

profile
개발당시에 직면한 이슈를 정리하는 곳

0개의 댓글