Data Cleaning and Text Preprocessing

기계가 텍스트를 이해할 수 있도록 텍스트를 정제하고 신호와 소음을 구분하여 아웃라이어 데이터로 인한 오버피팅을 방지하기 위해서는 다음과 같은 처리를 해주어야 한다.

  • HTML 태그, 특수문자, 이모티콘 처리
  • 토근화(Tokenization) : 문장의 단어를 분리하는 단계
  • 불용어(Stopword) 제거 : 자주 등장하지만 특별한 의미를 갖지 않는 단어 제거
  • 어간 추출(Stemming) 및 음소표기법(Lemmatization)
  • 정규 표현식

텍스트 데이터 전처리 이해

정규화 normalization (입니닼ㅋㅋ -> 입니다 ㅋㅋ, 샤릉해 -> 사랑해)
한국어를 처리하는 예시입니닼ㅋㅋㅋㅋㅋ -> 한국어를 처리하는 예시입니다 ㅋㅋ

토큰화 tokenization
한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어Noun, 를Josa, 처리Noun, 하는Verb, 예시Noun, 입Adjective, 니다Eomi ㅋㅋKoreanParticle

어근화 stemming (입니다 -> 이다)
한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어Noun, 를Josa, 처리Noun, 하다Verb, 예시Noun, 이다Adjective, ㅋㅋKoreanParticle

어근 추출 phrase extraction
한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어, 처리, 예시, 처리하는 예시

(출처 : 트위터 한국어 형태소 분석기)

BeautifulSoup

많이 쓰이는 파이썬용 파서로 html, xml을 파싱할때 주로 많이 사용한다.

BeautifulSoup에는 기본적으로 파이썬 표준 라이브러리인 html파서를 지원하지만, lxml을 사용하면 성능 향상이 있다.

BeautifulSoup(markup, '[파서명]')

  • html.parser : 빠르지만 유연하지 않기 때문에 단순한 html문서에 사용
  • lxml : 매우 빠르고 유연
  • xml : xml 파일에 사용
  • html5lib : 복접한 구조의 html에 대해서 사용.(속도가 느린편)
from bs4 import BeautifulSoup
import re

data = "<p>초등학교 입학을 축하합니다.~~~^^;<br/></p>"
soup = BeautifulSoup(data, "html5lib")
remove_tag = soup.get_text()
result_text = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…》;]', ''
              , remove_tag)
print(result_text)

결과 : 초등학교 입학을 축하합니다.

<p>초등학교 입학을 축하합니다.~~~^^;<br/></p> 문장에서 태그와 특수문자를 제거하기 위해 BeautifulSoup 와 정규표현식을 사용하였다.

토큰화 (Tokenization)

코퍼스(corpus)에서 토큰(token)이라 불리는 단위로 나누는 작업을 토큰화(Tokenization)라고 부른다. 토큰의 단위가 상황에 따라 다르지만, 보통 의미있는 단위로 토큰 정의한다.

토큰의 기준을 단어로 하는 경우 가장 간단한 방법은 띄어쓰기를 기준으로 자르는 것이다.

I loved you. machine learning 에서 구두점을 제외시키고 토큰화 한 결과는 다음과 같다.

"I", "loved", "you", "machine", "learning"

하지만 보통 토큰화 작업은 단순히 구두점이나 특수문자를 전부 제거하는 작업을 수행하는 것만으로 해결되지 않는다. 구두점이나 특수문자를 전부 제거하면 토큰이 의미를 잃어 버리는 경우가 발생하기도 하기때문이다. 띄어쓰기 단위로 자르면 사실상 단어 토큰이 구분되는 영어와 달리, 한국어는 띄어쓰기반으로는 단어 토큰을 구분하기 어렵다.

한국어는 단어의 다양한 의미와 높낮이 그리고 띄어쓰기가 어렵다 보니 잘못된 데이터를 많이 받게 되어 자연어처리를 하는데 있어 어려움이 많이 따른다. 하지만 다양한 곳에서 한국어 처리를 위한 형태소 분석기를 연구하고 있다. 얼마전 카카오에서도 카이라는 딥러닝 기술 기반의 형태소 분석기를 오픈소스로 공개 하였고 그외에 트위터, 코엔엘파이등 꽤 쓸만한 것들이 있다.

