2013년 9월 15일 일요일

내 자전거

한 여름 비 많이 오고 더웠던 것 생각하면 "올해도 가을은 없겠구나"란 생각이 들었었다. 그런데 요즘 날씨가 너무 좋다. 전형적인 가을 날씨다. 하늘은 높고 구름 한점 없고 적당히 덮고 적당히 시원하고~
"빨리 밖으로 나와 나랑 놀자~"라고 외치는 날씨다. 외면할 수 없지. 그래서 자전거를 샀다. 한 15만원 정도면 사겠지 했는데 두 배 더 들었다. 워낙 비싼 자전거들이 많아서 "그 정도면 싸네~" 라고 할 사람도 있겠지만 나한테는 좀 비싸게 느껴진다.
하지만 메탈 느낌의 진한 회색 프레임에 보라색으로 포인트를 준 것이 예뻐 보인다. 무게도 11kg 후반 정도로 무난한 것같고, 애들과 같이 탄천 자전거 도로를 따라 달리는데 페달질 할 때마다 쭉쭉 나가는 것이 마음에 든다. 큰 애가 타고 있는 접이식 자전거를 탈 때랑은 차원이 다르다. 비싼 값을 하는 듯~
탄천변 따라 한 25KM 정도 가면 한강인데 언제 한번 달려 봐야겠다.
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 6월 8일 토요일

C++ Programming Language 4th Edition

드디어 왔다!! The C++ Programming Language 4th Edition.
한달 전쯤 Amazon에서 사전 구매신청했는데 7월달에 온다던 책이 몇 일전에 왔다. 아이 좋아라. The C++ Programming Language Special Edition을 너무 재미있게 봤는데 이 책도 너무 기대된다.
Special Edition은 단순히 언어에 대한 설명에만 치우치지 않고 설계와 개발 방법을 실무적 관점에서 제시해 주었다. 조금 과장되게 말하면 C++와 관련된 다른 서적은 읽을 필요가 없을 정도로 내용이 훌륭했다.
오랜 시간을 들여 만들어진 새로운 C++ 표준(C++11)에 대한 내용을 Creator의 시각으로 담고 있다는 것만으로도 빨리 보고 싶게 만드는 책인 것 같다.
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 5월 26일 일요일

HttpURLConnection에서 301, 302 오류 발생 시

Redirection Exception
java.net 패키지 내 HttpURLConnection 클래스를 이용하여 HTTP로 데이터를 받을 때 HTTP_OK (200)가 아닌 HTTP_MOVED_TEMP (302), HTTP_MOVED_PERM (301) 예외가 발생할 때가 있다. 두 가지 형태로 처리할 수 있을 거 같은데 하나는 Exception을 던져 더 이상 진행하지 못하게 하는 것이고 다른 하나는 Redirect된 URL을 새로 받아 처리하는 방식이다.
Redirected URL 수용하기
Redirect된 URL을 새로 받아 처리하려면 HttpURLConnection의 헤더에서 "Location" 정보를 받아 다시 Connect 한다.
public InputStream GetHTTPInputStream(URL url) throws Exception
{
    int redirectedCount = 0;
    while( redirectedCount <= 1 )    // Redirection 1번 허용
    {
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
            
        httpConn.setConnectTimeout(10000);    //< 연결 제한시간(단위 : msec, 0은 무한)
        httpConn.setReadTimeout(0);           //< 읽기 제한시간(단위 : msec, 0은 무한)
        httpConn.setUseCaches(false);         //< 캐시 사용 여부 설정(기본 설정: true)

        // URL을 요청하는 방법 설정 (GET|POST|HEAD|OPTIONS|PUT|DELETE|TRACE, 기본 설정: GET)
        httpConn.setRequestMethod("GET");
        
        int resCode = httpConn.getResponseCode();

        if( resCode == HttpsURLConnection.HTTP_OK )
        {
            return httpConn.getInputStream();
        }
        // Redirection 발생하는 경우
        else if( resCode == HttpsURLConnection.HTTP_MOVED_TEMP
              || resCode == HttpsURLConnection.HTTP_MOVED_PERM )
        {
            // Redirected URL 받아오기
            String redirectedUrl = httpConn.getHeaderField("Location");
            url = new URL(redirectedUrl);
        }
        // 이외의 오류는 Exception 처리
        else
            throw new MalformedURLException("can not connect to the url [" + url.toString() + "] Code: " + resCode);
        
        ++redirectedCount;
    }
}
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 4월 29일 월요일

Java Swing Example

Java Swing Component를 이용하여 GUI를 만들려면 다음 사이트 참고!
Component 별로 Example이 잘 정리되어 있음.
Using Swing Components: Examples
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 4월 3일 수요일

Java enum의 실체

