git 명령어 정리

참고 url : https://rogerdudler.github.io/git-guide/index.ko.html

// 현재 디릭토리를 빈 local repo 만들고 github에 연결하기
git init
git remote add origin https://github.com/GEOSERVICE/test.git

// github로부터 local repo 만들기
git clone https://github.com/GEOSERVIC/test.git

// 추가, 삭제, 변경된 파일에 대한 반영 및 커밋
git add -A // WORKING TREE -> INDEX TREE
git commit -m “add all” // INDEX TREE -> HEAD TREE

// file1.txt의 추가 및 커밋
git add file1.txt
git commit -m “add file1.txt”

// file1의 삭제 및 커밋
git rm file1.txt
git commit -m “remove file1.txt”

// commit하기전의 지운 파일
git ls-files –deleted

// commit하기전의 지운 파일을 복원
git checkout — plugin/density.js

// commit하기전의 지운 전체 파일 복원
git checkout — *

// github로 반영
git push -u origin master // master는 가지의 이름임, master는 최초의 기본 가지임

// 원격 repo의 변경 사항을 local repo에 반영하기
git pull

// 내가 작업한 것 버리고 원격 repo에 맞추기
git fetch origin
git reset –hard origin/master

[Java] 고유값 만들기

고유한 값으로 사용할 만한 것은 UUID이다. UUID는 그 의미가 가지는 것 그대로 고유한 값이다. 그러나 저장소의 공간은 128 bits이며 문자열로 표현될 때 550e8400-e29b-41d4-a716-446655440000 처럼 그 길이가 상당히 길다. 물론 UUID는 이 기종의 하드웨어나, 시간 또는 공간적으로 다른 환경 모두에서 고유한 값을 제공해 주는 장점이 있다. 그런데, 만약 하나의 서버 내부에서 고유한 값이 필요하다면 어떨까? 한계는 있겠으나, 필자에게는 적합한 방법 중에 하나는 서버 측의 시간을 활용하는 것이다. Java에서 서버의 시간을 얻는 함수는 System.currentTimeMillis, 서버가 기동 된 후의 경과된 시간은 System.nanoTime를 통해 얻을 수 있다. 그 결과는 long형이므로 64 Bits이며, 문자열로 표현하면 대부분 21220887433392와 같이 14개면 표현이 가능하다. 그러나 나에게 고유값이 필요한 목적은 저장소에 저장할 필요는 없고 문자열로 표현했을때, 최대한 짧아야 한다는 것이다. 그런데 이 long 타입을 문자열로 표현하면 14개의 자리를 차지하는 것은 너무 길다. 짧게 표현하는 방법은 없을까? 이 long 값은 10진수이다. 이를 좀 더 높은 진수로 표현한다면 그 길이는 줄어들 것이다. 16진수, 32진수, 64진수, 128진수 등이 가능한데.. 표현 가능한 문자여야 하므로 64진수가 최적이다.

이러한 상황에서 아래의 코드 중 longToBase64라는 함수는 long 타입의 값을 받아 64진수로 진수 변환 방식을 통해 문자열로 변환해 주는 함수이다.

public static String longToBase64(long v) {
    final char[] digits = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 
        'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 
        'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
        'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 
        'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 
        'Y', 'Z', '#', '$'
    };

    int shift = 6;
    char[] buf = new char[64];
    int charPos = 64;
    int radix = 1 << shift;
    long mask = radix - 1;
    long number = v;

    do {
        buf[--charPos] = digits[(int) (number & mask)];
        number >>>= shift;
    } while (number != 0);

    return new String(buf, charPos, (64 - charPos));
}

아래의 코드는 위의 함수를 테스트한 것으로, 1000개의 고유한 값을 얻는 기능을 수행한다.

for(int i=0; i<1000; i++) {
    long time =  System.nanoTime();
    System.out.println(time + " -> " + longToBase64(time));
}

위의 코드의 결과는 아래와 같은데, 실행하는 매 순간마다 다르게 표시될 것이다.

...