불용어 (Stopword)

일반적으로 코퍼스에서 자주 나타나는 단어로 학습이나 예측 프로세스에 실제로 기여를 하지 않는다.

(조사, 접미사 - 나,나,은,는,이,가,하다,합니다….등)

NLTK에는 17개의 언어에 대해 불용어가 정의되어 있다. 하지만 아쉽게도 한국어는…없다.

간단하게 10개 정도만 영어의 불용어를 보면,

import nltk
from nltk.corpus import stopwords

nltk.download('stopwords')
stopwords.words('english')[:10]

결과 : [‘i’, ‘me’, ‘my’, ‘myself’, ‘we’, ‘our’, ‘ours’, ‘ourselves’, ‘you’, ‘your’]

간단하다, 불용어 사전에 있는 단어들은 제거하면 된다.

한국어의 경우 불용어를 제거하는 방법으로는 위에서 언급한 형태소 분석 후 조사, 접속사 등을 제거할 수 있다.

어간 추출 (Stemming)

스태밍이라고 하는데, 어간이란 말은 단어의 의미를 담고 있는 단어의 핵심부분이라 생각하면 된다. 쉽게, 단어를 축약형으로 바꿔주는 작업이라고도 할 수 있다.

한국어가, 한국어는, 한국어처럼 -> 한국어

대표적으로 포터 스태머(PorterStemmer)와 랭커스터 스태머(LancasterStemmer)가 있는데 포터는 보수적이고 랭커스터는 좀 더 적극적이다.

PorterStemmer

from nltk.stem.lancaster import LancasterStemmer
stemmer = LancasterStemmer()
print(stemmer.stem('maximum'))
print("running >> {}".format(stemmer.stem("running")))
print("runs >> {}".format(stemmer.stem("runs")))
print("run >> {}".format(stemmer.stem("run")))

maxim
running » run
runs » run
run » run

LancasterStemmer

from nltk.stem.lancaster import LancasterStemmer
lancaster_stemmer = LancasterStemmer()
print(lancaster_stemmer.stem('maximum'))
print("running >> {}".format(lancaster_stemmer.stem("running")))
print("runs >> {}".format(lancaster_stemmer.stem("runs")))
print("run >> {}".format(lancaster_stemmer.stem("run")))

maxim
running » run
runs » run
run » run

음소표기법 (Lemmatization)

언어학에서 음소표기법 (Lemmatization)은 단어의 보조 정리 또는 사전 형식에 의해 식별되는 단일 항목으로 분석 될 수 있도록 굴절 된 형태의 단어를 그룹화하는 과정이다. 어간 추출(Stemming)과는 달리 단어의 형태가 적절히 보존되는 양상을 보이는 특징이 있다. 하지만 그럼에도 의미를 알 수 없는 적절하지 못한 단어로 변환 하기도 하는데 음소표기법(Lemmatizer)은 본래 단어의 품사 정보를 알아야만 정확한 결과를 얻을 수 있기 때문이다.

  • 품사정보가 보존된 형태의 기본형으로 변환.
  • 단어가 명사로 쓰였는지 동사로 쓰였는지에 따라 적합한 의미를 갖도록 추출하는 것.
from nltk.stem import WordNetLemmatizer
n=WordNetLemmatizer()
words=['have', 'going', 'love', 'lives', 'fly', 'dies', 'has', 'starting']
[n.lemmatize(w) for w in words]

결과 : [‘have’, ‘going’, ‘love’, ‘life’, ‘fly’, ‘dy’, ‘ha’, ‘starting’]

결과에서 보면 알수 있듯이 dy나 ha는 그 의미를 알수 없는 적절하지 못한 단어로 변환이 되었다.

하지만 dies나 has가 동사로 쓰였다는 것을 알려준다면 좀더 정확한 결과를 얻을 수 있게 된다.

