IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  리눅스 | 오픈 소스  >

Python 3 입문, Part 2: 고급 주제

메타클래스, 데코레이터 및 기타 새로운 기능

developerWorks
문서 옵션
PDF format - Fits A4 and Letter

PDF - Fits A4 and Letter
52KB (15 pages)

Get Adobe® Reader®

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

Cesar Otero, Consultant, Freelance

원문 게재일 : 2009 년 1 월 30 일
번역 게재일 : 2009 년 3 월 31 일

Python 3은 Guido van Rossum이 개발한 강력한 범용 프로그래밍 언어의 최신 버전입니다. 이 버전에서는 이전 버전인 2.x 제품군과의 호환성이 지원되지는 않지만 일부 구문 문제가 정리되었습니다. 첫 번째 기사에서 이어지는 이 두 번째 기사에서는 새로운 Python 기능을 좀 더 살펴보고 추상 기본 클래스, 메타클래스 및 데코레이터의 변경 사항과 같은 고급 주제에 대해 자세히 설명합니다.

Python 3000 또는 Py3K이라고도 하는 Python 버전 3에 대한 이전 기사에서는 새로운 print() 함수, bytes 데이터 유형 및 string 유형의 변경 사항과 같이 이전 버전과의 호환성을 지원하지 않는 몇 가지 기본적인 변경 사항에 대해 살펴보았다. 이 연재 기사의 Part 2에서는 ABC(Abstract Base Class), 메타클래스, 함수 주석 및 데코레이터, 정수 리터럴 지원, 숫자 유형 계층 구조, 예외 발생 및 처리와 관련된 변경 사항 등과 같은 고급 주제를 설명한다. 이러한 변경 사항도 대부분 버전 2.x 제품군과 호환되지 않는다.

클래스 데코레이터

이전 버전의 Python에서는 메소드 변환을 메소드 정의 이후에 수행해야 한다. 메소드의 길이가 긴 경우에는 이 요구 사항으로 인해 정의의 중요한 구성 요소가 외부 인터페이스의 정의와 동떨어지게 된다. 이에 대한 자세한 설명은 PEP(Python Enhancement Proposal) 318(참고자료에서 제공하는 링크 참조)에서 볼 수 있다. 다음 코드에서는 변환 요구 사항의 예를 보여 준다.


Listing 1. 버전 3 이전의 Python에서 사용하는 메소드 변환 방법

def myMethod(self):
    # do something

myMethod = transformMethod(myMethod)

Python 버전 2.4에서는 동일한 메소드 이름을 여러 번 재사용하지 않으면서 가독성을 높이기 위해 메소드 데코레이터를 도입했다.

데코레이터는 다른 메소드를 수정하고 메소드 또는 호출 가능한 다른 오브젝트를 리턴하는 메소드이다. 데코레이터 이름 앞에 @ 기호를 추가하여 데코레이터임을 표시한다. 이러한 구문은 Java™ 주석과 비슷하다. Listing 2에서는 실제 데코레이터 예를 보여 준다.


Listing 2. 데코레이터 메소드

@transformMethod
def myMethod(self):
    # do something

데코레이터는 문법적 편의를 높여주는 요소이다. Wikipedia에서는 데코레이터를 "기능에 영향을 주지 않고 사용 편의성을 높여 주기 위해 컴퓨터 언어의 구문에 추가되는 요소"라고 설명하고 있다. 데코레이터는 주로 정적 메소드에 대한 주석을 작성하는 데 사용된다. Listing 1과 Listing 2는 동일한 기능을 수행하지만 Listing 2를 파악하기가 더 쉽다.

데코레이터는 다음과 같이 다른 메소드를 정의하듯이 정의한다.

def mod(method):
    method.__name__ = "John"
    return method

@mod
def modMe():
    pass

print(modMe.__name__)

게다가 Python 3에서는 메소드뿐만 아니라 클래스에 대해서도 데코레이터가 지원된다. 다음은 버전 3 이전의 Python에서 사용하는 코드이다.

class myClass:
    pass

myClass = doSomethingOrNotWithClass(myClass)

위 코드 대신 다음과 같은 코드를 사용할 수 있다.

@doSomethingOrNotWithClass
class myClass:
    pass




위로


메타클래스

