메인 컨텐츠로 가기

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

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

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

  • 닫기 [x]

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

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

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

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

  • 닫기 [x]

함수형 사고: 함수적으로 사고하기, Part 2

함수형 프로그래밍 및 제어 알아보기

Neal Ford, 소프트웨어 아키텍트, IBM
Neal Ford사진
Neal Ford는 글로벌 IT 컨설팅 업체인 ThoughtWorks의 소프트웨어 아키텍트이자 Meme Wrangler이다. 애플리케이션, 교육용 자료, 매거진 기사 및 비디오/DVD 프리젠테이션을 설계 및 개발하며 다양한 기술과 관련된 서적의 저자 또는 편집자이기도 하다. 최근에 출판된 책으로는 The Productive Programmer가 있다. 대규모 엔터프라이즈 애플리케이션의 설계 및 빌드에 많은 관심을 가지고 있는 그는 전세계의 개발자 컨퍼런스에서 국제적으로 인정 받고 있는 연사로도 활동하고 있다. 그의 웹 사이트를 살펴보자.

요약:  함수형 언어 및 프레임워크를 통해 반복, 동시성 및 상태 등과 같은 일상적인 코딩 세부사항을 런타임 제어할 수 있습니다. 하지만 이는 필요할 때 제어를 도로 찾을 수 없음을 의미하지는 않습니다. 함수적으로 사고하는 것의 중요한 측면 중 하나는 제어를 어느 정도로 그리고 어느 시점에 포기하려는지 인식하는 것입니다.

이 연재 자세히 보기

기사 게재일:  2011 년 5 월 31 일
난이도: 중급 원문:  보기 PDF:  A4 and Letter (42KB | 10 pages)Get Adobe® Reader®
페이지뷰:  1684 회
의견:  


이 시리즈의 정보

이 시리즈는 독자의 관점을 함수형 사고방식으로 새롭게 방향을 지정하여, 새로운 방식에서 공통적인 문제점을 살펴보고 일상적인 코딩을 개선하는 방법을 찾는 데 도움을 주는 것을 목표로 한다. 이는 함수형 프로그래밍 개념, Java™ 언어 내에서 함수형 프로그래밍을 허용하는 프레임워크, JVM에서 실행하는 함수형 프로그래밍 언어 및 언어 설계의 일부 미래 성향의 방향을 살펴본다. 이 시리즈는 Java 및 그 추상이 작업하는 방법을 알지만, 함수형 언어를 사용한 경험이 적거나 전혀 없는 개발자들에 적합하도록 맞춰져 있다.

이 시리즈의 첫 번째 기사에서 필자는 함수형 사고의 일부 특성을 논의하기 시작하여, 이러한 개념이 Java 및 더 함수형인 언어 모두에서 어떻게 나타나는지를 알려주었다. 이 기사에서는 최상급 함수, 최적화 및 클로저에 대해 논의하여 이러한 개념을 둘러보는 것을 계속할 것이다. 하지만 이번 기사의 기본 테마는 제어이다. 즉, 제어를 원하는 시점, 제어가 필요한 시점 그리고 제어를 풀어야 하는 시점을 논의한다.

최상급 함수 및 제어

Functional Java 라이브러리(참고자료 참조)를 사용하여 필자는 최근에 다음 목록 1과 같이 함수형 isFactor()factorsOf() 메소드로 숫자 분류자의 구현방식을 보여주었다.


목록 1. 숫자 분류자의 함수형 버전

import fj.F;
import fj.data.List;
import static fj.data.List.range;
import static fj.function.Integers.add;
import static java.lang.Math.round;
import static java.lang.Math.sqrt;

public class FNumberClassifier {

    public boolean isFactor(int number, int potential_factor) {
        return number % potential_factor == 0;
    }

    public List<Integer> factorsOf(final int number) {
        return range(1, number+1).filter(new F<Integer, Boolean>() {
            public Boolean f(final Integer i) {
                return number % i == 0;
            }
        });
    }

    public int sum(List<Integer> factors) {
        return factors.foldLeft(fj.function.Integers.add, 0);
    }

    public boolean isPerfect(int number) {
        return sum(factorsOf(number)) - number == number;
    }

    public boolean isAbundant(int number) {
        return sum(factorsOf(number)) - number > number;
    }

    public boolean isDeficiend(int number) {
        return sum(factorsOf(number)) - number < number;
    }
}    

