Showing posts with label 프로그래밍 도구와 언어. Show all posts
Showing posts with label 프로그래밍 도구와 언어. Show all posts

2010-08-16

[링크] Invalid XML Character

UTF-8 인코딩을 준수하는 문자 중에도 XML에서는 유효하지 않은 문자가 있다. (CDATA 혹은 escape 처리해야하는 <, > 등과는 별개로)


아래 링크는 이 유효하지 않은 XML 문자를 제거하는 방법을 다룬 블로그 포스트이다.

Invalid XML Characters: when valid UTF8 does not mean valid XML

2010-04-27

JUnit 이야기

JUnit에 대해서 새롭게 알게 된 사실들.


실행 시간을 기준으로 테스트 실패 여부를 판단할 수 있다.

@Test(timeout = 130)
public void something() {
    ...
}


Ignore Annotation으로 테스트가 실행되지 않도록 할 수 있다.

@Test
@Ignore
public void something() {
   ...
}


TestSetup 클래스로 전체 테스트에 대한 초기화를 수행할 수 있다.

동일한 테스트를 파라미터화해서 여러번 수행할 수 있다.

@RunWith(value = Parameterized.class)
public class SomeTest {

    @Parameters
    public static Collection<Some> getSomes() {
        return ...
    }

    public SomeTest(Some some) {
        ...
    }

    @Test
    ...
}



EasyMock과 JMock이라는 Mock 라이브러리가 있다.

Ant를 사용할 때 Ivy를 이용해서 라이브러리를 관리할 수 있다.


Winstone이라는 서블릿 엔진이 있다.

hamcrest 라이브러리로 assert 기능을 확장할 수 있다.

HtmlUnit, Selenium, HtmlUnit, JsUnit, Rhinounit, Jslint, jslint4java

Cobertura
라는 테스트 커버리지 툴이 있다.

2010-04-19

서평 - Modular Java: Creating Flexible Applications with OSGi and Spring

Modular Java: Creating Flexible Applications with OSGi and Spring에 대한 서평이다.

200 페이지가 조금 넘는 분량으로 OSGi 기반 웹 애플리케이션 개발 방법을 Step-by-Step으로 설명하고 있다.

소스코드, OSGi 설정 파일 내용, Maven(Pax Construct) 실행 결과 등이 1/3을 가까이 차지하니 내용은 많지 않다.

OSGi가 무엇인지, OSGi를 이용해서 웹 애플리케이션을 어떻게 개발해야 하는지를 파악하는데 부담없으면서 유용한 책이다.

부정적으로 본다면 다음 아마존 독자 서평에 동의한다.


스스로도 이클립스가 OSGi에 기반하고 있다는 정도로만 OSGi를 접하고 있었는데 이 책을 통해서 다음 내용들을 알 수 있었다.

  • OSGi를 구현한 플랫폼에는 Eclipse EquinoxApache Felix 등이 있다. 이 책에서는 주로 Equinox(뭐라고 발음해야 하는지^^)를 다룬다.

  • 모든 것이 모듈이다. 라이브러리도, 유틸리티나 모델(Bean이든 Domain 객체 등)도... 그리고 톰켓(혹은 Jetty)도 모듈이다.

  • Maven 기반 Pax Construct로 개발을 진행한다. EJB 만큼은 아니지만 소스코드보다 설정 파일이 많은 느낌이다. 이를 편리하게 해주는게 Pax Construct라는 도구이다.

  • Spring Dynamics Module로 OSGi 개발을 편리하게 진행할 수 있다(라고 쓰고 주장한다라고 읽는다). 잘 알지도 못하지만 여전히 Spring에는 정이 가지 않는다.


