<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sloth.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 25 Jul 2023 15:04:31 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sloth.log</title>
            <url>https://images.velog.io/images/sloth_/profile/210efb83-3542-4aa4-90e7-0370437911aa/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sloth.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sloth_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Websocket 이 뭐죠? (이론편)]]></title>
            <link>https://velog.io/@sloth_/Websocket-%EC%9D%B4-%EB%AD%90%EC%A3%A0-%EC%9D%B4%EB%A1%A0%ED%8E%B8</link>
            <guid>https://velog.io/@sloth_/Websocket-%EC%9D%B4-%EB%AD%90%EC%A3%A0-%EC%9D%B4%EB%A1%A0%ED%8E%B8</guid>
            <pubDate>Tue, 25 Jul 2023 15:04:31 GMT</pubDate>
            <description><![CDATA[<p>사내 서비스에서 사용자 알림 기능을 구현할 때 웹 소켓을 사용하기에 이를 정리하고 이해해보려 한다.</p>
<h3 id="웹-소켓이란">웹 소켓이란?</h3>
<p>웹 소켓이란 클라이언트와 서버가 http 프로토콜 안에서 전이중 통신을 할 수 있게 하는 통신 프로토콜이다.</p>
<blockquote>
<p>전이중 통신이란, 양방향 통신 중에서도 동시에 보내면서 받을 수도 있는 통신을 말한다.</p>
</blockquote>
<p>채팅방처럼 보내는 도중에 응답을 받을 수도 있는 그런 것을 떠올리면 된다.</p>
<p>위의 설명에서도 나오다시피 웹 소켓의 예제들은 주로 채팅 서비스를 구현하면서 많이들 공부하는데, 
이는 웹 소켓의 전이중 통신 방식을 제일 잘 보여주는 것이 채팅 서비스 기능이기 때문이다.</p>
<p>꼭 채팅이 아니더라도 실시간 알림 같은 기능으로도 이용할 수 있다.</p>
<h3 id="등장-배경사실은-추측에-가깝다">등장 배경(<del>사실은 추측에 가깝다</del>)</h3>
<p>웹 서비스는 본래 사용자의 요청이 들어오면 해당 요청에 대한 응답을 전해주고 연결이 종료된다.</p>
<p>하지만 웹 서비스가 발전하면서 기존의 요청에 대한 응답만 전달할 것이 아니라 특정 이벤트에 대한 사용자 알림이나 실시간으로 특정 정보를 동기화해야 하는 기능이 요구되면서,
해당 요구 사항을 만족하기 위해서는 사용자가 특정 시간마다 계속 요청을 보내거나 특정 시간 동안 응답을 지연하되 해당 응답을 보내고 나면 곧바로 요청하는 식의 기술이 필요했다.</p>
<p>웹 소켓은 이러한 것을 소켓 통신의 형태를 통해 해결하려고 개발되지 않았나 추측하고 있다.</p>
<h3 id="웹-소켓">웹 &quot;소켓&quot;</h3>
<p>웹 소켓은 말 그대로 http 통신 위에서 소켓 통신의 형태를 구현한 프로토콜이다.</p>
<p>당연히 통신 프로토콜로 TCP를 이용하고 있으며 첫 연결 시에 Handshake를 진행하여야 하며 통신 포트로 80번 포트(http)와 443 포트(https)를 같이 이용한다.
이 때문에 별도의 포트 설정은 하지 않아도 된다. (기본 포트를 사용한다는 가정 하에)</p>
<p>핸드쉐이크란 말 그대로 통신을 맺기 전 악수를 나누며 서로를 소개하는 인사같은 것이다.</p>
<p>대표적으로 TCP 통신 시 진행하는 3-Way hand shake가 있는데, 웹 소켓도 TCP를 사용하는 프로토콜이므로 웹 소켓만의 핸드쉐이크를 진행한 후 연결을 유지한다.</p>
<p>웹 소켓의 핸드쉐이크는 최초 연결을 요청하는 한 번의 http 요청과 해당 요청에 대한 응답으로 이루어진다.</p>
<p>최초 요청은 반드시 HTTP/1.1 버전 이상을 사용해야 하며, GET 메소드를 통해 요청하여야 한다. </p>
<p>MDN의 예제에 따르면 요청을 보내는 클라이언트는</p>
<blockquote>
<p>GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13</p>
</blockquote>
<p>라는 내용으로 요청을 하는데, </p>
<p>이는 해당 요청을 받는 서버에 Connection 헤더와 Upgrade 헤더를 전달하며 연결을 웹 소켓으로 변경하자는 요청을 보내고 있음을 볼 수 있다.</p>
<p>서버는 이에 대한 답변으로</p>
<blockquote>
<p>HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=</p>
</blockquote>
<p>을 보내 해당 요청에 대한 응답을 한다.</p>
<p>이 때 웹 소켓 연결을 정상적으로 진행할 수 있을 경우 101 상태 코드를 전달하고, 어떠한 이유로 현재 웹 소켓 연결이 불가능할 경우 4XX 대의 상태 코드를 응답한다. </p>
<p>응답으로 101 코드를 전달받았을 경우, 웹 소켓이 연결되며 이후 웹 소켓을 통해 소켓 통신을 진행할 수 있다.</p>
<h3 id="sockjs을-이용해보자">SockJS을 이용해보자</h3>
<p>위의 내용을 통해 간략하게라도 웹 소켓이 어떤 것인지, 어떨 때 사용하는지에 대해 이해했을 것이다. </p>
<blockquote>
<p>대충 양방향 통신이 필요할 때 쓰면 좋다는거잖아?</p>
</blockquote>
<p>라고 이해했다면, 잘 이해했다고 말해주고 싶다.</p>
<p>그런데 갑자기 위에서 웹 소켓에 대해 잘 설명해놓고 뜬금없이 SockJS는 또 뭐냐고 물으려 한다면 잠깐만 기다려 주시라.</p>
<p>SockJS는 위에서 설명한 웹 소켓과 비슷한 객체를 구현해놓은 라이브러리이다. </p>
<p>왜 웹 소켓도 아니고 웹 소켓 비슷한 객체라고 설명하냐면, 
기본적으로는 웹 소켓을 사용하지만 웹 소켓을 지원하지 않는 브라우저에 한해서는 웹 소켓이 아닌 다른 방식으로 웹 소켓 비슷한 기능을 제공해주는 라이브러리이기 때문이다.</p>
<p>해당 라이브러리를 사용하면 브라우저에서 해당 기능을 지원하는지에 대한 검증 없이도 웹 소켓을 통해 구현하고자 하는 기능을 구현할 수 있게 되는 것이다.</p>
<p>또한 SockJS는 Spring에서도 Configuration을 추가하여 간단하게 사용할 수 있으므로, 해당 라이브러리를 도입하기로 결정했다.</p>
<p>(<del>사실 사내에서 이미 사용하고 있는 라이브러리라 사용하기로 결정했다</del>)</p>
<h3 id="stomp는-또-뭐시당가">STOMP는 또 뭐시당가</h3>
<p>사내에서는 SockJS와 더불어 STOMP도 같이 사용하고 있는데 STOMP란,</p>
<blockquote>
<p>Simple (or Streaming) Text Orientated Messaging Protocol</p>
</blockquote>
<p>의 약자로 텍스트 기반 메시지 프로토콜을 뜻한다.</p>
<p>웹 소켓을 사용하면서 STOMP 또한 같이 사용하는 이유는 websocket은 텍스트와 바이너리 형식의 데이터를 통신하는 프로토콜이지만, 
웹 소켓 프로토콜 내의 데이터에 대해서는 메시지를 어떠한 방식으로 보낼지, 메시지를 어떠한 방식으로 받는지에 대한 특정한 양식이 없다.</p>
<p>그렇기 때문에 서브 프로토콜로 STOMP를 같이 사용하여 메시지 처리 방식에 대한 규약을 정하는 것이다.</p>
<p>이제 왜 STOMP를 사용하는지에 대해 간단히 소개했으니 STOMP에 대해 간단하게 정리해보자.</p>
<p>STOMP는 Publish-Subscribe의 구조를 하고 있다. </p>
<p>보통의 socket 통신을 떠올리면 Server와 Client가 1:1로 맺어져 통신을 주고 받는 것을 떠올리는데, 
STOMP는 이와 달리 사용자가 특정 경로를 구독하고 있으면 해당 경로로 보내진 메시지를 받아서 확인할 수 있는 구조이다.</p>
<p>요즘 널리 사용되는 정기 구독 시스템을 떠올리면 된다. </p>
<p>특정 잡지를 구독하고 있는 모든 사용자에게 해당 잡지가 배송되는 것처럼, 구독하고 있는 경로로 전달된 메시지는 모두에게 배송되어 읽어볼 수 있게 된다.</p>
<p>또한 pub/sub 구조로 이루어져 있어 중간에 메시지 브로커를 두어 메시지를 관리하기도 용이하고 구독 형식을 하고 있어서 해당 경로를 구독하고 있는 모든 사용자에게 broadcast를 보내기에도 수월하다.</p>
<p>이러한 특성 때문에 채팅 서비스로 예제를 많이 구현하는 것일 수도 있다.</p>
<p>또한 Spring에서도 STOMP에 대한 설정을 지원하고 있어서 메시지 브로커 설정만 해주면 쉽게 STOMP를 프로젝트에 적용할 수 있다.</p>
<p>이제 다음에 작성할 글을 통해, 구현 예제를 살펴보며 좀 더 자세히 파고들어보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[adapter 패턴이란?]]></title>
            <link>https://velog.io/@sloth_/adapter-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@sloth_/adapter-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Fri, 16 Jun 2023 15:44:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 내용은 &quot;JAVA 언어로 배우는 디자인 패턴 입문 3판&quot;을 읽으면서 내용을 정리한 것입니다.</p>
</blockquote>
<h2 id="adapter-패턴이-뭔데">adapter 패턴이 뭔데?</h2>
<p>adapter 패턴이란 이미 사용하던 코드를 필요한 형태로 변환하여 사용할 수 있게끔 할 때 사용하는 패턴이다.</p>
<p>다른 이름으로는 wrapper 패턴이라고도 불리는데, wrapper라는 이름처럼 어떤 것을 감싸서 포장하는 것과 비슷하다.</p>
<p>우리가 흔히 노트북이나 모니터 같은 장비의 전원을 연결할 때 쓰는 어댑터라고 불리는 것들 처럼 어떤 것을 용도에 맞게 변환할 때 사용하는 패턴이다.</p>
<p>기존에 사용하던 비슷한 계열의 클래스들을 모아 인터페이스를 만들 때, 
기존에 이미 작동하던 코드를 수정하지 않고도 인터페이스의 구조에만 맞춰서 사용하고 싶을 때 흔히 어댑터 클래스를 구현하여 사용한다.</p>
<h2 id="adapter-패턴의-종류">adapter 패턴의 종류</h2>
<p>adapter 패턴에는 크게 상속을 통한 구현과 위임을 통한 구현으로 크게 두 가지의 방법으로 분류할 수 있다.
<br/></p>
<blockquote>
<p><strong>상속</strong>은 말 그대로 기존에 사용하던 객체를 상속 받아 필요한 형태로 변환하는 것이고, </p>
</blockquote>
<blockquote>
<p><strong>위임</strong>은 기존에 사용하던 기능은 원래 객체에 위임하고 형태만 동일하게 변환하는 것이다.</p>
</blockquote>
<br/>
설명만 읽으면 무슨 말인지 잘 이해가 되지 않을 수도 있지만, 이는 추후에 예제를 통해 이해하고 넘어가면 될 일이고, 
여기서 중요한 핵심은 위에서도 계속 강조하듯이 **기존 객체를 필요한 형태에 맞춰 변환시키는 패턴**이라는 것이다. 

<p>노트북 어댑터가 가정용 전기를 노트북을 충전시킬 수 있는 전압으로 변환시켜 노트북에게 제공하듯이, 
이 어댑터 패턴도 기존의 사용하던 객체(가정용 전기)를 필요에 맞게 변환시켜(노트북에 맞는 전압으로 변환) 제공하는 패턴이라는 것을 이해하면 된다.</p>
<h2 id="adapter-패턴-예제">adapter 패턴 예제</h2>
<p>이제 위에서 설명했던 상속과 위임을 통한 adapter 패턴을 알아보도록 하자.</p>
<blockquote>
<p>우리는 기존 서비스를 운영 하면서 내부 데이터 분석 및 로깅 용으로 로그 데이터들을 저장할 때 Map 객체의 형태를 통해 데이터를 읽고 또 저장하고 있었습니다.</p>
<p>그런데 내부에 다른 서비스를 운영하던 팀과 회의 중 해당 팀은 Map이 아닌 로그 객체를 이용하고 있었다는 이야기를 듣고, 사내의 로그 저장 방식과 인터페이스에 대해 논의한 후,</p>
<p>사내의 모든 로그를 관리할 때는 Log data class를 이용하기로 논의했습니다. </p>
<p>이후 기존의 Map 객체를 이용해 저장하던 방식을 변경된 인터페이스에 맞춰 로그 객체로 수정하는 작업을 진행하는 대신, 
로그 인터페이스를 구현한 어댑터 클래스를 만들어 관리하기로 했습니다.</p>
</blockquote>
<p>만약 위와 같은 상황이 왔다고 가정할 경우,</p>
<p>이미 팀 내에서 광범위하게 사용하던 로그 생성 객체 자체를 수정하는 것은 해당 로그 객체를 사용하고 있던 모든 부분에 대해서 테스트를 진행해야 하는 상황이 오기 때문에, 
이미 잘 작동하고 있었으나 그저 반환 값을 Map으로 제공하던 것이 문제였던 로그 객체의 반환값만 수정하여 인터페이스에 맞추는 어댑터를 제공하는 것이 작업의 양을 줄일 수 있는 방식이다.</p>
<p>이제 예제를 작성해보자.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/bc24aeea-cf2e-4e25-947b-de69946c806d/image.png" alt="레거시"></p>
<p>실제 상세 구현은 하지 않았으니 이런 구조의 객체였을 것이라고 생각하고 넘어가자.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/bc52c3b7-ee7d-4617-931c-2ae010eb775a/image.png" alt="인터페이스"></p>
<p>이것은 팀 회의 후 나온 로거 인터페이스의 구조이다. </p>
<p>메소드의 역할을 명확하게 하기 위해서 이름도 기존과 다르게 조금 수정됐다.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/f31a6012-e00f-4bd3-a526-6bdd668f874e/image.png" alt="상속"></p>
<p>그리고 위의 코드가 레거시 로거 코드를 변경된 인터페이스에 맞게 변환한 어댑터이다.</p>
<p>기존에 사용하던 MapLogger 클래스를 사용하되 해당 클래스의 반환값 및 파라미터에 맞게 Log 객체를 Map 객체로, Map 객체를 Log 객체로 변환해주었다.</p>
<p>이를 위임을 통한 방식으로 구현하면 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/cb6abd47-d4ba-447f-bf8e-4a39cbc1cb20/image.png" alt="위임"></p>
<p>기존 클래스를 상속하여 구현하는 대신에 기존 클래스에게 핵심 기능에 대한 것을 위임하고 변환에 대한 것만 처리했다.</p>
<p>이렇게 하면 굳이 기존 클래스를 상속하여 구현하지 않아도 기존 클래스의 사용법만 안다면 효과적으로 변환을 처리할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB 정규화 (정규화 편)]]></title>
            <link>https://velog.io/@sloth_/DB-%EC%A0%95%EA%B7%9C%ED%99%94-%EC%A0%95%EA%B7%9C%ED%99%94-%ED%8E%B8</link>
            <guid>https://velog.io/@sloth_/DB-%EC%A0%95%EA%B7%9C%ED%99%94-%EC%A0%95%EA%B7%9C%ED%99%94-%ED%8E%B8</guid>
            <pubDate>Thu, 08 Jun 2023 15:25:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>데이터베이스 정규화에 대해 알아보자.</p>
