Tagged: java

스트림(Stream)

자바(Java) 세계에서 언제부터인지 스트림(Stream) 이라는 단어를 목격하게 되었다. 내 기억으로는 Java 8 에서부터 시작된 것 같은데 난데없는 이 단어가 왜 그렇게 핵심이 되었는지가 의문이였다. 도대체 왜 스트림(Stream) 이냐 하는 질문에 대한 대답을 듣기도 어려웠던 시절이기도 하다. 그져 사용하는 방법을 익히는데에 몰두하는 모습만 목격됐을 뿐이다.

java.util.stream

스트림(Stream) 에 대한 정의는 다양하다.

데이터 소스(Array, List) 로부터 흐름을 가지는 데이터의 집합체이며 통합연산을(bulk processing) 통해 데이터를 변형시키고 최종적으로 소비자가 그 데이터를 소비하도록 한다.

스트림을 다루게 되면 항상 다음과 같은 데이터 소스들을 만나게 된다. 모두 데이터의 집합체들이다.

  • Array
  • List

하필 왜 데이터 집합체들일까

컴퓨터 알고리즘 필요성과 유사한 스트림(Stream)

난데 없이 컴퓨터 알고리즘을 꺼내온 이유가 있다. 컴퓨터 알고리즘을 공부할때에 가장 먼저 만나는 것이 정렬(sort)문제이다. 그런데, 이런 질문을 하게된다.

왜 하필 정렬부터 인가?

이에 대한 대답은 간단다.

Compute 연산과 Memory 공간을 절약하기 위해서..

컴퓨터가 중복된 데이터를 어떻게 찾아낼까? 정렬을하면 쉽게 해결된다. 정렬된 데이터가 아니라면 모든 데이터를 비교해야 하지만 정렬할 경우에 같은 위상을 같은 데이터 값이 나오게 되는데 이를 하나만 남기고 지우면 간단해 진다.

이렇게 함으로써 Memory 공간도 절약하게 되고 이렇게 중복되지 않은 데이터를 가지고 Compute 연산을 할 경우에 당연히 그에 들어가는 비용도 줄게 된다.

자바에서 스트림도 이와 유사하다.

자바에서 데이터를 다루는 방법은 다양한다. 이는 데이터 소스를 통해서 다루어지는데, 이 데이터 소스를 간단하게 타입(Type) 이라고 생각해보자. 정수형, 문자열 등은 가장 단순한 타입이다.

이런 타입들은 단 하나의 데이터만 저장하고 있을 뿐 “데이터들” 을 가지고 있지 않다. Compute 연산 알고리즘에서는 여러 데이터들의 집합을 다룬다. 컴퓨터가 가지고 있는 데이터들이란 집합을 이야기 한다. 따라서 데이터 소스라고하면 “데이터들” 을 지칭하며 자바에서 이런형태의 데이터 타입은 Array, List 가 대표적이다.

그럼 이런 생각을 하게된다. 데이터 집합체들을 어떻게 하면 빠르게 중복을 제거하고 연산을 하게 만들 것인가? 과거에 For loop 문과 같은 것을 이용해서 조건식을 붙이면서 사용을 할 수도 있다.

람다(Lambda)

연속된 데이터들을 다루기만 할 거라면 단순하게 For loop 문을 이용하면 된다. 만일 이런 생각을 하게 된다.

연속된 데이터를 처리할때에 병렬을 이용해서 처리보자.

For loop 문에서 병렬처리는 쉬운게 아니다. Thread 를 이용할 수도 있지만 이건 동시성 프로그래밍이지 병렬은 아니다.

이를 위해서 자바 8 에서는 람다(Lambda) 를 도입했다. 이것에 대한 정의를 보면 함수형 프로그래밍(Funtional Programming) 이라는 말을 자주 접하게 되는데 병렬연산을 가능하게 하는 부분이다.

자바 8 스트림은 이 람다를 기반으로 한다. 결국에 스트림은 벌크 프로세싱(Bulk Processing) 을 람다를 사용해 구현하여 빠른 고속 데이터 처리가 가능하다.

