2018년 3월 31일 토요일

Node.js - 스터디 2주차

https://opentutorials.org/course/2136/
생활코딩 강의를 이용해 진행하는 스터디 2주차.

4/1(일) :: 12강 '웹페이지를 표현하는 방법' 부터 14강 'URL을 이용한 정보의 전달'까지.


Express-웹페이지를 표현하는 방법


지난 강의에 이어서... 프로그래밍 기법을 이용해 동적인 컨텐츠를 제공할 수 있다. 서버 애플리케이션이 미리 작성된 html 파일을 그대로 읽어 던지는 것이 아니라 클라이언트인 브라우저가 해석 가능한 형식(주로 html)으로 컨텐츠를 새롭게 '생성하여' 클라이언트에게 전달하는 방식이다. 정적인 서비스 방식과 동적인 서비스 방식은 서로 장단점을 갖는다. 매 요청마다 컨텐츠를 새롭게 생성하는 것은 서버 자원이 그만큼 소모되는 것이기 때문에 정적인 요소는 정적인 방식으로 서비스 하는 것이 맞다. Node.js를 사용하는 일정 규모 이상의 서비스에선 Node.js 앞단에 정적인 리소스만 serving하는 전용 웹서버를 두기도 하는 것으로 알고 있다.

기존 Javascript에선 multi-line 문자열을 표현하거나 동적인 문자열을 만들기 꽤 번거로웠는데 이를 해소하기 위해 Commented text라는 기능이 추가되었다. Grave accent 또는 백틱이라(` `) 불리는 문자로 문자열을 감싸주는 방법인데 multi-line 문자열을 쉽게 표현할 수 있고, 플레이스 홀더 '${ }'를 이용한 문자열 생성도 지원한다. (template literal. ES2015에선 'template strings'라 했다고 함.)

아래 예시처럼 읽기에도, 쓰기에도 편리하다.


Express-템플릿 엔진 (Jade)


템플릿 엔진을 사용하면 html을 좀 더 편하게 만들 수 있다. Node.js에선 'jade'라 하는 템플릿 엔진을 많이 쓰는 듯 하다. jade를 사용하려면 당연히 jade를 설치해야 하고 jade의 문법을 이해하고 있어야 한다.

설치와 의존성 추가 방법은 다른 모듈과 동일한 방법으로 제공된다.
$ npm install jade --save

서버 코드에 jade를 사용하겠다는 선언과 함께 jade 파일의 위치를 지정해 준다. 그리고 작성된 jade 파일을 사용자에게 전달할 땐 'render' 메서드를 이용한다.
app.set('view engine', 'jade');
app.set('views', './view');

tip> app.locals.pretty = true;로 해주면 jade가 생성하는 html 코드를 예쁘게(?) 줄바꿈 해준다. 브라우저에서 생성된 코드를 살펴볼 때 유용한 옵션이다.

jade에서 줄바꿈은 새로운 문법의 시작을 뜻하고, 들여쓰기는 앞선 태그 선언의 안 쪽에 위치하는 것을 가리키는 용도로 쓰인다. 프로그래밍 키워드 앞엔 '-'를 붙여 jade에게 프로그래밍 표현임을 알려주어야 한다. 변수의 전달은 render 함수의 두번째 인자로 key-value object로 작성된 변수 묶음을 던져주는 방식으로 동작한다. jade에선 '=' 할당식을 이용해 전달받은 key를 그대로 쓰면 된다.

html
  head
    title= title
  body
    h1 Hello Jade
    ul
      -for (var i = 0; i < 5; i++)
        li coding
    div= time


Express-URL을 이용한 정보의 전달


쿼리 스트링. http://a.com/topic?id=1
쿼리 스트링을 이용하면 topic이란 하나의 path로 보다 풍부한 요청식을 만들어낼 수 있다.

/topic?id=0 이라는 표현에서. 'id=0'이라는 값은 req 객체의 query dictionary에 기록된다. 따라서 '/topic' 핸들링 함수에서 req.query.id와 같은 표현식으로 값을 얻을 수 있다.


그런데.. topic?id=0 보다는 topic/0로 표현하는 것이 구조적으로 좀 더 깔끔하고 직관적이란 걸 알 수 있다. 이러한 표현방식을 Semantic(의미론적) URL이라 하는데 현대적인 웹-애플리케이션에서는 쿼리 스트링 방식보다 Semantic 방식을 선호하므로 알고 넘어갈 필요가 있다.
https://en.wikipedia.org/wiki/Clean_URL