isFactor()factorsOf() 메소드에서 필자는 프레임워크로 루핑 알고리즘의 제어를 이양한다 — 이제 이는 숫자의 범위에 대해 반복하는 최선의 방법을 결정한다. 프레임워크(또는 —Clojure 또는 Scala와 같은 함수형 언어를 선택하는 경우 — 언어)가 기본 구현 방식을 최적화할 수 있는 경우 독자는 자동으로 혜택을 누린다. 비록 처음에는 이 정도의 제어를 포기하는 데 주저할 수 있지만, 이는 프로그래밍 언어와 런타임 측면에서 일반적인 경향을 따른다는 것을 주목하자. 시간이 흐르면서 해당 개발자는 플랫폼이 더 효율적으로 처리할 수 있는 세부사항으로부터 더 추상화된다. 플랫폼이 이를 잊어버리도록 허용하기 때문에 필자는 JVM에서 메모리 관리에 대해 절대 걱정하지 않는다. 당연히 이로 인해 때로는 일이 더 어렵게 되기도 하지만, 일상적인 코딩에서 나타나는 이점을 생각해보면 훌륭한 거래 조건이다. 함수형 언어는 고차 함수 및 최상급 함수와 같이 추상 사다리를 한 칸 더 올라가도록 허용하고 이를 수행하는 방법보다는 코드가 수행하는 것이 무엇인지에 대해 집중하도록 구성한다.

Functional Java 프레임워크를 사용할 때에도 Java에서 이 스타일로 코딩하는 것은 골치거리이다. 왜냐하면 그 언어가 실제로 구문을 보유하지 않고 이에 대해 구성하기 때문이다. 수행하는 언어에서 함수형 코딩의 모습은 어떠한가?

Clojure에서 분류자

Clojure는 JVM을 위해 설계된 함수형 Lisp이다(참고자료 참조). 다음 목록 2와 같이 Clojure로 쓰인 숫자 분류자를 고려하자.


목록 2. 숫자 분류자의 Clojure 구현 방식

