Social Login + Strategy Pattern = πŸš€

Server The SOPTΒ·2022λ…„ 7μ›” 14일
5
post-thumbnail

✏️ μž‘μ„±μž: μ΄ν˜„μš°
πŸ“Œ μž‘μ„±μžμ˜ ν•œλ§ˆλ””: 처음 μ‚¬μš©ν•˜λŠ” ν”„λ ˆμž„μ›Œν¬λ”λΌλ„ λ‹€ 같은 ν”„λ‘œκ·Έλž˜λ°μ΄λ”λΌ

Motivation

λ‹€μ΄μ–΄ν„°λ“€μ˜ 외식 걱정을 μ€„μ—¬μ£ΌλŠ” λ‹€μ΄μ–΄νŠΈ 식당 지도 μ•±, ν—¬ν‘Έλ―Έμ˜ μ†Œμ…œλ‘œκ·ΈμΈ κΈ°λŠ₯을 κ΅¬ν˜„ν•˜λ©΄μ„œ μ•„λž˜μ™€ 같이 둜그인 μ„œλΉ„μŠ€ μ œκ³΅μžμ— 뢄기에 따라 λΆ„κΈ° μ²˜λ¦¬κ°€ λ˜λŠ” 것을 λ³΄μ•˜λ‹€.

// UserService.ts
import auth from "../config/auth";

export type SocialPlatform = "kakao" | "naver" | "apple";

const getUser = async (social: SocialPlatform, accessToken: string) => {
  try {
    let user;
    switch (social) {
      case "naver":
        user = await auth.naverAuth(accessToken);
        break;
      case "kakao":
        user = await auth.kakaoAuth(accessToken);
        break;
      case "apple":
        user = await auth.appleAuth(accessToken);
        break;
    }
    return user;
  } catch(error) {
    // ETC
  }
}

이 μ½”λ“œ μŠ€λ‹ˆνŽ«μ—μ„œ λ‹€μŒκ³Ό 같은 λ¬Έμ œμ μ„ λŠλ‚„ 수 μžˆμ—ˆλ‹€.

  • λ§Œμ•½ μ†Œμ…œ 인증 과정이 ν•œ 쀄에 μ²˜λ¦¬κ°€ 될 수 μžˆμ—ˆλ‹€λ©΄ userλ₯Ό κ°€λ³€λ³€μˆ˜κ°€ μ•„λ‹Œ λΆˆλ³€λ³€μˆ˜λ‘œ κ΄€λ¦¬ν•˜μ—¬ μ‚¬μš©ν•  수 μžˆμ§€ μ•Šμ•˜μ„κΉŒ?
  • μ†Œμ…œ 둜그인 제곡자λ₯Ό 이후에 더 μΆ”κ°€ν•˜κ±°λ‚˜ μ‚­μ œν•œλ‹€λ©΄ auth λͺ¨λ“ˆμ— μžˆλŠ” μ†Œμ…œ 인증 μ½”λ“œλ„ μˆ˜μ •ν•΄μ•Όν•˜κ³  switch λΆ„κΈ°μ²˜λ¦¬μ— κ΄€λ ¨λœ μ½”λ“œλ„ μˆ˜μ •ν•΄μ•Όν•œλ‹€. κΈ°λŠ₯을 μˆ˜μ •ν•  λ•Œ μ΅œλŒ€ν•œ μˆ˜μ •μ„ 덜 ν•˜λŠ” λ°©ν–₯으둜 μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μ—†μ„κΉŒ?

이 λ¬Έμ œμ λ“€μ„ ν•΄κ²°ν•˜κΈ° μœ„ν•΄ μ „λž΅ νŒ¨ν„΄(Strategy Pattern)을 μ μš©ν•˜μ—¬ μ½”λ“œ λ¦¬νŒ©ν† λ§μ„ ν•˜κΈ°λ‘œ ν–ˆλ‹€.

μ „λž΅ νŒ¨ν„΄(Strategy Pattern)μ΄λž€

μ „λž΅ νŒ¨ν„΄
객체듀이 ν•  수 μžˆλŠ” ν–‰μœ„ 각각에 λŒ€ν•΄ μ „λž΅ 클래슀λ₯Ό μƒμ„±ν•˜κ³ , μœ μ‚¬ν•œ ν–‰μœ„λ“€μ„ μΊ‘μŠν™” ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜,

객체의 ν–‰μœ„λ₯Ό λ™μ μœΌλ‘œ λ°”κΎΈκ³  싢은 경우 직접 ν–‰μœ„λ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šκ³  μ „λž΅μ„ λ°”κΏ”μ£ΌκΈ°λ§Œ ν•¨μœΌλ‘œμ¨ ν–‰μœ„λ₯Ό μœ μ—°ν•˜κ²Œ ν™•μž₯ν•˜λŠ” 방법

κ²Œμž„μ˜ μ˜ˆμ‹œλ₯Ό λ“€μ–΄λ³΄μž.