n.lemmatize('dies', 'v')

‘die’

n.lemmatize('has', 'v')

‘have’

음소표기법은 문맥을 고려하며, 수행했을 때의 결과는 해당 단어의 품사 정보를 보존한다. 하지만 어간 추출은 품사 정보가 보존이 되지 않는다.

마치며..

이런 작업들을 하는 이유는 눈으로 봤을 때는 서로 다른 단어들이지만, 하나의 단어로 일반화시킬 수 있다면 하나의 단어로 일반화시켜서 문서 내의 단어 수를 줄여보자는 것이다.

자연어 처리에서 전처리의 지향점은 언제나 갖고 있는 코퍼스로부터 복잡성을 줄이는 일이다!!!

'Article' 카테고리의 다른 글

Google AMP 개요 편  (0) 2019.05.30
Docker : Docker Compose 편  (0) 2019.04.05
자연어처리 - 데이터 정제  (0) 2019.03.22
REST API 디자인 가이드 적용기  (0) 2019.03.08
자연어처리 - Bag of words, n-gram  (0) 2019.03.08
The Scale Cube (규모 확장성 모델)  (0) 2019.02.22
Posted by @위너스

댓글을 달아 주세요

아주 작은 시스템을 개발할 때는 API들이 약간 엉켜 있어도 
문제가 생겼을때 원인을 찾는데 어렵지 않을 것이다.
하지만 시스템이 커지고 복잡 하다면, 서로의 인터페이스를 잘 정돈하고 관리하는 것이 중요해진다.

구글이나 페이스북 등 큰 기업들은 잘 정돈하고자 그들만의 REST API 가이드라인을 가지고 있다.

나는 여러 개발자들과 함께 REST API 개발을 담당하게 되었다.
그래서, 구체적인 개발을 시작 하기 전에 사내 REST API 디자인 가이드를 만들어야겠다고 마음을 먹었다.

가이드라인을 만들고 이것을 참고해 반년 정도 개발-운영을 했다.
처음 가이드라인을 만들때 했던 고민과 챙겨야할 것들, 
그리고 가이드라인과 함께 개발하면서 느낀점들을 정리해보고자 한다.


REST API를 쓰는 이유

REST API 에 대한 설명은 인터넷에 많다보니 생략하려고 한다.

  REST

  API

핵심은 표현에 대한 약속

원하는 결과는 얻을 수 있는 "표현에 대한 약속"은 종류가 많을 것이다. 
하지만 이것들은 좋은 약속도 있고 안좋은 약속도 있다.
안좋은 약속은 당장에 동작에 문제가 없더라도, 
나중에 가면 유지보수가 힘들어지거나 구조를 더 꼬이게 만드는 것들을 말한다.
좋은 약속에 해당하는 "표현에 대한 약속"들을 모아.. RESTful API 라고 부르고 있다고, 나는 생각한다.
  


디자인가이드의 필요성


협업
여러 개발자들이 여러가지 각자의 자원에 대해 개발을 할 때 필요하다. 
개발자가 많고 자원이 많은 상황에서 일일이 해당 자원의 개발자를 찾아가 API를 이해하는것은 시간 낭비가 크지만, 가이드라인에 따라 개발 되었다면 이 낭비를 줄일 수 있다.


개발 효율
위의 협업과 비슷하다. 자원에 대한 통일된 접근 방식은 개발 효율 을 높인다.
API를 만드는 사람 입장에서 가이드라인에 맞게만 만들면 되기 때문에 고민할 필요가 적어진다.
API를 사용하는 사람 입장에서도 동일하게 효율적으로 API를 이해할 수 있게 되어 좋다.


기능 단순화
API에서 표현해야 하는 자원의 범위는 중요한 문제다. 가이드라인에는 자원의 범위에 대한 내용을 다루어야 하고, 개발자들도 이것에 맞추어 개발해야 효율성이 높아진다.