메타클래스는 다른 클래스를 인스턴스로 가지고 있는 클래스이다. Python 3에서는 다른 메타클래스를 만들거나 런타임에 클래스를 동적으로 만드는 데 사용되는 내장 metaclass 유형이 계속 지원되므로 다음과 같은 구문을 사용할 수 있다.

>>>aClass = type('className', 
   (object,), 
   {'magicMethod': lambda cls : print("blah blah")})

이 구문은 클래스 이름과 같은 문자열, 상속된 오브젝트 Tuple(비어 있을 수 있음) 및 추가할 수 있는 메소드가 포함된 Dictionary(비어 있을 수 있음)를 인수로 받는다. 물론 다음과 같이 type에서 상속받아 고유한 메타클래스를 만들 수도 있다.

class meta(type):
    def __new__(cls, className, baseClasses, dictOfMethods):
        return type.__new__(cls, className, baseClasses, dictOfMethods)

키워드 전용 인수

Python 3에서는 함수 인수를 "매개변수 슬롯"에 할당하는 방법이 변경되었다. 전달할 매개변수에 별표(*)를 사용할 수 있으며, 이 경우 가변 길이 인수를 사용할 수 없다. 명명된 인수가 * 뒤에 있어야 한다(예: def meth(*, arg1): pass). Python 설명서나 PEP 3102에서 자세한 정보를 볼 수 있다(참고자료에서 제공하는 링크 참조).

참고: 마지막 두 예제가 잘 이해되지 않는 경우에는 메타클래스에 대한 David Mertz와 Michele Simionato의 연재 기사를 읽어보기를 권한다. 참고자료에서 해당 링크를 제공한다.

이제 클래스 정의를 살펴보자. 클래스 정의에서는 기본 클래스 목록 뒤에 키워드 인수를 사용할 수 있으며, 일반적으로 클래스는 class Foo(*bases, **kwds): pass의 형태로 정의된다. 메타클래스는 키워드 인수 metaclass를 통해 클래스 정의에 전달된다. 예를 들면, 다음과 같다.

>>>class aClass(baseClass1, baseClass2, metaclass = aMetaClass): pass

기존 메타클래스 구문에서는 다음과 같이 메타클래스를 내장 속성 __metaclass__에 할당한다.

class Test(object):
    __metaclass__ = type

버전 3에서는 새로운 속성인 __prepare__가 추가되었다. 이 속성을 사용하여 새 클래스 네임스페이스에 대한 Dictionary를 만들 수 있다. 이 속성은 클래스 본문이 평가되기 전에 호출된다(Listing 3 참조).


Listing 3. __prepare__ 속성을 사용하는 간단한 메타클래스

def meth():
    print("Calling method")

class MyMeta(type):
    @classmethod
    def __prepare__(cls, name, baseClasses):
        return {'meth':meth}

    def __new__(cls, name, baseClasses, classdict):
        return type.__new__(cls, name, baseClasses, classdict)

class Test(metaclass = MyMeta):
    def __init__(self):
        pass

    attr = 'an attribute'

t = Test()
print(t.attr)

PEP 3115에 실린 예제의 일부를 가져온 Listing 4의 흥미로운 예제에서는 클래스 메소드의 선언 순서를 유지하면서 클래스 메소드의 이름 목록이 포함된 메타클래스를 만든다.


Listing 4. 클래스 구성원의 순서를 유지하는 메타클래스

# The custom dictionary
class member_table(dict):
    def __init__(self):
        self.member_names = []

    def __setitem__(self, key, value):
        # if the key is not already defined, add to the
        # list of keys.
        if key not in self:
            self.member_names.append(key)

        # Call superclass
        dict.__setitem__(self, key, value)

# The metaclass
class OrderedClass(type):
    # The prepare function
    @classmethod
    def __prepare__(metacls, name, bases): # No keywords in this case
        return member_table()

    # The metaclass invocation
    def __new__(cls, name, bases, classdict):
        # Note that we replace the classdict with a regular
        # dict before passing it to the superclass, so that we
        # don't continue to record member names after the class
        # has been created.
        result = type.__new__(cls, name, bases, dict(classdict))
        result.member_names = classdict.member_names
        return result