κ²Œμž„μ—μ„œ νŠΉμ • μœ λ‹›μ˜ 곡격 ν–‰μœ„λ₯Ό Attack이라고 ν•˜μž. μ–΄λ–€ μœ λ‹›μ€ 칼둜 곡격을 ν•  수 있고, μ–΄λ–€ μœ λ‹›μ€ 총으둜 곡격을 ν•  수 μžˆλ‹€. κ·Έλ ‡λ‹€λ©΄ 무기둜 곡격을 ν•˜λŠ” ν–‰μœ„λ₯Ό μœ„μ˜ μ •μ˜μ— 따라 μΊ‘μŠν™”λ₯Ό ν•΄λ³Έλ‹€λ©΄

interface Weapon {
  // 곡격λ ₯을 λ¦¬ν„΄ν•œλ‹€.
  attack(): number;
}

무기둜 곡격을 ν•˜λŠ” 것이기에 μœ„μ™€ 같이 ν•  수 μžˆλ‹€. κ·Έλ ‡λ‹€λ©΄ 칼둜 κ³΅κ²©ν•˜λŠ” ν–‰μœ„μ™€ 총으둜 κ³΅κ²©ν•˜λŠ” ν–‰μœ„λ₯Ό μœ„μ˜ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 클래슀둜 생각을 ν•œλ‹€λ©΄ μ•„λž˜μ™€ 같이 κ΅¬ν˜„μ„ ν•  수 μžˆμ„ 것이닀.

interface Weapon {
  attack(): number;
}

class Gun: Weapon {
  attack(): number {
    return 5;
  }
}

class Swrod: Weapon {
  attack(): number {
    return 3;
  }
}

// 사싀 μœ„μ™€ 같이 ν–‰μœ„μ˜ 이름/Input Parameter/return νƒ€μž… 등을 λ‹€ λ§žμ·„λ‹€λ©΄
// Union Type λ§ŒμœΌλ‘œλ„ μ „λž΅νŒ¨ν„΄μ„ ν™œμš©ν•  수 μžˆλ‹€.
// ν•˜μ§€λ§Œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™œμš©ν•˜μ—¬ 클래슀의 κ΅¬ν˜„ μš”μ†Œλ₯Ό κ°•μ œν•¨μœΌλ‘œμ¨ 
// λ°œμƒν•  수 μžˆλŠ” Human Errorλ₯Ό 쀄일 수 μžˆκΈ°μ— μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™œμš©ν•˜κ³ μž ν•œλ‹€.

μœ„μ™€ 같이 νŠΉμ • ν–‰μœ„λ₯Ό μΆ”μƒν™”ν•œλ‹€λ©΄, λ‹€λ₯Έ ν΄λž˜μŠ€μ—μ„œ 이 ν–‰μœ„λ₯Ό ν•  λ•Œ, ν–‰μœ„μ— κ΄€λ ¨λœ μ½”λ“œλ₯Ό μ „λ©΄ μˆ˜μ •ν•˜λŠ” 것이 μ•„λ‹ˆλΌ ν–‰μœ„κ°€ κ΅¬ν˜„λœ 클래슀만 ꡐ체λ₯Ό ν•΄μ£Όλ©΄ λ˜μ–΄μ„œ μ½”λ“œ μˆ˜μ •μ„ λ”μš± μš©μ΄ν•˜κ²Œ ν•  수 μžˆλ‹€.

// λ§Œμ•½ μ „λž΅νŒ¨ν„΄μ„ μ μš©ν•˜μ§€ μ•Šμ•˜λ‹€λ©΄?
// 동일 μ½”λ“œμ˜ 반볡

class Human {
  healthPoint = 100;
  constrctor(point: number) {
    this.healthPoint = point;
  }
  hurt(weapon: any) {
    if (weapon instanceof Gun) {
      healthPoint -= weapon.attack()
    } else if (weapon instanceof Sword) {
      healthPoint -= weapon.attack()
    }
  }
}

// μ „λž΅ νŒ¨ν„΄μ„ μ μš©ν•˜μ˜€λ‹€λ©΄
class Human {
  let healthPoint = 100;
  hurt(weapon: Weapon) {
    healthPoint -= weapon.attack()
  }
}

UseCase - Social Login

μš°μ„  Auth λͺ¨λ“ˆμ— μžˆλŠ” ν–‰μœ„λ“€μ„ 좔상화할 수 μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•œλ‹€. ν—¬ν‘Έλ―Έμ˜ 경우 SocialAuthStrategyλΌλŠ” μΈν„°νŽ˜μ΄μŠ€λ‘œ 이 ν–‰μœ„λ₯Ό μΆ”μƒν™”ν•˜μ˜€λ‹€.

export interface SocialAuthStrategy {
  execute(accessToken: string): Promise<any>;
}

이제 각 ν–‰μœ„λ“€μ„ μΆ”μƒν™”ν•˜λŠ” ν΄λž˜μŠ€λ“€μ„ λ§Œλ“€λ„λ‘ ν•˜μž.

class NaverAuthStrategy implements SocialAuthStrategy {
  execute(accessToken: string): Promise<any> {
    return auth.naverAuth(accessToken);
  }
}