스트림 – 흐른다.

스트림의 중요한 특징은 흐름이다. 프로그래밍에서 데이터를 다룰때 흐름 없이 다루는 경우도 많다. 앞에서 컴퓨터가 다루는 데이터는 “데이터들” 이라고 했는데, 이것들을 흐름을 가지고 연산을 수행하는게 스트림이다.

“흐른다” 라는 말을 수도관을 떠올리게 한다. 왼쪽에 물을 흘려보내면 오른쪽으로 물이 나온다. 데이터를 왼쪽에서 흘려보내면 오른쪽으로 물이 나온다. 만일 이 물이 설탕물로 만들고 싶다면 중간에 설탕을 뿌리면된다. 이물질을 제거하고 싶다면 이물질 제거기를 설치하면 된다.

이렇게 보면 누군가 데이터를 흘려보내는 놈이 필요하고 데이터를 받아 마시는 놈이 필요하게 된다. 이것을 Producer 와 Comsumer 관계라고 부른다.

리액티브 와 무슨 관계?

자바 스트림과 Reactive 관계보다 차이가 존재한다.

스트림(Stream) 은 데이터를 생산하면 즉각 소비가 발생한다. 하지만 리액티브 는 그렇지 않다. 리액티브 은 시간이 지남에 따라서 생산과 소비가 발생한다. 생산과 소비가 즉각적이지 않다.

이말을 잘 생각해 볼 필요가 있다. 스트림은 데이터를 다루는 영역에서 매우 유용할 수 있다. 프로그래밍 연산을 할 경우에 적합하게 사용되어질 수 있다. 하지만 Reactive 는 프로그래밍 연산보다 네트워크를 통한 데이터 요청과 리턴에 접합한 모델이라고 할 수 있다.

차이는 또 있다. 리액티브 에서 생산자는 반드시 흐름 데이터만 만들지 않는다. 대표적으로 웹에서 클릭(Click) 조차도 리액티브 에서 생산자가 될 수 있다. 그래서 연속된 데이터 흐름이 없다보니 뭔가 생산하는 개념이 아닌것이여서 생산자(Producer) 라는 말을 쓰지 않는다.

“즉각적으로 소비가 발생하지 않는다” 라는 말도 중요하다. 비동기적으로 데이터 리턴이 발생한다는 것을 의미 한다. 하지만 리턴 값을 받기 위한 준비는 항상하고 있다는것도 중요하다.

리액티브 는 네트워크를 통한 데이터 요청, 리턴 모델에 적합하다. 리액티브 요청한 것에 대한 데이터들을 다룰때에는 스트림을 이용할 수도 있다.

Error – The server time zone value ‘KST’ is unrecognized

Java 와 MySQL 을 연동하는 상황에서 다음과 같은 오류를 만나기도 한다.

자세히 보면 java.sql.SQLException 이 보인다. 이 경우는 결국에는 데이터베이스쪽에 문제가 있다는 것이며, MySQL을 사용할 경우에 보이게 된다. 이는 MySQL의 시간을 나타내는 타임존 설정이 맞지 않아 생기는 오류다.

MySQL 5.7, MariaDB 10

MySQL 5.7 과 MariaDB 10 을 사용한다면 my.cnf 에서 다음과 같이 설정함으로써 문제 해결이 가능하다.

설정할 수 있는 타임존 리스트는 MySQL 메뉴얼을 참조하기 바란다. 이렇게 했는데도 다음과 같은 오류를 만날 가능성도 있다.

이럴때는 다음과 같이 해준다.

Tomcat 설치 디렉토리 구성

과거에 Tomcat Multi Instance 설치에 관해서 쓴 글이 있다. Multi Instance 설치에서 핵심은 CATANINA_HOME과 CATALINA_BASE 를 분리하는데 있다.

오늘은 한발 더 들어가서 배포를 위한 설정과 시작, 종료 스크립트의 변경등에 대해서 이야기해보고자 한다.

Multi Instance 구성