메타클래스가 변경된 데는 몇 가지 이유가 있다. 오브젝트는 순서를 고려하지 않은 채로 Dictionary에 메소드를 저장한다. 하지만 선언된 클래스 구성원의 순서가 유지되었을 때 더욱 효과적으로 수행할 수 있는 작업도 있다. 클래스를 만들 때 초기에 메타클래스를 포함시키면 정보를 사용 가능한 상태로 유지하면서 순서도 유지할 수 있다. 예를 들어, 이 방법은 C 구조체를 만들 때 유용하게 사용할 수 있다. 앞으로는 이 새로운 메커니즘으로 인해 클래스를 만드는 동안 생성된 클래스 네임스페이스의 본문에 기호를 삽입한 후 기호의 참조를 전달할 수도 있게 될 것이다. PEP 3115에서는 구문을 변경하게 된 미학적인 이유도 설명하고 있지만 이는 객관적으로 입증할 수 없기 때문에 논쟁 수준에 머물러 있다. (참고자료에서 PEP 3115에 대한 링크를 볼 수 있다.)




위로


추상 기본 클래스

이 연재 기사의 첫 번째 기사에서 말했던 것처럼 ABC는 인스턴스화할 수 없는 클래스이다. Java 또는 C++ 언어를 사용하던 프로그래머라면 이 개념에 익숙해져야 한다. Python 3에서는 ABC 작업을 지원하기 위해 새로운 프레임워크인 abc가 추가되었다.

abc 모듈에는 하나의 메타클래스(ABCMeta)와 데코레이터(@abstractmethod@abstractproperty)가 있다. ABC에 @abstractmethod@abstractproperty가 있으면 ABC가 인스턴스화되지 않는다. 이 경우에는 데코레이터를 서브클래스에서 재정의해야 한다. 예를 들어, 다음 코드를 살펴보자.

>>>from abc import *
>>>class C(metaclass = ABCMeta): pass
>>>c = C()

위 코드는 정상적으로 실행된다. 그러나 다음과 같이 해서는 안 된다.

>>>from abc import *
>>>class C(metaclass = ABCMeta): 
...    @abstractmethod
...    def absMethod(self):
...        pass
>>>c = C() 
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class C with abstract methods absMethod

대신 다음과 같은 코드를 사용하는 것이 좋다.

>>>class B(C):
...    def absMethod(self):
...        print("Now a concrete method")
>>>b = B()
>>>b.absMethod()
Now a concrete method

ABCMeta 클래스는 __instancecheck____subclasscheck__ 속성을 재정의하며 내장 함수인 isinstance()issubclass()를 오버로드하는 데 사용할 수 있다. ABCMeta에서 제공하는 register() 메소드를 사용하여 가상 서브클래스를 ABC에 추가할 수 있다. 다음은 간단한 예제이다.

>>>class TestABC(metaclass=ABCMeta): pass
>>>TestABC.register(list)
>>>TestABC.__instancecheck__([])
True

이 예제는 isinstance(list, TestABC)와 동일한 기능을 수행한다. Python 3에서는 __issubclass__ 대신 __instancecheck__를 사용하고, __issubclass__ 대신 __subclasscheck__를 사용한다는 것을 알 수 있으며 버전 3의 표현이 더 자연스럽게 보인다. 예를 들어, superclass.__isinstance__(subclass)에 대한 인수의 역인 isinstance(subclass, superclass)는 혼동을 초래할 수 있는 반면 superclass.__instancecheck__(subclass) 구문이 훨씬 쉽게 알아볼 수 있다.

다음과 같이 collections 모듈 내에서 여러 ABC를 사용하여 클래스에서 특정 인터페이스를 제공하는지 여부를 테스트할 수 있다.

>>>from collections import Iterable
>>>issubclass(list, Iterable)
True

표 1에서는 collections 프레임워크의 ABC를 보여 준다.


표 1. collections 프레임워크의 ABC
ABC상속
Container
Hashable
Iterable
Iterator Iterable
Sized
Callable
Sequence Sized, Iterable, Container
MutableSequence Sequence
Set Sized, Iterable, Container
MutableSet Set
Mapping Sized, Iterable, Container
MutableMapping Mapping
MappingView Sized
KeysView MappingView, Set
ItemsView MappingView, Set
ValuesView MappingView
Collections

