1. io를 통한 채팅 프로그램 개발 경험이 정말로 큰 도움이 되었음.(종료) 

   =>덕분에.. 이젠 내가 대충 소켓,스레드 사용해서 어떤 프로그램을 만들고 싶다라고 생각이 들면.. 자바 도큐만
   보면서 구현이 가능할듯.. 뭐랄까.. 나름 상당한 고민을 했었고.. 그만큼 풀어냈다고 혼자 착각중임 -_-;
   큰 수확은.. 예외 핸들링을 통한.. while() 업무의 종료 조건 / 수행 조건의 구분이엇고.. 스레드에게 작업을
   시킨다는 것,병렬 처리의 필요성과 순차적 의존에 대한 한계등.. 대해서도 여러가지 깨달음이 있었음..  
    글고 PrintWriter(system.out) .. 그리고 오토 플러쉬.. 머리에 담아 뒀었는데.. 실제로 시행착오를 겪었고.. 
    디버깅해서 원인을 찾고.. 뒤통수 한대 맞은 기분이었음..OTL

2. 기존 nio 계획중 모든 기능 구현이 아닌, 우선 간단한 버전 (채널및 셀렉터 사용, 다이렉트 버퍼 사용)
   으로 다중 (63명) 채팅 룸 하나 구현. (한마디로 셀렉터 하나만 사용하겠다는 의미 클라1개,서버 2개) (오늘중으로 처리 가능)