들어가기
enum! 키워드가 같기에 C++에서와 같이 생각했었다. 지난번 글에서도 이 생각이 깔려 있기에 Java의 enum type도 단순한 줄만 알았다. 기능이 조금 더 붙어서 설명과 같은 부가적인 정보를 추가로 정의해 쓸 수 있는 줄 알았다. 그런데 샘플 코드들을 보면 여기서 끝이 아닌 것 같다는 생각이 뽀록뽀록 든다...
Effective C++ 시리즈 정말 좋은 책이다. 다른 많은 사람들도 그렇게 생각하는지 Effective 시리즈는 언어별로 있는 듯 하다. 물론 저자는 다 틀리다. 예전에 Effective C# 이란 책도 있었는데 Java에도 Effective Java란 책이 있어 구입했다. Joshua Bloch가 지었으니 내용은 뭐 말할 필요 없겠지.
아직 다 보진 못했지만, 이 책 세 번째 항목에 enum으로 구현한 싱글톤 패턴에 대한 내용이 나온다. "enum으로 싱글톤을 구현하다니 말이되나?" 라고 생각했었는데 이 부분을 보고 Java의 enum에 대한 실체를 다시 생각하게 됐다.
Java의 enum
다음과 같이 정의된 enum이 있다고 하자.
public enum ValueType
{
      Unknown
    , Text
    , Number
    , DateTime
    ;
}
switch문에서 사용할 때는 차이가 있지만, 위 정의는 다음과 같이 동작하지 않나 싶다.
public class ValueType
{
    public final static Unknown = new ValueType();
    public final static Text = new ValueType();
    public final static Number = new ValueType();
    public final static DateTime = new ValueType();
}
컴파일러가 위와 같이 유일한 객체를 만드는 것이다. 위 예에서는 별도의 생성자가 없으므로 기본 생성자를 이용하고 있다(정의된 생성자가 없으면 Java 컴파일러가 기본 생성자는 만듬). 만약, 생성자가 다른 형태로 구현되어 있다면 그 형태를 맞춰야 한다. 다음의 예를 보자.
public enum ValueType2
{
      Unknown("unknown")
    , Text("string")
    , Real("real")
    , DateTime("datetime")
    ;
 
    private final String _description; 

    private ValueType(String description)
    {
        this._description = description;
    }
 
    public String getDescription()
    {
        return this._description;
    }
}
문자열 하나를 인수로 받는 생성자가 정의되어 있다. 이런 경우 다음과 같이 정의하면 컴파일러가 불만을 토로한다.
public enum ValueType2
{
      Unknown
    , Text
    , Number
    , DateTime
    ;
    ... (후략) ...
}
이런 현상을 종합해 보면, Java의 enum은 컴파일러가 만드는 유일 객체의 집합이란 것이다.
enum을 이용한 싱글톤 클래스
enum만 보면 굉장히 어색해 보이지만 Java 컴파일러가 enum을 처리하는 것을 생각해 보면 enum을 이용하여 싱글톤 클래스를 만드는 것이 전혀 어색하지 않다. 오히려 언어적으로 객체가 유일하도록 보장하는 것이니 가장 확실한 싱글톤 클래스를 만드는 것이 아닌가 싶다.
다음은 enum을 이용하여 만든 싱글톤 클래스의 예이다.
public enum ProgramSetting
{
    Instance;   //< 유일 객체

    /// 속성 저장 멤버
    private final HashMap<String, String>     _properties;

    private ProgramSetting()
    {
        _properties = new private HashMap<String, String>();
    }

    public String getProperty(String key) { ... }

    public void setProperty(String key, String value) { ... }

    ...
}
사용법은
    ...
    ProgramSetting.Instance.getProperty(...);
    ProgramSetting.Instance.setProperty(...);
    ...
Java, 참~ 재밌다.
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 3월 10일 일요일

Java enum Type

Enumeration Type
이뉴머레이션 타입. 우리나라 말로 열거형이다. C++로 작업할 때 많이 사용했었는데 다음과 같은 장점이 있다.
의미 있는 값 표시
#define을 이용하면 정의된 값으로 치환되기 때문에 디버깅할 때 힘들다. 이 때 enum을 이용하면, 정수로 변환되어 사용되긴 하지만 enum에서 정의한 단어 즉, 의미있는 단어가 그대로 나오기 때문에 직관적으로 알 수 있다.
형태에 안전한 코드
형태에 안전하도록 제약조건을 둘 수 있다. 어떤 함수(내지는 메소드)를 someFunc(int valueType)와 같이 정의했는데 인수로 들어가는 값이 0: Unknown, 1: String, 2: Number, 3: DateTime 이라고 해 보자. 인수를 정수형태로 받기 때문에 0 ~ 3 이외의 값을 입력받을 수 있다. 5가 입력되면 어떻게 처리한단 말인가...
자~ 다음과 같이 enum 타입을 정의하고
enum ValueType
{
      eUnknown = 0
    , eString = 1
    , eNnumber = 2
    , eDateTime = 3
    ;
}
someFunc(ValueType valueType) 으로 정의하면 받을 수 있는 인수값이 enum에 정의한 값으로 제한된다. 즉, 형태와 값에 안전한 코드를 생성할 수 있다.
enum in Java
지금까지는 C++ 얘기였고, Java 얘기를 해보자.
JDK 1.5부터 enum 형태를 지원하기 시작했단다. Java의 enum도 C++에서와 마찬가지 효과를 얻을 수 있으며, 정의는 다음과 같이 한다.
public enum ValueType
{
      Unknown
    , String
    , Number
    , DateTime
};
C++와 비슷하다... 여기서 끝나면 뭔가 허전하다. 뭔가 조금이라도 좋아진 것이 있을텐데... 그렇다. Java의 enum은 값을 정수 이외의 다른 형태의 값도 가질 수 있다. 즉, 다음과 같이 정의할 수 있다.
public enum ValueType
{
      Unknown("unknown")
    , Text("string")
    , Integer("integer")
    , Real("real")
    , DateTime("datetime")
    ;
 
    private String _description; 


    private ValueType(String description)
    {
        this._description = description;
    }
 
    public String getDescription()
    {
        return this._description;
    }
}
ValueType.Text.getDescription()와 같이 하면 지정된 enum 값에 할당된 값을 얻을 수 있다.
값을 가져올 수 있으면 값에서 enum 정의를 가져 올 수도 있을 것이다.
public enum ValueType
{
      Unknown("unknown")
    , Text("string")
    , Integer("integer")
    , Real("real")
    , DateTime("datetime")
    ;
 
