플렉스란 ?
내가 생각하기에는 플렉스 음 아주 이쁜 ui 웹 프로그램이다. 설정이 매우 복잡하다 외이리 복잡한지 ....
일반 프로그램처럼 소켓통신으로 웹화면을 수시로 바꺼줄수있다.. (이점이 매우 마음에 듣다).
할수잇는 방법은
xml 을 리턴받아 사용하는 방법
xml 하고 비슷한데 soap 를 사용하는 방법 << 요거는 공부를 더해야겠다 머 xml 하고 비슷한거같다
국제적 규약을 따라서 문서가 만들어지고 그것을 가지고 와서 데이터를만드는것 같다 ..
remoteObject  이방법 이방법은  flex 에있는 xml 에서 선언해주고 그 자바 클레스를 가져가다 사용하는 방법이다.. 이방법은 실시간으로 업데이트 안된다는 단점이 있는데 .. 사용하기에는 편한거 같다 클레스로 .. 파라메타를 가져다가  그값을 리턴 받을수 있는지는 아직 모르겟다.(좀더 공부해봐야 하는 과제이다).
메시지 서비스  --> 아직 안해봐서 모르겟다.
추론 : 일단 체팅처럼 메세지를 서로 교환 하는 방식이다 ..
일단   하나의 통신을열어서 거기다가 인풋을 가하면 그 객체를 사용하고잇는 다른 사용자들도 메세지를 받아 볼수 있는방법인거같다.
데이터 관리 서비스 --> 사용은 안해봤다 ..
추론 : 자바에서 쓰레드로 시간마다 돌면서 데이터를 인풋 해주는 경우이다. 이경우에는 사용자마다 다 틀리게 데이터를 받는게아니고 다 동일 하게 받는 거 같다 일단 봐야겠다.. 


요몇일 플렉스를 공부하면서  대략적인 나의 생각을 정리한것이다. 

 

FLEX는 encoding을 'utf-8'을 사용하고, JSP는 encoding을 'euc-kr'을 사용합니다. 그런데 tomcat을 encoding 방식을 한가지로 제공합니다. 이것때문에 2가지 이상의 Encoding방식이 필요한 경우 문제가 발생합니다. 즉, service.xml을 아래와 같은 방식으로 작성할 경우 FLEX에서 JSP로 정보가 넘어갈 때 한글은 깨어지게됩니다.

 <Connector  ...... URIEncoding="euc-kr" />

 

이럴 경우 각 페이지마다 필요한 encoding방식을 지정해야합니다.

 

즉, FLEX에서 JSP로 정보가 넘어올 경우만 "utf-8", 그 외는 "euc-kr"로 말입니다.

 

이렇게 할려면

 

1. service.xml에 URIEncoding="euc-kr"은 없애고 useBodyEncodingForURI="true"을 추가한다.

 <Connecter .... useBodyEncodingForURI="true">

 

2. 각 JSP에 request.setCharacterEncoding()을 필요한 encoding방식을 설정합니다.

 utf-8이 필요할 경우는 request.setCharacterEncoding("utf-8")로

 euc-kr이 필요할 경우는 request.setCharacterEncoding("euc-kr")로합니다.

 

참고로 tomcat version은 5.5.12입니다.



Tomcat으로 웹 어플리케이션을 돌릴 때 디렉토리를 URL로 지정하면 그냥 디렉토리 목록이 보인다.
보안상 매우 안 좋다.

$CATALINA_HOME/conf/web.xml 에 보면 (각 웹 컨텍스트의 web.xml이 아니다.)

    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>
          org.apache.catalina.servlets.DefaultServlet
        </servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>



listings 파라미터를 false로 하면 그 이후부터 디렉토리를 URL로 주면 오류 메시지가 뜬다.
JNI 사용법 웹프로그램/java 2008. 10. 15. 13:26
휴 ~_~ 인터넷에 있는 방법보다 쉬운방법 ...

new ->다이나믹 ->an empty project
생성한뒤

file view 에 헤더 파일 및 라이브러리 등록 한다. 소스파일도 당연하고 ..

그뒤 jni.h 및 각종 헤더파일을 등록할려면
메뉴에서 tools -> 텝에 directiories 에 각종 헤더 파일및 라이브러리 위치를 등록한다.

아무 생각업이 코딩하면 끝 ~
Base 64 웹프로그램/java 2008. 9. 17. 09:46
참고 사이트를 이용해 servlet 공부를 하던중 base64 인코더와 디코더를 이용하는 예제가 있었다.

참고 사이트 구문중 'sun에서 이렇게 base64로 인코딩된 문자열을

디코딩해주기위한 sun.misc.BASE64 클래스를 제공합니다.' 라는 글과

코드중 import sun.misc.BASE64Decoder; 구문으로 인하여 나의 오해를 샀다....

검색결과 JDK에 기본으로 들어가있는 패키지란다.. ㅡㅡ;;

jdk/jre/lib/rt.jar
<== 요안에 있단다..

  /**
     * BASE64 Encoder
     *
     * @param str
     * @return
     * @throws java.io.IOException
  */
  public static String base64Encode(String str)
  {
      String result = "";
      sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
      byte[] b1 = str.getBytes();
      result = encoder.encode(b1);
      return result;
  }

   /**
     * BASE64 Decoder
     *
     * @param str
     * @return
     * @throws java.io.IOException
  */

  public static String base64Decode(String str)
  {
      String result = "";
      try
      {
          sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
          byte[] b1 = decoder.decodeBuffer(str);
          result = new String(b1);
      }
      catch (IOException ex)
      {
      }
      return result;
  }

퍼온곳 : http://lastmind.net/mt/mt-tb.cgi/419

Java 프로그램에서 외부 프로그램을 실행하고 싶을 때, Runtime.exec() 또는 Java 1.5에서 추가된 ProcessBuilder를 사용해 Process 객체를 얻을 수 있다. 처음으로 이런 프로그램을 짤 때, 실수한 것이 없어보이는데도 실행한 프로그램이 종료되지 않는 경우가 있다. 더군다가 어떤 경우엔 정상적으로 종료되고 어떤 경우엔 종료가 되지않는 경우도 보인다.

원인은 바로 Process 클래스의 API Reference에 있는 다음과 같은 설명에서 찾을 수 있다.

The created subprocess does not have its own terminal or console. All its standard io (i.e. stdin, stdout, stderr) operations will be redirected to the parent process through three streams (getOutputStream(), getInputStream(), getErrorStream()). The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.

즉, Java에서는 Child 프로세스로의 표준 입출력을 Parent 프로세스가 다루어주어야 한다. 당연하게도, Child의 출력을 언제까지고 버퍼링할 수 없기 때문에, 다른 프로그램을 실행할 때는, 특히 표준 입출력이 존재하는 프로그램을 실행할 경우에는, 항상 이 스트림들의 입력 또는 출력을 제대로 다루어주어야 한다.

이러한 동작은 일반적으로 프로그래머들이 익숙한 시스템콜들, 이를테면 UNIX 계열의 fork()/exec()나 Windows의 CreateProcess()처럼, parent 프로세스의 터미널/콘솔을 공유하는 동작과는 다르기 때문에, 프로그래머들이 쉽게 간과하고 실수하기 쉽다. 더군다나 API Reference가 이러한 점을 명확히 설명하고 있지도 않다.

