반응형
출처: http://kr.blog.yahoo.com/kwon37xi/folder/3381246.html

JDOM은 XML 문서를 트리 구조로 구성한다는 점에서 DOM과 유사하다. 그러나 JDOM은 오직 Java만 지원한다.
JDOM은 DOM이 아니며 내부적으로 DOM을 확장하지도 않았다.

기본적으로 알아야 할 것

JDOM에는 DOM의 Node와 같은 트리 구조를 위한 상위 인터페이스가 존재하지 않는다. 요소(Element)와 속성(Attribute) 등 모든 것이 콘크리트 클래스(concrete class; 일반적인 인터페이스와 abstract 클래스를 제외한 클래스를 뜻한다. 또한 직접적으로 객체를 생성할 수 있는 클래스를 콘크리트 클래스라고 한다)로 이루어져 있다.

  • Element 클래스에 getText()가 있어 요소의 컨텐트를 구할 수 있다.
  • NodeListNamedNodeMap 같은 리스트 클래스가 없이 Java 컬렉션 API(java.util.List 등)을 사용한다.
  • Element.getAttributes() 로 속성의 List를 얻을 수 있다.

JDOM Document 생성하기

XML 원시 데이타가 없는 상태에서 JDOM Document 객체를 생성하려면 우선 JDOM의 Element 클래스를 사용해 XML 문서의 루트 요소를 생성하고, Element 클래스를 Document 클래스 생성자에 인자로 전달하여 Document 인스턴스를 생성하면 된다.
Element rootElement = new Element("root"); // "root"는 Root 요소
Document document = new Document(rootElement); // 새로운 Document 생성

입력과 출력

JDOM은 파서가 아니다. JDOM은 XML 원본을 읽기 위해 다른 파서에 의존한다. 또한 JDOM은 SAX 이벤트, DOM 트리, JDBC의 ResultSet 등을 입력 받는다.
  • 입력 스트림, 파일, 디스크상의 문서를 사용하거나 또는 기존의 XML을 DOM 트리로 생성할 필요가 없는 경우에는 SAXBuilder를 사용하는 것이 좋다.

    SAXBuilder builder = new SAXBuilder();
    Document doc = builder.build(new FileInputStream("contents.xml"));
    
  • DOM 빌더는 이미 DOM 트리 구조로 되어 있는 것을 JDOM 으로 바꿀 때만 사용한다. 성능이 SAXBuilder에 비해 떨어지므로 DOM 트리가 아닌 스트림에서 XML 데이터를 읽어 파싱할 때는 SAXBuilder를 사용한다.

    DOMBuilder builder = new DOMBuilder();
    
    // myDomDocumentObject는 DOM의 Document 객체이다.
    Document doc = builder.build(myDomDocumentObject);
    
    위는 org.w3c.dom.Documentorg.jdom.Document로 전환한다.
  • JDOM의 구조를 DOM 트리로 바꾸려면 DOMOutputter 클래스를 사용한다.

    DOMOutputter outputter = new DOMOutputter();
    org.w3c.dom.Document domDoc = outputter.output(myJDOMDocumentObject);
    
  • JDOM의 구조를 이용해 SAX 이벤트를 처리하려면

    SAXOutputter outputter = new SAXOutputter();
    outputter.setContentHandler(myContentHandler);
    outputter.setErrorHandler(myErrorHandler);
    outputter.output(myJDOMDocumentObject);
    
    SAXOutputter는 XML 문서가 아닌 JDOM Document를 분석하여 SAX 이벤트를 발생시키는 SAXReader라고 생각하면 된다.
  • XMLOutputter는 XML 문서를 Stream이나 Writer, 파일 또는 변환하려는 다른 구조 등으로 출력한다.

    XMLOutputter outputter = new XMLOutputter(org.jdom.output.Format.getPrettyFormat());
    outputter.output(jdomDocumentObject, new FileOutputStream("result.xml"));
    
    org.jdom.output.Format 객체를 이용해 출력 형식을 결성할 수 있으며 Format.getPrettyFormat()은 잘 정렬된 기본적인 XML 출력 형식을 지정한다.
  • XMLOutputter는 책에서 설명한 JDOM 버전과 현재 1.0 버전의 사용법이 다르다. 여기서 설명한 것은 1.0 기준이다.

요소, 속성 등을 추가하기

  • Element의 내용은 Element.setText(String)으로 추가한다.

    // <ClassName>org.jdom.Element</ClassName>
    Element element = new Element("ClassName");
    element.setText("org.jdom.Element");
    
  • Element나 그 외의 JDOM에 적합한 모든 구성 요소는 요소의 addContent() 메소드에 의해 추가될 수 있다. 추가 될 수 있는 타입으로는 Element, Attribute, Entity, Comment, ProcessingInstruction 등이 있다.

자식 노드 얻기

  • Element.getChild("요소이름")로 요소에서 "요소이름"의 자식 요소 첫번째를 얻을 수 있다. 자식 요소가 없으면 null을 리턴한다.
  • Element.getChildren()은 현재 요소의 자식 요소 List를 리턴한다.
  • Element.getChildren("요소이름")은 현재 요소의 "요소이름"이라는 자식 요소 List를 리턴한다.
  • Element.getTextTrim()은 요소의 텍스트에서 앞뒤 공백을 제거하고 리턴한다.
  • Element.getTextNormalize()은 요소의 텍스트에서 앞뒤 공백을 제거하고 중간에 나온 공백은 공백이 여러개라도 한개로 만들어 리턴한다.

속성

  • 속성 값 설정 첫번째 방식

    Attribute attribute =
      new Attribute("value", propertyValue); // 속성이름 value, 속성값 propertyValue
    element.setAttribute(attribute);
    
  • 속성 값 설정 두번째 방식

    element.setAttribute("value", propertyValue);
    
  • 속성 값 가져오기

    String text = element.getAttribut    eVal     ue("value"); // 속성 value의 값 가져오기
    
  • 속성 리스트 가져오기

    List attrs = element.getAttributes(); // 요소에 속한 속성(Attribute 객체)의 List
    

getContent()

Document나 Element 클래스에서 getContent()를 사용하면 컨텐트의 타입(요소, 처리지시어, 주석, 엔티티, 텍스트 등)과 상관없이 해당하는 모든 컨텐트를 리턴한다.
  • 주석을 XML문서의 맨 앞에 추가하려면 다음과 같이 해야한다. 그냥 doc.addContent(comment)를 하면 XML 문서의 맨 마지막에 주석이 추가되어 버린다.

    // doc 은 JDOM의 Document 객체이다.
    Comment comment = new Comment("이러쿵 저러쿵..");
    doc.getContent().add(0, comment);
    

XPath

XPath xpath = XPath.newInstance("/collection/dvd"); // XPath 문자열
List list = xpath.selectNodes(doc); // Document 객체

얻어진 list의 각 요소들을 Element, Attribute, String 등으로 캐스팅하여 사용하면 된다.

Element e = (Element)xpath.selectSingleNode(doc); 이처럼 XPath.selectSingleNode()를 사용하면 XPath결과 중에서 한 개의 값만을 가져올 수 있다.

XSLT

  • JDOM의 XSLTranformer는 파라미터를 받지 않는 문제가 있다. 이때 JAXP를 사용해야 한다.
  • 트랜스폼 결과는 Document로 리턴된다.

    StringWriter writer = new StringWriter();
    
    XSLTransformer transformer = new XSLTransformer("list.xsl");
    Document resultDoc= transformer.transform(doc); // Document 객체
    
    XMLOutputter out = new XMLOutputter(Format.getPrettyFormat());
    out.output(resultDoc, stringWriter); // 문자열 Writer에 결과를 저장.
    
    out.println(stringWriter.toString()); // 문자열로 출력
    

반응형
출처: http://kr.blog.yahoo.com/kwon37xi/folder/3381246.html

  • DOM은 XML을 생성하고 변경할 수 있다.

XML DOM 트리의 생성과 변경

  • 새로운 XML을 생성하기 위해서는 org.w3c.dom.DOMImplementation을 구현한 클래스를 사용해야한다.
  • Xerces의 DOMImplementation구현 : org.apache.xerces.dom.DOMImplementationImpl
  • 생성 예

    DOMImplementation domImpl = new DOMImplementationImpl();
    Document doc = domImpl.createDocument(null, "rootElement", null);
    
  • Document 객체 생성시 파서의 Document 구현 클래스를 사용하면 DocType 이 생성되지 않는다. DOMImplementation 을 사용해서 새로운 XML DOM 트리를 생성해야 한다.
  • DOMImplementation.createDocument(1,2,3);
    • 첫번째 인자 : 문서의 루트 요소를 위한 네임스페이스
    • 두번째 인자 : 루토 요소
    • 세번째 인자 : DocType 클래스의 인스턴스.
  • DocType이 필요할 경우 DOMImplementation.createDocType() 사용.
  • 변경 예

    Element root = doc.getDocumentEelment();
    root.setAttribute("id", id); // id 속성의 추가
    
    Element nameElement = doc.createElement("name");
    Text nameText = doc.createTextNode("내용");
    nameElement.appendChild(nameText); //name 요소에 텍스트 값 추가
    root.appendChild(nameElement); // rootElement 요소에 name 요소 추가
    
  • 모든 노드의 생성은 Document 객체의 create* 메소드를 통해서 이뤄진다.
  • "appendChild()"는 자식 노드를 추가한다.

네임스페이스

  • DOM Level 2는 네임스페이스를 지원한다.
  • 네임스페이스를 위해 Node 인터페이스는 "getPrefix()"와 "getNamespaceURI()" 메소드를 제공한다.
  • Document.createElementNS() 네임스페이스를 지원하는 요소 추가.
  • 네임스페이스를 인식하는 각 메소드의 첫번째 인자는 "네임스페이스 URI"이고, 두번째 인자는 요소와 속성등의 QName이다. QName은 "ora:copyright" 와 같은 형태를 띈다.
  • "ora:copyright" 요소에서 getPrefix() : "ora" 리턴
  • 네임스페이스에 속하지 않는 요소에서 getPrefix() : null 리턴
  • 네임스페이스를 지정했을 때는 루트 요소에 xmlns 속성을 지정해야 한다.