전 예제와 다르게 end-point가 '/topic/:id'로 변경된 것에 유의하자. 그리고 ':id'에 매칭된 값이 이전 예제와 다르게 query 프로퍼티에 담기지 않고 params에 기록된다.



쿼리 스트링에 대한 예제는 아래에..
https://expressjs.com/en/api.html#req.query
// GET /search?q=tobi+ferret
req.query.q
// => "tobi ferret"

// GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse
req.query.order
// => "desc"

req.query.shoe.color
// => "blue"

req.query.shoe.type
// => "converse"

4/3(화) :: 15강 'Express-POST 요청을 처리하는 방식에 대해' 부터 17강 '웹 애플리케이션 제작'까지.


Express-웹페이지를 표현하는 방법


GET 요청과 POST 요청에 대해..
GET은 정보에 대한 조회를 위해, POST는 정보에 대한 입력을 위해 주로 쓴다. 사용자 입력 데이터는 일반적으로 <form> 태그를 이용해 전달한다. form 태그를 jade 방식으로 선언하려면 대략 다음과 같다.

doctype html
html
  head
    meta(charset='utf-8')
  body
    form(action='/form_receiver' method='post')
      p
        input(type='text' name='title')
      p
        textarea(name='description')
      p
        input(type='submit')

위의 예시와 같이 jade에서 들여쓰기는 매우 중요하다. 속성은 '( )' 괄호를 이용해 지정하는데, form 태그의 경우 데이터가 전달되어야 하는 엔드포인트를 'action' 속성에 지정하게끔 되어 있다. 그리고 'method' 속성을 이용해 요청 타입을 구분하게 된다. 위 예시에서는 method 값이 post로 되어있고, 그렇게 하는 것이 맞지만 get으로 지정할 수도 있다. (그렇지만 form 데이터는 반드시 POST로 처리하자. GET으로 처리할 경우 form 데이터가 URL에 모두 드러나게 되어 보안에 취약하다. 또한 URI를 표현하는 길이에 대한 제한이 있으므로 긴 길이의 데이터 포함시 이를 적절히 처리하지 못하는 문제가 발생할 수 있다.)

일반적인 form 데이터는 request의 Content-Type이 기본적으로 'application/x-www-form-urlencoded'로 지정된다. (application/x-www-form-urlencoded는 key=value&key=value... 형식으로 데이터를 다룬다.) POST 요청에 대해선 추가로 body를 인코딩해서 전달하는데 express는 기본적으로 이러한 POST 요청에 대한 body 파싱을 해주지 않는다. 이를 처리하기 위해선 'body-parser'라는 미들웨어를 사용해야 한다. body-parser 미들웨어는 요청 body를 파싱해서 req.body 객체에 값을 넣어주는 역할을 한다. 사용자는 req.body에서 필요한 값을 꺼낼 수 있다.

var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended : false }));

extended 옵션이 false면 querystring 라이브러리로 body를 파싱하고, true면 qs라는 라이브러리로 body를 파싱한다. 자세한 건 잘 모르겠다.

팁 - Nodejs를 자동으로 재시작


npm install supervisor -g 로 supervisor 모듈을 설치하면,

node.js로 작성한 서버 코드 변경에 대해 서버 재시작 없이 변경점을 바로 확인할 수 있다.
일종의 watcher인 셈인데 사용하는 입장에서 매우 편리하다고 할 수 있다.

웹 애플리케이션 제작


본격적인(?) 웹 애플리케이션 제작. 강의 예제는 파일 read/write를 이용한 간단한 작성/조회 애플리케이션이다. 여기까지만 공부해도 배울 점이 꽤 많다. 완성된 형태는 아래와 같다.


앞선 강의에서 익힌 기본적인 라우터 설정과 jade 템플릿 사용법을 이용해서 위와 같은 애플리케이션을 제작하게 된다.

다른 부분보다 jade 표현 방식이 재미있는 것 같다.

doctype html
html
  head
    meta(charset='utf-8')
  body
    h1
      a(href='/topic/') Server Side JavaScript
    ul
      each topic in topics
        li
          a(href='/topic/' + topic)= topic
    article
      h2= title
      = description
    br
    div
      a(href='/topic/new') new

서버 코드에선 특징적인 내용은 없고, 여러 개의 요청을 하나의 엔드포인트 핸들링 메서드에서 처리하는 방식이 인상적이다.


위 사진과 같이 요청에 대한 처리 로직이 비슷한 경우 배열을 이용해 하나의 함수로 처리할 수 있다. ['/topic', '/topic/:id']


4/4(수) :: 18강 '파일 업로드' 부터 19강 '데이터베이스'까지.