해결책은 당연하게도 표준 입력이 필요할 때는 Process.getOutputStream()을 이용하여 필요한 입력을 해주고 스트림을 닫아주어야하고, 표준 출력이 필요할 때는 Process.getInputStream() 그리고 Process.getErrorStream()을 이용하여 스트림을 비워주어야한다. 드물겠지만 만약 실행할 프로그램이 interactive한 프로그램일 경우에는 입출력에 좀 더 신경을 써야한다.

다음은 표준 출력을 비워주기 위한 코드가 들어간 간단한 예다.

  1. Runtime rt = Runtime.getRuntime();  
  2. try {  
  3.     Process proc = rt.exec("cmd /c dir");  
  4.     // Process proc = new ProcessBuilder().command("cmd", "/c", "dir").start();  
  5.     InputStream is = proc.getInputStream();  
  6.     BufferedReader reader = new BufferedReader(new InputStreamReader(is));  
  7.     String line;  
  8.     while ((line = reader.readLine()) != null) {  
  9.         System.out.println(line);  
  10.     }  
  11.     int exitVal = proc.waitFor();  
  12.     System.out.println("Process exited with " + exitVal);  
  13. } catch (Exception e) {  
  14.     System.err.println("Failed to execute: " + e.getMessage());  
  15. }  

표준 출력 뿐만 아니라 표준 에러까지도 처리를 할 수 있어야 좀 더 일반적인 코드일 것이다. 표준 에러의 버퍼가 차버리면 표준 출력을 비우려고 해도 블럭될 수 있다. 이러한 문제는 Java World의 When Runtime.exec() won't 라는 글에서 StreamGobbler라는 클래스를 도입해서 약간 더 우아하게 해결하고 있다. 하지만, 간단한 작업을 하기 위해 쓰레드를 사용하고 있기 때문에 좀 오버킬이라는 생각이 든다.

표준 출력과 표준 에러를 간단하게 무시하고 다른 프로그램을 실행할 수 있도록 하는 옵션이 있다면 자주 사용할 수 있으며 편리할 듯 하다. 또는, 디폴트 동작을 일반적인 시스템콜의 동작과 비슷하게 만들어주는 것도 괜찮을 듯하다. 시간이 나면 이런 방식의 Wrapper를 찾아보거나 만들어보아야겠다

<<봉봉 주세요 님 글을 가져왔습니다.>>
스 구축할 때 안정적으로 서비스하려면 한 서버에 톰캣을 4개 정도 구동시켜야 한다고 하던데 ㅎㅎ

http://www.ibm.com/developerworks/kr/li ··· 61017%2F

웹 개발자에게 있어 톰캣은 JSP를 배우거나 간단한 테스트를 하는 정도의 웹 컨테이너로 생각하는 경우가 많다. 하지만 근래 들어 기업 및 대형 포탈에서 상용 서비스를 위한 웹 컨테이너로서 톰캣을 선택해, 성공적으로 적용한 사례들이 늘고 있다. 톰캣에서 안정적인 웹 서비스를 제공하기 위해서 지원하는 기능은 5가지가 있다. 아파치 웹서버와 연동, 로드밸런싱, 세션 클러스터링, 데이터베이스 처리, 모니터링 및 관리 등이 그것이다.
이 문서에서는 로드밸런싱과 세션 클러스터링 위주로 설명을 할 것이며, 다음에 기회가 된다면 다른 부분에 대해서도 자세히 알아보도록 하겠다.

아파치 웹 서버와 톰캣의 연동

일반적으로 정적인 페이지를 서비스할 때는 웹서버가 훨씬 더 좋은 성능을 발휘한다. 또한 이렇게 역할 분담을 함으로 톰캣에 가중되는 부하를 줄여주는 효과도 얻을 수 있다. 아파치웹서버와 톰캣을 연동하는 것을 일반적으로 ‘커넥터(Connector)'라고 부르며, 여기에는 WARP 커넥터, JK 커넥터 그리고 JK2 커넥터가 있다. 이중에서 WARP와 JK2는 공식 커넥터에서 제외되었고 현재 남아 있는 것은 JK 커넥터뿐이다. 그럼 먼저 JK 커넥터를 이용해서 아파치 웹서버와 톰캣을 연동해 보도록 하겠다.
아파치 웹사이트에서 바이너리 혹은 소스 코드를 다운로드 받도록 하자. 유닉스 혹은 리눅스는 mod_jk.so이며 윈도우용은 mod_jk.dll이다. 이 파일을 아파치 웹서버의 modules 디렉토리에 저장한다(주의, 아파치 웹서버를 컴파일해서 사용하는 경우는 컴파일시에 DSO 기능이 가능하도록 설정해줘야 한다). 저장을 한 후에 아파치 웹서버에 해당 모듈을 인식시켜야 하며 아파치 웹서버의 httpd.conf 파일에 다음 내용을 추가하도록 하자.


리스트 1. httpd.conf

        ...
LoadModule jk_module modules/mod_jk.so    # 모듈 추가
JkWorkersFile "conf/workers.properties"   # JK 설정 파일 위치 및 이름
 