</blockquote>
<h2 id="데이터베이스-정규화란">데이터베이스 정규화란.</h2>
<p>이전 글에서 정규화에 종류와 정규화란 무엇인가에 대해 설명을 했으니, 이제는 각각의 정규화의 종류와 간단한 예제를 통해 각각의 정규화 단계들을 정리해보자.</p>
<h3 id="1형-정규화">1형 정규화.</h3>
<p>1형 정규화란 테이블 내의 모든 값이 더 이상 나눌 수 없는 단일 값을 가지도록 데이터들을 나누는 것이다.</p>
<p>데이터의 원자성을 유지하기 위해서는 데이터베이스의 각각 필드에는 하나의 값만 저장되어야 한다는 원칙이 지켜져야만 한다.</p>
<p>앞서 살펴보았던 이상 현상들의 예제처럼 데이터들의 원자성이 지켜지지 않으면 이상 현상이 발생하기 때문에, 데이터들의 원자성을 보장하기 위해서 가장 먼저 진행하는 정규화가 바로 1형 정규화이다.</p>
<p>그럼 아래의 예제를 먼저 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/74c1fd57-ed4d-4eed-a220-a21d13355189/image.png" alt="1형 정규화 전"></p>
<p>해당 테이블은 고객이 작성한 문의 내용을 저장하는 게시판 테이블의 일부이다. </p>
<p>해당 문의글에서 고객은 파손된 상품에 대한 교환 요청을 위해 문의글을 작성하는 도중 파손 상태에 대해 찍은 상품의 사진을 첨부하였는데, 이 때 고객은 한 장 이상의 사진을 첨부할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/a076bdae-2225-40f6-8942-1f4a64f30e37/image.png" alt="1형 정규화 필요 예제 2"></p>
<p>해당 문제를 해결하기 위해 위의 사진과 같이 첨부 파일 하나당 컬럼 하나를 추가하는 방법도 있었으나, 추후 고객이 첨부할 수 있는 파일의 양이 증가할 때 마다 컬럼을 하나씩 늘려 나가야 하는 문제 때문에 기각되었다.</p>
<p>해당 테이블 구조의 문제점은, n개 이상의 데이터가 한 컬럼 내에 존재한다는 것이다. 이는 데이터의 원자성을 해치는 구조이다. </p>
<p>컬럼 내의 데이터는 더 이상 나눌 수 없는 유일한 식별자를 가진 값이여야 데이터의 원자성과 일관성을 유지할 수 있게 된다.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/408f8e31-3223-4270-b5e2-362953ad0fa3/image.png" alt="1형 정규화 후"></p>
<p>아래는 1형 정규화를 진행한 이후의 테이블이다. </p>
<p>위의 정규화를 진행하기 전의 테이블을 확인해보면 &#39;첨부 파일&#39; 이라는 이름을 가진 컬럼 내에 여러 첨부 파일의 정보가 &#39; , &#39; 값으로 구분되어 저장되어 있었는데 이를 더 이상 나눌 수 없는 단일 값을 가질 때 까지 데이터를 분리한 것이다.</p>
<p>이로써 해당 테이블은 데이터의 원자성을 유지하는 테이블로 1형 정규화가 진행되었다고 볼 수 있다.</p>
<h3 id="2형-정규화">2형 정규화.</h3>
<p>2형 정규화는 기본적으로 1형 정규화를 만족하고 있다는 가정하에 진행된다. </p>
<p>정규화는 큰 틀에서부터 각각의 정규화를 거치면서 세부적으로 진행되므로 2형 정규화는 1형 정규화를 만족하고 있는 테이블에서 진행해야 한다.</p>
<p>위에서 알아본 대로 1형 정규화를 만족하고 있는 테이블의 컬럼은 각각 원자성을 유지하는데, 이 때 해당 테이블 내의 데이터가 부분 함수 종속이 이루어지도록 분해하는 것이 2형 정규화이다.</p>
<h3 id="부분-함수-종속이-뭔데">부분 함수 종속이 뭔데?</h3>
<p>부분 함수 종속이란 해당 테이블의 식별자가 아닌 비 식별자에 의해 값이 종속되는 것을 말한다.</p>
<p>위에서 예시에 이용했던 문의 테이블을 다시 보자.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/7d930e6f-b449-4a89-8f35-653789a8325f/image.png" alt="2형 정규화"></p>
<p>위의 테이블과 같이 작성자 명에 고객 명을 저장한 문의 테이블이 있다고 가정해보자.</p>
<p>해당 테이블은 현재 문의글에 대한 정보를 저장하고 있으므로 해당 테이블의 식별자는 문의글 id라고 볼 수 있는데, 
위의 테이블을 다시 보면 문의글 테이블 내의 고객 명 컬럼은 문의글 id가 아닌 고객 id에 종속되어 있다. </p>
<p>고객의 아이디를 알면 문의글 id를 몰라도 고객 명을 알 수 있는 것이다.</p>
<p>이런 상황이 발생할 경우, 우리는 부분 함수 종속이 존재한다고 판단할 수 있다.</p>
<p>위에 상황에서 고객 명이 변경될 경우가 생긴다면, 우리는 고객 테이블과 해당 고객이 남긴 문의 테이블의 정보를 함께 변경해야 하는 상황이 생기는 것이다.</p>
<p>이제 2형 정규화를 적용해보자.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/02491e17-b37d-4fb4-b8d2-45bd3911f7b1/image.png" alt="문의 테이블"></p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/5a082a7b-01ff-4ff5-93cc-82c0aa1607c2/image.png" alt="고객 테이블"></p>
<p>2형 정규화가 적용된 이후의 문의 테이블이다. 이전 처럼 고객 명을 함께 저장하는 것이 아니라 고객의 정보는 고객 테이블에 두고 해당 고객의 식별자만 문의글 테이블에 저장했다.</p>
<p>이렇게 나누면 이후에 고객 테이블의 정보가 변경되더라도 해당 고객의 식별자가 바뀌지 않는 이상은 해당 고객의 정보의 변경을 신경쓸 필요가 없어진다.</p>
<h3 id="3형-정규화">3형 정규화.</h3>
<p>이제 3형 정규화에 대해 알아보자.</p>
<p>3형 정규화는 2형 정규화까지 진행된 상태에서 이행 함수 종속이 발생하지 않게 정규화를 진행하는 것을 말한다.</p>
<h3 id="이행-함수-종속">이행 함수 종속?</h3>
<p>이행 함수 종속은 쉽게 말하면 A -&gt; B, B -&gt; C 이므로 A -&gt; C 인 상황이 발생하는 것을 말한다. </p>
<p>예를 들어, 집 앞에 자주 가던 카페에서 10회, 30회, 50회 이상 커피를 구매한 고객들에게 실버, 골드, 다이아 등급을 부여해 각각 5%, 10%, 15%의 할인을 적용한다고 가정해보자.</p>
<p>만약 내가 해당 카페에서 10회 이상 커피를 구매했을 경우, 나는 실버 등급을 부여받으며 이후 결제 시 5%의 할인율을 적용받는다.</p>
<p>이를 이행 함수 종속의 관점에서 설명해보면</p>
<blockquote>
<p>고객(나) -&gt; 실버 (A -&gt; B)</p>
</blockquote>
<blockquote>
<p>실버 -&gt; 5% 할인 (B -&gt; C)</p>
</blockquote>
<blockquote>
<p>고객(나) -&gt; 5% 할인. (A -&gt; C)</p>
</blockquote>
<p>의 형태로 종속되는 것을 확인할 수 있다. </p>
<p>이를 토대로 다시 아까의 고객 테이블을 다시 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/7f313eac-faf3-4e63-9212-a1fb07922dab/image.png" alt="3형 정규화 전"></p>
<p>예시를 들었던 대로, </p>
<p>카리나라는 고객 ID를 가진 고객의 등급은 현재 GOLD 이다. (A -&gt; B)</p>
<p>해당 등급에 해당하는 할인율은 10% 이다. (B -&gt; C)</p>
<p>그러므로 카리나는 결제 시 마다 10%의 할인을 받는다. (A -&gt; C)</p>
<p>이 때 고객은 자신의 등급에 해당하는 식별자만 알고 있으면 해당 등급에 해당하는 할인율을 알 수 있게 된다.</p>
<p>그러므로 굳이 고객 테이블의 등급에 해당하는 할인율을 같이 저장하지 않아도 등급에 대한 정보만 저장하고 있으면 해당 등급을 토대로 할인율을 식별할 수 있으므로 할인율이라는 컬럼을 삭제할 수 있는 것이다.</p>
<p>이렇게 등급과 할인율을 따로 분리해 등급 테이블을 만들면, 고객이 늘어날 때 마다 해당 고객의 등급 별 할인율을 굳이 매번 저장하지 않아도 되는 것이다.</p>
<p>결과적으로 고객 테이블에서 할인율 컬럼 만큼의 중복을 제거할 수 있게 된다.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/b2dd0225-67ed-4a59-aa86-282aa984b333/image.png" alt="3형 정규화 후"></p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/cee10db9-d1fa-4f25-983d-26281dc806d6/image.png" alt="3형 정규화 후"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB 정규화 (이상 현상 편)]]></title>
            <link>https://velog.io/@sloth_/DB-%EC%A0%95%EA%B7%9C%ED%99%94-1</link>
            <guid>https://velog.io/@sloth_/DB-%EC%A0%95%EA%B7%9C%ED%99%94-1</guid>
            <pubDate>Tue, 23 May 2023 15:21:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>데이터베이스 정규화에 대해 알아보자.</p>