DOM Level 2 - 순회(Traverse)

  • DOM 트리를 순회하는 기능을 제공한다.
  • "org.w3c.dom.traversal.DocumentTraversal" 인터페이스를 이용한다.
  • 일반적인 파서의 Document 구현 클래스는 DocumentTraversal 도 함께 구현한다.
  • NodeIterator

    NodeList descriptionElements =
    	root.getElementsByTagNameNS(docNS, "description");
    Element description = (Element)descriptionElements.item(0);
    
    // NodeIterator를 구한다.
    NodeIterator i = ((DocumentTraversal)doc)
    	.createNodeIterator(description, NodeFilter.SHOW_ALL,
    	new FormattingNodeFilter(), true);
    
    Node n;
    
    while ((n = i.nextNode()) != null) {
    	System.out.println("Search phrase found: '" + n.getNod eVal  ue() + "'");
    }
    
  • createNodeIterator(1, 2, 3, 4)
    • 첫번째 인자 : 순회할 노드 요소
    • 두번째 인자 : 상수 필터
      1. NodeFilter.SHOW_ALL : 모든 노드를 포함하여 순회
      2. NodeFilter.SHOW_ELEMENT : 요소만 순회
      3. NodeFilter.SHOW_TEXT : 텍스트 노드만 순회
    • 세번째 인자 : NodeFilter 구현 객체
    • 네번째 인자 : 엔티티 참조의 실제값을 분석할 것인가?
    • 두번째와 세번째 인자가 함께 나올 경우 두번째 인자 필터를 우선적용하고 그 결과를 다시 세번째 인자로 필터링한다.
  • NodeFilter
    • public short acceptNode(Node n); 을 이용해서 순회할 노드인지 여부를 결정한다.
      • 리턴값 NodeFilter.FILTER_SKIP : 필터로 들어온 노드는 건너 뛰고 그 자식노드를 계속 탐색
      • 리턴값 NodeFilter.FILTER_REJECT : 필터로 들어온 노드와 그 자식 모두 건너 뜀
      • 리턴값 NodeFilter.FILTER_ACCEPT : 필터로 들어온 노드 사용
    • 노드 필터 예

      class FormattingNodeFilter implements NodeFilter {
      	public short acceptNode(Node n) {
      		if (n.getNodeType() == Node.TEXT_NODE) {
      			Node parent = n.getParentNode();
      
      			if ((parent.getNodeName().equalsIgnoreCase("b")) ||
      				(parent.getNodeName().equalsIgnoreCase("i"))) {
      				return FILTER_ACCEPT;
      			}
      		}
      
      		return FILTER_SKIP;
      	}
      }
      
  • TreeWalker
    트리 뷰를 얻는다. 필터를 이용해 특정한 요소 등만 가진 트리를 생성해낸다.

범위(Range)

알 수 없는 DOM 구조에 새로운 컨텐트를 추가하거나 또는 컨텐트를 삭제, 복사, 추출해야 할 경우에 범위 모듈을 사용한다.

Wrong document Exception

잘못된 문서 예외(Wrong document Exception)은 서로 다른 문서의 노드들을 함께 사용하려 할 때 발생한다.

다른 문서의 노드를 현재 문서에 append하려면 importNode를 사용한다.
Element otherDocElement = otherDoc.getDocumentElement();
Element thisDocElement = thisDoc.getDocumentElement();

// 대상 문서에 노드 임포트
Element readyToUseElement =
    (Element)thisDoc.importNode(otherDocElement);

// 아무문제없이 노드 추가
thisDocElement.appendChild(readyToUseElement);



반응형
출처: http://kr.blog.yahoo.com/kwon37xi/folder/3381246.html
DOM(Document Object Model)은 모든 프로그래밍 언어와 개발 도구에서 사용하는 문서의 컨텐트 모델을 표현하기 위해 설계되었다. 각 언어별로 바인딩이 존재한다.
이 점은 장점이 될 수도 있지만, JAVA 고유의 편리한 기능을 사용할 수 없어, 단점으로 작용하기도 한다. JAVA 고유의 기능을 사용한 JDOM이 DOM보다 훨씬 편리하다.
  • DOM은 모든 면에서 트리 모델이다.
  • DOM은 XML문서 전체를 메모리에 저장하여 표현한다.
  • 각 트리는 org.w3c.dom.Node 인턴페이스를 기반으로 한다. 요소, 속성, 텍스트, PI, 주석 등 모든 것이 Node로 표현된다.
  • 요소의 텍스트도 하나의 트리로 간주된다. 그러므로 Element 노드의 텍스트를 구할 때 "getText()"와 같은 방식으로 구할 수 없고, 요소의 Text 자식 노드들을 구한뒤, 거기서 값을 가져와야한다.

SAX의 장/단점

  • SAX는 순차적이라 XML문서의 요소를 무작위로 접근할 수 없다.
  • 형제 요소를 처리하기 어렵다.
  • 메모리를 훨씬 적게 사용한다.

XML 파싱 : Xerces 기준

  • DOM에서는 문서를 완전히 분석하여 트리 구조가 생성되어야 XML 문서의 데이터를 사용할 수 있다.
  • DOM에서 문서를 분석한 결과는 org.dom.w3c.dom.Document 객체로 표현된다.
  • 다음과 같이 XML 문서를 파싱한다.

    import org.apache.xerces.parsers.DOMParser; // 파서 import
    import org.w3c.dom.*; // DOM 인터페에스 import
    ...
    
    DOMParser parser = new DOMParser();
    
    // Document 객체인 DOM 트리 구성
    parser.parse("document.xml");
    Document doc = parser.getDocument(); // DOM Document 객체 얻기
    

  • import org.apache.xerces.parsers.DOMParser
    • void parse(org.xml.sax.InputSource inputSource)
    • void parse(java.lang.String systemId)

Node

  • DOM의 장점중의 하나는 XML을 표현하는(Document 객체를 포함한) 모든 DOM 객체가 DOM의 Node인터페이스를 상속한다는 점이다.
  • Node.getNodeType() : 현재 노드의 타입(요소, 속성, PI 등등..)을 가리킨다.
  • Document.getDocumentElement() : 최상위 요소(Root Element) 노드
  • Node.getNodeName() : 노드의 이름. Text 노드(요소의 값)의 경우 이 노드 이름은 의미가 없다.
  • Node.getNodeValue() : 노드의 값. Element 노드의 경우 이 값은 의미가 없으며 자식 노드를 구해 그 중 Text노드의 값을 가져와야만 한다.
  • Node.getChildNodes() : NodeList 인스턴스 반환. 자식 노드 목록.
  • NamedNodeMap Node.getAttributes() : Element 노드의 경우에만 유효. 속성 목록을 반환한다.
  • NodeList.item(int) : 자식 Node들을 순서대로 반환받는다.

DOCTYPE, PI 등의 처리

최상위 엘리먼트보다 상위에 오는 DOCTYPE과 PI(처리 지시어; Processing Instruction)등을 처리하려면 Document 노드 단에서 자식 노드들을 얻어야만 한다.

NodeList 사용시 주의점!

이것은 Java와 XML 책에는 없는 내용이다. 내 경험상의 주의점이다.

다음과 같은 XML이 있을 때
<root>
  <child1>hello</child1>
  <child2>hi~</chil2>
</root>
위 XML을 파싱하여 "root" 엘리먼트 Node 객체에서 getChildNodes()를 했을 경우에 주의할 점이 있다.
이 상황에서 "child1" 노드 객체를 가져올 때
DOMParser parser = new DOMParser();

parser.parse("test.xml");

Document doc = parser.getDocument();

Element root = doc.getDocumentElement();

NodeList children = root.getChildNodes(); // root 의 자식 노드 얻기 -- !! 요주의 부분!!

Node child1Node = children.item(0); // child1 요소 얻기 

Node textNode = child1Node.getChildNodes().item(0); // child1의 텍스트 얻기

System.out.println("child1 : " + textNode.getNodeValue());
위와 같이 하면 제대로 값을 가져올 수 있을까?

답은 "가져올 수도 있고 못 가져올 수도 있으나, 십중팔구는 NullPointerException이 발생한다는 것이다."
NullPointerException이 발생한다면 그것은 textNode가 null이기 때문이다.
위와 같이 NodeList.item(int) 메소드를 사용할 경우에 root 요소와 child1 요소 사이의 공백이 Text 노드로서 읽힐 수도 있다. 이 경우 NodeList.item(0)가 리턴하는 노드는 child1 요소가 아니라 root요소의 Text 노드가 되는 것이다.
만약 root 요소와 child1요소 사이에 아무런 공백도 없거나 DTD 등으로 공백을 무시하도록 했다면 안 그럴 수도 있다.

그러므로 child1 요소를 명백하게 가져오려고 할 경우에는 root.getChildNodes()를 사용해서는 안된다. root 노드를 Element 객체로 캐스팅하고 Element.getElementsByTagName(String)으로 명백하게 가져와야 한다.
Element root = doc.getDocumentElement();

// 이 부분이 바뀌었다!!
NodeList children = root.getElementsByTagName("child1"); // root 의 자식 노드 얻기

Node child1Node = children.item(0); // child1 요소 얻기

Node textNode = child1Node.getChildNodes().item(0); // child1의 텍스트 얻기

System.out.println("child1 : " + textNode.getNodeValue());

위와 같이 하면 children 객체에는 요소 이름이 "child1"인 요소만 남게 된다.

반응형
출처: http://kr.blog.yahoo.com/kwon37xi/folder/3381246.html

Property

파서에 추가되는 인터페이스로서 파서에 특정 인터페이스를 추가하여 분석 기능을 추가한다(수행 동작이 인터페이스를 구현한 객체에 구현되어 있다). Property는 Feature와 마찬가지로 URI를 사용하여 구별하는데, http://xml.org/sax/properites/를 접두어로 가지며 lexical-hander 같은 구분자가 뒤에 따라온다.

  • XMLReader.setProperty(String propertyID, Object value)
  • XMLReader.setFeature(String featureID, boolean value)
  • Object XMLReader.getProperty(String propertyID)
  • boolean XMLReader.getFeature(String featureID)