JkLogFile "logs/mod_jk.log"               # JK에 대한 로그 파일 위치
JkLogLevel info                           # 로그 레벨 지정
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"   # 로그 시간 포맷 지정
JkRequestLogFormat "%w %V %T"             # 로그 내용 포맷
JkMount /* loadbalancer                   # URL 링크 -> 모든 요청을 톰캣으로 지정
JkMount /servlet/* loadbalancer           # URL 링크 -> servlet 요청을 톰캣으로 지정
...

위와 같은 설정을 하게 되면 아파치 웹서버로 들어온 모든 요청을 톰캣으로 재전송 하게 된다. 만일 JSP와 서블릿만 톰캣에서 서비스를 하고 나머지는 아파치 웹서버에서 서비스 하고자 한다면 다음과 같이 수정하면 된다.

  
JkMount /*.jsp loadbalancer                # URL 링크 -> *.jsp 요청을 톰캣으로 지정 
JkMount /servlet/* loadbalancer           # URL 링크 -> servlet 요청을 톰캣으로 지정 

httpd.conf에는 위의 내용이 전부이다. 그럼 이제 workers.properties 파일을 작성해 보도록 하겠다. 이 파일이 실제 로드밸런싱을 위한 설정이 되겠다.




위로


라운드 로빈 방식의 로드밸런싱 설정

톰캣에서 제공하는 로드밸런싱은 정확히 톰캣 자체에서 제공하는 것이 아니라 아파치 웹서버와 연동되는 커넥터에 의해서 제공된다(로드밸런싱은 JK, JK2 커넥터에서만 제공된다). 현재는 라운드 로빈(Round Robin) 방식만이 제공되며 로드밸런싱에 대한 설정은 workers.properties 파일에서 정의하게 된다.

리스트 2. workers.properties

  
worker.list=tomcat1, tomcat2, loadbalancer
 
worker.tomcat1.type=ajp13
worker.tomcat1.host=localhost
worker.tomcat1.port=11009
worker.tomcat1.lbfactor=100
 
worker.tomcat2.type=ajp13
worker.tomcat2.host=localhost
worker.tomcat2.port=12009
worker.tomcat2.lbfactor=200
 
worker.loadbalancer.type=lb
worker.loadbalancer.balanced_workers=tomcat1,tomcat2

worker라는 개념은 톰캣의 프로세스로 보면 된다. 즉 worker를 설정하는 구성 요소는 JK 커넥터를 연결하는 방식(JK는 ajp13을 이용한다), 톰캣이 실행되어 있는 IP 혹은 도메인, ajp13 서비스 포트, 그리고 작업 할당량이다. 여기서 주의 깊게 볼 것이 작업 할당량인데 로드밸런싱 시에 lbfactor라는 작업량의 비율을 보고 라운드 로빈 방식의 서비스를 제공하게 된다. 여기서는 tomcat1과 tomcat2를 1대 2의 비율로 작업량을 할당한 것이다.
그럼 이제 남은 작업은 2개의 톰캣 프로세스를 실행시키는 것이다. 톰캣 프로세스를 여러 개 띄우는 방법은 2가지가 있다.

  • 톰캣을 2개 설치해서 기동시킨다. 이때 포트 충돌을 피하기 위해 서버 포트, AJP13과 HTTP 1.1 커넥터 포트 2개를 충돌되지 않게 재정의 한다.
  • 하나의 톰캣에 2개의 서비스를 정의하고 톰캣을 기동시킨다. 이때 AJP13과 HTTP1.1 커텍터 포트 2개를 충돌되지 않게 재정의 한다.

먼저 2개의 바이너리를 설치했다고 가정하면 각각의 톰캣은 다음과 같은 형태의 server.xml 파일로 적용해 준다.

리스트 3. server.xml

<Server port="11005" shutdown="SHUTDOWN"> <!-- 톰캣 프로세스를 관리하는 포트 --> <Service name="Catalina"> <Connector port="11080"/> <!-- 아파치를 통하지 않고 직접 접속하고자 할때의 포트 --> <Connector port="11009" protocol="AJP/1.3"/> <!-- 아파치와 연동하기 위한 포트 --> <!-- jvmRoute 명 JK 커넥터에서 톰캣 프로세스를 구분하는데 사용. 프로세스 별로 다르게 적용해야 함 --> <Engine jvmRoute="tomcat1" name="Catalina" defaultHost="localhost"> <Host name="localhost" appBase="webapps"/> </Engine> </Service> </Server>


리스트 4. server.xml
<Server port="12005" shutdown="SHUTDOWN"> <!-- 톰캣 프로세스를 관리하는 포트 --> <Service name="Catalina"> <Connector port="12080"/> <!-- 아파치를 통하지 않고 직접 접속하고자 할때의 포트 --> <Connector port="12009" protocol="AJP/1.3"/> <!-- 아파치와 연동하기 위한 포트 --> <!-- jvmRoute 명 JK 커넥터에서 톰캣 프로세스를 구분하는데 사용. 프로세스 별로 다르게 적용해야 함 --> <Engine jvmRoute="tomcat2" name="Catalina" defaultHost="localhost"> <Host name="localhost" appBase="webapps"/> </Engine> </Service> </Server>

리스트 3은 tomcat1의 환경 설정이고 리스트 4는 tomcat2의 환경 설정이다. 두 환경 설정의 차이는 3개의 포트번호와 <Engine> 태그의 jvmRoute 속성이다. <Server> 태그의 포트는 톰캣을 종료 시키는 작업을 할 때 사용하는 것이며 <Connector> 태그의 포트들은 아파치를 통하지 않고 직접 톰캣에 접속할 경우와 아파치와 연계하여 JK 커넥터와 연동할 때 사용하는 포트이다. 마지막으로 <Engine> 태그는 JK 커넥 터에서 로드밸런싱을 수행할 때 해당 값을 구분자로 활용하게 되는데 이 값을 반드시 다른 톰캣 프로세스와 다른 이름으로 지정해야 한다. 지금까지의 환경 설정은 하나의 아파치 웹서버와 두 개의 톰캣 간의 연계를 위한 것이며 톰캣은 동일한 하드웨어 장비에 설치되어 있다는 가정하에 적용한 것이다. 만일 각각의 톰캣이 서로 다른 하드웨어에 존재한다면 jvmRoute명만 다르게 하고 포트명은 동일해도 상관이 없다. 하지만 만일 하나의 장비에 4개의 톰캣 프로세스를 실행시키고 로드밸런싱을 하려고 한다면 어떻게 될까? 톰캣을 4번 설치하고 각각의 환경 설정 파일을 수정해 주어야 할까? 만일 필요한 환경 설정 내용이 변경된다면(예를 들어 JNDI Resource 정보) 모두 운영자가 환경 설정 파일을 수정해 주어야 할까? 다행히도 톰캣에서는 하나의 바이너리에 여러 개의 프로세스가 뜨도록 할 수 있다. 톰캣의 server.xml 태그는 다음과 같은 구조를 가지고 있다.

<Server> --> <Service> --> <Engine> --> <Host> --> <Context>

여기서 Server 태그는 유일해야 하며 Server 태그 밑에는 여러 개의 <Service> 태그가 올 수 있다. 여기서 말하는 <Service> 태그가 바로 하나의 톰캣 프로세스가 된다. 만일 2개의 <Service> 태그를 정의했다면 2개의 프로세스가 구동되는 것이다. 리스트 5는 이렇게 구현한 환경 설정 파일이다.

리스트 5. server.xml

<Server port="8005" shutdown="SHUTDOWN"> <!-- Service 1 --> <Service name="Catalina1"> <Connector port="11080"/> <Connector port="11009" protocol="AJP/1.3"/> <Engine jvmRoute="tomcat1" name="Catalina" defaultHost="localhost"> <Host name="localhost" appBase="webapps"/> </Engine> </Service> <!-- Service 1 --> <Service name="Catalina2"> <Connector port="12080"/> <Connector port="12009" protocol="AJP/1.3"/> <Engine jvmRoute="tomcat2" name="Catalina" defaultHost="localhost"> <Host name="localhost" appBase="webapps"/> </Engine> </Service> </Server>

리스트 5는 하나의 톰캣 바이너리를 통해 2개의 프로세스를 실행시키는 것이다. 이렇게 하면 환경 설정의 편리성을 가져올 수 있지만 특정 서비스만 실행하거나 종료 시키는 것은 아직 지원되지 않는다. 즉 모든 서비스가 동시에 실행되거나 혹은 동시에 종료되는 것을 의미한다. 이런 점을 잘 판단해서 두 가지 형태의 환경 설정 중 하나를 선택하면 되겠다.
지금까지는 로드밸런싱에 대해 알아보았다. 위의 환경설정을 가지고 테스트를 하다 보면 한가지 문제가 발생한다. 예를 들어 어떤 사용자가 tomcat1을 이용해서 쇼핑몰 서비스를 받고 있다가 tomcat1이 비정상 종료를 하게 되었다. 이때 사용자가 웹 페이지를 요청하게 되면 아파치 웹서버는 tomcat1이 종료된 것을 인지하고 그 이후부터 서비스를 tomcat2로 요청하게 된다. 하지만 tomcat1에 저장되어 있던 쇼핑바구니 정보 즉 세션 정보는 사라진 상태다. 즉, 서비스는 유지되지만 사용자는 다시 이유도 모르게 처음부터 쇼핑 항목들을 등록해야 하는 문제를 가지게 된다. 이제부터는 이런 문제를 해결할 수 있는 톰캣 프로세스 간의 세션 정보 공유에 대해서 알아보겠다.




위로


세션 클러스터링 설정

클러스터링은 톰캣 5.x 버전부터 지원이 되고 있지만 아직은 초기 단계이고 세션 클러스터링만이 제공되고 있는 수준이다. 기능이 많이 부족하긴 하지만 로드밸런싱과 더불어 사용할 경우에는 좀 더 안정적인 서비스를 제공할 수 있다. 작업을 해주어야 할 것은 다음과 같다.

  • server.xml에 <Cluster> 태그 정의
  • 웹 어플리케이션의 web.xml에 <distributable/> 태그 추가
그럼 server.xml에 설정해 보자. <Cluster> 태그는 <Host> 태그의 하위에 정의해 주면 된다. 즉 여러 개의 호스트(예를 들어 가상 호스트) 를 설정했다면 각각의 경우에 맞게 설정해 주어야 한다. 여기서는 tomcat1과 tomcat2가 동일한 하드웨어에 별도의 바이너리 형태로 설치되어 있다고 가정하고 진행하겠다.

리스트 6. server.xml

... <Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster" managerClassName="org.apache.catalina.cluster.session.DeltaManager" expireSessionsOnShutdown="false" useDirtyFlag="true"> <Membership className="org.apache.catalina.cluster.mcast.McastService" mcastAddr="228.0.0.105" mcastPort="45564" mcastFrequency="500" mcastDropTime="3000"/> <Receiver className="org.apache.catalina.cluster.tcp.ReplicationListener" tcpListenAddress="auto" tcpListenPort="4001" tcpSelectorTimeout="100" tcpThreadCount="6"/> <Sender className="org.apache.catalina.cluster.tcp.ReplicationTransmitter" replicationMode="pooled"/> <Valve className="org.apache.catalina.cluster.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"/> </Cluster> ...

리스트 6은 tomcat1의 server.xml에 적용한 <Cluster> 태그이다. 이 내용은 <Host> 태그 사이에 위치하게 된다. <Cluster> 태그 사이에는 <Membership>, <Receiver>, <Sender>라는 3개의 태그가 위치하는데 <Membership>은 멤버 그룹을 정의하는 것으로 해당 값이 동일한 모든 톰캣 프로세스는 클러스터로 묶이게 된다. <Receiver>는 클러스터 그룹에서 보내오는 메시지와 세션 정보 등을 받아오는 것이며 <Sender>는 자신의 세션 정보 및 메시지를 전송하는 것이다. 위의 내용을 tomcat2의 server.xml에 Receiver 태그의 tcpListenPort 값을 4002로 변경해서 적용하도록 하자.




위로


웹 어플리케이션 작성을 통한 테스트

먼저 테스트를 위해서 간단한 웹 어플리케이션을 작성하도록 하겠다. 여기서 웹 어플리케이션 이름은 lbtest라고 하겠다.

리스트 7. index.jsp

<%@ page contentType="text/html; charset=euc-kr" %> <HTML> <HEAD> <TITLE>세션 JSP 테스트</TITLE> </HEAD> <BODY> <h1>세션 JSP 테스트</h1> <% Integer ival = (Integer)session.getAttribute("sessiontest.counter"); if(ival==null) { ival = new Integer(1); } else { ival = new Integer(ival.intValue() + 1); } session.setAttribute("sessiontest.counter", ival); %> 당신은 이곳을 <b> <%= ival %> </b>번 방문 했습니다.<p> 여기를 클릭하세요. <a href="index.jsp">여기</a> <p> <h3>request 객체와 관련된 세션 데이터</h3> 요청된 세션 ID : <%= request.getRequestedSessionId() %><br /> 쿠키로 부터 요청된 세션 ID 인가? : <%= request.isRequestedSessionIdFromCookie() %><br /> URL로부터 요청된 세션 ID 인가? : <%= request.isRequestedSessionIdFromURL() %><br /> 유효한 세션 ID 인가? : <%= request.isRequestedSessionIdValid() %><br /> </BODY> </HTML>

작성된 웹 애플리케이션을 tomcat1과 tomcat2에 배포한다. 이때 가장 중요한 것이 web.xml에 반드시 <distributable/>이라는 항목을 넣어 야 한다. 그럼 이제 테스트를 해보도록 하자. 먼저 아파치 웹서버, tomcat1, tomcat2를 차례로 실행시켜 보자. 그리고 http://ipaddress/lbtest/index.jsp 접속하여 세션 객체를 생성해보자. 결과 화면은 그림 1과 같다. 여기서 요청된 세션ID를 보면 뒤에 어떤 톰캣에 접속한 상태인지를 알 수 있다. 이 화면상에서는 tomcat2에 접속한 세션이다. 그럼 tomcat2를 강제로 종료시켜 보도록 하자. 종료 후 화면에 보이는 “여기”를 계속 눌러 보자. 분명히 tomcat2가 종료되었음에도 불구하고 세션 값이 유지되고 있음을 알 수 있다. 이젠 반대로 tomcat2를 다시 실행시킨 후에 tomcat1을 종료시켜 보도록 하자. 역시 마찬가지로 세션 정보가 유지되는 것을 확인할 수 있을 것 이다.

test 결과화면
그림 1. 테스트 결과 화면

이상으로 톰캣을 이용한 로드밸런싱과 세션 클러스터링에 대해서 알아보았다. 일반적으로 로드밸런싱과 클러스터링은 성능 향상이라는 측면과 안정성 확보에 그 목적을 가지고 있다. 물론 고가의 상용 웹 어플리케이션 서버에 비하면 많이 부족하고 하드웨어를 이용한 로드밸런싱과 클러스터링에 비하면 안정성이 떨어질 수도 있지만 저렴한 비용으로 최대의 안정성과 성능을 얻고자 한다면 한번쯤 시도해 볼만한 좋은 기능이라고 할 수 있다. 아무쪼록 리눅스, 아파치, 톰캣 그리고 오픈소스를 통해 즐거운 웹 어플리케이션 개발 환경을 느껴보기 바란다.

객체들을 조작하기 위한 자료구조로 자바는 배열이나 Collection Framework 내의 여러클래스를 제공하고 있습니다.

Collection Framework는 크게 3가지 형태로 분류할 수 있는데 간단하게 살펴 보자면
  - Map : key와 Value를 가지는 자료구조입니다. HashMap, Hashtable, TreeMap과 같은 클래스들을 자주 쓰죠.
  - List : 순서가 있고 중복이 허용되는 자료구조입니다. ArrayList, LinkedList, Vector...
  - Set : 중복을 허용하지 않습니다.  HashSet, TreeSet...

이러한 자료구조 내에서 객체들을 컨트롤 할 때 정렬은 피할 수 없는 숙명이죠.


1. 정렬해야 하는데 정렬 알고리즘을 모르겠다. 누가 정렬 좀 안해주나?


버블소트는 간단하지만 퍼포먼스에 문제가 있고
일반적으로 가장 빠른 퀵소트는 구현하기가 까다롭습니다.
버블소트조차도 귀찮으신 분들을 위해 친절하게도 자바는 여러분에게 고성능(?)의 정렬기능을 제공하고 있습니다.
그러나 우리는 자바가 제공하는 정렬기능을 사용하기 위해서 한두가지는 해야 합니다. 공짜는 없는거죠.


먼저 정렬을 위한 두가지 인터페이스에 친숙해질 필요가 있습니다.

  java.lang.Comparable
  java.util.Comparator


  두 인터페이스가 서로 다른 패키지에 속해 있다는 사실에 유의하시구요.

Comparable 인터페이스는 객체의 고유한 순서를 제공하기 위한 것이라고 보면 되는데요.
우리말로 '비교할 수 있는' 정도로 풀 수 있겠죠.
그 뜻대로 이 인터페이스를 구현한 클래스의 객체들은 배열이나 컬렉션 프레임웤에서
자신과 딴넘을 비교할 수 있기 때문에 정렬의 기본조건을 충족시키고 있습니다.
정렬의 기본조건(객체간의 대소비교, 선후비교)이 준비되어 있기 때문에 정렬은 java API에 맡기면 되는 겁니다.


아래의 소스에서 우리는 Customer 클래스를 만들고 Comparable 인터페이스를 구현했습니다.
Comparable 인터페이스를 구현했기 때문에 그 인터페이스에서 정의한 compareTo 메쏘드를 반드시 오버라이딩 해야하죠.
이 메쏘드가 바로 자신과 딴넘을 비교해서
자기자신이 딴넘보다 앞에 와야하면 -1을, 똑같으면 0을, 뒤에 와야하면 1을 리턴하게 됩니다.
Customer 클래스에서는 디폴트로 한글이름으로 정렬되도록 compareTo를 재정의 했습니다.


이렇게만 구현을 해 놓으면 Customer로 이루어진 배열이 있다면
  Arrays.sort(arr);


Customer로 이루어진 리스트(컬렉션프레임웤)가 있다면
  Collections.sort(list);

를 호출하기만 하면 군말 없이 정렬해 줍니다.


자바API에서도 여러 클래스들이 이 Comparable을 구현하고 있는데요.

API문서를 참고하면

BigDecimal, BigInteger, Byte, ByteBuffer, Character, CharBuffer, Charset, CollationKey, Date, Double, DoubleBuffer, File, Float, FloatBuffer, IntBuffer, Integer, Long, LongBuffer, ObjectStreamField, Short, ShortBuffer, String, URI

등의 클래스들이 있습니다.

이러한 클래스들의 자료를 다룰때는 위의 sort 메쏘드를 자연스럽게 불러주면 되겠죠?


자바가 내부적으로 어떻게 정렬을 수행하는지 궁금하지 않으십니까?
그러면 SDK 설치 디렉토리 밑에 src.zip파일을 찾아 java.util.Arrays와 java.util.Collections의소스를 뜯어보십시오.
Collections의 sort(List)는 아규먼트로 들어온 list를 배열로 변환해서 다시 Arrays.sort(array)를 호출하며 Arrays.sort(array)는 합병정렬을 하고 있습니다

아마 합병정렬은 정렬성능도 우수하지만 정렬시간이 거의 일정하기 때문에 사용하는 것 같습니다.

(퀵소트는 최악의 경우 O(n^2) 의 시간이 걸릴수도 있습니다.)
단 메모리를 자기자신만큼, 그리고 절반씩 쪼개지는 크기들 만큼씩 더 사용한다는게 단점이죠.


우리는 Customer 클래스들이 배열로 되어 있건 리스트에 들어있건 한글이름으로는 정렬할  수는 있는데 그렇다면 나이순이라든지 영문이름순으로, 즉 다른 기준으로 정렬하고 싶을 땐 어떻게 할까요?

이럴때를 위해서 만들어 놓은 인터페이스가 Comparator입니다.
우리말로 '비교기', '비교측정기' 정도로 번역할 수 있는데 두 객체의 대소와 선후를 구현의도의 기준에 따라 판별해 줍니다.
이 인터페이스를 구현하는 클래스도 마찬가지로 compare 메쏘드를 구현해야 하는데요.
아규먼트로 들어오는 두개의 Object를 비교해서 역시 compareTo 와 같은 방식으로 리턴합니다.


그리고 아래처럼 사용하면 배열이나 리스트를  Comparator의 기준에 따라 정렬을 수행하게 됩니다.  
  Arrays.sort(arr, new YoungOrderComparator());
  Collections.sort(list, new EngNameComparator());


 

2. sort()를 부르기도 귀찮다. 그냥 알아서 줄 좀 서 봐라.

 

자바는 친절하게도 위의 요구까지 들어주고 있는데
TreeMap이나, TreeSet이 그 기대에 부응하는 클래스들입니다. (Tree~가 붙어 있죠? ^^)
TreeMap은 key로 정렬을 하게 되고 TreeSet은 그 원소들을 정렬합니다.
위의 컨테이너 객체에 원소들을 넣는 족족 알아서 정렬됩니다. 코딩할 일이 없습니다.


리스트 같은 객체들을 인자로 받아서 TreeSet을 만들면 알아서 기본순서로(compareTo에 정의된 대로) 정렬됩니다.
   Set set = new TreeSet(list);


정렬기준을 바꾸고 싶다면 Comparator를 구현한 객체를 생성자로 만든후 리스트를 집어넣어 줍니다.
   Set set2 = new TreeSet(new YoungOrderComparator());
   set2.addAll(list);


아래의 소스에 위에 언급한 사항을 간단하게 구현해 보았습니다.

자바의 컬렉션 프레임웤, 정렬기능 등을 차근차근 살펴보면 Interface, 다형성 등의 묘미를 만끽할 수 있죠.
이러한 객체지향적 언어로서의 매력은 이곳외에도 자바API의 여러부분에서 표현되어져 있습니다.
JAVA는 정말 아름다운 언어임에는 틀림없습니다.


import java.util.*;


public class A {
    public static void main(String args[]) {
      // 다섯명의 고객에 대한 배열 생성
      Customer[] arr = new Customer[] {
        new Customer("헤더 로클리어",1961, "Heather Deen Locklear"),
        new Customer("데미 무어", 1962, "Demetria Gene Guynes"),
        new Customer("안젤라 바셋", 1958, "Angela Bassett"),
        new Customer("신디 크로퍼드", 1966, "Cintia Ann Crawford"),
        new Customer("캐서린 제타 존스", 1969, "Catherine Jones")
      };
     
      printArray(arr, "Before Array sort Using Default sort");
       // 배열을 정렬 (클래스에 정의된 기본정렬)
      Arrays.sort(arr);
      printArray(arr, "\nAfter Array sort Using Default sort");

      // 배열을 어린 나이부터 정렬
      Arrays.sort(arr, new YoungOrderComparator());
      printArray(arr, "\nAfter Array sort Using YoungOrderComparator");


      List list = Arrays.asList(arr); // 배열을 리스트로
      Collections.shuffle(list); // 리스트의 순서를 마구 섞어 주세요.
      printList(list, "\nBefore List sort Using Default sort");


      // 리스트를 정렬  (클래스에 정의된 기본정렬)
      Collections.sort(list);
      printList(list, "\nAfter List sort Using Default sort");

      // 리스트를 영문이름으로 정렬
      Collections.sort(list, new EngNameComparator());
      printList(list, "\nAfter List sort Using EngNameComparator");


      // 디폴트 정렬할 수 있는 TreeSet을 만든다
      Set set = new TreeSet(list);
      System.out.println("\nAfter Making Set  Using Default sort\n" + set);


      // 어린 나이부터 정렬할 수 있는 TreeSet을 만든다
      Set set2 = new TreeSet(new YoungOrderComparator());
      set2.addAll(list);
      System.out.println("\nAfter Making Set  Using YoungOrderComparator\n" + set2);
    }


    static void printArray(Customer[] a , String title) {
      System.out.println(title);
      for (int i=0; i<a.length; i++)
        System.out.println(a[i]);
    }
   
    static void printList(List l, String title) {
      System.out.println(title);
      for (int i=0; i<l.size(); i++)
        System.out.println(l.get(i));
    }
   
}


 // 디폴트 소팅을 위해서 Comparable 인터페이스를 구현한다.
class Customer implements Comparable {
  String name;
  int birthYear;
  String engName;

   // Constructor
   public Customer(String name, int birthYear, String engName) {
     this.name = name;
     this.birthYear = birthYear;
     this.engName = engName;
   }


    // Object의 toString 메소드 overriding.. 객체의 문자적 표현
    public String toString() {
     return name + "(" + engName + ") " + birthYear + "년생";
   }


   // Comparable 인터페이스를 구현한 클래스에서 반드시 overriding 해야만 하는 비교 메쏘드
   public int compareTo(Object o) {
     // String의 compareTo 메소드를 호출(사전순서적( lexicographically)으로 비교)
     return name.compareTo(((Customer)o).name);
   }
}


// 젊은 순서대로 정렬하기 위해 Comparator 인터페이스를 구현
class YoungOrderComparator implements Comparator {
  public int compare(Object o1, Object o2) {
    int by1 = ((Customer)o1).birthYear;
    int by2 = ((Customer)o2).birthYear;
    return by1 > by2 ? -1 : (by1 == by2 ? 0 : 1); // descending 정렬.....
  }
}


// 영문이름으로 정렬하기 위해 Comparator 인터페이스를 구현
class EngNameComparator implements Comparator {
  public int compare(Object o1, Object o2) {
    String en1 = ((Customer)o1).engName;
    String en2 = ((Customer)o2).engName;
    return en1.compareTo(en2); // ascending 정렬
  }
}

Posted by 숭늉은셀프

트랙백 주소 :: http://yobaboom.tistory.com/trackback/17

  1. Subject

function useridcheek(pNum)
 {
  for (i = 0; i < pNum.value.length; i++)
  {   
   var ch = pNum.value.charCodeAt(i);
   
   if((ch>32&&ch<48)||(ch>57&&ch<127)) {
    alert('한글과 숫자만 입력할 수 있습니다');
    pNum.value = ""
    return false;
   }
  }
  return true;
 }



------------------ 숫자만 이력되게하는 코드 - -- - ------------
function onlyNumberInput()
 {
   var code = window.event.keyCode;

   if ((code > 34 && code < 41) || (code > 47 && code < 58) || (code > 95 && code < 106) || code == 8 || code == 9 || code == 13 || code == 46)
   {
     window.event.returnValue = true;
     return;
   }
   window.event.returnValue = false;
 }



onKeyDown = "javascript:onlyNumberInput()" style='IME-MODE: disabled'



-- 영어 소문자 대문자. 특수문자만 빼고 입력되게해놨음 ....

//읽어보지못했다 처음만보고 주말에 자세히 봐야겠다.
//퍼온곳 http://kwon37xi.egloos.com/2558053 
Ajax 기본 예제와 JSP 엔진에서 한글 인코딩 충돌 문제 처리
Ajax가 필요한 일이 발생해서, 급조해서 Ajax를 공부했다.
공부한 책은 Ajax 입문이며, 처음 약 5~60 페이지만 읽었다. 급조한 내용이니 너무 신뢰하지 말 것. 아.. 그리고 이 책, 중대한 오탈자가 은근히 있다. 혹시 이 책으로 공부하고자 한다면 오탈자를 확인한 뒤에 공부해서 불필요한 시간 낭비를 줄이는 것이 좋겠다.

아무튼, 책의 내용중 Ajax의 기본적인 사용에 관한 문제를 정리하고, 또 JSP/Servlet 엔진에서 Ajax사용시에 발생하는 한글 인코딩(encoding)문제의 처리방법도 정리해 둔다.


Ajax의 개념에 관한 설명은 인터넷 상에 차고 넘치므로 생략.

* A Simpler Ajax Path가 Ajax 입문에 좋은 글.

Ajax의 개발 순서

1. XMLHttpRequest 객체 생성
2. HTTP 요청을 발생시킴(open(), send())
3. 서버측에서 XMLHttpRequest를 통해 보낸 요청을 받아서 파라미터를 분석하고, 작업을 한 뒤에 결과를 XML이나 문자열로 리턴한다.
4. XMLHttpRequest로 서버가 리턴한 데이터를 받아서 처리(onreadystatechange, responseText, responseXML)

XMLHttpRequest에 의한 송수신 상세 예

JavaScript에서 XMLHttpRequest Object 생성하기

// XMLHttpRequest 오브젝트 생성 함수
// @sample oj = createHttpRequest();
// @return XMLHttpRequest 오브젝트
function createHttpRequest()
{
    if (window.ActiveXObject) {
        try {
            // IE6
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                // IE4, IE5
                return new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
                return null;
            }
        }
    } else if (window.XMLHttpRequest) {
        // Mozilla, FireFox, Opera, Safari, Konqueror3
        return new XMLHttpRequest();
    } else {
        return null;
    }
}


HTTP 요청 발생

 1. open() 메소드 실행 (POST/GET, 요청URL, 동기/비동기지정)
var request = createHttpRequest();
request.open("GET", "/test.xml");
 
// param 1 : GET/POST
// param 2 : URL
// param 3 : 생략가능. 동기/비동기 여부. 기본 비동기.

 2. send() 메소드(데이터 송신)
 request.send(""); // 데이터 없이 전송할때 혹은
 request.send(null); // Konqueror에서는 오류 발생함. Konqueror를 제외하고 데이터 없이 전송할 때 사용가능

위 a,b가 기본적인 모양새이지만, 실제로 GET과 POST 방식에 따라 차이가 많이난다.

  * GET 방식 : GET 방식은 URL에 파라미터를 직접 붙여 보내지만, 한글 등의 문제로 인코딩이 필요하고, RequestHeader 설정도 필요하다. 일반적으로 다음과 같은 모양이 된다.
var paramName = encodeURIComponent("파라미터명"); // 파라미터이름을 UTF-8로 인코딩
var paramValue = encodedURIComponent("파라미터값"); // 파라미터 값을 UTF-8로 인코딩

// 파라미터 구분에 사용되는 ?와 &는 인코딩 되면 안된다. 그래서 따로 붙인다.
var fullParameter = '?' + paramName + '=' + paramValue; // URL에 사용할 파라미터 구성

request.open("GET", "/test.cgi" + data);

// setRequestHeader()는 open()보다 이후에 나와야만 한다.
// 아래는 파라미터 값을 UTF-8로 인코딩해서 보내겠다는 의미.
// GET방식에서는 필요 없고, POST방식에서는 필수이다.
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

request.send(null);


  * POST 방식 : send() 메소드에 인수를 데이터로 넘긴다.
request.open("POST", "/test.cgi");
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
// POST 방식에서도 파라미터를 인코딩하여 send()메소드의 인자로 넘겨주면 된다.
request.send("name=test&data=123");

A Simpler Ajax Path에 보면 HTML폼에 입력된 값을 자동으로 파라미터 문자열로 변경해주는 메소드 예가 있다.

착신과 데이터 처리

 * onreadystatechange 이벤트(송수신 상태 변할때 기동) - IE 이외 부라우저에서는 콜백 스타일의 onload 이벤트 사용가능
 * readyState 프라퍼티 (송수신 상태를 나타내는 값. "4"가 송신 완료) - onload의 경우에는 불필요
onreadystatechange는 요청 처리 상태를 나타내는 readyState 프라퍼티의 값이 바뀔 때 발생한다.
 * 착신을 처리할 함수 지정은 request.open() 함수를 호출하기 전에 선언해야 정상 작동했다. 항상 요청을 보내기 전에 요청을 처리할 함수 지정을 해둔다.
request.onreadystatechange = callbackFunction; // callbackFunction() 함수가 호출된다.

function callbackFunction() {
    // readyState == 4는 착신 완료를 의미한다.
    if (request.readyState == 4) {
        // 착신시의 처리
    }
}

다른 방법으로, callback 함수를 인라인으로 정의하고, HTTP 상태 코드가 200일때만 작업하도록 할 수도 있다. 두가지를 한꺼번에 보면,
request.onreadystatechange = funcation() {
    if (request.readyState == 4 &&
            request.status == 200) {
        // 착신시의 처리
    }
}

onreadystatechange 대신 onload를 사용할 수 있다. Opera 8은 버그때문에 onload만 사용한다. (IE를 제외한 다른 브라우저에서 다 된다)
request.onload = function() {
    // 착신시의 처리
}

onload와 onreadystatechange를 동시에 이용하기 위해서 다음과 같이한다.
if (window.opera) {
    request.onload = function() { callback(request); }
} else {
    request.onreadystatechange = function() {
        if (request.readyState == 4) {
            callback(request);
        }
    }
}

function callback(request) {
    // 실제 착신시의 처리를 구현하는 부분
}

 * responseText 또는 responseXML (데이터를 텍스트 혹은 DOMDocument로 수신)
   * responseText : 텍스트로 받기
   * responseXml : XML로 받기
   * 여러줄의 CSV 텍스트를 받았을 때의 일반적 처리
var res = request.responseText;
var rows = res.split(" "); // 여러 줄을 한 줄씩 배열로 만든다.
var cols = rows[0].split(","); // 첫번째 줄을 쉼표 단위로 분리하여 배열로 만든다.

   * JSON 처리
eval("res = " + request.responseText)
//... 기타 처리

   * XML 처리
<?xml version="1.0"?>
<lists>
  <name>Toshiro Takahashi</name>
  <msg>hello</msg>
</lists>

var res = request.responseXML;
var msgs = res.getElementsByTagName("msg"); // DOM 객체 사용

alert(msg[0].firstChild.nodeValue);


서버측 스크립트

XMLHttpRequest.send() 에 의해 요청을 받아 처리하게 되는 서버측 스크립트(JSP, Servlet, ASP, PHP 등)은 요청 파라미터를 분석하여 작업을 처리한 뒤에 결과를 Text나 XML로 리턴하면 된다.
 * 리턴시 문자 인코딩은 기본적으로 UTF-8로 한다.
 * 텍스트로 리턴할 경우, Opera 8, Konqueror 3, Safari 등은 UTF-8을 인식하지 못한다. 서버는 응답 문자열을 UTF-8기준으로 URI 인코딩을 해서(Java의 경우 java.net.URLEncoder.encode() 메소드 사용) 리턴하고, 받는 측(웹 브라우져)는 다음과 같이 해석하면 정상적인 문자열을 받게 된다.(실제로는 작동하지 않으므로 URLEncoder를 사용하지말고 받는 자바 스크립트 측에서도 아래와 같이 받지 말고 그냥 request.responseText를 받을 것)
// Mozilla FireFox와 IE에서는 Encode/Decode할 경우
//공백이 +로 바뀌는 현상이 발생했다. 그래서 안쓴다.
// JavaScript측에서 decodeURIComponent를 안하면 서버측에서도 URLEncoding을 하면 안된다.
var res = decodeURIComponent(request.responseText);


JSP/Servlet 에서 Ajax와 한글 인코딩 문제

Ajax는 기본적으로 UTF-8 만으로 통신을 한다고 보면된다. 그 이외의 Encoding을 지원하는 인코딩 함수가 없기 때문에 EUC-KR로 데이터를 전송하려는 꿈은 접어야만 한다.
헌데, 아직 우리나라는 EUC-KR 인코딩으로 된 웹 어플리케이션이 압도적으로 많은 상황이다(어서 빨리 UTF-8로 옮겨가길 바라마지 않는다).
거기다가 보통 JSP/Servlet 웹 어플리케이션은 Servlet 스펙 2.3이후부터 문자 인코딩 서블릿 필터를 사용해 모든 요청에 대해 일관된 문자 인코딩을 사용하는 것이 보편적인 방법으로 자리잡았다.

서블릿 필터는 일관성있게 모든 요청을 EUC-KR로 받아들이게 했는데, 몇몇 Ajax관련 요청만 UTF-8로 받아들여야만 하는 것이다.
필터를 적용할 URL-Pattern을 따로 줘보려 했으나, 너무 복잡해졌다.
그래서 HTTP 요청의 헤더를 이용해서 해결 했다.

아.. 한가지 더. 현재 한글 문제는 "XMLHttpRequest 요청 -> JSP/Servlet" 이 상황에서만 발생하는 것이다.
"JSP/Servlet -> XMLHttpRequest"의 상황(서버에서 클라이언트로 값을 리턴)에서는 이 문제가 발생하지 않는다.
서버가 리턴하는 문자열은 간단하게 다음처럼 하면 WAS가 자동으로 UTF-8로 값을 변경해서 전달하기 때문이다.
<%@ page contentType="text/plain; charset=utf-8" pageEncoding="EUC-KR"%>

contentType에서 text/plain은 텍스트나 JSON으로 값을 리턴할 때이다. XML로 리턴할 때는 text/xml.

아래는 Ajax 요청을 처리하기 위해서 만들어본 간단한 Encoding Filter 이다.
package ajax.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 어플리케이션 전체에 적용되는 필터이다.
 *
 * <ul>
 * <li>encoding 파라미터 : encoding 파라미터를 설정하면 request 객체에
 * setCharacterEncoding(encoding)을 실행한다.</li>
 * <li>ajaxFlag 파라미터 : Ajax요청임을 나타내는 HTTP 파라미터 이름이다. ajaxFilter로 지정한 HTTP 파라미터의
 * 값이 true 로 설정되면 인코딩을 무조건 UTF-8로 설정한다.</li>
 * </ul>
 *
 * @author 손권남(kwon37xi@yahoo.co.kr)
 *
 */