실패한 EJB보다는 여러가지 장점을 갖는다.

  • 모듈이 갖는 적절한 크기. EJB는 너무 작았다. 그래서 모듈이 아닌 컴포넌트라는 이름을 사용했겠지만...

  • SOA in JVM. 빌어먹을 Remote^^

  • 확장이 유연하다. Fragments를 통한 모듈 확장이 가능하다. Eclipse가 보여준 Extenion과 Extension Point를 이용한 확장이 이것이 단순한 마케팅 용어가 아님을 증명한다.

  • 버전 관리. 동일한 모듈의 다양한 버전이 공존할 수 있다.

  • 라이브러리나 공통 클래스(유틸리티, Bean, Domain 객체)를 모듈로 공유하는 방법이 명확하다.

2010-04-13

데이터 유형과 의도적 제한

프로그래밍 언어들은 다양한 데이터 유형을 제공한다. 가령 자바가 제공하는 데이터 유형은 다음과 같다.

  • byte
  • short
  • int
  • long
  • float
  • double
  • boolean
  • char
  • String

* java.lang 패키지에 있는 Wrapper 클래스를 고려하면 2배가 된다.

그런데 애플리케이션 개발에 쓰는 데이터 유형 숫자를 최소화하면 생산성이 높아지고 유지보수 비용이 감소하지 않을까?

정수를 표현할 때 short나 byte는 사용하지 않고 int와 long만 사용하거나 극단적으로 long만을 사용하면 어떨까?

실수도 float 대신에 double 만을 사용하고 모든 문자열은 String으로만 표현한다든가...

* 아주 극단적으로 모든 숫자를 double로 표현할 수도 있다.


예외적인 경우를 제외한다면 이런 결정이 CPU 성능이나 메모리 양에 미치는 영향도 감내가능하지 않을까?

일단 기본적으로는 다음 데이터 유형만을 사용해서 애플리케이션 개발을 하려고 한다.

  • int
  • long
  • double
  • boolean
  • String

* 정말 int, long은 사용하지 않고 double만을 사용하면 어떨까?

이런 결정은 관계형 데이터베이스 테이블 스키마에도 선순환적 영향을 미칠 것이다.

2010-04-01

Ant 이야기

아파치 Ant를 쓴지도 8년이 넘었지만 제대로 파악하지 않고 대충 아는 지식에만 의존했기 때문에 항상 결과물에 모자람을 느껴왔다.

그러다 지금하는 일의 완성도를 높여야 한다는 당위성에 Ant에 시간 투자해서 얻은 경험 중 중요한 몇 가지를 정리해봤다.

Ant 작성에도 프로그래밍과 유사한 원칙들이 적용된다.

  • 내용 이해가 쉬워야 한다.
  • 중복된 내용이 없어서 변경하기 쉬워야 한다.

이런 원칙을 염두하고 다음 방법들을 사용했다.


이름을 명확하게


빌드 파일, 속성, 태스크 등의 이름을 길더라도 명확하게 설정한다.

예를 들어 테스크 이름을 copy로 하기보다는 copy_lib_to_web과 같이 명확하게 한다.


빌드 파일을 여러 개로 분리해서 모듈화 한다.

OOAD에서 클래스를 쪼개는 것과 동일하다. import 태스크와 antcall 태스크로 각 모듈을 효과적으로 조합할 수 있다.


라이브러리 작성

여러 빌드 모듈에서 사용하는 것들을 별도 파일에 정의한다. path 정보와 함수 역할을 하는 태스크 등이 여기에 해당된다.

라이브러리 모듈을 잘 작성하면 Ant 파일 내용을 크게 줄일 수 있다.

library.xml

<project name="library">

    <property file="build.properties" prefix="dimdol." />

    <path id="path.web.lib">
        <fileset dir="./lib">
            <include name="commons-fileupload-1.2.1.jar" />
            <include name="commons-io-1.4.jar" />
            <include name="commons-lang-2.4.jar" />
        </fileset>
    </path>

    <target name="library.compile">
        <javac srcdir="${source}"
               destdir="${target}"
               classpathref="${classpath}"
               source="${dimdol.compile.option.source}"
               target="${dimdol.compile.option.target}"
               debug="${dimdol.compile.option.debug}"
               encoding="${dimdol.compile.option.encoding}" />
    </target>

</project>

build_base.xml