Feature

파서가 수행해야 하는 동작을 나타내기 위한 플래그로서 파서가 수행 가능한 동작의 실행 여부를 표시한다(수행하는 동작은 이미 파서에 구현되어 있다). Feature는 URI를 사용하여 구분하는데 http://xml.org/sax/features/를 접두어로 가지며 뒤에 validation과 같은 구분자가 따른다. 즉, 유효성 검사와 관련된 Feature는 http://xml.org/sax/features/validation이라는 URI로 표시된다.

EntityResolver

  • org.xml.sax.EntityResolver
  • 엔티티를 분석하는 이벤트 핸들러

public InputSource resolveEntity(String publicID, String systemID)

  • XMLReader가 엔티티 참조를 만날 때마다 그 엔티티에 해당하는 공개ID와 시스템ID를 resolveEntity메소드에 넘겨준다.
  • resolveEntity() 메소드에 어떤 코드를 추가하더라도, 항상 기본적으로는 null 을 반환하도록 해야 한다.

DTDHandler

  • org.xml.sax.DTDHandler
  • DTDHandler 인터페이스는 XMLReader가 분석하지 못한 엔티티나 표기(Notation) 선언을 만나게 되면 이를 알려준다. 물론 이 두 가지 이벤트는 XML문서가 아니라 DTD에서 발생한다.
  • 유효성 검사를 하면서 동시에 이 핸들러를 사용하는 것은 피하는 것이 좋다.
  • DTDHandler를 등록했다고 해도 유효성 검사 Feature(http://xml.org/sax/features/validation)를 선언하지 않으면 유효성 검사를 하지 않는다.

public void notationDecl(String name, String publicID, String systemID)

표기 선언(Notation)을 만나면 호출된다.

public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName)

분석되지 않은 엔티티 선언을 만나면 호출된다.

org.xml.sax.helpers.DefaultHandler 클래스

  • 여러 핸들러(ContentHandler, ErrorHandler, EntityResolver, DTDHandler)를 모두 구현했다. 구현한 메소드는 아무런 작동도 하지 않는다.
  • 이것을 상속하여 클래스를 작성하면 불필요한 메소드를 뺀 핸들러 클래스를 만들 수 있다.
  • EntityResolver는 다른 핸들러 클래스와 별도로 분리하는 것이 좋다.

XMLFilter

  • 하나의 SAX Reader가 모든 것을 처리하도록 하는 대신 Reader가 특정 처리를 하고 다른 Reader에게 넘겨주는 작업을 반복적으로 수행하는 파이프라인.
  • org.xml.sax.helpers.XMLFilterImpl클래스를 상속하여 구현하고, 자기 부모로 XMLReader를 지정하면 된다.
  • 핸들러는 최종 필터에 등록해야 한다.
  • XMLFilterImpl 클래스는 기본적으로 5개의 인터페이스에 정의된 모든 메소드는 관련된 이벤트를 처리하지 않고 단순히 넘어가도록 구현돼 있다. XMLFilterImpl 클래스를 상속한 클래스는 필터가 처리하는 메소드만 오버라이딩하여 구현하면 된다.

XMLWriter

org.xml.sax.ext.LexicalHandler

  • 주석, 엔티티선언, DTD 선언 그리고 CDATA 영역과 같은 구문과 관련된 이벤트를 처리하는 메소드를 제공한다. ContentHandler는 이 구문 관련 이벤트를 기본적으로 무시한다.
  • 이것을 사용하기 위해서는 Property에 이 핸들러를 구현한 객체를 등록해야만 한다.

    reader.setProperty("http://xml.org/sax/properties/lexical-handler", lexicalHandler);
    

public void startDTD(String name, String publicID, String systemID)

DTD 참조 또는 선언의 시작

public void endDTD()

DTD 참조 또는 선언의 끝

public void startEntity(String name)

엔티티 참조 시작

public void endEntity(String name)

엔티티 참조 끝

public void startCDATA()

CDATA 영역 시작

public void endCDATA()

CDATA 영역 끝

public void comment(char[] ch, int start, int length)

  • 주석
  • <!-- 와 --> 주석 구분자를 제외한 텍스트만을 받는다.

org.xml.sax.ext.DeclHandler

  • DeclHandler 핸들러는 요소 선언이나 속성 선언과 같은 DTD에서 발생하는 특정 이벤트를 처리하는 메소드를 정의 하며 아주 특별한 경우에만 사용된다.
  • 이것을 사용하기 위해서는 Property로 이 핸들러를 구현한 객체를 등록해야 한다.

    reader.setProperty("http://xml.org/sax/properties/declaration-handler", declHandler);
    

public void attributeDecl(java.lang.String eName, java.lang.String aName, java.lang.String type, java.lang.String mode, java.lang.String value)

속성의 선언을 처리한다. <!ATTLIST>

public void elementDecl(java.lang.String name, java.lang.String model)

요소의 선언을 처리한다. <!ELEMENT>

public void externalEntityDecl(java.lang.String name, java.lang.String publicId, java.lang.String systemId)

외부 자원을 참조하는 엔티티 선언을 처리한다. <!ENTITY>

public void internalEntityDecl(java.lang.String name, java.lang.String value)

내부에서 선언된 엔티티 선언을 처리한다.

반응형
출처: http://kr.blog.yahoo.com/kwon37xi/folder/3381246.html
Java와 XML 2판의 내용정리이다.
책을 보지 않으면 이 내용을 이해할 수 없을 수 있다.

SAX 파서는 XML 문서를 순차적으로 분서하는데 문서를 분석하는 동안 시작 태그나 속성, 종료 태그, 텍스트 데이터 등을 만나면 각 구성 요소를 처리하는 메소드를 호출한다.
이때 메소드를 호출하는 구성요소를 만나는 것이 이벤트가 된다. 즉, SAX 파서가 시작태그를 만나는 것이 하나의 이벤트이고 속성을 만나는 것이 또 다른 이벤트가 된다. 그리고 각 구성요소를 처리하는 메소드를 호출하는 것은 이벤트를 처리한다라고 표현하며, 호출되는 메소드를 콜백(callback) 메소드라고 부른다.

SAX Parser

http://xml.apache.org 에서 Xerces 파서를 구할 수 있다.

SAX 클래스는 org.xml.sax 구조로 패키징되어 있다. 최소한 org.xml.sax.XMLReader 클래스가 있어야 한다.

SAX 공식 홈 : http://wwww.saxproject.org

SAXReader

SAX 사용의 시작은 org.xml.sax.XMLReader 의 객체를 얻는 것이다.
// 파서 클래스 지정. 여기서는 아파치 Xerces
private String parserClass="org.apache.xerces.parsers.SAXParser";

// 파싱에 사용할 XMLReader 객체 생성
XMLReader reader = XMLReaderFactory.createXMLReader(parserClass);

  • XMLReader 의 객체는 재사용이 가능하지만 한 번에 한개의 XML 파일만을 파싱해야한다.

XML 문서 파싱

XMLReader 객체의 parse(InputSource) 혹은 parse(String URI)로 파싱한다. InputSource 사용권장.

InputSource is = new InputSource(new FileInputStream(new File(xmlURI)));

// 필수적으로 SystemID를 설정해야 한다.
// SystemID는 XML 문서의 URI이다.(파일의 경우 파일의 경로)
is.setSystemId(xmlURI);

// 파싱
reader.parse(inputSource);
  • 항상 시스템 ID를 설정해야만 한다!!
  • SystemID는 XML문서의 URI이다. 파일의 경우는 파일의 경로를 의미한다.

Handler 등록

핸들러는 SAX 파서가 문서를 순차적으로 분석하는 동안 발생한 이벤트에 대해 호출할 메소드를을 가진 클래스 객체이다.

  • XMLReader.setContentHandler()
  • XMLReader.setErrorHandler()
  • XMLReader.setDTDHandler()
  • XMLReader.setEntityHandler()


  • 모든 핸들러는 throws SAXException 을 해야 한다.

Content Handler

  • 요소 등 XML의 내용을 만났을때 호출되는 콜백 메소드를 가지는 클래스.
  • ContentHandler 인터페이스 구현

public void setDocumentLocator(Locator locator)

  • 분석중인 문서의 위치 정보를 가진 Locator의 객체 생성. Locator 객체의 getLineNumber(), getColumnNumber() 메소드들로 이벤트가 발생한 위치를 알아낼 수 있다.
  • 이벤트의 위치는 실제 처리중인 분석과정에만 유효. Locator는 ContentHandler를 구현한 클래스 범위 내에서만 사용되어야 한다! Locator를 ContentHandler 의 인스턴스 멤버로 등록한다.

public void startDocument() throws SAXException

  • 문서 시작시 호출되는 콜백 메소드.
  • 모든 핸들러를 포함하여 가장 처음에 호출.(setDocumentLocator() 제외)

public void endDocument() throws SAXException

  • 문서 분석이 끝날 때 호출되는 콜백 메소드
  • 모든 핸들러를 포함하여 가장 나중에 호출.

public void processingInstruction(String target, String data)

  • 처리지시어(<? --- ?>)를 처리한다.
  • XML 데이터를 사용하는 실제 애플리케이션은 이 메소드로 처리 지시어를 받아 변수 값을 설정하거나 특정한 처리 과정을 수행하는 메소드를 실행한다.
  • <?xml version="1.0"?> 이 부분은 전달되지 않는다.