3. 앞으로 db나갈 예정인데.. 쿼리에 대해 학습겸 해서..  간단하게 쿼리 생성 툴 만들 예정. (예전에 jsp
   교재 땜에 마구잡이로 구현은 해놨었음.)

  =>요걸 실전에 쓸수 있을정도로 만들라면.. ui는 대충 구현(swing사용)..  아주 복잡해질 reflect도 집어치우고..

   1.그냥 사용자가 클래스 이름(=테이블이름), 멤버 필드 타입,변수명 타입,변수명,(pk = 인스턴스 구분까지) ... 
     지정하게 하면 
   2.이클립스가 세터,게터 자동 지원하는 것처럼..  특정 필드 세터(update쿼리) , 게터(selecte쿼리)를 생성
     해줌 (=>xml 파일로 쏴주는 것도 간단할듯.. java.util.property 클래스 땡큐~
   3.복잡한 테이블간 or 행(row)간 or 열(col)간  where  xx and bb  join 등의 복잡한 연산은.. 접어둠


   vs  

   1. data define language : create , alter, drop  생성
   2. modify : insert, update, delete 생성
   3. query : select 
   로 구분하고.. 각 타입에 따라 패턴이 있을테니.. 고민해보고 만들어 봐도.. 

  vs row(레코드) 단위로 처리: delete 
      column(필드) 단위로 처리: insert , update, selete

결론: 관계형 db 만세~  테이블 = 클래스 , pk column & 하나의 row = 인스턴스 , 각각의 column = 클래스 멤버필드
   음.. 그리고 잘은 모르지만.. get,set,조회,정렬,삽입,삭제,생성,소멸등.. 복잡한 비지니스라는 것도 잘개 쪼개면.. 
   거의다 쿼리 단위로 쪼개질듯..  글고 클래스간 관계(association)도 fk를 통해서 표힌이 가능하고..하여간 db쿼리 공부겸 
   툴 만들기 늦어도 다음주 부터 시작. 

추가: 관계형 db의 한계.. 객체 지향의 interface , extends 등의 상속과, 해당 클래스와 메서드간 연결을 못한다. 
       그럼으로 POJO(Plain old java Object) 처럼 아무것도 상속 받지 않고, 겟,셋만 구현한 빈 클래스로 dao를
       만들어서.. 사용한다. 서버와 디비 사이단에.. 하이버네이트, 또 머가 있었드라..?

자바 ee5 : 애플릿 컨테이너 , 어플리케이션 컨테이너, 웹 컨테이너(jsp컨테이너로 jsp,서블릿 관리), ejb컨테이너,
      데이터베이스

--front end--
JAVA CLIENT LAYER (html,xml,css,기타등등.. view단에서 바로 db와 연결할라면.. JQuery=Ajax + html)
{
client tier :일반 어플리케이션 client , 다이나믹 웹 어플리케이션 client
}

--back end--  (jsp 등등)
JAVA SEVER LAYER (사용자=클라이언트의 요청(view)에 따라 컨트롤하여 비지니스 수행(control -> model = db쿼리작업)
{
web tier : jsp 페이지
business tier : EnterprizeBean A , EnterprizeBean B , ...
}

DATABASE LAYER
{
els tier : DB A , DB B ...
}

(etc)오늘 받은 jdbc 쿼리 문서중.. where 조건 연산자를 보니까.. 프로그래밍 언어의 조건(논리,비교) 연산자와는 조금 달리
    실제 비지니스에서 필요한 조건 연산(범위,테이블간(도메인)간 조인, 인스턴스(로우)간 컬럼간.. 등등)과 데이터 베이스의 
     무결성(not null)등등.. 그리고 제공하는 내장 프로시저의 경우.. api를 공부하듯 보면 될듯..
     아 글고 join 문(& 연산자 같음) 사용시 퍼포먼스 올리기 위해 "인덱스"를 사용하는 것이 좋습니다. 란 말이 있는데.. 예전에
     담배 피다 떠올린.. 숫자 중복 코드에서 배열의 인덱스 + 논리 연산자만 가지고 했을때 약 20배의 성능이 좋아 졌던 것과 
     유사한듯.. 
     그게 아니라도 최소한 문자열 비교보단, 숫자 비교가 빠르지..
     
     select = 컬럼(필드),컬럼,컬럼...
     from = 테이블,테이블,테이블...  
     where = 조건 & 조건 or 조건... 
     values = 값, 값, 값...
     
    말 그대로 if else 의 조합 + 게터,세터 아닌가..

    특정 부서의(테이블) 특정 이름 직원(컬럼일수도 있고 테이블일 수도 있고)의 연봉정보(테이블)를 알고 싶어요~  
    조인을 연산을 통해.. 추출하거나, 서브쿼리를 이용한다고 함. 

    채팅 프로그램에 db를 사용한다면;; p2p가 아니고 서버 브로드 캐스트 방식일때.. 해당 채팅룸의 모든 데이터를 db에
    남겼다면,..  xxx날짜에 채팅한 사람중 로그인,아웃 시간이(사용시간) 3시간이 넘고 여자면서 나이가 20~30대인 사람들
    의 데이터를 조회해주세요. 란.. 요청에 대해 어떻게 처리를 할것인가?  전부 and , between 조건으로 처리할 것인가?
    테이블을 멋지게 설계해두고 효율적인 방법으로 타겟을 좁혀 나갈것인가..


    오.. oracle sequence 란게.. 순차적으로 자동증가 하는 값을 생성하는 오라클 객체라네..  내가 오늘 채팅 구현할때
    쬐끔 신경쓰였던 것이.. 클라가 accpet 단에서 개별적으로 i/o스레드를 배정받는데.. 이 경우에 채팅에서 유저를 식별
    하기 위한 name or nickname or id를 어떻게 처리해야 할까였다. 왜냐하면 유저에 대한 아웃풋 스트림의 경우 다중 채팅
    임으로 모아둘(컨테이너를 포함하는 클래스) 필요가 있었고.. 식별을 일반 리스트처럼 인덱스의 경우는 별 문제가 없지만..
    , 셋or 맵 구조라면.. 중복되지 않는 키값이 필요했다. 내 경우엔 i/o 스레드 객체에 소켓 정보를 저장해두지 않고 인풋 스트
    림만 개별 스레드가 보관하는 방식이라.. 아웃풋 스트림을 모아둔 클래스에 서비스 메서드로 중복되지 않는 문자열을 만들게
    끔.. 초간단하게.. guest 란 기본 문자열에 number란 static 변수를 0부터  getUniqueName() 호출 시마다 return "guest" +
     num++; 요 딴식으로 구현했었다. -_-; (자동 증가 = 시퀀스라니까.. 떠올랐다;;)
     key&Object로 하냐..(물론 일반 프로그램이라면.. 클라쪽에서 어셉 요청
    (한마디로 서버주소 적은 소켓 객체 생성 성공) 

(etc2) java.sql.Connection , java.sql.Statement, java.sql.ResultSet, PreparedStatement 등등.. 내가 공부하고 있는 jsp
 교재를 보면서 한번씩 보고 이해하고 사용해봤던 클래스들이다.. 디비와의 통신(i/o스트림) 또한 자바라면 .. 소켓io,파일
 io와 거의 똑같이 설계했을 것이다. 스트림 생성-> i/o작업 -> 스트림 종료..  익숙해서 반갑다 ^_^//

 Class.forName()도 나오네..(reflect 개념에 속함.. 문자열에 매치하는 타입이 있으면 리턴) 내가 이번에 파일 매니저 만들면
 서 구현했던 팩토리 클래스도 xml에서 읽어올때 
 workerMap.put((String)entry.getKey(), Class.forName((String)entry.getValue())) 요렇게 했었는데..

 아.. 뒤에 보니까 드라이버 이름,db url,포트, 계정 이름, 비번등.. .. 약 한달전인가.. 오라클 12c 갈때 sid가 먼지도 몰랐던 때..
 도대체 왜 계정 생성이 안되는지.. 10시간 넘게 고민했었던 당시의 기억이 떠오른다.. 내 경운 11g 로 버전 낮춰서 초 간단하게
 계정 생성 성공.. (12가 먼가 복잡한 개념이 있었음;;)

  dbConnection.createStatement()도 눈에 익숙하고.. 정말로 jsp 책에 절하고 싶은 기분.. 리절트셋이니.. 트랜잭션이니..
  등등.. 리절트 셋의 개념도 잡아 주었고..커넥션 풀링의 개념은.. 책에서도 나왔었고.. 이번에 nio 서버 구현 공부를 하면서
  풀링에 대한 고민도 했었고.. 

  하핫.. 짧은 교재 벌써 다 봤네.. 조금 쉬었다가 nio 채팅 서버,클라 구현 GoGo!
    
(etc3)똥을 싸놓은 파일 매니저의 경우에.. 그 뒤에 꽤나 큰 수정이 있었다.. 허나 중요한 것이 아니기에 우선순위에서 밀림
   .. 다음주는 db쿼리 툴 , jsp 에 우선 순위.. 내 웹서버(정확히는 jsp 컨테이너인 톰캣아..스클립틀릿과 html들아 지못미..)


by givingsheart 2014. 1. 1. 16:41

아하하..


표준 콘솔 출력도 내가 포장해서 쓰려다가.. 
내가 만들라던 PrintWriter 객체나, System.out 이란 객체나.. 똑같은 넘들인것을..

그러니 만들어 질리가 없고.. 작동 할리도 없지.. 생각이 짧았음.
(단순한 실수였는데 문제를 빠르게 풀지 못했었음)


by givingsheart 2014. 1. 1. 16:41
상위폴더, 하위폴더, 파일들... 파일 디렉터리 구조를 현재 내 설계에서 제대로 순회한다는 
것이 얼마나 어려운지 체감중이다. 너무너무 우울하다 ㅠㅠ (애초 계획데로.. 트리로 구성
하는걸 조금 더 고민했었으면.. 한결 덜 했지 않을까..)

겉 멋만 들었는지.. 이런 저런 패턴 적용해보고.. 파일 매니저 설계한답시고 깝죽대고..
더 복잡해지고.. 좌절하고.. OTLOTLOTL...바보가 된 기분...

정신이 헤롱거리는데 내일까지 지장 받을듯.. 건들수록 망가진다.. 한동안 봉인해둔다.



현재 문제! 워커측이 콜러측에 문제 발생시 아무런 통지를 안해줬다.
boolean 리턴도 안했고, throws 선언도 안했다. 그냥 내부에서 assert : new Exception()만 
했었다.!

파일 트리 순회 메서드의 설계를 완전히 뒤집어 엎고, 현재 워크 인포 클래스의 루트 패스
개념과(재귀로 상위로 올라갈때 한계치), 순회시 사용하는 패스에 대해서 조금 더 고민이 필요하다.

(추가)

결국.. 아쉽지만 포기를 해버렸다. 이것만 붙잡고 있을 수는 없기 때문이다.

by givingsheart 2014. 1. 1. 16:39
첨부한 파일은 
1.선생님이 숙제라고 내주신 파일 리네임 작업 초간단 버전..

2.파일 작업을 발전시켜 현재 구현중인 파일 작업 시스템 -_-; 

(똥을 싸놨다..OTL)


개선해야할점.

기존 파일 시스템과 트리 자료구조의 유사성을 발견하고 

20131213 파일및 디렉토리 작업에 스레드 사용.zip


homework.zip


해당 디렉토리 내의 파일 작업(하위 디렉토리,파일들)을 
work란 단위로 묶어서 트리 구조로 만들어 작업물 이란 
개념으로 추상화 했는데..

위의 작업물로 워커 객체에게 작업을 시킬라고 했는데.. 
만만치가 않다.. 접근 방법이 이상했는지.. 이건 똥이 돼었다.

새로운 접근 방법은..

실제 작업을 할 워커 객체의 주도로 전체 작업 관련 필요
정보(시작 디렉토리, 목표 디렉토리, 수행할 작업(카피,리네임,
복사,삭제,앞의것들의 조합) )를 전달하고!

기존엔 워커클래스를 파일 작업용-> 카피 작업용 , 리네임 작업용
,삭제 작업요 등등으로 상속구조로 쪼갰는데.. 

위의 개념을.. 매니저 & 워커 개념으로 쪼갤 예정이다. 
한마디로 매니저 개념은 현재의 디렉토리에서 파일이 잇다면,
워커or 워커들을 호출해 작업을 나눠줘서 일을 시키고,
하위 디렉토리가 있다면 디렉토리의 수많큼 매니저를 새로 호출
해서 각각에게 하위 디렉터리를 하나씩 맡기고(재귀) 기존 매니저
는 자신의 디렉토리에 대해 비지니스 작업(리네임등)을 처리하고
자신의 업무를 끝냈으니 팩토리(풀)로 돌아가는 것이다.

고려할 점은.. 디렉토리의 수가 많아지면.. 스레드의 수가 급격히 늘
어날수 있다. 현재까진 if(isDirectory) 처리  if(isFile) 처리 순서로
처리했는데.. 이 순서를 바꿔야 할듯하다. 

*************************************************

제일 처음 아이디어(파일 업무를 트리 구조로 정리) 하는 것의
목표는.. 트리를 만들어 트리를 조직화(구축하며 정렬처럼 밸런스 형태) 
하면.. 업무의 갯수를 적절히 나눠서 (총 9개의 디렉토리라면 3개의 
스레드를 쓸때 3개씩 디렉토리로 노드 분할해서 하나의 스레드가 
하나의 노드를 처리하는식)
병렬 처리의 효과를 극대화 해보고 싶었던 것인데..

처음에 트리를 구성할때 단순하게 파일의 형태 그대로 보관을 했으니..
디렉토리의 갯수가 전혀 균형이 맞질 않는 문제 + 워커에게 실제 작업
을 어떻게 쪼개서 전달할지가 막막해졌다. 

위의 문제는 추후 고민해볼 여지가 있어보인다. (느낌상..)

***************************************************

ps.언제부턴가 스태틱에 강박관념이 생긴듯하다.. 개념상 스태틱이 붙어선
안돼는 경우에도.. 가급적 스태틱변수, 메서드등 으로 처리하려 고집한것 같다..

스태틱은 위험하다. 정말로 위험하다. 스태틱의 사용에 신중해지자! 화이팅!


(추가)
제대로는 모른다, 허나 저 위의 착상이 파일 처리에 있어서 자가 분할(스레드 생성)이란..개념에서 분산 파일 처리 솔루션(하둡) 느낌이 아닐까?

(추가)
선생님이 원한 것은, 단순한 RENAME 함수를 통한 폴더 변경 였는데.. 나는 왜 이걸 파일 시스템으로 발전시켜 고민을 
하고 있을가..? 어렵지만.. 고민하고 삽질하는 시간은 즐겁다.


by givingsheart 2014. 1. 1. 16:38


자바캔 nio


nio bytebuffer, channel 클래스




Thread join()관련

자바 스레드 생성자-소비자 패턴

자바 스레드 책 소개


스레드풀 관련


스레드풀 (자바 캔)

스레드풀 by 익스큐터


자바 소켓프로그래밍

자바 어노테이션 + vlidator클래스 만들어 사용자 정의 어노테이션 만들기(사전 지식:java.lang.reflect, 사전조건,사후조건등)


JUnit



어느 훌륭한 분의 블로그




클래스간 관계


(추가) 
제대로 본게 몇개 없다. 습득 가능한 지식에는 한계가 있다. 이것 저것 혹하지 말아야 한다.


by givingsheart 2014. 1. 1. 16:35

http://blog.naver.com/genesung?Redirect=Log&logNo=130082920571 님감사해요.. (__ ) 


사실 채팅 프로그램에 대단한 보안이 필요한건 아니고.. 

대충 보아하니.. des인데.. 그럼 서버측, 클라측 코드에 String Key 하나씩 심어놔야하나?

어디다가 숨겨놔야 안전할까...끙 -_-;;;  그나저나 text.getBytes()하고 있네.. 그나마 반갑다.

내가 암호,복호화할 데이터도.. directByteBuffer님까 -_-;



import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

 

private static final String _cipherAlgorithm = "DES";

 

String encryptText(String text, String key)
{
    String encrypted;
        
    try
    {
        SecretKeySpec ks = new SecretKeySpec(generateKey(key), _cipherAlgorithm);
        Cipher cipher = Cipher.getInstance(_cipherAlgorithm);
        cipher.init(Cipher.ENCRYPT_MODE, ks);
        byte[] encryptedBytes = cipher.doFinal(text.getBytes());
        encrypted = new String(Base64Coder.encode(encryptedBytes));
    }
    catch (Exception e)
    {
        e.printStackTrace();
        encrypted = text;
    }
        
    return encrypted;
}

 

String decryptText(String text, String key)
{
    String decrypted;

    try
    {
        SecretKeySpec ks = new SecretKeySpec(generateKey(key), _cipherAlgorithm);
        Cipher cipher = Cipher.getInstance(_cipherAlgorithm);
        cipher.init(Cipher.DECRYPT_MODE, ks);
        byte[] decryptedBytes = cipher.doFinal(Base64Coder.decode(text));
        decrypted = new String(decryptedBytes);
    }
    catch (Exception e)
    {
        decrypted = text;
    }

    return decrypted;
}

 

byte[] generateKey(String key)
{
    byte[] desKey = new byte[8];
    byte[] bkey = key.getBytes();
        
    if (bkey.length < desKey.length)
    {
        System.arraycopy(bkey, 0, desKey, 0, bkey.length);
            
        for (int i = bkey.length; i < desKey.length; i++)
            desKey[i] = 0;
    }
    else
        System.arraycopy(bkey, 0, desKey, 0, desKey.length);
        
    return desKey;
}


'프로그래밍 > 보안' 카테고리의 다른 글

jvm의 입장?  (0) 2014.01.02
"보안" 관련 문서(2013.12.11 자바 보안객체 사용 추가)  (0) 2014.01.01
by givingsheart 2014. 1. 1. 16:35

결론:thread safe가 필요할 경우 아래의 패키지를 활용할 것! 

 멀티스레드 동기화 문제에 대한 자바측의 편의 제공 클래스! java.util.concurrent 패키지!

=>한마디로 데이터를 원자적으로 보호한다. (내부적으로 synchrozied 처럼 lock, unlock 래핑한 것인듯.. 
   참조를 보호하는 거야.. reflect가 있으니.. Class 를 Object로 처리 가능할테고.. 아님 말고 -_-;)


패키지 java.util.concurrent.atomic

단일의 변수에 대한 락 프리로 thread 세이프인 프로그래밍을 지원하는 클래스의 소규모의 툴 킷입니다.


************************기타 찌끄레기 방법? 심플하니 좋은건가?********************


멀티쓰레드 환경에서 클래스의 멤버변수를 안전하게 사용하기 위해서 사용하는 방법이 sychronized 블록을 사용하거나, 변수를 volatile로 선언하는 방식이 있습니다. 두 방식의 차이에 대해 알아보았습니다.

 

Question of the month: Volatile vs. Synchronized re-visited

 

먼저 각각의 방식에 대한 한 쓰레드에서 처리 방식을 살펴보면 다음과 같습니다.

 

volatile

synchronized

Get a global lock on the variableGet a global lock on the monitor
Update the one variable from main memoryUpdate all shared variables that have been accessed from main memory
Process some statements
Write any change of the one variable back to main memoryWrite all shared variables that have been changed back to main memory
Release the lockRelease the lock

 

여기에서 알 수 있듯이 volatile이 선언되지 않은 멤버변수는 sychronized 블록이 아닌 곳에서 사용할 경우, 쓰레드가 변수값들을 복사해서 사용하기 때문에 다른 쓰레드에서 값을 변경했을 경우 같은 변수임에도 불구하고 실제와 다른 값을 읽을 수 있다는 것입니다.

 

Use Synchronized or Volatile when Accessing Shared Variables

 

각각의 방식의 장단점을 살펴보면 다음과 같습니다.

 

TechniqueAdvantagesDisadvantages

synchronized

Private working memory is reconciled with main memory when the lock is obtained and when the lock is released.Eliminates concurrency.

volatile

Allows concurrency.Private working memory is reconciled with main memory on each variable access.


by givingsheart 2014. 1. 1. 16:33
지식을 나눠주시니.. 감사할 따름.. ^^

http://helloworld.naver.com/helloworld/  

아래의 결론 = 대부분의 서비스에 UTF-8 이 일반적이다.

Java와 한글

Java는 String에서 사용하는 인코딩은 UTF-16 BE(Big Endian)이다. 문자열 전송/수신을 위해서 직렬화가 필요할 때에는 변형된 UTF-8(Modified UTF-8)을 사용한다. Java의 DataInput, DataOutput 인터페이스 구현체에서는 문자열을 기록하거나 읽어들일 때 이 변형된 UTF-8을 사용한다. 변형된 UTF-8의 인코딩 규칙은 표5에서 볼 수 있다.

표 5 변형된 UTF-8 인코딩 규칙

코드 범위

인코딩 규칙

U+0000

11000000 10000000 (0xC080)

U+0001 ~ U+FFFF

UTF-8 인코딩과 동일

U+010000 ~ U+1FFFFF

UTF-16 인코딩한 값을, UTF-8 인코딩함 (CESU-8)

변형된 UTF-8에서 U+0000을 2바이트로 표시하는 이유는 인코딩된 결과에 널 문자(00)가 나타나지 않도록 하기 위해서이다. C언어와 같이 NULL 문자를 문자열의 끝으로 처리하는 언어에서 U+0000을 읽을 때, 문자열의 끝으로 잘못 처리하는 일이 없도록 하기 위해서이다. 그리고 U+010000 이상의 코드를 표현하기 위한 CESU-8(Compatibility Encoding Scheme for UTF-16:8-bit)은 UTF-8의 변형인데 코드 포인트 U+010000 이상의 글자를 표현하기 위한 방법이다. Java에서 글자를 표현하기 위해서 2바이트 크기를 가지는 char를 사용하는데, 전체 유니코드 글자를 2바이트로 표현할 수 없기 때문에 이러한 방식을 사용한다. Java의 변형된 UTF-8은 CESU-8에 NULL 문자 처리(U+0000)을 추가한 것이다.

한글의 표현과 인코딩

Java에서는 유니코드의 코드 포인트 값을 String.codePointAt(int); 메서드를 이용하여 확인할 수 있다. 다음은 '한글'(U+D55C U+AE00)에 대한 코드 포인트 값을 출력한 예이다.

1
2
3
4
5
String string = "한글";
for (int i = 0; i < string.length(); i++) {

    System.out.print(String.format("U+%04X ", string.codePointAt(i)));
}
System.out.println();

코드 포인트에 대한 개념을 이해하고 있다면, 한글/영어 개수를 세거나, 바이트 수에 맞추어 한글/영어 문자열 자르기 등은 어렵지 않을 것이다.

Java에서 인코딩된 값을 알아보려면, getBytes() 메서드를 이용하여 확인할 수 있다. 다음은 '한글'에 대한 인코딩 값을 출력한 예이다.

1
2
3
4
5
6
String string = "한글";

byte[] bytes = string.getBytes();

for (byte b : bytes) {

    System.out.print(String.format("0x%02X ", b));
}
System.out.println();

여기에서 염두에 둘 점은 Java에서 문자열은 항상 UTF-16 BE 인코딩으로 저장되며, file.encoding시스템 프로퍼티에 의해 인코딩 값이 결정된다는 점이다. 특히 C언어를 많이 다루어 본 개발자라면 문자열을 C의 1바이트 char 배열로 여기는 경향이 강하기 때문에 이 차이점을 잘 이해해야 한다. 언어 차원에서 유니코드와 같은 캐릭터 인코딩을 지원하지 않는 C와 달리 Java에서는 언어 차원에서 유니코드와 여러 코드 페이지를 지원한다.

Java는 String 객체 내부(메모리 상에서) UTF-16 BE 인코딩으로 문자열을 저장하고, 문자열을 입/출력할 때에만 사용자가 지정한 인코딩 값 또는 운영체제의 기본 인코딩 값으로 문자열을 인코딩한다. JVM 기본 인코딩은 JVM 로딩 시에만 초기화되므로, 코드 중간에서 file.encoding 프로퍼티를 바꾸는 것은 아무 의미가 없다. 만약 file.encoding이 지정되어 있지 않다면, OS 환경 변수(예: LANG) 값을 따른다. Java에서 글자를 깨뜨리지 않으려면, 문자 집합의 이름을 지정해야 한다. 예를 들어, 문자열 객체의 getBytes() 메서드를 이용하여 바이트 배열을 얻고자 할 때, getBytes() 대신 getBytes(String charsetName) 메서드를 사용하고, 반대로 바이트 배열에서 문자열 객체를 얻고자 할 때, new String(byte[] b) 대신 new String(byte[] bs, String charsetName) 메서드를 사용한다.

웹과 한글

한글 처리, 특히 웹에서의 한글 처리는 무척 까다롭다. 그 이유는 사용자의 환경이 매우 다르다는 데 있다. 웹 프로그래밍을 하려면, 운영체제의 기본 인코딩, Java 소스 코드의 인코딩, JSP 파일의 인코딩, HTTP 요청의 인코딩, HTTP 응답의 인코딩, 데이터베이스의 인코딩, 파일의 인코딩 - 이렇게 많은 인코딩과 마주하게 된다.

 

a0a6fa0efd3c5c256f870861df6f7e46

웹에서 한글이 왜 깨지는가? 브라우저 인코딩 값과 서버 인코딩 값이 다르기 때문이다. Tomcat에서는 파라미터 인코딩 및 키와 값을 설정하기 위해 org.apache.catalina.connector.Request.parseParameters 메서드와 org.apache.tomcat.util.http.Parameters.processParameters 메서드를 이용하여 처리하고 있다.

org.apache.catalina.connector.Request.parseParameters 메서드

1
2
3
4
5
6
7
8
9
10
11
protected void parseParameters() {

...

String enc = getCharacterEncoding();


...
if (enc != null

{
    parameters.setEncoding(enc);

} else {

    parameters.setEncoding("ISO-8859-1");

}
...
}

org.apache.tomcat.util.http.Parameters.processParameters 메서드

1
2
3
4
5
6
7
8
public void processParameters(byte bytes[], int start, int len, String enc) {
...

tmpName.setBytes(bytes, nameStart, nameEnd – nameStart);

tmpValue.setBytes(bytes, valStart, valEnd – valStart);
...

addParam(urlDecode(tmpName, enc), urlDecode(tmpValue, enc));

...
}

위 코드를 보면 알 수 있듯이, 인코딩이 올바르지 않게 설정되면 파라미터에 잘못된 값이 들어감을 알 수 있다. 다음 코드는 URL 디코딩이 잘못되면 어떤 결과가 초래되는지 쉽게 살펴볼 수 있는 예이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String hangul = "한글";

String[] encodings = new String[] {"EUC-KR", "UTF-8", "ISO8859-1"};
  
for (String encoding1 : encodings) {

    String encoded = URLEncoder.encode(hangul, encoding1);

    System.out.println(encoded);

    System.out.print("\t");
      
    for (String encoding2 : encodings) {

        String decoded = URLDecoder.decode(encoded, encoding2);

        System.out.print(decoded + "\t\t");
    }
    System.out.println("\n");
}

5ea8352df1fc1bc1fb1bb9bae636ec5c

웹에서 여러 인코딩을 지원하려면, 인코딩된 URL 문자열과 사용한 인코딩 정보를 파라미터로 전달 해야 한다. 예를 들어, "/search.nhn?query=%C7%D1%B1%DB&ie=EUC-KR" 과 같이 URL이 설정되어 있다면, ie 파라미터 값을 이용하여 query의 파라미터 값을 URL 디코딩하면 된다. 그리고 가능하다면 Javascript의 encodeURI 메서드 (또는 encodeURIComponent 메서드)를 사용하는 것이 좋다.

Javascript에서의 URL 인코딩

Javascript는 escape, encodeURI, encodeURIComponent 메서드를 이용하여 URL을 인코딩할 수 있다. 이 중 escape 메서드는 A~Z, a~z, 0~9, @*-_+./ 문자가 아니면 유니코드 형식으로 인코딩하는데, ASCII 문자는 %XX, 그 외는 %uXXXX 형태로 인코딩된다. 예를 들어, '한글'을 escape 메서드로 인코딩하면, %uD55C%uAE00으로 인코딩되므로, Tomcat에서 URL 디코딩 시에 문제가 발생하게 된다. 일반적으로 문자열을 URL 인코딩하기 위해서 encodeURI 메서드를 많이 사용하며, :;=?& 문자는 인코딩하지 않는다.

Java의 URLEncoder.encode 메서드와 Javascript의 encodeURI 메서드는 공백(whitespace)을 '%20'으로 인코딩하느냐, '+'로 인코딩하느냐만 다르다. 마지막으로 encodeURIComponent 메서드는 encodeURI 메서드와 유사하지만, :;/=?&도 인코딩한다.

브라우저에서의 EUC-KR 인코딩

EUC-KR 인코딩은 2,350자의 한글만 사용할 수 있다. 그러면 EUC-KR 인코딩으로 이루어진 웹 페이지에서 '똠방각하'와 같은 문자열은 어떻게 처리될까? 브라우저 별로 다국어가 포함된 URL 인코딩 처리하는 방법이 다르다. EUC-KR 인코딩으로 표현 가능한 '한글'을 웹 URL에 넣어 브라우저 인코딩 테스트를 해보면 Internet Explorer, Firefox, Chrome 모두 한글을 '%C7%D1%B1%DB' 로 인코딩한다. 그러나 EUC-KR로 인코딩할 수 없는 '똠방각하' 를 처리할 때는 브라우저마다 결과가 다르다. 브라우저 별 EUC-KR 인코딩 방법을 테스트하기 위해 EUC-KR 인코딩을 사용하는 검색 시스템인 알타비스트를 이용해보기로 한다.

3b154714c4b23edcc47499ba5a3c75ff

브라우저 검색 URL의 p 파리미터 값을 살펴보자.

표 5 브라우저 별 EUC-KR 인코딩 결과

브라우저

인코딩된 값

화면에 표시되는 문자열

Internet Explorer

%26%2346624%3B%B9%E6%B0%A2%C7%CF

&#46624;방각하

Firefox

%A4%D4%A4%A8%A4%C7%A4%B1%B9%E6%B0%A2%C7%CF

ㄸㅗㅁ방각하

Chome

%8Cc%B9%E6%B0%A2%C7%CF

c방각하

Chrome은 EUC-KR에 있는 확장 완성형의 문자를 지원하지 않기 때문에 '똠'을 인코딩할 수 없다. Internet Explorer는 EUC-KR에 없는 문자의 경우 유니코드 포인트 값으로 표현한다. 즉 '똠'의 유니코드 코드 포인트 값인 46624(U+B620)으로 URL 을 인코딩 한다. Firefox는 한글 채움 문자를 이용하여 음절을 표시하고 있다. 한글 채움 문자는 KS X 1001 표준안에 정의되어 있으며, (채움) 초성 중성 종성의 형태로 표시하고, 초성, 중성, 종성의 값이 없는 경우 (채움)으로 표시한다. EUC-KR 인코딩에서는 (채움) 값이 0xA4 0xD4이므로, 다음과 같이 인코딩된다.

표6 EUC-KR로 똠방각하를 인코딩할 때

인코딩

%A4%D4

(채움)

%A4%A8

%A4%C7

%A4%B1

%B9%E6

%B0%A2

%C7%CF

알아두면 좋은 것들

영문 MS Windows는 CP1252, 한글 MS Windows는 MS949가 기본 인코딩이다. 리눅스에서는 LANG 환경 변수에 따라 다르지만, ko, ko_KR, ko_KR.eucKR은 모두 EUC-KR 인코딩이며, ko_KR.UTF-8만 UTF-8 인코딩이다. CentOS의 경우 /etc/sysconfig/i18n에서 시스템 기본 인코딩을 설정할 수 있다. 참고로 i18n은 국제화(internationalization)를 의미하며, l10n은 지역화(localization)을 의미한다. 18과 10이라는 숫자는 i와 n 사이, 또는 l과 n 사이의 글자 수를 의미한다. 요즘 편집기는 여러 인코딩을 처리할 수 있으므로, 보통 문서의 처음에 BOM(Byte Order Mark)이라는 값을 지정하여 인코딩 정보를 저장한다. UTF-8은 0xEF 0xBB 0xBF이며, 나머지 인코딩에 대한 BOM 값은 위키백과(http://en.wikipedia.org/wiki/Byte_order_mark)를 참고하면 좋다.

[1] 유니코드, http://ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C

[2] UTF-8, http://ko.wikipedia.org/wiki/UTF-8

[3] 유니코드 정규화,http://ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C_%EC%A0%95%EA%B7%9C%ED%99%94

[4] 옛한글, http://ko.wikipedia.org/wiki/%EC%98%9B%ED%95%9C%EA%B8%80

[5] 바이트 순서 표식,http://ko.wikipedia.org/wiki/%EB%B0%94%EC%9D%B4%ED%8A%B8_%EC%88%9C%EC%84%9C_%ED%91%9C%EC%8B%9D




표2 유니코드 범위 목록에서의 한글 관련 범위

이름

처음

개수

한글 자모 (Hangul Jamo)

1100

11FF

256

호환용 한글 자모 (Hangul Compatibility Jamo)

3130

318F

96

한글 자모 확장 A (Hangul Jamo Extended A)

A960

A97F

32

한글 소리 마디 (Hangul Syllables)

AC00

D7AF

11184

한글 자모 확장 B (Hangul Jamo Extended B)

D7B0

D7FF

80


표 3 한글 소리마디에서 초성/중성/종성에 대한 순서 값

초성

중성

종성

초성

중성

종성

0

채움

14

1

15

2

16

3

17

4

18

5

19

 

6

20

 

7

21

  

8

22

  

9

23

  

10

24

  

11

25

  

12

26

  

13

27

  



한글 음절의 코드 포인트 값은 시작 값인 U+AC00에 ((초성 값 x 21) + 중성 값) x 28 + 종성 값을 더하면 된다. 예를 들어, '한'이라는 글자는 'ㅎ', 'ㅏ', 'ㄴ'으로 구성되어 있으며, 각각 18, 0, 4 값을 가지고 있으므로, '한'의 코드 포인트 값은 U+AC00 + ((18 x 21) + 0) x 28 + 4 = U+AC00 + U+295C = U+D55C가 된다. 이를 역으로 생각해 보면, 한글 음절에 대해 초성, 중성, 종성의 분리가 가능하다. 즉 한글 음절의 코드 포인트 값에서 U+AC00을 뺀 값을 ①이라 한다면, 다음과 같이 정리할 수 있다.

  • 의 값을 (21 x 28)로 나눈 몫은 초성
  • 의 값을 (21 x 28)로 나눈 나머지를, 28로 나눈 몫은 중성
  • 의 값을 28로 나눈 나머지는 종성
간단하게 3차원 배열로 생각하면 될듯  [초성][중성][종성]  결국 구하려면 
((초성의 인덱스 *중성의 배열크기) +중성의 인덱스 )*종성의 배열크기 + 종성의 인덱스



유니코드 정규화(Unicode equivalence)

한글 소리 마디와 한글자모, 한글 자모 확장 이렇게 두 개의 코드 영역이 있다는 것은 같은 글자를 표현하는 서로 다른 두 개의 방법이 있다는 것을 말한다. 이것은 한글뿐만 아니라 다른 언어에서도 나타나는 현상이다. 가령 "n"을 표현할 때 U+00F1을 사용할 수도 있고, U+006E (라틴 소문자 "n") 과 U+0303( 결합 틸데 "◌̃")을 연이어 사용하여 표현할 수도 있다. 유니코드 정규화(Unicode equivalence)란 이렇게 연속적인 코드를 사용하여 표현한 어떤 글자를 처리하는 방법을 다루는 명세이다. 유니코드 정규화에는 다음과 같은 네 가지 방법이 있다.

표 4 유니코드 정규화 방법과 예

정규화 방법

NFD

(정준 분해)

Normalization Form Canonical Decomposition

A (U+00C0)  A (U+0041) + ̀ (U+0300)

위 (U+C704)   (U+110B) +  (U+1171)

NFC

(정준 분해한 뒤 다시 정준 결합)

Normalization Form Canonical Composition

A (U+0041) + ̀ (U+0300)  A (U+00C0)

 (U+110B) +  (U+1171)  위 (U+C704)

NFKD

(호환 분해)

Normalization Form Compatibility Decomposition

 (U+FB01)  f (U+0066) + i (U+0069)

NFKC

(호환 분해한 뒤 다시 정준 결합)

Normalization Form Compatibility Composition

 (U+F914),  (U+F95C),  (U+F9BF)   (U+6A02)

이중 한글 처리와 관련된 것은 NFD(소리 마디를 첫가끝 코드로 분해)와 NFC(첫가끝 코드를 소리 마디로 결합)이다.

Java는 유니코드 정규화 기능을 지원하고 있다. 아래 코드는 그 예제이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.text.Normalizer;
  
public class NormalizerTest {

    private void printIt(String string) {

        System.out.println(string);

        for (int i = 0; i < string.length(); i++) {

            System.out.print(String.format("U+%04X ", string.codePointAt(i)));
        }
        System.out.println();
    }
  
    @Test
    public void test() {

        String han = "한";

        printIt(han);
          
        String nfd = Normalizer.normalize(han, Normalizer.Form.NFD);

        printIt(nfd);
          
        String nfc = Normalizer.normalize(nfd, Normalizer.Form.NFC);
        printIt(nfc);
    }
     
}

아래는 위의 코드를 실행한 결과이다.

U+D55C
ㅎㅏㄴ
U+1112 U+1161 U+11AB
U+D55C

만약, 아래아 한(e049e1742f7b78edf7fd7b9762fb2523)을 NFC로 만들고자 한다면, 'ㅎ'(U+1112), 'ㆍ'(U+119E), 'ㄴ'(U+11AB)를 결합하면 된다. 옛한글의 경우 글꼴에 따라 출력이 되지 않을 수 있으므로, 옛한글 글꼴을 지원하는 은글꼴 또는 함초롬체를 사용하여야 한다.


'프로그래밍 > 인코딩' 카테고리의 다른 글

java에서 한글 encode  (0) 2014.01.01
by givingsheart 2014. 1. 1. 16:31
package threadFight;

public class main
{
public static void main(String[] args)
{
// TODO Auto-generated method stub
Unit unit1 = new Unit();
Thread th = new Thread(unit1);
th.start();
}
}

class Unit implements Runnable
{
public String name = "홍길동";
public int hp = 50;
@Override
public void run()
{
while(this.isLive())
{
// TODO Auto-generated method stub
//랜덤으로 상대에게 타격한다.
int at = (int)(Math.random()*6 ); //0~5
hp-=at;
System.out.println(name + " 님이 " + at + " 데미지로 자해하고 있습니다. hp:" + hp);
}
System.out.println(name + " 님이 뒤졌습니다.");
}
public boolean isLive()
{
return hp >0;
}
}


아..글고,.. 런에이블 편하게 쓸라면...흠.. 멤버 필드로 Thread를 갖고.. 위임 패턴을 적용해볼까?  

class A 라면.. 런에이블 조립해서 run 재정의 하고.. 내부적으로 생성자에서 멤버 필드 스레드 객체를 생성할때.. this(자신)을 
매개변수로 넣으면 스레드는 이제 내 객체(a)를 참조할수 있으니.. start(){ thread.start(); } 처럼 간단하게 사용 가능할듯 .. 
=>문제는.. run()이 퍼블릭 인터페이스로 노출되는점 ㅠㅠ  사용자께서(나?) start()로 사용해 주시길..

public class ddd
{

public static void main(String[] args)
{
// TODO Auto-generated method stub
ChildA a = new ChildA();
ChildB b = new ChildB();
a.start();
b.start();

}

}

abstract class Base
{
protected Thread thread;
}

class ChildA extends Base implements Runnable
{
ChildA()
{
thread = new Thread(this);
}
@Override
public void run()
{
for(int i=0; i< 100; i++)
{
System.out.println(thread.getName());
}
}
public void start()
{
thread.start();
}
}

class ChildB extends Base implements Runnable
{
ChildB()
{
thread = new Thread(this);
}
@Override
public void run()
{
for(int i=0; i< 100; i++)
{
System.out.println(thread.getName());
}
}
public void start()
{
thread.start();
}
}


=>core api에서 제공하는 인터페이스의 사용에 대해 아주 쬐금 감을 잡은듯도 하다.. 상속을 받으면 유연하지 않으니.. 가급적
core api 의 경우 interface를 구현하고 해당 클래스를 멤버 필드로 갖자(상속보단 구성!)

아니면 말고.. 그만 놀고 공부하자  -_-;


ps.멀티 스레딩 관련 볼만한 책들.. 갖고 싶다..




by givingsheart 2014. 1. 1. 16:29

예전에 시스템 프로퍼티 전부 출력해보고.. 그때 awt 분석하다가..os 종속적인 부분들에 대해 프로퍼티로

값을 읽어오는걸 봐서..


java.util.properties 는 내 예상보다 우월한 클래스였다.

테스트 고고 

아.. 그나저나 컨테이너간의 형 변환이 아주 쉽다. 프로퍼티는 해쉬셋 형태인데.. 요걸 list구조에
쉽게 넣을수 있다. addAll() 메서드 대박!

java.util.concurrent 요 패키지 어제 살짝 훑어봣는데.. 대박임 (스레드 관련해서)

load(Reader) / store(Writer, String) 메소드는, 문자 베이스의 스트림에 대한 프로퍼티의 로드와 포함을, 후술 하는 단순한 행 지향 형식에서 실시합니다. load(InputStream) / store(OutputStream, String) 메소드는 load(Reader) /store(Writer, String) 페어와 같이 동작합니다만, 입력/출력 스트림이 ISO 8859-1 문자 인코딩으로 encode 되는 점만은 다릅니다. 이 인코딩으로 직접 표현할 수 없는 문자는,Unicode 이스케이프를 사용해 기술할 수 있습니다. escape sequence로 사용할 수 있는 것은, 단일의 문자 'u' 뿐입니다. native2ascii 툴을 사용하면(자), 프로퍼티 파일을 다른 문자 인코딩으로 변환하거나 그 역을 실행할 수 있습니다.

loadFromXML(InputStream) 및 storeToXML(OutputStream, String, String) 메소드는, 프로퍼티을 단순한 XML 형식으로서 로드 및 포함할 수 있습니다. 디폴트에서는 UTF-8 문자 인코딩이 사용됩니다만, 필요에 따라서 인코딩을 지정할 수 있습니다. XML 프로퍼티 문서에서는, 다음의 DOCTYPE 선언이 사용됩니다.

<! DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 
프로퍼티을 export/임포트 할 경우에, 시스템 URI (http://java.sun.com/dtd/properties.dtd)에는 액세스 하지 않습니다. 시스템 URI 는, DTD 를 일의에 식별하는 캐릭터 라인으로서 사용됩니다. 그 내용은 다음과 같습니다.
<? xml version="1.0" encoding="UTF-8"? ><! -- DTD for properties --><! ELEMENT properties ( comment?, entry* ) ><! ATTLIST properties version CDATA #FIXED "1.0"><! ELEMENT comment (#PCDATA) ><! ELEMENT entry (#PCDATA) ><! ATTLIST entry key CDATA #REQUIRED>


음.. 사용하기 엄청 간단함..
package properties;
import java.io.*;
import java.util.*;
public class main
public static void main(String[] args) 
{  
// TODO Auto-generated method stub  
//properties를 통한 xml 파일 저장,로드    
//로드 테스트  
Properties pro2 = new Properties();  
try  {   pro2.loadFromXML(new FileInputStream("park.xml"));  }  
catch (InvalidPropertiesFormatException e1)  
{   // TODO Auto-generated catch block   e1.printStackTrace();  }  
catch (FileNotFoundException e1)  
{   // TODO Auto-generated catch block   e1.printStackTrace();  }  
catch (IOException e1)  {   // TODO Auto-generated catch block   e1.printStackTrace();  }    

//테스트용 출력     
Iterator<Object> it = pro2.keySet().iterator();  
System.out.println("로드 테스트 출력");     
while(it.hasNext())     {      System.out.println(it.next());     }       

Properties pro = new Properties();  
pro.put("1", "장호1");  
pro.put("2", "장호2");  
pro.put("3", "장호3");  
pro.put("4", "장호4");  
pro.put("5", "장호5");    

try  {   pro.storeToXML(new FileOutputStream("park.xml"),"a");  }  
catch (FileNotFoundException e)  {   // TODO Auto-generated catch block   e.printStackTrace();  }  
catch (IOException e)  {   // TODO Auto-generated catch block   e.printStackTrace();  }      
}}


'자바se api > util' 카테고리의 다른 글

set,map,properties (2013/12/04)  (0) 2014.01.01
by givingsheart 2014. 1. 1. 16:29