    private String _description; 


    private ValueType(String description)
    {
        this._description = description;
    }
 
    public String getDescription()
    {
        return this._description;
    }

    public static ValueType getTypeFromString(String description)
    {
        for(ValueType valueType : ValueType.values())
        {
            if( description.equalsIgnoreCase(valueType._description) )
                return valueType;
        }

        return Unknown;
    }
}
위와 같이 정의된 getTypeFromString()을 이용하면, 설정파일에서 값을 읽어 바로 enum으로 변환하여 사용할 수 있다.
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 2월 17일 일요일

Expression Parser in Java #1

우연히
C/C++로 데이터마이닝 툴 만들다가 적(跡)이 바뀌는 바람에 Java로 작업을 하고 있다. 12~3년 전에 잠깐 써보고 다시 하는 건데 아쉬운 부분도 있지만, 신경 쓸 부분이 많이 줄어 꽤 편하게 작업하고 있다. 하지만 어떤 클래스가 무슨 기능을 하고 어떤 메소드를 이용해야 하는 지 잘 모르니 많이 뒤져 보면서 필요한 것들을 찾고 있다.
그러던 중, Shunting-Yard Algorithm을 우연히 알게 되었다. 예전에 C/C++로 수식파서 만든다고 있는 머리 없는 머리 굴려가며 고생한 적이 있었는데 그때 이걸 알았다면 많이 쉽게 하지 않았을까 싶다. 수식파싱할 때 어려운 게 연산의 순서를 계산하는 것인데 이 알고리즘을 이용하면 쉽게 할 수 있기 때문이다.
데이터를 가공 / 처리할 때 수식을 통하여 새로운 의미의 변수를 만드는 작업을 굉장히 많이 한다. 이런 파생변수를 만들 때 필요한 것이 수식파싱 후 값을 계산하는 모듈이다. 공부도 할 겸 Java로도 이 모듈을 만들어 놓으면 여기 저기 쓸 데가 있지 않을까 싶다.
잠깐만 시간을~
그렇다면, 날 다시 수식 파싱의 세계로 밀어 넣은 Shunting-Yard Algorithm은 뭔지 잠깐만 시간을 내 보자.
출처: Wikipedia
1에 2를 더하는 수식을 우리는 보통 "1 + 2"로 표시한다. 이와 같이, 보통 우리가 쓰는 수식은 infix notation 방식을 취하고 있다. 무슨 말인고 하니 연산자가 가운데(in) 있다는 얘기다.
Shunting-Yard Algorithm은 infix notation 형태의 수식을 Reverse Polish Notation (RPN) 형태로 바꾸는 데 사용할 수 있는 알고리즘이다. RPN은 연산자를 뒤에 위치시키는 표현법으로 "1 + 2"를 "1 2 +"와 같이 표시한다.
사람이 보기에는 "1 더하기 2"나 "1과 2를 더해"나 별 차이 없어 보이지만 컴퓨터가 보기엔 천지차이다. RPN 형태로 되어 있으면 루프 돌면서 쭉 계산해 나가면 되는데 infix 형태로는 그렇게 하기가 힘들다. 그래서 RPN 형태로 변경해야 하고 Shunting-Yard와 같은 알고리즘을 이용하는 것이다.
Shunting-Yard Algorithm은 이름에서 알 수 있듯이 차량기지(Shunting yard)에서 기차를들 정렬하기 위하여 사용하는 방식과 비슷하게 로직이 수행된다. 자세한 내용은 Wikipedia 참고.
Pseudocode
이야기를 풀어 나가기 위하여 Shunting-Yard Algorithm의 처리 과정을 정리해 보았다.
for each token in expression
{
    if token == 'number'
        token --> output queue

    if token == 'function'
        token --> stack

    if token == 'function argument separator' (e.g. a comma)
    {
        while( stack not empty and top of stack != '(' )
            pop stack onto output queue
        if top of stack != '('
            mismatched parentheses error.
        else
            pop '(' off stack, ignore it.
    }

    if token == 'operator'
    {
        while( stack not empty and stack top token == 'operator' )
        {
            if token precedence < stack top token or (token == left-associative and precedence <= stack top token)
                pop stack onto output queue
            else
                break
        }
        token --> stack
    }

    if token == '('
        token --> stack

    if token == ')'
    {
        while( stack not empty and stack top token != '(' )
            pop stack onto output queue
        if stack is empty
            mismatched parentheses error.
        pop '(' off stack, ignore it.

        if top of stack == 'function'
            pop stack onto output queue
    }
}

while( stack not empty )
    pop stack onto ouput queue
만들어 보자
알고리즘만 간단히 구현할 거라면 필요 없겠지만, 확장성 있는 수식파서를 만들기 위해 Psedocode를 보면서 우리가 알 수 있는 것들을 정리해 보자.
  1. 우선, 수식을 한땀 한땀 이태리 장인처럼 따서 Token을 추출할 무언가가 필요하다.
  2. 추출된 Token에 따라 처리할 내용이 달라지는 것을 보아 Token은 특정 형태를 가지고 있다.
  3. 형태는 수식에 사용되는 연산자(Operator), 숫자(Operand), 함수(Function)와 괄호, 쉼표 등이 있다.
  4. 연산자는 left-associative, right-associative의 형태를 가진다.
  5. 알고리즘을 구현하기 위하여 Stack 한개와 Queue 한개가 필요하며,
  6. 수행완료 후 결과는 Queue가 남는다.
  7. 수식을 계산을 위한 것이므로 이 Queue에는 계산 순서에 맞게 Token이 위치할 것이다.
  8. Queue와 Stack에는 Token이 들어 간다. 다양한 Token을 넣기 위한 인터페이스가 필요하다.
  9. 수행 중 괄호가 맞지 않는 경우 등 사용자 오류를 처리하기 윈한 Exception 클래스가 필요하다.