21767297606161 -> 4YMo2G8h
21767297618803 -> 4YMo2JdP
21767297627494 -> 4YMo2LlC
21767297634210 -> 4YMo2M#y
21767297647642 -> 4YMo2Qgq
21767297654358 -> 4YMo2RVm
21767297661469 -> 4YMo2TEt
21767297668185 -> 4YMo2Vhp
21767297676877 -> 4YMo2Xpd
21767297683593 -> 4YMo2Z29
21767297690704 -> 4YMo2#Ng
21767297697420 -> 4YMo30qc
21767297704531 -> 4YMo329j
21767297718753 -> 4YMo35Dx
21767297727445 -> 4YMo37Ll
21767297741272 -> 4YMo3b7o
21767297748383 -> 4YMo3cSv
21767297755099 -> 4YMo3evr
21767297762210 -> 4YMo3gey
21767297776037 -> 4YMo3jCB
21767297783148 -> 4YMo3llI

...

[Java] Thumbnail Image 만드는 코드

코드 정리하다가 블로그에 정리되지 않은 코드가 있어 정리해 봅니다. 일반적인 이미지를 작은 이미지, 즉 썸네일 이미지로 만들어 주는 코드입니다. 이미지의 종횡비 크기를 유지하며 썸네일 이미지를 만들어 줍니다. 썸네일 이미지 만드는 코드를 실제 프로젝트에 붙이기 위해 만들다 보니, 부가적인 내용이 덧붙여 있으니 참고하시기 바랍니다.

사용은 아래처럼 하면 됩니다. 즉, d:/a.jpg 파일을 썸네일 이미지를 만드는데, 가로 또는 세로 크기 중 하나를 256을 만듭니다.

reateThumbnail("d:/a.jpg", 256);

아래는 d:/a.jpg의 실제 이미지입니다. 이미지 크기는 920×600입니다.

위의 이미지가 위의 코드를 통해 아래와 같이 d:/a.thumbnail.jpg 파일로 만들어지고 크기는 256×166이 됩니다. 원본 이미지의 가로 크기가 세로보다 길다보니 가로를 256으로 만들어지게 되는 것입니다.

createThumbnail 함수는 아래와 같습니다.

private static boolean createThumbnail(String fileName, int maxSize) {
    try {
        int thumbnail_width = maxSize;
        int thumbnail_height = maxSize;

        File origin_file_name = new File(fileName);
		    
        String ext = getFileExt(fileName);
        String newFileName = fileName.replace("." + ext, ".thumbnail." + ext);
		    
        BufferedImage buffer_original_image = ImageIO.read(origin_file_name);
		    
        double imgWidth = buffer_original_image.getWidth();
        double imgHeight = buffer_original_image.getHeight();
		    
        if(imgWidth < imgHeight) {
            thumbnail_width = (int)((imgWidth / imgHeight) * maxSize);
        } else {
            thumbnail_height = (int)((imgHeight / imgWidth) * maxSize);
        }
		    
        int imgType = (buffer_original_image.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        BufferedImage buffer_thumbnail_image = new BufferedImage(thumbnail_width, thumbnail_height, imgType);
        Graphics2D graphic = buffer_thumbnail_image.createGraphics();
		    
        graphic.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        graphic.drawImage(buffer_original_image, 0, 0, thumbnail_width, thumbnail_height, null);
		    
        if(ext.equalsIgnoreCase("jpg")) {
            writeJpeg(buffer_thumbnail_image, newFileName, 1.0f);
        } else {
            File thumb_file_name = new File(newFileName);
            ImageIO.write(buffer_thumbnail_image, ext.toLowerCase(), thumb_file_name);
        }
		    
        graphic.dispose();
    } catch (Exception e) {
        e.printStackTrace(System.err);
        return false;
    }
		
    return true;
}

위의 함수는 2개의 내부 함수를 호출하고 있는데, 해당되는 함수들은 아래와 같습니다.

private static String getFileExt(String fileName) { // "abc.txt" -> "txt", not ".txt"
    int i = fileName.lastIndexOf('.');
    int p = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));

    if (i > p) {
        return fileName.substring(i+1);
    }

    return null;
}
	 
private static void writeJpeg(BufferedImage image, String destFile, float quality) throws IOException {
    ImageWriter writer = null;
    FileImageOutputStream output = null;
		  
    try {
        writer = ImageIO.getImageWritersByFormatName("jpeg").next();
	
        ImageWriteParam param = writer.getDefaultWriteParam();
		    
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionQuality(quality);
		    
        output = new FileImageOutputStream(new File(destFile));
        writer.setOutput(output);
		    
        IIOImage iioImage = new IIOImage(image, null, null);
        writer.write(null, iioImage, param);
    } catch (IOException ex) {
        throw ex;
    } finally {
        if (writer != null) {
            writer.dispose();
        }
		    
        if (output != null) {
            output.close();
        }
    }
}