public class EncodingFilter implements Filter {

    private Log log = LogFactory.getLog(this.getClass());

    /** HTTP 요청 문자 인코딩 */
    private String encoding = null;

    /** Ajax 요청임을 나타내는 플래그 파라미터 이름 */
    private String ajaxFlag = null;

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        if (ajaxFlag != null
                && "true".equals(((HttpServletRequest) request)
                        .getHeader(ajaxFlag))) {
            // Ajax 처리 요청일 경우 무조건 UTF-8 지정.
            request.setCharacterEncoding("UTF-8");
            if (log.isDebugEnabled()) {
                log.debug("요청 헤더에 " + ajaxFlag + "가 "
                        + ((HttpServletRequest) request).getHeader(ajaxFlag)
                        + "로 설정되어 있어 문자 인코딩에  UTF-8을 사용합니다.");
            }
        } else if (encoding != null) {
            // Ajax 플래그가 true가 아니면, 기본적인 인코딩을 적용한다.
            request.setCharacterEncoding(encoding);
            if (log.isDebugEnabled()) {
                log.debug("문자 인코딩에 " + encoding + "을 사용합니다.");
            }
        }

        chain.doFilter(request, response);
    }

    public void init(FilterConfig config) throws ServletException {
        encoding = config.getInitParameter("encoding");

        ajaxFlag = config.getInitParameter("ajaxFlag");

        if (log.isDebugEnabled()) {
            log.info("encoding : " + encoding + ", ajaxFlag : " + ajaxFlag);
        }
    }

    public void destroy() {
    }
}