Tomcat 을 다운로드 받아 압축을 풀면 그것이 곧 CATALINA_HOME 이 된다. 그리고 CATALINA_BASE 를 위해서 conf 디렉토리를 복사해주고 bin, lib, logs, temp, webapps, work 디렉토리를 생성해준다. 그리고 더블어서 war 파일을 배포를 위한 디렉토리를 Deployments 를 생성해주고 PID 저장을 위한 run 디렉토리도 생성해준다.

CATALINA_HOME 에서 복사해주는 디렉토리는 conf 밖에 없다는걸 상기해야 한다.

setenv.sh 작성

catalina.sh 스크립트를 보면 Tomcat 를 위한 설정등은 setenv.sh 를 읽어오도록 되어 있다.

CATALINA_BASE/bin/setsenv.sh 를 먼저 확인하고 없으면 CATALINA_HOME/bin/setenv.sh 를 찾도록 되어 있다.

setenv.sh 에는 CATALINA_OPTS, JAVA_OPTS 등의 환경변수등을 설정할 수 있다.  환경변수에 대해서는 catalina.sh 파일을 열어보면 자세히 나온다.

‘-Dport.http’ 와 같은 변수 선언은 server.xml 에 다음과 같이 활용할 수 있다.

그리고 이제 start.sh 와 shutdown.sh 는 다음과 같이 아주 간단하게 작성하면 된다.

CATALINA_HOME, CATALINA_BASE 디렉토리를 함께 정의해 준다.

기본적은 환경 변수를 start.sh, shutdown.sh 에 넣어주거나 하니면 별도로 빼서 소스해줘도 된다.

배포

Tomcat 에는 appBase, docBase 와 같은 지시자가 존재한다. 이것을 별도로 지정하지 않으면 webapps 디렉토리가 appBase, docBase 가 된다. 이를 분리하면 war 파일 배포와 app 실행 디렉토리를 분리할 수 있게 된다.

이를 위해서 먼저 server.xml 에서 다음과 같이 설정을 변경한다.

위와같이 Context 에서 docBase 로 war 파일을 지정하면 된다. 이렇게하면 war 파일을 읽어서 webapps 디렉토리에 압축을 해제하게 된다.

이렇게해야 하는 이유가 있는데, war 파일을 배포하는 디렉토리의 소유권을 배포계정으로 하고 others 에 읽기(read) 퍼미션만 준다. 즉, 운영계정과 배포 계정을 분리해서 운영할 수 있게 된다는 것이다.

배포는 conf/CATALINA/${virtualhost}/ 디렉토리에 context 이름으로 xml 파일을 작성해도 배포가 된다. Multi Instance 를 구성하게 되면 Manager 가 없게 된다. 그런데, 많은 사람들은 이를 CATALINA_HOME/webapps/manager 디렉토리를 CATALINA_BASE/webapps 디렉토리에 복사해 넣는다. 하지만 그렇게 하지 않아도 된다.

이를 ${catalina.base}/conf/Catalina/localhost/manager.xml 파일로 작성한다.

그러면 Tomcat 은 이를 읽어서 manager context 를 실행 준다.

왜 이렇게 하나?

CATALINA_HOME 과 CATALINA_BASE 를 분리하면 좋은 점이 업그레이드를 해야할 경우 나타난다.

몇몇 프로젝트하는 곳에서 Tomcat 설치된 것을 보면 CATALINA_HOME 에 startup.sh 파일이나 catalina.sh 파일을 직접 수정하는 경우를 볼 수 있다. 만일 보안 업데이트가 발생해 이를 Tomcat 업데이트를 해야한 경우라면 startup.sh, catalina.sh 파일을 새롭게 수정해줘야 한다.

하지만 CATALINA_HOME 과 CATALINA_BASE 를 분리하면 CATALINA_HOME 은 건드린게 없기 때문에 그냥 새로운 버전의 Tomcat 으로 바꿔치기하면 그만이다.

이는 휴먼 에러를 줄이는 확실한 방법이며 보안 업데이트 시간을 단축시켜준다.

 

Spring5 requirements

