2023-08-31 20:25:57

class 란?

객체 지향 프로그래밍을 하기 위한 문법으로 TypeScript의 class는 Javascript의 ES6의 class 구문에 기반하며 몇가지 추가적인 특징을 포함하고 있습니다. TypeScript는 이 구문을 기반으로 추가적인 기능을 제공합니다.

  • 정적 타이핑을 지원하여 클래스의 속성 및 메소드에 타입을 지정할 수 있습니다.
  • 접근제어(public, private, protected)를 사용하여 속성과 메소드의 접근 범위를 제한 할 수 있습니다.
  • 추상 클래스, 인터페이스, 제네릭 등의 고급 객체 지향 기능을 제공합니다.

기본 문법

class ClassName {
    // 필드 (fields), 생성자 (constructor), 메서드 (methods)
}

 

class 구성 요소

  • 생성자 : class의 객체가 생성될 때 호출되는 메소드입니다.
class Person {
    name: string;
    age: number;
    
// 생성자
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

// 또는

class Person {
// 생성자
    constructor(public name: string, public age: number) {}
}
  • 접근제한자 : 속성이나 메소드의 접근 범위를 제한하는 키워드로 public, private, protected가 있습니다.
class Product {
  // public : 모든 곳에서 접근 가능
  public name: string;
  // private : 해당 클래스 내에서만 접근 가능
  private price: number;
  // protected : 해당 클래스 및 서브 클래스에서만 접근 가능
  protected discountAmount: number;
  constructor(name: string, price: number) {
    this.name = name;
    this.price = price;
    this.discountAmount = 0;
  }
}

 

class를 활용한 간단한 가격 할인 코드

// private : 접근 불가 키워드로 직접 참조가 되지 않는 값
class Product {
  private name: string;
  private price: number;
  private discountAmount: number;
  constructor(name: string, price: number) {
    this.name = name;
    this.price = price;
    this.discountAmount = 0;
  }
  // private 키워드를 사용했기때문에 직접 참조가 불가하여 get메소드를 사용하여 호출
  getName(): string {
    return this.name;
  }

  getPrice(): number {
    return this.price - this.discountAmount;
  }

  getProduct() {
    return { name: this.name, price: this.getPrice() };
  }

  // 할인가 조정
  // set 메소드를 활용하여 할인가 조정
  setDiscountAmount(amount: number): void {
    this.discountAmount = amount;
  }
}

const product = new Product("동복", 1000); 
console.log(product.getProduct()); // { name: '동복', price: 1000 }
product.setDiscountAmount(200); 
console.log(product.getProduct()); // { name: '동복', price: 800 }

interface란?

interface는 TypeScript에서 객체의 형태를 기술하여 객체의 속성 이름과 그 값들이 가질 수 있는 데이터타입에 대한 정보를 TypeScript 컴파일러에 제공하는데 사용됩니다. 인터페이스는 인터페이스를 구현하는 함수, 변수 또는 클래스에 타입 검사 기능을 추가합니다.

 

기본 문법

interface Person {
   // 필드 (fields), 메서드 (methods)
}

 

interface 구성 요소

  • 속성 : 인터페이스는 객체의 속성과 변수에 대한 타입을 정의합니다.
interface Person {
    name: string;
    age: number;
}
  • 옵셔널 체이닝 : 인터페이스의 속성 중 필수가 아닌 속성에 대한 선택적 속성을 부여할 수 있습니다.
interface Person {
    name: string;
    age?: number; // 선택적 속성. 있어도 되고 없어도 됨
}

읽기 전용 속성 : 인터페이스의 객체가 생성된 후 수정 될 수 없게 설정 할 수 있습니다.

interface Person {
    readonly name: string;
    readonly age: number;
}

implements

TypeScript에서 클래스가 특정 인터페이스를 준수하도록 강제하는데 사용되는 키워드로 이를 사용하여 클래스가 인터페이스에 정의된 모든 속성과 메소드를 제대로 구현하도록 보장합니다.

 

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;
  }
}

interface를 활용한 간단한 가격 할인 코드

// 할인
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;
  }
}

// 할인율로 가격 수정
class PercentDiscount implements Discount {
  private amount: number;
  constructor(amount: number) {
    this.amount = amount;
  }

  getDiscountPrice(price: number): number {
    return price * (1 - this.amount / 100);
  }
}

