메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

Clojure와 동시성

Clojure의 네 가지 동시성 모델에 대한 정보

Michael Galpin, 개발자, eBay
Michael Galpin 사진
Michael Galpin은 1998년부터 전문적으로 자바 소프트웨어를 개발하고 있으며 이베이에서 근무중이다. 캘리포니아 공대에서 수학을 전공하였다.

요약:  Clojure 프로그래밍 언어는 최근에 많은 관심을 받고 있습니다. 하지만, 이런 관심은 이 프로그래밍 언어가 현대적인 Lisp 언어라거나 Java™ 가상 머신 상에서 작동한다는 점과 같이 어떤 분명한 이유가 있어서는 아닙니다. Clojure 프로그래밍 언어에 사람들의 시선을 끄는 특징은 바로 이 언어가 지닌 동시성입니다. Clojure는 아마 기본적으로 STM(Software Transactional Memory) 모델을 지원하는 것으로 가장 잘 알려져 있을 것입니다. 하지만, STM이 모든 동시성 문제에 항상 최선의 솔루션인 것은 아닙니다. Clojure에는 에이전트 및 아톰 형태의 다른 패러다임에 대한 지원이 포함됩니다. 이 기사에서는 Clojure가 제공하는 각각의 동시성 접근 방식과 각 접근 방식이 언제 가장 적절한지 살펴봅니다.

원문 게재일:  2010 년 9 월 14 일 번역 게재일:   2010 년 12 월 28 일
난이도:  중급 원문:  보기 PDF:  A4 and Letter (43KB | 10 pages)Get Adobe® Reader®
페이지뷰:  3435 회
의견:  


시작

이 기사에서는 Clojure 프로그래밍 언어와 이 언어의 동시성에 대해 살펴볼 것이다. 본 기사에서 Clojure에 대한 개론을 설명하려는 것은 아니므로, Clojure에 대해 어느 정도 알고 있는 것으로 가정하고 서술한다. 예제를 실행하려면 Clojure 1.1이 필요하며, 따라서 Java 1.5 이상을 사용해야 한다. 이 기사를 작성하면서 사용한 버전은 Java 1.6.0_20이었다. 이런 도구에 대한 링크는 참고자료를 참조한다. 아래의 다운로드 표에서 이 기사에 사용되는 소스 코드를 다운로드할 수 있다.


공용체의 동시성 상태

소프트웨어 개발자들은 지난 수년에 걸쳐 어떻게 동시 프로그래밍이 사실상의 프로그래밍 방식이 되어갈 것인지 수도 없이 많이 들어왔다. 이렇게 바뀌게 되는 가장 큰 이유는 컴퓨터에 탑재되는 프로세서 수는 증가했지만 컴퓨터 프로세서의 속도는 비슷해졌기 때문이다. 사실, 이렇듯 칩당 프로세서 수의 증가 때문에 무어의 법칙은 계속 유효하게 적용되었다. 이 내용은 Wikipedia(링크는 참고자료 참조)에 다음과 같이 잘 요약되어 있다.

"무어의 법칙에서 허용되는 이득을 완벽히 활용하기 위해, 최근에는 병렬 계산이 필수가 되었다. 수년 동안 프로세서 메이커들이 클록 속도와 명령 레벨 병렬 처리 능력을 꾸준히 높인 결과, 아무런 수정 없이 단일 스레드 코드가 최신 프로세서에서 더 빨리 실행된다. 프로세서 메이커들은 CPU 전력 분산을 관리하기 위해 다중 코어 칩 디자인을 장려하고, 하드웨어를 최대한 활용하려면 소프트웨어를 다중 스레드 또는 다중 프로세스 방식으로 작성해야 한다."

위 단락의 내용을 보고 이 기사를 써야겠다는 생각이 많이 들었다. 그러나 이런 종류의 미사여구가 지금껏 수년간 횡행했지만, 아직도 많은 개발자들은 단일 스레드 코드를 이용해 순조롭게 프로그래밍하고 있다. 이에 대한 한 가지 큰 이유는 다름아닌 인터넷의 힘이다. 새 애플리케이션 중 상당수가 웹 애플리케이션이다. 서버 측 웹 애플리케이션 개발은 주로 단일 스레드 프로그래밍이다. 웹 서버는 서버의 많은 코어를 이용해 사용자로부터의 수많은 동시 요청을 처리할 수 있지만, 단일 스레드 코드로 이런 각각의 요청을 처리할 수 있는 경우가 많다. 이는 좋은 일이며, 웹 애플리케이션이 성공을 거두고 있는 많은 이유 중 하나다. 따라서 다수의 개발자에게는 사용자의 랩탑 및 데스크탑 컴퓨터에 나타나는 그런 코어가 전부 활동을 시작하는 것은 아니다.