</blockquote>
<h2 id="그래서-정규화가-뭔데">그래서 정규화가 뭔데?</h2>
<p>데이터베이스 정규화란 관계형 데이터베이스 사용을 위해 테이블의 구조를 설계하거나 혹은 기존에 사용하던 테이블의 데이터가 방대해져 테이블을 세분화할 때 데이터 간의 중복을 최소화하게 데이터를 구조화하는 프로세스이다.</p>
<p>초기에 구상했던 것과는 달리 데이터베이스에 저장되는 데이터가 점차 방대해지고 서비스를 확장시키면서 점차 추가되어는 데이터들 사이에 중복이 발생하거나 편의를 위해 같은 데이터를 다른 테이블에 중복으로 저장하는 일이 발생하는데,</p>
<p>이 때 데이터베이스 정규화를 진행하여 데이터 간의 중복을 줄이고 테이블의 구조를 논리적으로 구성하여 데이터베이스를 구조화 하는 것을 정규화라고 부른다.</p>
<p>정규화의 형태는 1~6단계의 정규형 + BCNF 정규형이 존재하며, 보편적으로는 6단계 까지 정규화를 진행하는 것은 아니고 3정규화 이후 BCNF 정규화까지 적용되었으면 데이터베이스가 정규화 되었다고 말한다.</p>
<p>(실무에서는 4~6 단계 정규화는 정규화를 진행하는 도중에 관계가 오히려 너무 많아져 관리가 힘들어지므로 하지 않는다고 한다.)</p>
<h2 id="정규화를-왜-해야-하는데">정규화를 왜 해야 하는데?</h2>
<p>초기에 정규화를 고려하여 데이터베이스를 설계한 것이 아니라면, </p>
<p>요구 사항이 늘어나거나 서비스를 확장해 나가면서 추가되는 부분들 때문에 초기 의도와는 달리 같은 테이블 내에 중복된 정보를 저장하거나, 
서로 다른 테이블 사이에 동일한 정보를 저장해야만 하는 상황이 발생할 수 있다.</p>
<p>이 때 해당 정보를 테이블과 분리하거나 중복된 데이터를 제거한다면 해당 데이터 만큼의 저장 공간을 확보할 수 있고,
중복된 값이 제거되어 전체적인 테이블 내의 데이터가 줄어드므로 해당 테이블을 조회할 때의 검색 속도 또한 증가되는 효과를 가져올 수 있다.</p>
<p>또한 불필요한 중복 값이 사라지고 테이블 간에 데이터 연관성이 사라진다면, </p>
<p>데이터베이스 내의 정보들을 좀 더 논리적인 구조로 관리할 수 있고 추후에 또 발생할지 모를 추가 사항에 대해 좀 더 유연하게 대처할 수 있게되어 유지보수 또한 용이해지게 된다.</p>
<h2 id="정규화가-나오게된-과정-설명">정규화가 나오게된 과정 설명.</h2>
<p>데이터베이스 정규화라는 개념을 이해하기 위해서는 먼저 데이터베이스 이상 현상에 대해 정리하고 넘어가야만 한다. </p>
<p>사실 정규화라는 개념 자체가 이러한 이상 현상들을 해결할 수 있는 방법론에 가깝기 때문이다.</p>
<p>데이터베이스 이상 현상은 크게 3가지로 나뉘는데, </p>
<ul>
<li><strong>갱신 이상 (Modification Anomaly)</strong><br/></li>
<li><strong>삽입 이상 (Insertion Anomaly)</strong><br/></li>
<li><strong>삭제 이상 (Deletion Anomaly)</strong></li>
</ul>
<p>이 있다.</p>
<p>먼저 <strong>갱신 이상</strong> 이란, </p>
<p>말 그대로 데이터베이스 내의 정보를 갱신할 때 이상 현상이 발생하는 것을 말하는데, </p>
<p>이는 데이터베이스 내부에 중복된 값이 저장되어 있어서 중복된 데이터를 전부 동일한 값으로 바꿔주지 않을 경우에 데이터에 대해 일관성을 해치게 되는 문제가 발생한다.</p>
<p>예시를 들어보자.</p>
<blockquote>
<p>정규화를 진행하지 않은 학생 정보 데이터베이스가 있다고 가정해보자.</p>
<p>해당 데이터베이스 내부에는 학생 이름, 학년, 주소, 학생이 듣고 있는 수업 명의 정보를 포함하고 있다.</p>
<p>학생 정보에 해당하는 이름, 학년, 주소는 학생이 듣고 있는 수업 명의 정보가 바뀔 때 마다 모두 다 같은 정보를 저장해준다.</p>
</blockquote>
<p>위와 같은 가정에서 만약 학생이 <strong>이사</strong>를 가게 된다면 어떻게 될까?</p>
<p>학생의 주소가 변경되었기 때문에 학생 정보를 포함하고 있는 모든 데이터를 조회해서 일일이 변경된 주소로 정보를 수정해 주어야 할 것이다.</p>
<p>이 때 혹시라도 데이터 하나가 누락되어 수정이 진행되지 않았다면? </p>
<p>이 경우에 학생의 주소 정보에 대한 일관성이 깨지게 된다.</p>
<p>이를 해결하기 위해서는 학생 테이블과 수업 테이블을 분리하도록 정규화를 진행해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/aa87366b-cd6a-4d73-984c-f03988a096a5/image.png" alt="갱신 이상 전"></p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/8aaeeb3c-768a-4040-bb0b-0c17be815c72/image.png" alt="갱신 이상 정규화"></p>
<p>갱신 이상을 해결하기 위해 정규화를 진행하여 학생 테이블과 수업 테이블을 분리하였다.</p>
<p>이제 학생 테이블의 학생 정보가 바뀌어도 해당 학생에 대한 수업 정보는 연관성을 가지지 않게 되므로 학생 정보에 대한 일관성을 유지할 수 있게 되었다.</p>
<br/>