단위 테스트로 만들어 놓은 코드인지라 최적화가 되어 있지 않으니, 살펴보시고 최적화 해 사용하시기 바랍니다.

실시간 미세먼지 측정

아두이노와 미세먼지 센서를 활용하여 사무실에서 측정한 실제 구동 화면입니다. 캡쳐 받은 이미지 아닙니다~ ^^ 측정 결과를 인터넷을 통해 서버에 전송하여 위처럼 웹에서 살펴볼 수 있도록 하였습니다. 사무실의 미세먼지가 제법 많군요. 오늘 실외 미세먼지가 나쁘지 않은 날인데도, 이렇다면 문제인데.. 공기청정기를 설치해야겠습니다.

옛날에 짬짬이 만들어 본 것을 사무실에 가져와 설치를 한 것인데요. 흔히 말하는 IoT입니다. 1um 크기인 극초미세먼지까지 측정해 줍니다. 흔히 극초미세전지가 건강에 훨씬 위험하지만 우리나라에서는 이 극초미세먼지의 측정값을 제공하지 않습니다.

아래는 실제 장비에 대한 사진입니다.

WebSocket과 Web에서의 Javascript를 통한 바이너리 데이터 통신

크아! 제목 한번 요상합니다. JavaScript가 클라이언트 뿐만 아니라 Java나 서버단(Node.js 등)에서도 활용되다 보니… 여튼, WebSoket과 웹에서의 Javascript를 통한 통신의 예로 Binary 데이터 타입을 주고 받는 코드를 정리합니다. 이 글은 텍스트 타입의 데이터를 주고 받는 [jetty를 이용한 WebSocket 서버 구현하기]라는 글을 기반으로 작성된 글입니다.

WebSocket을 위한 서버는 jetty를 활용하였습니다. 먼저 클라이언트가 서버 단에 바이너리 데이터가를 전달하는 코드는 다음과 같습니다.

var buffer = new ArrayBuffer(8);
var dataview = new DataView(buffer);

dataview.setInt32(0, 9438);
dataview.setFloat32(4, 3224.3224);
            
webSocket.send(buffer);

8 Bytes 크기의 데이터 덩어리를 만들고, 이 덩어리 안에 Int와 Float 타입에 대한 값으로 각각 9438과 3224.3224를 넣어서 웹소켓으로 전송하는 코드입니다. 이렇게 전송하면 서버가 받아야 하는데, 아래의 코드는 서버 측의 코드입니다.

@Override
public void onWebSocketBinary(byte[] payload, int offset, int len)
{
    if ((outbound != null) && (outbound.isOpen()))
    {
        InputStream is = new ByteArrayInputStream(payload);
        DataInputStream dis = new DataInputStream(is);
    		
        try {
            int v1 = dis.readInt();
            float v2 = dis.readFloat();
            double result = (double)v1 * (double)v2;
    			
            ByteBuffer bb = ByteBuffer.allocate(8);
            bb.putDouble(0, result);
            outbound.getRemote().sendBytes(bb);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

10번과 11번에서 클라이언트가 보낸 int와 float 값을 읽고, 엄청나게 복잡한 연산인 곱의 결과 값을 16번 코드에서 클라이언트로 전달하고 있는 코드입니다. 이제 아래는 다시 클라이언트가 서버가 계산한 소중한 결과를 받는 코드입니다.

webSocket.onmessage = function (message) {
    var blob = message.data;
    var fileReader = new FileReader();

    fileReader.onload = function (event) {
        var arrayBuffer = event.target.result;
        var dataview = new DataView(arrayBuffer);
        var answer = dataview.getFloat64(0);

        alert("Server> : " + answer);
    };

    fileReader.readAsArrayBuffer(blob);
};

서버로부터 받는 바이너리 데이터는 blob 형식으로 받습니다. 이 데이터는 FileReader를 통해 읽어 해석할 수 있습니다. 이상으로 웹소켓에서 바이너리 데이터를 주고 받는 코드에 대한 글을 정리한 글이였습니다.