웹의 성공이 동시 프로그래밍의 발생을 저해하는 유일한 이유는 아니다. 실은, 웹 애플리케이션 개발과 그 역사를 살펴보면 개발자 입장에서 웹 애플리케이션 개발이 얼마나 쉬워졌는지 바로 알 수 있다. PHP 및 JSP에서 Ruby on Rails에 이르기까지, 웹 개발이 점점 더 쉬워져 개발자가 웹에서 더 많은 일을 하고 더욱 놀라운 애플리케이션을 개발할 수 있었다. 이를 동시 프로그래밍과 대조해보자. 가장 인기 있는 프로그래밍 언어(예: C++ 및 Java)에서는 동시 프로그래밍의 구문(스레드, 잠금)이 수십 년간 크게 변하지 않았다. 동시 프로그래밍은 항상 어려웠고 앞으로도 계속 그럴 것이다. 따라서 개발자들이 동시 프로그래밍을 피하는 것이다. 어느덧 동시 프로그래밍이라는 것이 단순하지 않은 동시 프로그래밍 작업을 할 때면 회사 내에서 누구든 그 실력을 믿어 의심치 않는 실력자 한두 명쯤은 있어야 안심이 되는 상황이 되었다.

이런 사정 때문에 새롭고 더욱 세련된 프로그래밍 언어가 등장하게 된 것이며, 그 훌륭한 예가 바로 Clojure이다. Clojure는 하위 레벨에 동시성이 빌드되어 있다. 그래서 개발자가 스레드와 잠금을 직접 다루지 않아도 되므로, 사용하기에 더 간단하고 문제도 적은 모델을 얻게 된다. 따라서 애플리케이션 논리에 다시 집중할 수 있고 시스템을 갑자기 정지시키는 교착 상태가 발생하지 않을까 너무 걱정하지 않아도 된다. Clojure에 빌드된 동시성 구문을 살펴보자.


Clojure의 동시성 구문

앞서 언급한 바와 같이, 가장 인기 있는 프로그래밍 언어에서는 스레드잠금이라는 매우 기본적인 동시성 기능을 제공한다. 예를 들어, Java 5 및 6에서는 동시성을 위해 다수의 새 유틸리티 API를 도입했지만, 이들 대부분은 스레드 풀 및 다양한 유형의 잠금과 같이 스레드와 잠금을 바탕으로 빌드된 유틸리티이거나 동시성/성능 특성이 더 나은 데이터 구조였다. 동시 프로그램의 디자인 방법에 대한 기초에는 아무런 변화가 없었다. 그래서 여전히 똑같은 문제를 해결해야 하고, 그에 대한 해결책도 똑같이 취약하다. 단지 상용구 코드를 덜 작성해도 될 뿐이다.

Clojure는 모든 면에서 기본적으로 다르다. Clojure에서는 일반적인 기본 기능인 스레드 및 잠금 기능을 제공하지 않는다. 그 대신, 스레드 또는 잠금에 대한 언급이 전혀 없는 완전히 다른 동시 프로그래밍 모델을 얻는다. 여기서 말하는 모델은 복수의 의미로 사용된다. Clojure에는 네 가지 다른 동시성 모델이 있다. 이들 각 모델은 스레드와 잠금 상위에 있는 추상화로 간주할 수 있다. 가장 간단한 var부터 시작해서 이들 각각의 동시성 모델을 살펴보자.

스레드 로컬 var

가장 간단한 유형의 Clojure 동시성 모델이 바로 var이다. Var는 변수와 변수 값의 선언일 뿐이다. 목록 1은 Clojure에서 var를 사용하는 간단한 예제를 나타낸 것이다.


목록 1. Clojure var

1:1 user=> (defstruct item :title :current-price)
#'user/item
1:2 user=> (defstruct bid :user :amount)
#'user/bid
1:3 user=> (def history ())
#'user/history
1:4 user=> (def droid (struct item "Droid X" 0))
#'user/droid
1:5 user=> (defn place-offer [offer] 
  (binding [history (cons offer history) 
  droid (assoc droid :current-price (get offer :amount))] 
    (println droid history)))
#'user/place-offer
1:9 user=> (place-offer {:user "Anthony" :amount 10})
{:title Droid X, :current-price 10} ({:user Anthony, :amount 10})
nil
1:17 user=>  (println droid) ;there should be no change
{:title Droid X, :current-price 0}
nil

