클라우드 아이디 생성

우선 사용하기 위해 클라우드 아이디를 생성해야 한다.

 

기존 네이버 아이디와 연동이 되기때문에, 해당 가입 과정은 넘어가겠다.

 

 

아래의 네이버로 간편 로그인을 사용하면 된다.

 

이제 로그인시 결제수단을 등록하면 3개월동안 사용할 수 있는 10만원치의 크레딧과 Micro Server 1년 무료 사용가능한 상태가 된다.

 

 

결제수단을 등록한 후 우측 상단의 콘솔을 누른다.

 

누르면 다음과 같은 페이지가 나오며, 사용중인 서비스의 사용량과 여러가지를 볼 수 있는 대시보드를 볼 수 있다.

 

 

좌측 바의 Service 카테고리를 클릭하면 수 많은 지원 서비스에 대해 나오며, 우리는 Compute 카테고리에 있는 Server 서비스를 클릭한다.

 

All 글씨 아래의 Server를 클릭하면 아래 화면이 나오게 된다.

 

서버가 하나도 존재하지 않는다면 서버 상품에 대한 표가 나오며, 왼쪽에 있는 서버 생성 버튼을 눌러주도록하자.

 

그러면 해당 화면이 나타나며, 위의 검정색 부분을 보면 서버 운영체제 베이스이미지를 고르게 되어있는데, 나는 Ubuntu 기반 Linux 서버를 운영할 것이며, 현재 일자 기준으로 문의한 결과 micro 서버는 CenOS와 Ubuntu 20, Rocky운영체제에서만 만들 수 있게 되어있다. 따라서 "최신 서버 이미지"가 아닌 "NCP 서버 이미지"로 들어가보자

 

 

넘어오면 여러가지 운영체제가 존재하는데, 필자는 ubuntu 20.04-base 이미지를 선택했다.

 

고르고 난 후 VPC와 서브넷을 설정하면 서버 스펙을 결정할 수 있다.

그러면 위 사진과 같이 나타나는데 해당 Micro 서버를 택하고 나머지 옵션등을 세팅하면 끝이다.

 

 

모두 세팅하면 두번째인 스토리지 설정단계로 넘어간다.

 

별건 없고 그냥 스토리지 용량만 정의해주면 된다. 필자는 기본인 10GB를 그대로 사용했다.

 

인증키 파일 생성 과정이다. 이름에 네이밍 룰은 없으며 관리자가 찾기 편한 이름으로 하는 것이 좋다. 추후에 서버 접속 패스워드 확인 등 사용할 곳이 많으므로 이름을 잘 정의하도록 하자.

 

 

네트워크 접근 설정

네트워크 접근 설정은 서버에 접속할 수 있는 인바운드, 아웃바운드에 대한 룰을 정의해놓고 어떤 룰을 적용할지 선택하면 된다. 난 그냥 기본옵션으로 했으며, 모든 IP의 아웃바운드를 받게끔해놨다.

 

그러면 최종 설정에 대한 확인절차를 가지며, 본인이 설명한 것이 맞다면 확인버튼을 누르도록 하자.

 

이후 다시 Service -> Compute -> Server에 보면 생성한 서버의 상태가 "생성 중" 으로 나타나 있는 것을 볼 수 있으며, 해당 상태가 "운영 중"으로 변경되면 그때부터 서버가 작동하고 있다고 판단할 수 있다.

 

그러면 서버에 접속하기 위해 생성되는 동안 Putty를 설치하자. 설치하는 과정은 구글에 Putty를 검색하고 다운로드만 받으면 되기때문에 생략하겠다.

 

설치 후 위 사진처럼 서버가 운영중이라면, 우선 해당 서버의 정보를 확인하기 위해 서버 row 클릭하자. 그러면 아래와 같이 공인 IP등 정보가 나타난다.

 

이후 Putty를 실행하면 아래 사진이 나오는데, 위 사진의 공인 IP와 기본으로 열리는 FTP 포트인 22번 포트를 입력하자.

그러면 아래와 같은 화면으로 연결된다.

로그인 화면

해당 OS에 로그인을 해야하는데, 이것을 확인하기 위해 서버생성시 설정했던 인증키 파일이 필요하다.

 