// 가격, 할인율로 가격 수정
class FlatPercentDiscount implements Discount {
  private flatAmount: number;
  private percent: number;
  constructor(flatAmount: number, percent: number) {
    this.flatAmount = flatAmount;
    this.percent = percent;
  }

  getDiscountPrice(price: number): number {
    const flatDiscountAmount = price - this.flatAmount;
    return flatDiscountAmount * (1 - this.percent / 100);
  }
}

class Products {
  private name: string;
  private price: number;
  constructor(name: string, price: number) {
    this.name = name;
    this.price = price;
  }
  getName(): string {
    return this.name;
  }

  getPrice(): number {
    return this.price;
  }
}

class ProductDiscount {
  private product: Products;
  private discount: Discount;
  constructor(product: Products, discount: Discount) {
    this.product = product;
    this.discount = discount;
  }

  getPrice(): void {
    console.log(this.discount.getDiscountPrice(this.product.getPrice()));
  }
}

const _product = new Products("mac", 100000);
const _product2 = new Products("window", 10000);

const productDiscount = new PercentDiscount(10);
const productDiscount2 = new FlatDiscount(1000);
const productDiscount3 = new FlatPercentDiscount(1000, 10);

// 10% 할인된 가격으로 출력
const productWithDiscount = new ProductDiscount(_product, productDiscount);
productWithDiscount.getPrice(); // 90000

// 1000원 할인 후 10% 할인된 가격으로 출력
const productWithDiscount2 = new ProductDiscount(_product2, productDiscount3);
productWithDiscount2.getPrice(); // 8100

 

class와 interface를 활용한 로그인 로직 만들기

  • 실제 동작하지는 않지만 추후 API를 연결하여 사용할 수 있습니다.

Auth.js

// 로그인, 회원가입 관련 작업
// 카카오, 네이버, 구글 로그인

import { Strategy } from "./auth";

// 이메일과 패스워드를 받음
export interface AuthProps {
  email: string;
  password: string;
}

// 로그인 성공 여부에 따른 결과값 반환
interface AuthenticationResponse {
  success: boolean;
  message?: string;
}

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

// 이메일 로그인
export class EmailAuthenticator implements Authenticator {
  async authenticate(credentials: AuthProps): Promise<AuthenticationResponse> {
    console.log("Email login");
    return { success: true };
  }
}

// kakao 로그인
export class KakaoAuthenticator implements Authenticator {
  async authenticate(credentials: AuthProps): Promise<AuthenticationResponse> {
    console.log("Kakao login");
    return { success: true };
  }
}

export interface LoginService {
  // 어떤 방식으로 로그인 했는지 확인(Email, kakao)
  login(type: string, credentials: AuthProps): Promise<AuthenticationResponse>;
}

// strategy를 다른 파일에서 받아오기 떄문에 type 부분이 string이 아니라 넘어온 값을 직접 써줘야함
export class Login implements LoginService {
  constructor(private readonly strategy: Strategy) {}
  async login(
    type: "email" | "kakao",
    credentials: AuthProps
  ): Promise<AuthenticationResponse> {
    // 어떤 로그인 로직으로 처리할지 type 구분
    const result = await this.strategy[type].authenticate(credentials);
    return result;
  }
}

 

Authent.ts

import {
  AuthProps,
  EmailAuthenticator,
  KakaoAuthenticator,
  Login,
  LoginService,
} from "./Authent";

// I를 붙히는 이유는 인터페이스라고 알려주는 뜻
interface IEmailSender {
  sendEmail(email: string): void;
}

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

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

class Auth {
  // private 키워드가 붙어서 생성자의 객체의 키로 추가
  constructor(
    private readonly authProps: AuthProps,
    private readonly emailSender: EmailSender,
    private readonly loginServer: LoginService
  ) {}

  // 로그인 구조
  public async login() {
    console.log(this);
    await this.loginServer.login("kakao", this.authProps);
  }

  // emaill 인증 처리 구조
  public register(): void {
    this.emailSender.sendEmail(this.authProps.email);
  }
}

// email, password 임시 객체
const authProps: AuthProps = { email: "winnel@naver.com", password: "1234" };
const _emailSender = new EmailSender();

// email 로그인 로직 클래스 동적 할당
const _email = new EmailAuthenticator();
// kakao 로그인 로직 클래스 동적 할당
const _kakao = new KakaoAuthenticator();

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

const _loginService = new Login(_strategy);
const auth = new Auth(authProps, _emailSender, _loginService);
auth.login();
728x90