목록 1에서 첫 번째로 수행하는 작업은 데이터 구조의 쌍 itembid를 선언하는 것이다. 그 다음, 그냥 빈 목록인 history라는 var를 작성한 다음, 어떤 항목을 나타내는 droid라는 var를 작성한다. 그리고 place-offer라는 함수를 작성한다. 이 함수는 입찰 기회를 잡아 droidcurrent-price를 변경하고 history에 해당 입찰을 추가한다. 이를 위해 바인딩 매크로를 사용했다. 이 매크로는 var의 스레드 로컬 값을 변경한다. 그래서 place-offer 함수의 실행 범위에서 droidhistory가 가리키는 값이 서로 다를 것이다. 하지만, 실행 범위를 벗어나서는 값이 변경되지 않는다. Clojure에서는 기본적으로 모든 것이 불변이다. var를 바인딩하면 스레드 로컬 범위에 있는 것들을 간단히 변경할 수 있다. 다른 스레드에서 이 값을 읽을 경우 아무런 변경도 관찰하지 못할 것이다. 분리된 태스크를 실행하는 과정의 일부로서 상태를 변화시킬 필요가 있는 상황에서는 이렇게 하는 것이 간단한 방법이다. 다른 스레드에서 상태를 보게 되는 방식으로 상태를 변경하려는 경우에는 Clojure의 아톰을 사용하고 싶을 수 있다.

간단한 동기 아톰

아톰은 상태가 바뀔 수 있는 변수이다. 이 변수는 사용법이 매우 간단하고 완전히 동기적이다. 다시 말해, 아톰의 값을 바꾸는 함수를 호출하면 그 함수가 리턴할 때 모든 스레드에서 이 새 값을 보게 될 것임을 확신할 수 있다. 목록 2는 아톰을 사용한 예제를 나타낸 것이다.


목록 2. Clojure 아톰

1:21 user=> (def droid (atom (struct item "Droid X" 0)))
#'user/droid
1:22 user=> (def history (atom ()))
#'user/history
1:28 user=> (defn place-offer [offer] 
  (reset! droid (assoc @droid :current-price (get offer :amount))))
#'user/place-offer
1:33 user=> (place-offer {:user "Anthony" :amount 10})
{:title "Droid X", :current-price 10}
1:36 user=> (println @droid)
{:title Droid X, :current-price 10}
nil

이 코드는 목록 1에서 보여준 이전 예제를 바탕으로 한 것이다. 이번에는 atom 함수를 사용하여 droidhistory를 아톰으로 다시 정의한다. atom 함수를 사용하면 초기값 주위의 랩퍼인 atom 오브젝트를 얻는다. 새 place-offer 함수에서 reset! 함수를 사용하여 droid의 값을 변경한다. @ 기호와 함께 droidhistory를 제자리에 추가했음을 알 수 있다. 이렇게 하면 Clojure에서 포인터를 참조 해제하게 되어 사용자에게 실제 값을 알려준다. 그 다음, 새 place-offer 함수를 호출하고 그 후에 droid를 인쇄하여 그 값이 실제로 바뀌었음을 알 수 있다. place-offer에서는 하나의 atom, 즉 droid만 변경했다. history atom은 변경하지 않았다. 이 아톰에서도 reset!을 확실히 사용할 수 있었다. 하지만, 두 가지 변경 내용을 모두 볼 수 있을지에 대한 보장은 없다. 즉, 한 스레드가 droid의 값이 바뀌는 것을 보는 것은 가능하겠지만, history의 값이 바뀌는 것을 볼 수는 없다. 그런 종류의 일관성을 얻으려면 조정 작업이 필요하다. 또한, 트랜잭션과 ref도 필요하다.

트랜잭션 ref

Clojure의 ref는 가장 강력한 동시성을 제공한다. 이것은 Clojure의 STM(Software Transactional Memory) 구현이다. Ref는 atom과 비슷하다. Atom과 비교해볼 때, 코드 행을 하나만 더 추가하면 되는 경우가 많다. 그 주요 이점이 바로 조정이다. Ref를 사용하면 단일 트랜잭션에서 여러 오브젝트의 상태를 변경할 수 있다. 이 트랜잭션은 원자적이고 일관되며 격리될 것이다(ACID의 ACI - 이것은 전부 메모리에 있으므로 영속성이 없음). 격리된 특성은 어떤 관찰자든 트랜잭션의 모든 변경 내용을 보거나 전혀 아무 것도 보지 못할 것임을 암시한다. 그런데 atom에서는 그렇지 않다. 목록 3은 ref의 사용 예제를 나타낸 것이다.


