자바 스크립트에서 함수를 오버로드하는 방법은 무엇입니까?


103

오버로딩에 대한 고전적인 (비 -js) 접근 방식 :

function myFunc(){
 //code
}

function myFunc(overloaded){
 //other code
}

Javascript는 동일한 이름으로 둘 이상의 함수를 정의 할 수 없습니다. 따라서 다음과 같은 내용이 표시됩니다.

function myFunc(options){
 if(options["overloaded"]){
  //code
 }
}

오버로드가있는 객체를 전달하는 것 외에 자바 스크립트에서 함수 오버로딩에 대한 더 나은 해결 방법이 있습니까?

오버로드를 전달하면 가능한 각 오버로드에 조건문이 필요하기 때문에 함수가 너무 장황해질 수 있습니다. //code이러한 조건문 내부 를 수행하기 위해 함수를 사용하면 범위가있는 까다로운 상황이 발생할 수 있습니다.


1
그렇다면 자바 스크립트에 대해 잘못 생각하고 있습니다. 당신이 과부하 및 범위에 문제가 있다면 그것은 다른 작업하고있는 것처럼 소리, 당신은 아마 다른 방법 이름을 가져야한다
joshcomley

@joshcomley-오버로드 및 범위에는 처리 할 코드가 필요합니다. 나는 단지 내가 사용하는 코드가 가능한 한 효율적인지 확인하려고 노력하고있다. 해결 방법으로 범위를 처리하는 것은 좋지만 최소한 모범 사례 방법을 사용하는 것을 선호합니다.
Travis J

답변:


126

Javascript에서 인수 오버로딩에는 여러 측면이 있습니다.

  1. 가변 인수 -다른 인수 세트 (유형 및 수량 모두)를 전달할 수 있으며 함수는 전달 된 인수와 일치하는 방식으로 작동합니다.

  2. 기본 인수 -전달되지 않은 경우 인수의 기본값을 정의 할 수 있습니다.

  3. 명명 된 인수 -인수 순서는 관련이 없으며 함수에 전달할 인수의 이름 만 지정하면됩니다.

다음은 이러한 각 인수 처리 범주에 대한 섹션입니다.

가변 인수

자바 스크립트에는 인수에 대한 유형 검사가 없거나 인수의 수량이 필요 myFunc()하기 때문에 인수의 유형, 존재 또는 수량을 확인하여 전달 된 인수에 적응할 수있는 구현을 하나만 가질 수 있습니다.

jQuery는 항상이 작업을 수행합니다. 인수 중 일부를 선택적으로 만들거나 전달되는 인수에 따라 함수에서 분기 할 수 있습니다.

이러한 유형의 오버로드를 구현할 때 사용할 수있는 몇 가지 다른 기술이 있습니다.

  1. 선언 된 인수 이름 값이인지 확인하여 주어진 인수의 존재를 확인할 수 있습니다 undefined.
  2. 을 사용하여 총 수량 또는 인수를 확인할 수 있습니다 arguments.length.
  3. 주어진 인수의 유형을 확인할 수 있습니다.
  4. 가변 개수의 인수의 경우 arguments의사 배열을 사용하여 arguments[i].

여기 몇 가지 예가 있어요.

jQuery의 obj.data()방법을 살펴 보자 . 다음과 같은 네 가지 사용 형태를 지원합니다.

obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);

각각은 다른 동작을 트리거하며이 동적 형태의 오버로딩을 사용하지 않으면 4 개의 개별 함수가 필요합니다.

다음은 이러한 모든 옵션을 영어로 구분 한 다음 코드로 모두 결합하는 방법입니다.

// get the data element associated with a particular key value
obj.data("key");

전달 된 첫 번째 인수 .data()가 문자열이고 두 번째 인수가 undefined인 경우 호출자는이 양식을 사용해야합니다.


// set the value associated with a particular key
obj.data("key", value);

두 번째 인수가 정의되지 않은 경우 특정 키의 값을 설정하십시오.


// get all keys/values
obj.data();