<p><strong>삽입 이상</strong> 이란, </p>
<p>말 그대로 데이터베이스에 INSERT 중 발생하는 이상 현상을 말한다.</p>
<p>삽입 이상 또한 위에서 설명했던 학생 테이블을 통해 예시를 들어보자.</p>
<blockquote>
<p>또 다시, 정규화가 진행되지 않은 학생 테이블이 있다고 가정해보도록 하자.</p>
<p>해당 데이터베이스에는 이번엔 수업 명 + 강의실 정보까지 포함되어 있다.</p>
<p>학년 정보는 학생 명, 학년 정보가 포함되어 있다고 가정한다.</p>
</blockquote>
<p>위와 같은 구조에서, </p>
<p>우리는 수업 정보가 학생 테이블 안에 같이 포함되어 있는 구조로 잘못된 설계를 진행했다.</p>
<p>이 상황에서 <strong>새로운 수업</strong>이 생긴다면 어떻게 될까?</p>
<p>우리는 해당 수업을 데이터베이스에 추가하고 싶지만, 아직 새로 생긴 수업이라 해당 수업을 듣는 학생이 없을 경우 수업 정보를 학생 테이블에 저장할 수 없을 것이다.</p>
<p>만약 억지로 수업 정보를 추가하기 위해 학생 정보를 비워둔 채 수업 정보만 추가하게 된다면, 
학생 테이블 내에 학생 없는 학생 정보가 생겨버리는 불상사가 발생한다.</p>
<p>이는 학생 정보를 저장하려고 생성한 학생 테이블 내의 일관성을 깨트리는 일이 된다.</p>
<p>또, 수업의 <strong>강의실 정보가 변경</strong>된다면?</p>
<p>우리는 해당 강의실 정보를 가지고 있는 모든 학생 테이블의 정보를 일괄적으로 전부 수정해야 한다. </p>
<p>이를 진행하다 혹여 특정 데이터에 수정 누락이 발생할 경우 똑같이 데이터에 대한 일관성이 박살나게 된다.</p>
<p>이를 방지하기 위해, </p>
<p>위의 갱신 이상을 해결한 방법과 동일하게 학생 테이블과 수업 테이블을 서로 분리하여 정규화를 진행한다면, 
수업 테이블과 학생 테이블 모두 일관성을 보장한 채 서로 영향을 주지 않고 데이터를 삽입, 수정할 수 있다.</p>
<br/>

<p><strong>삭제 이상</strong> 이란, </p>
<p>삽입 이상과 마찬가지로 데이터 삭제를 진행하는 도중에 발생하는 이상 현상을 말한다.</p>
<p>마찬가지로 이 이상 현상도 학생 테이블을 이용해 예시를 들어보도록 하겠다.</p>
<p>삭제 이상은 어떻게 보면 삽입 이상과 유사한 점이 많아 이해하기가 좀 더 수월할 수도 있다.</p>
<blockquote>
<p>학생 테이블에는 삽입 이상 때와 동일하게 학생 명, 학년, 수업 명, 강의실 정보가 저장되어 있다.</p>
</blockquote>
<p>이와 같은 상황에서,</p>
<p>만약 수업이 어떤 사정으로 인해 <strong>폐강</strong> 하게 된다면 어떻게 될까?</p>
<p>우리는 폐강하는 수업의 정보를 삭제해야 하지만, 이 때 해당 수업을 듣고 있는 학생들의 정보도 같이 삭제되어 버리기 때문에, 삭제를 진행할 수 없다.</p>
<p>물론, 이러한 구조에서는 학생 정보가 수업을 듣고 있는 개수마다 중복되어 저장되기 때문에 하나쯤 삭제된다고 해도 문제가 없을 수도 있지만 어떤 학생이 한 가지의 수업만 듣고 있었다거나, 
본인이 듣고 있던 수업이 운 없이 모두 폐강되어 버리는 상황이 오면, 우리는 그 학생의 정보를 같이 삭제해버리고 말 것이다.</p>
<p>우리는 이러한 상황을 방지하기 위해 정규화를 진행해야 하는 것이다.</p>
<br/>

<p>이제 정규화를 진행해야먄 하는 배경도 배워봤으니,</p>
<p>다음 시간에는 본격적으로 정규화에 대해 알아보도록 하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[확장을 고려한 시스템 설계]]></title>
            <link>https://velog.io/@sloth_/%ED%99%95%EC%9E%A5%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@sloth_/%ED%99%95%EC%9E%A5%EC%9D%84-%EA%B3%A0%EB%A0%A4%ED%95%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Thu, 11 May 2023 15:41:17 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>해당 글은 가상의 사례들의 시나리오를 작성하고 각각 사례들에서 가정한 요구사항과 해결 방안을 통해 시스템이 확장해 나가는 과정을 정리한 글이다.</p>
<br/>

<p>일상에서 발생할 수 있는 사례들의 가정을 통해 단일 서버로 구축되어 있던 작은 서비스에서 추후에 확장성을 고려해 다중화를 지원하는 서비스까지 확장해 나가며 시나리오를 작성하고,</p>
<p>다양한 상황에서 발생할 수 있는 다양한 요구사항 및 요구사항에 대한 해결 방안을 고민해보며 시스템 확장 설계에 대한 이해도를 길러보기 위해 작성한 글이다.</p>
<br/>

<h2 id="단일-웹서버">단일 웹서버</h2>
<h4 id="시나리오">시나리오</h4>
<blockquote>
<p>A는 개발자의 꿈을 꾸며 코딩을 공부해나가던 통칭 “코린이” 이다.</p>
</blockquote>
<p>A는 자신의 포트폴리오를 만들기 위해 고민하던 와중, 자신의 기술 스택 및 자기 소개를 담은
웹 페이지를 개발해 이력서에 링크로 남기면 어떨까 하는 생각이 들어 당장 개인 웹서버를 하나 개발하기로 결심했다.</p>
<blockquote>
</blockquote>
<p>A는 HTML, CSS, Javascript 만을 이용해 간단한 본인 PR만 업로드할 목적이므로, 별도의 Backend 없이 화면 몇 장을 퍼블리싱해 tomcat을 이용해 웹 서버를 구축하기로 결정했다.</p>
<blockquote>
</blockquote>
<p>A는 아직 웹 서버를 구축해본 경험이 없어 일단 경험해볼 겸 자신이 사용하던 개인 컴퓨터에 가벼운 단일 웹 서버를 구축하기로 결정하였다.</p>
<p>위의 시나리오에서, A군은 자신의 초기 서비스의 빠른 오픈을 위해 별도의 확장성을 고려하지 않고 서비스를 설계하였다.</p>
<p>A군은 별도의 설계 없이 자신이 전달할 몇 가지의 정보를 담은 웹 페이지 몇 장을 구현해 평소에 사용하던 개인 PC를 통해 웹 서버를 구축하여 서비스를 제공했다.</p>
<h3 id="요구사항">요구사항</h3>
<p>서비스는 방문자가 페이지에 방문했을 때, 구현해놓은 웹 페이지를 방문자에게 전달해 줄 수 있어야 한다.</p>
<br/>