목록 3. Clojure ref

1:90 user=> (def droid (ref (struct item "Droid X" 0)))
#'user/droid
1:91 user=> (def history (ref ()))
#'user/history
1:92 user=> (defn place-offer [offer] 
  (dosync
    (ref-set droid (assoc @droid :current-price (get offer :amount)))
    (ref-set history (cons offer @history))    
    ))
1:97 user=> (place-offer {:user "Tony" :amount 22})
({:user "Tony", :amount 22})
1:99 user=> (println @droid @history)
{:title Droid X, :current-price 22} ({:user Tony, :amount 22})
nil

목록 3에 있는 코드는 목록 2에 있는 코드와 매우 유사하다. Ref는 atom에서 사용한 것과 같은 랩퍼 패턴을 따른다. place-offer 함수 구현은 dosync에 대한 호출로 시작된다. 이 함수는 트랜잭션을 랩핑한다. 그래서 필자가 앞서 언급한 조정 기능을 제공한다. 이 함수를 사용하면 droidhistory를 모두 변경할 수 있고, 데이터의 더티 읽기가 없을 것임을 알 수 있다. 아톰과 꼭 마찬가지로, 이 함수를 실행한 후 값을 참조 해제하고 인쇄하여 값이 바뀌었음을 확인할 수 있다.

여기서 STM이 정확히 어떻게 작동하는지 궁금할 것이다. 한 스레드가 22의 값으로 place-offer 함수를 호출하는 다른 스레드와 동시에 25의 값으로 이 함수를 호출하면 어떻게 될까? Clojure는 트랜잭션 중간에 값이 바뀌지 않도록 할 것이다. 따라서 트랜잭션이 dosync 블록의 끝에 도달했을 때 STM에서 다른 트랜잭션이 현재 트랜잭션의 시작 이후에 완료했음을 확인하는 경우, 현재 트랜잭션이 롤백되고 다시 실행된다. 함수가 여러 번 실행될 수 있으므로, 이는 순수 함수, 즉 부작용이 없는 함수만 트랜잭션의 일부로 사용된다는 점을 매우 중요하게 만든다. Clojure는 매우 높은 성능의 지속적 데이터 구조를 사용하여 이런 종류의 트랜잭션/롤백을 효율적으로 수행한다.

새 오퍼의 양이 이전 오퍼보다 많은 경우에만 새 오퍼를 선택할 수 있는지 확인하려면 ref 선언에 유효성 검증 함수를 추가하기만 하면 된다. 이 경우, 트랜잭션 중에 변경 사항이 발견된 경우 트랜잭션이 롤백되고 다시 시작된다. 유효성 검증 확인에 실패하는 경우에는 트랜잭션이 중단된다.

Clojure의 STM을 사용하는 데 있어 관건은 dosync 함수 내부에 있는 것을 랩핑하는 것이다. 눈치 빠른 관찰자라면 이것이 동기화된 블록 내부 또는 잠금 획득/해제 플로우 내부의 랩핑 코드와 매우 유사하다는 점을 지적할 수도 있다. 물론, 그런 전통적인 동시성 메커니즘은 어렵기로 악명이 높다. Clojure는 더 간단하다. 상태를 변경하려는 경우에는 dosync를 사용해야 한다. dosync 외부에 있는 ref의 상태를 변경할 수는 없다. 더 나아가, Clojure의 트랜잭션을 작성할 수 있다. dosync 블록 내부에서 역시 dosync 블록을 가진 다른 함수를 호출할 수 있다. 함수들이 공유한 잠금이 어떤 종류인지 애써 알아낼 필요가 없다. 교착 상태를 걱정할 필요도 없다. Ref와 atom은 둘 다 동기 함수이다. 상태를 동기적으로 변경할 필요가 없다면, 에이전트가 몇 가지 이점을 제공할 수 있다.

사용하기 쉬운 비동기 에이전트

상태를 변경해야 할 때가 많지만, 상태가 변경되기를 기다리거나 여러 스레드에서 변경 작업을 할 수 있는 경우 변경 순서에 대해 신경을 쓸 필요는 없다. 이는 공통 패턴으로서, Clojure는 이 문제를 해결하기 위한 프로그래밍 모델인 에이전트를 제공한다. 목록 4는 에이전트의 사용 예제를 나타낸 것이다.


목록 4. Clojure 에이전트