네임스페이스 매핑

  • 네임스페이스를 선언하는 xmlns 를 만나면 발생
  • public void startPrefixMapping(String prefix, String uri)
  • public void endPrefixMapping(String prefix)
  • 프로그래밍 패턴

    private Map namespaceMappings = new HashMap();
    
    public void startPrefixMapping(String prefix, String uri) {
    	// 시각적인 이벤트는 발생하지 않는다.
    	namespaceMappings.put(uri, prefix);
    }
    
    public void endPrefixMapping(String prefix) {
    	// 시각적인 이벤트는 발생하지 않는다.
    	for (Iterator i = namespaceMappings.keySet().iterator(); i.hasNext(); ) {
    		String uri = (String)i.next();
    		String thisPrefix = (String)namespaceMappings.get(uri);
    		if (prefix.equals(thisPrefix)) {
    			namespaceMappings.remove(uri);
    			break;
    		}
    	}
    }
    

public void startElement(String namespaceURI, String localName, String qName, Attributes atts)

  • 요소가 시작될 때 호출
  • qName 은 네임스페이스 접두어가 있다면 그것을 이름에 포함한다.
  • atts 는 요소의 속성들을 배열로 가지고 있다.

public void endElement(String namespaceURI, String localName, String qName, Attributes atts)

  • 요소가 닫힐 때 호출

public void characters(char[] ch, int start, int length)

  • 요소의 텍스트 데이터
  • ch 배열의 start 인덱스부터 length 길이 만큼 만이 데이터이다!!

public void ignorableWhitespace(char[] ch, int start, int length)

  • 무시 가능한 공백문자가 올 경우 호출
  • DTD나 스키마가 없으면 호출되지 않는다. DTD나 스키마에서 무시가능한 공백문자를 지정하기 때문이다.

public void skippedEntity(String name)

  • 파서가 엔티티를 처리하지 않을 때 호출
  • 실제 이 메소드가 호출되는 경우는 거의 없다.(Xerces는 절대 호출하지 않는다.)
  • 이 콜백을 수행하면 엔티티 참조의 앞의 앰퍼샌드와 뒤의 세미콜론을 포함하지 않는 엔티티 참조를 매개변수로 전달한다.

ErrorHandler

  • 파싱중 오류가 발생하면 호출되는 핸들러
  • ErrorHandler 인터페이스 구현

public void warning(SAXParseException ex)

  • 경고

public void error(SAXParseException ex)

  • 치명적이지 않은 오류

public void fatalError(SAXParseException ex)

  • 치명적 오류
  • 파싱을 중지하는게 낫다.

반응형

중앙대학교 컴퓨터공학과 자바 동호회 JSTORM   http://www.jstorm.pe.kr  최진호님이 작성하신 문서입니다.

JDOM을 이용한 Java/XML 프로그래밍 자료입니다.
반응형

본 문서는 http://cafe.naver.com/specialj.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=1336 을 참조 

DOM 문서 객체 모델 [-, document object model]
본문
브라우저를 통한 확장성 생성 언어(XML) 문서의 상호 연동을 위한 객체 기반의 문서 모델. 플랫폼과 언어 면에서 중립적인 인터페이스로서 프로그램과 스크립트에 의한 문서의 내용˙구조˙종류의 동적인 접근과 변경이 가능하며, 스크립트나 프로그램 언어에 웹 페이지를 연결해 준다. 웹 페이지를 조작˙생성하기 위해 사용되는 속성, 방법 및 이벤트가 객체를 구성하는데, 이러한 객체들은 대부분의 웹 브라우저에서 스크립트 언어를 통해 접근할 수 있다.

 

 

 

JDOM [Java Document Object Model]
본문
자바에 최적화확장성 생성 언어(XML) 데이터 가공을 위해 만든 개방 소스 라이브러리. 자바 기반의 개발 도구로, 좀 더 직관적인 방법으로 XML을 조작할 수 있는 개방 소스 응용 프로그램 인터페이스(API)이며, SAX(Simple API for XML)와 문서 객체 모델(DOM)과도 상호 연동되고, SAX와 DOM의 장점만을 골라 만든 클래스인터페이스를 제공한다. JDOM은 W3C DOM과 유사하지만 DOM을 기반으로 설계되거나 DOM을 모델로 하지 않은 대안적인 문서 객체 모델로, 차이점은 DOM은 언어 중립적으로 설계되었고, 초기에 HTML 페이지의 자바 스크립트 가공에 주로 이용되었던 반면, JDOM은 자바 전용으로 설계되어 메소드 오버로딩(method overloading), 컬렉션(collection), 리플렉션(reflection), 친숙한 프로그램밍 환경 등 자바의 기본 기능들을 활용한다는 데 있다.

 

SAX

[simple API for XML, simple application program interface for Extensible Markup Language]

본문
확장성 생성 언어(XML)로 된 웹 파일을 해석할 수 있게 해 주는 응용 프로그램 인터페이스(API). XML을 해석하기 위해 문서 객체 모델(DOM) 대신 사용할 수 있는 대안으로 DOM보다 간단하고 대량의 파일용으로 적절하나 데이터 내용 운용면에서는 능력이 떨어진다. 또한 이벤트형 인터페이스이벤트를 제어하고 상황을 처리하여 XML 파서(parser)와 함께 사용된다.

 

JAX [Java API for XML]
본문
확장성 생성 언어(XML) 데이터 처리를 위한 자바 응용 프로그램 인터페이스. XML 파서(paser)를 얻기 위한 표준 인터페이스 JAXP(Java API for XML Procession), 메세징 프로토콜의 기초를 제공하는 JAXM(Java API for XML Messaging), 단순 객체 접근 프로토콜(SOAP) 스팩(제품 명세)에 따른 메시지를 만들고 소비하도록 하는 SAAJ(SOAP with Attachments API for Java), XML을 기반으로 한 자바 원격 프로시져 호출 응용 프로그램 인터페이스(API)인 JAX/RPC(Java API for XML-based RPC), XML 레지스터가 저장한 퍼블리싱된 웹 서비스 정보를 액세스하는 방법을 제공하는 JAXR(Java API for XML Registries)로 구성된다.

 

자바 XML팩 [Java XML Pack]
본문
선 마이크로시스템스사가 2001년 12월에 발표한 자바 언어(환경)를 확장성 생성 언어(XML)나 웹 서비스에 대응시키는 응용 프로그램 인터페이스(API)를 정리한 패키지. 자바 개발자에게 프로그램의 XML 대응이나 웹 서비스의 구축에 필요한 기술 제공을 목적으로 한다. JAXP(Java API for XML Processing), JAXM(Java API for XML Messaging), JAXB(Java API for XML Data Binding) 등 몇 개의 XML 관련 패키지를 통합한 것이다. 향후 자바 XML 팩에는 문서 처리, 데이터 처리 등 다른 XML 기술도 수록될 예정이다.

 

 

 

그외에 웹로직으로 알려진 BEA Systems의 스택스(StAX: Streaming API for Java)가 있다.


반응형
어느날 갑자기 내가 사용하는 무선 마우스가 생명을 다하고 더이상 작동하지 않는다.. ㅠㅠ

망할놈의 DIGIX 무선 세트.. 싸다고 해서 구입했는데 후회가 막심하다..

키감도 별로고 키보드 자판 배열도 영 적응이 안된다.. 므튼 요점은 마우스가 작동되지 않아서

키보드만으로 하루를 버틸 수 밖에 없는 상황이였다. 물론 회사 업무시간에 죽어버린것이다.

마우스를 손에 달고 살다보니 없으니 여간 불편하고 할수 있는게 없는듯 했다.

할수 없이 일은 해야 했기에 내가 사용하는 프로그램 단축키를 죄다 뽑기 시작했다.

eclipse,visual studio,totalcommander 등등.. ㅎㅎ

책상위가 온통 단축키(A4용지) 로 덮여졌다.. 더듬더듬 찾아가면서 단축키를 사용하다 보니 첨엔 불편하더니

점차 마우스를 사용하는 것보다 더 편한것이 아닌가..

므릇 스타크래프트를 할때에도 하수와 고수의 차이는 단축키가 아니던가.. ㅎㅎ

뭐 마우스 없이 사용하는데 별 무리가 없었던데에는 launchy 와 total commander의 도움이 컷다..ㅎㅎ

마우스가 없다보니 단축키도 훨씬 빨리 손에 익혀지는 듯하고 그렇게 불편함은 없다.

그리고 어디선가 읽었던 글이 생각이 난다. 목수는 자신의 도끼를 항상 날카롭게 하고

군인은 자신의 총을 항상 정비한다. 자신의 도구를 최대한 활용하기 위해서 이다.

개발자는 자신의 도구(IDE?)를 다루는데 항상 능숙할수 있도록 준비해야 한다는 말이였던듯..

요는 자신이 사용하는 도구를 최대한 활용할수 있도록 단축키를 익히자 이런뜻이였던듯 하다.

그리고 참고로 단축키를 정리해 놓은 사이트가 있어서 링크에 올려 놓는다..

http://allhotkeys.com/    

후기> 다음날 마우스를 가져와서 사용하니 아직도 손에 마우스가 붙어있네.. 이걸 아예 없애버려? 마우스?

근데 웹서핑할때는 마우스가 없으면 너무 힘들어서 ㅠㅠ
반응형

 응용 프로그램 사용 현황, 시스템 속성, 사용자 활동을 감시하여 노트북과 데스크톱 컴퓨터의 전력 관리 시스템을 좀 더 효율적으로 활용하자

리눅스(Linux®) 컴퓨터에서 응용 프로그램 사용 현황과 사용자 활동을 감시하여 전력 소모를 줄이는 방법을 소개합니다.

현대 컴퓨터에 내장된 ACPI(Advanced Configuration and Power Interface)와 전력 관리 시스템은 전반적인 전력 소모를 줄여주는 다양한 옵션을 제공한다. 또한 리눅스는 다양한 상황에서 PC 전력 소모량을 파악하는 도구를 사용자 영역 프로그램 형태로 다양하게 제공한다.

대다수 문서는 커널 매개변수와 hdparm 설정을 수정하여 불필요한 디스크 활동을 줄이는 방법에 초점을 맞춘다. 또한 전력 공급원에 따라서 주파수를 동적으로 조정하도록 프로세서 설정을 변경하는 방법을 상세하게 알려주는 문서도 있다.