정리된 내용을 수용하려면 다음과 같은 것들을 만들어야 할 것 같다.
만들 애 이름 기능/역할 비고
ExprParser 수식파서 메인 클래스 -
ExprTokenizer 수식을 잘 잘라서 Token을 뽑아 줄 클래스 1번
ExprTokenType Token의 형태를 정의한 Enumeration. 연산자, 괄호, 함수, 피연산자 등을 정의. 2번, 3번
OperatorAssoc 연산자의 Associative 방법을 정의한 Enumeration 4번
ExprToken ExprTokenizer에서 Token을 이 객체로 추출 2번, 8번
OperatorToken 연산자를 처리하기 위한 토큰 3번
FunctionToken 함수를 위한 토큰 3번
ValueToken 피연산자를 대변하기 위한 토큰 3번
EtcToken 연산자, 함수, 피연산자 이외 것을 대변하기 위한 토큰. 예를 들어 괄호 같은 거~ 3번
ParserException 수식파싱 및 값 계산 중 발생할 수 있는 오류 처리 클래스 9번
5번에서 필요한 Stack과 Queue는 알고리즘 구현할 때 java.util 패키지에 있는 것을 이용하면 된다. 6번은 파싱하는 메소드에서 생성한 Queue를 반환하거나 멤버 변수로 저장하면 되고, 7번은 알고리즘의 결과이다.
조금만 더 생각해 보자.
수식 내에서 Token을 구분짓는 건 Token의 형태를 문자로 표현한 것들이다. 무슨 말인고 하니, 연산자, 숫자, 괄호, 함수 등을 표현한 수식 내 문자들이 Token을 뽑는 기준이라는 것이다. 오호라~ Token 뽑는 방법은 수식내 문자를 하나 하나 살펴보며 앞서 말한 문자와 일치하는 지 비교해 보면 될 듯 하다.
그렇다면 어떤 문자들이 있을까? 연산자를 먼저 살펴보면, 사칙연산(+, -, *, /)을 기본으로 승수연산(^), 나머지 연산(Modulus, %) 등이 있다. 물론, 비교 연산자(=, >=, <=, !=), 논리 연산자(and, or, not)도 포함되면 좋을 것이다. 숫자와 괄호는 특별히 말할 것이 없고, 함수는 정의하기에 따라 무궁무진 하기 때문에 연산자처럼 몇 개 정의해서 끝날 문제가 아니다. 뭔가 함수객체 들만 몰아 넣어 관리할 Gallery스러운 것이 필요하다. 조금 더 나가서 연산자도 함수처럼 관리하면 편할 것 같다. 실제 계산하는 메소드도 같이 포함시켜 놓고~
그래서 다음과 같은 것들을 추가로 만들면 조금 더 멋진 파서가 되지 않을까 싶다.
만들 애 이름 기능/역할
ExprTokenGallery 연산자, 함수 형태의 Token을 자신을 표현할 문자와 함께 Key & Value 형태로 관리하는 클래스.
Calculatable 연산자나 함수의 실제 계산을 위한 인터페이스. Token을 정의한 클래스에 멤버로 넣어 쉽게 값을 계산할 수 있게 함.
다음 편에 계속
휴... 사실 이 글을 쓰기 시작한 지 한달이 넘었다. 살은 더 붙여야 하지만 위에서 말한대로 구현도 다 해놨다. 그런데 왜 이리 안써지는지... 일 때문에 바쁜 것도 있었지만 이렇게 안써질 때가 가끔(사실 아주 많이) 있다. 이럴 땐 잠시 시간을 가져야 한다. 여기서 잠시 멈추고 다음에 소스와 함께 다시 해야겠다.
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 1월 29일 화요일

JDBC에서 LOAD DATA INFILE 오류발생

Workbench나 mysql console에서는 잘 되는데 JDBC로 접속해 "LOAD DATA INFILE"을 하면 안될 때가 있다. 컬럼 개수가 안 맞는다거나 형태를 바꿀 수 없다는 오류 메시지만 계속 나올 뿐이다.
이 문제는 지정한 데이터 파일 내 빈 필드값이 있을 때 간혹(?) 발생한다. 그렇니까, 다음과 같이 구분자가 연속으로 나오는 경우이다.
COL1, COL2, COL3
1,A,B
2,,C
3,D,
해결방법은 데이터 파일의 빈필드에 문자열 '\N'을 명시적으로 넣어 NULL임을 알려 주는 것이다.
COL1, COL2, COL3
1,A,B
2,\N,\NC
3,D,\N
이 방법이 여의치 않다면 "LOAD DATA INFILE" 명령을 다음과 같이 실행하여 해결할 수도 있다.
LOAD DATA INFILE 'data file path'
REPLACE INTO TABLE_NAME
FIELD TERMINATED BY ','
LINES TERMINATED BY '\n'
(@V1, @V2, @V3)
SET COL1 = NULLIF(@V1, '')
  , COL2 = NULLIF(@V2, '')
  , COL3 = NULLIF(@V3, '')