파일 업로드


파일 업로드를 구현하는 방법엔 여러가지가 있겠지만 그 중 multer 모듈을 사용하기로 한다. multer 모듈은 파일 업로드를 지원하는데 설치와 사용 방법은 다른 모듈과 같다.

npm install --save multer
var multer = require('multer');

업로드를 지원하는 폼을 준비해보자. 파일의 경우 input 타입을 file로 지정해야 하고, form 태그의 enctype을 multipart/form-data라 지정해 주어야 한다.
강의에서 나온 예제 jade 뷰는 아래와 같다.

doctype html
html
  head
    meta(charset='utf-8')
  body
    form(action='upload' method='post' enctype='multipart/form-data')
      input(type='file' name='userfile')
      input(type='submit')

남은 것은 전송한 파일을 서버에서 취득하는 방법과 특정 위치에 저장하는 방법에 대한 것인데 multer가 바로 이러한 기능을 지원한다.

var upload = multer({ dest: 'uploads/' });

multer는 함수인데 multer를 호출하면 업로드를 지원하는 미들웨어를 반환해준다. multer는 옵션 객체를 인자로 받는데 옵션엔 파일이 업로드될 위치와 파일 필터 등을 지정할 수 있게 구성되어 있다. multer의 경우 핸들링 메서드의 두번째 인자로 upload.single()과 같은 코드를 삽입해서 처리하는 것이 특징적인 부분이다. single 함수의 인자엔 file 타입을 갖는 input 태그의 name 속성을 지정해주어야 multer가 처리할 수 있다.

multer는 사용자가 요청한 데이터에서 파일이 포함되어 있다면 파일을 가공해서 req.file 프로퍼티에 암시적으로 추가하도록 되어있다. file 객체를 콘솔에 찍어보면 다음과 같다.


multer의 고급 기능으로 프로그래밍 방식으로 파일의 위치나 형식을 변경할 수 있다. 강의에 나온 예제는 아래와 같은데. filename 함수를 통해 파일의 이름을 지정해줄 수 있고, destination 함수를 통해 파일이 기록되는 경로를 지정해 줄 수 있다.



데이터베이스

별 내용은 없어서 패스. (문제 유형에 맞는 데이터베이스를 선정하는 능력을 기르려면 다양한 데이터베이스 환경에서의 경험이 필요하다. 최근 백업, 업그레이드, 하드웨어 안정성 부분을 클라우드 사업자가 잘 지원해주고 있기 때문에 러닝 커브는 확실히 줄어든 편이다.)


2018년 3월 25일 일요일

Node.js - 스터디 1주차

https://opentutorials.org/course/2136/
생활코딩 강의를 이용해 진행하는 스터디 1주차.

이고잉님 짱..


3/25(일) :: 1강 '서버 측 자바스크립트와 nodejs 소개' 부터 4강 '모듈과 NPM'까지.

Node.js?

"Node.js = V8 + event-driven + non-blocking IO"

구글은 크롬 브라우저를 발표하면서 자바 스크립트 V8 엔진을 오픈 소스 형태로 함께 공개하였다. 구글이 공개한 V8 엔진의 성능은 매우 뛰어났는데, V8 엔진에서 가능성을 엿본 라이언 달은 이를 이용해 node.js라는 프레임워크를 개발하게 된다.

Node.js는 웹 브라우저를 벗어난 일반 os 환경에서도 자바스크립트를 사용할 수 있는 런타임 환경을 제공한다. 그리고 V8엔진과 더불어 event-driven, non-blocking 패러다임을 채택하여 자바스크립트의 약점으로 지적받는 성능 문제를 극복한다. 무엇보다 node.js가 갖는 가장 큰 장점은 하나의 언어를 이용해 완결된 웹-애플리케이션을 만들 수 있다는 것!




Module


require 키워드를 이용해 필요한 모듈을 가져올 수 있고, 모듈에서 제공하는 기능을 사용할 수 있다. 모듈엔 JavaScript가 제공하는 모듈, node.js에서 제공하는 모듈, 그리고 다른 개발자가 만들어 배포한 모듈로 구분된다. JavaScript, Node.js가 제공하는 모듈은 node.js 환경에서 기본적으로 사용할 수 있지만 다른 개발자가 만들어 배포한 모듈은 NPM(Node Package Manager)라 하는 패키지 매니저를 통해 가져와야 사용할 수 있다. NPM은 모듈에 대한 설치, 삭제, 업그레이드, 의존성 관리 기능을 지원한다.