이 기사에서는 응용 프로그램 사용 현항을 감시하여 전력을 한층 더 절약하는 도구와 코드를 소개한다. 여기서 소개하는 기술을 사용하면 현재 사용하는 응용 프로그램, 사용자 활동, 전반적인 시스템 성능에 따라 전력 설정을 변경할 수 있다.

하드웨어/소프트웨어 요구사항

2000년 이후에 제작된 PC는 하드웨어와 소프트웨어 모두 절전 기능을 제공한다. 여기서는 현대적인 리눅스 커널이 필요하다. 절전 도구가 많이 내장된 리눅스 배포판이라면 더 좋겠지만, 단순히 화면이나 시스템을 자동으로 꺼버리는 정도로도 상당한 전력을 절약한다. ACPI 기능이 없는 하드웨어나 옛날 하드웨어라도 여기서 소개하는 코드를 유용하게 활용할 수 있다.

여기서는 사용자가 직접 사용하는 PC를 예제로 삼았지만, 서버나 원격 터미널에도 같은 개념을 적용하여 사용자 활동에 따라 전력 소모를 줄일 수 있겠다.





focusTracker.pl 프로그램

응용 프로그램 사용 현황을 감시해 전력을 절약하는 방법은 다양하다. 이 기사에서는, 가장 먼저 전력을 “낭비”하는 특징적인 사용 패턴을 추적한다. 그런 다음, 시스템이 이런 패턴에 따라 전력을 낭비한다고 판단하면, 절전 모드를 활성화한다. Listing 1은 추적 작업을 수행하는 focustTracker.pl 프로그램 시작 부분이다.


Listing 1. focusTracker.pl 프로그램 헤더
#!/usr/bin/perl -w
# focusTracker.pl - 사용량 시각화를 위한 초점 자료를 수집한다.
use strict;
use X11::GUITest qw( :ALL );    # 응용 초점을 찾는다.
use threads;                    # xev에서 비차단 읽기를 수행한다.
use Thread::Queue;              # xev에서 비차단 읽기를 수행한다.
$SIG{INT} = \&printHeader;      # 종료 시에 stderr로 헤더 파일을 출력한다.

$|=1;                # 버퍼링을 끈 출력
my %log = ();        # 초점 추적 자료 구조
my $lastId = "";     # 마지막으로 초점이 맞춰진 윈도우 식별자
my $pipe = "";       # xev를 거쳐 비차단 사건 읽기
my @win = ();        # maxWindows 응용을 위한 이진 활성 자료
my $cpu = "";        # iostat에서 얻은 CPU 사용량
my $mbread_s = "";   # iostat에서 얻은 디스크 읽기(mb/s)
my $totalWindow = 0; # 전체 사용된 윈도우
my $maxWindows = 50; # 전체 추적된 윈도우

위 코드는 필요한 모듈을 인클루드하고 변수를 정의하는 외에도 시그널 인터럽트 처리기를 정의한다. 시그널 인터럽트 처리기는 자료 헤더 정보를 출력한다. 이렇게 자료와 헤더 파일을 분리하면 kst와 같은 도구로 자료 시각화 작업이 쉬워진다. Listing 2는 주 루프를 시작하는 부분이다.


Listing 2. focusTracker.pl 주 루프 시작부
while(my $line = <STDIN> )
{
  for my $c (0..$maxWindows){ $win[$c] = 0 }  # 모든 자료 위치를 초기화한다.

  next if( $line =~ /Linux/ || length($line) < 10 ); # 헤더 행, 빈 행

  my $windowId   = GetInputFocus();
  my $windowName = GetWindowName( $windowId ) || "NoWindowName";

  if( ! exists($log{$windowId}) )
  {
    # 새로운 윈도우라면 자료 집합에서 다음 위치에 대입한다.
    $log{ $windowId }{ order } = $totalWindow;
    $log{ $windowId }{ name }  = $windowName;
    $totalWindow++ if( $totalWindow < $maxWindows );

  }# 새롭게 추적된 윈도우라면

표준 입력(stdin)에서 입력이 들어올 때마다 현재 초점 정보를 저장하는 이진 변수 @win0으로 재설정한다. 사용자가 아직 초점이 맞춰지지 않은 윈도우로 초점을 맞출 때마다 윈도우 위치와 이름이 %log 해시로 기록된다(한 번 기록된 윈도우는 다시 기록되지 않는다). 현재 초점/비초점 정보로 윈도우 이름을 찾으려면 이 단계가 필수다. Listing 3은 계속하여 입력을 읽은 후 파이프 관리를 시작하는 코드다.


Listing 3. focustTracker.pl 파이프 관리
  if( $line =~ /avg-cpu/ )
  {
    # CPU 사용량을 읽어서 시각화를 위해 2-12 사이 값으로 변환한다.
    ( $cpu ) = split " ", <STDIN>;
    $cpu = sprintf("%2.0f", ($cpu /10) + 2 );

  }elsif( $line =~ /Device/ )
  {
    # 디스크 읽기 사용량을 읽어서 시각화를 위해 2-12 사이 값으로 변환한다.
    ( undef, undef, $mbread_s ) = split " ", <STDIN>;
    $mbread_s = $mbread_s * 10;
    if( $mbread_s > 10 ){ $mbread_s = 10 }
    $mbread_s = sprintf("%2.0f", $mbread_s + 2);

    # 초점 정보를 점검한다.
    if( $windowId ne $lastId )
    {
      if( $lastId ne "" )
      {
        # 옛날 파이프를 닫는다.
        my $cmd = qq{ps -aef | grep $lastId | grep xev | perl -lane '`kill \$F[1]`'};
        system($cmd);
      }
      $lastId = $windowId;
  
      # 새 파이프를 연다.
      my $res = "xev -id $windowId |";
      $pipe = createPipe( $res ) or die "no pipe ";

CPU 사용량과 디스크 사용량을 읽어 2-12 등급 중 하나로 변환한다. 순전히 시각화를 위한 값으로, CPU가 여러 개이거나 광섬유 채널 디스크를 사용한다면 등급 범위를 바꿔도 괜찮다. 여기서 사용한 등급 2-12는 IBM® 씽크패드 T42p, 단일 CPU, IDE 디스크에서 적당하다.

입력을 읽는 동안 초점이 바뀌었다면 기존 xev 파이프를 죽이고 새 파이프를 시작한다. 현재 초점 윈도우에 붙은 xev 프로그램은 키 입력이나 마우스 이동을 추적한다. Listing 4는 키 입력이나 마우스 이동 이벤트를 처리하는 코드다.


Listing 4. focusTracker.pl 파이프 읽기
    }else
    { 
      # 파이프에 자료가 넘어오면 활동하고 있음을 알려준다.
      if( $pipe->pending )
      { 
        for my $c (0..$maxWindows){ $win[$c] = 0 }  # 모든 자료 위치를 초기화한다.
        $win[ $log{$windowId}{order} ] = 1;
        
        # 파이프를 정리한다.
        while( $pipe->pending ){  my $line = $pipe->dequeue or next }
      
      }# 해당 윈도우에 대한 사건을 감지했을 경우
    
    }# 파이프를 설정했을 경우
    
    # CPU 사용량, 디스크 읽기 사용량, 초점 추적 자료
    print "$cpu $mbread_s @win \n";
  
  }# 디바이스가 연결되어 있을 경우

}# 입력받는 동안

위 블록은 직전 루프 반복에서 입력을 읽은 윈도우와 이번 루프 반복에서 입력을 읽은 윈도우가 똑같은 경우에만 실행된다. 초점 윈도우에 파이프 자료가 있다면, 즉 사용자가 초점 윈도우에서 키를 누르거나 마우스를 움직였다면, 초점 윈도우의 활동 이진 상태 변수를 (@win) 설정한다. 파이프를 정리하려면 xev 출력 행을 모두 읽으면 된다.

개별 입력을 처리한 후에는 CPU 사용량, 디스크 사용량, 초점 이진 변수 값을 모두 출력한다. Listing 5는 xev 모니터링 프로그램에 비차단 링크를 생성하는 createPipe 하위 루틴과, 표준 오류(stderr)로 자료 헤더 정보를 출력하는 printHeader 하위 루틴이다.


Listing 5. focusTracker.pl 하위 루틴
sub createPipe
{ 
  my $cmd = shift;
  my $queue = new Thread::Queue;
  async{ 
      my $pid = open my $pipe, $cmd or die $!;
      $queue->enqueue( $_ ) while <$pipe>;
      $queue->enqueue( undef );
  }->detach;
  
  # 분리할 경우 프로그램을 마칠 때 스레드를 조용히 끝내도록 만든다.
  return $queue;

}#createPipe

sub printHeader
{
  for my $key ( sort { $log{$a}{order} <=> $log{$b}{order} } keys %log )
  {
    print STDERR "$log{$key}{order} $key $log{$key}{name} \n";
  }
}#printResult

focusTracker.pl 사용법

focusTracker.pl은 iostat 프로그램에서 주기적으로 입력을 받는다. Listing 6은 응용 프로그램 사용 현황을 기록하는 명령 행 예다.


Listing 6. focusTracker.pl 예제 명령
iostat -m -d -c 1 | \
  perl focusTracker.pl 2> activityLog.header | tee activityLog.data

focusTracker.pl 프로그램을 종료하려면 Ctrl+C를 누른다. 위에서 \ 문자는 줄바꿈을 뜻하므로, 실제 명령에 입력하지 않도록 주의한다. -m 스위치를 지정하면 iostat은 (가능한 경우) 값을 초당 메가바이트 단위로 표시한다. -d 스위치는 디바이스 정보를 (이 경우에는 디스크 처리량을) 출력하고, -c 스위치는 출력할 CPU 사용량 정보를 지정한다. 마지막 옵션 1은 iostat에게 매 초마다 새로 획득한 정보를 표시하라는 뜻이다. Listing 7은 이 명령이 출력하는 결과 일부다. 후반부는 사용자가 Ctrl+C를 눌렀을 때 activityLog.header에 출력된 헤더 파일 내용이다.


Listing 7. focusTracker.pl 예제 결과
 5  2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
 6  2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 9  2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 5  2 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 7  2 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