인수가 전달되지 않으면 반환 된 객체의 모든 키 / 값을 반환합니다.


// set all keys/values from the passed in object
obj.data(object);

첫 번째 인수의 유형이 일반 객체 인 경우 해당 객체의 모든 키 / 값을 설정합니다.


이 모든 것을 하나의 자바 스크립트 로직 세트로 결합하는 방법은 다음과 같습니다.

 // method declaration for .data()
 data: function(key, value) {
     if (arguments.length === 0) {
         // .data()
         // no args passed, return all keys/values in an object
     } else if (typeof key === "string") {
         // first arg is a string, look at type of second arg
         if (typeof value !== "undefined") {
             // .data("key", value)
             // set the value for a particular key
         } else {
             // .data("key")
             // retrieve a value for a key
         }
     } else if (typeof key === "object") {
         // .data(object)
         // set all key/value pairs from this object
     } else {
         // unsupported arguments passed
     }
 },

이 기술의 핵심은 수락하려는 모든 형식의 인수를 고유하게 식별 할 수 있고 호출자가 어떤 형식을 사용하고 있는지에 대해 혼동이 없도록하는 것입니다. 일반적으로 인수의 순서를 적절하게 지정하고 인수의 유형과 위치에 충분한 고유성이 있는지 확인하여 항상 어떤 형식이 사용되고 있는지 알 수 있어야합니다.

예를 들어, 세 개의 문자열 인수를받는 함수가있는 경우 :

obj.query("firstArg", "secondArg", "thirdArg");

세 번째 인수를 쉽게 선택적으로 만들 수 있으며 해당 조건을 쉽게 감지 할 수 있지만 두 번째 인수 만 선택적으로 만들 수는 없습니다. 인수는 두 번째 인수를 의미하거나 두 번째 인수가 생략되었으므로 두 번째 인수의 자리에있는 것은 실제로 세 번째 인수입니다.

obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");

세 개의 인수가 모두 동일한 유형이므로 다른 인수의 차이를 알 수 없으므로 호출자가 의도 한 바를 알 수 없습니다. 이 호출 스타일에서는 세 번째 인수 만 선택 사항이 될 수 있습니다. 두 번째 인수를 생략하려면 null대신 (또는 다른 감지 가능한 값) 으로 전달 해야하며 코드에서 다음을 감지합니다.

obj.query("firstArg", null, "thirdArg");

다음은 선택적 인수의 jQuery 예제입니다. 두 인수 모두 선택 사항이며 전달되지 않으면 기본값을 사용합니다.

clone: function( dataAndEvents, deepDataAndEvents ) {
    dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
    deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

    return this.map( function () {
        return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
    });
},

다음은 인수가 누락 될 수있는 jQuery 예제 또는 네 가지 다른 오버로드를 제공하는 세 가지 유형 중 하나입니다.

html: function( value ) {
    if ( value === undefined ) {
        return this[0] && this[0].nodeType === 1 ?
            this[0].innerHTML.replace(rinlinejQuery, "") :
            null;

    // See if we can take a shortcut and just use innerHTML
    } else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
        (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
        !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {

        value = value.replace(rxhtmlTag, "<$1></$2>");

        try {
            for ( var i = 0, l = this.length; i < l; i++ ) {
                // Remove element nodes and prevent memory leaks
                if ( this[i].nodeType === 1 ) {
                    jQuery.cleanData( this[i].getElementsByTagName("*") );
                    this[i].innerHTML = value;
                }
            }

        // If using innerHTML throws an exception, use the fallback method
        } catch(e) {
            this.empty().append( value );
        }

    } else if ( jQuery.isFunction( value ) ) {
        this.each(function(i){
            var self = jQuery( this );

            self.html( value.call(this, i, self.html()) );
        });

    } else {
        this.empty().append( value );
    }

    return this;
},

명명 된 인수

Python과 같은 다른 언어에서는 일부 인수 만 전달하고 전달되는 순서와 관계없이 인수를 만드는 수단으로 명명 된 인수를 전달할 수 있습니다. Javascript는 명명 된 인수의 기능을 직접 지원하지 않습니다. 그 자리에서 일반적으로 사용되는 디자인 패턴은 속성 / 값의 맵을 전달하는 것입니다. 이것은 속성과 값이있는 객체를 전달하거나 ES6 이상에서 실제로 Map 객체 자체를 전달할 수 있습니다.

다음은 간단한 ES5 예입니다.

jQuery $.ajax()는 속성과 값이있는 일반 Javascript 객체 인 단일 매개 변수를 전달하는 사용 형태를 허용합니다. 전달하는 속성은 ajax 호출에 전달되는 인수 / 옵션을 결정합니다. 일부는 필수 일 수 있으며 많은 것은 선택 사항입니다. 객체의 속성이므로 특정 순서가 없습니다. 실제로 해당 객체에 전달할 수있는 30 개 이상의 서로 다른 속성이 있으며 하나 (URL) 만 필요합니다.

예를 들면 다음과 같습니다.

$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
    // process result here
});