<project name="build base" default="default">

    <import file="library.xml" />

    <target name="default">
        <antcall target="library.package_jar">
            <param name="source" value="${dimdol.base.src}" />
            <param name="target" value="${dimdol.base.target}" />
            <param name="classpath" value="path.web.lib" />
            <param name="jar" value="${dimdol.base.jar}" />
        </antcall>
    </target>

</project>

build_crm.xml

<project name="build crm" default="default">

   <import file="library.xml" />

   <target name="default">
       <antcall target="library.package_jar">
           <param name="source" value="${dimdol.crm.src}" />
           <param name="target" value="${dimdol.crm.target}" />
           <param name="classpath" value="path.web.lib" />
           <param name="jar" value="${dimdol.crm.jar}" />
       </antcall>
   </target>

</project>


컴파일을 담당하는 library.package_jar 태스크와 컴파일에 필요한 클래스 패스를 정의한 path.web.lib 패스를 library.xml 파일에 정의하고 build_base.xml, build_crm.xml 파일은 이 파일을 import한다.

* include가 더 유연하지만 Ant 1.8 부터 사용할 수 있다.


property는 properties 파일에 설정한다.

properties 파일에서도 ${}를 통해서 내용을 최소화한다.

build.properties

source = ./source
target = ./target
jar = ./jar

base.src = ${source}/base
base.target = ${target}/base
base.jar = ${jar}/base.jar

crm.src = ${source}/crm
crm.target = ${target}/crm
crm.jar = ${jar}/crm.jar

그런데 이 내용을 Ant 파일에서 사용할 때는 다른 많은 property와 쉽게 구분하기 위해서 prefix를 붙여 사용한다.

<property file="build.properties" prefix="dimdol." />

따라서 실제 사용할 때는 다음과 같이 한다.

<antcall target="library.package_jar">
   <param name="source" value="${dimdol.base.src}" />
   <param name="target" value="${dimdol.base.target}" />
   <param name="classpath" value="path.web.lib" />
   <param name="jar" value="${dimdol.base.jar}" />
</antcall>


2010-03-31

OS 스크립트에서 변수 설정 여부 확인하기

윈도우 스크립트(bat) 파일에서는 다음과 같이 변수 설정 여부를 확인한다.

if "%STARTUP_PORT%" == "" SET STARTUP_PORT=8080
if "%SHUTDOWN_PORT%" == "" SET SHUTDOWN_PORT=8005


유닉스 혹은 리눅스(sh)에서는 다음과 같이 변수 설정 여부를 확인한다.

STARTUP_PORT="${STARTUP_PORT}"
SHUTDOWN_PORT="${SHUTDOWN_PORT}"

if [ "${STARTUP_PORT}" = "" ]
then
    STARTUP_PORT="8080"
fi

if [ "${SHUTDOWN_PORT}" = "" ]
then
    SHUTDOWN_PORT="8005"
fi


2010-01-13

Putty에서 캐릭터 셋 고정

Putty에서 캐릭터 셋을 UTF-8로 변경해도 다시 접속하면 값이 초기화되는 문제가 있었다.

이를 해결하는 방법이다.

http://k.daum.net/qna/view.html?qid=3yKL8



결론은 계정 정보 자체를 저장해야 한다는 것이다... 직관적이지 못하다...

2010-01-12

Color...

00FF66과 같은 HEX 문자열로 java.awt.Color 객체를 만들어야 했다.

Color 클래스 생성자는 RGB 값만을 파라미터로 받으니 귀찮은 데이터 전환을 해야 하나 고민하다가... 다음 메소드를 이용하면 된다는 걸 알았다.

Color color = Color.decode("00FF66");

그런데 예외가 발생했다. 앞에 #을 붙여 주어야 했다.

Color color = Color.decode("#00FF66");


그런데 스택트레이스 덕분에

Exception in thread "main" java.lang.NumberFormatException: For input string: "0ff66"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
    at java.lang.Integer.parseInt(Integer.java:458)
    at java.lang.Integer.valueOf(Integer.java:528)
    at java.lang.Integer.decode(Integer.java:958)
    at java.awt.Color.decode(Color.java:707)

