티스토리 뷰

WEB2.0/프로그래밍

덕타이핑은 뭐냐

나를찾는아이 2022. 5. 4. 21:52
728x90
반응형
컴퓨터 프로그래밍 분야에서 덕 타이핑(duck typing)은 동적 타이핑의 한 종류로, 객체의 변수 및 메소드의 집합이 객체의 타입을 결정하는 것을 말한다. 클래스 상속이나 인터페이스 구현으로 타입을 구분하는 대신, 덕 타이핑은 객체가 어떤 타입에 걸맞은 변수와 메소드를 지니면 객체를 해당 타입에 속하는 것으로 간주한다. “덕 타이핑”이라는 용어는 다음과 같이 표현될 수 있는 덕 테스트에서 유래했다. (덕은 영어로 오리를 의미한다.)

 

만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.

 

 

 

일단 바로 코드로 얘기해보죠

 

interface Animal {
    fun shout()
}

class Duck : Animal {
    init {}
    
    override fun shout() {
        println("I'm a duck");
    }
}

class Tiger : Animal {
    init {}
    
    override fun shout() {
        println("I'm a tiger");
    }
}

class Noiser {
    fun makeSomeNoise(animal: Animal) {
        animal.shout();
    }
}

fun main() {
    val noiser = Noiser();
    noiser.makeSomeNoise(Duck()); // I'm a duck
    noiser.makeSomeNoise(Tiger()); // I'm a tiger
}

코틀린으로 작성한 코드입니다

 

Animal이라는 인터페이스는 shout라는 메소드를 가지고 있습니다

 

그리고 이 Animal 인터페이스를 구현한 Duck과 Tiger 클래스를 인스턴스화하여

 

다형성을 테스트할수 있는 Noiser라는 클래스의 makeSomeNoise 메소드의 매개변수로 넣습니다

 

당연히 Duck과 Tiger의 인스턴스는 Animal을 구현한 클래스로부터 생성되었기 때문에

 

Animal 매개변수로 전달될수 있습니다

 

 

 

import Foundation

protocol Animal {
    func shout()
}

class Duck: Animal {
    func shout() {
        print("I'm a duck");
    }
}

class Tiger: Animal {
    func shout() {
        print("I'm a tiger");
    }
}

class Noiser {
    func makeSomeNoise(animal: Animal) {
        animal.shout();
    }
}

let noiser = Noiser();
noiser.makeSomeNoise(animal: Duck()); // I'm a duck
noiser.makeSomeNoise(animal: Tiger()); // I'm a tiger

 

Swift로 같은 코드를 짜봅니다

 

java의 interface와 비슷한 개념의 protocol을 사용하여 Animal을 정의합니다

 

그리고 마찬가지로 protocol을 상속받은 Duck과 Tiger 클래스를 생성합니다

 

마찬가지로 다형성을 테스트하기 위해 Animal을 매개변수로 받는 메소드를 가진 클래스를 만들어서 넣어봅니다

 

물론 잘동작합니다

 

모두 같은 protocol을 상속받았기 때문이죠

 

 

 

코틀린, swift로 작성한 코드 모두 

 

Animal과 Duck, Tiger 사이에 부모 자식의 관계로 연결이 되어있고

 

그렇기 때문에 Animal 타입으로도 전달 될 수 있습니다

 

 

 

interface Animal {
    shout() : void
}

class Duck {
    shout() : void {
        console.log("I'm a duck");
    }
}
  
class Tiger {
    shout() : void {
        console.log("I'm a tiger");
    }
}
  
class Noiser {
    makeSomeNoise(animal: Animal) : void {
        animal.shout();
    }
}
 
const noiser = new Noiser();
noiser.makeSomeNoise(new Duck()); // I'm a duck
noiser.makeSomeNoise(new Tiger()); // I'm a tiger

 

이번엔 같은 코드를 타입스크립트로 작성하겠습니다(자바스크립트로도 물론 가능합니다)

 

어라? 뭔가 다릅니다

 

내가 만든 interface Animal을

 

Duck, Tiger 이 두개 클래스 모두 상속을 받지 않고 아무런 연관성이 없는데

 

makeSomeNoise 라는 메소드에서 Animal 타입의 매개변수를 받는데 정상동작하는것을 볼수 있습니다

 

 

 