우선 서버 목록 화면 상단의 "서버 관리 및 설정"을 클릭하고, "관리자 비밀번호 확인"을 눌러준다.

 

 

이후 아래 화면에서 생성됬던 인증키 파일을 올리고 확인을 누른다.

그러면 아래와 같은 화면에 ID와 비밀번호가 나올것이다.

 

확인 후, 첫 화면에 관리자 이름을 입력한다.

 

패스워드는 Git 처럼 입력해도 아무것도 보이지 않으니 당황하지말고 천천히 입력하자.

 

제대로 입력되면 위와같이 성공적으로 접속되었다고 나타나며, 해당 서버에서 작업을 진행하면 되겠다.

 

서버 띄우는데도 한참동안 걸렸다. 우선 우분투 22에서도 만들었다는 글을 보고 22에서 뻘짓을 3시간정도 하다가 문의에 대한 답변을 보고 그제야 알아챘다. 하지만 앞으로 도커로 이미지를 배포하고 쿠버네티스를 또 배워서 해당 클라우드에 적용하게 되는것에 대한 압박감이 있긴하다. 첫걸음이 어렵다고 클라우드서버 생성은 했으니 클라우드 지식들을 학습하며 다들 같이 나아가길 바라고 있다.

 

 

Stream이란?

Stream은 원문 그대로 해석시 “흐르다” 라는 뜻을 가지고 있습니다. 기술의 개발로 인해 서비스가 고도화되고 복잡해지면서 데이터 처리에 대해 효율적인 방안이 필요했고, 그러다 나온 것이 이 Stream입니다.

 

Stream은 Oracle 공식문서에서 “연속된 데이터를 효율적으로 처리하기 위한 인터페이스” 라고 나와있습니다.이제부터 그 이유를 알아보겠습니다.

 

우선 코드로 어떤 점이 차이가 나는지 알아보겠습니다.

 

우선 컬렉션을 하나 만들어 두겠습니다.

다음 값을 출력하면 해당 리스트가 생겼습니다.

여기서 저는 “구” 라는 성을 가진 데이터만 뽑아서 리스트를 가져오고 싶다는 요청을 받았다고 가정하고, 우선 1번째 글자가 구씨인 데이터만 골라서 다른 리스트를 가지도록 해보겠습니다.

public class Main {
    public static void main(String[] args) {
        List<String> humenName = new ArrayList<>();
        humenName.add("구길동");
        humenName.add("홍길동");
        humenName.add("김길동");

        System.out.println(humenName);
        List<String> kooList = new ArrayList<>();

        for (int i = 0; i < humenName.size(); i++) {
            String name = humenName.get(i);
            String nameSlice = name.substring(0,1);
            if (nameSlice.equals("구")) {
                kooList.add(name);
            }
        }

        System.out.println(kooList);
    }
}

이렇게 간단한 주제임에도 반복문이 5줄이나 나오며, 쓸모없이 하나의 변수가 더 나오게 되어버렸습니다.

그렇다면 이것을 스트림과 람다식 문법으로 사용하면 어떻게 변할까요?

List<String> kooList = humenName.stream()
                .filter(item -> item.substring(0,1).equals("구"))
                .collect(Collectors.toList());

보시면 머 쓸데없는 코드를 제외하더라도 코드의 순수한 양은 물론이고, 가독성도 좋아진 것을 한눈에 보실 수 있습니다.

이렇게 코드를 먼저 비교해봤는데요, 코드의 관리나 개발측면에서도 스트림을 사용하면 좋을 거같아 현재 신규 프로젝트에도 사용해보고 있습니다.

Stream의 특징