ex) npm init // initial package.json을 만드는 과정. npm이 사실상 표준이기 때문에 package.json으로 node.js 프로젝트의 정보를 관리해 주는 것이 좋다.
npm install underscore // underscore 모듈을 설치.
npm install underscore --save // package.json의 'dependencies'에 underscore 모듈이 추가됨.


3/27(화) :: 5강 '콜백(callback) 함수' 부터 9강 'Express-간단한 웹애플리케이션 만들기'까지.


콜백


"콜백은 Caller가 Callee를 호출하는 것이 아니라 Callee가 Caller를 호출하는 것을 말한다."

콜백을 짚고 넘어가기 전에 자바스크립트의 타입 체계를 알 필요가 있는 것 같다. 자바스크립트의 기본적인 데이터 타입 체계는 다음과 같다.

기본 자료형 (Primitive)

1. Boolean - true / false
2. Null - 딱 한가지 값, null을 가질 수 있다. 의도적인 할당이라 보는 것이 맞을듯. typeof 값이 'object'인 것은 함정.
3. Undefined - 값을 할당하지 않은 변수
4. Number - 64비트 실수. 정수 표기 따로 없음. 기타 NaN, 무한대 표현이 있다.
5. String - 문자열, 변경 불가능(immutable)임.
6. Symbol (ECMAScript 6) - 일단 스킵..




객체 (참조 타입)

- 객체 - 식별자로 참조할 수 있는 메모리에 있는 값.
1. Object - 키와 값의 매핑.
2. Array - 일련의 값들을 표현하기 위한 오브젝트
3. Function - 일반 오브젝트에서 호출 가능한 특성을 추가한 오브젝트.
4. Dates, 기타 잡 컬렉션 - 이것도 스킵..

특징은 함수를 걍 객체처럼 다룰 수 있다는 것이다. 함수를 함수의 파라미터로 전달하거나 반환받을 수 있고, 변수에 저장할 수도 있다. 여기에서 오는 편리함이 상당한데 그래서인지  자바스크립트에선 함수의 파라미터로 함수를 지정한다든가 하는 콜백 형태를 많이 볼 수 있다.


동기 vs 비동기 (Synchronous vs Asynchronous)


동기 : 순차 처리 방식. 동기 방식으로 동작하는 API를 호출했다면 작업이 끝날 때까지 기다려야 한다.
비동기 : 일종의 위임 모델인데 일을 던져놓고, 일이 끝나면 통지를 받는 방식.

기본적으로 node.js 시스템은 비동기 방식으로 동작한다.
* 처리 결과에 대한 통지는 어떻게 받는가? 앞서 배운 콜백 형태로 결과를 통지 받는다.
node.js가 제공하는 기본적인 API는 파라미터로 콜백을 전달받는 형태의 비동기 방식으로 정의되어 있는 것을 볼 수 있다.

비동기 버전의 파일 읽기.
fs.readFile('sample.txt', { encoding: 'utf8' }, (err, data) => { console.log(data); });

동기 버전의 파일 읽기.
var data = fs.readFileSync('text.txt', { encoding: 'utf8' });
// sample.txt를 읽는데 10분이 걸린다고 하면 10분간 blocking 된다. 다중 사용자의 요청을 처리하는 웹서버에 이런 로직이 넣으면.. 음 쫓겨난다.


Express (expressjs.com/ko/)

"Node.js를 위한 빠르고 개방적인 간결한 웹 프레임워크"

설치 (expressjs.com/ko/starter/installing.html)

ㄴ npm init // 프로젝트 설정
ㄴ npm install express --save // 프로젝트에 express 모듈 종속성 추가.


간단한 웹애플리케이션 만들기

앱 루트의 GET 메소드에 대한 라우팅을 다음과 같이 지정할 수 있다.
라우팅은 애플리케이션의 엔드 포인트(URI)가 클라이언트 요청에 응답하는 방식을 나타낸다.



3/28(수) :: 10강 '연결성' 부터 11강 'Express-정적파일을 서비스하는 법' 까지.


정적 파일을 서비스하는 법

Express가 제공하는 static 미들웨어를 사용하면 된다. static 미들웨어는 Express 기반 애플리케이션의 정적 자산을 제공하는 역할을 한다.

express.static(root, [options])
root; 정적 자산을 제공할 루트 디렉토리를 지정한다.
options; express.static 미들웨어가 제공하는 옵션을 지정할 수 있다. (etag, extentions 등)

하나의 앱은 2개 이상의 정적 디렉토리를 가질 수 있는데 그냥 디렉토리 목록을 순차적으로 선언해주면 된다.
app.use(express.static('public'));
app.use(express.static('uploads'));