실제로는 Integer 클래스의 decode 메소드를 사용하면 된다는 것도 알았다.


2010-01-07

이클립스에 static import 처리

이클립스에서 static import를 편리하게 사용하려면 다음 설정을 해준다.

1. Organize Import 처리

http://www.prever.co.kr/josh/?p=35

2. Code Assist 처리

http://blog.naver.com/kcufl/60094963610

2009-12-10

Java EE 6.0

Java EE 6.0이 공식적으로 완결되었다.

Introducing the Java EE 6 Platform



한마디로 Java EE 6.0을 정의하면 Ruby on Rails 따라하기가 되지 않을까...

짧아도 2년 주기로 버전 업그레이드가 되니 지금 좁혀진 간격은 (몇달 안에) 다시금 넓어지겠지...



그래도 톰켓 7.0은 기대가 된다. 빨리 나와라^^

Mark Thomas on Apache Tomcat 7

2009-12-07

아파치 더비 - 칼럼 크기 수정

ALTER TABLE USER ALTER NAME SET DATA TYPE VARCHAR(100);

2009-11-29

Ruby on rails에서 테이블없이 모델 사용하기

Ruby on rails에서 테이블에 매핑되지 않는 입력 폼을 검증할 필요가 있었다.

구글링에서 얻은 가장 만족스러운 방법은 다음이다.

Tableless models in RailsTableless models in Rails


플러그인이나 라이브러리도 필요 없고, 직접 칼럼을 선언하면 된다. 자바에서 get/set을 만드는 걸 생각하면 아무 것도 아니다.

class OnlineDemoRequest < ActiveRecord::Base
  def self.columns() @columns ||= []; end
 
  def self.column(name, sql_type = nil, default = nil, null = true)
    columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
  end
 
  column :email, :string
  column :first_name, :string
  column :last_name, :string
  column :company, :string
 
  validates_presence_of :email
  validates_format_of :email, :with => /^$|^\S+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,4})(\]?)$/ix
  validates_presence_of :first_name
  validates_presence_of :last_name
  validates_presence_of :company

end


* 2.3.4에서 테스트를 했다.

Procedure와 Function

메소드는 Procedure와 Function으로 구분된다.

기계적이거나 배타적인 구분을 의미하지는 않는다.

Procedure는 상태의 변화(side effect)를 야기한다.

Function은 무언가를 반환한다.

2009-11-26

AJAX를 사용할 수 없을 때

도메인이 다르면 AJAX를 사용할 수 없다. 자바 스크립트로 이를 흉내내는 방법이 있다.

var HTTP = {};

HTTP.getTextWithScript = function(url, callback) {
    var script = document.createElement("script");
    document.body.appendChild(script);
    var funcname = "func" + HTTP.getTextWithScript.counter++;
    HTTP.getTextWithScript[funcname] = function(text) {
                                           callback(text);
                                           document.body.removeChild(script);
                                           delete HTTP.getTextWithScript[funcname];
                                       }
    var emptyImage = new Image();
    emptyImage.onerror =
        function() {
            Progress.hide();
            callback("<div>Error Happened</div>");
        };
    emptyImage.onload =
        function() {
            script.src = HTTP.url + url + "&func=" + encodeURIComponent("HTTP.getTextWithScript." + funcname);
        };
    emptyImage.src = HTTP.url + "/empty.gif";
}

HTTP.getTextWithScript.counter = 0;

해당 서버에 empty.gif 이미지가 존재해야 한다. 서버가 응답하면 문제가 없지만 그렇지 않으면 행이 걸릴 수 있다. 이를 방지하기 위해서 이미지 파일를 이용한다.



사용 방법은 다음과 같다.

HTTP.url = url;
Progress.show("result");
HTTP.getTextWithScript("/some_request",
                       function(text) {
                           $("result").innerHTML = text;
                           Progress.hide();
                           DocResize.adjustHeight();
                       }
                       );