이제 코드로 사용해야하는 간단한 이유를 먼저 알아봤으니 Stream의 특징과 그에 따라오는 장점을 알아볼 차례입니다.

  1. 파이프 라이닝
  • 위의 코드를 보시면 데이터를 스트림화 하고 필터링 후에 콜렉션화 하는 과정이 이어져 있으며, 그에 따라 자연스럽게 파이프라인이 잡혀있는 것을 볼 수 있습니다.
  • 이렇게 스트림 연산은 연산끼리 연결해서 스트림 자신을 반환하는데, 여기에서 laziness(게으름)과 short-circuting(쇼트서킷)이라는 장점을 얻을 수 있습니다.
    • laziness(지연) : Stream은 코드가 읽히는 즉시 연산을 하지 않고 최소한의 준비작업만 실시하다가, 해당 결과값에 대한 호출이 있을 때 계산한다.
    • loop Fusion(반복병합) : 연속적으로 체이닝된 복수의 연산을 하나로 병합시킨다.
    • short-circuting(쇼트 서킷) : 불필요하다 판단된 연산의 수행을 하지않고 중단한다.
  1. 내부 반복
  • Stream은 Collection과 반복문을 명시하지않아도 내부에서 순환시킬 수 있다.

이렇게 여러가지 장점에 대해 찾아보았습니다. 물론 Stream이 만능은 아닐 것입니다.

기본적으로 호출 시점이 확실하고 용량이 큰 BLOB데이터 같은 경우에는 비동기로 돌려서 미리 호출에 대비해야하지않나 라는 아주 얕은 생각이 내비치기도 하였으며,

파이프라인을 DB에 쿼리문 튜닝으로 1차적으로 잡아야 좀 더 효율적으로 움직일 수 있을거라는 생각도 조금은 들었습니다.

다음번에 Stream의 여러 종류중 하나에 대해 다루게 된다면 이 이론들을 다시 곱씹어 보겠습니다.

Stream이란?

Stream은 원문 그대로 해석시 “흐르다” 라는 뜻을 가지고 있습니다. 기술의 개발로 인해 서비스가 고도화되고 복잡해지면서 데이터 처리에 대해 효율적인 방안이 필요했고, 그러다 나온 것이 이 Stream입니다.

Stream은 Oracle 공식문서에서 “연속된 데이터를 효율적으로 처리하기 위한 인터페이스” 라고 나와있습니다.이제부터 그 이유를 알아보겠습니다.

우선 코드로 어떤 점이 차이가 나는지 알아보겠습니다.

우선 컬렉션을 하나 만들어 두겠습니다.

다음 값을 출력하면 해당 리스트가 생겼습니다.

여기서 저는 “구” 라는 성을 가진 데이터만 뽑아서 리스트를 가져오고 싶다는 요청을 받았다고 가정하고, 우선 1번째 글자가 구씨인 데이터만 골라서 다른 리스트를 가지도록 해보겠습니다.

public class Main {
    public static void main(String[] args) {
        List<String> humenName = new ArrayList<>();
        humenName.add("구길동");
        humenName.add("홍길동");
        humenName.add("김길동");

        System.out.println(humenName);
        List<String> kooList = new ArrayList<>();

        for (int i = 0; i < humenName.size(); i++) {
            String name = humenName.get(i);
            String nameSlice = name.substring(0,1);
            if (nameSlice.equals("구")) {
                kooList.add(name);
            }
        }

        System.out.println(kooList);
    }
}

이렇게 간단한 주제임에도 반복문이 5줄이나 나오며, 쓸모없이 하나의 변수가 더 나오게 되어버렸습니다.

그렇다면 이것을 스트림과 람다식 문법으로 사용하면 어떻게 변할까요?

List<String> kooList = humenName.stream()
                .filter(item -> item.substring(0,1).equals("구"))
                .collect(Collectors.toList());

보시면 머 쓸데없는 코드를 제외하더라도 코드의 순수한 양은 물론이고, 가독성도 좋아진 것을 한눈에 보실 수 있습니다.

이렇게 코드를 먼저 비교해봤는데요, 코드의 관리나 개발측면에서도 스트림을 사용하면 좋을 거같아 현재 신규 프로젝트에도 사용해보고 있습니다.

Stream의 특징