...
0 10485764 NoWindowName 
1 33554434 Eterm 
2 44040194 Eterm 
3 41943042 focusTracker.pl (~/smartInactivityDetector) - VIM 
4 27263164 Controlling ACPI Centrino features - Mozilla Firefox 

두 번째 항목과 다섯 번째 항목은 Eterm과 파이어폭스 윈도우 초점 정보를 보여준다. 키보드나 마우스로 윈도우를 전환하면 해당 윈도우의 초점 지시자가 0에서 1로 변한다.

컴퓨터를 사용하는 중에 focusTracker 프로그램을 잠깐 동안 실행하면서 응용 프로그램 사용 현황과 컴퓨터 비활성 상태를 감시해도 좋겠다. 아니면 종일이나 컴퓨터를 사용하는 내내 프로그램을 돌려서 자료를 대량으로 수집한 후 나중에 분석하고 살펴봐도 좋겠다.




위로


사용 현황 시각화

focusTracker.pl 프로그램이 생성하는 대량의 자료를 분석하려면 kst와 같은 도구가 가장 적합하다. 그림 1은 예제로 생성한 activityLog.dat 파일을 분석한 모습이다.


그림 1. kst 시각화 예제
kst 시각화 예제

위 그래프는 CPU 사용량과 디스크 사용량을 선으로 표시하고 이진 초점 정보를 점으로만 표시한다. 점은 응용 프로그램마다 색깔과 모양이 다르다. X축으로 196 즈음에서 Firefox 윈도우로 초점이 옮겨진다. 그리고 동시에 CPU 사용량과 디스크 사용량이 증가한다. 이후로 별다른 입력이 없어서 CPU 사용량과 디스크 사용량은 '보통' 상태로 돌아온다.

실제로 사용자는 여러 Eterm 윈도우를 열어서 코드를 작성하다가 196 즈음에서 파이어폭스로 초점을 옮겨서 근처 교통 정보를 확인한 후 컴퓨터를 켜둔 상태로 점심을 먹으러 나갔다.

전형적인 전력 관리 설정에서는 시스템 비활성 타이머를 이용하여 화면 보호기를 띄운다. 하지만 위와 같이 사용자가 보이는 패턴을 안다면 11:30에서 12:30 사이에 파이어폭스 프로그램이 잠시 사용되는 경우 저전력 모드로 전환할 수 있다.

게다가 Eterm/vim을 사용하는 중에는 CPU 사용량과 디스크 사용량이 아주 낮다. 그러므로 Eterm/vim을 사용하는 도중에도 저전력 모드가 합리적이다. Vim에서 텍스트를 입력할 때는 전력이 많이 필요하지 않다. CPU 사용량과 디스크 사용량이 임계값 이하일 때 일정한 기간 동안 디스크 회전 속력과 CPU 속력을 낮추는 방법도 가능하다.

inactivityRulesFile 설정

Listing 8에 방금 설명한 규칙을 코드화했다.


Listing 8. 예제 inactivityRulesFile
# 형식은 다음과 같다.
# 시작 끝 CPU 디스크 타임아웃 응용 명령
1130_#_1230_#_null_#_null_#_10_#_firefox_#_xscreensaver-command -activate
0610_#_1910_#_6_#_2_#_30_#_vim_#_echo 6 > /proc/acpi/processor/CPU0/performance

사용자 활성 상태만 확인하는 경우에는 CPU 값과 디스크 값을 null로 설정한다. CPU 값과 디스크 값을 설정한 경우에는 각 값을 임계값으로 취급한다. 즉, CPU 사용량과 디스크 사용량이 임계값 이하로 떨어지면 명령을 실행한다. timeOut 변수는 사용자가 비활성인 시점부터 CPU 사용량과 디스크 사용량을 임계값과 비교할 시점까지 기다리는 시간을 가리킨다. 응용 프로그램 변수는 X 윈도우 시스템에서 응용 프로그램 제목에 들어가는 문자열을 가리킨다.




위로


monitorUsage.pl 프로그램

monitorUsage.pl 프로그램은 inactivityRuleFile을 읽어 응용 프로그램 사용 현황을 확인하는 기능을 제공한다. Listing 9는 프로그램 첫 부분이다.


Listing 9. monitorUsage.pl 프로그램 헤더
#!/usr/bin/perl -w
# monitorUsage.pl - 응용 사용 패턴을 추적하고 명령을 수행한다.
use strict;
use X11::GUITest qw( :ALL );    # find application focus
use threads;                    # non blocking reads from xev
use Thread::Queue;              # non blocking reads from xev

$|=1;               # 버퍼링을 끈 출력
my $cpu = "";       # iostat에서 얻은 CPU 사용량
my $mbread_s = "";  # iostat에서 얻은 디스크 읽기(mb/s)
my @rules = ();     # inactivityRulesFile에서 얻은 규칙
my $ruleCount = 0;  # 전체 규칙
my $lastId = "";    # 마지막으로 초점이 맞춰진 윈도우 식별자
my $pipe = "";      # xev를 거쳐 비차단 사건 읽기
my %app = ();       # 현재 초점이 맞춰진 응용 속성
    