;
데이터를 넣을 테이블에는 컬럼 COL1, COL2, COL3가 있으며, 해결책의 키-포인트는 "SET"을 이용한 부분이다.
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 1월 28일 월요일

MySQL 재시작 방법

RedHat 계열의 Linux에 설치된 MySQL을 재시작하려면
# service mysqld restart
or
# /etc/init.d/mysqld restart
비슷하게 시작, 종료하려면,
# service mysqld [start | stop]
or
# /etc/init.d/mysqld [start | stop]
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

MySQL 한글이 안될 때

한글이 안 되는 경우

MySQL 사용 시 한글이 안 될 경우 세 부분을 의심해 보아야 한다.

1. MySQL 서버 characterset 설정.
2. 사용하고 있는 툴의 문자 인코딩 지원 종류.
3. 쿼리를 저장한 파일 혹은 데이터 파일의 문자 인코딩 방식.

한글을 사용하려면 문자 인코딩 방식을 euc-kr나 Unicode를 지원해야 한다. 요즘 쿼리 툴은 대부분 Unicode 중 utf-8을 지원하기 때문에 2번은 크게 문제가 되지 않는다. 물론 확인은 해 봐야 한다. 3번도 무료로 사용할 수 있는 Sublime Text 등을 이용하면 쉽게 확인할 수 있다. 문제는 1번이다.

MySQL을 설치하고 utf-8을 기본으로 제공해 주면 좋은데 그렇지가 않다. 다음은 설치 후 MySQL의 상태를 확인한 화면이다.

mysql> status
--------------
mysql  Ver 14.12 Distrib 5.0.95, for redhat-linux-gnu (x86_64) using readline 5.1
 
Connection id:          20
Current database:
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server version:         5.0.95 Source distribution
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    latin1
Conn.  characterset:    latin1
UNIX socket:            /var/lib/mysql/mysql.sock
Uptime:                 6 days 5 hours 2 min 54 sec
 
Threads: 1  Questions: 41  Slow queries: 0  Opens: 13  Flush tables: 1  Open tables: 6  Queries per second avg: 0.000
--------------

mysql> 

문자 인코딩 방식이 latin1이다. 이러니 한글이 안되지... utf-8으로 바꿔보자.

MySQL Characterset 바꾸기

문자셋을 바꾸려면 /etc/my.cnf 파일의 해당 부분에 다음 내용을 추가한다.

# vi /etc/my.cnf
 
[mysqld]
...
default-character-set=utf8
collation-server=utf8_unicode_ci
character-set-server=utf8
character-set-client-handshake=FALSE
...

[client]
default-character-set=utf8
...    

그리고 나서 재시작. 다시 상태를 확인해 보면,

mysql> status
--------------
mysql  Ver 14.12 Distrib 5.0.95, for redhat-linux-gnu (x86_64) using readline 5.1
 
Connection id:          2
Current database:
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server version:         5.0.95 Source distribution
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
UNIX socket:            /var/lib/mysql/mysql.sock
Uptime:                 22 sec
 
Threads: 1  Questions: 5  Slow queries: 0  Opens: 12  Flush tables: 1  Open tables: 6  Queries per second avg: 0.227
--------------

mysql> 

이제 한글을 사용할 수 있다.

저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 1월 19일 토요일

HDFS 셀 명령

Hadoop Homepage에 있는 문서 "File System Shell Guide"를 참고하여 작성하였음!
들어가기
HDFS(Hadoop Distributed File System)는 Linux의 i-node나 Windows의 FAT과 같은 파일을 저장하고 관리, 활용하기 위한 시스템이다. 다른 파일 시스템과 크게 다른 점은 여러 컴퓨터에 파일내용을 나누어 저장한다는 것이다. HDD에서의 i-node나 FAT의 개념을 컴퓨터로 확장해 보면 개념적으로는 HDFS와 거의 같아 보인다. 사용하기 위한 명령도 Linux 명령과 매우 흡사하다.
HDFS 명령 기본형식
HDFS 명령의 기본 형식은 다음과 같다.
[hadoop@alpha ~]$ hadoop fs -[command] [args]
 or
