<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jong_sense.log</title>
        <link>https://velog.io/</link>
        <description>공부 중인 학부생</description>
        <lastBuildDate>Fri, 08 Mar 2024 07:04:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jong_sense.log</title>
            <url>https://velog.velcdn.com/images/jong_sense/profile/b7a7216e-8df9-4e8f-a816-8af0826a5b5d/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jong_sense.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jong_sense" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Efficient Estimation of Word Representations in Vector Space]]></title>
            <link>https://velog.io/@jong_sense/Efficient-Estimation-of-Word-Representations-in-Vector-Space</link>
            <guid>https://velog.io/@jong_sense/Efficient-Estimation-of-Word-Representations-in-Vector-Space</guid>
            <pubDate>Fri, 08 Mar 2024 07:04:32 GMT</pubDate>
            <description><![CDATA[<h2 id="1-introduction">1. Introduction</h2>
<p>많은 NLP 시스템과 기술에서 단어 간의 유사성 개념이 존재하지 않는다. 이는 단순한 모델에 방대한 양의 데이터를 학습시키는 것이 복잡한 모델에 적은 양의 데이터로 학습시키는 것보다 성능이 좋기 때문이다. </p>
<p>하지만 유사성을 무시한 이런 단순한 기법은 모델의 성능을 끌어올리는 데에 한계가 있는 등의 문제가 있다.</p>
<h3 id="one-hot-encoding"><strong>One-Hot Encoding</strong></h3>
<p>자연어 처리에서 컴퓨터에게 인간이 사용하는 언어를 입력하기 위해서는 수치화가 필요하다.</p>
<p>표현하려는 모든 단어에 대한 벡터를 만들고 해당하는 단어에는 1을, 나머지 단어에는 0을 부여한다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/4dd627ef-6c96-4b36-beb8-a7255ed7e46b/image.png" alt=""></p>
<ul>
<li>차원이 높고, 대부분이 sparse한 벡터가 되기에 정보를 추출하기 어려워진다.</li>
<li>단어 간 similarity에 대한 정보를 담고 있지 않다.</li>
</ul>
<p>위의 문제점들로 단어 자체가 가지는 의미를 다차원 공간에 표현하는 Distributed Representation을 제안했다.</p>
<h3 id="vector-representation-of-words"><strong>Vector Representation of Words</strong></h3>
<p><em>‘Words that occur in similar contexts tend to have similar meanings.’</em>
다시말해, ‘비슷한 분포를 가지는 단어들은 비슷한 의미를 갖는다’는 가정으로 시작된다.</p>
<p>ex. ‘공부’, ‘열심히’, ‘바쁘다’ 등의 단어들이 주로 함께 등장
→ 이 단어들을 벡터화 했을 때 의미적으로 가까운 단어이다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/a46275c6-d7f8-43c7-8a9d-dcac22af7188/image.png" alt=""></p>
<p>원핫 인코딩의 모든 원소들이 값을 가진 벡터로 표현한다.</p>
<blockquote>
<p><strong>이점:</strong></p>
<ul>
<li><p>단어 간의 similarity를 계산할 수 있다.</p>
<p>  분산 표현된 벡터들은 단어 간의 유사도를 측정할 수 있게 된다.</p>
<p>  공간 상에 맵핑했을 때 가까이 있는 단어들은 유사도가 높고, 반대의 경우는 유사도가 낮다고 판단 할 수 있다.</p>
</li>
<li><p>Algebric operation이 가능하다.</p>
<p>  또한 벡터 간의 연산을 통해 관계를 추론해낼 수 있다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/d1ff0a41-626c-4e48-b691-6108a5e9b823/image.png" alt="">₩</p>
<p>king-queen, man-woman 간의 벡터가 각각 같은 방향을 갖는다.
→ 비슷한 관계임을 추론할 수 있다. </p>
</blockquote>
<h3 id="goals-of-the-paper">Goals of the Paper</h3>
<p>본 논문의 목표는 대량의 data로부터 높은 수준의 word vector를 학습하는 기술을 제안하는 것이다.</p>
<p>따라서 비슷한 단어는 가까워야 할 뿐만 아니라 유사점을 가지고 있어야 한다.</p>
<p>또한 학습 시간과 정확도에 워드 벡터의 차원가 데이터의 양이 어떠한 영향을 주는지 실험을 통해 살펴본다.</p>
<p>덧붙여 모델 비교를 위한 Computational Complexty를 다음과 같이 정의한다.</p>
<aside>
💡 **Computational Complexty:**

<p>$$
O = E \times T \times Q</p>
<p>$$</p>
<p>$E: number \space of \space the \space training \space epochs$</p>
<p>$T: number \space of \space the \space words \space in \space the \space training \space set$</p>
<p>$Q: 각 \space model \space 구조에 \space 의해 \space 정의됨$</p>
</aside>

<h2 id="2-previous-work">2. Previous Work</h2>
<p>이 논문 이전에 제시된 word embedding 모델에 대해 설명한다. </p>
<h3 id="feed-forward-neural-net-language-model-nnlm"><strong>Feed-Forward Neural Net Language Model (NNLM)</strong></h3>
<ul>
<li><p>Input Layer</p>
<p>  현재 보고 있는 단어 이전의 단어들이 one-hot 벡터로 인코딩 되어 input으로 들어간다.</p>
<p>  이때 $V$는 Vocabulary의 크기, $N$은 input에 들어간 이전 단어의 개수이다. </p>
</li>
<li><p>Projection Layer</p>
<p>  $N \times V$ matrix는 Vocabulary보다 저차원인 $N \times D$에 벡터로 사용되어 Projection Layer에 들어간다.</p>
<p>  Projection matrix는 $V \times D$ matrix가 되어야 한다.</p>
</li>
<li><p>Hidden Layer</p>
<p>  Projection layer의 값들은 $H$개의 노드를 가지는 hidden layer로 들어가 각각의 단어의 확률을 계산한다.</p>
</li>
<li><p>Output Layer</p>
<p>  출력값을 정답 단어의 one-hot 벡터와 비교하여 loss를 계산하고 가중치를 업데이트 한다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/85c3519c-feea-4220-9bf7-e925f5334c20/image.png" alt=""></p>
<blockquote>
<p><strong>한계:</strong></p>
<ul>
<li>History로 사용할 단어의 개수를 고정해주어야 한다.</li>
<li>History만을 보고 예측하기 때문에 미래 시점의 단어들을 고려하지 않는다.</li>
<li>Computational Complexity: 
$Q = N \times D + N \times D \times H + H \times V$</li>
</ul>
</blockquote>
<h3 id="recurrent-neural-net-language-model-rnnlm"><strong>Recurrent Neural Net Language Model (RNNLM)</strong></h3>
<p>Projection Layer가 없다는 특징을 가지고 있다.</p>
<p>History 단어의 개수를 설정해줄 필요 없이 input으로 들어오는 단어를 순서대로 읽는다.</p>
<p>그리고 Hidden Layer의 값이 다시 Input Layer로 들어온다. (Recurrent)
→ Short-term memory 역할로, 과거 단어를 보는 역할을 한다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/2fa3a5b2-f5f1-4354-a2c1-45321bb08bc4/image.png" alt=""></p>
<aside>
💡 **Computational Complexty:**

<p>$$
Q = H \times H + H \times V
$$</p>
</aside>

<blockquote>
<p><strong>이점:</strong></p>
<ul>
<li>Input으로 사용할 단어의 수를 정해줄 필요 없이 학습할 단어를 순차적으로 입력하면 된다.</li>
<li>Word representation에 사용되는 matrix D는 hidden layer H와 같은 dimension을 갖는다.</li>
<li>Computational Complexity: 
$Q = H \times H + H \times V$</li>
</ul>
</blockquote>
<h2 id="3-word2vec">3. Word2Vec</h2>
<p>자연어 처리 특성상 많은 데이터를 학습시켜야 해서 학습 속도가 중요하다.</p>
<p>이에, Computer complexity 관점에서 앞선 모델들을 개선하는 모델을 제안했다.</p>
<h3 id="model-structure">Model Structure</h3>
<ul>
<li>Distributed representation of words를 학습하는 모델이다.</li>
<li>Complexity의 대부분이 model의 non-linear hidden layer로부터 야기된다.
→ Feedforward NNLM의 structure를 따른다.</li>
</ul>
<blockquote>
<p><strong>Structure:</strong></p>
</blockquote>
<ul>
<li>Continuous Bag-of-Words Model (CBOW)</li>
<li>Continuous Skip-gram Model (Skip-gram)<blockquote>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/96cea9f7-c850-44b3-b33e-701b7140fcf0/image.jpeg" alt=""></p>
<h3 id="continuous-bag-of-words-model"><strong>Continuous Bag-of-Words Model</strong></h3>
<p>과거의 워드의 순서가 projection의 영향을 받지 않는 ‘Bag-of-Words Model’을 사용한다.  또한 context에 앞으로 나올 단어들을 사용하는 continuos한 distributed representation을 사용한다는 점에서 Continuous Bag-of-Words Model이라 표현된다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/fb40c10f-ecb0-4592-8f95-2c1d5d963c29/image.png" alt=""></p>
<p>문장의 중간 단어가 정해지고, 앞뒤 2개의 단어가 one-hot 벡터로 input에 들어간다. </p>
<p>그리고 이 one-hot 벡터들은 projection matrix에 의해 projection 되고, 단어별로 프로젝션된 벡터들을 더해 하나의 $1 \times D$ 벡터로 만든다. </p>
<p>$N \times D$ 벡터는 모든 어휘에 대한 확률을 개선하기 위해 다시 weight matrix를 이용해 $1 \times V$ 벡터로 만든다. 그리고 이 벡터는 softmax로 확률값을 만들고, 가장 높은 확률을 가지는 단어가 중심 단어로 예측된다.</p>
<h3 id="continuous-skip-gram-model"><strong>Continuous Skip-gram Model</strong></h3>
<p>또 다른 방식은 중심 단어를 input으로 해서 주변 단어를 예측하는 Skip-gram Model이다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/45c603ac-ffca-478e-b620-98302e61a128/image.png" alt=""></p>
<p>이 모델은 현재 가지고 있는 단어의 주변 시계의 단어를 샘플링하여 예측한다. 가까이 있는 단어일수록 현재 단어와 더 관련있는 단어임을 나타내기 위해 가까이 있는 단어를 더 높은 확률로 택한다.</p>
<p>중심 단어가 one-hot 벡터로 input에 들어가고, 결과적으로 $1 \times V$ 벡터가 되고 이는 주변 단어로 뽑은 단어의 개수 만큼 반복된다.</p>
<p><strong>Ex: “The fat cat sat on the mat”</strong></p>
<p>중심 단어를 ‘sat’이라 할 때, 주변 단어 ‘cat’과 ‘on’이 one-hot 벡터로 인코딩 되어 input에 들어간다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/bb000e3b-9a3a-47f9-8f81-0d6053f7b5d1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/440aaeba-0f26-4746-9eac-0855a767e55c/image.png" alt=""></p>
<p>Projection되는 과정</p>
<p>‘cat’ 단어에 해당하는 one-hot 벡터는 ‘cat’ 외의 단어는 전부 0으로 표현된다. 따라서 projection matrix와 곱해지면 ‘cat’에 해당하는 행만 읽게 되는 것과 같아지고, 차원은 $1 \times D$로 축소된다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/b672f329-39be-48c9-a10c-5af86f76c500/image.png" alt=""></p>
<p>앞선 상황으로 다시 돌아와, ‘cat’과 ‘on’ 두 단어는 projection matrix에 의해 $2 \times D$ matrix가 되고, 이는 다시 합쳐저 $1 \times D$ matrix가 된다. 그리고 다시 weight matrix에 의해 $1 \times V$ 벡터가 된다.</p>
<p>벡터에 softmax를 취하고, 이를 통해 얻은 결과를 정답과 비교하여 weight를 업데이트 한다.</p>
<h3 id="computational-complexity"><strong>Computational Complexity</strong></h3>
<p>위의 모델들에 의해 개선된 Computational Complexity를 계산해보자. </p>
<p> <strong>CBOW</strong></p>
<p>$Q = N \times D + D \times V$</p>
<p>$N \times D$ : 현재 단어를 중심으로 $N$개의 단어를 $D$차원으로 projection하는 복잡도.</p>
<p>$D \times V$ : $D$차원의 projection layer로부터 $V$개의 단어들 각각의 단어의 output layer를 계산하는 복잡도.</p>
<p> <strong>Skip-gram</strong></p>
<p>$Q = C \times (D + D \times V)$</p>
<p>$D + D \times V$ : 현재 단어 하나만 projection하는 복잡도 $D$ + projection된 단어 $V$개의 output(확률값) 계산하는 복잡도.</p>
<p>$\times C$ : $C$ 개의 단어에 대해 진행해야 하므로 $C$ 만큼 곱해준다.  </p>
<hr>
<p>2가지 모델 모두 앞선 모델들보다 개선된 시간 복잡도를 갖지만, 여전히 $V$의 곱 만큼 복잡도가 있기에, 학습이 빠르게 진행되기는 어렵다.  </p>
<p>$V$는 단어들의 확률값을 구하기 위해 단어마다 softmax를 취하면서 발생하는 복잡도이다.
따라서 높은 성능을 위해서 vocabulary가 커질수록 복잡도는 증가한다. </p>
<p>이 trade-off를 해결하기 위해 ****CBOW와 Skip-gram은 모든 단어에 대해 확률값을 계산하지 않는 Hierachycal Softmax를 사용한다.</p>
<h2 id="4-complexity-reduction">4. Complexity Reduction</h2>
<h3 id="hierachical-softmax">Hierachical Softmax</h3>
<p>단어들에 대해 모든 단어가 리프 노드로 오는 풀-트리를 만들어 단어별 확률을 계산한다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/9bc2da6b-55cd-4d83-ba72-f7969f5141b8/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/b6538811-7bce-49e0-96e9-15607f7a3cdf/image.png" alt=""></p>
<p>다른 단어에 대한 확률을 구하지 않고도 특정 단어의 값을 구할 수 있다.</p>
<p><strong>Ex.</strong></p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/99c3798e-7069-4317-920e-8fc3d31eb3b4/image.png" alt=""></p>
<p>$$
P(n, left): node \space n의 \space left로 \space 갈 \space 확률
$$</p>
<p>$$
P(n, right): node \space n의 \space right로 \space 갈 \space 확률
$$</p>
<p>$P(n, left)+P(n, right) = 1$이므로 위의 식이 성립한다.</p>
<p>$\sum_{i=1}^{6}P(w_{i} = w_{0}) = 1$</p>
<p>모든 단어의 값에 대한 계산 없이 전체 합이 1이 되는 확률값을 구할 수 있다. </p>
<p>Softmax가 모든 단어에 대해 output layer의 값을 구하던 이유는 모델이 예측한 단어가 정답일 확률을 구하기 위해 모든 단어에 대한 output 값을 더해 확률을 구했기 때문이다.</p>
<p>Hierarchical softmax는 정답 단어에 대한 확률만을 다른 단어의 확률을 구하지 않고도 구할 수 있게 해준다.</p>
<p>Binary tree의 depth는 $ln(V)$에 비례하므로, 기존의 $D \times V$에서 $D \times ln(V)$로 복잡도를 줄일 수 있다.</p>
<hr>
<aside>
💡 **모델의 최종 Computational Complexity**

<p>$$
Q = N \times D + D \times log_{2}(V)
$$</p>
</aside>

<h2 id="5-evaluation">5 Evaluation</h2>
<h3 id="test-description">Test Description</h3>
<p>Semantic question 5가지</p>
<p>Syntactic question 9가지</p>
<h3 id="maximization-of-accuracy">Maximization of Accuracy</h3>
<p>정확도를 향상시키기 위해 워드 벡터의 차원과 train set의 사이즈를 조절하여 실험하였다.</p>
<p> <strong>CBOW</strong></p>
<p>Train set의 사이즈를 조절할 때 출현 빈도수를 기반으로 했다.  </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/ba1ecba5-67a5-4a35-b68b-a92892a9c85a/image.png" alt=""></p>
<p>‘단순히 차원수’, ’데이터셋의 크기’ 중 하나만 조절하는 것은 성능 향상에 큰 도움이 되지 않는단 것을 확인할 수 있다. 
→ 동시에 2가지 모두 증가시켜야 한다. </p>
<p>$Q = N \times D + D \times log_{2}(V)$ 이므로, Training data의 양을 두 배로 늘렸을 때 복잡도가 2배 만큼 증가하는데($2Q$), 이는 vector 차원을 두 배로 늘렸을 때와 동일하게 증가한다.
→ 어느 하나를 증가시키는 것이 다르지 않기에 둘을 잘 조율해서 다 조절하는 것이 좋다고 한다.</p>
<h3 id="comparison-of-model-architecture">Comparison of Model Architecture</h3>
<p><strong>비교 1</strong></p>
<p>Word vector의 차원은 640으로 고정하고, 동일한 Training data를 사용했다.</p>
<p>test set에 있는 3만개의 어휘의 semantic과 syntactic word relationship 질문 모두를 사용했다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/9d8f3419-946a-4f85-8dd4-00e32e5b072e/image.png" alt=""></p>
<p>실험 결과, NNLM이 RNNLM보다 좋은 성능을 보였다. 이에 대해 NNLM이 projection layer를 거쳐 hidden layer로 들어가기 때문이라고 설명한다.</p>
<p>CBOW는 syntacitc task에서 NNLM보다 큰 성능 향상을 보였다. </p>
<p>Skip-gram은 syntactic 질문에 대해서는 CBOW보다 약간 성능이 떨어지지만, semantic에서는 가장 좋은 성능을 보여줬다.</p>
<p><strong>비교 2</strong></p>
<p>데이터 사이즈와 epoch, 그리고 벡터 차원에 따른 실험을 진행한 결과이다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/9e36e552-f0bc-4854-8c6e-73f427ac98e0/image.png" alt=""></p>
<ul>
<li>CBOW와 Skip-gram 둘을 비교했을 때, 모든 경우에서 CBOW가 더 짧은 시간을 보였다.</li>
<li>1번째와 2번째 실험 결과를 비교했을 때, 같은 data size로 3번의 epoch을 도는 것보다, 두 배 이상의 data도 epoch 한번을 도는 것이 수행 시간도 짧고, 비슷하거나 더 좋은 결과를 낸다는 것을 확인할 수 있다.</li>
<li>Training data size를 2배 이상 늘리는 것보다 vector 차원을 2배 늘리는 것이 더 큰 성능 향상이 있었다.</li>
</ul>
<h2 id="6-learned-relationships">6 Learned Relationships</h2>
<p>이 모델을 사용해 도출한 word vector가 단어 관계를 잘 담고 있는지 best word vector를 통해 확인한다. </p>
<h3 id="word-relationship"><strong>Word Relationship</strong></h3>
<p>Best word vector들을 사용해 도출해낸 word pair relationship의 예시이다.</p>
<p>Skip-gram 모델에 783M개의 단어들을 300개의 차원에서 학습시켰다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/b59d8314-c18f-465d-8d81-95ba4162b4ab/image.png" alt=""></p>
<ul>
<li>두 vector를 뺀 결과에 다른 벡터를 더했을 때의 결과이다.
ex. France - Paris + Italy = Rome</li>
<li>이 때의 정확도는 약 60%지만, relationship example이 10개 이상 주어졌을 때, sematic-syntactic test에서 정확도가 10%까지 향상될 수 있다고 한다.</li>
</ul>
<h2 id="7-conclusion">7 Conclusion</h2>
<p>본 논문으로부터 얻을 수 있는 의의를 살펴보자.</p>
<ul>
<li><p>Hidden layer가 없는 매우 간단한 model 구조를 사용해 feedforward NNLM, RNNLM 같은 NN model과 비교했을 때 높은 퀄리티의 word vector를 train 할 수 있다는 것을 발견했다.</p>
</li>
<li><p>다양한 syntactic, semantic language task를 제시하여 word vector가 다양한 의미들에 의한 여러 similarity를 반영하는가를 평가할 수 있었다.</p>
<p>  ex. Algebric operation</p>
</li>
<li><p>이전 모델보다 낮은 계산 복잡도로 훨씬 많은 dataset으로부터 높은 차원의 정확한 word vector를 계산하는 것이 가능해졌다.</p>
</li>
<li><p>Pre-trained embedding model로써 다양한 NLP task에 사용될 수 있다.</p>
</li>
</ul>
<hr>
<h2 id="참고자료">참고자료</h2>
<blockquote>
<p><a href="https://arxiv.org/abs/1301.3781">https://arxiv.org/abs/1301.3781</a>
<a href="https://cpm0722.github.io/paper-review/efficient-estimation-of-word-representations-in-vector-space">https://cpm0722.github.io/paper-review/efficient-estimation-of-word-representations-in-vector-space</a>
<a href="https://velog.io/@pabiya/Efficient-Estimation-of-Word-Representations-inVector-Space">https://velog.io/@pabiya/Efficient-Estimation-of-Word-Representations-inVector-Space</a>
논문에 대한 사전지식이 많이 없어 자료를 찾던 중 고려대학교 DBSA 연구실의 유튜브 영상을 통해 많이 도움을 받았다. 본 글 또한 아래의 영상으로부터 많이 참조했다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[회고] 더 나은 프로젝트를 위한 발걸음]]></title>
            <link>https://velog.io/@jong_sense/%ED%9A%8C%EA%B3%A0-%EB%8D%94-%EB%82%98%EC%9D%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%B0%9C%EA%B1%B8%EC%9D%8C</link>
            <guid>https://velog.io/@jong_sense/%ED%9A%8C%EA%B3%A0-%EB%8D%94-%EB%82%98%EC%9D%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%B0%9C%EA%B1%B8%EC%9D%8C</guid>
            <pubDate>Mon, 22 Jan 2024 05:38:24 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jong_sense/post/8d0723b6-fd04-492a-9f54-85fb73af2e76/image.png" alt=""></p>