이제 코드로 사용해야하는 간단한 이유를 먼저 알아봤으니 Stream의 특징과 그에 따라오는 장점을 알아볼 차례입니다.

  1. 파이프 라이닝
  • 위의 코드를 보시면 데이터를 스트림화 하고 필터링 후에 콜렉션화 하는 과정이 이어져 있으며, 그에 따라 자연스럽게 파이프라인이 잡혀있는 것을 볼 수 있습니다.
  • 이렇게 스트림 연산은 연산끼리 연결해서 스트림 자신을 반환하는데, 여기에서 laziness(게으름)과 short-circuting(쇼트서킷)이라는 장점을 얻을 수 있습니다.
    • laziness(지연) : Stream은 코드가 읽히는 즉시 연산을 하지 않고 최소한의 준비작업만 실시하다가, 해당 결과값에 대한 호출이 있을 때 계산한다.
    • loop Fusion(반복병합) : 연속적으로 체이닝된 복수의 연산을 하나로 병합시킨다.
    • short-circuting(쇼트 서킷) : 불필요하다 판단된 연산의 수행을 하지않고 중단한다.
  1. 내부 반복
  • Stream은 Collection과 반복문을 명시하지않아도 내부에서 순환시킬 수 있다.

이렇게 여러가지 장점에 대해 찾아보았습니다. 물론 Stream이 만능은 아닐 것입니다.

기본적으로 호출 시점이 확실하고 용량이 큰 BLOB데이터 같은 경우에는 비동기로 돌려서 미리 호출에 대비해야하지않나 라는 아주 얕은 생각이 내비치기도 하였으며,

파이프라인을 DB에 쿼리문 튜닝으로 1차적으로 잡아야 좀 더 효율적으로 움직일 수 있을거라는 생각도 조금은 들었습니다.

다음번에 Stream의 여러 종류중 하나에 대해 다루게 된다면 이 이론들을 다시 곱씹어 보겠습니다.

PrintWriter와 FileWriter??

릴리즈 버젼으로 빌드한 앱은 디버깅하기 굉장히 까다롭습니다. 물론 실 기기와 연동되면 로그캣이나 여러 툴을 사용해 디버깅이 가능하지만, 고객사가 굉장히 다양한 현재 회사 특성상 이상현상 하나때문에 매번 부산, 전라도부터 전국적인 지역을 출장갈 수는 없고, 폐쇄적인 네트워크 환경상 사무실에서는 로그인도 안됩니다.

그래서 앱을 테스트할 때는 VM(ex : Nox, LDplayer)등으로 사용하는데, 이때는 로그파일에 스택트레이스등을 남겨 테스트하곤 합니다.

PrintWriter 클래스는 뭘까?

PrintWriter 클래스는 PrintStream이라는 클래스의 기능을 구현한 Writer 클래스의 확장 클래스로, 기존 Stream처럼 글자마다 flush하는 것이 아니라, printf(),println()등 PrintStream메서드가 호출될때마다 flush되어 기록되며, Stream 방식으로 구현되는 것이 아니기때문에 출력 메서드를 사용할 때 byte가 아닌 String값을 통해 기록한다.

개인적으로 해당 값에 따라 String 값을 byte로 바꾸어 넣어야하는 번거로움과 해당 코드를 줄일 수 있을 뿐 아니라, String 값을 직접 입력하기에 소스를 처음 보는 상황이 생겨도 비교적 직관성을 향상시킨다고 생각한다.

FileWriter 클래스란?

문자 파일 작성을 위한 편의 클래스로, FileOutputStream 클래스와 OutputStreamWriter클래스의 기능을 간소화 시킨 것이다.

생성자로는 File 객체가 인자값으로 필요하며, 파일명의 문자열값(String fileName)이 들어가도 되기에 편한대로사용하면 되겠다.

두 클래스를 같이 사용하는 이유는?

해당 목표는 apk를 디버깅할 때 txt파일에 로그를 남겨 비교적 편리한 디버깅을 하는것이다.

또한 PrintWriter생성자에 FileWriter를 인자로 넣음으로서 파일을 지정하고 쓰기작업을 하는데 굉장히 간편하게 할 수 있다.

공식 문서에 보면 PrintWriter생성자에 Writer 객체와 자동 플러시의 여부를 지정하여 File에 자동으로 PrintStream의 메서드를 사용할 시 해당 문자열 값을 지정한 파일에 기록할 수 있다.

위 사진에 나오는 메서드들이 PrintWriter에 구현되어있는 print메서드들이다.

예시 코드는 아래와 같다.