Spring5 를위한 필요사항들을 정리.

  1. java 8. 원래 Spring5 는 Java 9를 기반으로 하려고 했지만 변경됐다. 이로 인해서 Reactive Programming을 하기 위해서는 의존성 라이브러리를 필요로 한다. 현재Java 9 은 지원이 중단된 상태다. Java 10 을 사용해도 된다.
  2. Java EE 8 호환. Servlet 4.0, Bean Validation 2.0 등을 지원한다.
  3. HTTP/2 지원한다.
  4. Jackson 2.9, Protobuf 3.0 지원.

 

Java 시간 대역 체크하기

Java 프로그래밍을 하다보면 시간을 다루게 된다. 만일 점심시간인지 아닌지를 시간을 체크하고 싶다면 어떻게 할까? 대략 다음과 같이 할 수 있다.

isAfter, isBefore 메소드를 이용하면 손쉽게 체크할 수 있다.

Java BookMark

이 페이지는 Java 관련 외부 링크를 정리한 것입니다. 훌륭하게 정리한 글들이 넘쳐나고 있는데 이것을 퍼오는것보다 링크를 걸어두는게 더 좋을 것 같아 만들었습니다. 물론 몇몇 글중에는, 내 개인 기준으로 이런게 더 들어갔으면 좋겠다라고 했던 것은 제 블로그에 Post 로 작성하기도 했습니다.

Java

JBoss

Spring

SpringBoot

MongoDB

AWS

Nginx

Maven 저장소 URL 변경하기

Maven 3 을 쓰다보면  Update나 Build 시에 Maven은 어딘서가 의존성 패키지를 다운받는다. 다운받은 파일을 어디에 저장할 것인가 를 지정하는것이 localRepository 이다.

문제는 어딘가에서 받아오는 저장소 URL 이 간혹 접속이 불가할 경우가 문제가 된다. 나 같은 경우에 일하고 있는 사무실 환경에서는 어찌된 영문인지 Maven이 패키지를 제대로 가지고 오지 못하고 있었다. 원인은 “Connection Time Out” 으로서 “https://repo.maven.apache.org/maven2” 에 접속이 되지 않아 발생 했다. 자세히 보니 https 로 접속이 안되는 문제였다.

이와같이 접속이 불가능 할 경우에는 Maven 저장소 URL 을 바꿀 필요가 있다.

pom.xml 에서 바꾸기

일차적인 방법으로는 프로젝트의 pom.xml 파일에서 저장소 위치를 지정하는 것이다. 먼저 저장소 위치가 어떻게 되는지 알수가 있다. 그것은 Eclipse 에서 pom.xml 을 열면 편집영역 하단에 “Effective POM” 탭이 보인다. 여기서 중간쯤에 보면 다음과 같은 저장소 URL 이 보인다.

Maven 3 에서 기본 세팅된 저장소 URL 을 살펴 볼수 있다.

아니면 pom..xml 이 있는 프로젝트 디렉토리에서 다음과 같은 명령어로 Effective POM 상태를 출력해 볼 수 있다.

이제 기본적인 저장소 URL 을 알았으니 이것을 바꿔보자. pom.xml 탭을 클릭해 다음과 같이 바꿔보자.

이렇게 하면 Maven의 기본 저장소 URL을 변경이 된다.

Maven settings.xml 설정 파일에서 바꾸기

만일 프로젝트가 아주 많이 있다면 일일이 프로젝트마다 앞에 방법대로 pom.xml 을 수정한다는 것이 바보같은 짓이된다. 이럴때에는 Maven의 설정 파일인 settings.xml 에서 설정 해주면 된다.

다음과 같이 설정 해준다.

이렇게 변경 후 저장하고 Eclipse 를 재시작하면 모든 프로젝트에 Effective POM 을 보면 저장소 위치가 변경된 것을 확인할 수 있다.

Maven 다중 모듈 프로젝트 구성

STS Eclipse를 이용하면 손쉽게 Spring MVC 프로젝트를 구성할 수 있다. Spring MVC 프로젝트에는 Java 파일과 Web Content 파일을 모두 포함한다. 이를 Dynamic Content, Static Content 등로 구분하기도 한다.