간혹 자원에 대한 범위나 접근 방법을 정하기 애매할 때가 있다. 
나도 모르게 여러가지 자원을 한번에 묶어서 처리하거나,
필요 이상으로 여러가지 기능을 한번에 묶어서 처리하는 식으로 개발하게 될 수 있다.
이렇게 되면 API(ex. 엔드포인트)는 기능에 의존도가 높아져서 다양한 용도로 활용이 어렵게 된다.

예를 들어 사용자정보, 사용자부가정보 라는 자원이 있다고 하자. 
이것을 1가지 자원으로 묶어서 표현을 하게되면, 사용자정보만 필요한 상황이 왔을때 이 자원은 사용자정보+사용자부가정보 로 묶여 있어서 필요하지 않은 자원도 가져가야 한다.

API 범위가 크면 사용할 필요 없는 자원도 가져가야 할 경우가 많아질 것이다. 
그렇다고 범위가 너무 작으면 API 호출이 많아 지게 된다.

MSA 구조 관련된 이슈로 볼 수도 있다. 
경험상 가급적 자원 범위를 작게 유지하는 편이 나중에 추가기능이 필요할 때 더 유연하게 대처할수 있다.
그래서 디자인가이드에 이런 내용을 담아 약속하면 좋다.

 

디자인가이드 필수요소

나는 구글의 가이드라인을 많이 참조했다. 

바퀴를 다시 발명하고 싶은게 아니라면..앞서간 개발자들의 지식을 누리는건 좋은일이다.
시작부터 잘못되면 나중에 이도저도 안되서 후회할 수도 있다.

구글의 가이드라인은 양이 방대하다. 때문에 그대로 적용 했다가는 구성원들에게 버림 받을 수 있다.
나는 적당한 선에서 타협을 하고 개발하면서 규칙을 추가해 나가기로 정하였다.

아래는 REST API 가이드라인을 만들면서 처음에 정했던 규칙들이다.
 

URL 규칙

URL을 만들때 아래 규칙에 따라 만든다.

https://[서버주소]/[버전]/[자원명]/[ID]

https://localhost/v1.0/articles/1
  • 자원명은 반드시 명사로 표시해야 한다. 
  • 자원명은 복수로 표시해야 한다.

게시판에 대한 자원명 이라면 "게시물들(articles)" 로 정해야한다.
자원명은 자원에 대한 복수 명사 가 되어야 한다.

아래 예시들처럼 어떠한 행동이나 조건을 포함하면 안된다.
"게시판조회하기(boardView)","게시판최근게시물(boardNewView)","게시판모아보기(boardAllView)"

  • 페이징 처리가 필요할 때는 offset 과 limit 을 사용한다
/articles?offset=0&limit=20

  • 계산, 변환 등 어쩔수 없이 URL 에 동사를 사용해야 하는 케이스는 자원명 뒤에 콜론(:)을 넣고 뒤에 동사를 붙인다.
/articles:backup


Http Method 기능 정의

주로 사용하는 Http Method 는 GET, POST, PUT, DELETE 4가지가 있다.
앞에서 정한 URL(자원명)에 이 4가지 행동의 조합으로 모든 요청을 받아서 처리해야 한다.

  • POST : 새로운 자원 생성
  • GET : 자원의 목록 혹은 단건 가져오기
  • PUT : 기존의 자원 업데이트
  • DELETE : 기존의 자원 삭제


버전 관리

버전은 v[메이저].[마이너] 로 구성한다.

  • 메이저 : API의 변경이 이전 버전과 호환이 되지 않는 수준의 변경
  • 마이너 : 호환 가능한 수준의 변경

이 규칙들과 함께 요청(request)의 표준 표현, 응답(response)의 표준 표현, 에러응답의 표준 표현을 정하고 추가해서 우리 조직만의 REST API 가이드라인을 만들어 사용하였다.



디자인 가이드 운영하며 느낀점

가이드라인을 처음 만드는 것은 그리 어려운 일은 아니었다.
제일 어려운점은 계속해서 가이드라인을 지키는것이다.