[hadoop@alpha ~]$ hadoop dfs -[command] [args]
"hadoop dfs"가 항상 앞에 붙고 뒤에 명령어(command)가 온다. [command] 앞에 "-" 오타가 아니다. 명령어는 항상 "-"로 시작하기 때문에 명시적으로 표시하였다.
끝에는 명령을 수행하는데 필요한 인수들이 위치한다. HDFS의 모든 명령은 인수(args)로 파일 혹은 파일경로를 가지며, 이들은 URI 형태로 정의된다.
URI
URI(Uniform Resource Identifier)는 말그대로 동일한 리소스를 유일하게 정의하고 식별하기 위한 규약으로 다음과 같은 형태를 가진다.
scheme://authority/path
크게 scheme, authority, path 세 부분으로 구성되며, HDFS 명령의 인수로 사용 시, 각 부분은 HDFS와 Local File System을 나타내기 위하여 다음과 같이 정의된다.
URI HDFS Local FS
scheme 문자열 "hdfs" 문자열 "file"
authority hostname:post (지정안함)
path Linux 파일경로와 동일하게 지정
Exmaple hdfs://alpha.centos:8020/data/README.txt file:///opt/hadoop-1.0.4/README.txt
HDFS의 authority에서 기본 포트를 이용하고 있다면 port는 지정할 필요가 없다. scheme와 authority은 생략가능하며, 생략했을 경우 명령을 수행하는 머신의 설정을 참고한다.
HDFS 셀 명령
이젠 HDFS의 셀 명령에 대하여 본격적으로 알아 보자.
명령어 설명
cat hadoop dfs -cat URI [URI ...]
지정한 파일(들)을 화면에 표시한다.
chgrp hadoop dfs -chgrp [-R] GROUP URI [URI ...]
지정한 파일의 group을 변경한다. 단, 파일의 소유자이거나 슈퍼유저만 변경 가능하다. "-R" 옵션을 붙이면 하위 디렉토리들도 재귀적으로 그룹이 변경된다.
chmod hadoop dfs -chmod [-R] MODE URI [URI ...]
지정한 파일의 권한(Permission)을 변경한다. 파일의 소유자이거나 슈퍼유저만 변경 가능할 수 있으며, "-R" 옵션을 붙이면 하위 디렉토리들도 재귀적으로 권한이 변경된다. MODE는 Linux 명령의 chmod와 똑 같이 지정하면 된다.
chown hadoop dfs -chown [-R] [OWNER][:[GROUP]] URI [URI ...]
슈퍼유저만 사용할 수 있는 파일의 소유자를 변경하는 명령이다. "-R" 옵션을 붙이면 하위 디렉토리들도 재귀적으로 소유자가 변경된다.
copyFromLocal hadoop dfs -copyFromLocal <localsrc> URI
지정한 로컬 파일을 HDFS로 복사한다.
copyToLocal hadoop dfs -copyToLocal [-ignorecrc] [-crc] URI <localdst>
지정한 HDFS 내 파일을 로컬 파일로 복사한다. "-ignorecrc" 옵션을 지정하면 CRC 체크를 실패하더라도 복사해 온다. CRC도 복사해 오고 싶으면 "-crc" 옵션을 사용한다.
count hadoop dfs -count [-q] <path> [path ...]
지정한 디렉토리(들) 내에 있는 디렉토리 개수, 파일 개수, 파일 크기, 디렉토리 명을 순서대로 표시한다. "-q" 옵션은 할당량(quota)에 대한 정보를 추가로 표시한다. QUOTA, REMAINING_QUATA, SPACE_QUOTA, REMAINING_SPACE_QUOTA 값들이 앞쪽에 붙여 나온다.
(왜 이름이 count인지 모르겠다.)
cp hadoop dfs -cp URI [URI ...] <dest>
지정한 파일(들)을 복사한다. 여러 파일이 지정된다면 <dest>는 디렉토리이어야 한다.
du hadoop dfs -du [-s] [-h] URI [URI ...]
지정한 디렉토리에 있는 파일과 디렉토리의 크기를 표시한다. 디렉토리의 경우 디렉토리 내 포함된 파일 및 서브 디렉토리의 크기를 모두 합한 값이 표시된다.
-s 옵션은 표시된 파일의 총크기를 표시한다.
-h 옵션은 사용자 친화적 형태로 크기를 표시한다. (67108864를 64.0m 표시)
※ 1.0.4에서 -s와 -h는 지원하지 않는 듯~
dus hadoop dfs -dus <arg>
지정한 디렉토리 내 파일, 서브 디렉토리의 크기를 모두 더한 값이 표시된다.
expunge hadoop dfs -expunge
휴지통을 비운다. 휴지통에 대한 자세한 내용은 HDFS Architecture Guide 참고.
get hadoop dfs -get [-ignorecrc] [-crc] <src> <localdst>
HDFS 내 지정된 파일을 로컬 파일로 복사한다. copyToLocal 명령과 유사하다.
-ignorecrc 옵션은 CRC 체크를 실패하더라도 파일을 복사해 온다.
파일과 CRC를 복사하려면 "-crc" 옵션을 이용한다.
getmerge hadoop dfs -getmerge <src> <localdst> [addnl]
지정한 HDFS의 디렉토리 내 모든 파일을 붙여 localdst에 저장한다. addnl을 지정하면 저장 시 파일 사이에 addnl에 지정한 문자(열)가 추가로 붙는다.
ls hadoop dfs -ls URI [URI ...]
지정한 HDFS 디렉토리(들)에 포함된 파일, 디렉토리 목록을 표시한다. Linux의 "ls -l" 명령과 비슷하다.
lsr hadoop dfs -lsr URI [URI ...]
ls의 recusive 버전. Linux의 "ls -Rl" 명령과 비슷하다.
mkdir hadoop dfs -mkdir URI [URI ...]
지정한 경로대로 디렉토리를 생성한다. "/data/text" 디렉토리를 생성하려고 할 때 "/data" 디렉토리가 없다면 생성 후 "/data/text"를 생성한다. Linux의 "mkdir -p ..."와 비슷하게 동작한다.
moveFromLocal hadoop dfs -moveFromLocal <localsrc> <dst>
복사 후 파일을 삭제하는 것을 제외하면 put 명령과 동일하다.
put hadoop dfs -put <localsrc> ... <dst>
로컬 PC 내 지정된 파일(들)을 HDFS로 복사한다. 여러 파일을 지정할 경우 <dst>은 디렉토리를 저징해야 한다.
rm hadoop dfs -rm [-skipTrash] URI [URI ...]
지정한 파일(들)을 지운다. 디렉토리를 지우려면 rmr 명령을 이용, 이 명령은 파일에만 가능하다.
"-skipTrash" 옵션을 지정하면 휴지통에 넣지 않고 바로 삭제한다.
rmr hadoop dfs -rmr [-skipTrash] URI [URI ...]
Recursive한 rm 버전이다. 디렉토리를 지정하면 포함된 모든 내용을 지우고 지정한 디렉토리도 지운다.
"-skipTrash" 옵션을 지정하면 휴지통에 넣지 않고 바로 삭제한다.
setrep hadoop dfs -setrep Number [-R] <path>
지정한 파일의 Replication Factor(복제 개수)를 변경한다.
"-R" 옵션을 지정하면 recursive하게 동작한다.
stat hadoop dfs -stat URI [URI ...]
지정한 디렉토리의 통계정보를 표시한다. 그런데 실제로 해보면 뭔가 좀 이상하다.
tail hadoop dfs -tail [-f] URI
지정한 파일의 마지막 1KB를 화면에 표시한다. "-f" 옵션은 Linux의 "tail -f ..."와 같다.
test hadoop dfs -test -[ezd] URI
"-e" 옵션: 지정한 파일이 존재한다면 0을 반환.
"-z" 옵션: 파일 크기가 0이라면 0을 반환.
"-d" 옵션: 지정한 파일이 디렉토리라면 0을 반환.
text hadoop dfs -text <src>
지정한 파일을 텍스트 형태로 화면에 표시한다. zip과 텍스트 형태만 사용할 수 있다.
touchz hadoop dfs -touchz URI [URI ...]
크기가 0인 파일을 생성한다.
명령 실행 샘플
말로만 하면 잘 안 와 닿는다. 직접 해 보는 것이 최고다. 다음은 Hadoop을 "/opt/hadoop-1.0.4"에 설치 후 셀 명령을 실행해 본 결과이다.
[hadoop@alpha hadoop-1.0.4]$ ls -l
합계 7524
-rw-rw-r--.  1 hadoop hadoop  446999 2012-10-03 14:17 CHANGES.txt
-rw-rw-r--.  1 hadoop hadoop   13366 2012-10-03 14:17 LICENSE.txt
-rw-rw-r--.  1 hadoop hadoop     101 2012-10-03 14:17 NOTICE.txt
-rw-rw-r--.  1 hadoop hadoop    1366 2012-10-03 14:17 README.txt
. . . (중략) . . .
drwxr-xr-x.  7 hadoop hadoop    4096 2013-01-11 21:47 docs
-rw-rw-r--.  1 hadoop hadoop    6840 2012-10-03 14:17 hadoop-ant-1.0.4.jar
-rw-rw-r--.  1 hadoop hadoop     410 2012-10-03 14:17 hadoop-client-1.0.4.jar
-rw-rw-r--.  1 hadoop hadoop 3928530 2012-10-03 14:17 hadoop-core-1.0.4.jar
-rw-rw-r--.  1 hadoop hadoop  142452 2012-10-03 14:17 hadoop-examples-1.0.4.jar
-rw-rw-r--.  1 hadoop hadoop     413 2012-10-03 14:17 hadoop-minicluster-1.0.4.jar
-rw-rw-r--.  1 hadoop hadoop 2656646 2012-10-03 14:17 hadoop-test-1.0.4.jar
-rw-rw-r--.  1 hadoop hadoop  287807 2012-10-03 14:17 hadoop-tools-1.0.4.jar
drwxr-xr-x.  2 hadoop hadoop    4096 2013-01-11 21:47 ivy
. . . (중략) . . .
drwxr-xr-x.  9 hadoop hadoop    4096 2012-10-03 14:17 webapps
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -mkdir /data/text
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -copyFromLocal *.txt /data/text
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -ls /data/text
Found 4 items
-rw-r--r--   3 hadoop supergroup     446999 2013-01-25 22:41 /data/text/CHANGES.txt
-rw-r--r--   3 hadoop supergroup      13366 2013-01-25 22:41 /data/text/LICENSE.txt
-rw-r--r--   3 hadoop supergroup        101 2013-01-25 22:41 /data/text/NOTICE.txt
-rw-r--r--   3 hadoop supergroup       1366 2013-01-25 22:41 /data/text/README.txt
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -mkdir /data/bin
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -copyFromLocal *.jar /data/bin
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -ls /data/bin
Found 7 items
-rw-r--r--   3 hadoop supergroup       6840 2013-01-25 23:21 /data/bin/hadoop-ant-1.0.4.jar
-rw-r--r--   3 hadoop supergroup        410 2013-01-25 23:21 /data/bin/hadoop-client-1.0.4.jar
-rw-r--r--   3 hadoop supergroup    3928530 2013-01-25 23:21 /data/bin/hadoop-core-1.0.4.jar
-rw-r--r--   3 hadoop supergroup     142452 2013-01-25 23:21 /data/bin/hadoop-examples-1.0.4.jar
-rw-r--r--   3 hadoop supergroup        413 2013-01-25 23:21 /data/bin/hadoop-minicluster-1.0.4.jar
-rw-r--r--   3 hadoop supergroup    2656646 2013-01-25 23:21 /data/bin/hadoop-test-1.0.4.jar
-rw-r--r--   3 hadoop supergroup     287807 2013-01-25 23:21 /data/bin/hadoop-tools-1.0.4.jar
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -count /data/text
           1            4             461832 hdfs://alpha.centos:8020/data/text
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -count /data/bin
           1            7            7023098 hdfs://alpha.centos:8020/data/bin
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -count /data
           3           11            7484930 hdfs://alpha.centos:8020/data
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -count -q /data
        none             inf            none             inf            3           11            7484930 hdfs://alpha.centos:8020/data
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -cat /data/text/NOTICE.txt
This product includes software developed by The Apache Software
Foundation (http://www.apache.org/).
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -chmod 666 /data/text/NOTICE.txt
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -ls /data/text
Found 4 items
-rw-r--r--   3 hadoop supergroup     446999 2013-01-25 22:41 /data/text/CHANGES.txt
-rw-r--r--   3 hadoop supergroup      13366 2013-01-25 22:41 /data/text/LICENSE.txt
-rw-rw-rw-   3 hadoop supergroup        101 2013-01-25 22:41 /data/text/NOTICE.txt
-rw-r--r--   3 hadoop supergroup       1366 2013-01-25 22:41 /data/text/README.txt
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -du /data
7023098     hdfs://alpha.centos:8020/data/bin
461832      hdfs://alpha.centos:8020/data/text
[hadoop@alpha hadoop-1.0.4]$ hadoop dfs -dus /data
hdfs://alpha.centos:8020/data   7484930
[hadoop@alpha hadoop-1.0.4]$ mkdir test
[hadoop@alpha hadoop-1.0.4]$ cd test
[hadoop@alpha test]$ hadoop dfs -getmerge /data/text/ ./MERGED.txt "\n\n\n"
13/01/27 00:41:32 INFO util.NativeCodeLoader: Loaded the native-hadoop library
[hadoop@alpha test]$ ls -l
합계 456
-rwxrwxrwx. 1 hadoop hadoop 461832 2013-01-27 00:41 MERGED.txt
[hadoop@alpha test]$ 
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 1월 17일 목요일

MySQL / LOAD DATA INFILE 실행 시 ERROR 13

구분자로 구분되어 있는 텍스트 파일에서 데이터를 빠르게 올리기 위하여 LOAD DATA INFILE 구문을 사용한다. 예전에 Sybase ASIQ에서도 비슷한 걸 사용한 적이 있는데 INSERT 문을 건건이 사용하는 것보다 비교가 안 될 만큼 빨랐다. MySQL도 마찬가지다. 그런데 다음과 같은 오류가 날 때가 있다.
ERROR 13 (HY0000): Can't get stat of '/home/~' (Errcode: 13)
파일('/home/~')의 stat(상태? 통계? 뭘까?)을 얻을(get) 수 없다니... 이 메시지만 봐서는 무슨 오류인지 알 수가 없다.
알고 보니, 지정한 파일을 읽을 수 없는 경우에 나타나는 오류 메시지이다. 그리고 읽을 수 없는 경우는 다음과 같은 것들이 있다.
  • Others에 읽기 권한이 없는 경우.
  • Others가 파일경로 내의 디렉토리에 접근할 수 없는 경우.
  • 지정한 파일이 MySQL 서버 내에 없는 경우.
  • "LOAD DATA INFILE ~" 구문을 실행 권한이 없는 경우.
위와 같은 문제들을 해결하면 아주 잘~ 된다.
마지막 문제는 mysql 쿼리 환경에서
mysql> show variables like 'local%';
문장을 실행하여 나오는 값 중 local_infile이 ON이어야 한다. 만약, ON이 아니라면 mysql을 실행할 때 다음과 같이 "-local-infile=1"옵션을 주면 된다.
$ mysql -h 'host' -u 'userName' -p -local-infile=1
끝!
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 1월 11일 금요일

Eclipse에서 Subversion Commit 문제

Java로 작업할 일이 있어 Eclipse(JUNO)에 Subversion 플러그-인(어떤 건지는 잘 모르겠음)을 설치하여 사용하고 있다. 오늘 야심차게 작업한 소스를 Commit하려고 했는데 아무런 메시지도 없이 계속 사용자 인증화면만 나타나는 것이다. 내 계정 암호가 틀려서 그런가 해서 다른 사람 계정으로 인증하려고 해도 안되고 조용히 인증화면만 보여 줄 뿐이다.

왜 그런지 이것 저것 해 보다가 안 사실! 내가 사용하고 있는 Subversion 플러그-인은 다음과 같은 현상이 있다는 것이다.

  • 사용자 계정이 틀리거나 암호가 틀려 인증이 안될 경우 조용히 인증화면만 띄운다.
  • 한글로된 계정을 제대로 인식하지 못한다.

뭔가 위 문제를 해결할 옵션 같은 것이 있을텐데 잘 모르겠다. 어떻게 할까 하다가 Tortoise SVN을 이용하여 Commit하였다. Explorer에서 해야 한다는 불편함은 있지만 작업을 빨리 해야 하는 상황이니 어쩔 수 없지. Tortoise SVN은 한글 계정도 잘 처리한다.

플러그-인 이름이 뭔지 알아 내야 하는데... 일하다 보면 일 외에 다른 문제로 시간을 잡아 먹는 경우가 참 많다.

저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No

2013년 1월 8일 화요일

피싱 메시지 1588-9999

오늘 아침 회사가는 셔틀버스에서 막 졸 때쯤 울린 핸드폰. 아침부터 누가 문자를 보낸거지 하며 확인. 국민은행에서 정보유출로 문제가 있다는 메시지다.
그런데 메시지에 문제가 있다.
  1. 내가 아는 국민은행 URL과 다르다.
  2. 은행이 자발적으로 정보유출 문제를 알려 줄 리가 없다.
  3. 정보가 유출됐으니 보안승급을 하란다. 문맥이 좀 이상하다.
내가 내린 결론은 낚시질이구나~
회사에 와서 검색해 보니 많은 분들이 받은 메시지인듯.
뭐든지간에 개인정보를 묻는건 꼭 의심해 봅시다.
저작자: Yes, 상업적 이용: No, 컨텐츠 변경: No