자바(서버 사이드)에서는 다음과 같이 처리한다.

JavascriptCallResponse javascriptResponse = new JavascriptCallResponse(response);
include(request, javascriptResponse, "actual_page.jsp");

response.setContentType("text/javascript; charset=UTF-8");
String func = request.getParameter("func");
PrintWriter writer = response.getWriter();
String text = StringUtils.strip(javascriptResponse.getContent().toString()).replaceAll("(?m)\\s+", " ");
writer.write(func + "('" + text + "');");

실제 화면 로직 처리는 actual_page.jsp에 있다.


JavascriptCallResponse 클래스는 다음과 같다.

package com.dimdol.example;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class JavascriptCallResponse extends HttpServletResponseWrapper {

    private PrintWriter writer;

    private StringWriter stringWriter;

    public ReportServletResponse(HttpServletResponse response) {
        super(response);
    }

    public String getContentType() {
        return "text/html; charset=UTF-8";
    }

    public PrintWriter getWriter() throws IOException {
        if (writer == null) {
            stringWriter = new StringWriter();
            writer = new PrintWriter(stringWriter);
        }
        return writer;
    }

    public StringBuffer getContent() {
        return stringWriter == null ? new StringBuffer() : stringWriter.getBuffer();
    }

}



* 보안 문제일까? 일부 웹 브라우저에서는 동작하지 않는다. 그래서 이 방법보다는 서버에서 프락시 방식으로 처리하는 걸 선호한다.


* JavaScript: The Definitive Guide에 있는 내용을 참조

Guard Clause

이 코드보다는

void initialize() {
  if (!isInitialized()) {
    ...
  }
}


이 코드가 좋다

void initialize() {
  if (isInitialized()) {
   return;
  }
  ...
}



이 코드를 보면 더 명확하다.