가이드라인이 처음 만들었을 때는 잘 지켜나갔다. 
하지만 시간이 지나고 일정이 바빠질 때가 위험한 순간이다. 
이럴때 가이드라인 만으로 해결할 수 없는 예외가 생기면, 
가이드라인을 조금 벗어나더라도 일단은 동작하는 방향으로 개발을 완료하는 경향이 생겨났다.
  
예외가 생겼다면 2가지 케이스중 하나일 것이다.
  • 가이드라인을 잘못 이해해서 생긴 예외
  • 기존 가이드라인에서 해결할수 없어서 추가 규칙이 필요한 예외

잘못 이해한것이라면 좀 더 고민해서 자원을 나누고 표현방법을 바꾸면 될것이다.
규칙의 추가나 변경이 필요하다면, 구성원들과 충분히 논의하고 결정하면 된다.
  
이 두가지 과정이 꼭 필요하다.

처음부터 완벽한 가이드라인은 있을 수 없다. 
또한 조직이나 업무에 따라 필요한 가이드라인은 다를 것이다.

그렇기 때문에..
계속해서 의문을 제기하고, 구성원간의 논의-결정들이 가이드라인에 쌓이는 것이 중요하다.
그래야 좋은 가이드라인을 만들수 있고, 시스템의 품질을 올리는데 도움이 될 거라고 생각한다.
  
제일 중요한것은 구성원 모두가 이해하고, 도움이 된다는 확신과 개선해 나가려고 하는 의지라고 생각한다.

하지만 역시 쉽지 않은 일이다. 
잊혀지지 않으려고 주기적으로 강조를 해야했고 앞으로도 계속 그래야 할 것으로 보인다.


Posted by panpid

댓글을 달아 주세요

자연어 처리(natural language processing)는 인간의 언어 현상을 기계적으로 분석해서 컴퓨터가 이해할 수 있는 형태로 만드는 자연 언어 이해 혹은 그러한 형태를 다시 인간이 이해할 수 있는 언어로 표현하는 제반 기술을 의미한다. (위키피디아)

간단하게 말하면, 자연어의 의미를 분석하여 컴퓨터가 처리할 수 있도록 하는 일 이라고 생각하면 될 것 같다.

텍스트

기계학습 모델을 만들기 위해서는 데이터를 모델에 맞게 변형시켜 주어야 한다. 알고리즘에서 텍스트를 그대로 받아들일수 없기 때문에 받아들일 수 있는 어떤 숫자값으로 변환을 해주어야 한다. 하지만 텍스트는 일단 언어가 제각기 다르기 떄문에 텍스트 자체를 어떻게 숫자화 할지 부터 시작해야한다.

그럼 어떤 방법들이 있는지 살펴보자.

BOW(bag of words)


BOW(Bag of words)는 텍스트 데이터를 표현하는 방법 중 하나로 가장 간단하지만 효과적이라 기계학습에서 널리 쓰이는 방법이다. BOW는 텍스트의 구조와 상관없이 단어들을 담는 가방(Bag)으로 생각하면 된다.

The game is fun
The game is interesting
The game is not funny

세 문장에서 나타나는 단아들을 모으고 세 문장을 각각 binary vector로 표현하는 것이 BOW이다. 각각의 문장을 binary vector로 표현하면 다음과 같다.


thegameisfuninterestingnotfunny
1111000

The game is fun : [1, 1, 1, 1, 0, 0, 0]

thegameisfuninterestingnotfunny
1110100

The game is interesting : [1, 1, 1, 0, 1, 0, 0]

thegameisfuninterestingnotfunny
1110011

The game is not funny : [1, 1, 1, 0, 0, 1, 1]

만약 이 가방에 들어있지 않는 단어가 포함된 문장이 있어도 BOW는 그 없는 단어는 제외하고 있는 단어만을 가지고 벡터로 만들 것이다.

그럼 이 수치로 표현된 값을 어떻게 활용할까?

머신러닝 모델에 입력값으로 사용할 수도 있디만 단순히 계산만으로 문장간의 유사도(Sentence similarity)를 알 수도 있다.

the game is fun 과 The game is interesting 문장의 유사도를 구해보자.