<h2 id="데이터베이스-도입">데이터베이스 도입</h2>
<h4 id="시나리오-1">시나리오</h4>
<blockquote>
<p>자신의 기술 스택 및 자신이 어떤 사림인지를 소개하는 페이지를 개발한 A군.</p>
</blockquote>
<p>하지만 A군은 모든 내용을 HTML 파일 안에 일일이 작성하여 웹 서버에 기재한 탓에,</p>
<blockquote>
<p>기재한 내용에 대해 변경 사항이 생기거나, 오탈자를 발견했을 경우 웹서버에 올라가 있는 파일을 직접 수정하거나 최악의 경우 파일을 다시 개발하여 업로드 해야 하는 상황에 직면했다.</p>
</blockquote>
<p>A군은 이와 같은 상황을 벗어나기 위해, 페이지에 기재된 내용 중 변경 사항이 발생할 수 있는 부분을 분리하여 따로 관리하려고 한다.</p>
<blockquote>
</blockquote>
<p>그리하여 A군은 데이터베이스를 구축하고 이후 구축 해놓은 데이터베이스에서 미리 작성한 내용을 읽어와 방문자에게 보여주는 역할을 수행할 Backend 서버를 개발하여 자신이 기존에 개발했던 화면에 연결하기로 결심했다.</p>
<p>위 가정의 상황에서, A군은 변경된 요구 사항을 충족시키기 위해 기존에 사용하던 단일 웹 서버 구조를 탈피하여 기존의 단일 웹 서버 구조에서 WEB &amp; WAS 서버 / DB 서버의 구조로 시스템 구조를 변경하였다.</p>
<p>A군은 방문객이 자신의 사이트를 방문했을 때, DB에 저장되어 있는 포스트를 조회하여 화면을 구성한 후 방문객에게 전달하려고 한다. </p>
<p>이를 위해 본문을 미리 작성해 DB에 저장시켜 두고, 최초 방문 시 해당 내용을 DB에서 조회해 웹 페이지에 전달해주는 Backend 서버를 구축하였다.</p>
<h3 id="요구사항-1">요구사항</h3>
<p>서비스는 사이트에서 보여줄 내용을 DB에 저장해 두다가, 방문자가 최초로 방문 시 해당 내용을 조회해 보여주어야 한다.</p>
<br/>

<h2 id="스케일-아웃">스케일 아웃</h2>
<h4 id="시나리오-2">시나리오</h4>
<blockquote>
<p>서버 엔지니어 지망생인 B군.</p>
</blockquote>
<p>B군은 요즘들어 자신이 진행하고 있는 프로젝트의 서버가 불안정한 모습을 보이는 것을 확인했다.</p>
<blockquote>
<p>아마도 서버로 사용하고 있는 컴퓨터의 성능이 문제인 것으로 파악되지만, 학교에서 강의를 위해 제공되는 저 사양 PC를 이용해 서버를 구축한 터라 더 이상의 성능 업그레이드는 불가능 했다.</p>
</blockquote>
<p>결국 B군은 이 문제를 해결하기 위해, 프로젝트 팀원 모두와 회의를 진행한 끝에 웹 서버를 분리하여 부하를 분산시키기로 결정했다.</p>
<blockquote>
</blockquote>
<p>그래서 기존에 단일로 존재하던 서버를 두 개의 PC에 설치하고 사용량에 따라 특정 서버에 부하가 많이 발생할 경우, 그 이외의 서버에 사용자를 연결해주게끔 설계를 진행했다.</p>
<p>위 가정의 상황에서, B군은 기존의 단일 서버에서 더 이상의 스케일 업이 불가능한 상황이라고 판단하고 스케일 아웃 형태로 서버를 확장하는 것을 선택하였다.</p>
<p>초기 사용자가 적을 때에는 서버를 분리하여 다중화 하는 것 보다 서버 단말 자체의 사양을 업그레이드 하는 것이 더 나은 선택이 될 수 있으나, </p>
<p>해당 경우에는 더 이상 서버 단말을 업그레이드 할 수 없는 특수한 상황이라 서버를 다중화 하는 것을 선택하였다.</p>
<h3 id="요구사항-2">요구사항</h3>
<p>서비스는 사용자가 많아져 자신의 서버 단말에 부하가 발생했을 경우, 사용자의 요청에 대한 별도의 제한 없이 해당 부하를 해소시킬 수 있어야 한다.</p>
<br/>

<h2 id="로드-밸런서-도입">로드 밸런서 도입</h2>
<h4 id="시나리오-3">시나리오</h4>
<blockquote>
<p>스케일 아웃을 진행하기로한 B군과 B군의 팀원들.</p>
</blockquote>
<p>하지만, 서버 자체에서 트래픽을 어떻게 감지하여 어떻게 다른 서버로 요청을 전달 하게끔 구현해야 하는지 방법을 몰라 고민하던 중, </p>
<blockquote>
</blockquote>
<p>서버의 트래픽 관리 및 부하 분산을 위해 로드 밸런서를 도입하기로 결정 했다.</p>
<blockquote>
</blockquote>
<p>기존의 사용자의 트래픽이 바로 서버로 전달되던 구조에서, </p>
<blockquote>
<p>다중화한 서버의 앞에 로드 밸런서를 설치해 트래픽 발생 시 로드 밸런서가 먼저 해당 트래픽을 감지해 현재 가동중인 서버들의 상태를 확인하여 상태가 양호한 서버로 전달해주게끔 설정했다.</p>
</blockquote>
<p>해당 부분에서 B군은 스케일 아웃을 적용하며 다중화된 서버에 트래픽을 분산해주기 위해 로드 밸런서를 설치하기로 결정했다. </p>
<p>로드 밸런서는 다중화된 서버의 트래픽을 모니터링 하며 하나의 서버 인스턴스에 트래픽이 몰리지 않고 고루 분산되도록 앞에서 먼저 사용자의 트래픽을 전달 받아 각각의 서버 인스턴스에 전달하는 역할을 한다.</p>
<h3 id="요구사항-3">요구사항</h3>
<p>서비스는 사용자의 트래픽을 다중화된 서버에 고루 배분하여 리소스를 낭비하지 않고 처리할 수 
있어야 한다. </p>
<br/>

<h2 id="세션-관리">세션 관리</h2>
<h4 id="시나리오-4">시나리오</h4>
<blockquote>
<p>우여곡절 끝에 로드 밸런서까지 성공적으로 도입한 B군.</p>
</blockquote>
<p>바뀐 설계를 토대로 서버를 구축한 상태에서 다시 부하 테스트를 시작하는데, 간헐적으로 사용자의 세션이 초기화되는 현상이 발생했다.</p>
<blockquote>
</blockquote>
<p>해당 문제의 원인을 찾기 위해 살펴보던 도중 사용자의 인증 세션 관리를 WAS에서 전담하고 있다는 것을 발견한 B군은 </p>
<blockquote>
<p>기존의 세션 관리 방식을 WAS에서 관리하는 것이 아닌 JWT 토큰 형식으로 변경해 별도의 세션 관리 없이 서버에서 토큰을 이용해 인증 및 인가를 관리하는 방식으로 변경했다.</p>
</blockquote>
<p>위의 가정에서 B군은 부하 분산을 위해 사용자의 요청을 기존 WAS가 아닌 다른 WAS에 전달했을 경우에도 해당 사용자의 인증이 유지되어야 한다는 요구 사항을 충족하기 위해, </p>
<p>기존의 WAS의 세션 관리를 이용해 서버 단에서 세션을 유지하던 방식에서 사용자 인증 완료 시 토큰을 발급해 사용자의 쿠키에 담아 전달하고 해당 토큰을 이용해 인가를 진행하는 방식으로 구조를 변경하였다.</p>
<h3 id="요구사항-4">요구사항</h3>
<p>사용자는 한 번 인증을 통과하여 세션이 유지되고 있는 경우, 어떤 서버에게 정보를 요청해도 해당 인증이 유지되어야 한다.</p>
<br/>

<h2 id="cdn-도입">CDN 도입</h2>
<h4 id="시나리오-5">시나리오</h4>
<blockquote>
<p>세션 관리 방식을 변경해 다중화된 서버에서도 사용자의 인증 상태를 유지할 수 있게된 B군과 B군의 팀원들.</p>
<p>그들은 이제 본격적으로 자신들이 구축한 서비스의 단위 테스트 및 부하 테스트를 진행하며 서비스를 안정화 시키고 불편 사항들을 개선해 나가는 중이었다.</p>
<p>그러던 중 자신들이 만든 서비스의 최초 화면 진입 속도가 최초 구현에 비해 조금씩 느려지고 있는 것을 확인하고는 해당 사유를 분석했고,</p>
<p>이내 자신들이 추가한 정적 리소스가 초기 구현에 비해 시간이 지날수록 점점 많아져 화면의 최초 로딩 속도를 떨어트리고 있다는 것을 발견했다. </p>
<p>B군과 팀원들은 이 문제를 해결하기 위해 정적 리소스들을 모아서 CDN을 구축해 해당 CDN을 통해 관리하기로 결정하였다.</p>
</blockquote>
<p>위의 시나리오에서 B군은 자주 사용되는 정적 리소스들을 캐시하여 페이지의 로딩 속도를 개선하기 위해 CDN을 도입하기로 결정했다.</p>
<p>이를 이용해 자주 사용되는 로고 이미지나 메인 화면의 아이콘 및 사진들을 캐시하여 보관해두고, 추후 사용자 요청이 들어오면 접속 요청 지역에서 제일 가까운 캐시 서버를 통해 미리 캐시해 놓은 정적 리소스들을 사용자에게 전달하여 서비스 응답 속도를 향상시켰다.</p>
<h3 id="요구사항-5">요구사항</h3>
<p>변경 사항이 적은 정적 컨텐츠(css, js, 그 밖의 resource 파일)을 보다 빠른 속도로 불러와 사용자의 요청에 전달 할 수 있어야 한다.</p>
<br/>

<p>지금까지 다양한 가상의 사례들을 통해 시스템이 확장되어 가는 요구사항 및 해결 과정을 그려보며 그에 따른 확장 방법 및 개선 사항들을 알아보았다.</p>
<p>이 밖에도 다양한 사례들이 있는 만큼 설계 및 확장에 대한 대략적인 개념을 익혀 추후에 발생할 문제에 대해 더 잘 대응할 수 있게 되었으면 좋겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Iterator 패턴이란? (예제편)]]></title>
            <link>https://velog.io/@sloth_/Iterator-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-%EC%98%88%EC%A0%9C%ED%8E%B8</link>
            <guid>https://velog.io/@sloth_/Iterator-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-%EC%98%88%EC%A0%9C%ED%8E%B8</guid>
            <pubDate>Mon, 08 May 2023 15:09:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 내용은 &quot;JAVA 언어로 배우는 디자인 패턴 입문 3판&quot;을 읽으면서 내용을 정리한 것입니다.</p>