collections 프레임워크는 컨테이너 데이터 유형, deque(double-ended queue) 및 기본 Dictionary(defaultdict)로 구성되어 있다. deque는 앞이나 뒤에서 추가 및 검색이 가능한 데이터 구조다. Python 3 설명서에 따르면 defaultdict 컨테이너는 내장 Dictionary의 서브클래스로 "메소드 한 개를 재정의하고 쓰기 가능한 인스턴스 변수 한 개를 추가한다". 무엇보다도 이 컨테이너는 Dictionary처럼 작동한다. 또한 collections 프레임워크는 데이터 유형 팩토리 함수인 namedtuple()을 제공한다.

ABC 유형 계층 구조

Python 3에서는 숫자 클래스를 나타내는 ABC의 유형 계층 구조가 지원된다. 이러한 ABC는 numbers 모듈에 있으며, Number, Complex, Real, RationalIntegral을 포함한다. 그림 1에서는 숫자 계층 구조를 보여 준다. 이러한 계층 구조를 사용하여 고유한 숫자 유형 또는 다른 숫자 ABC를 구현할 수 있다.


그림 1. 숫자 계층 구조
숫자 계층 구조
숫자 타워

Python의 숫자 계층 구조는 Scheme 언어의 숫자 타워에서 영감을 받아 설계되었다.

새로운 모듈인 fractions은 숫자 ABC인 Rational을 구현한다. 이 모듈은 유리수 계산을 지원한다. dir(fractions.Fraction)을 사용해 보면 imag, real__complex__와 같은 속성이 있다는 것을 알 수 있다. 이는 숫자 타워에 따라 RationalsComplex에서 상속받는 Reals에서 상속받기 때문이다.




위로


예외 발생 및 처리하기

Python 3에서는 통사적 모호성 문제를 처리하기 위해 except 절이 변경되었다. Python 버전 2.5에서는 다음과 같은 try . . . except 구문을 사용한다.

>>>try:
...    x = float('not a number')
... except (ValueError, NameError):
...    print "can't convert type"

위 코드는 다음과 같이 잘못 작성될 수 있다.

>>> try:
...    x = float('not a number')
... except ValueError, NameError:
...    print "can't convert type"

위 코드의 문제점은 ValueError가 발생하면 인터프리터가 해당 예외 오브젝트를 NameError라는 이름에 바인딩하기 때문에 ValueError 예외가 처리되지 않는다는 것이다. 다음 예제에서 이를 확인할 수 있다.

>>> try:
...    x = float('blah')
... except ValueError, NameError:
...    print "NameError is ", NameError
...
NameError is invalid literal for float(): not a number

따라서 예외 오브젝트를 다른 이름에 바인딩하려는 경우에는 쉼표(,)를 키워드 as로 대체하여 모호성을 제거해야 한다. 여러 개의 예외를 처리하려면 괄호(())를 사용해야 한다. Listing 5의 코드에서는 Python 3에서 유효한 두 가지 예제를 보여 준다.


Listing 5. Python 3의 예외 처리

# bind ValueError object to local name ex
try:
    x = float('blah')
except ValueError as ex:
    print("value exception occurred ", ex)
 
# catch two different exceptions simultaneously
try:
    x = float('blah')
except (ValueError, NameError):
    print("caught both types of exceptions")

예외 처리 방법 중 하나인 묵시적 또는 명시적 예외 체인도 변경되었다. Listing 6에서는 묵시적 예외 체인의 예를 보여 준다.


Listing 6. Python 3의 묵시적 예외 체인

def divide(a, b):
    try:
        print(a/b)
    except Exception as exc:
        def log(exc):
        fid = open('logfile.txt') # missing 'w'
        print(exc, file=fid)
        fid.close()

        log(exc)

divide(1,0)

divide() 메소드에서 0으로 나누어지면서 ZeroDivisionError 예외가 발생한다. 그리고 예외 절의 중첩된 log() 메소드 내에서 print(exc, file=fid)가 파일에 쓰기를 시도하지만 해당 파일이 쓰기를 위해 열려 있지 않다. 이 경우 Python 3에서는 Listing 7과 같은 예외가 발생한다.


Listing 7. 묵시적으로 연결된 예외에 대한 추적 기록 예제