1:100 user=> (def history (agent ()))
#'user/history
1:101 user=> (def droid (agent (struct item "Droid X" 0)))
#'user/droid
nil
1:107 user=> (defn place-offer [offer]
  (send droid #(assoc % :current-price (get offer :amount))))
1:110 user=>  (place-offer {:user "Tony" :amount 33})
#<Agent@396477d9: {:title "Droid X", :current-price 0}>
1:111 user=> (await droid)
nil
1:112 user=> (println @droid)
{:title Droid X, :current-price 33}
nil
            

다시 한 번 agent 함수를 사용하여 droidhistory의 초기값을 랩핑하는 것부터 시작해보자. 그런 다음, place-offer의 새 버전을 정의한다. 이번에는 에이전트 뒤에서 값을 직접 변경할 수 없다. 그 대신, send 함수를 사용한다. 이 함수는 에이전트와 다른 함수를 매개변수로 취한다. 두 번째 함수는 에이전트의 값에 적용될 다른 함수이다. 결과 값은 에이전트의 값을 바꾸는 데 사용될 것이다. 목록 4에서는 익명 함수를 사용하여 send로 전달했다. Atom과 ref 모두 이런 종류의 시맨틱도 지원한다는 점에 유의해야 하며, 여기서 상태를 업데이트하기 위해 함수가 전달 및 사용된다. 그 다음, await 함수를 사용했다는 점에 주목하자. 이 함수는 에이전트가 자신에게 전송된 함수를 실행할 때까지 스레드를 차단하는 역할을 한다. 원하는 변경 내용이 실제로 적용되었는지 확인하는 것이 좋다. 그렇지 않으면, 에이전트의 비동기 특성으로 인해 전송된 함수가 적용되었는지 확신할 수 없을 것이다.


결론

본 기사에서는 Clojure의 각 동시성 모델을 보여주었다. 매우 다양한 동시성 문제가 있지만, 그 중 다수는 Clojure의 모델 중 하나에 쉽사리 맵핑된다. 그런 경우, Clojure의 여러 가지 기능을 이용하면 문제를 훨씬 쉽게 해결할 수 있을 것이다. 문제점이 잘 맵핑되지 않을 때는 Clojure가 지닌 Java와의 상호 운용성을 활용하고 Java의 스레드와 잠금을 대신 사용할 수 있다. 따라서 동시성에 크게 의존할 태스크를 수행할 때면 항상 Clojure라는 언어가 있음을 염두에 두어야 한다.



다운로드 하십시오

설명이름크기다운로드 방식
Article source codeauctions.clj.zip1KBHTTP

다운로드 방식에 대한 정보


참고자료

교육

  • Wikipedia에서 무어의 법칙(Moore's law)에 대해 알아보자.

  • Clojure 프로그래밍 언어(Michael Galpin, developerWorks, 2009년 9월): 이 기사에서 Clojure에 대한 개론을 습득할 수 있다.

  • Clojure 커뮤니티에서 작성하여 수많은 Clojure 프로젝트에서 사용되는 필수 라이브러리에 대해서는 clojure-contrib를 살펴보자. 이 라이브러리에는 기본적으로 Eclipse 플러그인이 포함되어 있다.

  • Clojure 초보자에서 전문가로 발전하는 최선의 길은 Stuart Halloway의 Programming Clojure를 숙독하는 것이다.

  • Beginning Haskell(David Mertz, developerWorks, 2001년 12월): 다른 실용적 언어에 대한 소개는 이 튜토리얼을 확인해본다.

  • developerWorks 웹 개발 사이트에서는 다양한 웹 기반 솔루션을 다루는 기사를 전문적으로 게시한다.

제품 및 기술 얻기

  • Clojure 사이트를 방문하면 Clojure를 다운로드하고 튜토리얼을 읽고 참조 문서에 액세스할 수 있다.

  • Java SDK를 얻을 수 있다. 이 기사에서는 JDK 1.6.0_17을 사용했다.

  • IBM 제품 평가 버전을 다운로드하고 DB2®, Lotus®, Rational®, Tivoli® 및 WebSphere®에서 실습용 애플리케이션 개발 도구와 미들웨어 제품을 구할 수 있다.

토론

필자소개

Michael Galpin 사진

Michael Galpin은 1998년부터 전문적으로 자바 소프트웨어를 개발하고 있으며 이베이에서 근무중이다. 캘리포니아 공대에서 수학을 전공하였다.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=웹 개발, 자바
ArticleID=604834
ArticleTitle=Clojure와 동시성
publish-date=09142010
author1-email=mike.sr@gmail.com
author1-email-cc=dwxed@us.ibm.com

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.