문장벡터값
the game is fun[111, 1, 0, 0, 0]
The game is interesting[111, 0, 1, 0, 0]

유사도 = (1x1) + (1x1) + (1x1) + (1x0) + (0x1) + (0x0) + (0x0) = 3

또한 수치로 표한된 값을 이용해 기계학습 모델에 입력값으로 사용할 수가 있다. 문장에 대한 감성분석을 해주는 모델이 있다면, 벡터화된 값을 모델에 입력값으로 사용할 수 있고 모델은 우리가 원하는 Good 또는 Bad의 결과를 출력해 줄 수 있다.



하지만 BOW는 몇 가지 단점이 있다.

  • Sparsity
    실제 사전에는 100만개가 넘는 단어들이 있을 수도 있다. 그렇게 되면 벡터의 차원이 100만개가 넘어가기 때문에 실제 문장하나를 표현할 때 대부분의 값이 0이고 그외의 값들은 상당히 적을 것이다. 결국 학습량이 많아지고 컴퓨터 자원도 상당히 많이 사용하게 된다.
    (the game is fun [1,1,1,1,0,0,0,0,0,0,0,,,,,,0,0,0,0,0])

  • 빈번한 단어는 더 많은 힘을 가진다.
    많이 출현한 단어는 힘이 세진다. 만약 의미없는 단어들이 많이 사용 되었다면 우리가 원하는 결과를 얻기는 어려울 것이다.

  • Out of vocabulary
    오타, 줄임말 등의 단어들이 포함되면 굉장히 난감해진다.^^;

  • 단어의 순서가 무시됨
    단어의 출현 횟수만 셀수 있고 단어의 순서는 완전히 무시 된다. 단어의 순서가 무시된다는 것은 다른 의미를 가진 문장이 동일한 결과로 해석될 수 있다는 것이다.

전혀 반대의 의미를 가진 두 문장을 보자.



두 문장은 의미가 전혀 반대이지만 BOW를 이용해 처리한다면, 동일한 결과를 반환하게 될 것이다. 이런 단점을 보완하기 위해 좀더 개선된 n-gram이란 것이 있다. BOW는 하나의 토큰을 사용하지만 n-gram은 n개의 토큰을 사용하여 어느정도 단어의 순서를 반영 결과에 반영해 준다.

N-Gram

BOW를 조금 더 개선하여 단어 하나만을 보는 것이 아니라 주변의 n개 단어를 뭉쳐서 보는 것이다. 뭉쳐진 n개의 단어들을 gram이라고 한다. 단어 개수에 따라 부르는 명칭이 다른데 2개의 단어를 묶어서 사용하면 bi-gram, 3개면 tri-gram이라고 부른다. (1-gram은 uni-gram이라고 한다.)


“home run” 과 “run home” 문장을 bow와 bi-gram으로 처리한 결과를 보자.

bag of words : [home, run] , [run, home]
bi-gram : [home run], [run home]

BOW를 사용한다면 두 문장은 같은 백터의 값을 갖게 되겠지만 bi-gram을 사용하면 2개를 뭉쳐서 사용하므로 어느정도의 순서가 보장되는 효과를 볼수 있게 되어 다른 결과 값을 가지게 될 것이다.

이런 특성을 이용해 n-gram은 다음 단어 예측하거나 어떤 단어를 입력 했을때 오타를 발견하고 다른 단어를 추천해 주는데 활용할 수 있다.

텍스트 전처리 (Preprocessing)


BOW나 n-gram이나 모두 많이 쓰이지만 가장 중요한 것은 단어의 전처리가 확실해야 한다는 것이다. 이 글에서는 설명을 위해 간단한 문장만을 사용하여 크게 신경을 쓸 필요는 없겠지만, 자연어 처리를 하다보면 다양한 케이스의 문장들을 접하게 될 것이며 이런 문장들을 토큰화하고 불필요한 단어들은 제거하고 같은 의미의 단어들은 치환하는 등의 고단한 작업 들을 해야 할 것이다. 하지만 다행인 것은 이런 전처리 작업들을 편하게 할 수 있도록 도와주는 좋은 라이브러리들이 있다.