<pre><code>행사 포스터</code></pre><p>1/14 내가 활동하고 있는 동아리인 UMC의 중앙에서 진행한 Chapter 행사에 다녀왔다. </p>
<p>IT업계의 다양한 직군의 사람들이 모여 그간 공부, 프로젝트 등을 진행하며 얻은 각종 인사이트 등을 공유하는 행사였다. 나는 직접 발표를 하진 않았지만, 다양한 사람들의 발표를 들으며 가장 인상깊게 들었던 토픽에 대해 정리해보고자 한다.</p>
<hr>
<h2 id="야무진-프로젝트를-위한-회고">야무진 프로젝트를 위한 회고</h2>
<p>프로젝트를 진행하며 의미 있는 결과를 내기 위한 방법에 대해 소개해주셨다.</p>
<p>협업을 중점적으로 다루셨는데 ‘똑똑하게 회의하기’, ‘똑똑하게 질문하기’, ‘문서화에 신경쓰기’, ‘프로젝트를 마무리하기’ 등에 대해 말씀해주셨다.</p>
<h3 id="똑똑하게-회의하기">똑똑하게 회의하기</h3>
<p>프로젝트 진행 시 프로젝트를 진행하다 보면 시간만 흘러가고, 남는 것은 없을 때가 많았다.</p>
<p>연사분께서는 장시간 회의를 하다보면 미스 커뮤니케이션이 나는 경우가 많았어서 회의는 무조건 짧게 진행하는 것을 권장하신다.</p>
<p>그리고 우선 순위와 마감기한, 그리고 이슈 같은 것들을 하나도 빠짐없이 공유한다고 한다. 너무나도 당연한 말이지만 실제 회의를 하다보면 ‘이 정도는 뭐~’ 하면서 넘기는 경우가 많은데 팀 전체가 이런 분위기로 흘러가다 보면 프로젝트가 산으로 가는 경우가 발생하고는 한다.</p>
<p>이를 방지하기 위해 아래의 표와 같이 미리 정리하여 회의에 참석하고, 전체 회의가 15분을 넘기지 않도록 하는 것을 추천해주셨다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/2b9c964c-6fe0-495d-b54c-807834bff284/image.png" alt=""></p>
<pre><code>회의 시작 전 회의록 작성하기</code></pre><h3 id="똑똑하게-질문하기">똑똑하게 질문하기</h3>
<p>팀원 간의 질문이 있어야 하는 것은 당연하지만, 질문의 종류가 중요하다. 기한에 쫒겨 ‘못하겠어요, 도와주세요, 어떻게 하나요’ 등의 질문들은 질문을 받는 사람도 지치게 하기 마련이다. </p>
<p>연사분께서는 프로젝트 진행 시 질문에 대해 4가지의 마인드셋을 정립한다고 한다.</p>
<ol>
<li>마음의 준비하기
질문을 할 때, 받을 때 모두 열린 마음으로 임해 상대방을 배려하자.</li>
<li>충분히 검색하고 고민해보기
어떤 문제를 해결하고 싶고, 내가 궁금한 것 혹은 답하고자 하는 것의 요점이 무엇인지 확실하게 하고, 알아보자.</li>
<li>해결하기 위해 해본 것들을 구체적으로 정리하기
단순히 코드만 보여줄 것이 아니라, 참고한 링크를 첨부하여 상대방이 이해하기 쉽게 하자.</li>
<li>가능하다면 객관식으로 질문하기
문제의 원인이 될 수 있는 부분들을 미리 정리해보자.</li>
</ol>
<p>특히 개발 관련 문제라면 1~2시간 정도는 스스로 찾아보며 러버덕 디버깅과 같은 방법을 적용해보는 것을 추천해주셨다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/0afe2cc3-5511-4be3-b0fc-e5a5d6fc2279/image.png" alt=""></p>
<pre><code>러버덕 디버깅</code></pre><h3 id="문서화에-신경쓰기">문서화에 신경쓰기</h3>
<p>팀 전반의 상황과, 기술 설명서들을 주기적인 최신화를 통해 정리하자. 너무나 당연하지만, 프로젝트 막바지가 되어 기한에 쫒기다 보면 당장의 문제를 해결하기 위해 미루게 되고, 한번 미룬 것들은 프로젝트가 끝나도록 건드리지 않게 된다.</p>
<p>연사분께서는 특히 트러블 슈팅 히스토리라도 남기는 것을 적극적으로 권장하신다. 나도 이에 크게 공감하는 것이 문제는 항상 끝나갈 때 발생하고, 매번 기록을 안남기니 매번 같은 문제를 해결하기 위해 시간을 투자하곤 한다.</p>
<h3 id="프로젝트-마무리-하기">프로젝트 마무리 하기</h3>
<p>팀 프로젝트를 개인의 성장으로 만들기 위해 기록을 남기는 것은 중요하다. 연사분께서는 최소한 프로젝트의 중간과 끝나고 2번은 팀원들과 같이 회고하며 기록을 남기기를 권장하신다. 그리고 전체적인 프로젝트 뿐 아니라, ‘나’에 대한 피드백을 받는 것이 중요하다고 한다. </p>
<p>구체적으로는 KPT 회고를 적용하고, 개발이든 협업이든 조금이라도 고민해본 것들을 대충이라도 기록으로 남겨두라 한다.</p>
<aside>
💡 KPT 회고