이 필터를 적용하고서, web.xml에 다음 내용을 추가하면 필터가 작동한다.
<filter>
    <description>이중 인코딩 필터</description>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>ajax.filter.EncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>euc-kr</param-value>
    </init-param>
    <init-param>
        <param-name>ajaxFlag</param-name>
        <param-value>Ajax</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

여기 내용을 보면, 기본적인 인코딩은 EUC-KR이고, 요청 헤더에 "Ajax" 헤더의 값이 "true"일 경우에는 강제로 UTF-8을 지정하라고 한 것이다. "ajaxFlag"의 값을 변경하면 헤더의 키을 "Ajax"가 아닌 다른 값으로도 지정할 수 있다. 하지만 아무튼 해당 헤더의 값을 "true"로 지정하면 Ajax로 인식하게 되는 것이다.

이를 위해서는 XMLHttpRequest에도 한가지 처리를 더 보태야 한다.
    request.open("GET", "AjaxProcessor.jsp" + fullParameter);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    request.setRequestHeader('Ajax', 'true');

당연히 헤더에 값 Ajax요청임을 명시하는 값을 설정하는 것이다.

그리고 다음 문제가 또 있는데, Tomcat 버전별로 문자 인코딩 설정이 달라질 수 있다는 것이다.
위 내용은 Tomcat 4.x에서는 정상 작동하지만, Tomcat 5.x 에서는 제대로 작동하지 않는다.
Tomcat 5.x에서는 server.xml 에 GET 방식의 요청에 대한 인코딩을 지정하기 때문이다.
여기서 URIEncoding="euc-kr" 을 사용하지 않고, useBodyEncodingForURI="true"을 사용하면 Tomcat 4.x 처럼 request.setCharacterEncoding()의 값을 따라가게 할 수 있다.
Tomcat과 한글에 대해서는 Tomcat/JSP와 한글문서를 참조한다.