</blockquote>
<p>이제 예제를 통해 내용을 정리하며 이해하는 시간을 가져보자.</p>
<p>설명편에서 예시를 들었던 해리포터라는 소설의 시리즈를 관점으로 예제를 생성해보자.</p>
<p>먼저 해리포터라는 소설 시리즈는 총 7부작의 소설의 모음인데, 이 관점에서 볼 때 해리포터 시리즈는 해리포터라는 객체를 7개 가진 Iterable 객체라고 볼 수 있다.</p>
<p>이를 JAVA 문법으로 표현하면,</p>
<pre><code class="language-java">    Iterable&lt;해리포터&gt; 해리포터 시리즈</code></pre>
<p>로 볼 수 있다.</p>
<p>이제 이를 토대로 Kotlin을 이용해 예제를 작성해보자.</p>
<p>우선 해리포터라는 책을 객체로 만들어보자.</p>
<pre><code>data class Harry(val title: String)</code></pre><p>간단한 예제로 작성하기 위해 해리포터 객체는 title 변수를 가진 데이터 클래스로 작성했다.</p>
<p>이제 해리포터 시리즈 객체를 생성해보자.</p>
<pre><code>class HarrySeries : Iterable&lt;Harry&gt; {
    private var series = arrayOf(
        Harry(&quot;해리포터와 마법사의 돌&quot;),
        Harry(&quot;해리포터와 비밀의 방&quot;),
        Harry(&quot;해리포터와 아즈카반의 죄수&quot;),
        Harry(&quot;해리포터와 불의 잔&quot;),
        Harry(&quot;해리포터와 불사조 기사단&quot;),
        Harry(&quot;해리포터와 혼혈 왕자&quot;),
        Harry(&quot;해리포터와 죽음의 성물&quot;)
    )

    override fun iterator(): Iterator&lt;Harry&gt; {
        TODO(&quot;Not yet implemented&quot;)
    }
}</code></pre><p>편의를 위해 내부에 series라는 Harry 객체 배열을 가진 Iterable 인터페이스를 구현한 HarrySeries라는 Iterable 객체를 생성했다.</p>
<p>우리는 Iterable 인터페이스를 상속받은 구현체로 HarrySeries 객체를 선언했기 때문에 Iterator 객체를 반환할 iterator() 메소드를 구현해야 한다.</p>
<p>이제 Iterator 인터페이스를 구현할 HarryIterator 객체를 만들어보자.</p>
<p>Iterator는 앞서 설명한 대로 반복 가능한 반복체를 반복하여 호출하며 반복체 내부의 값을 반환하는데, </p>
<p>여기서 우리는 Iterable 인터페이스 구현체인 HarrySeries 객체에 대한 Iterator를 반환할 객체를 구현하는 것이므로 HarryIterator 객체의 생성자에 내부 배열인 series를 전달하여 series를 반복하게끔 구현할 것이다.</p>
<p>이제 HarryIterator 객체를 생성해보자.</p>
<pre><code>class HarryIterator(series: Array&lt;Harry&gt;) : Iterator&lt;Harry&gt; {

    override fun hasNext(): Boolean {
        TODO(&quot;Not yet implemented&quot;)
    }

    override fun next(): Harry {
        TODO(&quot;Not yet implemented&quot;)
    }
}</code></pre><p>객체의 생성자에 series를 전달했다.</p>
<p>이제 내부 구현에 해당 객체를 사용할 것이다.</p>
<p>그리고 HarryIterator를 선언했으므로, 아까 미구현으로 남겨두었던 iterator() 메소드를 구현해보자.</p>
<pre><code>override fun iterator(): Iterator&lt;Harry&gt; {
    return HarryIterator(series)
}</code></pre><p>iterator() 메소드 호출 시 HarryIterator를 생성해 반환하는 것으로 구현했다. 이후 우리가 전달받은 배열을 이용해 HarryIterator 객체를 잘 구현하기만 하면, 이를 사용하는 부분에서는 HarryIterator의 내부 구현을 이해하지 않아도 Iterator 인터페이스를 사용하던 그대로 사용할 수 있게 된다.</p>
<p>이 때, Iterable 객체와 Iterator 객체는 의존성을 가지게 된다. </p>
<p>Iterable 객체의 내부 반복체의 형태를 해당 반복체 속의 값들을 반환해야 하는 Iterator 객체는 반드시 알고 있어야 해당 반복체를 이용해 값을 반환할 수 있기 때문이다.</p>
<p>하지만 두 객체 사이의 반복체에 관한 의존성만 지켜준다면, 이를 사용하는 곳에서는 Iterable 객체와 Iterator 객체의 내부 구현에 대해 자세히 알고 있지 않고도, Iterable 인터페이스와 Iterator 인터페이스를 이용하던 그대로 사용할 수 있게 된다.</p>
<p>이제 HarryIterator 객체의 내부 구현을 확인해보자.</p>
<pre><code>class HarryIterator(series: Array&lt;Harry&gt;) : Iterator&lt;Harry&gt; {
    private var series = series
    private var index = 0


    override fun hasNext(): Boolean {
        return index &lt; series.size
    }

    override fun next(): Harry {
        return series[index++]
    }
}</code></pre><p>초기에 생성했던 것과 달리, 내부 배열이 현재 가리키고 있던 위치를 저장할 index 변수가 생겼다. 해당 변수는 next() 메소드가 호출될 경우, 하나씩 증가할 것이다.</p>
<p>이제 Iterator 객체를 통해 Harry 객체를 반환받을 수 있을 것이다.</p>
<pre><code>while (harryIterator.hasNext()) {
    val harry = harryIterator.next()

    println(harry.title)
}</code></pre><p>hasNext() 메소드와 next() 메소드의 사용법만 알면 내부 구현을 신경쓰지 않고도 이처럼 원하는 값을 얻을 수 있다.</p>
<p>이제 HarrySeries의 내부 배열이 List로 변경되든, 별도의 배열 구현체를 이용해 구현하든 간에 상관 없이 해당 구현체 대로 hasNext() 메소드와 next() 메소드가 동작하게끔 수정만 해주면, </p>
<p>기존에 작성해 두었던 해당 Iterator 객체를 사용하던 기존 객체는 내부 배열에 대한 의존성을 가지지 않게 구현할 수 있게 된다.</p>
<p>만일 Iterable 객체와 Iterator 객체를 이용하지 않고 단순 배열을 이용해 해당 반복을 처리했을 경우, 반복자에 대한 구현이 변경될 때 마다 해당 반복자를 이용하던 모든 소스를 다 수정하고 다시 테스트를 진행했어야 하지만, </p>
<p>Iterable 객체와 해당 객체의 Iterator 객체를 이용해 구현했을 경우에는 Iterable 객체의 내부 반복자가 변경됐을 경우에도, 해당 반복자에 대한 Iterator 객체의 구현만 변경해주면 수정한 Iterator가 정상 동작 한다는 가정 하에 다른 소스를 테스트하지 않아도 될 것이다.</p>
<p>이에 대한 보증은 반복자에 대한 재사용성을 증가시킬 수 있는 아주 좋은 디자인 패턴 중 하나이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Iterator 패턴이란? (설명편)]]></title>
            <link>https://velog.io/@sloth_/Iterator-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-%EC%84%A4%EB%AA%85</link>
            <guid>https://velog.io/@sloth_/Iterator-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-%EC%84%A4%EB%AA%85</guid>
            <pubDate>Thu, 27 Apr 2023 14:45:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 내용은 &quot;JAVA 언어로 배우는 디자인 패턴 입문 3판&quot;을 읽으면서 내용을 정리한 것입니다. </p>
</blockquote>
<p>iterator 패턴은 주로 배열이나 List같은 객체들의 집합의 아이템을 순차적으로 읽어서 처리하고 싶을 때,
주로 사용하는 디자인 패턴의 한 종류이다.</p>
<p>iterator 패턴에서는 패턴의 이름대로 Iterable 인터페이스와 Iterator 인터페이스를 이용하여 
객체의 집합체와 해당 집합체에 대해 반복하여 처리를 진행할 반복자를 구현하는데,</p>
<p>이 중 Iterable 인터페이스를 집합체, Iterator 인터페이스를 반복자로 사용한다.</p>
</br>