바로 이것이 덕타이핑입니다

 

덕타이핑은 동적언어의 대표적인 특징이라고 할 수 있습니다

 

파이썬, 자바스크립트, 타입스크립트, golang 등 많은 언어에서 사용해볼수 있습니다

 

 

덕 타이핑에서는, 객체의 타입보다 객체가 사용되는 양상이 더 중요합니다

 

Animal을 상속받지 않더라도

 

Animal이 가지고 있는 shout() 메소드를

 

Duck과 Tiger 모두 가지고 있기 때문에

 

Animal도 shout()할수 있고, Duck도 할수 있고, Tiger도 할수 있어서

 

모두 같은것으로 취급해주는 개념입니다

 

 

만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.

 

 

이제 이 말이 좀 더 와닿을까요?

 

그래서 우리는 이러한 동적언어를 매우 간편하게 사용할수 있습니다

 

굳이 직접적으로 클래스를 상속받거나 구현을 하지 않아도

 

같은 역할을 한다면 같은놈(?) 취급을 해주니깐요

 

 

 

axios의 post method는 AxiosRequestConfig라는 타입의 세번째 인자값을 갖습니다

 

post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;

 

post 메소드는 이런식으로 생겼습니다

 

아마 java, kotlin이었다면

 

우리는 세번째 인자값의 config를 전달하기 위해

 

AxiosRequestConfig의 인스턴스를 직접 생성하거나 이것을 상속받는 클래스를 생성하여 인스턴스화 한다음에 변수로 넣었을거예요

 

 

new AxiosRequestConfig()

new MyAxiosRequestConfig()

AxiosRequestConfig.builder.build()

 

뭐 이런식으로 말이죠

 

 

하지만 덕타이핑의 javascript는 AxiosRequestConfig와 같은 시그니처를 가지고 있다면

 

그러니깐 같은 프로퍼티를 가지고 있거나, 같은 메소드를 가지고 있거나 한다면

 

얼마든지 값을 넣을수 있는거죠

 

export interface AxiosRequestConfig {
  url?: string;
  method?: Method;
  baseURL?: string;
  transformRequest?: AxiosTransformer | AxiosTransformer[];
  transformResponse?: AxiosTransformer | AxiosTransformer[];
  headers?: any;
  params?: any;
  paramsSerializer?: (params: any) => string;
  data?: any;
  timeout?: number;
  timeoutErrorMessage?: string;
  withCredentials?: boolean;
  adapter?: AxiosAdapter;
  auth?: AxiosBasicCredentials;
  responseType?: ResponseType;
  xsrfCookieName?: string;
  xsrfHeaderName?: string;
  onUploadProgress?: (progressEvent: any) => void;
  onDownloadProgress?: (progressEvent: any) => void;
  maxContentLength?: number;
  validateStatus?: (status: number) => boolean;
  maxRedirects?: number;
  socketPath?: string | null;
  httpAgent?: any;
  httpsAgent?: any;
  proxy?: AxiosProxyConfig | false;
  cancelToken?: CancelToken;
}

AxiosRequestConfig는 위와 같은 시그니처를 가지고 있습니다

 

모든 필드값이 ? 로 표시되어 optional 하기 때문에

 

{} 빈 object도 AxiosRequestConfig가 될수 있고,

 

저 많은 필드중에 하나의 필드값만 가져도 AxiosRequestConfig로 간주되어집니다

 

import axios, {AxiosRequestConfig} from "axios";

// AxiosRequestConfig의 signiture와 매칭된다면 어떤것이든 가능 

// 가능
axios.post("/test", {}, {});

// 가능
axios.post("/test", {}, { url : "https://www.naver.com"});

 

이런식으로 config 옵션을 얼마든지 넣을수 있는거예요

 

 

최근 7년간 덕타이핑을 사용하는 동적언어 golang, javascript(typescript)를 주력으로 사용하고 있습니다

 

이러한 덕타이핑 개념은 정적언어만 사용하시던 분들은 다소 어라??? 이렇게 생각할수 있는 개념이기도하고

 

그래서 정적->동적언어로 언어스킬이 변경될때 다소 어리둥절할수 있습니다

 

 

제가 최대한 쉽게 덕타이핑을 설명드렸는데 도움되셨으면 좋겠어요

 

 

 

 

728x90
반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함