open(INFILE," inactivityRulesFile") or die "can't open rules file";
  while( my $line = <INFILE> )
  { 
    next if( $line =~ /^#/ );        # 주석 행을 건너뛴다.
    my( $start, $stop, $cpu, $disk, 
        $timeOut, $appName, $cmd ) = split "_#_", $line;
      
    $rules[$ruleCount]{ start }   = $start;
    $rules[$ruleCount]{ stop }    = $stop;
    $rules[$ruleCount]{ cpu }     = $cpu;
    $rules[$ruleCount]{ disk }    = $disk;
    $rules[$ruleCount]{ timeOut } = $timeOut;
    $rules[$ruleCount]{ appName } = $appName;
    $rules[$ruleCount]{ cmd }     = $cmd;
    $ruleCount++;
      
  }#while infile
      
close(INFILE); 

focusTracker.pl 프로그램과 마찬가지로 라이브러리를 포함하고 변수를 정의한다. inactivityRulesFile 내용은 %rules 해시에 저장했다가 나중에 처리 과정에서 사용한다. Listing 10은 iostat 입력을 처리하고 초점을 확인하는 코드다.


Listing 10. monitorUsage.pl 자료 읽기, 파이프 처리
while(my $line = <STDIN> )
{
  next if( $line =~ /Linux/ ); # 헤더 행

  next if( length($line) < 10 ); # 공백 행

  my $windowId = GetInputFocus();
  my $windowName = GetWindowName( GetInputFocus() ) || "NoWindowName";

  if( $line =~ /avg-cpu/ )
  {
    ( $cpu ) = split " ", <STDIN>;
    $cpu = sprintf("%2.0f", ($cpu /10) + 2 );

  }elsif( $line =~ /Device/ )
  {
    # 디스크 읽기 사용량을 읽어서 시각화를 위해 2-12 사이 값으로 변환한다.
    ( undef, undef, $mbread_s ) = split " ", <STDIN>;
    $mbread_s = $mbread_s * 10;
    if( $mbread_s >10 ){ $mbread_s = 10 }
    $mbread_s = sprintf("%2.0f", $mbread_s + 2);

    if( $windowId ne $lastId )
    { 
      if( $lastId ne "" )
      { 
        # 옛날 파이프를 닫는다.
        my $cmd = qq{ps -aef | grep $lastId | grep xev | perl -lane '`kill \$F[1]`'};
        system($cmd);
      }
      $lastId = $windowId;

      # 새 파이프를 연다.
      my $res = "xev -id $windowId |";
      $pipe = createPipe( $res ) or die "no pipe ";

      # 현재 추적 중인 응용을 다시 설정한다.
      %app = ();
      $app{ id }           = $windowId;
      $app{ name }         = $windowName;
      $app{ cmdRun }       = 0;
      $app{ lastActivity } = 0;

여러 응용 프로그램을 추적할 필요가 없으므로 %app는 단순히 현재 초점이 있는 응용 프로그램의 속성만 추적한다. Listing 11은 같은 응용 프로그램에서 여러 경로로 입력이 계속 들어오는 경우를 처리하는 논리 분기를 보여준다.


Listing 11. monitorUsage.pl 파이프 읽기
    }else
    {
      # 파이프에 자료가 넘어오면 활동하고 있음을 알려준다.
      if( $pipe->pending )
      { 
        # 파이프를 정리한다.
        while( $pipe->pending ){ my $line = $pipe->dequeue or next }

        $app{ cmdRun } = 0;
        $app{ lastActivity } = 0;

응용 프로그램이 일정 기간 동안 비활성 상태인 경우에만 규칙을 확인하고 명령을 실행한다. 그러므로 키보드 입력이나 마우스 움직임은 응용 비활성 타이머를 재설정한다. Listing 12는 파이프에 사용자 활동 자료가 없을 때 실행되는 코드다.


Listing 12. monitorUsage.pl 규칙 확인
      }else 
      {
        $app{ lastActivity }++;
        print "no events for window $windowName last "
        print "activity seconds $app{lastActivity} ago\n";
      
        my $currTime = `date +%H%M`;

        for my $ruleNum ( 0..$ruleCount-1)
        {
          next unless( $app{cmdRun} == 0 );

          next unless( $windowName =~ /$rules[$ruleNum]{appName}/i );

          next unless( $app{lastActivity} >= $rules[$ruleNum]{timeOut} );

          next unless( $currTime >= $rules[$ruleNum]{start} &&
                       $currTime <= $rules[$ruleNum]{stop} );

          my $conditions = 0;
          $conditions++ if( $rules[$ruleNum]{cpu}  eq "null" );

          $conditions++ if( $rules[$ruleNum]{disk} eq "null" );

          $conditions++ if( $rules[$ruleNum]{cpu}  ne "null" &&
                            $rules[$ruleNum]{cpu}  <= $cpu );

          $conditions++ if( $rules[$ruleNum]{disk} ne "null" &&
                            $rules[$ruleNum]{disk} <= $mbread_s );

          next unless( $conditions > 1 );

          print "running $rules[$ruleNum]{cmd}\n";
          $app{ cmdRun } = 1;
          system( $rules[$ruleNum]{cmd} );

        }# 개별 규칙을 처리한다.

      }# 해당 윈도우에 대한 사건을 감지했을 경우

    }# 파이프를 설정했을 경우

  }# 디바이스가 연결되어 있을 경우

}# 입력받는 동안

앞서 살펴본 목록에 따라 개별 규칙을 확인한다. 가장 먼저 규칙에서 지정하는 명령을 이미 실행했는지 확인한다. 다음으로 응용 프로그램 이름을 확인한다. "Controlling ACPI Centrino features / enhanced speedstep via software in Linux - Mozilla Firefox"와 같은 이름은 "firefox"라는 응용 규칙 이름과 일치한다. 다음으로, 비활성 기간(초)이 임계값에 도달했는지 확인한다. 이 때 규칙 파일에 지정된 시간 범위도 확인한다. 마지막으로 CPU 조건과 디스크 조건이 맞으면 명령을 실행한 후 다음 규칙으로 넘어간다. Listing 13은 monitorUsage.pl 프로그램이 사용하는 익숙한 createPipe 하위 루틴이다.


Listing 13. monitorUsage.pl createPipe 하위 루틴
sub createPipe
{ 
  my $cmd = shift;
  my $queue = new Thread::Queue;
  async{ 
      my $pid = open my $pipe, $cmd or die $!;
      $queue->enqueue( $_ ) while <$pipe>;
      $queue->enqueue( undef );
  }->detach;
  
  # 분리할 경우 프로그램을 마칠 때 스레드를 조용히 끝내도록 만든다.
  return $queue;

}#createPipe

monitorUsage.pl 사용법

monitorUsage.pl 프로그램을 실행하려면 명령행에서 iostat -m -d -c 1 | perl monitorUsage.pl을 실행한다. inactivityRulesFile 파일에 "beep"나 "xmessage" 같은 명령을 지정하면 조건이 맞았을 때 좀 더 효과적인 반응이 오므로 테스트하기에 편하다. 테스트 시나리오에서 규칙 시작 시간과 종료 시간을 넓힌 다음에 시도해 봐도 좋겠다.





결론, 추가 예제

지금까지 소개한 도구와 코드를 사용하면 응용 프로그램 사용 현황으로 몇 가지 규칙을 지정하여 전력 소모를 줄일 수 있다. 커널, hdparm, ACPI, CPU 설정을 조율한 후 위 응용 프로그램 감시기를 추가하면 좀 더 효율적으로 저전력 모드로 들어갈 수 있다. focusTracker와 kst를 사용하여 비활성 기간을 찾은 다음 세상을 녹색으로 만들게끔 전력을 절약할 규칙을 세워보자.






다운로드 하십시오

설명 이름 크기 다운로드 방식
예제 코드 os-smart-monitors-InactivityDetector.0.1.zip 4KB HTTP

반응형

rath님의 블로그에서 퍼온 글입니다. 하루에 한번씩은 방문하는 블로그인데요..

좋은 글들과 자료들이 많네요.. 그리고 여러가지 문서를 번역하시는 분이시기도 하구요.(Java API등..)

이번에도 좋은 글이 있어 퍼왔습니다. rath님이 번역해서 한글 번역본을 올리셨다는 군요.

아래 링크에..

http://www.markforster.net/autofocus-system/

Autofocus 시간관리 시스템

 

이 시스템에서 무엇을 기대할 수 있나요?

지금부터 말씀드릴 것은 지난 몇주간 이 시스템을 사용하며 느낀 순수한 제 경험에 바탕한 것들로 아래와 같은 것들이 있습니다.

 

엄청나게 올라간 업무 처리량. 일을 훨씬 빨리 처리할 수 있다는 것을 알게 되었습니다. 이것은 저항이나 꾸물거림 같은 것들이 없기 때문인 것 같습니다.

적은 스트레스. 분명 일을 하긴 해야 합니다만, 극복해야할 반발심이나 귀찮은 감정들이 거의 생기지 않습니다. 사실 모든 일들이 즐거운 것이 되었지요. 제가 이 시스템을 믿는 것을 배우면 배울수록 더 즐거운 것이 되었습니다.

중요한 것에 집중하기. 한 사람의 이성적인 마음만으로는 중요한 것에 집중하기가 대단히 어렵습니다. 왜냐하면 자의식이 중요하다고 생각한 것과 무의식이 중요하다고 생각한 것은 다를 수 있기 때문입니다. 여기서 제가 발견한 것은 제가 끝낸 일들을 뒤돌아 보며 시스템이 집중하라고 한 것들이 ‘옳았다’고 느껴진 것입니다.

잡일들의 매우 빠른 처리. 하지 않으면 안되는 다양한 기계적인 일들을 처리하는 속도가 기하급수적으로 빨라졌습니다. 제가 생각하는 잡일의 종류는 이메일에 답장하기, 블로그 코멘트에 응답해주기, 회신 전화걸기 등입니다. 

중요한 일과 프로젝트들의 철저한 처리. 이 시스템은 중요한 일들에 대해 ‘조금씩 자주’ 접근하는 것을 장려합니다. 그 결과 이 방법론에 의해 프로젝트는 (이 시스템을 사용해보는 것과 같은) 일정 주기를 가지고 처리하는 것이 된다는 겁니다. 이 ‘조금씩 자주’ 접근법의 또 하나의 이점은 한 작업에 주기적으로 관심을 쏟게 해서 아이디어나 통찰들이 자연스럽게 떠오른다는 것이지요.

 

Quick Start

이 시스템은 줄쳐진 노트(한 페이지에 25~35줄 정도라 이상적입니다)에 해야할 모든 일들을 적은 긴 목록으로 이루어져 있습니다. 새로운 할 일이 생각났으면 목록의 맨 끝에 추가하세요. 다음과 같이 한번에 한 페이지에서만 일하게 됩니다.

  1. 해당 페이지의 모든 할 일들을 빠르게 읽어본다. 읽기만 하고 어떤 추가 행동도 하면 안된다.
  2. 하고 싶은 일이 떠오를 때까지 그 페이지의 할 일들을 천천히 다시 읽어본다.
  3. 그 할 일을 하고 싶은 기분이 지속되는 한 계속 한다.
  4. 목록에서 그 할 일에 줄을 긋는다. 만약 그 일을 다 끝내지 못했다면 목록의 맨 끝에 다시 넣는다.
  5. 같은 방법으로 그 페이지 내에서 계속 반복한다. 어느 일도 하고 싶지 않을 경우를 제외한다면, 그 페이지의 모든 일을 끝내기 전까지 다음 페이지로 가면 안된다.
  6. 다음 페이지로 이동하여 이 과정을 반복한다.
  7. 만약 페이지를 넘겼는데 떠오른 일이 하나도 없다면, 목록의 맨 끝에 넣는 작업없이 그 페이지의 모든 일들을 취소한다. (이 규칙은 마지막 페이지에는 적용되지 않는다. 마지막 페이지는 아이템을 계속 추가할 것이기 때문이다.) 취소한 아이템들을 표시할 때는 형광편을 쓴다.
  8. 마지막 페이지까지 다 끝냈으면 아직 끝내지 못한 일이 있는 첫 페이지로 가서 다시 시작한다.

아래에서는 각 단계들을 더 자세히 설명할 것이긴 합니다만 지금 바로 일하러 가고 나머지를 읽어보는 것은 나중에 하기를 제안합니다. “설명서 마저 읽어보기”를 할 일로 넣어두는 것을 잊지 마세요. 물론 처음부터 수많은 할 일 목록을 구성할 필요는 없습니다. 그저 생각난 것을 할 일 목록에 추가하면 언젠가 당신 앞에 나타날 것입니다.

 

자세한 사용법

 

새로운 할 일이 생각나면 목록의 끝에 추가한다.

이 시스템의 특성 중 하나는 여러분은 어떤 할 일도 없애버릴 수 있다는 것입니다. 생각난 모든 것들을 목록에 넣을지 말지 평가하지 말고 일단 목록에 넣는 것을 권장합니다. 평가는 시스템이 해줄 것입니다.

 

해당 페이지의 모든 할 일들을 빠르게 읽어본다. 읽기만 하고 어떤 추가 행동도 하면 안된다.

페이지를 빨리 훑어보는 것은 당신의 마음에 어떤 압박도없이 일을 시작하도록 해줍니다.

 

하고 싶은 일이 떠오를 때까지 그 페이지의 할 일들을 천천히 다시 읽어본다.

이것이 이 시스템의 핵심입니다. 마음속으로 할 일의 우선순위를 매기려들지 마세요. 그러면 이성적인 마음과 직관적인 마음 사이의 균형이 흔들릴 것입니다. 대신 하나의 일을 하고 싶은 기분이 들 때까지 기다리세요. 이러한 과정은 설명하기는 힘들지만 인지하기는 쉽습니다. 당신은 그저 어떤 일이 완료될 준비가 된 것이라고 느낄 것입니다. 페이지 아래쪽으로 내려가다보면 어떤 일이 당신의 마음을 빼앗아 버려서 다시 올려다보게 하는 것을 느낄 수 있습니다. 어떤 일에 대한 감정이 생기고 나면 일을 함에 있어 느껴지는 모든 저항감들이 사라지고 그저 하기 쉬운 일이 됩니다. 

 

그 할 일을 하고 싶은 기분이 지속되는 한 계속 한다.

더이상 그 일을 계속 하는 것이 옳다고 느껴지지 않는다면 절대 자기 자신에게 그 일을 계속하도록 강제하지 마세요. 이 시스템은 ‘조금씩 자주’ 접근법을 장려합니다. 할만큼 했다는 기분이 들면 그만하세요.

 

목록에서 그 일에 줄을 긋는다. 만약 그 일을 다 끝내지 못했다면 목록의 맨 끝에 다시 넣는다.

사실 목록의 맨 끝에 먼저 넣고나서 줄을 긋는 것이 더 좋습니다. 그래야 어디까지 진행했는지 잊을 확률이 적기 때문이지요. 하지만 저는 이런 순서를 잘 기억하지 못한다는 것을 인정할 수밖에 없군요. 아이템을 다시 넣는 것은 이 시스템의 필수적인 부분입니다. 여러분은 주기적으로 처리해야 하는 일들을 끝냈을 때마다 아이템을 맨 끝에 다시 넣어야 합니다. (이메일, 문서 작업, 운동하기 같은) 아직 완성되지 않은 것들도 모두 넣어야 하고요 (기사나 레포트의 초안같은), 체크할 필요가 있는 모든 일들도 넣어야 하며, (마이크가 이메일에 응답을 했었나? 같은) 책이나 잡지, 저널을 읽는 것처럼 시간이 오래 걸리는 작업들도 넣어야 합니다. 바로 이어서 할 일들이나 추적해야할 아이템들도 넣을 수 있습니다. 제 경우 일들의 대략 1/3 정도가 다시 넣어야 되는 작업들이였습니다.

 

같은 방법으로 그 페이지 내에서 계속 반복한다. 어느 일도 하고 싶지 않을 경우를 제외한다면, 그 페이지의 모든 일을 끝내기 전까지 다음 페이지로 가면 안된다.

각 페이지를 하나의 단위(unit)로 간주하는 것은 당신이 ‘구조화된 꾸물거림’의 이점을 얻을 수 있게 해줍니다. ‘구조화된 꾸물거림’이란 것은 꾸물거림이 상대적이다라는 사실에 근거한 것입니다. 말하자면 어떤 일을 하는 것과 더 어려운 다른 일을 하는 것 사이에 하나의 선택 밖에 없다면 그 무엇도 쉬운 것이 된다는 것이지요.

 

다음 페이지로 이동하여 이 과정을 반복한다.

여러분은 한 페이지를 아주 빠르게 훑어볼 수도 있고 일을 어떻게 할 것인지 충분히 생각해볼 수도 있습니다. 어떤 방법을 사용해도 상관없습니다. 그저 그 방법이 당신을 ‘떠오름’의 방법으로 안내해주기만 한다면요.

 

만약 페이지를 넘겼는데 떠오른 일이 하나도 없다면, 목록의 맨 끝에 넣는 작업없이 그 페이지의 모든 일들을 취소한다. 취소한 아이템들을 표시할 때는 형광편을 쓴다.

시스템에 어떠한 평가 과정도 거치지 않고 집어넣은 모든 아이템들을 어디서 없애버릴지에 대한 것입니다. 여기서 평가라는 것은 시스템이 일들을 분류하고 원하는 것을 찾는다는 것이지요. 이것은 아주 빠르게 처리할 수도 있습니다만 (예를들면 읽어야겠다고 생각한 책들의 긴 목록을 집어넣었다거나) 다소 천천히 하는 것이 좋습니다.

이 아이템들을 다시 집어 넣지 않아야 한다는 규칙을 심도있게 받아들여주세요. 결코 이 아이템들을 다시 집어 넣으면 안된다는 얘기를 하는 것이 아닙니다만 그렇게 하기 전에 충분한 시간을 쓰는 것이 좋고 그 일들이 왜 없어져야 하는지, 정말 끝내야 될 필요가 있는 것인지, 완료되기 위해 아직 시간이 더 필요한 것인지, 주 목표를 방해하는 것들인지 등에 대해 조심스럽게 고민해보라는 것입니다. 취소된 일을 없애버리거나 그 일을 나타내는 말을 여러가지 다른 방법으로 재표현해볼 수 있는 최상의 시간은 그 일을 다시 넣으려고 할 때 입니다.

취소된 아이템들을 형광펜으로 칠해두는 것은 이것들을 리뷰하기 쉽게 만들어줍니다.

이 규칙은 할 일을 계속 적어나가고 있는 페이지에는 적용되지 않는다는 것을 꼭 기억하세요. (맨 마지막 페이지)

 

마지막 페이지까지 다 끝냈으면 아직 끝내지 못한 일이 있는 첫 페이지로 가서 다시 시작한다.

저는 더이상 할 일이 없는 페이지의 상단 구석에 표시를 해둡니다. 그리고 이전 페이지까지 끝내지 못한 일이 하나도 없는 페이지라면 줄 친 곳 주변에 원을 그려 넣습니다. 이렇게 하면 활성화된 일이 있는 첫번째 페이지를 쉽게 찾을 수 있습니다. 그리고 시간이 흘러감에 따라 첫 활성화 페이지가 몇 개인지 여러가지 방법으로 둘러볼 수 있을 것 입니다. 제가 이 글을 쓰는 시점에서는 9개의 페이지가 있었지만 그것은 3~4페이지에서 15페이지까지 다양했습니다.

제 경험에 의하면 이 시스템은 중독성이 꽤 강합니다. 그러므로 당신은 하루에 일하는 시간을 정확하게 정해두는 것이 필요하다는 것을 알아낼 수 있을 겁니다. 시간이 다 되면 일하기를 그만두세요, 그리고 일을 시작할 때가 됐을때만 동일한 위치에서 다시 시작하세요. 솔직히 이 조언을 제 자신도 잘 못지키고 있음을 인정해야겠네요.

 


밀린 작업들

이 시스템을 도입할 때에도 밀린 작업들이 있을 것 입니다. 잔여작업들이 남아있다면 저는 그 모든 일들을 시스템에 한번에 넣고 시스템에 알아서 감별하도록 둘 것을 추천해드립니다. 만약 그 중 일부분을 취소하는 것이 더 좋겠다고 생각했다면 이 일들이 정말 필요한 것인지 스스로에게 심각하게 질문을 던지는 과정을 거쳐야 합니다.

아무튼 아직 처리하지 못한 이메일이나 문서 업무들이 상당히 많다면 이것을 시스템에 넣는 것은 위험합니다. 왜냐하면 새로 도착한 이메일이나 문서 작업들의 업무 효율이 떨어질 수 있기 때문이지요. 그래서 저는 잔여작업들을 위한 별도의 폴더를 만들어서 그 폴더들에게 “밀린 이메일들”, “밀린 문서 작업들”로 이름을 지으시고 “깨끗한 이메일들”, “깨끗한 문서 작업들” 로 이름을 지으시길 추천해드립니다.

 

이것이 왜 동작하는가

이 시스템은 뇌의 이성적인 부분과 직관적인 부분 사이의 균형을 맞춰주는 틀을 제공해줍니다.

만약 우리가 이성적인 뇌만 가지고 살아가려고 한다면, 우리의 계획을 우리들의 마음이 망쳐버릴 확률이 높습니다. 왜냐하면 우리들의 마음은 이성적인 기반으로만 동작하는 것이 아니기 때문이지요. 우리들 대부분은 (이성적인 뇌가) 확실한 것이 제공되기를 좋아하지만 그럼에도 불구하고 우리의 자연스러운 성향은 그것들을 거부하려고 한다는 것을 경험을 통해 알고 있습니다.

반면에 우리가 자연스러운 성향만을 따라가며 살려고 한다면, 추진력에 휩쓸리고 충동적이 되고 분별없이 행동하게 됩니다. 

어쨌든 이 두가지 사고방식이 균형을 이룰 때, 우리의 느낌과 감정이 충분히 조화를 이룬 상태의 합리적인 결정을 내릴 수 있습니다. Autofocus 시스템이 이것을 가능하게 해주는 틀을 제공하는 것이고요. 비록 제가 ‘시스템’이라는 단어를 사용했지만 제가 정말 말하고자 하는 것은 이 시스템은 스트레스를 받지 않고도 균형잡힌 결정을 할 수 있게 도와주는 틀을 제공한다는 것입니다.

 

해야하는 것과 하지 말아야 할 것들

  • 이 시스템을 신뢰하세요. 잘 구조화된 이 방법은 당신에게 도움없이 일을 하는 것보다 더 나은 결정을 내릴 수 있게 도와줄 것입니다.
  • 상식을 사용하세요. 만약 어떤 일을 지금 당장 해야될 필요가 있다고 생각되면 그 일을 바로 하세요.
  • 시스템에 작업을 넣기 전에 수정하려고 하지 마세요. 그렇게 한다면 시스템보다 비효율적인 우선순위 선별방법을 사용하게 될 것입니다.
  • 각 일들이 다른 주기를 가지고 움직일거라고 기대하세요. 어떤 일을 빠르게 움직이고 어떤 것을 느리게 움직이고 어떤 일은 일정 시간동안 멈춰있을 것이고 어떤 일은 완전히 거절될 것입니다. 일들이 어떻게 가정될 수 있는지를 보여줍니다.
  • 특정 날짜나 시간까지 끝내야하는 일은 이 시스템에 넣지 마세요. 식사 준비하기, 음악 연습, 가게 문 닫기와 같은 것들이 포함됩니다.
  • 작업 하나를 끝내고 페이지로 돌아갔을 때, 아직 떠오르지 않은 모든 아이템들을 다시 읽어보세요. 무엇을 끝내야 하는지에 대한 전체적인 모습을 되돌아 볼 수 있도록 도와줄 것입니다.
  • 추적해야할 일(follow-up)이나 까먹지 않아야할 것(reminder)들도 시스템에 넣는 것을 잊지 마세요. 이것들은 작업의 흐름을 놓치지 않기 위한 필수적인 방법입니다.
  • “~에 대해서 생각하기”, “~ 연구하기”, “~ 토론하기”, “~ 리뷰하기” 같은 창의적인 일들도 넣어두세요.
  • 각 날마다 첫번째 할 일에게 날짜를 적어두세요. 시스템에 필수적인 것은 아니지만 진행 상황을 보는데 도움이 됩니다.
  • 기록한 아이디어들과 여러분 마음속에 들어온 작업들의 의미들을 목록에 적지 않은 상태로 내버려두지 마세요.
  • 미래에 완료될 수 있다고 공표한 작업들의 의미를 사용하세요. (수첩, Outlook reminders, Calendar 등)
  • 각 장소마다 다른 공책을 사용하세요. 집, 작업실 처럼요.

+ Recent posts