$.ajax()그런 다음 구현 내 에서 들어오는 객체에 전달 된 속성을 조사하여 명명 된 인수로 사용할 수 있습니다. 이는 for (prop in obj)모든 속성을 배열로 가져 오거나 배열 Object.keys(obj)을 반복 하여 수행 할 수 있습니다 .

이 기술은 많은 수의 인수가 있거나 많은 인수가 선택 사항 인 경우 Javascript에서 매우 일반적으로 사용됩니다. 참고 : 이는 최소한의 유효한 인수 집합이 있는지 확인하고 불충분 한 인수가 전달 된 경우 누락 된 부분을 호출자에게 디버그 피드백을 제공하기 위해 구현 함수에 부담을줍니다 (아마도 유용한 오류 메시지와 함께 예외를 던짐). .

ES6 환경에서는 위의 전달 된 객체에 대한 기본 속성 / 값을 생성하기 위해 구조 분해를 사용할 수 있습니다. 이에 대해서는 이 참조 문서 에서 자세히 설명 합니다.

다음은 해당 기사의 한 가지 예입니다.

function selectEntries({ start=0, end=-1, step=1 } = {}) {
    ···
};

이렇게하면 함수에 전달 된 객체 의 start, endstep속성에 대한 기본 속성과 값이 생성 selectEntries()됩니다.

함수 인수의 기본값

ES6에서 Javascript는 인수의 기본값에 대한 기본 제공 언어 지원을 추가합니다.

예를 들면 :

function multiply(a, b = 1) {
  return a*b;
}

multiply(5); // 5

이것이 MDN에서 사용되는 방법에 대한 자세한 설명입니다 .


그래서 적응 가능한 함수를 고수하고 함수 오버로드 파사드를 구현하는 모범 사례로 받아 들여야합니까?
Travis J

2
@TravisJ-예, 적응 형 기능은 자바 스크립트에서 오버로드가 수행되는 방식입니다. 다른 옵션은 다른 인수와 함께 별도로 명명 된 함수를 사용하는 것입니다. 두 구현에 공통점이없는 경우 둘 중 하나가 허용되는 방법입니다. 두 구현이 본질적으로 동일한 작업을 수행하지만 다른 인수로 시작하는 경우 적응 가능한 함수를 사용하기 위해 인터페이스를 더 간결하게 만들고 두 관련 구현간에 코드를 공유 할 수있는 기회를 제공한다고 생각합니다.
jfriend00

영어와 실제 코드로 설명 된 일부 코드 예제를 추가했습니다.
jfriend00 2014-10-26

이를 시도 할 때 Safari 최신 버전에서 오류, 예기치 않은 토큰 '='이 표시 resetProgressBar: function(display_errors, lockout = false)됩니다.
Nick

@Nick-이 ES6 호환성 표에 따라 기본 매개 변수 값은 Safari 10 이상에서 작동해야합니다. 당 이 MDN 페이지 , 그것은 Safari에서 "지원하지"말한다. Mac이 없어서 직접 테스트 할 수 없습니다. 일반적으로 많은 브라우저에서 나중에 구현 된 ES6 기능 중 하나 인 것 같습니다. 예를 들어 아직 iOS9에있는 것처럼 보이지 않습니다.
jfriend00