public class FileLogTest{	
	public static void main(String[] args) {
					private PrintWriter writer;
	        try {
		        writer = new PrintWriter(new FileWriter(fileName, true), true);
		        writer.println("아");
		        writer.printf("%s 하자\\n","야근");
		        writer.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	    }
}

광범위 하게 사용한다면 try-catch를 해당 로직에 감싸서 편하게 writer.println(String message)등의 방식으로 입력하면 된다. 하지만 정말 제한적인 환경에서 도저히 사용할 방법이 없을때 하는 로깅방법이라 크게 자세히 다루진않고, 다른 예제나 기능과 관련하여 나오게 되면 조금 더 딥하게 알아보고싶다.

PrintWriter와 FileWriter??

릴리즈 버젼으로 빌드한 앱은 디버깅하기 굉장히 까다롭습니다. 물론 실 기기와 연동되면 로그캣이나 여러 툴을 사용해 디버깅이 가능하지만, 고객사가 굉장히 다양한 현재 회사 특성상 이상현상 하나때문에 매번 부산, 전라도부터 전국적인 지역을 출장갈 수는 없고, 폐쇄적인 네트워크 환경상 사무실에서는 로그인도 안됩니다.

그래서 앱을 테스트할 때는 VM(ex : Nox, LDplayer)등으로 사용하는데, 이때는 로그파일에 스택트레이스등을 남겨 테스트하곤 합니다.

PrintWriter 클래스는 뭘까?

PrintWriter 클래스는 PrintStream이라는 클래스의 기능을 구현한 Writer 클래스의 확장 클래스로, 기존 Stream처럼 글자마다 flush하는 것이 아니라, printf(),println()등 PrintStream메서드가 호출될때마다 flush되어 기록되며, Stream 방식으로 구현되는 것이 아니기때문에 출력 메서드를 사용할 때 byte가 아닌 String값을 통해 기록한다.

개인적으로 해당 값에 따라 String 값을 byte로 바꾸어 넣어야하는 번거로움과 해당 코드를 줄일 수 있을 뿐 아니라, String 값을 직접 입력하기에 소스를 처음 보는 상황이 생겨도 비교적 직관성을 향상시킨다고 생각한다.

FileWriter 클래스란?

문자 파일 작성을 위한 편의 클래스로, FileOutputStream 클래스와 OutputStreamWriter클래스의 기능을 간소화 시킨 것이다.

생성자로는 File 객체가 인자값으로 필요하며, 파일명의 문자열값(String fileName)이 들어가도 되기에 편한대로사용하면 되겠다.

두 클래스를 같이 사용하는 이유는?

해당 목표는 apk를 디버깅할 때 txt파일에 로그를 남겨 비교적 편리한 디버깅을 하는것이다.

또한 PrintWriter생성자에 FileWriter를 인자로 넣음으로서 파일을 지정하고 쓰기작업을 하는데 굉장히 간편하게 할 수 있다.

공식 문서에 보면 PrintWriter생성자에 Writer 객체와 자동 플러시의 여부를 지정하여 File에 자동으로 PrintStream의 메서드를 사용할 시 해당 문자열 값을 지정한 파일에 기록할 수 있다.

위 사진에 나오는 메서드들이 PrintWriter에 구현되어있는 print메서드들이다.

예시 코드는 아래와 같다.

public class FileLogTest{	
	public static void main(String[] args) {
					private PrintWriter writer;
	        try {
		        writer = new PrintWriter(new FileWriter(fileName, true), true);
		        writer.println("아");
		        writer.printf("%s 하자\\n","야근");
		        writer.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	    }
}

광범위 하게 사용한다면 try-catch를 해당 로직에 감싸서 편하게 writer.println(String message)등의 방식으로 입력하면 된다. 하지만 정말 제한적인 환경에서 도저히 사용할 방법이 없을때 하는 로깅방법이라 크게 자세히 다루진않고, 다른 예제나 기능과 관련하여 나오게 되면 조금 더 딥하게 알아보고싶다.

Timer / TimerTask 클래스

Timer클래스란?

말 그대로 정말 타이머(Timer)의 역할을 수행하는 클래스이다.

schedule이라는 내부 메서드를 통해 특성시간, 기간마다 인자로 받은 TimerTask인스턴스의 run메서드 로직을 수행하여 특정 시간마다 반복적인 로직을 실행하게끔하거나 하는 식으로 원하는 로직을 수행시킬 수 있다.

TimerTask 클래스란?

Timer클래스의 schedule함수로 작업 스케줄링을 관리할 때 들어가는 추상 클래스로,

Runnable클래스에서 run함수만 따와 별도의 스레드로 함수를 구동시키는 원리인 것같다.

사용 예제

우선 Timer클래스에 들어가면 생성자부터 schedule함수의 재정의에 따라 각각의 로직을 가지고있는데, 그것은 생략하겠다.

해당 예제에서 다루는 것은 Timer클래스의 생성, TimerTask의 정의, 이후 schedule에 함수에 들어간 TimerTask의 정상 작동 확인에 대해 다루겠다.

예제 1

ackage org.example;

import java.util.Timer;
import java.util.TimerTask;

//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
    public static void main(String[] args) {

        Timer timer = new Timer(); // 타이머 객체생성
        TimerTask timerTask = new TimerTask() { 
        // 추상 클래스이므로 인스턴스 생성시에 가진 메서드를 구현해야함 
            @Override
            public void run() {
                System.out.println("시간이 지났습니다~"); // 작동 로직
                timer.cancel(); // 타이머 종료
            }
        };
        
        // 구동 후 5초 뒤에 timerTask의 로직을 구동한다.
        timer.schedule(timerTask,5000); 
    }
}

아주 간단한 예제로 앱을 실행하고 5초뒤에 프린팅이 찍히게끔 되어있다.

TimerTask의 로직에 timer를 종료하는 cancel()를 사용하여 한번만 구동하게끔해놓았다.

왼쪽의 실행 시간을 보면 5초 후 구동이 완료되었고, 이후에 추가로 작동하지않고 종료됨을 알 수 있다.

그럼 타이머는 객체생성부터 작동될까, schedule()부터 구동될까?

객체 생성시부터 시간이 계산되는 것 같다. Timer의 클래스를 들어가보면