Traceback (most recent call last):
  File "chainExceptionExample1.py", line 3, in divide
  print(a/b)
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "chainExceptionExample1.py", line 12, in <module>
    divide(1,0)
  File "chainExceptionExample1.py", line 10, in divide
    log(exc)
  File "chainExceptionExample1.py", line 7, in log
    print(exc, file=fid)
  File "/opt/python3.0/lib/python3.0/io.py", line 1492, in write
    self.buffer.write(b)
  File "/opt/python3.0/lib/python3.0/io.py", line 696, in write
    self._unsupported("write")
  File "/opt/python3.0/lib/python3.0/io.py", line 322, in _unsupported
    (self.__class__.__name__, name))
io.UnsupportedOperation: BufferedReader.write() not supported

두 예외가 모두 처리되었다는 것을 알 수 있다. 이전 버전의 Python에서는 ZeroDivisionError가 유실되었다. 그렇다면 어떻게 해서 두 예외가 모두 처리된 것일까? 이제부터는 모든 예외 오브젝트에 ZeroDivisionError과 같은 __context__ 속성이 있다. 이 경우에는 발생한 IOError__context__ 속성에 ZeroDivisionError가 "유지된다".

__context__ 외에도 예외 오브젝트에는 항상 None으로 초기화되는 __cause__ 속성도 있다. 이 속성은 예외의 원인을 명시적으로 기록하기 위해 사용된다. __cause__ 속성은 다음과 같은 구문으로 설정된다.

>>> raise EXCEPTION from CAUSE

이 구문은 다음과 같은 코드와 동일하다.

>>>exception = EXCEPTION
>>>exception.__cause__ = CAUSE
>>>raise exception

그러나 첫 번째 코드가 훨씬 보기 좋다. Listing 8은 명시적 예외 체인을 보여 주는 예제이다.


Listing 8. Python 3의 명시적 예외 체인

class CustomError(Exception): 
    pass

try:
    fid = open("aFile.txt") # missing 'w' again
    print("blah blah blah", file=fid)
except IOError as exc:
    raise CustomError('something went wrong') from exc

이전 예제에서는 파일이 쓰기를 위해 열려 있지 않았기 때문에 print() 함수에서 예외가 발생한다. Listing 9에서는 추적 기록을 보여 준다.


Listing 9. 예외 추적 기록

Traceback (most recent call last):
  File "chainExceptionExample2.py", line 5, in <module>
    fid = open("aFile.txt")
  File "/opt/python3.0/lib/python3.0/io.py", line 278, in __new__
    return open(*args, **kwargs)
  File "/opt/python3.0/lib/python3.0/io.py", line 222, in open
    closefd)
 File "/opt/python3.0/lib/python3.0/io.py", line 615, in __init__
    _fileio._FileIO.__init__(self, name, mode, closefd)
IOError: [Errno 2] No such file or directory: 'aFile.txt'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "chainExceptionExample2.py", line 8, in <modulei>
  raise CustomError('something went wrong') from exc
__main__.CustomError: something went wrong

추적 기록을 보면 "The above exception was the direct cause of the following exception" 행 뒤에 CustomError, "something went wrong"을 발생시킨 다른 추적 기록이 있다는 것을 알 수 있다.

마지막으로, 예외 오브젝트에 추가된 또 다른 속성은 __traceback__ 속성이다. 발생한 예외에 __traceback__ 속성이 없으면 새로운 추적 기록이 설정된다. 다음은 간단한 예제이다.

from traceback import format_tb

try:
    1/0
except Exception as exc:
    print(format_tb(exc.__traceback__)[0])

format_tb는 하나의 예외만 있는 목록을 리턴한다.

with 명령문

Python 버전 2.6부터는 with가 키워드이므로 더 이상 __future__를 통해 이 기능을 가져오지 않아도 된다.




위로


정수 리터럴 지원 및 구문

Python에서는 8진, 10진 및 16진와 같은 다양한 기수를 사용하는 정수의 문자열 리터럴이 지원되며 이제는 2진도 추가되었다. 8진 리터럴의 표현이 변경되었다. 이제부터 8진 리터럴은 0o 또는 0O(즉, 0 뒤에 대문자 또는 소문자 o가 있는) 접두부와 평가될 리터럴로 표현된다. 예를 들어, 8진수 13 또는 10진수 11은 다음과 같이 표현된다.

>>>0o13
11

새로운 2진 리터럴 표현에는 0b 또는 0B(즉, 0 뒤에 대문자 또는 소문자 b가 있는) 접두부가 추가된다. 10진수 21은 다음과 같은 2진수로 표현된다.