33

JavaScript에서 함수 오버로딩은 여러 가지 방법으로 수행 할 수 있습니다. 이들 모두는 모든 프로세스를 수행하거나 하위 기능 / 프로세스에 위임하는 단일 마스터 기능을 포함합니다.

가장 일반적인 간단한 기술 중 하나는 간단한 스위치입니다.

function foo(a, b) {
    switch (arguments.length) {
    case 0:
        //do basic code
        break;
    case 1:
        //do code with `a`
        break;
    case 2:
    default:
        //do code with `a` & `b`
        break;
    }
}

더 우아한 기술은 배열 (또는 모든 인수 개수에 대해 오버로드를 수행하지 않는 경우 객체)을 사용하는 것입니다 .

fooArr = [
    function () {
    },
    function (a) {
    },
    function (a,b) {
    }
];
function foo(a, b) {
    return fooArr[arguments.length](a, b);
}

이전 예제는 그다지 우아하지 않고 누구나 수정할 수 fooArr있으며 누군가에 2 개 이상의 인수를 전달하면 실패 foo하므로 모듈 패턴과 몇 가지 검사를 사용하는 것이 더 나은 형식입니다.

var foo = (function () {
    var fns;
    fns = [
        function () {
        },
        function (a) {
        },
        function (a, b) {
        }
    ];
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = foo.length;
        }
        return fns[fnIndex].call(this, a, b);
    }
    return foo;
}());

물론 오버로드는 동적 수의 매개 변수를 사용하기를 원할 수 있으므로 fns컬렉션에 개체를 사용할 수 있습니다 .

var foo = (function () {
    var fns;
    fns = {};
    fns[0] = function () {
    };
    fns[1] = function (a) {
    };
    fns[2] = function (a, b) {
    };
    fns.params = function (a, b /*, params */) {
    };
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = 'params';
        }
        return fns[fnIndex].apply(this, Array.prototype.slice.call(arguments));
    }
    return foo;
}());

내 개인적인 선호 switch는 마스터 기능을 대량으로 만들지 만. 이 기술을 사용하는 일반적인 예는 접근 자 / 변이 자 메서드입니다.

function Foo() {} //constructor
Foo.prototype = {
    bar: function (val) {
        switch (arguments.length) {
        case 0:
            return this._bar;
        case 1:
            this._bar = val;
            return this;
        }
    }
}

이것은 대안의 좋은 예입니다. :)
Travis J

1
니스, 나는 그 2 기술 등
닉 롤란

8

엄격한 의미에서 메서드 오버로딩을 수행 할 수 없습니다. java또는 에서 지원되는 방식과 다릅니다 c#.

문제는 JavaScript가 기본적으로 메서드 오버로딩을 지원하지 않는다는 것입니다. 따라서 동일한 이름을 가진 두 개 이상의 함수를보고 구문 분석하는 경우 마지막으로 정의 된 함수를 고려하고 이전 함수를 덮어 씁니다.

대부분의 경우에 적합하다고 생각하는 방법 중 하나는 다음과 같습니다.

방법이 있다고 가정 해 봅시다.

function foo(x)
{
} 

자바 스크립트에서 불가능한 오버로딩 메소드 대신 새로운 메소드를 정의 할 수 있습니다.

fooNew(x,y,z)
{
}

다음과 같이 첫 번째 함수를 수정하십시오.

function foo(x)
{
  if(arguments.length==2)
  {
     return fooNew(arguments[0],  arguments[1]);
  }
} 

이러한 오버로드 된 메서드가 많은 switch경우 단순한 if-else문 사용을 고려하십시오 .

( 상세 정보 ) 추신 : 위 링크는 이에 대한 추가 정보가있는 개인 블로그로 이동합니다.


그것은 문제가 아니라 기능입니다.
inetphantom 19

4