다음 포스팅에서는 전처리에 대해 자세히 살펴보도록 하겠다…

Posted by @위너스

댓글을 달아 주세요

INTRO

일정 규모 이상의 정보 서비스를 제공하고 있다면, 아마도 대부분 한대 이상의 서버를 배치하여 부하분산을 시키고 있을 것입니다. 이런 배치 전략을 로드밸런싱이라고 하죠.


로드밸런싱은 대량의 트래픽을 수용하기 위해 여러대의 (동일한) 서버가 요청을 나눠서 처리하도록 하는 부하분산을 통해 서비스의 처리량을 증가 시키고자 하는 것이 주 목적입니다.

 

물론 로드분배 알고리즘과 서버 상태를 기반으로 해서 고가용성(HA)의 요건도 같이 충족되는 것이 일반적입니다.


만일 트래픽이 점점 더 늘어난다면, 그에 맞춰 Service #4, Service #5, ... 이런식으로 서비스의 복제본을 늘려 나가기만 하면 되기 때문에 손쉽게 확장이 가능합니다.


애플리케이션의 확장성을 보장하기 위해서는 다양한 전략을 구사할 수 있습니다. 로드밸런싱은 그 중에서도 트래픽의 처리량을 증가시켜서 서비스의 능력을 향상하는 아키텍처 전략에 해당합니다.


이 글에서는 애플리케이션의 확장성 보장을 위한 다양한 전략을 'Scale Cube'라는  규모 확장성 모델에 근거하여 설명하고자 합니다.



확장성

먼저 소프트웨어 분야의 확장성이라는 개념을 먼저 정리하자. 확장성은 쉽게 말해서,

애플리케이션이 얼마나 손쉽게 확장될 수 있는 가에 대한 가능성에 대한 정도로써 애플리케이션의 여러 품질속성 중 하나이다.


확장성이 좋다는 말은, 애플리케이션의 능력을 손쉽게 향상 시킬수 있다는 의미가 된다.

여기서 애플리케이션의 능력이란, 알고리즘이나 로직과 같은 세부적인 기능 측면보다는 성능/가용성/보안과 같은 비기능적인 요소(이를 품질속성이라 함)에 해당하는 능력을 말한다.


'정보통신기술용어해설'에서는 확장성을 다음과 같이 정의하고 있다.

대규모적인 재설계/재설치 등의 필요없이 확장이 얼마나 쉽고 가능한가에 대한 용이성

확장의 대상과 확장 방식에 따라 확장성을 다음과 같이 세분화 할 수 있다.


1) 규모 확장성

일반적으로 모든 확장성은 규모 확장성을 의미한다. 규모란 용어는 양적인 측면의 크기를 지칭하는 용어로써 특정 대상만을 한정하지는 않는다. 예를 들어 트래픽 처리량에 대한 규모, 데이터 보관량에 대한 규모, 기능의 종류와 양에 대한 규모 등 얼마든지 수식이 가능하다.


다만, 여기서의 규모 확장성은 아래에 설명하는 다른 확장성과 구분하기 위해 트래픽 처리량에 대한 확장으로 한정하고자 한다. 

규모 확장성을 보장하기 위해서는 앞서 살펴본 로드밸런싱과 같이, 동일한 기능을 수행하는 애플리케이션의 복사본을 여러대 구성하여 트래픽 부하를 나눠 가지도록 구성한다. 이런 구성을 Scale out(수평 스케일)이라고도 하며 손쉽게 트래픽 처리량을 확장할 수 있어 흔히 사용되는 방법이다.


규모 확장성을 보장하는 로드밸런싱의 구조를 다시 확인해 보자. Service #1~#3은 모두 동일한 복제본으로 동일한 기능을 수행한다.





2) 기능 확장성
애플리케이션의 기능을 얼마나 손쉽게 추가, 수정할 수 있느냐에 대한 정도이다. 다시 말해, 기존 기능을 확장하거나 새기능을 추가하여 애플리케이션의 능력을 향상시킬 수 있는 가능성에 대한 정도로 풀어 쓸 수 있다.


