기계에게 어느 구간 까지가 문장이고, 단어인지를 알려주는 구간
- 문장 토큰화( Sentence Tokenization )
- 단어 토큰화( Word Tokenization )
- Sunword 토큰화( 단어의 앞이나 뒤에 오는 접미사나, 접두사를 모아놓은 것 )
- 음절 토큰화
문서( Document ) -> 문단( Paragraph ) -> 문장( Sentence ) -> 단어( Word )
- 대부분 문장과 단어 단위로 수집
- 뉴스( 문서 )를 문단 단위( P )로 수집을 먼저 하고,
- 그 문단을 문장으로 쪼개서 저장
- 그 다음에 딥러닝이나 머신러닝을 하기 전에 word 단위로 짤라서 훈련
- 문서를 바로 문장 단위로 저장하는 경우도 많다. ( 문단을 안 쓰고 )
단순하게 띄어쓰기를 기준으로 구분을 해서 토큰화를 한다면 같은 뜻의 문장이지만 서로 다른 토큰이 나오게 된다.
특수문자를 제거한 후 토큰화를 하면 원래 단어의 의미를 잃어버릴 수 있다.
단어에 들어가는 특수문자는 종종 중요한 의미를 내포하고 있기 때문에 함부로 지우면 안된다.
💡 특수문자가 중요한 역할을 하는 경우에는 별로 효용적이지 못한 것 같다.
?
또는 !
로 문장을 자른다
.
으로 나눈다면
sample_text = "I never thought through love we'd be. Making one as lovely as she. But isn't she lovely made from love."
# 단순하게 온점과 공백을 이용해서 잘라낸다면...
tokenized_sentence = sample_text.split(". ")
tokenized_sentence
~~>
["I never thought through love we'd be",
'Making one as lovely as she',
"But isn't she lovely made from love."]
sample = "a b c"
sample.split(" ")
~~>
['a', 'b', 'c']
sample2 = "a b c"
sample2.split(" ")
~~>
['a', ' ', 'b', 'c']
sample2.split()
~~>
['a', 'b', 'c']
tokenized_word = sample_text.split()
tokenized_word
~~>
['I',
'never',
'thought',
'through',
'love',
"we'd",
'be.',
'Making',
'one',
'as',
'lovely',
'as',
'she.',
'But',
"isn't",
'she',
'lovely',
'made',
'from',
'love.']
💡 단숭하게 공백으로만 토큰화를 수행하면, 사람은 같은 문장이라고 인지할 수 있지만, 기계는 위 세문장이 다른 문장이라고 판단
📍 설치 코드
!pip install nltk
로컬에서 사용하는 경우 자바(JVM)가 미리 설치 되어 있어야 한다.
✨ nltk는 영문 텍스트에 관련된 여러 토크나이져나 도구들이 한꺼번에 존재하는 패키지
import nltk
nltk.download('punkt') # 영어 토크나이져 패키지 다운로드
sentence = "Ain't nothin' sweeter, you want this sugar, don't ya?"
📍 일반 split()을 활용해서 공백 단위로 단어 토큰화 한 경우
print(sentence.split())
~~>
["Ain't", "nothin'", 'sweeter,', 'you', 'want', 'this', 'sugar,', "don't", 'ya?']
📍 nltk 기본 tokenizer를 활용한 경우
from nltk.tokenize import word_tokenize
print(word_tokenize(sentence))
~~>
['Ai', "n't", 'nothin', "'", 'sweeter', ',', 'you', 'want', 'this', 'sugar', ',', 'do', "n't", 'ya', '?']
from nltk.tokenize import WordPunctTokenizer
tokenizer = WordPunctTokenizer()
print(tokenizer.tokenize(sentence))
~~>
['Ain', "'", 't', 'nothin', "'", 'sweeter', ',', 'you', 'want', 'this', 'sugar', ',', 'don', "'", 't', 'ya', '?']
from nltk.tokenize import TreebankWordTokenizer
tokenizer = TreebankWordTokenizer()
print(tokenizer.tokenize(sentence))
~~>
['Ai', "n't", 'nothin', "'", 'sweeter', ',', 'you', 'want', 'this', 'sugar', ',', 'do', "n't", 'ya', '?']
sample = "I'm Iron-man"
print(tokenizer.tokenize(sample))
~~>
['I', "'m", 'Iron-man']
한국어는 띄어쓰기가 지켜지지 않아도 사람이 읽기가 쉽기 때문
- 이렇게띄어쓰기를하지않아도일단은잘읽을수있다.
반면에 영어는 띄어쓰기가 매우 엄격
- yousugaryespleasewouldyoucomeandputitdownonme
한국어 띄어쓰기 검사 패키지로 ko-spacing
이나 py-hanspell
패키지를 사용
같은 의미의 문장을 자유롭게 쓸 수 있다.
- 나는 점심을 먹었다. 집에서.
- 나는 집에서 점심을 먹었다.
- (나는) 집에서 점심을 먹었다.
- 나는 점심을 집에서 먹었다.
Language Model에게는 매우 어려운 상황
- 타는 배, 먹는 배, 사람의 배
- 하늘에서 내리는 눈, 사람의 눈
📍 설치 코드
!pip install konlpy
📍 konlpy의 모든 형태소 분리기는 명사 추출, 형태소별 토큰화, 형태소 토큰 및 종류를 표기하는 동일 함수가 존재
def print_tokenizer(tokenizer, s):
print(tokenizer.nouns(s)) # 형태소 분리기에서 명사만 추출
print(tokenizer.morphs(s)) # 각 형태소 별로 토큰화만 시켜준다.
print(tokenizer.pos(s)) # 각 형태소 별 토큰 및 형태소 종류를 튜플로 표현
- Okt : Stemming, Normalization 기능을 제공
from konlpy.tag import Hannanum, Kkma, Komoran, Okt, Mecab
hannanum = Hannanum()
kkma = Kkma()
komoran = Komoran()
okt = Okt()
mecab = Mecab()
sentence="좋으니 그 사람 솔직히 견디기 버거워"
📍 트위터( Okt )
print_tokenizer(okt, sentence)
~~>
['그', '사람']
['좋으니', '그', '사람', '솔직히', '견디기', '버거워']
[('좋으니', 'Adjective'), ('그', 'Noun'), ('사람', 'Noun'), ('솔직히', 'Adjective'), ('견디기', 'Verb'), ('버거워', 'Adjective')]
📍 꼬꼬마( Kkma )
print_tokenizer(kkma, sentence)
~~>
['사람']
['좋', '으니', '그', '사람', '솔직히', '견디', '기', '버겁', '어']
[('좋', 'VA'), ('으니', 'ECD'), ('그', 'MDT'), ('사람', 'NNG'), ('솔직히', 'MAG'), ('견디', 'VV'), ('기', 'ETN'), ('버겁', 'VA'), ('어', 'ECS')]
📍 코모란( Komoran )
print_tokenizer(komoran, sentence)
~~>
['사람']
['좋', '으니', '그', '사람', '솔직히', '견디', '기', '버거워']
[('좋', 'VA'), ('으니', 'EC'), ('그', 'MM'), ('사람', 'NNG'), ('솔직히', 'MAG'), ('견디', 'VV'), ('기', 'ETN'), ('버거워', 'NA')]
📍 한나눔( Hannanum )
print_tokenizer(hannanum, sentence)
~~>
['사람', '버거워']
['좋', '으니', '그', '사람', '솔직히', '견디', '기', '버거워']
[('좋', 'P'), ('으니', 'E'), ('그', 'M'), ('사람', 'N'), ('솔직히', 'M'), ('견디', 'P'), ('기', 'E'), ('버거워', 'N')]
📍 Mecab
print_tokenizer(mecab, sentence)
~~>
['사람']
['좋', '으니', '그', '사람', '솔직히', '견디', '기', '버거워']
[('좋', 'VA'), ('으니', 'EC'), ('그', 'MM'), ('사람', 'NNG'), ('솔직히', 'MAG'), ('견디', 'VV'), ('기', 'ETN'), ('버거워', 'VA+EC')]
단순하게 물음표, 느낌표, 온점( . )으로만 문장을 잘라내면 안된다.
text = "Since I'm actively looking for Ph.D. students. I get the same question a dozen times every year."
📍 단순하게 온점으로만 토크나이징 한 경우
print(text.split('.'))
~~>
["Since I'm actively looking for Ph", 'D', ' students', ' I get the same question a dozen times every year', '']
📍 Tokenizer 사용
from nltk.tokenize import sent_tokenize
print(sent_tokenize(text))
~~>
["Since I'm actively looking for Ph.D. students.", 'I get the same question a dozen times every year.']
📍 단순하게 온점으로만 토크나이징 한 경우
text = "My IP Address is 192.168.56.51. Hello World!"
print(text.split("."))
~~>
['My IP Address is 192', '168', '56', '51', ' Hello World!']
📍 Tokenizer 사용
print(sent_tokenize(text))
~~>
['My IP Address is 192.168.56.51.', 'Hello World!']
📍 설치 코드
!pip install kss
import kss
text = "제 아이피는 192.168.56.21 입니다. 자연어 처리가 재미있나요?ㅋㅋㅋ"
print(kss.split_sentences(text))
~~>
['제 아이피는 192.168.56.21 입니다.', '자연어 처리가 재미있나요?ㅋㅋㅋ']
📍 설치 코드
!pip install git+https://github.com/haven-jeon/PyKoSpacing.git
📍 설치 코드
!pip install git+https://github.com/ssut/py-hanspell.git
from hanspell import spell_checker # 한국어 맞춤법 관리
from pykospacing import spacing # 한국어 띄어쓰기 관리
text = "4번놀고있지.4번은팀워크가없어.4번은개인주의야.4번은혼자밖에생각하지않아."
spacing_text = spacing(text)
print(spacing_text)
~~>
4번 놀고 있지.4번은 팀워크가 없어.4번은 개인주의야.4번은 혼자 밖에 생각하지 않아.
📍 띄어쓰기 검사 및 교정
hanspell_text = spell_checker.check(text)
print(hanspell_text.checked)
~~>
4번 놀고 있지. 4번은 팀워크가 없어. 4번은 개인주의야. 4번은 혼자밖에 생각하지 않아.
📍 맞춤법 검사
text = "맞춤뻡 틀리면 외 않되?"
hanspell_text = spell_checker.check(text).checked
print(hanspell_text)
~~>
맞춤법 틀리면 왜 안돼?
Beautiful : beaut
Allowance ; allow
Medical : medic
books : book
this : thi
from nltk.stem import PorterStemmer
from nltk.tokenize import word_tokenize
porter_stemmer = PoterStemmer()
text = "This was not the map we found in Billy Bones's chest, but an accurate copy, complete in all things--names and heights and soundings--with the single exception of the red crosses and the written notes."
# Stemming : 단어에 대한 어간을 추출하는 과정이기 때문에 단어 토큰화 부터 수행
words = word_tokenize(text)
print(words)
~~>
['This', 'was', 'not', 'the', 'map', 'we', 'found', 'in', 'Billy', 'Bones', "'s", 'chest', ',', 'but', 'an', 'accurate', 'copy', ',', 'complete', 'in', 'all', 'things', '--', 'names', 'and', 'heights', 'and', 'soundings', '--', 'with', 'the', 'single', 'exception', 'of', 'the', 'red', 'crosses', 'and', 'the', 'written', 'notes', '.']
stem_list = [porter_stemmer.stem(w) for w in words]
print(stem_list)
~~>
['thi', 'wa', 'not', 'the', 'map', 'we', 'found', 'in', 'billi', 'bone', "'s", 'chest', ',', 'but', 'an', 'accur', 'copi', ',', 'complet', 'in', 'all', 'thing', '--', 'name', 'and', 'height', 'and', 'sound', '--', 'with', 'the', 'singl', 'except', 'of', 'the', 'red', 'cross', 'and', 'the', 'written', 'note', '.']
words = ["Serialize", "Allowance", "Allowed", "Medical", "Pretty", "Lovely", "Beautiful"]
print([porter_stemmer.stem(w) for w in words])
~~>
['serial', 'allow', 'allow', 'medic', 'pretti', 'love', 'beauti']
okt = Okt()
text = "4번 놀고 있지. 4번은 팀워크가 없어. 4번은 개인주의야. 4번은 혼자밖에 생각하지 않아."
print(okt.morphs(text)) # 일반적인 형태소 분리
print(okt.morphs(text, stem=True)) # 어간이 추출된 형태소 분리 ( 동사와 형용사에 대한 어간을 추출 )
~~>
['4', '번', '놀고', '있지', '.', '4', '번은', '팀워크', '가', '없어', '.', '4', '번은', '개인주의', '야', '.', '4', '번은', '혼자', '밖에', '생각', '하지', '않아', '.']
['4', '번', '놀다', '있다', '.', '4', '번은', '팀워크', '가', '없다', '.', '4', '번은', '개인주의', '야', '.', '4', '번은', '혼자', '밖에', '생각', '하다', '않다', '.']
print(okt.pos(text))
print(okt.pos(text, temp=True))
~~>
[('4', 'Number'), ('번', 'Noun'), ('놀고', 'Verb'), ('있지', 'Adjective'), ('.', 'Punctuation'), ('4', 'Number'), ('번은', 'Noun'), ('팀워크', 'Noun'), ('가', 'Josa'), ('없어', 'Adjective'), ('.', 'Punctuation'), ('4', 'Number'), ('번은', 'Noun'), ('개인주의', 'Noun'), ('야', 'Josa'), ('.', 'Punctuation'), ('4', 'Number'), ('번은', 'Noun'), ('혼자', 'Noun'), ('밖에', 'Josa'), ('생각', 'Noun'), ('하지', 'Verb'), ('않아', 'Verb'), ('.', 'Punctuation')]
[('4', 'Number'), ('번', 'Noun'), ('놀다', 'Verb'), ('있다', 'Adjective'), ('.', 'Punctuation'), ('4', 'Number'), ('번은', 'Noun'), ('팀워크', 'Noun'), ('가', 'Josa'), ('없다', 'Adjective'), ('.', 'Punctuation'), ('4', 'Number'), ('번은', 'Noun'), ('개인주의', 'Noun'), ('야', 'Josa'), ('.', 'Punctuation'), ('4', 'Number'), ('번은', 'Noun'), ('혼자', 'Noun'), ('밖에', 'Josa'), ('생각', 'Noun'), ('하다', 'Verb'), ('않다', 'Verb'), ('.', 'Punctuation')]
text = "딥러닝 진짜 어렵닼ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 이렇게 어려울지 몰랐어옄ㅋㅋㅋㅋㅋㅋㅋㅋ"
print(okt.pos(text))
print(okt.pos(text, norm=True)) # 정규화
~~>
[('딥', 'Noun'), ('러닝', 'Noun'), ('진짜', 'Noun'), ('어렵닼', 'Noun'), ('ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ', 'KoreanParticle'), ('이렇게', 'Adverb'), ('어려울지', 'Verb'), ('몰랐어', 'Verb'), ('옄', 'Noun'), ('ㅋㅋㅋㅋㅋㅋㅋㅋ', 'KoreanParticle')]
[('딥', 'Noun'), ('러닝', 'Noun'), ('진짜', 'Noun'), ('어렵다', 'Adjective'), ('ㅋㅋㅋ', 'KoreanParticle'), ('이렇게', 'Adverb'), ('어려울지', 'Verb'), ('몰랐어여', 'Verb'), ('ㅋㅋㅋ', 'KoreanParticle')]
📍 어간 추출(Stem), 정규화를 동시에
print(okt.pos(text, stem=True, norm=True))
~~>
[('딥', 'Noun'), ('러닝', 'Noun'), ('진짜', 'Noun'), ('어렵다', 'Adjective'), ('ㅋㅋㅋ', 'KoreanParticle'), ('이렇게', 'Adverb'), ('어리다', 'Verb'), ('모르다', 'Verb'), ('ㅋㅋㅋ', 'KoreanParticle')]
📍 이모티콘이나 의미없이 반복되는 문자들을 정제하기 위한 패키지
!pip install soynip
from soynip.normalizer import emoticon_normalize
print(emoticon_normalize("앜ㅋㅋㅋ 이 게임 존잼쓰ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ", num_repeats=2))
print(emoticon_normalize("앜ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 이 게임 존잼쓰ㅠㅠㅠㅠㅠㅠㅠㅠ", num_repeats=2))
print(emoticon_normalize("앜ㅋㅋㅋㅋㅋㅋ 이 게임 존잼쓰ㅠㅠㅠㅠㅠㅠㅠ", num_repeats=2))
~~>
아ㅋㅋ 이 게임 존잼쓰ㅠㅠ
아ㅋㅋ 이 게임 존잼쓰ㅠㅠ
아ㅋㅋ 이 게임 존잼쓰ㅠㅠ
📍 반복되는 문자를 정규화
from soynlp.normalizer import repeat_normalize
print(repeat_normalize("문을 쿵쿵쿵쿵쿵쿵쿵쿵쿵쿵쿵쿵쿵쿵 두드렸다.", num_repeats=2))
print(repeat_normalize("문을 쿵쿵쿵쿵쿵쿵쿵 두드렸다.", num_repeats=2))
print(repeat_normalize("문을 쿵쿵쿵쿵 두드렸다.", num_repeats=2))
~~>
문을 쿵쿵 두드렸다.
문을 쿵쿵 두드렸다.
문을 쿵쿵 두드렸다.
정규식을 이용한 정제 방법
불용어 정제( stopwords )
import re # 정규식을 사용하기 위해 re모듈 임포트
eng_sent = "\n\n\n\n\n\n\nYeah, do you expect people to read the FAQ, etc. and actually accept hard\natheism? No, you need a little leap of faith, Jimmy. Your logic runs out\nof steam!\n\n\n\n\n\n\n\nJim,\n\nSorry I can't pity you, Jim. And I'm sorry that you have these feelings of\ndenial about the faith you need to get by. Oh well, just pretend that it will\nall end happily ever after anyway. Maybe if you start a new newsgroup,\nalt.atheist.hard, you won't be bummin' so much?\n\n\n\n\n\n\nBye-Bye, Big Jim. Don't forget your Flintstone's Chewables! :) \n--\nBake Timmons, III"
print(eng_sent)
~~>
Yeah, do you expect people to read the FAQ, etc. and actually accept hard
atheism? No, you need a little leap of faith, Jimmy. Your logic runs out
of steam!
Jim,
Sorry I can't pity you, Jim. And I'm sorry that you have these feelings of
denial about the faith you need to get by. Oh well, just pretend that it will
all end happily ever after anyway. Maybe if you start a new newsgroup,
alt.atheist.hard, you won't be bummin' so much?
Bye-Bye, Big Jim. Don't forget your Flintstone's Chewables! :)
--
Bake Timmons, III
📍 영문 알파벳이 아닌 것은 모두 공백으로 치환
eng_sent = re.sub("[^a-zA-Z]", # [] : 안에 있는 글자 1개, ^ : 시작하는게 아닌 것, a-z : 알파벳 소문자 a~z, A-Z 알파벳 대문자 A ~ Z
" ",
eng_sent)
print(eng_sent)
~~>
Yeah do you expect people to read the FAQ etc and actually accept hard atheism No you need a little leap of faith Jimmy Your logic runs out of steam Jim Sorry I can t pity you Jim And I m sorry that you have these feelings of denial about the faith you need to get by Oh well just pretend that it will all end happily ever after anyway Maybe if you start a new newsgroup alt atheist hard you won t be bummin so much Bye Bye Big Jim Don t forget your Flintstone s Chewables Bake Timmons III
📍 예를 들어 4글자 이상인 단어만 추출해서 새롭게 문장을 만들기
eng_sent = " ".join([w for w in eng_sent.split() if len(w) > 3])
print(eng_sent)
~~>
Yeah expect people read actually accept hard atheism need little leap faith Jimmy Your logic runs steam Sorry pity sorry that have these feelings denial about faith need well just pretend that will happily ever after anyway Maybe start newsgroup atheist hard bummin much forget your Flintstone Chewables Bake Timmons
kor_sent = "와 오늘 날씨 실화냐...ㅋㅋㅋㅋ 엄청 더워서 졸려요.."
kor_sent
~~>
'와 오늘 날씨 실화냐...ㅋㅋㅋㅋ 엄청 더워서 졸려요..'
kor_sent = re.sub("[^ㄱ-ㅎㅏ-ㅣ가-힣]", " ", kor_sent)
kor_sent
~~>
'와 오늘 날씨 실화냐 ㅋㅋㅋㅋ 엄청 더워서 졸려요 '
📍 공백이 2개 이상이면 사라지게 하기
kor_sent = re.sub("[ ]{2,}", " ", kor_sent)
kor_sent
~~>
'와 오늘 날씨 실화냐 ㅋㅋㅋㅋ 엄청 더워서 졸려요 '
📍 nltk 모듈
import nltk
nltk.download('stopwords') # 불용어 단어 사전 다운로드
print(list(stopwords.words('english'))) # 불용어도 비즈니스에 맞게 따로 설정할 수 있어야 한다.
~~>
['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her', 'hers', 'herself', 'it', "it's", 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that', "that'll", 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don', "don't", 'should', "should've", 'now', 'd', 'll', 'm', 'o', 're', 've', 'y', 'ain', 'aren', "aren't", 'couldn', "couldn't", 'didn', "didn't", 'doesn', "doesn't", 'hadn', "hadn't", 'hasn', "hasn't", 'haven', "haven't", 'isn', "isn't", 'ma', 'mightn', "mightn't", 'mustn', "mustn't", 'needn', "needn't", 'shan', "shan't", 'shouldn', "shouldn't", 'wasn', "wasn't", 'weren', "weren't", 'won', "won't", 'wouldn', "wouldn't"]
sample_text = "Family is not an important thing. It's everything"
# 사람이 직접 만든 불용어 사전에는 중복이 존재할 수도 있다. 따라서 중복을 제거
stop_words = set(stopwords.words("english"))
word_tokens = word_tokenize(sample_text.lower()) # 문장을 소문자로 만들어서 토큰화 하기
result = [ w for w in word_tokens if w not in stop_words ]
print("원본 : {}".format(word_tokens))
print("불용어 제거 후 : {}".format(result))
~~>
원본 : ['family', 'is', 'not', 'an', 'important', 'thing', '.', 'it', "'s", 'everything']
불용어 제거 후 : ['family', 'important', 'thing', '.', "'s", 'everything']
sample_text="와 이런 것도 영화라고 차라리 뮤직비디오를 만드는 게 나을 뻔"
word_tokens = mecab.morphs(sample_text)
print(word_tokens)
~~>
['와', '이런', '것', '도', '영화', '라고', '차라리', '뮤직', '비디오', '를', '만드', '는', '게', '나을', '뻔']
📍 개발자가 임의로 불용어를 선정
stop_words = ['의','가','이','은','들','는','좀','잘','걍','과','도','를','으로','자','에','와','한','하다', '것', '게']
result = [w for w in word_tokens if not w in stop_words]
print("원문 : {}".format(word_tokens))
print("불용어 제거 후 : {}".format(result))
~~>
원문 : ['와', '이런', '것', '도', '영화', '라고', '차라리', '뮤직', '비디오', '를', '만드', '는', '게', '나을', '뻔']
불용어 제거 후 : ['이런', '영화', '라고', '차라리', '뮤직', '비디오', '만드', '나을', '뻔']
✨✨✨ 자연어 처리에서 데이터 정제는 따로 정해진 루틴이 없다보니 각자의 비즈니스에 맞게 문자열 편집능력을 활용하는 것이 중요하다.