미들웨어에 대해서 좀 더 알아보면. 미들웨어는 요청-응답 과정에서 공통적으로 수행될 수 잇는 로직을 수행하기 위해 필요한 개념으로 보면 될 것 같다. Java의 어쩌구저쩌구 Filter와 비슷한 개념인데. 동일하게 Chaining 형태로 구성되기 때문에 선언한 순서대로 동작한다. 따라서 미들웨어를 'use'하는 순서가 중요할 수 있다.

Express에서는 logger, json, urlencoded, bodyParser, cookieParser등 기본적인 웹 애플리케이션을 만들 때 필요한 대부분의 기능을 제공한다. 물론 사용자가 미들웨어를 작성할 수 있다. 주의할 부분은 미들웨어의 끝엔 next()를 반드시 호출해주어야 한다는 점이다. next()를 호출해서 그 다음 실행될 미들웨어 함수에 제어권을 전달해야 하기 때문인데, 그렇게 하지 않으면 사용자의 요청은 정지된 채로 방치(hang)된다.

아래는 요청 시간을 기억하게 해주는 미들웨어 함수 예제이다.
var requestTime = function (req, res, next) {
    req.requestTime = Date.now();
    next(); // next naming은 관례.
};

app.use(requestTime);

app.get('/', function (req, res) {
    // req.requestTime 프로퍼티를 사용할 수 있다.
});




2018년 3월 8일 목요일

Java GC 튜닝

GC? 프로그램은 동작 과정에서 데이터를 메모리에 저장하게 된다. 저장할 데이터가 생기면 메모리의 일정 공간을 할당받아 사용하는 것이다. 메모리는 한정적이므로 저장하고자 하는 모든 데이터를 메모리에 기록할 수 없다. 따라서 더 이상 사용(참조)되지 않는 데이터가 있다면 사용하던 메모리 공간을 회수하는 과정이 필요하다. 사용되지 않는 메모리에 대한 회수를 Garbage Collection이라고 하고, Garbage Collector(이하 GC)가 이러한 작업을 수행한다.

Java에선 개발자가 할당한 메모리를 명시적으로 해제하지 않기 때문에 GC가 이를 대신한다. GC는 더 이상 필요없는 객체를 찾아 지우는 작업을 수행하는 것인데, Java엔 이 작업을 효율적으로 동작시키기 위한 여러 장치와 방법이 존재한다.

GC가 동작하면 JVM에 의해 애플리케이션의 실행이 잠시 중단된다. 이를 stop-the-world라 하는데 이 시간이 길어지면 그만큼 애플리케이션의 성능에 안좋은 영향을 준다. GC 튜닝이란 바로 stop-the-world 시간을 줄이는 과정이다. 우선 JVM 메모리 구조에 대해 알 필요가 있을 것이다.

//

# Java Application의 동작 방식과 JVM 메모리 레이아웃.
Java Compiler는 *.java 코드를 *.class(ByteCode)로 변환한다. 컴파일된 *.class 파일의 정보는 Class Loader에 의해 JVM Memory 공간으로 옮겨지게 되는데, 이 공간을 Runtime Data Area라 한다. 마지막으로 Runtime Data Area에 적재된 ByteCode는 Execution Engine에 의해 기계어로 변환되어 실행된다.

Runtime Data Area는 목적에 따라 5개의 영역으로 나뉜다. 1) PC Register, 2) Native Method Stack, 3) JVM Stack, 4) Heap, 5) Method Area로 1~3은 스레드 마다 할당되며 4~5는 모든 스레드가 공유한다.
1) PC Register: OS의 Register와 유사한 것으로 현재 수행중인 JVM 명령의 주소를 기억한다.
2) Native Method Stack: 자바 외 언어로 작성된 네이티브 코드를 위한 스택이다.
3) JVM Stack: 스레드의 제어를 위해 사용되는 스레드의 메서드 실행에 대한 스택 프레임이다.

4) Heap: 인스턴스 또는 객체를 저장하는 공간. GC 대상이다.
5) Method Area: JVM이 시작될 때 생성되는 공간으로 JVM이 읽어들인 클래스, 인터페이스에 대한 상수, 필드 및 메서드, static 변수, 메서드의 바이트 코드 등을 보관한다. Permanent Area 또는 PermGen이라 불리기도 하고 GC 대상 여부는 선택사항이다. (* Java 8 부터는 metaspace라는 이름으로 불린다.)

//