문제는 이렇게 하나의 프로젝트에 모든 것을 담을 경우에 다음과 같은 문제가 발생할 수 있다.

  • 분리 배포가 불가능 하다. 요즘에는 WAS 앞에 Web 서버를 따로 두어 Static Content 를 서비스 하는 아키텍쳐가 많은데 하나의 프로젝트에 모든 것을 담게 되면 분리 배포가 어렵다.

단 하나의 큰 문제인데 이 문제를 가볍게 여길 수 없다. 실제 프로젝트를 할때에 이것을 간과해 나중에 아주 힘들어지는 경우가 허다 하다.

그래서 Spring MVC 기반의 프로젝트를 할때에는 Dynamic Content, Static Content를 분리해 진행하는 것이 좋은데, Maven 에서는 이를 ‘다중 모듈 프로젝트 구성’ 이라는 기능으로 제공하고 있다.

Root 프로젝트 생성

다중 모듈 프로젝트를 생성하는 첫번째는 Root 프로젝트 생성이다. 이름에서도 알수 있듯이 이 모든 모듈 프로젝트에 뿌리가 되는 프로젝트를 말한다.

이는 ‘Maven Project’ 를 통해서 생성 할 수 있다.

Maven Project 생성
Maven Project 생성

위와같이 Maven Project를 통해서 생성할때 한가지 주의해야 하는 것이 있는데, Pakaging 형태를 pom으로 해야만 한다. Root 프로젝트는 하위 프로젝트에 기반을 제공하는 것으로 별도 was 배포용 패키지를 만들지 않는다.

Root Project 에 pom Packaging
Root Project 에 pom Packaging

이렇게 하면 Root Project가 생성이 된다.

이 상태를 그대로 두고 Static Content를 위한 하위 프로젝트를 먼저 생성한다.

하위 프로젝트 생성(Static Content)

Static Content 를 위한 하위 프로젝트는 오로지 CSS, Java Script, Images 만을 위한 것이여서 Spring Mvc 샘플 프로젝트를 만들 필요는 없다. 간단하게 Maven Module 로 제작하면 된다.

앞에서 생성한 Root Project 에서 마우스 오른쪽 버튼을 눌러 Maven Module 생성하기를 시작한다. 그리고 다음과 같이 ‘maven-archetype-webapp’ 를 선택해준다.

Maven 으로 webapp 생성
Maven 으로 webapp 생성

이렇게 하면 Root Project 밑에 디렉토리로 하위 프로젝트가 위치하는게 보이고 이와 별도로 이클립스에 프로젝트로도 보인다.

Root 와 Static 프로젝트 모습
Root 와 Static 프로젝트 모습

Static 프로젝트에 pom.xml 를 보면 Root 프로젝트와 연결된 설정이 보인다.

이제 Static 의 디렉토리 구조를 바꾼다. 이는 이전에 글을 참고 하면 잘 나와 있는데 여기서는 WebContent 디렉토리만 인식시켜주면 되기 때문에 pom.xml 에 설정해주고 파일을 옮기면 된다.

그리고 Static 파일을 위한 디렉토리를 생성한 다음에 가짜 파일을 하나 만든다.

Static 파일을 위한 CSS, JS, Images 디렉토리 생성
Static 파일을 위한 CSS, JS, Images 디렉토리 생성

이렇게 한 후에 Build 를 하면 WebContent 이하 디렉토리 내용이 war 파일로 작성되어 진다. war 파일이긴하지만 Static만 있으면 되기 때문에 이 Static 파일만 담기도록 해보자. pom.xml 을 다음과 같이 수정 한다.

** 위와같이 설정해도 원하는데로 동작하지 않습니다. 아신다면 덧글 남겨 주시면 고맙겠습니다. **

이렇게 한 후에 SystemVLabs-Static 에 빌드를 하면 WebContent 디렉토리에 내용들이 war로 만들어진다. 또, RootProject 에서 빌드를 하면 하위 모듈로등록된 SystemVLabs-Static 도 빌드가 되면 정상적으로 프로젝트가 세팅된 것이다.

하위 프로젝트 생성(Dynamic Content)