과거 유행했던 SOA(Service Oriented Architecture)와 근래 유행하는 MSA(Micro Service Architecture)가 바로 기능 확장성 보장하는 아키텍처 구조이다.


MSA는 애플리케이션을 기능 및 역할 관점에서 분리하여 개별 서비스로 구성하고 상호 연동을 통해서 전체 애플리케이션을 구성하는 방식으로 애플리케이션의 독립성과 자율성을 보장함으로써 유지보수와 기능 확장을 용이하게 하는 아키텍처 구조이다.

다음 그림은 애플리케이션이 역할 단위로 분리되어 서로 다른 기능을 제공하는 모습을 보여준다.



3) 데이터 확장성

각각의 애플리케이션이 데이터의 일부분만을 책임지도록 하여 처리 효율을 증가시키거나 데이터 규모에 대한 확장성을 확보하는 기법이다. 데이터 샤딩이나 파티셔닝같이 데이터 자체를 분리하여 저장하는 방식으로 많이 활용되며 경우에 따라서는 애플리케이션 단위의 파티셔닝을 통해 각각이 애플리케이션이 서로 다른 데이터 영역을 책임지게 함으로써 데이터의 처리효율과 애플리케이션의 처리 성능을 향상시킬 수 있다.


다음 그림은 서로 다른 데이터가 서로 다른 저장소에 분리되어 저장된 샤딩된 구조를 보여준다.
여기서 각 Application은 모두 동일한 복제본으로 동일한 기능을 실행하지만 책임지는 데이터 영역이 서로 다르다.


예를 들어, 대량의 데이터에 대한 검색 기능을 개발한다고 할 경우, 다음과 같이 검색 API를 두고 뒷단으로 애플리케이션을 파티셔닝해 그 결과를 조합해서 반환하도록 구성할 수도 있다.




The Scale Cube

'THE ART OF SCALABILITY'라는 책에서는 매우 유용한 3가지 차원의 규모 확장성 모델을 제시한다고, 'Chris Richardson'이 서술한 바 있다.

상단의 그림은 Chris Richardson의 The Scale Cube라는 글에서 발췌해온 것이다. Chris Richardson은 해당 글에서 각 축(axis)에 대해서 설명한다. 글이 짧지만 핵심을 잘 전달해 주고 있으니 일독해 보기를 권장한다.


어느 훌륭한 한국분이 이 글을 번역해서 올려 뒀으니 참고 바란다.
스케일 큐브 (크리스 리차드슨)



X축 확장
애플리케이션의 동일한 복제본을 다수로 구성하여 애플리케이션의 처리 능력을 향상시키는 기법이다.
앞서 살펴본 처리량에 대한 확장성 즉, '규모 확장성'이 바로 이 축에 해당한다.


Y축 확장
애플리케이션의 기능을 역할별로 분리하여 서로 독립적인 서비스로 구성하는 기법이다.
앞서 살펴본 '기능 확장성'이 바로 이 축에 해당한다.


Z축 확장
데이터이 일부만을 책임지도록 하여 데이터 저장과 활용 관점에서 애플리케이션의 처리 능력을 향상시키는 기법이다. 앞서 살펴본 '데이터 확장성'이 바로 이 축에 해당한다




확장성 모델 요약

각각의 확장성 모델을 다음과 같이 하나의 표로 정리해 보았다.




실무 환경

실제 실무환경에서는 각각의 확장성 축이 모두 혼합되어 구성되는 것이 일반적이다. 특히 MSA 아키텍처 구조를 표방하는 애플리케이션들은 거의 대부분 아래와 같은 구조로 설계된다. 즉 모든 축의 확장성을 보장하는 형태로 구성하여 극강의(?) 확장성을 확보한다.

[그림출처: 마이크로서비스 아키텍처로 개발하기(안재우)]

Posted by 사용자 박종명

댓글을 달아 주세요