# 기본적인 GC의 동작 방식.
HotSpot VM은 Young 영역과 Old 영역으로 물리적 공간을 구분해서 객체를 관리한다. 이는 생성된지 얼마 안되는 객체와 생성된지 오래된 객체를 구분함으로써 GC를 효율적으로 처리하기 위함이다. Young 영역에서 발생하는 GC를 Minor GC라 하고, Old 영역에서 발생하는 GC를 Major GC라 한다.

Young 영역은 다시 Eden 영역과 2개의 Survivor 영역으로 나뉜다. 새로 생성된 객체는 Eden 영역에 위치하고, Minor GC 후 살아남으면 Survivor 영역 중 하나로 이동한다. 하나의 Survivor 영역이 가득차면 그 중에서 또다시 살아남은 객체를 다른 Survivor 영역으로 이동하고, 이러한 과정이 반복하다 또다시 살아남은 객체는 Old 영역으로 이동하게 된다.

Old 영역은 기본적으로 Young 영역보다 큰 공간으로 잡힌다. Old 영역은 영역이 곽 찰 때 GC를 실행한다. (Major GC). GC엔 여러가지 알고리즘이 있다.

1. Serial GC (-XX:+UseSerialGC) : Mark - Sweep - Compaction 단계를 거친다. 적은 메모리와 코어 개수가 적을 때 쓴다.
2. Parallel GC (-XX:+UseParallelGC)  : Serial GC의 멀티코어 버전. Young Gen에서의 작업을 병렬로 처리한다. Throughput GC라고도 하며 메모리가 충분하고 코어수가 많은 경우에 유리하다. Parallel GC는 Java 7과 Java 8의 기본 GC 수행 방식이다.
3. Parallel Old GC (-XX:+UseParallelOldGC) : Mark - Summary - Compaction 단계를 거친다. Compaction 단계 이전에 Summary 단계를 거치는데, GC 이후의 메모리를 인덱싱한 후 compact 작업을 수행한다. 공간에 대한 인덱싱 작업 때문에 Parallel GC에 비해 메모리를 더 사용하게 된다.
4. CMS GC (-XX:+UseConcMarkSweepGC) : Concurrent Mark and Sweep. 애플리케이션의 응답 속도가 중요할 때 주로 이용되며 Low Latency GC라고도 부른다. stop-the-world 시간이 짧다는 장점이 있지만 메모리와 CPU를 더 많이 쓰는 단점이 있다. Compaction 단계가 기본적으로 제공되지 않아 메모리 조각이 많이 생길 수 있다. 이로 인해 Full GC가 자주 발생하는 등의 안정성 문제가 있다.
5. G1 GC : Garbage First. G1 GC는 메모리 공간을 region이란 이름의 고정 크기로 나눈다. region이 Eden이거나 Survivor, Old 영역일 수 있다. 앞서 Young / Old 컨셉관 조금 다르게 동작하며 구조적으로 Parallel, Concurrent 하게 동작한다. (참고: http://initproc.tistory.com/50)

+ Parallel GC의 경우 CMS에 비해 안정성이 높다고 알려져있다. CMS 알고리즘의 경우 Compaction 단계가 없기 때문에 메모리 단편화에 따른 Full GC 발생 등의 문제가 생길 수 있다. (CPU 자원도 꽤 필요하다고 함.) G1의 경우 Xmx 2G 이하에서는 오버헤드가 발생할 수 있으므로 Xmx 2G 이상의 환경에서 사용하도록 하자.

2018년 3월 7일 수요일

Elasticsearch

Elasticsearch는 엘라스틱의 대표 기술이다. Elasticsearch는 아파치 Lucene을 기반으로 만든 분산 검색엔진으로, 태생이 분산시스템이기에 서버 확장이 간편하다. (Elasticsearch는 '샤이 배넌' 이라는 개발자가 만들었는데, 셰프가 되려는 아내를 위해 요리법 검색 도구를 만들어 줄라다 보니 괜찮은 것 같아서 창업을 했다고 함. ㄷㄷ.)

보통 Logstash와 Kibana를 같이 쓰기 때문에 ELK라 하는데 최근엔 Beats등 제품군이 늘어남에 따라 그냥 Elastic stack이라 하기도 한다.

//

Elasticsearch는 기존의 RDB와 자주 비교되는데 가장 큰 특징은 역-인덱스(Inverted Index) 구조로 데이터를 관리하는 부분이다. 기술 서적의 목차를 바라보면 개념에 대한 이해가 빠른데,
기존의 Index가 토픽 기준으로 정리한 책 앞부분의 목차와 같은 구조라면 역-인덱스는 단어(Term) 기준으로 정리한 책 뒷 부분의 색인과 같은 구조를 가진다. 단어를 기준으로 색인을 만들게 되면 영단어의 경우 대소문자, 복수형/단수형 등 세밀한 처리가 필요하게 된다. 이러한 처리를 위해 Elasticsearch는 Analyzer(Filter, Tokenizer, ..) 같은 장치가 마련되어 있다. (한글은 지원을 안하지만 '은전한닢' 프로젝트에서 플러그인을 제공하는 것 같다.)

//

개념 잡기엔 inflearn 무료강의나 공식 홈페이지 튜토리얼이 좋다. 문제는 운영 측면에서, 클러스터 구축이나 메모리, 캐싱 설정 등등 경험해보지 않으면 알 수 없는 미지의 영역이 있는듯 하다. 작게 일을 벌여보기엔 굉장히 쉽고, 크게 일을 벌여보기엔 꽤나 어려운듯..

운영 관련해서,, 엄청 낮은 버전에서의 아티클이지만 아래 자료를 읽어볼 만하다.
http://d2.naver.com/helloworld/273788

보통 RDB를 많이 쓰니까 흔히 RDB와 많이 비교된다. 구조(개념) 측면에서
RDB : Database - Table - Row - Column - Schema는
Elasticsearch : Index - Type - Document - Field - Mapping으로 비교될 수 있다.
* 개인적으로 RDB의 'Database'에 대응되는 녀석의 이름을 'Index'라 지은게 좀 아쉽다. 영문으로 작성된 아티클을 읽어내려갈 때 이 'Index'가 이건지 저건지(?) 이해하기가 힘듬..

* Mapping은 따로 지정해주지 않아도 알아서 데이터가 들어가지만 잘못된 형식으로 저장될 수 있기 때문에 시각화 도구인 Kibana 까지 고려해서 사용한다면 Mapping이 있는 것이 좋다.

Elasticsearch는 REST API로 CRUD 작업을 할 수 있다. 메서드 타입으로 동작을 구분하며 각각 GET - Select / PUT - Update / POST - Insert / DELETE - Delete 와 같이 대응된다. Search 작업의 경우 간단한 것은 URI로 표현할 수 있지만 복잡한 구문은 Body에 실어서 수행하게 된다. 'BOOL' 구문과 같이 조금 특이한 구문이 많고 양이 많아 요건 천천히 들여다 볼 예정..

//

Logstash는 Input, Filter, Output을 지정해 특정 데이터 소스에 기록되는 데이터를 Elasticsearch 까지 보내주는 역할을 수행한다. 개인적으론 이쪽이 Elasticsearch에 비해 더 멋진 것 같다. 입력 소스로 표준 입출력이나 파일, 소켓, 등등을 지정할 수 있다. 로그 파일의 경우 파일 롤링까지 인식해서 처리되는듯 하다. Filebeat는 Logstash의 Input 타입이 파일인 경우에 쓰인다. (모든 수집 대상에 Logstash를 설치할 순 없는 노릇이다.)

[참고] csv를 input으로 받는 logstatsh 설정 예시. (출처: inflearn)
















마지막으로 Kibana.
Elasticsearch에 쌓인 데이터를 입맛에 맞게 시각화해 볼 수 있는 엄청 멋진 툴인데, Front-end 코드를 한 줄도 작성하지 않고도 대시보드를 만들어 운영할 수 있다.

2018년 3월 6일 화요일

Back To The Basic - Spring 설정.

설정에 앞서 동작 패턴을 다시 떠올려보자.
a. 사용자가 뭔가를 요청한다.
b. web.xml의 dispatcher servlet이 사용자의 요청을 핸들링한다.
c. servlet-context.xml에서 해당 요청에 대한 컨트롤러를 찾는다.
d. 컨트롤러가 뷰를 반환하든 데이터를 넘기든 요청을 처리한다.

우리가 지정해주어야 하는 건 결국에 서버로 유입되는 요청을 어떤 방식으로 처리할 것인가에 대한 설정일 뿐이다. 그런데 이게 꽤 복잡한 것이 함정.. 이미 만들어진 Spring 프로젝트의 설정을 바라보면 고개가 끄덕여지는데 막상 새로 시작하려면 골칫거리다.

..

왜인진 모르겠는데 아래 문구로 검색하면 대충 기본 설정 파일을 찾을 수 있다.
'The definition of the Root Spring Container shared by all Servlets and Filters'
막상 여느 프로젝트를 들여다봐도 대부분의 설정이 비슷하다. 어느 정도 규격이 있다는 뜻.
Spring Boot 같은게 그래서 나왔겠지.

Spring 3.X에서 annotation으로 설정을 가져갈 것인지, xml로 가져갈 것인지는 개발팀 취향인데 이러나 저러나 큰 문제는 없어 보인다.
예를 들어 아래의 xml 설정은
<mvc:annotation-driven>
  <mvc:argument-resolvers> ...
  <mvc:message-converters> ...

아래의 annotation으로 같은 표현을 할 수 있다.
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
  @Override public void addArgumentResolvers ...
  @Override public void configureMessageConverters ...

  // 인스턴스 생성은 @Bean으로.

..

큰 맥락에서 설정파일은 크게 3개+ 로 유지된다.

1. web.xml : 웹 프로젝트의 환경설정 파일. context configuration, dispatcher servlet location, filter 등이 들어간다.
2. root-context.xml : 스프링의 환경설정 쯤으로 봐도 무방하다.
3. servlet-context.xml : 스프링에 내장된 DispatcherServlet이 참조하게 되는 설정 파일. Spring Data/JPA 설정이라던가 비즈니스, 서비스에 대한 설정 등이 포함된다.

물론 위 xml은 모두 없애고 annotation 기반 설정으로 프로젝트를 진행할 수도 있다. 관련 아티클은 아래 링크에 잘 정리되어 있다.
http://www.robinhowlett.com/blog/2013/02/13/spring-app-migration-from-xml-to-java-based-config/
https://www.slipp.net/wiki/pages/viewpage.action?pageId=22282242

아래 두갠 걍 걸어둠.
https://idodevjobs.wordpress.com/2014/07/12/spring-data-rest-xml-configuration-example/
https://idodevjobs.wordpress.com/2014/07/22/spring-data-rest-java-configuration-example/

2018년 3월 4일 일요일

RESTful - Stateless

Stateless.. 서버가 클라이언트의 상태를 저장하지 않는 것.

클라이언트는 매 요청마다 자신을 인증할 수 있는 Token이나 Key 등을 Request에 포함시켜 전송해야 한다. 또는 연속된 작업에서의 상태 정보를 포함시켜야 할 수도 있다.

이렇게 했을 때 가져갈 수 있는 장점은?
Caching, Load balancing, Scale-out.

단점은? 매 요청시마다 상태 정보를 전달해야 하기 때문에 그 만큼의 네트워크 리소스가 소모될 수 있다. (또는 상태 정보 처리에 따른 서버의 부하..)

/

REST architecture(참고 : https://ko.wikipedia.org/wiki/REST)는 stateless 하면서 동시에 어떤 클라이언트 관련정보도 서버에 저장하지 않는 방식을 사용한다. 즉, 상태 정보는 클라이언트가 관리하고 서버는 scale-out 할 수 있게 된다. (클라이언트의 요청을 어느 서버가 처리하던 동일한 결과를 보장할 수 있다.)

만약에, 대규모 환경에서 동일한 웹서버를 다수 배치해 로드밸런싱 하는 경우에 stateful 하다면 각 서버는 클라이언트의 상태(세션)을 공유할 수 있는 Redis와 같은 별도 시스템이 필요하게 된다.

인증을 거치는 서비스가 Stateless 하다면 매번 인증을 위해 ID / Password를 보내야 하나? 그렇게 해도 되지만 대신할 수 있는 몇 가지 기술이 있다.

- HTTP Basic Auth
- HTTP Digest Authentication
- API Key or Access Token
- OAuth

Lombok

Lombok은 getter, setter, toString, equals, hashCode 메서드 등을 자동으로 추가해주는 Annotation 기반 라이브러리이다.

아래 링크에서 다운받을 수 있다.
https://projectlombok.org/download

다운로드 받은 Jar를 실행해서 설치하면 되는데, 설치 IDE를 인스톨러가 찾아준다. 기다리기 귀찮으면 직접 지정해주어도 됨.

설치가 끝났으면.
lombok.jar를 프로젝트에 추가 > IDE를 재시작 해주면 적용된다.

gradle엔 dependencies에 compileOnly('org.projectlombok:lombok:1.16.20')과 같이 지정해주고,

쓰면 된다.

/

주로 쓰게되는 Annotation은 '@Data' 이다.
모든 필드에 getter, setter를 만들어 주고 toString, equals, hashCode 메서드를 만들어 준다.

그 밖에 toString 출력 형식을 조정하거나, 특정 프로퍼티의 getter 또는 setter의 접근 레벨을 제한하거나, 등등의 디테일한 조정을 할 수 있다.

세부적인 기능은 https://projectlombok.org/features/all를 참고할 것.