이제 Dynamic Content 를 위한 하위 모듈 프로젝트를 생성해야 한다. 이는 여러 방법이 존재하는데, 내가 보기에 가장 손쉬운 방법을 설명하고자 한다.

먼저 STS Eclipse 의 Spring MVC 샘플 프로젝트를 생성한다. 그러면 기존의 RootProject와는 별도로 프로젝트가 생길 것이다.

Spring5 샘플 프로젝트
Spring5 샘플 프로젝트

이제 이것을 export 를 해주는데, File System 으로 export 를 한다.

Export를 File System 으로 한다.
Export를 File System 으로 한다.

대상 디렉토리는 RootProject 로 지정한다.

Spring5의 RootProject 로 Export
Spring5의 RootProject 로 Export

이렇게 한 후에 RootProject 를 ReFresh 하면 방금 Export 한 Spring5 디렉토리가 보인다.

이제 기존의 Spring5 프로젝트는 디스크에서 삭제도 체크해  삭제한다. 그리고 이제 프로젝트를 Import 한다.

Existing Project into Workspace
Existing Project into Workspace

“Existing Projects into Workspace” 를 선책하고 Next,

Export된 Spring5 디렉토리 지정
Export된 Spring5 디렉토리 지정

이렇게 하면 RootProject 하위가 아닌 독립된 프로젝트로 나타난다.

마지막으로 RootProject 의 pom.xml 에 방금 등록한 하위 모듈 프로젝트로 등록해 준다.

그리고 새로 등록한 하위 모듈 프로젝트의 pom.xml 에는 parent 모듈을 등록해 준다.

이렇게 함으로써 Dynamic Content 를 하위 모듈 프로젝트 등록은 다 된 것이다.

 

Tomcat plugin 을 이용한 배포.

만일 WAS 서버를 Tomcat 을 이용해 개발을 하고 있다면 Maven tomcat7 플러그인을 이용해서 배포를 할 수 있다. 참고로 이클립스의 tomcat add-on 과는 다른 것이다. 이것은 Tomcat 에서 제공하는 Manager 기능을 이용한 것이다.

그리고 이 문서의 내용은 Tomcat 8 버전에서도 이용 가능 하다.

Tomcat 설정

Tomcat 의 Manager 기능을 이용하는 것이여서 Tomcat Manager 설정을 먼저 해줘야 한다. 아시겠지만 Manager 접근을 위해서는 인증 설정을 해줘야 하는데 이 인증은 Tomcat 의 ㅊCATALINA_HOME/conf/tomcat-users.xml 파일을 다음과 같이 설정 해준다.

Tomcat 방화벽 설정

앞서 설정을 한다고 해도 원격에서 Manager 접속은 불가능 하다. 왜냐하면 Tomcat 자체에 Manager 접속을 로컬호스트만 허용하도록 되어 있기 때문이다. 이것은 “CATALINA_HOME/webapps/manager/META-INF/context.xml” 파일에 설정되어 있다.

위 설정을 보면 ‘192.168.96.2,192.168.96.3’ 두개의 원격아이피가 허용되어 있다.

이 설정은 매우 중요하다. 모든 설정이 정상인데도 배포 실패가 나온다면 이 설정을 잘 살펴봐냐 한다.

Maven 설정(pom.xml)

이제 pom.xml 설정을 해야 한다. Maven 에서는 Tomcat Manager 에 원격 접속해 배포할수 있도록 도와주는 플러그인을 제공하는데 그 설정은 다음과 같다.

package tomcat7:[deploy|undeploy|redeploy]

tomcat7 maven 플러그인은 위 주제와 같이 사용하면 된다. Maven Build 시에 위 내용을 입력하면 설정한 Manager URL 로 배포 파일을 업로드 해주는데 이때 업로드 되는 파일 이름은 path 설정 이름으로 된다.

web.xml 스키마 예제 헤더

web.xml 스키마 헤더 예제는 버전별로 다음과 같다.

Servlet 2.5

Servlet 3.0

Servlet 3.1

Servlet 4.0

 

참고: IBM Knowledge Center