(ns nealford.perfectnumbers)
(use '[clojure.contrib.import-static :only (import-static)])
(import-static java.lang.Math sqrt)

(defn is-factor? [factor number]
  (= 0 (rem number factor)))

(defn factors [number] 
  (set (for [n (range 1 (inc number)) :when (is-factor? n number)] n)))

(defn sum-factors [number] 
    (reduce + (factors number)))

(defn perfect? [number]
  (= number (- (sum-factors number) number)))

(defn abundant? [number]
  (< number (- (sum-factors number) number)))

(defn deficient? [number]
  (> number (- (sum-factors number) number)))

목록 2의 대부분의 코드는 완강한 Lisp 개발자가 아닌 경우에도 따라하기에 매우 간편하다. — 특히 샅샅이 읽도록 배울 수 있는 경우에 그렇다. 예를 들어, is-factor? 메소드가 두 개의 매개변수를 취하여, numberfactor를 곱할 때 남은 수가 0과 같은지 묻는다. 마찬가지로 perfect?, abundant?deficient? 메소드가 해독하기에 간편할 것이다. 특히 목록 1에서 Java 구현 방식을 참조하는 경우에 그렇다.

sum-factors 메소드는 내장 reduce 메소드를 사용한다. sum-factors가 각 요소에서 첫 번째 매개변수로 제공되는 함수를 사용하여 한 번에 하나의 요소씩 목록을 줄인다(이 경우에 +). reduce 메소드는 몇 가지 언어 및 프레임워크에서 다른 모양으로 나타난다. 즉, 독자는 이를 foldLeft() 메소드로서 목록 1의 Functional Java 버전을 확인했다. factors 메소드는 숫자 목록을 리턴하므로, 필자는 한 번에 목록을 하나씩 처리하고 있으며, 각 요소를 누적된 합계로 더하며, 이는 reduce의 리턴 값이다. 고차 및 최상급 함수의 측면에서 사고하는 데 익숙해지면 코드에서 많은 노이즈를 줄일 수 있음(말장난을 의도함)을 확인할 수 있다.

factors 메소드는 기호의 무작위 콜렉션과 같이 보일 수 있다. 하지만, 목록 포괄성을 확인해보면 말이 된다. 이는 Clojure에서 강력한 목록 조작 기능 몇 가지 중 하나이다. 그 전처럼 factors를 샅샅이 이해하는 것이 가장 간편하다. 언어 용어를 충돌시켜 혼란에 빠지지 말자. Clojure에서 for 키워드는 for 루프를 의미하지 않는다. 오히려 이를 모든 필터링과 변환 구성의 할아버지 정도로 생각할 수 있다. 이 경우에 필자는 is-factor? 조건부(이는 필자가 이전에 목록 2 에서 정의한 대로 is-factor 메소드 — 최상급 함수의 막대한 사용에 주의한다)를 사용하여 1에서부터 (number + 1)까지 숫자 범위를 필터링하도록 요청하고 있으며, 일치하는 숫자를 리턴한다. 이 조작에서 나온 리턴은 필자의 필터 기준에 부합하는 숫자 목록이며, 여기에서 필자는 중복을 제거하는 세트로 강제한다.

비록 새 언어를 학습하는 것이 귀찮은 일이기는 하지만, 해당 기능을 이해할 때 함수형 언어에서 본전을 뽑을 만한 가치를 많이 얻게 된다.

최적화

함수형 스타일로 전환하는 이점 중 하나는 언어 또는 프레임워크로 제공되는 고차 함수 지원을 활용하는 기능이다. 하지만 이러한 제어를 포기하려는 경우에 시간은 어떠한가? 필자의 이전 예제에서 필자는 메모리 관리자의 내부 작업으로 반복 메커니즘의 내부 작동에 비유했다. 즉, 독자는 대부분의 경우에 이러한 세부사항에 대해 걱정하지 않게 된다. 하지만 때로는 최적화 및 유사한 수정의 경우에 이에 대해 고려한다.

필자가 "Thinking functionally, Part 1"에서 보여준 숫자 분류자의 두 가지 Java 버전에서 필자는 인수를 판별하는 코드를 최적화했다. 원래의 단순한 구현 방식은 모듈러스(%) 연산자를 사용했으며, 이는 인수인지 판별하기 위해 2에서부터 최대로 대상 숫자 자체까지 모든 숫자를 확인할 정도로 매우 비효율적이다. 인수가 쌍으로 나오는 것을 주목하여 해당 알고리즘을 최적화할 수 있다. 28의 인수를 찾고 있는 경우 예를 들어 2를 찾을 때, 14도 취할 수 있다. 인수를 쌍으로 수집할 수 있는 경우 최대로 대상 숫자의 제곱근까지만 인수를 확인해야 한다.

Java 버전에서 수행한 간편한 최적화는 Functional Java 버전에서는 불가능한 것처럼 보인다. 왜냐하면 필자가 반복 메커니즘의 구현 방식을 직접 제어하지 않기 때문이다. 하지만 함수적으로 사고하는 것을 학습하는 부분은 해당 제어 종류에 대한 개념을 포기하는 것이 필요하여, 독자가 다른 종류를 행사하도록 허용한다.

필자는 함수적으로 원래 문제점을 다시 서술할 수 있다. 즉, 1에서부터 number까지 인수를 모두 필터링하여, 필자의 isFactor() 조건부와 일치하는 해당 인수만 유지한다. 이는 목록 3에 구현되었다.


목록 3. isFactor() 메소드

public List<Integer> factorsOf(final int number) {
    return range(1, number+1).filter(new F<Integer, Boolean>() {
        public Boolean f(final Integer i) {
            return number % i == 0;
        }
    });
}

선언적인 관점에서 보면 세련되지만, 목록 3의 코드는 숫자를 모두 확인하기 때문에 매우 비효율적이다. 한 번 최적화를 이해하면(최대로 제곱근까지만 인수를 쌍으로 수집하는 것) 필자는 이와 같은 문제점을 다음과 같이 다시 서술할 수 있다.

  1. 1에서부터 숫자의 제곱근까지 대상 숫자의 인수를 모두 필터링한다.
  2. 대칭 인수를 얻기 위해 대상 숫자를 이러한 각 인수로 나누고 인수의 목록에 이를 더한다.

이 목표를 염두에 두고 필자는 목록 4와 같이 Functional Java 라이브러리를 사용하여 factorsOf() 메소드의 최적화된 버전을 쓸 수 있다.


목록 4. 최적화된 인수 찾기 메소드

public List<Integer> factorsOfOptimzied(final int number) {
    List<Integer> factors = 
        range(1, (int) round(sqrt(number)+1))
        .filter(new F<Integer, Boolean>() {
            public Boolean f(final Integer i) {
                return number % i == 0;
            }});
    return factors.append(factors.map(new F<Integer, Integer>() {
                                      public Integer f(final Integer i) {
                                          return number / i;
                                      }}))
                                      .nub();
}

목록 4의 코드는 Functional Java 프레임워크가 요청한 일부 특이한 구문을 통해 이전에 서술한 알고리즘을 기반으로 한다. 먼저, 필자는 1에서부터 대상 숫자의 제곱근 더하기 1까지의 숫자 범위를 취한다(모든 인수를 확실히 취하기 위해). 두 번째로 필자는 이전 버전과 같이 모듈러스 연산자의 사용에 따라 결과를 필터링하고 Functional Java 코드 블록으로 랩핑한다. 필자는 factors 변수에서 이러한 필터링된 목록을 저장한다. 네 번째로(샅샅이 읽기) 필자는 이 인수 목록을 취하고 map() 함수를 실행하며, 이는 각 요소에 대해 코드 블록을 실행하여 새 목록을 제작한다(각 요소를 새 값으로 맵핑). 필자의 인수 목록은 대상 숫자의 모든 인수가 최대로 그 수의 제곱근까지 들어있다. 즉, 대칭 인수를 취하기 위해 대상 숫자로 각각을 나누어야 하며, 이는 map() 메소드로 전송된 코드 블록이 수행하는 것이다. 다섯 번째로, 이제 필자는 대칭 인수의 목록을 보유했으니 원본 목록에 이를 추가한다. 마지막 단계로서, 필자는 Set이라기 보다는 List에서 인수를 유지하고 있다는 사실을 고려해야 한다. List 메소드는 이러한 조작의 유형에 편리하지만, 알고리즘의 부작용은 정수 제곱근이 나타날 때 중복 항목이다. 예를 들어, 대상 인수가 16이면, 4의 정수 근은 인수의 목록에서 두 번 나타나게 된다. 편리한 List 메소드를 계속 사용하기 위해 필자는 종료 시에 nub() 메소드만 호출해야 하며, 이는 중복을 모두 제거한다.

독자가 함수형 프로그래밍과 같은 고차 추상을 사용할 때 세부적인 구현 방식에 대한 지식을 대개 포기한다고 해서 이를 반드시 해야 하는 경우에 지저분해지지 않을 수 있음을 의미하지 않는다. Java 플랫폼은 대부분의 경우 하위 레벨 작업으로부터 보호해주지만, 독자가 결정한 경우 필요한 레벨로 파고 들 수 있다. 마찬가지로, 함수형 프로그래밍 구성에서 독자는 일반적으로 세부사항을 추상화하려 하여, 실제로 문제가 될 때 추상화하지 않는 시간을 절약해 준다.

필자가 지금까지 확인한 모든 Functional Java 코드에서 시각적으로 특히 눈에 띄는 것은 블록 구문이며, 이는 모조 코드 블록(pseudo-code-block), 클로저 유형 구성의 종류로 제네릭과 익명 내부 클래스를 사용한다. 클로저는 함수형 언어의 일반적인 기능 중 하나이다. 이 분야에서 매우 유용하게 된 요인은 무엇인가?


클로저의 매우 특별한 사항

클로저는 내재된 바인딩을 이 내부에서 참조된 모든 변수로 전달하는 함수이다. 다시 말해서, 함수(또는 메소드)는 참조하는 것 주변의 컨텍스트를 둘러싼다. 클로저는 함수형 언어 및 프레임워크에서 이식 가능한 실행 메커니즘으로 매우 자주 사용되며, 변환 코드로 map()과 같은 고차 함수로 전달된다. Functional Java는 일부 "실제" 클로저 작동을 모방하는 익명의 내부 클래스를 사용하지만, Java가 클로저에 대해 지원하지 않기 때문에 이를 끝까지 진행할 수는 없다. 하지만 이는 무엇을 의미할까?

목록 5에서는 클로저를 매우 특별한 것으로 만드는 예제를 보여준다. 이는 Groovy로 쓰였으며, 코드 블록 메커니즘을 통해 클로저를 지원한다.


목록 5. 클로저를 시연하는 Groovy 코드

def makeCounter() {
  def very_local_variable = 0
  return { return very_local_variable += 1 }
}

c1 = makeCounter()
c1()
c1()
c1()
c2 = makeCounter()

println "C1 = ${c1()}, C2 = ${c2()}"
// output: C1 = 4, C2 = 1

makeCounter() 메소드는 먼저 로컬 변수를 적절한 이름으로 정의한 다음에 해당 변수를 사용하는 코드 블록을 리턴한다. makeCounter() 메소드에 대한 리턴 유형이 값이 아니라 코드 블록임을 주목하자. 이러한 코드 블록은 로컬 변수의 값을 늘리고 이를 리턴하는 것만 수행한다. 필자는 이 코드에서 명시적 return 호출을 위치 지정했으며, 이는 둘 다 Groovy에서 선택적이지만, 해당 코드는 이 내용이 없으면 훨씬 더 모호하다!

makeCounter() 메소드를 연습하기 위해 필자는 코드 블록을 C1 변수로 지정한 다음에 이를 세 번 호출한다. 필자는 코드 블록을 실행하기 위해 Groovy의 신택틱 슈거(syntactic sugar)를 사용하는 중이며, 이는 코드 블록의 변수에 인접한 소괄호 세트를 위치 지정하는 것이다. 그 다음으로 필자는 makeCounter()를 다시 호출하여, 코드 블록의 새 인스턴스를 C2로 지정한다. 마지막으로 필자는 다시 C1C2와 함께 실행한다. 각 코드 블록이 very_local_variable의 개별 인스턴스를 계속 추적하는 것을 주목하자. 이것이 바로 컨텍스트 인클로즈하기가 의미하는 것이다. 로컬 변수가 메소드 내에서 정의될 지라도 코드 블록이 이를 참조하기 때문에 해당 변수로 바운드되어, 코드 블록 인스턴스가 존재하는 동안 이를 추적해야 함을 의미한다.

Java에서 동일한 작동과 가장 유사한 사항은 다음 목록 6에 나와 있다.


목록 6. Java에서 MakeCounter

public class Counter {
    private int varField;

    public Counter(int var) {
        varField = var;
    }

    public static Counter makeCounter() {
        return new Counter(0);
    }

    public int execute() {
        return ++varField;
    }
}  

Counter 클래스의 몇 가지 변형은 가능하지만, 독자는 여전히 상태를 관리하는 데 사로잡혀 있다. 이는 클로저의 사용이 함수형 사고의 전형적인 예가 되는 이유를 설명한다. 즉, 상태를 관리하기 위해 런타임을 허용하는 것이다. 필드 작성을 처리하도록 강제 실행하고 상태를 아이처럼 다루는 대신에 (멀티스레드로 된 환경에서 코드를 사용하는 것의 좋지 않은 예상을 포함하여) 언어 또는 프레임워크를 통해 독자에 맞는 상태를 보이지 않게 관리할 수 있다.

결과적으로 향후 Java 릴리스에서 클로저가 나올 것이다(이러한 논의는 다행히도 이 기사의 범위를 벗어남). Java에 나타나면 두 가지의 특권을 가질 것이다. 첫 번째로, 이는 구문을 개선하는 동시에 프레임워크 및 라이브러리 라이터의 기능을 엄청나게 간소화할 것이다. 두 번째로, 이는 JVM에서 실행하는 모든 언어로 클로저 지원에 하위 레벨 공통 분모를 제공할 것이다. 많은 JVM 언어가 클로저를 지원하지만, 이는 모두 자체적인 버전을 구현해야 하며, 복잡한 언어들 사이에 클로저를 전달하게 만든다. Java 언어가 하나의 형식을 정의한 경우, 모든 다른 언어는 이를 활용할 수 있었다.


결론

하위 레벨 세부사항에 대해 제어를 이양하는 것은 소프트웨어 개발 분야에서 일반적인 경향이다. 우리는 가비지 콜렉션, 메모리 관리 및 하드웨어 차이점에 대한 책임을 기꺼이 포기했다. 함수형 프로그래밍은 그 다음의 추상 도약을 표현한다. 즉, 반복, 동시성 및 상태와 같은 더 일상적인 세부사항을 가능한 한 많이 런타임으로 이양하는 것이다. 이는 필요한 경우 도로 찾을 수 없음을 의미하지 않는다. — 하지만 독자에 강제 실행되는 것이 아니라 독자가 원해야 한다.

다음 기사에서는 커링(currying)부분적 메소드 애플리케이션을 소개하여 Java 및 유사한 종류에서 함수형 프로그래밍 구성을 계속 탐색할 것이다.


참고자료

교육

제품 및 기술 얻기

토론

필자소개

Neal Ford사진

Neal Ford는 글로벌 IT 컨설팅 업체인 ThoughtWorks의 소프트웨어 아키텍트이자 Meme Wrangler이다. 애플리케이션, 교육용 자료, 매거진 기사 및 비디오/DVD 프리젠테이션을 설계 및 개발하며 다양한 기술과 관련된 서적의 저자 또는 편집자이기도 하다. 최근에 출판된 책으로는 The Productive Programmer가 있다. 대규모 엔터프라이즈 애플리케이션의 설계 및 빌드에 많은 관심을 가지고 있는 그는 전세계의 개발자 컨퍼런스에서 국제적으로 인정 받고 있는 연사로도 활동하고 있다. 그의 웹 사이트를 살펴보자.

잘못된 도움말 신고

부정사용 신고

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


잘못된 도움말 신고

부정사용 신고

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


디벨로퍼웍스 로그인


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=751168
ArticleTitle=함수형 사고: 함수적으로 사고하기, Part 2
publish-date=05312011
author1-email=nford@thoughtworks.com
author1-email-cc=jaloi@us.ibm.com

태그

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

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

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

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

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