<p>이제, 본격적으로 세부 내용을 살펴보자.</p>
<h2 id="iterable">Iterable</h2>
<p>먼저 살펴볼 인터페이스는 Iterable 이다. </p>
<p>여기서 Iterable 인터페이스는 이름 그대로 반복이 가능한 객체를 뜻하는데, 흔히 우리가 사용하는 List 객체가 Iterable 인터페이스를 구현한 구현체이다.</p>
<p>정확히는 List의 부모 인터페이스인 Collection 인터페이스가 Iterable 인터페이스를 구현하고 있는데, 이로 인해 List 객체들은 내부에 iterator() 메소드를 포함하고 있다.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/a2f10e15-aef1-4991-9fd3-3f860123d46a/image.png" alt="List Interface"></p>
<h2 id="iterator">Iterator</h2>
<p>집합체에 대해 알아봤으니, 이제 해당 집합체의 내부를 순환할 반복자 객체인 Iterator 인터페이스에 대해 알아보자.</p>
<p>앞서 내용에서, Iterable 인터페이스는 집합체라고 설명했는데, 여기서 집합체란 어떤 객체에 대한 모음이라고 생각하면 된다.</p>
<p>예를 들어 보자.</p>
<blockquote>
<p>어느 날 우연히 OCN에서 방영하던 해리포터 영화를 보고, 오랜만에 소설판으로 다시 읽어 보고 싶어진 나는 해리포터 전권을 빌리려고 동네 책방에 들렀다.</p>
<p>수 많은 책들 가운데, 나는 해리포터 전 권이 꽂혀 있는 책장 앞에 서서 해리포터 전 권을 모두 꺼내어 카운터로 향했다.</p>
</blockquote>
<p>이 때의 상황을 디자인 패턴에 대입해보자.</p>
<p>이 상황에서 &#39;해리포터&#39; 라는 시리즈는 해리포터와 마법사의 돌, 비밀의 방, etc. 등 해리포터라는 이름을 가진 소설책 객체에 대한 집합체라고 볼 수 있다.</p>
<p>해리포터 1권을 Book이라는 객체로 생각했을 때, 해리포터라는 시리즈는 Iterable &lt; Book &gt;이라고도 볼 수 있다. </p>
<p>다른 예시를 보자.</p>
<blockquote>
<p>나는 현재 새해 기념으로 독서실을 다녀보려고 결심했다.</p>
<p>집 근처에 저렴한 독서실이 있어 해당 독서실의 자리를 잡은 나는 가져온 책들을 모조리 독서실 책상의 서랍에 꽂아 넣었다.</p>
</blockquote>
<p>이 때의 상황을 디자인 패턴에 대입해보자.</p>
<p>이 상황에서 내가 독서실에 가져온 책들을 객체라는 관점에서 봤을 때, 책들이 꽂혀 있는 독서실의 책상 서랍은 내가 가져온 책들에 대한 집합체로 볼 수 있다.</p>
<br>

<p>이 관점에서 Iterator 인터페이스를 다시 보자.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/dcbb1824-2974-4c9a-9eb6-f8eb7418e1aa/image.png" alt="Iterator Interface"></p>
<p>Iterator 인터페이스는 next() 메소드와 hasNext() 메소드를 가진 인터페이스이다. </p>
<p>먼저, next() 메소드는 현재 인덱스에 위치한 객체를 반환하는 메소드이다.</p>
<p>이 때 해당 메소드는 객체를 반환하고나서 내부적으로 인덱스를 하나 증가시켜, 다음 순서의 인덱스에 위치시킨다. </p>
<p>이로써 해당 메소드 이후에 인덱스를 증가시키지 않아도, 다음 순서에 객체에 접근할 수 있다.</p>
<p>이쯤에서 의문이 들 수 있다.</p>
<blockquote>
<p>내부적으로 인덱스를 늘리는데 대체 어디까지 인덱스를 늘려야 해? 값이 얼마나 더 있는거야?</p>
</blockquote>
<p>해당 부분에서 비롯되는 의문을 해소하기 위해 사용하는 것이 hasNext() 메소드이다.</p>
<p>해당 메소드는 이 Iterator 내부에 위치한 인덱스가 가르키는 위치에 값이 존재하는지에 대한 여부를 반환해 준다.</p>
<p>예를 들어보자.</p>
<br>