<p>Keep, Problem, Try의 약자로 회고 내용을 세가지 관점으로 분류하여 회고하라는 회고 방법론이다.</p>
<ul>
<li>Keep: 현재 만족하고, 이어나가고 싶은 것들이다.</li>
<li>Problem: 불편하게 느끼는 부분, 개선이 필요하다고 느끼는 것들이다.</li>
<li>Try: Problem에 대한 해결책이자, 다음 회고 때 판별 가능하고, 당장 실행 가능한 것들이다.</aside>

</li>
</ul>
<hr>
<p>동아리나 랩실에서의 프로젝트만 진행해본 경험으로 매번 느끼는 것은 너무 당연한 얘기지만, ‘내가 너무 부족하다는 것’이다.</p>
<p>하지만, 개발 실력은 그렇다하더라도, 협업에서의 문제는 지금 당장 더 나은 방법으로 해결할 수 있는 문제들이다.</p>
<p>이번 행사에서의 발표를 통해 얻은 노하우를 당장의 프로젝트에 적용시켜 더 나은 성장 기회로 만들고자 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Server] 빌드와 배포]]></title>
            <link>https://velog.io/@jong_sense/%EB%B9%8C%EB%93%9C%EC%99%80-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@jong_sense/%EB%B9%8C%EB%93%9C%EC%99%80-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Thu, 18 Jan 2024 05:33:44 GMT</pubDate>
            <description><![CDATA[<p>Spring을 이용해 코드를 짜고, AWS와 GitHub Action을 이용해 서버에 배포하는 과정을 실습해보았다. 내가 그 원리에 대해서는 너무 얕게 알고 있다고 느꼈고, Web 서버의 원리부터 어떻게 코드가 배포되는 건지 차근차근 공부해보았다. </p>
<hr>
<h2 id="인터넷의-작동-원리">인터넷의 작동 원리</h2>
<p>컴퓨터 간에 통신을 할 때에, 컴퓨터는 다른 컴퓨터와 물리적으로나, 무선으로 연결되어야 한다. 이더넷 케이블, WiFi, Bluetooth 등이 있다. 연결되는 컴퓨터가 많아질수록, 이는 더 복잡해질 수 밖에 없는데, 이러한 문제를 해결하기 위해 라우터를 사용한다.</p>
<p>라우터는 컴퓨터들 사이에서 통신을 중계해주는 역할을 한다. 그리고, 라우터는 다른 라우터와 연결될 수 있다. 그리고 세계 어느 곳과도 연결되기 위해 라우터는 전화 시설에 연결된다. 이때 네트워크의 정보를 전화 시설에서 처리할 수 있는 정보로 바꾸는 것이 모뎀이다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/f338995b-787f-489f-9763-ecc76d5efd76/image.png" alt=""></p>
<pre><code>왼쪽부터 컴퓨터, 라우터, 모뎀이다.</code></pre><p>이렇게 연결된 네트워크는 다시 인터넷 서비스 제공업체(ISP)의 라우터에 연결되고, ISP는 다른 ISP의 라우터에 연결된다. 이렇게 전체 네트워크 인프라를 구성하고, 이는 인터넷이 된다. 인터넷은 인프라로서 웹, 이메일 등 다양한 서비스가 그 기반 위에 구축된다. 또한 네트워크에 연결된 각각의 컴퓨터는 각각을 특정할 수 있는 식별번호인 IP 주소를 부여 받는다. 그리고 이 IP 주소를 사람이 기억하기 쉽게 도와주는 것이 <strong>도메인 이름</strong>이다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/3059cbe3-c61f-4901-b7aa-a1845e629d9b/image.png" alt=""></p>
<pre><code>구글의 도메인 역시 해당하는 IP번호로 접속 가능하다.</code></pre><hr>
<h2 id="웹-서비스의-작동-원리">웹 서비스의 작동 원리</h2>
<p>웹에 연결된 컴퓨터는 클라이언트/서버의 역할을 하게 된다. </p>
<p>브라우저에서 웹 주소를 통해 다른 사이트로 이동하는 과정을 통해 이해해보자.</p>
<ol>
<li>클라이언트가 브라우저에 웹 주소를 입력하면 브라우저는 DNS 서버에서 웹사이트가 있는 서버의 IP 주소를 찾는다.</li>
<li>브라우저는 서버에게 웹사이트의 사본을 클라이언트에게 보내달라는 HTTP 요청 메시지를 서버에 전송한다. 이때 클라이언트와 서버 간에 전송된 요청과 데이터는 모두 TCP/IP 연결을 통해 전송된다.</li>
<li>서버가 요청을 승인하면, 클라이언트에게 웹사이트의 파일들을 데이터 패킷으로 나누어 전송한다.</li>
<li>브라우저는 패킷들을 다시 하나로 만들어 클라이언트에게 화면을 보여준다.</li>
</ol>
<h3 id="웹-서버">웹 서버</h3>
<p>웹 브라우저(클라이언트)에서 HTTP 요청을 받고, HTML 문서와 같은 웹페이지를 <strong>정적으로 처리</strong>해서 반환하는 소프트웨어와 하드웨어이다.</p>
<p>대표적인 예로 Apache, NginX 등이 있다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/f57d6d0c-b2f4-48b0-b85f-0a9e23625d26/image.png" alt=""></p>
<pre><code>Apache와 NginX</code></pre><h3 id="웹-어플리케이션-서버-was">웹 어플리케이션 서버 (WAS)</h3>
<p>CGI(Common Gateway Interface)는 정적으로 동작하는 웹서버를 동적으로 기능하게 만드는 것을 도와준다. 웹서버에 들어온 요청을 외부 프로그램과 연결하여 해당 프로그램이 요청을 처리하게 <strong>연결</strong>해주는 역할을 한다.</p>
<p>WAS는 웹 서버와 CGI가 결합된 것으로 생각할 수 있다. Tomcat, JBoss 등이 있다.</p>
<p>CGI는 요청을 외부 프로그램과 연결만 해주는 것에 반해, WAS는 웹 서버가 외부 프로그램을 직접 실행하도록 도와준다. 이를 통해 더 많은 처리를 한번에 할 수 있다는 장점이 있다.</p>
<p>다만 웹 서버는 빠르고 안정적이라는 장점이 있기에, 큰 규모의 프로젝트를 진행할 때에는 단순 htm 파일과 같이 정적인 데이터는 WAS와 분리하여 사용되기도 한다. </p>
<p>위의 웹 서비스의 작동 순서에서, 3번의 과정 중에 WAS는 데이터베이스에 요청/응답을 통해 동적 요청을 처리하고, 그 결과를 전달한다. </p>
<hr>
<h2 id="빌드와-배포">빌드와 배포</h2>
<h3 id="빌드">빌드</h3>
<p>빌드는 소스 코드를 실행 가능한 소프트웨어 산출물로 만드는 과정으로, jar/war 파일 등이 결과물이다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/43cd7d5d-0249-43c1-a300-53589ef96a1c/image.png" alt="IntelliJ에서의 Build"></p>
<pre><code>IntelliJ에서의 Build</code></pre><p>빌드는 자동으로 소스 코드를 컴파일, 테스트, 정적 분석 등의 과정을 거쳐 실행 가능한 어플리케이션으로 생성해주는 과정을 거친다. 이때 이러한 일련의 과정을 도와주는 것을 빌드 도구라하고, Java에는 Maven, Gradle 등이 있다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/f0acb7ee-4ed3-43da-bbe0-f272b017422b/image.png" alt="Java의 Gradle 사용 예"></p>
<pre><code>Java의 Gradle 사용 예</code></pre><p>Spring intializr를 사용해 프로젝트를 생성할 때, Gradle로 만들면 자동으로 Gradle 관련 파일들이 프로젝트에 생성된다. 이를 통해 jar 파일을 만들고, 이 파일을 서버 컴퓨터에서 실행한다.</p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/22b9acf0-d0ac-4066-a679-57f5938c102b/image.png" alt="프로젝트의 Gradle 파일들"></p>
<pre><code>프로젝트의 Gradle 파일들</code></pre><pre><code class="language-bash"># .jar 파일을 ./build/libs에 생성하고, java -jar로 파일을 실행한다.
./gradlew clean build
cd build/libs
java -jar &quot;생성된 .jar 파일 이름&quot;</code></pre>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/ccf01a32-542e-428f-ae05-787bb2d8f86a/image.png" alt=".jar 파일을 실행 결과"></p>
<pre><code>.jar 파일 실행 결과</code></pre><p>jar 파일을 성공적으로 빌드하고 실행하면 IDE에서 실행한 것과 같은 결과를 얻을 수 있다.</p>
<h3 id="배포-deployment">배포 (Deployment)</h3>
<p>처음에는 배포라는 것에 막연히 어렵게만 생각했었다. 이렇게 빌드를 통해 얻은 실행 가능한 파일(jar/war)을 클라이언트가 접근할 수 있는 환경에 배치하는 것을 배포라고 생각하니 간단하다. </p>
<p>이 환경을 설정하는 것에 AWS의 다양한 기능들을 포함한 각종 기술들을 적용할 수 있다. 공부를 하면 할수록 어려워 차근차근 공부해봐야 할 듯 하다. </p>
<h3 id="cicd">CI/CD</h3>
<p>배포와 관련된 개념을 찾아보면 항상 같이 따라나오는 내용이다. </p>
<p>CI/CD는 &quot;Continuous Integration&quot; 및 &quot;Continuous Deployment&quot;의 약어로, 소프트웨어 개발과 배포 프로세스를 지속적으로 자동화하는 개념이다. 이를 통해 품질을 향상시키고 개발자 팀이 소프트웨어를 더 빠르게, 더 안정적으로 배포할 수 있게한다. </p>
<p>나는 배포를 하는 하나의 방법으로 이해했다. GitHub Action과 AWS의 Elastic Beanstalk을 통해 무중단 CI/CD를 실습해보았다. </p>
<p>Github Action을 통해 배포하려는 브랜치에 커밋이 merge 되면 이를 테스트하고 jar 파일까지 생성한다. </p>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/66f035c8-04ac-48ef-94b9-2122f858b9df/image.png" alt="Github Action을 실행하기 위한 yml 파일"></p>
<pre><code>Github Action을 실행하기 위한 yml 파일</code></pre><p>Elastic Beanstalk은 새로운 EC2를 생성해서 jar 파일을 실행해준다. </p>
<hr>
<blockquote>
<p><strong>참고자료</strong></p>
</blockquote>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Learn/Common_questions/Web_mechanics/How_does_the_Internet_work">https://developer.mozilla.org/ko/docs/Learn/Common_questions/Web_mechanics/How_does_the_Internet_work</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Learn/Getting_started_with_the_web/How_the_Web_works">https://developer.mozilla.org/ko/docs/Learn/Getting_started_with_the_web/How_the_Web_works</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Learn/Common_questions/Web_mechanics/How_does_the_Internet_work">https://developer.mozilla.org/ko/docs/Learn/Common_questions/Web_mechanics/How_does_the_Internet_work</a></li>
<li><a href="https://bunnnybin.tistory.com/entry/%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4%EC%9D%98-%EA%B5%AC%EC%A1%B0">https://bunnnybin.tistory.com/entry/%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4%EC%9D%98-%EA%B5%AC%EC%A1%B0</a></li>
<li><a href="https://msmk530.tistory.com/124">https://msmk530.tistory.com/124</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 람다와 스트림]]></title>
            <link>https://velog.io/@jong_sense/Java-%EB%9E%8C%EB%8B%A4%EC%99%80-%EC%8A%A4%ED%8A%B8%EB%A6%BC</link>
            <guid>https://velog.io/@jong_sense/Java-%EB%9E%8C%EB%8B%A4%EC%99%80-%EC%8A%A4%ED%8A%B8%EB%A6%BC</guid>
            <pubDate>Tue, 09 Jan 2024 06:01:43 GMT</pubDate>
            <description><![CDATA[<h2 id="lambda">Lambda</h2>
<p>메서드를 ‘하나의 식’으로 표현한 것이다.</p>
<p>메서드의 이름과 반환값이 없어지므로 ‘익명함수’라고도 한다.</p>
<p>메서드의 이름과 반환 타입을 제거하고 매개변수 선언부와 몸통 { } 사이에 “<code>-&gt;</code>”를 추가한다.</p>
<p><strong>장점</strong></p>
<ul>
<li>불필요한 코드를 줄이고, 이해를 도와준다.</li>
<li>람다 표현식은 메소드로 전달할 수 있는 익명함수를 단순화한 코드의 블록이다.</li>
<li>특정 클래스에 종속되지 않으며 함수라는 이름으로 명명한다.</li>
<li>함수 자체를 전달 인자로 쓰거나, 변수에 저장하는 것이 가능하다.</li>
</ul>
<p>함수형 인터페이스를 타입으로 사용해 람다식을 변수로서 받을 수 있다.</p>
<pre><code class="language-java">Runnable runnable = new Runnable() {
        @Override
        public void run() {
                // ~
        }
);

Runnable runnable = () -&gt; { ... }; // Lambda</code></pre>
<p>이때 함수형 인터페이스를 <code>@FunctionalInterface</code>를 통해 직접 정의할 수도 있지만, 일반적으로 java.util.function 패키지에 정의된 함수형 인터페이스를 사용한다.</p>
<p><strong>java.util.function 패키지의 기본적인 함수형 인터페이스</strong></p>
<table>
<thead>
<tr>
<th>함수형 인터페이스</th>
<th>메서드</th>
<th>반환타입</th>
<th>함수 디스크립터</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>java.lang.Runnable</td>
<td>run()</td>
<td>void</td>
<td>() -&gt; void</td>
<td>매개변수x, 반환값x</td>
</tr>
<tr>
<td>Supplier<T></td>
<td>get()</td>
<td>T</td>
<td>() -&gt; T</td>
<td>매개변수x, 반환값o</td>
</tr>
<tr>
<td>Consumer<T></td>
<td>accept(T t)</td>
<td>void</td>
<td>T -&gt; void</td>
<td>매개변수o, 반환값x</td>
</tr>
<tr>
<td>Function&lt;T, R&gt;</td>
<td>apply(T t)</td>
<td>R</td>
<td>T -&gt; R</td>
<td>매개변수 1개, 반환값o</td>
</tr>
<tr>
<td>Predicate<T></td>
<td>test(T t)</td>
<td>boolean</td>
<td>T -&gt; boolean</td>
<td>매개변수 1개, 반환값o</td>
</tr>
<tr>
<td>…</td>
<td>…</td>
<td>…</td>
<td>…</td>
<td>…</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/jong_sense/post/3c4cd3ac-050f-45cc-99a1-c8df22c4f264/image.png" alt=""></p>
<p><code>forEach()</code>는 Consumer 타입의 함수형 인터페이스를 매개변수로 받는다.</p>
<blockquote>
<p><strong>참고 영상</strong>
<a href="https://www.youtube.com/watch?v=u5sLXaWo7tY">https://www.youtube.com/watch?v=u5sLXaWo7tY</a>
<a href="https://www.youtube.com/watch?v=cHIKmmWFwng">https://www.youtube.com/watch?v=cHIKmmWFwng</a></p>
</blockquote>
<hr>
<h2 id="stream이란">Stream이란?</h2>
<p>Java8 API에서 추가된 람다를 사용하는 기술 중 하나이다.</p>
<p>선언형으로 컬렉션 데이터를 처리할 수 있도록 도와준다.</p>
<p>다양한 데이터 소스를 표준화된 방법으로 다룰 수 있다.</p>
<p>Collection Framework를 통해 관리하는 데이터를 처리하기 위해 주로 사용한다.</p>
<pre><code class="language-java">List&lt;String&gt; list = Arrays.asList(&quot;Lee&quot;, &quot;Kim&quot;, &quot;Park&quot;);

// 기존
Iterator&lt;String&gt; it = list.iterator();
while (it.hasNext()) {
        System.out.println(it.next());
}

// stream 활용
list.stream().forEach(name -&gt; System.out.println(name));</code></pre>
<p>Stream API의 최상위 인터페이스는 BaseStream 인터페이스지만 직접 사용하지는 않는다.</p>
<p>Stream 인터페이스는 BaseStream을 상속하는 인터페이스이다.</p>
<p>여러 메소드들을 정의하고 있으며 많은 메소드들의 파라미터에 람다와 메소드 참조가 필요하다.</p>
<table>
<thead>
<tr>
<th>메소드</th>
<th>기능</th>
</tr>
</thead>
<tbody><tr>
<td>long count()</td>
<td>해당 스트림에 포함된 항목의 수를 반환한다.</td>
</tr>
<tr>
<td>Stream concat(Stream, Stream)</td>
<td>파라미터로 전달되는 두 개의 스트림을 하나의 스트림으로 반환한다.</td>
</tr>
<tr>
<td>R collect(Collector)</td>
<td>스트림의 항목들을 컬렉션 타입의 객체로 반환한다.</td>
</tr>
<tr>
<td>Stream filter(Predicate)</td>
<td>스트림의 항목들을 파라미터의 조건에 따라 필터링하고 결과 항목들을 스트림 형태로 반환한다.</td>
</tr>
<tr>
<td>void forEach(Consumer)</td>
<td>스트림 항목들을 순회한다.</td>
</tr>
<tr>
<td>Optional reduce(BinaryOperator)</td>
<td>람다 표현식을 기반으로 데이터를 소모하고 그 결과를 반환한다.</td>
</tr>
<tr>
<td>Object toArray()</td>
<td>스트림 항목들을 배열 객체로 전환한다.</td>
</tr>
<tr>
<td>Stream sorted()</td>
<td>스트림 항목들에 대해 정렬하고 이를 스트림으로 반환한다.</td>
</tr>
<tr>
<td>…</td>
<td>…</td>
</tr>
</tbody></table>
<h3 id="stream-객체를-생성하는-방법"><strong>Stream 객체를 생성하는 방법</strong></h3>
<p><strong>Collection 객체 사용</strong> </p>
<p>처리할 데이터가 이미 존재하고, 이를 처리할 때 일반적으로 사용하는 방식이다.</p>
<p>List 인터페이스는 Collection 인터페이스를 상속받고, Collection에는 Stream 객체를 반환하는 <code>stream()</code> default 메소드가 있다.</p>
<p>한번 생성한 Stream은 사용 후 다시 사용할 수 없고, 데이터에 대한 처리가 완료되면 종료된다.</p>
<pre><code class="language-java">List&lt;String&gt; list = Arrays.asList(&quot;Lee&quot;, &quot;Kim&quot;, &quot;Park&quot;);

Stream&lt;String&gt; stream = list.stream();
stream.forEach(name -&gt; System.out.println(name));</code></pre>
<p><strong>Stream.Builder를 사용</strong></p>
<p>데이터가 존재하지 않고, Stream을 만들었을 때, 직접 데이터를 추가해서 사용하고자 할 때 사용한다.</p>
<table>
<thead>
<tr>
<th>메소드</th>
<th>기능</th>
</tr>
</thead>
<tbody><tr>
<td>void accept(T)</td>
<td>스트림 빌터에 데이터를 추가한다.</td>
</tr>
<tr>
<td>Stream.Builder<T> add(T)</td>
<td>스트림 빌더에 데이터를 추가하고, 스트림을 반환한다.</td>
</tr>
<tr>
<td>Stream<T> build()</td>
<td>스트림 빌더에 데이터 추가를 종료하고, 스트림을 반환한다.</td>
</tr>
</tbody></table>
<pre><code class="language-java">Stream.Builder&lt;String&gt; builder = Stream.builder();
builder.accept(&quot;Kim&quot;);
builder.accept(&quot;Lee&quot;);
builder.accept(&quot;Park&quot;);

Stream&lt;String&gt; stream = builder.build();
stream.forEach(name -&gt; System.out.println(name));</code></pre>
<blockquote>
<p><strong>참고 영상</strong>
<a href="https://www.youtube.com/watch?v=mUJGw_b6DfI&amp;t=5s">https://www.youtube.com/watch?v=mUJGw_b6DfI&amp;t=5s</a>
<a href="https://www.youtube.com/watch?v=ASQDml4NyKM">https://www.youtube.com/watch?v=ASQDml4NyKM</a></p>
</blockquote>
<hr>
<h2 id="stream-연산">Stream 연산</h2>
<p>스트림의 연산은 각 연산의 연결을 통해 파이프라인을 구성할 수 있다.</p>
<p>연산 처리는 스트림 객체의 생성, 중간 연산, 최종 연산 단계로 구분할 수 있다.</p>
<pre><code>Collection Data -&gt; Filter -&gt; Sort -&gt; Map -&gt; Collect</code></pre><h3 id="중간-연산"><strong>중간 연산</strong></h3>
<p><code>filter</code>, <code>map</code>과 같은 중간 연산은 Stream을 반환하고, 연속해서 호출하는 Method Chaining으로 구현 가능하다.</p>
<p>최종 연산이 실행되어야 중간 연산이 처리되므로, 중간 연산들로만 구성된 메소드 체인은 실행되지 않는다.</p>
<table>
<thead>
<tr>
<th>연산</th>
<th>반환 형식</th>
<th>연산 인수</th>
</tr>
</thead>
<tbody><tr>
<td>filter</td>
<td>Stream<T></td>
<td>Predicate<T></td>
</tr>
<tr>
<td>map</td>
<td>Stream<T></td>
<td>Function&lt;T, R&gt;</td>
</tr>
<tr>
<td>limit</td>
<td>Stream<T></td>
<td></td>
</tr>
<tr>
<td>sorted</td>
<td>Stream<T></td>
<td>Comparator<T></td>
</tr>
<tr>
<td>distinct</td>
<td>Stream<T></td>
<td></td>
</tr>
<tr>
<td>peek</td>
<td>Stream<T></td>
<td>Consumer<T></td>
</tr>
<tr>
<td>skip</td>
<td>Stream<T></td>
<td></td>
</tr>
</tbody></table>
<p><strong>필터링</strong></p>
<p>전체 데이터에서 불필요한 데이터를 없에고, 원하는 데이터를 추출하기 위한 과정으로 중간 연산이다.</p>
<p>Stream API의 <code>filter()</code>, <code>distinct()</code>와 같은 메소드를 이용하여 데이터 추출이나 중복 데이터를 제거한다.</p>
<p>이때 중복 데이터를 제거하는 <code>distinct()</code>는 병렬 스트림에서의 성능과, <code>equals()</code> 메소드의 필요성을 고려해야 한다.</p>
<p><strong>정렬</strong></p>
<p>특정 조건에 따라 데이터를 정렬하고, 이를 다시 Stream으로 반환한다.</p>
<p>sorted()를 이용한 정렬을 위해서는 객체들이 Comparable 인터페이스를 구현한 클래스여야 한다.</p>
<p>또한 Comparator 인터페이스를 통해 다른 방식의 정렬도 사용 가능하다.</p>
<pre><code class="language-java">List&lt;T&gt; list = new ArrayList&lt;&gt;();

// Comparable을 통한 정렬의 경우 인자를 전달받지 않는다
list.stream()
        .sorted()
        .forEach(System.out::println);

// Comparator를 사용한 정렬의 경우 인자를 전달받는다.
list.stream()
        .sorted(Comparator.comparing(T::getThings))
        .forEach(System.out::printlin);</code></pre>
<p><strong>맵핑</strong></p>
<p>스트림이 관리하는 데이터를 다른 형태의 데이터로 반환해준다.</p>
<p><code>map()</code>, <code>mapToInt()</code>, <code>mapToDouble()</code>, <code>mapToLong()</code> 등이 있다.</p>
<p>Function을 파라미터로 받는다.</p>
<pre><code class="language-java">List&lt;Curtomers&gt; customers = new ArrayList&lt;&gt;();
customers.add(new Customer(&quot;Kim&quot;, 33));
customers.add(new Customer(&quot;Lee&quot;, 28));
customers.add(new Customer(&quot;Park&quot;, 31));
customers.add(new Customer(&quot;Yoon&quot;, 25));

List&lt;String&gt; names = customers.stream()
                .map(Customer::getName)
                .collect(Collectors.toList());</code></pre>
<h3 id="최종-연산"><strong>최종 연산</strong></h3>
<table>
<thead>
<tr>
<th>연산</th>
<th>반환 형식</th>
</tr>
</thead>
<tbody><tr>
<td>forEach</td>
<td>스트림의 각 요소를 소비하며 람다식을 적용하고, void형을 반환한다.</td>
</tr>
<tr>
<td>count</td>
<td>스트림 요소의 수를 Long형으로 반환한다.</td>
</tr>
<tr>
<td>collect</td>
<td>List, Map 형태의 컬렉션을 반환한다.</td>
</tr>
<tr>
<td>sum</td>
<td>스트림의 모든 요소에 대한 합을 반환한다.</td>
</tr>
<tr>
<td>reduce</td>
<td>스트림의 요소를 하나씩 줄여가며 연산을 수행하고 결과를 Optional로 반환한다.</td>
</tr>
<tr>
<td>allMatch</td>
<td>파라미터로 전달되는 람다식 기준으로 데이터가 모두 일치하는지 확인한다.</td>
</tr>
<tr>
<td>findFirst</td>
<td>스트림 데이터 중 첫번째 데이터를 반환한다.</td>
</tr>
<tr>
<td>reduce</td>
<td>스트림 데이터 중 임의의 데이터를 반환한다.</td>
</tr>
<tr>
<td>…</td>
<td>…</td>
</tr>
</tbody></table>
<pre><code class="language-java">List&lt;String&gt; list = Arrays.asList(&quot;Kim&quot;, &quot;Lee&quot;, &quot;Park&quot;, &quot;Yoon&quot;);
        list.stream()
                .filter(name -&gt; name.length() &gt; 3) // 중간 연산
                .sorted()
                .forEach(System.out::println); // 최종 연산</code></pre>
<blockquote>
<p><strong>참고 영상</strong>
<a href="https://www.youtube.com/watch?v=v7n66yvM8bw&amp;list=TLPQMDkwMTIwMjSH9w8ohdh0rQ&amp;index=2">https://www.youtube.com/watch?v=v7n66yvM8bw&amp;list=TLPQMDkwMTIwMjSH9w8ohdh0rQ&amp;index=2</a>
<a href="https://www.youtube.com/watch?v=7cVPlgyx3d8">https://www.youtube.com/watch?v=7cVPlgyx3d8</a>
<a href="https://www.youtube.com/watch?v=0ZJr0O1DN8g">https://www.youtube.com/watch?v=0ZJr0O1DN8g</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Spring의 예외처리 방법]]></title>
            <link>https://velog.io/@jong_sense/Spring-Spring%EC%9D%98-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@jong_sense/Spring-Spring%EC%9D%98-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Thu, 04 Jan 2024 13:08:38 GMT</pubDate>
            <description><![CDATA[<h1 id="spring의-예외-처리-방법">Spring의 예외 처리 방법</h1>
<h2 id="요약">요약</h2>
<p>Spring의 기본적인 예외 처리 방법이 WAS까지 에러가 전달된다는 문제점과 코드의 가독성 등의 이유로 HandlerExceptionResolver를 구현하여 예외를 처리한다.</p>
<p>구현체들 중 ExceptionHandlerExceptionResolver, 그리고 이를 위해 @RestControllerAdvice를 사용하는 이유를 공부해 보았다.</p>
<hr>
<h2 id="spring의-기본적인-예외-처리-방법">Spring의 기본적인 예외 처리 방법</h2>
<p>컨트롤러에서 예외가 발생했을 때, 별도의 예외처리를 하지 않으면 WAS까지 에러가 전달됨 
→ WAS는 에플리케이션에서 처리를 못하는 예외라 판단하고 컨트롤러를 한번 더 호출</p>
<hr>
<h2 id="spring이-제공하는-예외-처리-방법">Spring이 제공하는 예외 처리 방법</h2>
<p>Java의 예외 처리 방법: try-catch ⇒ 모든 코드에 붙이는 것은 비효율적</p>
<h3 id="handlerexceptionresolver-인터페이스">HandlerExceptionResolver 인터페이스</h3>
<p>예외처리 전략 추상화 → 메인 로직으로부터 분리</p>
<p>예외를 catch하고 HTTP 상태, 응답 메세지 설정</p>
<p>WAS는 해당 요청을 정상적인 응답으로 인식하여 에러 전달X</p>
<p><code>HandlerExceptionResolver</code> 구현체들을 빈으로 등록해서 관리한다.</p>
<ul>
<li><code>DefaultErrorAttributes</code>: 에러 속성을 저장하며 직접 예외를 처리하지는 않는다.</li>
<li><code>ExceptionHandlerExceptionResolver</code>: 에러 응답을 위한 Controller나 ControllerAdvice에 있는 ExceptionHandler를 처리함</li>
<li><code>ResponseStatusExceptionResolver</code>: Http 상태 코드를 지정하는 @ResponseStatus 또는 ResponseStatusException를 처리함</li>
<li><code>DefaultHandlerExceptionResolver</code>:  Spring 내부의 기본 예외들을 처리</li>
</ul>
<p>아래의 도구들로 ExceptionResolver를 동작시켜 에러를 처리한다.</p>
<ul>
<li><code>@ResponseStatus</code></li>
<li><code>@ResponseStatusException</code></li>
<li><code>@ExceptionHandler</code></li>
<li><code>@ControllerAdvice, RestControllerAdvice</code></li>
</ul>
<p><code>@ResponseStatus</code>: 에러 HTTP 상태를 변경하도록 도와주는 어노테이션</p>
<p>Exception 클래스 자체, 메소드에 @ExceptionHandler와 함께, 클래스에 @RestControllerAdvice와 함께 적용 가능</p>
<ul>
<li>에러 응답의 내용 수정 불가</li>
<li>같은 예외는 같은 상태와 에러 메세지를 반환</li>
<li>별도의 응답 상태가 필요하다면 예외 클래스를 추가해야 함</li>
<li>WAS까지 예외가 전달되고 WAS의 에러 요청 전달</li>
<li>외부에서 정의한 예외 클래스에는 @ResponseStatus 사용할 수 없음</li>
</ul>
<p><code>@ResponseStatusException</code>: <code>@ResponseStatus</code>와  마찬가지로<code>ResponseStatusExceptionResolver</code>가 에러를 처리</p>
<p>기본적인 예외 처리를 빠르게 적용할 수 있어 손쉽게 프로토타이핑 할 수 있고, HttpsStatus를 직접 설정할 수 있다는 장점이 있음</p>
<ul>
<li>직접 예외 처리를 프로그래밍함</li>
<li>예외 처리 코드가 중복될 수 있음</li>
<li>Spring 내부의 예외 처리 어려움</li>
<li>예외가 WAS까지 전달되고 WAS의 에러 요청 전달이 진행됨</li>
</ul>
<h3 id="exceptionhandler">@ExceptionHandler</h3>
<p>컨트롤러의 메소드, <code>@ControllerAdvice</code>나 <code>@RestControllerAdvice</code>가 있는 클래스의 메소드에 <code>@ExceptionHandler</code> 어노테이션을 추가하여 에러를 처리한다.</p>
<p>발생한 예외는 <code>ExceptionHandlerExceptionResolver</code>에 의해 처리</p>
<p>Exception 클래스들을 속성으로 받아 처리할 예외를 지정
예외 클래스를 지정하지 않는다면 파라미터에 설정된 에러 클래스를 처리</p>
<p>에러 응답을 자유롭게 다룰 수 있음</p>
<ul>
<li>code: 어떤 종류의 에러인지 알려주는 에러 코드</li>
<li>message: 왜 에러가 발생했는지 설명</li>
<li>errors: 어느 값이 잘못되어 @Valid에 검증이 실패한 것인지를 위한 에러 목록</li>
</ul>
<pre><code class="language-java">@ExceptionHandler(NoSuchElementFoundException.class)
public ResponseEntity&lt;ErrorResponse&gt; handleItemNotFoundException(NoSuchElementFoundException exception) {
    ...
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity&lt;ErrorResponse&gt; handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
    ...
}

@ExceptionHandler(Exception.class)
public ResponseEntity&lt;ErrorResponse&gt; handleAllUncaughtException(Exception exception) {
    ...
}</code></pre>
<p>Spring은 예외 발생 시 가장 구체적인 예외 핸들러를 찾고, 없으면 부모 예외의 핸들러를 찾음</p>
<p>이때 @ExceptionHandler에 등록된 예외 클래스와 파라미터로 받는 예외 클래스가 동일 해야 함</p>
<pre><code class="language-java">@ExceptionHandler(Exception.class)
public ResponseEntity&lt;ErrorResponse&gt; handleAllUncaughtException(Exception exception) {
    ...
}</code></pre>
<h3 id="controlleradvice와-restcontrolleradvice">@ControllerAdvice와 @RestControllerAdvice</h3>
<p>여러 컨트롤러에 대해 전역적으로 <code>@ExceptionHandler</code>를 적용</p>
<p>응답을 Json으로 내려주는 차이점 </p>
<pre><code class="language-java">@RestControllerAdvice(annotations = {RestController.class})
public class ExceptionAdvice extends ResponseEntityExceptionHandler {

        @ExceptionHandler(value = GeneralException.class)
      public ResponseEntity&lt;Object&gt; onThrowException(GeneralException generalException, HttpServletRequest request) {
          ErrorReasonDTO errorReasonHttpStatus = generalException.getErrorReasonHttpStatus();
          return handleExceptionInternal(generalException,errorReasonHttpStatus,null,request);
      }

}</code></pre>
<p><strong>장점:</strong></p>
<ul>
<li>하나의 클래스로 모든 컨트롤러에 대한 전역적인 예외 처리</li>
<li>직접 정의한 에러 응답을 일관성 있게 클라이언트에게 내려줄 수 있음</li>
<li>별도의 try-catch문이 없음</li>
</ul>
<p><strong>주의점:</strong></p>
<ul>
<li>한 프로젝트당 하나의 ControllerAdvice만 관리</li>
<li>여러 ControllerAdvice 필요시 basePackages나 어노테이션 지정</li>
<li>직접 구현한 Exception 클래스들은 한 공간에서 관리</li>
</ul>
<hr>
<h2 id="실제-적용">실제 적용</h2>
<pre><code class="language-java">@RestControllerAdvice(annotations = {RestController.class})
public class ExceptionAdvice extends ResponseEntityExceptionHandler {

        @ExceptionHandler(value = GeneralException.class)
    public ResponseEntity&lt;Object&gt; onThrowException(GeneralException generalException, HttpServletRequest request) {
        ErrorReasonDTO errorReasonHttpStatus = generalException.getErrorReasonHttpStatus();
        return handleExceptionInternal(generalException,errorReasonHttpStatus,null,request);
    }

        ...

}</code></pre>
<pre><code class="language-java">@Getter
@AllArgsConstructor
public enum ErrorStatus implements BaseErrorCode {

        // 가장 일반적인 응답
    _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, &quot;COMMON500&quot;, &quot;서버 에러, 관리자에게 문의 바랍니다.&quot;),
    _BAD_REQUEST(HttpStatus.BAD_REQUEST, &quot;COMMON400&quot;, &quot;잘못된 요청입니다.&quot;),

        ...

        // 멤버 관련 응답
        MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, &quot;MEMBER4001&quot;, &quot;사용자가 없습니다.&quot;),

        ...

        // For test
    TEMP_EXCEPTION(HttpStatus.BAD_REQUEST, &quot;TEMP4001&quot;, &quot;테스트용입니다.&quot;);

        ...

}</code></pre>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class TempQueryServiceImpl implements TempQueryService {

    @Override
    public void CheckFlag(Integer flag) {
        if (flag == 1) {
            throw new TempHandler(ErrorStatus.TEMP_EXCEPTION);
        }
    }
}</code></pre>
<pre><code class="language-java">public class TempHandler extends GeneralException {

    public TempHandler(BaseErrorCode errorCode) {
        super(errorCode);
    }
}</code></pre>
<pre><code class="language-java">@Getter
@AllArgsConstructor
public class GeneralException extends RuntimeException {

    private BaseErrorCode code;

    public ErrorReasonDTO getErrorReason() {
        return this.code.getReason();
    }

    public ErrorReasonDTO getErrorReasonHttpStatus() {
        return this.code.getReasonHttpStatus();
    }
}</code></pre>
<p>서비스에서 예외 발생이 확인되면 <code>TempHandler</code>를 통해 <code>GeneralException</code>을 생성하고, 이는 <code>RuntimeException</code>을 상속 받았기 예외로 던져진다.</p>
<p>그럼 <code>@RestControllerAdvice</code>가 있는 <code>ExceptionAdvice</code>에서 <code>@ExceptionHandler</code>인 적절한 메소드를 찾아 예외를 처리하게 되는 것이다.</p>
<hr>
<blockquote>
<p><strong>참고 자료</strong>
<a href="https://mangkyu.tistory.com/204#recentEntries">https://mangkyu.tistory.com/204#recentEntries</a>
[MangKyu&#39;s Diary:티스토리]</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[사전 지식 공부 - CNN 모델]]></title>
            <link>https://velog.io/@jong_sense/%EC%82%AC%EC%A0%84-%EC%A7%80%EC%8B%9D-%EA%B3%B5%EB%B6%80-CNN-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@jong_sense/%EC%82%AC%EC%A0%84-%EC%A7%80%EC%8B%9D-%EA%B3%B5%EB%B6%80-CNN-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Wed, 27 Dec 2023 06:25:12 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[데이터 전처리 - 음원 추출]]></title>
            <link>https://velog.io/@jong_sense/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC-%EC%9D%8C%EC%9B%90-%EC%B6%94%EC%B6%9C</link>
            <guid>https://velog.io/@jong_sense/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC-%EC%9D%8C%EC%9B%90-%EC%B6%94%EC%B6%9C</guid>
            <pubDate>Wed, 27 Dec 2023 06:23:50 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[데이터 전처리 - Praat을 이용한 레이블링]]></title>
            <link>https://velog.io/@jong_sense/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC-Praat%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%A0%88%EC%9D%B4%EB%B8%94%EB%A7%81</link>
            <guid>https://velog.io/@jong_sense/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC-Praat%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%A0%88%EC%9D%B4%EB%B8%94%EB%A7%81</guid>
            <pubDate>Wed, 27 Dec 2023 06:22:42 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[클러치]]></title>
            <link>https://velog.io/@jong_sense/%ED%81%B4%EB%9F%AC%EC%B9%98</link>
            <guid>https://velog.io/@jong_sense/%ED%81%B4%EB%9F%AC%EC%B9%98</guid>
            <pubDate>Wed, 27 Dec 2023 06:11:31 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[개요]]></title>
            <link>https://velog.io/@jong_sense/%EC%9D%8C%EC%84%B1%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%9B%84%EB%91%90%EC%95%94-%EC%9E%90%EA%B0%80%EC%84%A0%EB%B3%84</link>
            <guid>https://velog.io/@jong_sense/%EC%9D%8C%EC%84%B1%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%9B%84%EB%91%90%EC%95%94-%EC%9E%90%EA%B0%80%EC%84%A0%EB%B3%84</guid>
            <pubDate>Wed, 27 Dec 2023 06:09:25 GMT</pubDate>
            <description><![CDATA[<p>2023.06부터 홍익대 AIA 랩실에서 진행한 프로젝트이다.
2023.06 ~ 2023.11 데이터 전처리 및 모델링에 필요한 지식 공부
2023.12 모델 개발
2023.12.18 바이오헬스 종합설계경진대회 참가
2023.12.22 CDE 학회 논문 제출</p>
]]></description>
        </item>
    </channel>
</rss>