class KakaoAuthStrategy implements SocialAuthStrategy {
  execute(accessToken: string): Promise<any> {
    return auth.kakaoAuth(accessToken);
  }
}

class AppleAuthStrategy implements SocialAuthStrategy {
  execute(accessToken: string): Promise<any> {
    return auth.appleAuth(accessToken);
  }
}

이제 이 κ΅¬ν˜„μ²΄λ₯Ό μ†Œμ…œλ‘œκ·ΈμΈμ— ν™œμš©ν•  수 있게 Path에 λ”Έλ €μ˜€λŠ” μ†Œμ…œ 둜그인 μ œκ³΅μžμ— λ”°λΌμ„œ μ „λž΅μ„ μ œκ³΅ν•΄μ€„ 수 μžˆλŠ” 객체λ₯Ό λ§Œλ“€μ–΄λ³΄λ„λ‘ ν•˜μž.

JSκ³„μ—΄μ—μ„œ κ°μ²΄λŠ” 기본적으둜 Dictionary νƒ€μž…μ΄κΈ°μ— JS μ–Έμ–΄ νŠΉμ„±μ— λ§žμΆ°μ„œ Keyκ°€ SocialPlatform type("naver", "kakao", "apple"의 λ¦¬ν„°λŸ΄ μœ λ‹ˆμ˜¨ νƒ€μž…)이고 valueκ°€ SocialAuthStrategy인 Mapped Type을 λ§Œλ“ λ‹€λ©΄

// export type SocialPlatform = "kakao" | "naver" | "apple";

type AuthType = {
  [social in SocialPlatform]: SocialAuthStrategy;
};

μ•„λž˜μ™€ 같이 Key에 λ”°λΌμ„œ ν–‰μœ„λ₯Ό κ°€μ Έμ˜¬ 수 μžˆλŠ” 객체λ₯Ό λ§Œλ“€ 수 μžˆλ‹€.

export const authStrategy: AuthType = {
  naver: new NaverAuthStrategy(),
  kakao: new KakaoAuthStrategy(),
  apple: new AppleAuthStrategy(),
};

이제 이λ₯Ό μ‹€μ œ μ½”λ“œμ— μ μš©ν•˜λ©΄?

import { authStrategy } from "./SocialAuthStrategy";

export type SocialPlatform = "kakao" | "naver" | "apple";

const getUser = async (social: SocialPlatform, accessToken: string) => {
  try {
    const user = await authStrategy[social].execute(accessToken);
    return user;
  } catch (error) {
    // ETC
  }
};

μœ„μ™€ 같이 λΆ„κΈ°λ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•Šκ³  μ†Œμ…œλ‘œκ·ΈμΈ 인증을 μˆ˜ν–‰ν•  수 μžˆλ‹€. λ˜ν•œ μ†Œμ…œ 둜그인 제곡자의 변동이 μžˆλ”λΌλ„ SocialPlatform에 λ¦¬ν„°λŸ΄ νƒ€μž…μ„ μΆ”κ°€/μ‚­μ œ ν•˜κ³  SocialAuthStrategyλ₯Ό κ΅¬ν˜„ν•˜λŠ” ν–‰μœ„ 클래슀(λ¬Όλ‘  이에 λ”°λ₯Έ 인증 κΈ°λŠ₯도 κ΅¬ν˜„ν•΄μ•Όν•œλ‹€)만 κ΅¬ν˜„ν•˜λ©΄ λ˜κΈ°μ— 고유 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ— 큰 변동 없이 κΈ°λŠ₯을 μˆ˜μ •ν•  수 μžˆλ‹€.

profile
λŒ€ν•™μƒμ—°ν•© ITλ²€μ²˜μ°½μ—… 동아리 SOPT 30κΈ° SERVER 파트 기술 λΈ”λ‘œκ·Έμž…λ‹ˆλ‹€.

2개의 λŒ“κΈ€

comment-user-thumbnail
2022λ…„ 7μ›” 14일

μ§„μ§œ λ°°μ›Œκ°‘λ‹ˆλ‹€

λ‹΅κΈ€ 달기
comment-user-thumbnail
2022λ…„ 7μ›” 17일

저도 ν•œμˆ˜ λ°°μ›Œκ°‘λ‹ˆλ‹€~ μ†Œμ…œ λ‘œκ·ΈμΈμ—λ„ μ „λž΅ νŒ¨ν„΄μ„ μ μš©ν•  수 μžˆμ—ˆκ΅°μš”.
λ§Œμ•½ λ‹€λ₯Έ μ†Œμ…œ λ‘œκ·ΈμΈμ„ μΆ”κ°€ν•΄μ•Ό ν•˜λŠ” κ²½μš°μ—λ„
κΈ°μ‘΄ μ½”λ“œ 변경없이 SocialAuthStrategy μΈν„°νŽ˜μ΄μŠ€λ§Œ κ΅¬ν˜„ν•˜λ©΄ λ˜κ² λ„€μš”!

λ‹΅κΈ€ 달기