    public Timer() {
        this("Timer-" + serialNumber());
    }

    public Timer(String name) {
        thread.setName(name);
        thread.start();
    }

이렇게 두개의 생성자가 있는데, 위 코드에서 기본생성자로 생성시 아래있는 타이머 생성자가 실행되면서 스레드가 시작됨으로 봤을때 생성자체가 실행된다고 . 볼수 있다.

특정 로직에만 실행시키기

그러면 특정 로직을 탈 때에 특정시간마다 실행을 시키려면, TimerTask만 미리 정의하거나 임의의 커스텀 클래스로 제작하여 빼고, 특정 로직을 탈때 타이머 인스턴스를 생성하면 되겠다.

아래는 해당 예제에 대한 코드이다.

우선 TimerTask를 커스터마이징했다.

public class CustomTimerTask extends TimerTask {
    Timer timer;

    public CustomTimerTask(Timer timer){
        this.timer = timer;
    }

    @Override
    public void run() {
        System.out.println("타이머 로직이 실행됩니다.");
        timer.cancel();
    }
}
public class Main {
    public static void main(String[] args) {
        // 특정 로직을 실행하는지에 대한 bool 값
        boolean isTimerExecute = false;

        if (isTimerExecute) {
            Timer timer = new Timer();
            TimerTask timerTask = new CustomTimerTask(timer);
            timer.schedule(timerTask,5000);
        }else{
            System.out.println("타이머를 타야하는 로직이 아닙니다.");
        }

        
    }
}

이렇게 한 후 실행해보겠다.

특정 로직이 아니기에 불과 224ms만에 실행되었고, 그대로 앱은 종료되었다. isTimerExecute 변수값을 true로 바꾼 후, 타이머 로직이 실행되는지 테스트해보자

기존처럼 5초뒤에 TimerTask에 실행이 한번 되었고, 추가 작동없이 타이머가 종료됨을 알 수 있다.

이렇게 Timer클래스와 TimerTask에 대해 알아보았다. 해당 클래스에 대한 역할은 Spring Framework같은 경우 스프링 스케줄러나 스프링 배치등 여러 대체 구현 기능이 되어있지만, 간단한 경우에는 해당 클래스로 커스터마이징을 해서 사용해도될 거 같다.

 

+ Recent posts