인수 번호에 따라 약간 다른 오버로딩 접근 방식을 사용하고 있습니다. 그러나 저는 John Fawcett의 접근 방식도 좋다고 생각합니다. 다음은 John Resig (jQuery의 작성자) 설명을 기반으로 한 코드입니다.

// o = existing object, n = function name, f = function.
    function overload(o, n, f){
        var old = o[n];
        o[n] = function(){
            if(f.length == arguments.length){
                return f.apply(this, arguments);
            }
            else if(typeof o == 'function'){
                return old.apply(this, arguments);
            }
        };
    }

유용성 :

var obj = {};
overload(obj, 'function_name', function(){ /* what we will do if no args passed? */});
overload(obj, 'function_name', function(first){ /* what we will do if 1 arg passed? */});
overload(obj, 'function_name', function(first, second){ /* what we will do if 2 args passed? */});
overload(obj, 'function_name', function(first,second,third){ /* what we will do if 3 args passed? */});
//... etc :)

4

자바 스크립트에서는 함수를 한 번만 구현하고 매개 변수없이 함수를 호출 할 myFunc() 수 있습니다. 그런 다음 옵션이 '정의되지 않음'인지 확인합니다.

function myFunc(options){
 if(typeof options != 'undefined'){
  //code
 }
}

3

여기에 설명 된이 문제에 대한 우아한 해결책을 개발하려고 노력했습니다 . 여기 에서 데모를 찾을 수 있습니다 . 사용법은 다음과 같습니다.

var out = def({
    'int': function(a) {
        alert('Here is int '+a);
    },

    'float': function(a) {
        alert('Here is float '+a);
    },

    'string': function(a) {
        alert('Here is string '+a);
    },

    'int,string': function(a, b) {
        alert('Here is an int '+a+' and a string '+b);
    },
    'default': function(obj) {
        alert('Here is some other value '+ obj);
    }

});

out('ten');
out(1);
out(2, 'robot');
out(2.5);
out(true);

이를 달성하기 위해 사용 된 방법 :

var def = function(functions, parent) {
 return function() {
    var types = [];
    var args = [];
    eachArg(arguments, function(i, elem) {
        args.push(elem);
        types.push(whatis(elem));
    });
    if(functions.hasOwnProperty(types.join())) {
        return functions[types.join()].apply(parent, args);
    } else {
        if (typeof functions === 'function')
            return functions.apply(parent, args);
        if (functions.hasOwnProperty('default'))
            return functions['default'].apply(parent, args);        
    }
  };
};

var eachArg = function(args, fn) {
 var i = 0;
 while (args.hasOwnProperty(i)) {
    if(fn !== undefined)
        fn(i, args[i]);
    i++;
 }
 return i-1;
};

var whatis = function(val) {

 if(val === undefined)
    return 'undefined';
 if(val === null)
    return 'null';

 var type = typeof val;

 if(type === 'object') {
    if(val.hasOwnProperty('length') && val.hasOwnProperty('push'))
        return 'array';
    if(val.hasOwnProperty('getDate') && val.hasOwnProperty('toLocaleTimeString'))
        return 'date';
    if(val.hasOwnProperty('toExponential'))
        type = 'number';
    if(val.hasOwnProperty('substring') && val.hasOwnProperty('length'))
        return 'string';
 }

 if(type === 'number') {
    if(val.toString().indexOf('.') > 0)
        return 'float';
    else
        return 'int';
 }

 return type;
};

1
입력을 처리하는 함수, 매우 영리합니다.
Travis J

3

https://github.com/jrf0110/leFunc

var getItems = leFunc({
  "string": function(id){
    // Do something
  },
  "string,object": function(id, options){
    // Do something else
  },
  "string,object,function": function(id, options, callback){
    // Do something different
    callback();
  },
  "object,string,function": function(options, message, callback){
    // Do something ca-raaaaazzzy
    callback();
  }
});

getItems("123abc"); // Calls the first function - "string"
getItems("123abc", {poop: true}); // Calls the second function - "string,object"
getItems("123abc", {butt: true}, function(){}); // Calls the third function - "string,object,function"
getItems({butt: true}, "What what?" function(){}); // Calls the fourth function - "object,string,function"