<p>우리는 1 ~ 5까지 수를 가지고 있는 길이가 5인 Iterator 객체가 있다고 가정해보자.</p>
<p>배열 내부에는 작은 순서대로 1부터 5까지의 수가 저장되어 있고, 해당 Iterator 내부 인덱스는 초기 값으로 0을 저장하고 있다.</p>
<p>이 때 hasNext() 메소드를 실행할 경우, 해당 메소드는 현재의 인덱스에 위치한 값이 존재하는지에 대한 여부를 알려주므로 현재 인덱스 위치인 0번째의 위치하는 값이 있는지 확인한 후 여부를 알려준다.</p>
<p>현재 0번째의 위치에는 1이라는 값이 존재하므로 hasNext() 메소드는 true 값을 반환하게 된다.</p>
<p>이후 값이 존재한다는 것을 확인한 사용자가 next() 메소드를 호출하면, 해당 메소드에서는 0번째에 위치하는 값인 1을 반환하고 이후 내부 인덱스를 1만큼 증가시킨다.</p>
<p>이렇게 1에서 2, 2에서 3으로 점차적으로 증가하던 Iterator의 내부 인덱스는 0번째에서 4번째까지 점차적으로 증가해 결국 4번째에 위치한 5라는 값을 반환한 후 5로 1만큼 증가하게 된다.</p>
<p>그리고 사용자는 여느 때 처럼 메소드 명 그대로 다음이 있는지 확인하기 위해 hasNext() 메소드를 실행한다. </p>
<p>지금 Iterator 내부의 인덱스는 Iterator 자체의 길이를 넘어서 5번째 위치에 존재하는지도 모르는 값을 가리키고 있다.</p>
<p>Iterator는 사용자에 요청에 따라 5번째 위치에 값이 존재하는지에 대해 확인하는데, </p>
<blockquote>
<p>5번째 위치에는,</p>
</blockquote>
<p>Iterator는 잠시 머뭇거리더니 이내 고개를 숙였다.</p>
<blockquote>
<p>...아무 것도 없었습니다.</p>
</blockquote>
<p>Iterator는 씁쓸한듯 입술을 오물거리다 고개를 돌렸다.</p>
<p>그게 마지막이었다.</p>
<hr>
<p>다음 시간에 계속...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Basic 로그인 모듈]]></title>
            <link>https://velog.io/@sloth_/Basic-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%AA%A8%EB%93%88</link>
            <guid>https://velog.io/@sloth_/Basic-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%AA%A8%EB%93%88</guid>
            <pubDate>Mon, 26 Dec 2022 15:28:44 GMT</pubDate>
            <description><![CDATA[<h2 id="로그인-모듈-개발">로그인 모듈 개발</h2>
<p>간단한 핵심 기능만 구현되어 있는 모듈을 개발하며 해당 모듈의 핵심을 파악해보려고 합니다.</p>
<blockquote>
</blockquote>
<p>개인 공부를 위해 진행한 토이프로젝트를 분석하며 정리한 내용이므로 혹여 틀린 부분이 있다면
덧글로 알려주시면 감사하겠습니다.</p>
<h2 id="시나리오">시나리오</h2>
<p>사용자는 로그인을 위해 &#39;/public/login&#39; 페이지로 최초 진입합니다.</p>
<p>사용자는 자신의 계정 정보를 입력하고 로그인 버튼을 클릭합니다.</p>
<p>로그인이 완료된 사용자는 /private/success 페이지로 이동됩니다.</p>
<p>&#39;/public/**&#39; 경로는 모든 사용자가 접근할 수 있으며, </p>
<p>그 외의 경로는 인증된 사용자만 접근할 수 있습니다.</p>
<h2 id="소스-구성">소스 구성</h2>
<h3 id="로그인-필터-체인-구성">로그인 필터 체인 구성</h3>
<p>SecurityFilterChain bean을 생성해 기본적인 URL Mapping을 설정합니다.</p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/a332072f-eadf-48fe-a04a-cf4b5989a354/image.png" alt=""></p>
<ol>
<li><p>GET method로 전달되는 &#39;/public/&#39; URL은 permitAll로 설정합니다. 
그 외의 Request는 모두 인증이 필요하게끔 설정합니다.</p>
</li>
<li><p>로그인 페이지는 &#39;/public/login&#39; 경로로 연결됩니다.</p>
</li>
<li><p>추후 로그아웃 관련 로직을 처리할 로그아웃 핸들러로 
CustomLogoutHandler Object를 등록합니다.</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sloth_/post/9129ec5e-6eb3-466d-92b6-cfd9592f525b/image.png" alt=""></p>
<ol>
<li><p>사용자의 인증 정보는 사용자의 request에 쿠키 형식으로 저장하고 별도의 세션은 가지고 있지 
않을 예정이므로, 세션 생성 정책을 STATELESS로 설정합니다.   </p>
</li>
<li><p>로그인 관련 로직을 처리할 필터인 LoginFilter 객체를 등록합니다.    </p>
</li>
</ol>
<ul>
<li>로그인 진입 URL : /doLogin.</li>
<li>필터 종류 : UsernamePasswordAuthenticationFilter</li>
</ul>
<ol start="3">
<li>사용자 인가 관련 로직을 처리할 필터인 AuthenticationFilter를 등록합니다.</li>
</ol>
<ul>
<li>인가가 필요한 URL : &#39;/public/&#39;하위 경로를 제외한 모든 URL이나 현재 시나리오 상에서는
&#39;/private/&#39; 하위 경로입니다.</li>
</ul>
<h2 id="디버깅">디버깅</h2>
<blockquote>
<p>실제 로그인 요청이 올 시에 모듈이 작동하는 소스를 디버깅 해보면서 소스의 동작 순서 및 세부 동작 로직을 정리해봅시다.</p>
</blockquote>
<ol>
<li>사용자는 로그인 시도를 위해 &#39;/public/login&#39; 경로로 접속합니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sloth_/post/9552d7dc-13bf-4cc6-8421-a192907400e5/image.png" alt=""> </p>
<ol start="2">
<li><p>이후 사용자는 본인의 계정을 입력하고 로그인 버튼을 클릭합니다.</p>
</li>
<li><p>로그인 버튼 클릭 시 필터에 설정한 로그인 진입 URL인 /doLogin으로 사용자가 입력한 
계정 정보를 전송합니다.</p>
</li>
<li><p>서버는 사용자의 인증 로직을 처리할 필터로 LoginFilter라는 이름의 커스텀 필터를 구현해
UserPasswordAuthenticationFilter 전에 등록해 두었습니다.</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sloth_/post/817257af-f09c-4208-9bde-4d3000ea0330/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/5cdcda79-fec6-4af2-bc5b-87492b0acc15/image.png" alt=""></p>
<ul>
<li>로그인 필터는 AbstractAuthenticationProcessingFilter 클래스를 상속받아 구현하였으므로,
이후 디버깅 시 부모 클래스의 메소드나 변수가 나올 수 있습니다.</li>
</ul>
<ol start="5">
<li>Spring Security는 Filter chain을 구성하고 chain 순서에 맞게 각각의 Filter를 
순차적으로 처리하는데, 이 중 사용자 인증을 담당하는 UserPasswordAuthenticationFilter 순서에
등록한 LoginFilter에서 사용자 인증 요청이 전달되어 처리되게 됩니다.</li>
</ol>
<ul>
<li>doFilter() 메소드에 의해 인증이 필요한지 여부를 비교하고 인증이 필요할 경우
attemptAuthentication() 메소드를 실행합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/52dc13f9-033c-4880-a8d6-24ffec5c290a/image.png" alt=""></p>
<ul>
<li>이후 인증 처리가 완료될 경우, successfulAuthentication() 메소드를 실행합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/872a0bdf-19f5-48ad-9af6-32c1526f3998/image.png" alt=""></p>
<ul>
<li>LoginFilter에서는 부모 클래스에서 실행된 attemptAuthentication() 메소드를 통해
request와 response를 전달 받아 실행됩니다. (authenticate() 메소드는 하단에 따로 서술)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/3751a7a1-2afe-4a55-be83-cbfabb803ab7/image.png" alt=""></p>
<ol start="6">
<li>전달받은 request에서 사용자가 전달한 계정 정보를 가져옵니다. 
(현재 소스에서는 username/password).</li>
</ol>
<ul>
<li>파라미터 명은 form의 input name으로 설정해 두었으므로 해당 이름을 key로 조회합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/f38ef876-7bb2-444a-9891-c8013609408b/image.png" alt=""></p>
<ol start="7">
<li><p>이후 객체 생성 시 전달받은 authenticationManager에게 인증 시 실제 사용자 정보와 비교할 때 
사용할 임시 토큰을 생성해 파라미터로 전달하며 authenticate() 메소드를 실행합니다.</p>
<ul>
<li>authenticationManager는 SecurityConfig의 getAuthManager() 메소드를 통해 
생성 시 전달받았습니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/0e2076f3-3281-4510-82f4-438c29407057/image.png" alt=""></p>
<ul>
<li>getAuthManager() 메소드는 내부에서 auth의 getOrBuild() 메소드의 return 값을
반환합니다.</li>
</ul>
</li>
</ol>
<ul>
<li>getOrBuild() 메소드는 내부에서 현재 클래스가 빌드되었는지 체크 후 빌드가 되어 있으면
해당 객체를, 빌드가 되어있지 않으면 빌드 후 해당 결괏값을 반환합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/64966b9d-925c-4c67-b7ab-6fc09f4db5a6/image.png" alt=""></p>
<ul>
<li>이때, build() 메소드에서는 doBuild() 메소드를 실행 후 해당 결과를 object 멤버 변수에 
담아 반환하도록 개발되어 있습니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/ad8b8e22-8d73-4bcf-92a0-65dffde0bb8a/image.png" alt=""></p>
<ul>
<li>doBuild() 메소드는 synchronized 키워드를 통해 스레드 동기화를 진행한 상태로 메소드를
실행하고, init() 및 관련 configure() 메소드들을 순차적으로 실행한 후, 
performBuild() 메소드의 결괏값을 반환합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/41c5fbda-f1a5-4920-8c3e-fc96a8c9faea/image.png" alt=""></p>
<ul>
<li>performBuild() 메소드 내부에서는 providerManager 객체를 생성해 반환합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/fd6bcf89-8705-4858-8218-9fd72df3b5d5/image.png" alt=""></p>
<ul>
<li>이 때 Component로 등록해두었던 CustomAuthProvider가 AuthBuilder를 Bean으로 주입 할 때 함께 주입되며 결과적으로 providerManager 객체의 생성자에 전달됩니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/67cb9e63-331a-45dd-90c1-4376021ac7ce/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/2da5a572-2c69-4484-9fc7-fe75069bac90/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/1efcc376-70f1-4aa3-b836-0dc5bb383afc/image.png" alt=""></p>
<ol start="8">
<li>filter에 전달된 authenticationManager 객체는 결과적으로 providerManager 객체를
담고있으므로, providerManager.authenticate() 메소드가 실행되게 됩니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sloth_/post/fc3ca780-606a-46c2-b8b9-9236878b7819/image.png" alt=""></p>
<ul>
<li>authenticate() 메소드에서는 생성자로 전달받은 providers 멤버 변수의 iterator를 
var9 변수에 저장합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/dc9d9043-65dd-41c8-9416-927ff70b3d81/image.png" alt=""></p>
<ul>
<li>이후 iterator에서 provider 객체를 하나 꺼내어 처리할 준비를 합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/7d6d2a63-370c-49ea-bec6-e77eb757505c/image.png" alt=""></p>
<ul>
<li>iterator에서 꺼내어진 provider 객체의 authenticate() 메소드를 실행한 후 결괏값이 존재할 
경우, result 변수에 담아 반환하고 반복문을 종료합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/f2aee90f-0c69-4b28-baea-9392068645ba/image.png" alt=""></p>
<ul>
<li>해당 부분에서 같이 providerManager 객체에 전달되었던 CustomAuthProvider의 부모 메소드인 AbstractUserDetailsAuthenticationProvider의 authenticate() 메소드가 실행되게 됩니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/8d99d57e-b97b-4cae-a830-189c9586cced/image.png" alt=""></p>
<ul>
<li><p>determineUsername() 메소드를 실행한 후 결괏값으로 username을 반환받습니다.
이때 반환되는 것은 사용자가 로그인 시도 시 입력한 계정 id가 됩니다.</p>
</li>
<li><p>이후 userCache를 이용해 캐시된 사용자 목록에 존재하는지 확인하는데, 해당 프로젝트는 캐시 설정을
별도로 하지 않았으므로 NullUserCache 객체를 담고 있는 userCache는 null값을 반환합니다.</p>
</li>
<li><p>userCache의 반환 값이 null이므로 retrieveUser() 메소드를 실행한 후 반환값을 user에
저장합니다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/54d8ef72-fc89-4eae-9d6e-32f8d3ca82a0/image.png" alt=""></p>
<ul>
<li>retrieveUser() 메소드는 함께 전달받았던 userDetailsService를 이용해 해당 계정을 가진
userDetails가 존재하는지 확인 후 존재할 경우 userDetails를 반환합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/f58685ff-732f-45bd-81a5-9b2a0de21006/image.png" alt=""></p>
<ul>
<li>이후 preAuthenticationChecks 변수에 저장되어 있던 DefaultPreAuthenticationChecks
객체를 이용해 기본적인 userDetails의 체크를 진행한 후, 통과되면 authProvider의
additionalAuthenticationChecks() 메소드에 userDetails를 전달합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/8846bd76-0f30-4709-a1ce-7c5089906c32/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/95810676-79cd-47f6-8f49-d8a44bb435bb/image.png" alt=""></p>
<ul>
<li>전달 이후 DefaultPostAuthenticationChecks 객체의 check() 메소드를 실행한 후
마찬가지로 체크 통과 시 createSuccessAuthentication() 메소드를 실행합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/02528d32-75ad-4c10-9139-d138e939b64b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/7d289ca2-bd86-40ab-8ef0-b9f1bd8e0565/image.png" alt=""></p>
<ul>
<li>인증이 완료된 userDetails를 이용해서 UsernamePasswordAuthenticationToken를
생성해 토큰을 반환합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/22d0ec4b-d76d-4c4e-87a4-d0f81f447cdc/image.png" alt=""></p>
<ol start="9">
<li>위에서 초기에 분석한 대로 provider에서 Authentication 객체가 반환되었으므로, 
LoginFilter의 successfulAuthentication() 메소드가 실행되게 됩니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sloth_/post/2c223136-4493-4132-8c94-296426601a20/image.png" alt=""></p>
<ul>
<li>인증 성공 처리를 위해 생성된 인증 결과인 authResult를 쿠키에 담아 사용자에게 전달할 준비를 합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sloth_/post/fe56898e-d939-43d0-9441-ef3ff67e6494/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sloth_/post/a36c6db9-8b74-4b16-a253-da7b56ab1127/image.png" alt=""></p>
<ul>
<li>이후 쿠키를 포함한 response와 함께 사용자를 인증 성공 페이지(&quot;/private/success&quot;)로 
rediect 시켜주며 로그인에 대한 처리를 완료합니다.</li>
</ul>
<h2 id="결론">결론</h2>
<p>현재까지 간단한 모듈 개발을 통해 Spring Security에서 로그인을 시도할 때 어떤 클래스와 메소드들을 
이용해 처리해주는지 간략하게 살펴보았습니다. </p>
<p>사실, 상속받아 직접 구현하는 부분은 극히 일부분이고 그 이외의 부분들은 Spring에 추상 클래스들을 통해
이미 어느정도 개발되어 로직이 이어지기 때문에 위에 소스처럼 LoginFilter의 Overriding을 진행한
부분만 구현하여도 기본적인 로그인 기능은 제공할 수 있습니다만, </p>
<p>그래도 해당 부분을 조금이라도 알고 있으면 도움이 될까 싶어 디버깅 및 소스 분석 연습 겸 간단한 로그인 
모듈을 구현하여 개발해보았습니다.</p>
<h4 id="사용자-인가-부분은-다음-글에-작성될-예정입니다">사용자 인가 부분은 다음 글에 작성될 예정입니다.</h4>
<h4 id="해당-소스는-언젠가-제가-git-공부를-조금-더-하고-git-사용법을-정리할-때-올라갈-예정입니다">해당 소스는 언젠가 제가 git 공부를 조금 더 하고 git 사용법을 정리할 때 올라갈 예정입니다.</h4>
<blockquote>
<p>혹시 틀린 부분이나 수정하면 좋을법한 부분이 보인다면 해당 부분을 댓글로 남겨주세요.
댓글로 남겨주신 부분에 대해서는 더 공부한 후에 해당 글에 추가하거나 별도의 글로 또 남겨두겠습니다. </p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>