또하나 Ajax임을 나타내는 플래그를 HTTP 요청 헤더에 설정하도록 했는데, 그러지 않고 요청 파라미터(request.getParameter()로 값을 얻어올 수 있는 것)으로 설정하면 안된다.
request.getParameter()가 실행되어 Ajax 플래그의 값을 감지하는 그 순간, 그 이후 호출되는 request.setCharacterEncoding()는 완전히 무시되어 버리기 때문이다.

예제

급조한 예제이다.
* AjaxCaller.jsp - Ajax 호출부(클라이언트) : 수식을 만들어서 서버측에 계산을 요청한다.
<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Insert title here</title>
<script type="text/javascript">
// XMLHttpRequest 오브젝트 생성 함수
// @sample oj = createHttpRequest();
// @return XMLHttpRequest 오브젝트
function createHttpRequest()
{
    if (window.ActiveXObject) {
        try {
            // IE6
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                // IE4, IE5
                return new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
                return null;
            }
        }
    } else if (window.XMLHttpRequest) {
        // Mozilla, FireFox, Opera, Safari, Konqueror3
        return new XMLHttpRequest();
    } else {
        return null;
    }
}

// 계산을 수행한다.
function calc() {
    var request = createHttpRequest();
    var nameParam = encodeURIComponent("name");
    var nameValue = encodeURIComponent(document.getElementById("name").value);
   
    var oper1Param = encodeURIComponent("oper1");
    var oper1Value = encodeURIComponent(document.getElementById("oper1").value);
   
    var oper2Param = encodeURIComponent("oper2");
    var oper2Value = encodeURIComponent(document.getElementById("oper2").value);
   
    var operatorParam = encodeURIComponent("operator");
    var operatorValue = encodeURIComponent(document.getElementById("operator").value);
   
    var fullParameter =
        "?" + nameParam + "=" + nameValue
        + "&" + oper1Param + "=" + oper1Value
        + "&" + oper2Param + "=" + oper2Value
        + "&" + operatorParam + "=" + operatorValue;
   
    request.onreadystatechange = function() {
        if (request.readyState == 4) {
            alert("Response : " + request.responseText);
            eval("var result = " + request.responseText);
            alert(result.name + "님 계산결과는 " + result.value + "입니다.");
        }
    }
   
    request.open("GET", "AjaxProcessor.jsp" + fullParameter);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    request.setRequestHeader('Ajax', 'true');
    request.send("");

} // end of calc()
</script>
</head>
<body>
이름 : <input type="text" id="name" /> <br />
<input type="text" id="oper1" />
<select id="operator">
    <option value="+">+</option>
    <option value="-">-</option>
    <option value="*">*</option>
    <option value="/">/</option>