3

JS에서 오버로딩에 문제가 없습니다. pb 함수를 오버로딩 할 때 깨끗한 코드를 유지하는 방법은 무엇입니까?

두 가지를 기반으로 전달 을 사용 하여 깨끗한 코드를 가질 수 있습니다 .

  1. 인수 수 (함수 호출시).
  2. 인수 유형 (함수 호출시)

      function myFunc(){
          return window['myFunc_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
       }
    
        /** one argument & this argument is string */
      function myFunc_1_string(){
    
      }
       //------------
       /** one argument & this argument is object */
      function myFunc_1_object(){
    
      }
      //----------
      /** two arguments & those arguments are both string */
      function myFunc_2_string_string(){
    
      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function myFunc_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }
    
       //--- And so on ....   


2

JavaScript에는 함수 오버로드 옵션 이 없으므로 대신 사용할 수 있습니다. 하나 또는 두 개의 필수 인수가있는 경우 옵션 개체와 별도로 유지하는 것이 좋습니다. 다음은 옵션 객체에 값이 전달되지 않은 경우 옵션 객체와 채워진 값을 기본값으로 사용하는 방법에 대한 예입니다.

function optionsObjectTest(x, y, opts) {
    opts = opts || {}; // default to an empty options object

    var stringValue = opts.stringValue || "string default value";
    var boolValue = !!opts.boolValue; // coerces value to boolean with a double negation pattern
    var numericValue = opts.numericValue === undefined ? 123 : opts.numericValue;

    return "{x:" + x + ", y:" + y + ", stringValue:'" + stringValue + "', boolValue:" + boolValue + ", numericValue:" + numericValue + "}";

}

다음 은 옵션 객체를 사용하는 방법에 대한 예입니다.


2

이를 위해 객체에 함수를 추가하는 함수를 만들어야합니다. 그러면 함수에 보내는 인수의 양에 따라 실행됩니다.

<script > 
//Main function to add the methods
function addMethod(object, name, fn) {
  var old = object[name];
  object[name] = function(){
    if (fn.length == arguments.length)
      return fn.apply(this, arguments)
    else if (typeof old == 'function')
      return old.apply(this, arguments);
  };
}


  var ninjas = {
   values: ["Dean Edwards", "Sam Stephenson", "Alex Russell"]
};

//Here we declare the first function with no arguments passed
  addMethod(ninjas, "find", function(){
    return this.values;
});

//Second function with one argument
  addMethod(ninjas, "find", function(name){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i].indexOf(name) == 0)
        ret.push(this.values[i]);
    return ret;
  });

//Third function with two arguments
  addMethod(ninjas, "find", function(first, last){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i] == (first + " " + last))
        ret.push(this.values[i]);
    return ret;
  });


//Now you can do:
ninjas.find();
ninjas.find("Sam");
ninjas.find("Dean", "Edwards")
</script>

0

동일한 기능에 대해 인수 그룹을 구별하는 기능을 얻기 위해 상위 함수 내에 하위 함수를 추가하는 것을 좋아합니다.

var doSomething = function() {
    var foo;
    var bar;
};

doSomething.withArgSet1 = function(arg0, arg1) {
    var obj = new doSomething();
    // do something the first way
    return obj;
};

doSomething.withArgSet2 = function(arg2, arg3) {
    var obj = new doSomething();
    // do something the second way
    return obj;
};

0

달성하려는 것은 함수의 로컬 인수 변수를 사용하는 것이 가장 좋습니다 .

function foo() {
    if (arguments.length === 0) {
        //do something
    }
    if (arguments.length === 1) {
        //do something else
    }
}

foo(); //do something
foo('one'); //do something else

여기에서 작동 방식에 대한 더 나은 설명을 찾을 수 있습니다 .


충돌 감지는 어떻습니까? 이것은 매개 변수없는 함수를 사용하거나 존재 여부를 확인하는 좋은 방법이지만 오버로딩과 관련하여 실제로는 모범 사례가 아닙니다.
Travis J
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.