>>>0b010101
21

oct()hex() 메소드가 제거되었다.




위로


함수 주석

함수 주석은 컴파일 시간에 매개변수와 같은 함수의 부분을 표현식에 연결한다. 함수 주석 자체는 아무런 의미도 가지고 있지 않다. 즉, 써드파티 라이브러리에서 처리하지 않는 한 함수 주석은 처리되지 않는다. 함수 주석의 목적은 함수의 매개변수나 리턴값에 대한 주석 처리 방법을 표준화하는 것이다. 함수 주석의 구문은 다음과 같다.

def methodName(param1: expression1, ..., paramN: expressionN)->ExpressionForReturnType:
    ...

다음 예제에서는 함수의 매개변수에 대한 주석을 보여 준다.

def execute(program:"name of program to be executed", error:"if something goes wrong"):
    ...

다음 예제에서는 함수의 리턴값에 대한 주석을 달았다. 이렇게 하면 함수의 리턴 유형을 쉽게 확인할 수 있다.

def getName() -> "isString":
     ...

함수 주석에 대한 전체 문법은 PEP 3107(참고자료에서 제공하는 링크 참조)에서 볼 수 있다.




위로


결론

Easter egg

필자는 antigravity를 Python 3에 추가된 최고의 모듈이라고 생각한다. Python을 시작한 후 명령행에 import antigravity를 입력해 보면 절대 실망하지 않을 것이다.

Python 3의 최종 릴리스는 2008년 12월에 발표되었다. 그때 이후로 필자는 몇몇 블로그를 둘러보면서 사람들이 이전 버전과의 비호환성 문제에 어떻게 대응하고 있는지 살펴볼 수 있었다. 필자가 살펴본 블로그에서는 공식적인 의견의 일치를 기대하기도 어려웠을 뿐만 아니라 상반된 의견이 극명하게 갈려 있었다. Linux® 개발 커뮤니티의 일부 회원들은 실제로 이식해야 하는 코드의 양이 너무 많아서 버전 3으로 이주하지 않을 것으로 보인다. 그에 반해 많은 웹 개발자들은 변경된 유니코드 지원이라는 한 가지 장점 때문에라도 이주하게 될 것이다.

새 버전으로 이주하지 않을거라면 그 전에 PEP와 개발 메일링 목록을 충분히 살펴본 후 새 버전으로의 이식 여부를 결정해도 늦지 않을 것이다. PEP에서는 구현에 대한 설명뿐 아니라 변경하게 된 합리적인 이유와 그로 인해 얻게 되는 장점에 대한 설명도 볼 수 있다. 이러한 변경 사항은 심도 깊은 논의와 검토를 거쳐서 이루어진 것이다. 이 연재 기사에서는 일반 Python 프로그래머가 모든 PEP를 살펴보지 않고도 변경된 사항을 빠르게 파악할 수 있도록 도와주기 위해 중요한 주제만을 선정해서 다루었다.



참고자료

교육

제품 및 기술 얻기


필자소개

author photo - cesar otero

Cesar Otero는 프리랜서로 활동하는 Java 및 Python 컨설턴트이다. 전자 공학을 전공했으며 수학을 부전공으로 이수했다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us





위로


IBM, IBM 로고, ibm.com, DB2, developerWorks, Lotus, Rational, Tivoli 및 WebSphere는 미국 또는 기타 국가에서 사용되는 International Business Machines Corporation의 상표 또는 등록 상표이다. 이와 함께 기타 IBM 상표가 기재된 용어가 상표 기호(® 또는 ™)와 함께 이 정보에 처음 표시된 경우, 이와 같은 기호는 이 정보를 발행할 때 미국에서 IBM이 소유한 등록 상표 또는 일반 법적 상표이다. 또한 이러한 상표는 기타 국가에서 등록 상표 또는 일반 법적 상표이다. 현재 IBM 상표 목록은 웹 "저작권 및 상표 정보"에 있다. Java 및 모든 Java 관련 상표는 미국 또는 기타 국가에서 사용되는 Sun Microsystems, Inc.의 상표이다. Linux는 미국 또는 기타 국가에서 사용되는 Linus Torvalds의 상표이다. 기타 회사, 제품, 및 서비스명은 다른 상표나 서비스 마크일 수 있습니다.

developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의