</select>
<input type="text" id="oper2" />
<input type="button" value="계산하기" onclick="calc()" />
</body>
</html>

* AjaxProcessor.jsp - Ajax 처리부(서버) : 수식을 계산한 결과를 JSON 형태로 리턴한다
<%@ page contentType="text; charset=utf-8" pageEncoding="EUC-KR"%>
<%
System.out.println("AjaxProcessor.jsp 시작");

String result = null;

String name = request.getParameter("name");
String oper1 = request.getParameter("oper1");
String oper2 = request.getParameter("oper2");
String operator = request.getParameter("operator");

double oper1Value = Double.parseDouble(oper1);
double oper2Value = Double.parseDouble(oper2);

double calcResult = 0;

if ("+".equals(operator)) {
    calcResult = oper1Value + oper2Value;
} else if ("-".equals(operator)) {
    calcResult = oper1Value - oper2Value;
} else if ("*".equals(operator)) {
    calcResult = oper1Value * oper2Value;
} else {
    calcResult = oper1Value / oper2Value;
}

result = "{  " +
    ""name" : "" + name + "", " +
    ""value" : "" + calcResult + "" " +
    "} ";
System.out.println("Result : " + result);

%><%= result%><%
System.out.println("AjaxProcessor.jsp 끝");
%>