void compute {
  Server server = getServer();
  if (server != null) {
    Client client = server.getClient();
    if (client != null) {
      Request current = client.getRequest();
      if (current != null) {
        processRequest(current);
     }
  }
}


void compute {
  Server server = getServer();
  if (server == null)
    return;
  Client client = server.getClient();
  if (client == null)
    return;
  Request current = client.getRequest();
  if (current == null)
    return;
  processRequest(current);
}



* 켄트 백의 구현 패턴 7장 Behavior에서 발췌

2009-11-25

내가 사용하는 소프트웨어

내가 사용하는 소프트웨어들이다.

운영 체계는 윈도우 XP ㅠㅠ... 우분투 사용을 시도했지만 IE 테스트 때문에...

웹 브라우저는 파이어폭스...  주요 웹 브라우저들을 대부분 설치했지만 호환성 테스트가 아니면 사용하지는 않는다.

파이어폭스 플러그은 파이어버그만... 웹 개발자의 축복...


메일은 썬더버드(지메일에 붙어서)를 사용하는데... 지메일을 직접 사용하는 것도 괜찮을 듯...

메신저는 구글 톡만(친구가 별로 없다^^) 사용한다. 파일 전송을 하려면 영어판을 사용해야 한다.


백신은 회사에서 라이센스를 구매한 알약을 쓴다. ClamWin을 사용하기도 했는데...


동영상은 KMP를, PDF 문서는 Adobe Acrobat Reader로...

텍스트 편집 도구로는 Notepad++를...

간단한 그래픽 작업을 위해서 Paint.NET를... 닷넷을 설치해야 한다.

압축 프로그램으로는 빵집을...

텔넷/ssh 클라이언트로는 putty를...

CD를 구울 때는 InfraRecorder...

로그를 볼 때는 TailMe...



개발을 위해서 자바(JDK는 1.5와 1.6 모두, JRE는 1.6만)와 이클립스를...

플렉스 개발을 위해서 Flex SDK도...


기타 등등으로

- 클릭 투 트윅(알약에도 비슷한 기능이 있어서 지금은 사용하지 않는다.)
- 오라클 인스턴트 클라이언트
- Microsoft Script Debugger
- Linuxnew

예외 처리

이 코드보다는

try {
   ...
} catch (Exception e) {
   // ignore exception
}


이 코드가

try {
    ...
} catch (Exception ignore) {}

좋다.

2009-11-22

Ruby on rails에서 Gmail을 이용해서 메일 보내기

Ruby on rails에 기반한 애플리케이션에서 Gmail SMTP를 이용해서 메일을 보내야 했다.

구글링 결과 플러그인을 사용하면 된단다. 시도 때도 없이 플러그인(혹은 라이브러리)을 쓰는 건 피하고 싶기에 머뭇거렸다.

그런데 댓글들을 읽다가 플러그인 없이도 가능하다는 정보에 반색하며 시도해봤다.

우선, config/environment.rb 파일에 다음 내용을 추가한다.

ActionMailer::Base.smtp_settings = {
  :enable_starttls_auto => true,
  :address => "smtp.gmail.com",
  :port => 587,
  :domain => "mycompany.com",
  :authentication => :plain,
  :user_name => "username@mycompany.com",
  :password => "password"
}

enable_starttls_auto를 true로 설정해야 한다는 것을 제외하면 일반 설정과 다르지 않다.

그런데 이렇게 해도 메일이 발송되지 않았다. 에러 메시지는 없고 메일이 정상적으로 발송된 것 처럼 로그가 기록된다.


댓글들을 자세하게 읽어본 결과 루비 1.8.7 이상을 사용해야 한다고 한다. 내가 사용하는 윈도우 개발 환경은 1.8.6이었다.

루비 윈도우 인스톨 버전이 1.8.6이다.


루비 1.8.7이 설치되어 있는 리눅스 운영 환경에는 정상적으로 메일이 전송되는 것을 확인했다.


* Rails 버전은 2.3.2이다.

2009-11-09

자바 2D와 CPU 사용률

자바 2D로 액티브한 프로그램을 작성한 경우에 CPU 사용률을 몇 가지 옵션으로 최적화할 수 있다.

특히 하드웨어 가속 기능을 사용하는 경우에 역설적으로 더 느려지는 현상도 관찰된다. 하드웨어 가속을 OFF할 수 없는 환경에서 다음 옵션으로 자바만을 제어할 수 있다.

-Dsun.java2d.d3d=false

이렇게 설정하면 Direct3D를 사용하지 않게 되면서 하드웨어 가속 기능을 OFF한 것과 동일한 CPU 사용률을 보여준다.

* Direct3D 드라이버를 업데이트하면 하드웨어 가속기를 사용하면서도 성능 향상을 얻을 수도 있다.


또한 다음과 같이 설정하면 (Swing Back Buffer에 대한 DirectDraw와 Direct3D를 사용하지 않도록 설정)

-Dsun.java2d.ddoffscreen=false

CPU 사용률을 더 줄일 수 있다.


다음 옵션은 DirectDraw와 Direct3D를 아예 사용하지 않도록 하는 것인데 CPU 사용률 측면에서는 이전과 큰 차이가 없다.

-Dsun.java2d.noddraw=true (false가 아닌 true)


코딩 방법과 하드웨어에 따라서 상이한 결과가 나타날 수 있다.



[참고 자료]

http://java.sun.com/javase/6/docs/technotes/guides/2d/flags.htm

2009-10-27

USB로 우분투 설치하기

어제 DVD/CD 플레이어가 없는 컴퓨터에 우분투를 USB로 설치했다.

우선 우분투(8.04 LTS 서버/32 비트)를 여기에서 ISO 파일로 다운로드한다.

그리고 여기에서 UNetbootin, Universal Netboot Installer(unetbootin-windows-372.exe)를 다운로드한다.

* 윈도우즈 XP 기준


USB를 넣고 UNetbootin를 실행하여 다운로드 받은 우분투 ISO 파일로 설치 파일을 작성한다.


그리고 우분투를 설치할 컴퓨터에 USB를 넣고 부팅 디스크로 USB를 지정해주면 된다.