이름 입력 부분에 한글을 입력하여 문제없이 처리되는지 확인해보기 바란다.

JSON

Ajax(JavaScript)는 데이터를 리턴 받는 방법으로 XML/Text/JSON을 지원한다. JSON은 텍스트 형태로 객체를 정의하는 방식이다. 이것은 XML과 1:1 매칭을 할 수도 있다. XML보다 훨씬 만들기 쉽고 사용법도 자바 객체를 사용하는 것과 유사하다. 그래서 이걸 사용해서 프로그램을 작성했다.
{
  "test1": "hello",
  "test2": "hello2"
}

위와 같은 메시지를 서버에서 응답으로 내보냈다고 할 때
// JSON 형태의 텍스트를 JavaScript 객체화 한다.
eval("res = " + request.responseText);

// 객체를 사용한다.
alert(res.test2); // 이 명령은 "hello2"를 출력한다.

배열은 다음과 같이 생성한다.
[
  ["test1", "test2"],
  ["test3", "test4"]
]

아래와 같이 사용한다.
eval("res = " + request.responseText);

// test4를 출력한다.
alert(res[1][1])

 * JSON 홈페이지 : http://www.json.org/
 * Java 객체를 이용해서 JSON 객체를 위한 텍스트 생성하기 : http://www.json.org/java/simple.txt 매우 단순한 방법으로 핵심 기능만 가지고 있다. 이것을 사용하길 권장한다. 라이브러리 다운로드는 http://www.JSON.org/java/json_simple.zip 에서 한다.
 * XML과 JSON간의 변환