<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>BBLOG</title>
        <link>https://velog.io/</link>
        <description>천천히 기록해보는 비비로그</description>
        <lastBuildDate>Fri, 12 Jan 2024 08:36:58 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>BBLOG</title>
            <url>https://velog.velcdn.com/images/lee_bb/profile/761684ac-0295-4251-95db-c19ade96e72c/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. BBLOG. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/lee_bb" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[HTTP 메소드]]></title>
            <link>https://velog.io/@lee_bb/HTTP-%EB%A9%94%EC%86%8C</link>
            <guid>https://velog.io/@lee_bb/HTTP-%EB%A9%94%EC%86%8C</guid>
            <pubDate>Fri, 12 Jan 2024 08:36:58 GMT</pubDate>
            <description><![CDATA[<h1 id="http-api">HTTP API</h1>
<h2 id="api-uri를-정할-때">API URI를 정할 때</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>URI를 정할 때 가장 중요한 것은 리소스를 잘 식별하는 것이다.</strong></span> 회원 가입/수정/삭제/조회에 대한 API를 만든다고 가정했을 때 등록, 수정, 조회, 삭제하는 것을 리소스로 구분하지 않는다. 리소스는 “회원”이다. 때문에 URI를 만들 때 <code>create-member</code>, <code>update-member</code>이 아닌 <code>member/{id}</code>로 만드는 것이다.</p>
<p>단, 리소스를 최대한 사용해서 URI를 설계하되, <strong>리소스만으로 URI를 설계하기 어려운 경우 행위가 포함된 URI를 설계할 수도 있다.</strong> 이런 URI를 <code>컨트롤 URI</code>라고 한다.</p>
<h1 id="http-메소드">HTTP 메소드</h1>
<p>위처럼 URI를 설계한다고 했을 때, 회원 조회/등록/수정/삭제 모두 <code>member/{id}</code>라는 URI를 가지고 가면 어떤 게 수정인지 삭제인지 행위를 구분할 수 없게 되는데 이때 사용하는 것이 <span style="background-color:#fff5b1; color:black"><strong>행위는 메소드를 가지고 구분한다.</strong></span></p>
<p>예를 들어 member/{id}라는 URI에 GET 방식으로 요청이 오면 해당 리소스를 조회하고 DELETE 방식으로 오면 해당 리소스를 삭제하는 요청으로 구분할 수 있다.</p>
<h2 id="👀-주요-메소드">👀 주요 메소드</h2>
<h3 id="get--리소스-조회">GET : 리소스 조회</h3>
<p><span style="background-color:#fff5b1; color:black"><strong>서버에 전달하고 싶은 데이터는 쿼리(쿼리 파라미터, 쿼리 스트링)를 통해서 전달한다.</strong></span> 메시지 바디를 통해서 전달할 수 있긴 하지만 지원하지 않는 곳이 많아서 잘 사용하지 않는다.</p>
<h3 id="post--요청-데이터-처리주로-등록에-사용">POST : 요청 데이터 처리(주로 등록에 사용)</h3>
<p>메시지 바디를 통해서 서버로 요청 데이터를 전달한다. 서버는 클라이언트로부터 받은 요청 데이터를 처리하는데 메시지 바디를 통해 들어온 데이터를 처리하는 모든 기능을 수행한다. 보통 신규 데이터를 등록하는(ex 회원가입) 데 많이 사용한다.</p>
<p><span style="background-color:#fff5b1; color:black"><strong>POST는 단순히 새 리소스를 등록하는 것 뿐만 아니라 데이터 생성, 변경을 넘어 프로세스를 처리해야하는 경우에도 사용할 수 있다.</strong></span> 예를 들어 주문이 완료된 경우 배송이 시작됐을 때, 배송 기사가 지정되는 등 큰 데이터 프로세스가 변경되는 경우에는 POST로 처리한다. 때문에 POST의 결과로 새로운 리소스가 생성되지 않더라도 큰 변화가 일어나는 경우 POST를 이용한다.</p>
<p>또한, JSON을 이용해서 조회 데이터를 넘겨야 하는데 GET 메소드를 사용하기 어려운 경우에도 POST를 이용할 수 있다.</p>
<h3 id="put--리소스-대체">PUT : 리소스 대체</h3>
<p><span style="background-color:#fff5b1; color:black"><strong>리소스가 있으면 대체하고 없으면 새로 생성해준다.</strong></span> 덮어쓰기 하는 행위라고 생각할 수 있다. <span style="background-color:#fff5b1; color:black"><strong>POST와의 차이점은 클라이언트가 서버에서 어디에 리소스를 생성할지 모르지만 PUT은 클라이언트가 리소스 위치를 알고 URI를 지정한다는 것이다.</strong></span></p>
<p>🚨 <span style="color:red"><strong>리소스를 완전히 대체한다는 점을 주의!</strong></span>
username : young, age : 20 인 데이터가 존재했을 때 age값만 업데이트 하기 위해서 age : 30만 보내면 서버는 username : young은 없는 age : 30만 가지고 있게 된다.</p>
<h3 id="patch--리소스-부분-변경">PATCH : 리소스 부분 변경</h3>
<p>PUT을 이용해서 리소스가 <span style="background-color:#fff5b1; color:black"><strong>완전히 대체되게 하고 싶은 것이 아니라 리소스의 일부분(ex age 값만 변경)만 변경</strong></span>하고 싶다면 PATCH를 이용할 수 있다. PATCH 사용도 거의 가능하긴 하지만 HTTP가 못 받아들이는 경우, POST를 이용하면 된다.</p>
<h3 id="delete--리소스-삭제">DELETE : 리소스 삭제</h3>
<h3 id="기타-메소드">기타 메소드</h3>
<ul>
<li>HEAD : GET과 동일하지만 메시지 부분을 제외하고 상태 줄과 헤더만 반환</li>
<li>OPTIONS : 대상 리소스에 대한 동신 가능 옵션을 설명(주로 CORS에서 사용)</li>
<li>CONNECT : 대상 자원으로 식별되는 서버에 대한 터널 설정</li>
<li>TRACE : 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트 수행</li>
</ul>
<h1 id="http-메소드-속성">HTTP 메소드 속성</h1>
<h2 id="안전safe">안전(Safe)</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>호출해도 대상 리소스를 변경되지 않는 것을 안전하다고 표현한다.</strong></span> 안전은 해당 리소스만을 고려하기 때문에 계속 호출하면서 발생하는 장애까지는 고려하지 않는다. GET, HEAD 등(무언가 바뀌지 않는 것)이 안전한 메소드에 포함된다.</p>
<h2 id="멱등idempotent">멱등(Idempotent)</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>한 번을 호출하든 여러 번 호출하든 결과가 똑같은 것을 말한다.</strong></span> 몇 번을 조회해도 같은 결과를 조회하는 GET이나, 같은 데이터를 가지고 값을 대체하면 항상 최종 결과가 같은 PUT이나, 같은 요청으로 여러 번 삭제해도 삭제 결과가 같은 DELETE를 멱등하다고 말한다.</p>
<p>POST의 경우 만약 결제 요청이라고 가정했을 때 중복 결제 요청이 발생하기 때문에 멱등이 아니라고 본다.</p>
<h3 id="🤔-멱등을-구분하는-이유">🤔 멱등을 구분하는 이유?</h3>
<p>서버가 타임아웃 등의 이유로 정상적인 응답을 주지 못 했을 때, 똑같은 요청을 다시 보내더라도 결과가 똑같기 때문에 문제가 발생하지 않는다. 이것을 근거로 <span style="background-color:#fff5b1; color:black"><strong>자동 복구 메커니즘에 이용할 수 있다.</strong></span></p>
<p>🚨 <span style="color:red"><strong>재요청을 보내는 중간에 리소스가 변경되는 경우?</strong></span>
멱등은 외부 요인으로 인해 중간에 리소스가 변경되는 것까지 고려하지 않는다. 동일한 요청인 경우 동일한 결과를 내는 것을 가지고 판단한다.</p>
<h2 id="캐시가능cacheable">캐시가능(Cacheable)</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>응답 결과 리소스를 캐시해서 사용해도 되는 것들을 말한다.</strong></span> GET, HEAD, POST, PATCH가 캐시 가능에 포함되지만 실제로는 GET, HEAD 정도만 캐시로 사용한다. POST나 PATCH는 본문 내용까지 캐시 키로 고려해야 하기 때문에 구현이 까다롭다.</p>
<blockquote>
<p>강의 : 모든 개발자를 위한 HTTP 기본 지식 - 김영한님</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 기본 개념]]></title>
            <link>https://velog.io/@lee_bb/HTTP-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@lee_bb/HTTP-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Fri, 12 Jan 2024 08:25:29 GMT</pubDate>
            <description><![CDATA[<h1 id="http">HTTP</h1>
<p>HyperText Transfer Protocol의 약어인 HTTP는 하이퍼텍스트(HTML)을 전송하는 프로토콜로 시작한 것으로 현재는 이미지, 음성, 영상, 파일, JSON, XML 등 모든 형태의 데이터를 전송하기 위해 사용하고 있다. 서버 간에 데이터를 주고 받을 때도 대부분 HTTP를 사용한다.</p>
<h2 id="http-역사">HTTP 역사</h2>
<ul>
<li>HTTP/0.9 1991년 : GET 메소드만 지원하며 HTTP 헤더가 없다.</li>
<li>HTTP/1.0 1996년 : 다른 메소드와 헤더가 추가됐다.</li>
<li>HTTP/1.1 1997년 : 가장 많이 사용하는 버전으로 가장 중요한 버전이다.
해당 버전에 웬만한 기능이 다 들어있고, 2,3 버전은 성능 개선 위주다.
RFC2068(1997) → RFC2616(1999) → RFC7230 ~ 7235(2014)</li>
<li>HTTP/2 2015년 : 성능 개선</li>
<li>HTTP/3 진행 중 : TCP 대신 UDP를 사용, 성능 개선</li>
</ul>
<h3 id="기반-프로토콜">기반 프로토콜</h3>
<p>HTTP 1.1이나 2 버전은 TCP 프로토콜 기반으로 사용하고 3 버전의 경우 UDP 기반으로 사용한다.</p>
<h1 id="http-특징">HTTP 특징</h1>
<h2 id="클라이언트-서버-구조">클라이언트 서버 구조</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>클라이언트가 서버에 요청을 보내고 응답을 대기하면 서버가 요청에 대한 결과를 만들어서 응답하는 Request Response 구조로 되어있다.</strong></span> 이렇게 구분이 되어있으면 클라이언트는 사용자에게 보여지는 UI에 집중하고 서버는 데이터나 비즈니스 로직에 집중할 수 있게 된다. 각각의 집중해야 할 부분이 구분된다면 <span style="background-color:#fff5b1; color:black"><strong>각 부분을 더 진화시키는 데 있어 분화될 수 있다.</strong></span></p>
<h2 id="무상태-프로토콜stateless-비연결성">무상태 프로토콜(Stateless), 비연결성</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>HTTP는 무상태 프로토콜(Stateless)를 지향한다.</strong></span> 상태 유지를 뜻하는 Stateful과 반대되는 개념으로 서버가 클라이언트의 상태를 유지하지 않는다는 뜻이다. 클라이언트가 서버와 통신에 필요한 요청 정보를 함께 보내기 때문에 중간에 다른 서버가 투입되더라도 문제가 발생하지 않는다. <span style="background-color:#fff5b1; color:black"><strong>클라이언트 요청이 갑자기 증가해도 서버를 대거 투입할 수 있고, 응답 서버를 쉽게 교체할 수 있다는 장점이 있다.</strong></span> →<strong>스케일 아웃(수평 확장)</strong>에 유리하다.</p>
<p>대표적인 예로 HTTP와 UDP가 있다. UDP는 handshake 과정을 거치지 않고 무작정 보내기 때문에 세션 정보를 서버가 저장하지 않는다는 점에서 무상태 프로토콜이라고 말할 수 있다.</p>
<p>💡 <span style="color:orange"><strong>스케일 아웃(Scale-out)?</strong></span>
서버를 여러 대 추가하여 시스템을 확장하는 방법이다. 서버가 여러 대가 되기 때문에 각 서버에 걸리는 부하를 균등하게 해주는 로드밸런싱이 필수적으로 동반되어야 한다. 서버 한 대가 장애가 걸려 다운되더라도 다른 서버를 이용할 수 있다는 장점이 있으나, 모든 서버가 동일한 데이터를 가지고 있어야 하므로 데이터 변화가 적은 웹 서버에 적합하다.</p>
<p>💡 <span style="color:orange"><strong>Stateful(상태 유지)?</strong></span>
서버가 클라이언트의 이전 상태를 보존하는 것을 말한다. 클라이언트는 이전 상태를 유지하고 있다고 보고 있기 때문에 서버가 중간에 바뀌면 서버에서는 클라이언트의 상태 정보를 저장하고 있지 않기 때문에 다른 서버에 요청에 필요한 정보를 미리 알려줘야 한다. 안 그러면 원하는 응답을 받아낼 수 없게 된다.
대표적인 예로 TCP의 3-way handshake 과정이 있다.</p>
<h3 id="stateless의-실무-한계-🫠">Stateless의 실무 한계 🫠</h3>
<p>Stateless는 로그인 상태를 유지해야 하는 것처럼 무상태로는 설계가 안 되는 경우 한계가 있다. 페이지를 이동해서 새로운 요청을 보낼 때마다 로그인 정보가 날아가면 안 되기 때문이다. 그래서 일반적으로 브라우저의 쿠키나 세션에 로그인 정보 같은 유지해야 할 데이터를 담아서 상태 유지를 한다. 그래도 상태 유지는 최소한만 이용하는 것이 좋다.</p>
<p>그리고 무상태로 설계할 때는 매 요청마다 필요한 정보를 함께 보내줘야 하기 때문에 보내는 데이터 양이 많아진다는 단점도 있다.</p>
<h2 id="비연결성">비연결성</h2>
<p><span style="background-color:#99ccff; color:black"><strong>연결을 유지하는 모델</strong></span>은 클라이언트 1~3이 순차적으로 연결이 되어도 먼저 연결된 클라이언트 1의 연결이 끊기지 않는다. 또한 클라이언트 1만 요청을 보내고 있어도 클라이언트 2, 3은 그냥 놀고 있는 상태가 된다. 이때 <span style="background-color:#99ccff; color:black"><strong>서버가 연결을 계속 유지하면서 서버의 자원이 계속 소모된다.</strong></span></p>
<p><span style="background-color:#fff5b1; color:black"><strong>연결을 유지하지 않는 HTTP 같은 모델은 요청할 때 서버와 연결을 하고 응답을 받고 나면 서버와 연결을 종료한다.</strong></span> 때문에 <span style="background-color:#fff5b1; color:black"><strong>서버 자원을 매우 효율적으로 사용할 수 있다.</strong></span> 일반적으로 초 단위 이하의 빠른 속도로 응답이 오고 1시간 안에 수천명이 서비스를 사용해도 실제 서버에 동시에 처리하는 요청은 수십개 정도로 매우 작아진다.</p>
<h3 id="비연결성의-단점-😢">비연결성의 단점 😢</h3>
<p>현재 페이지를 보다가 이전 페이지로 돌아가더라도 TCP/IP 연결을 새로 맺어야 하기 때문에 3-way handshake 시간이 추가된다. 웹 브라우저로 사이트를 요청하면 HTML 뿐만 아니라 JS, CSS, 추가 이미지 등 많은 자원이 함께 다운로드 된다.</p>
<p>지금은 HTTP 지속 연결(Persistent Connections)로 문제를 해결하고 HTTP 2, 3 버전에서 더 많은 최적화가 이루어지고 있다.</p>
<h1 id="http-메시지-구조">HTTP 메시지 구조</h1>
<ul>
<li>start-line : 시작 라인</li>
<li>header : 헤더</li>
<li>empty line : 공백라인 (CRLF)</li>
<li>message body</li>
</ul>
<p>HTTP는 위의 구조로 이뤄어져 있다. 요청 메시지와 응답 메시지는 보통 다른 구조로 사용하는데 요청 메시지에는 시작 라인과 헤더, 공백 라인을 갖고 요청 시 보낼 메시지가 없으면 바디 부분은 생략하고 보낸다. 응답 메시지는 시작 라인, 헤더, 공백 라인을 다 갖고 응답에 대한 내용을 메시지 바디에 넣는다.</p>
<h2 id="시작-라인start-line">시작 라인(Start-line)</h2>
<p>시작 라인은 크게 <code>request-line</code>과 <code>status-line</code>으로 되어있는데 <span style="background-color:#fff5b1; color:black"><strong>요청 메시지</strong></span>인 경우 <strong>request-line</strong>이라고 한다. request-line에는 <code>메소드(GET, POST 등), 공백, request-target(PATH : 요청 대상), 공백, HTTP-version, CRLF(엔터)</code>가 순서대로 들어간다.</p>
<p><span style="background-color:#fff5b1; color:black"><strong>응답 메시지</strong></span>인 경우 <strong>status-line</strong>인데 <code>HTTP-version, 공백, status-code(HTTP 상태코드), 공백, reason-phrase, CRLF</code> 순서로 들어간다.</p>
<p>💡 <span style="color:orange"><strong>HTTP 상태코드?</strong></span>
요청의 성공, 실패를 나타내는 코드로 맨 앞 숫자를 가지고 분류한다.</p>
<ul>
<li><strong>1xx (정보)</strong> : 요청을 받았으며 프로세스를 계속함.</li>
<li><strong>2xx (성공)</strong> : 요청을 성공적으로 받았으며 인식했고 수용함.</li>
<li><strong>3xx (리다이렉션)</strong> : 요청 완료를 위해 추가 작업 조치가 필요.</li>
<li><strong>4xx (클라이언트 오류)</strong> : 요청의 문법이 잘못되었거나 요청을 처리할 수 없음.</li>
<li><strong>5xx (서버 오류)</strong> : 서버가 명백히 유효한 요청에 대해 충족을 실패.</li>
</ul>
<h2 id="헤더">헤더</h2>
<p>HTTP 헤더는 <code>filed-name”:” OWS field-value OWS</code> 구조로 이루어진다. OWS는 띄어쓰기를 허용한다는 뜻으로 띄어써도 되고 붙여써도 된다.</p>
<p>HTTP header는 전송에 필요한 메시지 바디의 내용, 메시지 바디 크기, 압축, 인증, 요청 클라이언트 정보 등 모든 부가 정보를 담고 있다.</p>
<h2 id="메시지-바디">메시지 바디</h2>
<p>실제 전송할 데이터를 담는 곳으로 HTML 문서나 JSON, 이미지, 영상 등 바이트(byte)로 표현할 수 있는 모든 데이터가 전송 가능하다.</p>
<blockquote>
<p>강의 : 모든 개발자를 위한 HTTP 기본 지식 - 김영한님</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[IP, TCP 프로토콜과 URI]]></title>
            <link>https://velog.io/@lee_bb/IP-TCP-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EA%B3%BC-URI</link>
            <guid>https://velog.io/@lee_bb/IP-TCP-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EA%B3%BC-URI</guid>
            <pubDate>Sun, 07 Jan 2024 09:51:22 GMT</pubDate>
            <description><![CDATA[<h1 id="ip인터넷-프로토콜--internet-protocol">IP(인터넷 프로토콜 : Internet Protocol)</h1>
<p>멀리있는 컴퓨터와 통신할 때 기본적으로 IP 주소를 가지고 통신하게 된다. 인터넷 프로토콜은 지정한 IP 주소(IP Address)에 데이터를 전달하고 패킷(Packet)이라는 통신 단위로 데이터를 전달하는 역할을 한다.</p>
<p>IP 패킷은 규칙이 존재하는데 IP 패킷을 만들 때 데이터를 보내는 전송 IP 주소와 받는 수신 IP 주소, 전송할 데이터 등을 넣는다. 이렇게 만들어진 패킷을 서로 주고 받아서 통신하게 된다.</p>
<p>💡 <span style="color:orange"><strong>Packet?</strong></span>
Package(포장)와 bucket(양동이)의 합성어로 네트워크를 통해 전송되는 형식화된 데이터 덩어리를 말한다.</p>
<h2 id="ip-프로토콜의-한계">IP 프로토콜의 한계</h2>
<p>하지만 이렇게 IP 프로토콜을 가지고 통신하는 것은 한계가 있다. 우선 패킷을 받을 대상이 없거나 서비스 불능 상태여도 패킷을 전송하기 때문에 <strong>비연결성</strong>이라는 한계가 존재한다. 그리고 패킷이 중간에 사라지거나 여러 개의 패킷을 넘겼을 때, 순서대로 오지 않을 경우가 발생할 수 있기 때문에 <strong>비신뢰성</strong>이 존재하고, 같은 IP를 사용하는 서버에서 통신하는 애플리케이션이 둘 이상이라면 <strong>프로그램을 구분할 수 없다.</strong></p>
<h1 id="tcp">TCP</h1>
<h2 id="tcpip-패킷-정보">TCP/IP 패킷 정보</h2>
<p>IP 패킷의 경우 출발지 IP, 목적지 IP, 기타 정보를 담고 있다면 TCP 패킷은 출발지 Port, 목적지 Port, 전송 제어, 순서, 검증 정보 등을 담고 있다. 때문에 IP만으로 해결이 안 됐던 순서 제어나 검증에 대한 문제가 해결이 된다.</p>
<h2 id="tcp-특징">TCP 특징</h2>
<p>전송 제어 프로토콜(Transmission Control Protocol)인 <span style="background-color:#fff5b1; color:black"><strong>TCP는 연결지향(TCP 3 way handshake)이다.</strong></span> 전송하는 곳과 받는 곳이 연결이 됐는지 안 됐는지 확인하기 때문이다. 또한, <span style="background-color:#fff5b1; color:black"><strong>상대가 데이터를 받았는지 안 받았는지 데이터 전달과 순서에 대한 보증이 된다.</strong></span> 때문에 신뢰할 수 있는 프로토콜이다. 현재 대부분 TCP 프로토콜을 사용한다.</p>
<h3 id="tcp-3-way-handshake가상-연결">TCP 3 way handshake(가상 연결)</h3>
<p>클라이언트가 먼저 SYN(접속 요청)을 보내고 서버가 SYN(접속 요청)과 ACK(요청 수락)을 보낸다. 그러고 나서 클라이언트가 다시 ACK(요청 수락)을 보낸다. 그 후에 데이터를 전송하게 된다.</p>
<p>단, TCP 3 way handshake는 실제로 연결이 된 것은 아니고 SYN, ACK를 주고 받으면서 논리적으로 연결이 됐다고 가정하는 것이다. 전용 랜선이 보장되지는 않는다.</p>
<h1 id="udp">UDP</h1>
<p>사용자 데이터그램 프로토콜(User Datagram Protocol)인 UDP는 TCP와 같은 계층에 있는 프로토콜이다. <strong>UDP는 TCP와 다르게 기능이 거의 없다. IP와 거의 비슷한 수준이지만, UDP에는 Port를 함께 담는다.</strong> 이 메시지가 맞는지에 대한 체크섬 정도만 추가되어있다.</p>
<p>💡 <span style="color:orange"><strong>체크섬(Checksum)?</strong></span>
네트워크를 통해서 전송된 데이터의 값이 변경되었는지 무결성을 검사하는 값으로 무결성을 이용해서 네트워크를 통해 수신된 데이터의 오류 여부를 확인한다.</p>
<p>UDP는 검증이 확실하지 않지만 기능이 단순한 만큼 속도가 빠르다. TCP 프로토콜이 많은 범위를 차지하고 있지만, HTTP 통신을 할 때 handshake가 일어날 때 걸리는 시간을 줄이기 위해서 UDP의 사용이 조금씩 늘고 있다.</p>
<h1 id="port">PORT</h1>
<p><span style="background-color:#fff5b1; color:black"><strong>하나의 IP에 두 개 이상을 연결하려고 했을 때 구분을 하기 위해 포트를 사용한다.</strong></span> 같은 IP 내에서 프로세스를 구분하는 역할을 하는 것이 포트로 예를 들어 하나의 IP 주소에서 게임 프로세스에는 port가 8000이고 웹 브라우저는 10010일 때 게임 프로세스에 접근하려고 하면 해당 포트가 8000인 곳에 데이터를 보내면 된다.</p>
<p>IP가 하나의 아파트라고 생각하면 Port는 호수라고 생각하면 편하다.</p>
<p>💡 포트는 0<del>65535 사이에 할당이 가능하다.
0</del>1023 : 잘 알려진 포트로 사용하지 않는 것이 좋다.
<strong>* 기본 포트</strong></p>
<ul>
<li>FTP : 20, 21</li>
<li>TELNET : 23</li>
<li>HTTP : 80</li>
<li>HTTPS : 443</li>
</ul>
<h1 id="dnsdomain-name-system">DNS(Domain Name System)</h1>
<p><strong>IP는 길고 숫자로 되어있어 기억하기 어렵고, 변경될 수 있기 때문에 DNS라는 도메인 네임 시스템을 이용한다.</strong>  우리가 흔하게 접속하는 네이버의 아이피 주소는 실제로는 숫자로 되어있겠지만 <a href="http://www.naver.com%EB%A7%8C">www.naver.com만</a> 작성하더라도 네이버 웹 사이트에 접속할 수 있는 것처럼 사용 편의를 위해 도메인 네임을 이용한다.</p>
<h1 id="uriuniform-resource-identifier">URI(Uniform Resource Identifier)</h1>
<p>Uniform은 리소스를 식별하는 통일된 방식, Resource는 URI로 식별할 수 있는 모든 자원, Identifier은 식별하기 위해 필요한 정보를 뜻한다.</p>
<h2 id="uri와-urlurn">URI와 URL/URN</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>URI라는 리소스를 식별하는 가장 큰 개념이 존재하고 그 안에서 URL과 URN이 존재한다.</strong></span> URL은 Resource Locator로 리소스의 위치를 가르키고 URN은 Resource Name으로 리소스의 이름을 가르킨다.</p>
<p>위치는 변할 수 있지만 이름은 변하지 않는다. 하지만 URN 이름만으로 실제 리소스를 찾을 수 있는 방법은 보편화 되지 않다.</p>
<h2 id="url-분석하기">URL 분석하기</h2>
<p><code>https://www.google.com:443/search/q=hello&amp;hl=ko</code></p>
<p>위 URL에서 맨 앞에 https는 프로토콜 방식, 그 뒤에 나오는 <a href="http://www.google.com%EC%9D%80">www.google.com은</a> 호스트명, : 뒤에 붙는 숫자가 포트를 의미한다. /search는 패스(경로), 물음표 뒤에 붙는 것은 쿼리 파라미터라고 한다.</p>
<p>scheme 위치에 오는 프로토콜은 어떤 방식으로 자원에 접근할 것인가 하는 약속 규칙을 나타낸다. 호스트명에는 도메인명이나 IP주소를 작성하고, 포트는 생략 가능하다. 패스는 리소스가 있는 경로를 말하는데 보통 계층적 구조( ex) /home/file )로 간다. 쿼리는 key=value 형태로 데이터가 들어가며 물음표(?)로 시작한다. 데이터가 여러 개인 경우 &amp;을 이용해서 추가한다. 쿼리를 보통 웹에서 넘기는 데이터라고 해서 쿼리 파라미터나 전부 문자열로 넘어가기 때문에 쿼리 스트링이라고 많이 부른다.</p>
<blockquote>
<p>강의 : 모든 개발자를 위한 HTTP 웹 기본지식 - 김영한님</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[빈 생명 주기와 콜백]]></title>
            <link>https://velog.io/@lee_bb/%EB%B9%88-%EC%83%9D%EB%AA%85-%EC%A3%BC%EA%B8%B0%EC%99%80-%EC%BD%9C%EB%B0%B1</link>
            <guid>https://velog.io/@lee_bb/%EB%B9%88-%EC%83%9D%EB%AA%85-%EC%A3%BC%EA%B8%B0%EC%99%80-%EC%BD%9C%EB%B0%B1</guid>
            <pubDate>Sun, 03 Dec 2023 12:18:14 GMT</pubDate>
            <description><![CDATA[<h1 id="빈-생명주기-콜백-시작">빈 생명주기 콜백 시작</h1>
<p>데이터베이스 커넥션 풀이나 네트워크 소캣처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고 애플리캐이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면 객체의 초기화와 종료 작업이 필요하다.</p>
<p>스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메소드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공한다. 또한, 스프링은 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 준다. 따라서 안전하게 종료 작업을 진행할 수 있다.</p>
<h2 id="스프링-빈의-이벤트-라이프사이클">스프링 빈의 이벤트 라이프사이클</h2>
<p>🔔 해당 라이프사이클은 싱글톤 기준.</p>
<p>스프링 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 → 사용 → 소멸 전 콜백 → 스프링 종료</p>
<ul>
<li>초기화 콜백 : 빈이 생성되고 의존관계 주입이 완료 된 후 호출</li>
<li>소멸 전 콜백 : 빈이 소멸되기 직전에 호출</li>
</ul>
<p>💡 <span style="color:orange"><strong>객체의 생성과 초기화를 분리</strong></span>
생성자는 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가진다. 반면 초기화는 이렇게 생성된 값들을 활용해서 외부 커넥션을 연결하는 등의 무거운 동작을 수행한다.
따라서 <span style="background-color:#fff5b1; color:black"><strong>생성자 안에 무거운 초기화 작업을 함께 하는 것보다 객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 유지보수 관점에서 좋다.</strong></span> 단, 초기화 작업이 매우 단순하다면 생성자에서 한 번에 처리하는 것이 나을 수 있다.</p>
<h1 id="스프링이-제공하는-빈-생명-주기-콜백-3가지">스프링이 제공하는 빈 생명 주기 콜백 3가지</h1>
<h2 id="인터페이스-initializingbean-disposablebean">인터페이스 InitializingBean, DisposableBean</h2>
<p><code>InitializingBean</code> 인터페이스는 <code>afterPropertiesSet()</code>이라는 의존관계 주입이 끝나면 실행되는 메소드를 가진다. 해당 인터페이스를 클래스에 <code>implements</code> 하면 <code>afterPropertiestSet()</code> 메소드를 오버라이딩 하면서 의존관계 주입 후 초기화할 로직을 메소드 안에 작성할 수 있다.</p>
<p><code>DisposableBean</code> 인터페이스는 <code>destory()</code>라는 소멸 직전에 발생하는 메소드를 갖고 있다. 해당 인터페이스를 <code>implements</code>한 후 <code>destory()</code> 메소드를 오버라이드 해서 빈 소멸 직전에 실행할 로직을 작성할 수 있다.</p>
<p>💡 <span style="color:orange"><strong>InitializingBean, DisposableBean의 단점?</strong></span>
해당 인터페이스는 <strong>스프링 전용 인터페이스로 해당 코드가 스프링 전용 인터페이스에 의존하게 된다는 단점을 가진다.</strong> <strong>초기화, 소멸 메소드의 이름 또한 변경할 수 없다. 그리고 내가 코드를 고칠 수 없는 외부 라이브러리에는 적용할 수 없다는 단점이 존재한다.</strong>
해당 방법은 스프링 초기에 나온 방법으로 최근에는 더 나은 방법들이 존재해서 거의 사용되지 않는다.</p>
<h2 id="빈-등록-초기화-소멸-메소드">빈 등록 초기화, 소멸 메소드</h2>
<p>설정 정보에서 <code>@Bean(initMethod = &quot;초기화 메소드명&quot;, destroyMethod = &quot;소멸 메소드명&quot;)</code> 처럼 초기화, 소멸 메소드를 지정할 수 있는 방법이다.</p>
<pre><code class="language-jsx">@Configuration
    static class LifeCycleConfig {
        @Bean(initMethod = &quot;init&quot;, destroyMethod = &quot;close&quot;)
        public NetworkClient networkClient() {
            NetworkClient networkClient=new NetworkClient();
            networkClient.setUrl(&quot;http://hello-spring.dev&quot;);
            return networkClient;
        }
    }
==========================================================================
//NetworkCient 클래스 내부
public void init() {
        System.out.println(&quot;NetworkClient.init&quot;);
        connect();
        call(&quot;초기화 연결 메시지&quot;);
}

public void close() {
        System.out.println(&quot;NetworkClient.close&quot;);
        disconnect();
}</code></pre>
<p>해당 방법을 이용하면 메소드 이름을 개발자가 자유롭게 지정할 수 있다는 장점이 있고 스프링 빈이 스프링 코드에 의존하지 않아도 된다. 코드가 아니라 설정 정보를 사용하기 때문에 코드를 고칠 수 없는 외부 라이브러리에서도 초기화, 종료 메소드를 지정할 수 있다.</p>
<h3 id="bean의-destroymethod-속성">@Bean의 destroyMethod 속성</h3>
<p>라이브러리는 대부분 <code>close</code>, <code>shutdown</code>이라는 이름의 종료 메소드를 사용하는데 <code>destroyMethod</code> 속성은 기본값이 <code>(inferred)</code>(추론)이라고 등록되어있다. 이 추론 기능은 <code>close</code>, <code>shutdown</code>이라는 이름의 메소드를 자동으로 호출해준다. 때문에 직접 스프링 빈으로 등록하면 종료 메소드를 따로 작성하지 않아도 종료 메소드가 알아서 잘 동작하게 된다.</p>
<p>💡 추론 기능을 사용하고 싶지 않을 때는 <code>destroyMethod=””</code> 처럼 안에 빈 공백 값을 지정해주면 된다.</p>
<h2 id="애너테이션-postconstruct-predestory">애너테이션 @PostConstruct, @PreDestory</h2>
<p>스프링에서 권고하는 방법으로 실행할 초기화 메소드에 <code>@PostContruct</code> 애너테이션을 사용하고 종료 메소드에 <code>@PreDestory</code> 애너테이션을 사용하면 된다.</p>
<p><span style="background-color:#fff5b1; color:black"><strong>애너테이션 하나로 적용되기 때문에 코드 작성에 있어서 매우 편리</strong></span>하고 <code>javax.annotation.PostConstruct</code> , <code>javax.annotation.PreDestroy</code> 패키지에 있는 JSR-250이라는 자바 표준이기 때문에 <span style="background-color:#fff5b1; color:black"><strong>스프링에 종속적인 기술이 아니다.</strong></span> 때문에 스프링이 아닌 다른 컨테이너에서도 동작할 수 있다.</p>
<p>💡 <span style="color:orange"><strong>@PostConstruct, @PreDestory 단점?</strong></span>
메소드에 직접 작성해서 사용하다 보니 외부 라이브러리에는 적용하지 못한다는 단점이 있다. 외부 라이브러리를 초기화, 종료할 때는 @Bean의 기능을 이용하는 것이 좋다.</p>
<blockquote>
<p>강의 : 스프링 핵심 원리 - 김영한 님</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[의존관계 자동 주입]]></title>
            <link>https://velog.io/@lee_bb/%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%9E%90%EB%8F%99-%EC%A3%BC%EC%9E%85</link>
            <guid>https://velog.io/@lee_bb/%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%9E%90%EB%8F%99-%EC%A3%BC%EC%9E%85</guid>
            <pubDate>Sun, 26 Nov 2023 07:25:32 GMT</pubDate>
            <description><![CDATA[<h1 id="의존관계-주입-방법">의존관계 주입 방법</h1>
<h2 id="생성자-주입">생성자 주입</h2>
<pre><code class="language-java">public class OrderServiceImpl implements OrderService{

    private final DiscountPolicy discountPolicy;
    private final MemberRepository memberRepository;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository,
                            DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
        this.memberRepository = memberRepository;
    }
}</code></pre>
<p><span style="background-color:#fff5b1; color:black"><strong>생성자를 통해서 의존 관계를 주입 받는 방법이다. 생성자 호출 시점에 한 번만 호출되는 것이 보장된다.</strong></span> 때문에 불변하는 필수 의존 관계에 주로 사용된다.</p>
<p><strong>생성자 주입은 스프링이 빈으로 등록할 때 생성자를 이용해서 등록하기 때문에 스프링 빈이 등록되는 시점에 생성자 주입에 의한 의존 관계 주입까지 완료된다.</strong></p>
<p>💡 생성자가 딱 한 개만 있다면 <code>@Autowired</code>를 생략해도 자동 주입 된다. (스프링 빈에서만 한함)</p>
<h2 id="수정자-주입setter-주입">수정자 주입(setter 주입)</h2>
<pre><code class="language-java">      @Autowired(required = false)
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
    }

    @Autowired(required = false)
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }</code></pre>
<p><code>setter</code><span style="background-color:#fff5b1; color:black"><strong>라 불리는 필드 값을 변경하는 수정자 메소드를 이용해서 의존 관계를 주입하는 방법이다.</strong></span> <code>setter</code> 메소드는 자바빈 프로퍼티 규약의 수정자 메소드 방식을 이용하는 것이다. <code>setter</code> 메소드를 통해서 값을 변경할 수 있기 때문에 선택해서 사용해야 하거나 변경 가능성이 있는 의존 관계에서 주로 사용한다.</p>
<p>💡 <span style="color:orange"><strong>@Autowried(required = false)</strong></span>
<code>required</code> 속성은 기본 값이 <code>true</code>로 되어있는데 이 때 의존 관계를 주입할 대상이 없으면 에러가 발생한다. 주입할 대상이 존재하지 않아도 동작하게 하려면 해당 속성의 값을 <code>false</code>로 주면 된다.</p>
<h2 id="필드-주입">필드 주입</h2>
<pre><code class="language-java">@Autowired private DiscountPolicy discountPolicy;
@Autowired private MemberRepository memberRepository;</code></pre>
<p><strong>필드에 의존 관계를 주입하는 방식이다.</strong> 코드가 간결해서 단순하게 사용할 수 있지만 <span style="background-color:#fff5b1; color:black"><strong>외부에서 변경이 불가능하기 때문에 테스트하기 힘들다는 단점이 존재한다.</strong></span> DI 프레임워크가 없으면 아무것도 할 수 없게 된다. 때문에 권장하지 않는 방법으로 사용하지 않는 것이 좋다.</p>
<p>단, 애플리케이션의 실제 코드와 상관 없는 테스트 코드나 스프링 설정을 목적으로 하는 <code>@Configuration</code> 같은 곳에서 특별한 용도로 사용하기도 한다.</p>
<h2 id="일반-메소드-주입">일반 메소드 주입</h2>
<pre><code class="language-java">    @Autowired
    public void init(MemberRepository memberRepository,
                        DiscountPolicy discountPolicy) {
        this.memberRepository=memberRepository;
        this.discountPolicy=discountPolicy;
    }</code></pre>
<p><strong>일반 메소드를 통해서 의존 관계 주입을 받는 것을 말한다.</strong> 한 번에 여러 필드를 주입 받을 수 있지만 생성자 주입이나 수정자 주입으로 의존 관계 주입을 설정하기 때문에 일반적으로 잘 사용하지 않는다.</p>
<h1 id="자동-주입-대상-옵션-처리">자동 주입 대상 옵션 처리</h1>
<ol>
<li><code>Autowired(required=false)</code></li>
</ol>
<p><strong>자동 주입 대상이 없으면 수정자 메소드 자체가 호출이 안 된다.</strong></p>
<pre><code>```java
@Autowired(required = false) //실행 자체를 안 함
public void setNoBean1(Member member) {
   System.out.println(&quot;nobean1 : &quot;+member);
}
```</code></pre><ol start="2">
<li><code>org.springframework.lang.@Nullable</code></li>
</ol>
<p><strong>자동 주입 대상이 없으면 <code>null</code>을 입력한다.</strong></p>
<pre><code>```java
@Autowired
public void setNoBean2(@Nullable Member member) {
     System.out.println(&quot;nobean2 : &quot;+member); //null 출력
}
```</code></pre><ol start="3">
<li><p><code>Optional&lt;&gt;</code>
Java8의 <code>Optional</code>을 이용해서 <strong>자동 주입 대상이 없는 경우 <code>Optional.empty</code>가 입력되게 한다.</strong></p>
<pre><code class="language-java"> @Autowired(required = false)
 public void setNoBean3(Optinal&lt;Member&gt; member) {
    System.out.println(&quot;nobean3 : &quot;+member); //Optional.empty 출력
 }</code></pre>
</li>
</ol>
<h1 id="🤔-생성자-주입을-권장하는-이유">🤔 생성자 주입을 권장하는 이유?</h1>
<p>대부분의 의존 관계 주입은 한 번 일어나면 애플리케이션 종료 시점까지 의존 관계를 변경할 일이 없다. 대부분의 의존 관계는 애플리케이션 종료 전까지 불변하게끔 개발하도록 하기 때문이다.</p>
<p>수정자 주입을 이용하는 경우에는  <code>setter</code> 메소드를 <code>public</code>으로 열어두어야 하는데 이런 경우 프로그램이 돌아가는 도중 누군가 실수로 변경할 수도 있게 되고, 변경되면 안 되는 메소드를 열어두는 것은 좋은 설계 방식은 아니다. </p>
<p>생성자 주입을 쓰는 경우 <code>final</code>을 사용할 수 있다. <code>final</code>을 사용하는 경우 내가 생성자 안에 코드를 누락하는 경우 컴파일 에러가 발생해서 프로그램을 돌려보지 않아도 누락을 확인할 수 있다. 그리고 수정자 주입을 사용하는 경우 주입 데이터를 누락했을 때 <code>NullPointException</code>이 발생하게 된다.</p>
<h1 id="requiredargsconstructor"><code>@RequiredArgsConstructor</code></h1>
<p>롬복을 이용해서 <code>@RequiredArgsConstructor</code> <span style="background-color:#fff5b1; color:black"><strong>애너테이션을 사용하면 해당 필드에 final이 붙어있는 것들을 가지고 생성자를 만들어준다.</strong></span> 기존에 생성자 주입을 사용하기 위해서 길게 작성했던 코드를 롬복을 이용하면 더 간편하게 생성할 수 있다.</p>
<p>생성자가 하나인 경우 <code>@Autowired</code>를 생략하기 때문에 의존 관계 주입도 적용된다.</p>
<pre><code class="language-java">@Component
public class OrderServiceImpl implements OrderService{

    private final DiscountPolicy discountPolicy;
    private final MemberRepository memberRepository;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
        this.memberRepository = memberRepository;
    }
}
===================================================================================
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService{

    private final DiscountPolicy discountPolicy;
    private final MemberRepository memberRepository;

}</code></pre>
<h1 id="🚨-조회-빈이-2개인-경우">🚨 조회 빈이 2개인 경우</h1>
<ol>
<li><p><code>@Autowired</code> 필드 명 매칭
<code>@Autowired</code>는 기본적으로 타입으로 매칭을 시도하지만 <strong>여러 빈이 있으면 필드 이름이나 파라미터 이름으로 추가 매칭을 시도한다.</strong></p>
<pre><code class="language-java"> @Component
 public class OrderServiceImpl implements OrderService{

     private final DiscountPolicy discountPolicy;
     private final MemberRepository memberRepository;

     @Autowired
     public OrderServiceImpl(MemberRepository memberRepository,
                                                         DiscountPolicy rateDiscountPolicy) {
         this.discountPolicy = rateDiscountPolicy;
                 //기존에 discountPolicy라고 했을 땐 에러가 발생하던게 이름을 변경해주는 경우
                 //에러 없이 실행된다.
         this.memberRepository = memberRepository;
     }
 }</code></pre>
</li>
<li><p><code>@Qualifier</code>끼리 매칭해서 <strong>빈 이름을 매칭</strong>
단, <code>@Qualifier</code> 이용 시 문자열로 구분해서 찾기 때문에 타입 체크가 되지 않는다.</p>
<pre><code class="language-java"> @Component
 @Qualifier(&quot;mainDiscountPolicy&quot;)
 public class RateDiscountPolicy implements DiscountPolicy {
     @Override
     public int discount(Member member, int price) {
         return 0;
     }
 }
 ===========================================================================
 @Autowired //의존관계 생성자 주입
     public OrderServiceImpl(MemberRepository memberRepository,
                         @Qualifier(&quot;mainDiscountPolicy&quot;) DiscountPolicy discountPolicy) {
         this.discountPolicy = discountPolicy;
         this.memberRepository = memberRepository;
     }</code></pre>
</li>
<li><p><code>@Primary</code> 사용</p>
</li>
</ol>
<p><strong>우선 순위를 지정하는 방법이다.</strong> <code>@Autowired</code>를 사용 시 여러 빈이 매칭되면 <code>@Primary</code>를 가진 빈이 우선권을 갖게 된다. 보통 메인 DB에 <code>@Primary</code>를 지정해서 가져오고 보조 DB에 <code>@Qualifier</code>를 지정해서 필요한 순간에만 가져오는 방식으로 사용한다.</p>
<pre><code>```java
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {
    @Override
    public int discount(Member member, int price) {
        return 0;
    }
}
===========================================================================
@Autowired //의존관계 생성자 주입
    public OrderServiceImpl(MemberRepository memberRepository,
         DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
        this.memberRepository = memberRepository;
    }
```</code></pre><p>💡 <code>@Primary</code> 는 기본값처럼 동작하고 <code>@Qualifier</code> 는 매우 상세하게 동작하기 때문에 이런 경우  <code>@Qualifier</code>가 우선권을 가져간다. 스프링은 기본적으로 자동 보다 수동이, 넓은 범위 보다는 작은 범위의 선택이 우선권이 높게 설정되어있다.</p>
<h1 id="☝️자동-수동의-올바른-실무-운영-기준">☝️자동, 수동의 올바른 실무 운영 기준</h1>
<p>스프링이 나오고 시간이 갈 수록 자동을 선호하는 추세이고, 스프링 부트는 컴포넌트 스캔을 기본으로 사용하기 때문에 <strong>자동으로 등록하도록 설계하는 것을 기본으로 간다.</strong> 설정 정보를 기반으로 애플리케이션을 구성하는 부분과 실제 동작하는 부분을 명확하게 나누는 것이 가장 이상적이겠지만 <strong>개발하면서 수동으로 빈을 등록하고 주입할 대상을 일일이 지정해주는 것은 굉장히 번거로워지고 설정 정보가 커지면서 관리하는 것 자체에 큰 부담이 될 수 있다.</strong></p>
<p><span style="background-color:#fff5b1; color:black"><strong>무엇보다 자동 빈 등록을 이용해도 OCP, DIP를 위반하지 않고 지킬 수 있다.</strong></span></p>
<h2 id="업무-로직-빈-기술-지원-빈">업무 로직 빈, 기술 지원 빈</h2>
<p>애플리케이션 개발은 크게 업무 로직 빈과 기술 지원 빈으로 나눌 수 있다. <span style="background-color:#fff5b1; color:black"><strong>업무 로직 빈</strong></span>은 웹을 지원하는 컨트롤러, 핵심 비즈니스 로직이 있는 서비스, 데이터 계층 로직을 처리하는 리포지토리 등을 이야기 한다. 보통 비즈니스 요구사항을 개발할 때 추가되거나 변경된다.</p>
<p><span style="background-color:#fff5b1; color:black"><strong>기술 지원 빈</strong></span>은 기술적인 문제나 공통 관심사인 AOP를 처리할 때 주로 사용된다. 데이터베이스 연결이나 공통 로그 처리처럼 업무 로직을 지원하기 위한 하부 기술이나 공통 기술들이다.</p>
<p><span style="background-color:#fff5b1; color:black"><strong>업무 로직은 숫자도 너무 많고 어느 정도 유사한 패턴이 존재하기 때문에 자동 기능을 적극 활용한다. 기술 지원 로직은 업무 로직에 비해 수가 매우 적지만 애플리케이션 전반에 걸쳐서 광범위하게 영향을 미치기 때문에 수동 기능을 활용하는 것이 좋다.</strong></span> 업무 로직 같은 경우 에러가 많이 발생하더라도 에러 범위가 명확하게 좁혀지는 경우가 많지만 기술 지원 로직의 경우 적용 여부 조차 파악하기 어려운 경우가 많기 때문이다.</p>
<blockquote>
<p>강의 : 스프링 핵심 원리 - 김영한 님</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[@ComponentScan]]></title>
            <link>https://velog.io/@lee_bb/ComponentScan</link>
            <guid>https://velog.io/@lee_bb/ComponentScan</guid>
            <pubDate>Sat, 18 Nov 2023 12:46:04 GMT</pubDate>
            <description><![CDATA[<h1 id="컴포넌트-스캔과-의존관계-자동-주입">컴포넌트 스캔과 의존관계 자동 주입</h1>
<p>스프링 빈을 등록할 때 자바 코드에 <code>@Bean</code>이나 XML의 <code>&lt;bean&gt;</code> 등을 이용하여 설정 정보에 직접 등록할 빈을 나열할 수 있지만 이렇게 사용하게 되면 실무에서 등록해야 할 스프링 빈이 수십, 수백개가 되는 경우 설정 정보가 커지고 너무 번거롭다는 문제가 발생한다. 또한, 스프링 빈으로 등록해야 할 것을 누락하는 경우 찾기 힘들어지기도 한다.</p>
<p>그래서 <span style="background-color:#fff5b1; color:black"><strong>설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.</strong></span></p>
<h1 id="componentscan">@ComponentScan</h1>
<p>해당 애너테이션은 <code>@Component</code><strong>이 붙은 모든 클래스를 스프링 빈으로 등록한다.</strong> 이 때 스프링 빈의 기본 이름은 클래스 명을 사용하되 맨 앞글자만 소문자로 사용한다.</p>
<p>ex) MemberService(클래스 이름) → memberService(스프링 빈 이름)</p>
<h2 id="탐색-위치-지정">탐색 위치 지정</h2>
<p><code>basePackages</code> 속성을 지정해서 컴포넌트 스캔을 시작할 대상 위치를 지정할 수 있다. <span style="background-color:#fff5b1; color:black"><strong>지정하지 않으면 컴포넌트 스캔 애너테이션을 사용한 설정 정보 클래스 위치를 탐색 위치 대상으로 지정한다.</strong></span></p>
<p>ex) <code>@ComponentScan( basePackages=”hello.core.member”)</code> : hello.core.member 패키지 하위에서만 컴포넌트 스캔을 실행한다. 상위 패키지는 컴포넌트 스캔을 적용하지 않는다.</p>
<p>💡 권장 방법은 패키지 위치를 따로 지정하지 않고 설정 정보 클래스 위치를 프로젝트 최상단에 두는 것이다. 설정 정보 클래스는 프로젝트를 대표하는 정보이기 때문에 시작 루트 위치에 지정해두는 것이 좋고 그렇게 하면 하위 경로가 전부 컴포넌트 대상이 되기 때문에 <code>basePackages</code> 속성을 생략할 수 있다.
스프링 부트를 사용하면 스프링 부트의 대표 시작 정보인 <code>@SpringBootApplication</code>이 있는 <strong>시작 설정 정보를 시작 위치에 두는 것을 관례로 하고,</strong> <code>@SpringBootApplication</code> <strong>애너테이션에는 @ComponentScan이 포함되어있다.</strong></p>
<h2 id="기본-스캔-대상">기본 스캔 대상</h2>
<p><code>@Component</code> 애너테이션 외에도 다음과 같은 애너테이션들을 추가로 대상에 포함한다.</p>
<ul>
<li><code>@Component</code> : 컴포넌트 스캔</li>
<li><code>@Controller</code> : 스프링 MVC 컨트롤러에서 사용, 스프링 MVC 컨트롤러로 인식</li>
<li><code>@Service</code> : 스프링 비즈니스 로직에서 사용, 별다른 기능을 추가적으로 하지 않지만 개발자가 핵심 비즈니스 로직을 구분하는데 사용</li>
<li><code>@Repository</code> : 스프링 데이터 접근 계층에서 사용, 스프링 데이터 접근 계층으로 인식하면서 데이터 계층의 예외를 스프링 예외로 변환</li>
<li><code>@Configuration</code> : 스프링 설정 정보에서 사용, 스프링 설정 정보로 인식하고 싱글톤이 유지되도록 추가 처리</li>
</ul>
<p>위와 같은 애너테이션들을 대상에 포함하는 이유는 해당 애너테이션들에 전부 <code>@Component</code> 애너테이션이 포함되어 있기 때문이다.</p>
<p>🚨 <span style="background-color:#fff5b1; color:black"><strong>애노테이션에는 상속 관계가 존재하지 않는다.</strong></span> 때문에 애노테이션이 특정 애노테이션을 포함하고 있는 것을 인식하게 만들어주는 건 자바에서 지원하는 기능이 아닌 스프링에서 지원하는 기능이다.</p>
<h2 id="usedefaultfilters">useDefaultFilters</h2>
<p>기본적으로 켜져 있는 옵션이지만 해당 옵션을 끄면 기본 스캔 대상들이 제외된다.</p>
<h2 id="filtertype-옵션">FilterType 옵션</h2>
<p><em>ANNOTATION</em> : 기본값, 애노테이션을 인식해서 동작한다.</p>
<p><em>ASSIGNABLE_TYPE</em> : 지정한 타입과 자식 타입을 인식해서 동작한다.</p>
<p><em>ASPECTJ</em> : AspectJ 패턴을 사용해서 동작한다.</p>
<p><em>REGEX</em> : 정규 표현식을 사용해서 동작한다.</p>
<p><em>CUSTOM</em> : TypeFilter라는 인터페이스를 구현해서 처리한다.</p>
<h1 id="component">@Component</h1>
<p>스프링은 <code>@Component</code> 애너테이션을 사용한 클래스들을 전부 스프링 빈으로 등록해준다. <code>@Configuration</code> 애너테이션도 <code>@Component</code> 애너테이션을 포함하고 있기 때문에 컴포넌트 스캔의 대상이 된다.</p>
<p><code>@Component</code>를 사용해서 스프링 빈으로 등록할 때 이름을 따로 지정해서 사용할 수 있다. <code>@Component(”memberService2”)</code>처럼 사용하면 해당 클래스의 빈 이름은 memberService2가 된다.</p>
<h1 id="autowired">@Autowired</h1>
<p><span style="background-color:#fff5b1; color:black"><strong>생성자에 해당 애너테이션을 지정하면 스프링 컨테이너가 자동으로 해당하는 스프링 빈을 찾아서 주입한다.</strong></span> 자동 의존관계 주입이 되는 것이다. 이 때, 기본 조회 전략은 타입이 같은 빈을 찾아서 주입하게 된다. <code>getBean(MemberService.class)</code>와 동일하다고 이해하면 된다.</p>
<p>💡 생성자에 파라미터가 많다고 하더라도 상관없이 전부 찾아서 의존관계를 주입해준다.</p>
<p><strong>ClassPathBeanDefinitionScanner</strong></p>
<p>프로젝트를 실행시키면서 찍힌 로그를 보면 위와 같은 스캐너가 동작한 것을 볼 수 있는데 컴포넌트 스캐너가 잘 동작했음을 알 수 있다.</p>
<h1 id="중복-등록과-충돌">중복 등록과 충돌</h1>
<h2 id="자동-빈-등록-vs-자동-빈-등록">자동 빈 등록 vs 자동 빈 등록</h2>
<p>컴포넌트 스캔에 의해서 자동으로 스프링 빈이 등록되는데 이름이 같은 경우, <span style="color:indianred"><strong>ConflictingBeanDefinitionException</strong></span>이 발생한다.</p>
<h2 id="수동-빈-등록-vs-자동-빈-등록">수동 빈 등록 vs 자동 빈 등록</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>수동으로 등록하는 빈의 이름과 자동으로 등록하는 빈의 기본 설정되는 이름이 동일한 경우 수동으로 등록하는 빈이 우선권을 가지게 된다.</strong></span> MemberService라는 클래스를 자동으로 빈으로 등록하면서 memberService라는 이름을 갖게 되고 수동으로 memberService라는 이름을 가진 빈을 등록하면 예외 없이 정상적으로 실행하게 되면서 로그에 <code>Overriding bean definition for bean</code>라는 메시지가 포함된 로그가 찍힌다.</p>
<p>이렇게 수동이 우선권을 갖도록 개발자가 의도했다면 우선권이 존재하는 것이 장점일 수 있지만 개발자가 의도하지 않은 경우 이렇게 우선권이 설정되면 개발자가 의도하지 않았는데 설정이 꼬이게 될 수 있다. 하지만 예외가 발생하지 않기 때문에 에러를 찾기도 힘들어지는 경우가 발생한다.</p>
<p>그래서 <strong>최근 스프링 부트에서는 수동 빈 등록과 자동 빈 등록이 충돌나면 오류가 발생하도록 기본 값이 변경됐다.</strong></p>
<blockquote>
<p>강의 : 스프링 핵심 원리 - 김영한 님</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 컨테이너와 싱글톤]]></title>
            <link>https://velog.io/@lee_bb/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%8B%B1%EA%B8%80%ED%86%A4</link>
            <guid>https://velog.io/@lee_bb/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%8B%B1%EA%B8%80%ED%86%A4</guid>
            <pubDate>Sat, 11 Nov 2023 10:41:23 GMT</pubDate>
            <description><![CDATA[<h1 id="웹-애플리케이션과-싱글톤">웹 애플리케이션과 싱글톤</h1>
<p>스프링은 기업용 온라인 서비스 기술을 지원하기 위해 만들어졌기 때문에 대부분의 스프링 애플리케이션이 웹 애플리케이션이다. 웹 애플리케이션은 보통 여러 고객이 동시에 요청을 하게 된다. 때문에 동시에 여러 클라이언트가 동시에 요청을 하게 되면 DI 컨테이너에 3개의 요청이 들어오게 되는데 요청에 따라 새로운 객체를 생성해서 반환하게 되면 문제가 생긴다.</p>
<p>고객 트레픽이 초당 100이 나온다고 가정했을 때 초당 100개의 객체를 생성하고 소멸하게 된다면 메모리 낭비가 심해지기 때문이다.</p>
<p>때문에 <strong>해당 객체를 1개만 생성해서 객체를 공유하도록 설계</strong>를 해서 문제점을 해결하면 되는데 이렇게 <span style="background-color:#fff5b1; color:black"><strong>하나의 객체만 생성해서 공유하도록 하는 것을 싱글톤 패턴이라고 한다.</strong></span></p>
<h1 id="싱글톤-패턴">싱글톤 패턴</h1>
<p><strong>클래스의 인스턴스가 딱 1개만 만들어지는 것을 보장하는 디자인 패턴을 말한다.</strong> 인스턴스가 1개만 만들어지도록 하기 위해서 생성자의 접근 제한자를 <code>private</code>으로 지정하여 외부에서 임의로 <code>new</code> 생성자를 이용해서 만들지 못하도록 막을 수 있다.</p>
<pre><code class="language-java">public class SingletonService {

    private static final SingletonService instance=new SingletonService();
        //static 영역에 하나의 인스턴스만 만들 수 있게 한다.

    public static SingletonService getInstance() {
        //SingletonService의 인스턴스가 필요하면
        //getInstance() 메소드를 통해서만 조회할 수 있다.
        //-&gt;항상 같은 인스턴스를 반환
        return instance;
    }

    private SingletonService() {
        //private 생성자를 만들어서 외부에서 SingletonService를 생성할 수 없게 한다.
    }
}</code></pre>
<p>위와 같은 객체를 미리 생성해서 더 이상 생성할 수 없게 하는 단순하고 안전한 방법 외에도 객체가 없다면 생성하고 있으면 생성된 객체를 사용하는 등 다양한 방법이 존재한다.</p>
<h1 id="🤔-싱글톤-패턴의-문제점">🤔 싱글톤 패턴의 문제점</h1>
<p>위 코드처럼 구현한다고 하면 객체 하나만 생성해서 쓰려고 해도 구현하는 코드 자체가 많이 들어가게 된다. 또한 의존관계상 클라이언트가 구체 클래스에 의존하게 되기 때문에 <strong>DIP를 위반</strong>하게 되며 클라이언트가 구체 클래스에 의존하게 되면서 <strong>OCP 원칙을 위반할 가능성이 높아진다.</strong></p>
<p>내부 속성을 변경하거나 초기화하기 어렵기도 어렵고 <code>private</code> 생성자로 구현하면서 자식 클래스를 만들기 어려워진다는 단점이 존재한다.</p>
<p><span style="background-color:#fff5b1; color:black"><strong>결과적으로 유연성이 굉장히 떨어진다는 문제점이 크다.</strong></span></p>
<h1 id="싱글톤-컨테이너">싱글톤 컨테이너</h1>
<p>스프링 컨테이너는 이런 싱글톤의 문제점을 해결하면서 객체 인스턱스를 1개로(싱글톤 패턴) 관리한다. <span style="background-color:#fff5b1; color:black"><strong>스프링 컨테이너에 이미 빈을 등록해놓고 해당하는 빈을 호출해주기 때문이다.</strong></span> 이렇게 스프링 컨테이너는 싱글톤 컨테니어 역할을 수행한다. 그리고 이렇게 싱글톤 객체를 생성, 관리하는 기능을 싱글톤 레지스트리라 한다.</p>
<p>💡 스프링 컨테이너가 기본 빈 등록 방식을 싱글톤 패턴으로 지원하지만 요청 할 때마다 새로운 객체를 생성, 반환하는 기능도 제공한다.</p>
<h2 id="🤔-어떻게-싱글톤-패턴의-문제점을-해결하는지">🤔 어떻게 싱글톤 패턴의 문제점을 해결하는지?</h2>
<p>싱글톤 패턴을 사용하기 위해 만들었던 지저분한 코드들이 들어가지 않아도 되며, 스프링 컨테이너가 관리하기 때문에 DIP, OCP를 위반하지 않고 테스트가 힘들어지지도 않는다. <code>private</code> 생성자로부터 자유로워지기 때문에 유연성이 떨어진다는 문제점으로부터 자유로워진다.</p>
<h1 id="⚠️-싱글톤-패턴의-주의점">⚠️ 싱글톤 패턴의 주의점</h1>
<p>싱글톤 패턴이나 스프링 같은 싱글톤 컨테이너를 사용할 때는 객체의 인스턴스를 하나만 생성해서 공유하기 때문에 <span style="background-color:#fff5b1; color:black"><strong>싱글톤 객체가 상태를 유지(stateful)하게 설계해서는 안 되고 무상태(stateless)로 설계해야 한다.</strong></span></p>
<p>무상태로 설계한다는 것은 특정 클라이언트에 의존적인 필드가 없어야 하고, 값을 변경할 수 있는 필드가 있으면 안 된다는 것이다. 가급적 읽기만 허용하도록 하되, 필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.</p>
<p>스프링 빈의 필드에 공유 값을 설정하면 큰 장애가 발생할 수 있기 때문이다.</p>
<h1 id="configuration과-싱글톤">@Configuration과 싱글톤</h1>
<p>스프링 컨테이너는 싱글톤 레지스트리로 스프링 빈이 싱글톤이 되도록 보장시킨다. 하지만 Configuration 클래스에 정의했던 자바 코드대로라면 여러 번 생성이 되었어야 한다. 이런 객체들을 싱글톤 패턴을 유지할 수 있게 만드는 것은 <code>@Configuration</code> 애너테이션이다.</p>
<p><code>AnnotationConfigApplicationContext</code>에 파라미터로 넘긴 값은 스프링 빈으로 등록되는데 이 때 <code>@Configuration</code>으로 지정한 Configuration 클래스도 빈으로 등록이 된다. 그래서 빈으로 등록한 설정 클래스를 조회해서 정보를 출력해보면 일반적인 <code>hello.test.AppConfig…</code>처럼 출력되지 않고 <code>hello.test.AppConfig$$EnhancerBySpringCGLIB$$…</code>처럼 출력 되는 것을 볼 수 있다.</p>
<p>스프링이 바이트를 조작하는 CGLIB라는 라이브러리를 사용해서 AppConfig를 상속 받은 임의의 클래스를 만들고 그 클래스를 스프링 빈으로 등록한다. 이 임의의 클래스가 싱글톤이 보장되도록 해준다.</p>
<p>실제 내부 라이브러리 기술은 복잡하기 때문에 <code>@Configuration</code> 애너테이션을 사용함으로써 싱글톤 패턴을 보장할 수 있다고 인지하면 된다.</p>
<blockquote>
<p>강의 : 스프링 핵심 원리 - 기본편 (김영한 님)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpringBoot]IT학원 그룹웨어 기능 구현 - 인사관리]]></title>
            <link>https://velog.io/@lee_bb/SpringBootIT%ED%95%99%EC%9B%90-%EA%B7%B8%EB%A3%B9%EC%9B%A8%EC%96%B4-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84-%EC%9D%B8%EC%82%AC%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@lee_bb/SpringBootIT%ED%95%99%EC%9B%90-%EA%B7%B8%EB%A3%B9%EC%9B%A8%EC%96%B4-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84-%EC%9D%B8%EC%82%AC%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Wed, 06 Sep 2023 13:05:52 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-소개">프로젝트 소개</h1>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/39afbc38-f7ba-4ba7-8969-df207044d327/image.png" alt=""></p>
<p>IT 학원에서 사용하는 그룹웨어를 제공한다.</p>
<p>일반적인 그룹웨어와 동일하게 사원 생성, 로그인, 인사 관리, 근태 관리, 전자결재, 채팅 기능을 제공하고 별도로 강의 등록, 회의실 및 학생 상담실 예약 등의 기능을 제공한다.</p>
<blockquote>
<p>자세한 소스 코드는 GitHub에서 확인 가능합니다!</p>
<p><a href="https://github.com/leebib1/FilnalProject_workit">https://github.com/leebib1/FilnalProject_workit</a></p>
</blockquote>
<h1 id="담당-기능---인사관리">담당 기능 - 인사관리</h1>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/9a64a3c1-481d-433f-893e-490f7f43f5a9/image.png" alt=""></p>
<p>사원 생성 기능은 이전 포스팅에서 설명하고 넘어갔으므로 넘기고 나머지 기능들에 대해서 작성해보도록 한다!</p>
<h2 id="🔍사원-조회">🔍사원 조회</h2>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/c4d2cec0-6244-4e67-9561-8aadce031d4f/image.png" alt=""></p>
<p>인사팀 혹은 부대표 이상의 직급을 가진 상태면 확인할 수 있는 사원 조회 페이지를 구현했다. 위 화면에서 사원 중 하나를 클릭하면 해당 사원의 정보를 확인 및 수정 가능한 상세 정보 화면으로 이동한다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/b9953b77-3ab4-40e0-b74c-beac15077cbf/image.png" alt=""></p>
<p>개인 정보 수정 화면이랑 다른 점은 인사 이동을 시킬 수 있도록 부서 및 직책에 대한 부분도 수정 가능하다는 점이다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/daa9c5c3-abbc-4fe7-99a4-e2554946f00e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/62f33dcc-8f39-442e-aca7-553a1887990a/image.png" alt=""></p>
<p>재직 상태를 구분해서 조회할 수 있고, 원하는 키워드를 선택해서 조회할 수도 있도록 해놨다.</p>
<h3 id="검색-결과-조회-후-페이지-이동-시-정보를-유지-시키기">검색 결과 조회 후 페이지 이동 시 정보를 유지 시키기</h3>
<p>페이지바를 쓰는 페이지가 많다 보니 공용으로 페이지 바를 만들어서 넘겨주는 클래스를 하나 생성해서 사용하고 있었는데 이걸 사용하니 검색 결과를 유지 시키면서 페이지를 이동시킬 수 없었다.</p>
<p>그래서 기본적으로 페이지바는 공용 클래스를 사용해서 넘겨주되 <code>Pagenation 클래스</code>에서 만들어서 넘겨 보냈던 스크립트를 <code>JSP 파일</code>에서 덮어쓰기 해서 이용했다.</p>
<pre><code class="language-jsx">//페이징 함수 덮어쓰기
function fn_paging(no){
    console.log(&quot;paging&quot;);
    location.assign(&#39;${path}/employee/list?cPage=&#39;+no+&#39;&amp;entFl=&#39;+&#39;${entFl==null?&quot;&quot;:entFl }&#39;
            +&quot;&amp;category=&quot;+&#39;${category==null?&quot;&quot;:category }&#39;
            +&quot;&amp;keyword=&quot;+&#39;${keyword==null?&quot;&quot;:keyword}&#39;);
}</code></pre>
<h2 id="👔부서-관리">👔부서 관리</h2>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/84ae7400-0d82-4935-bd05-fc7bb27ea7f0/image.png" alt=""></p>
<p>인사팀 팀장 이상 혹은 부대표 이상의 직급만 접근 가능한 부서 관리 기능을 구현했다. 새로운 부서를 추가, 부서 삭제, 부서 이름 수정 등을 가능하도록 했다.</p>
<p>부서가 없는 사원을 둘 수 없어서 <strong>해당 부서 별 총 인원을 구해서 사용자가 확인할 수 있도록 하고 해당 부서에 인원이 있다면 삭제할 수 없도록 했다.</strong></p>
<p>한 화면에서 부서 관리를 관리하고 있기 때문에 <code>AJAX</code>를 이용해서 요청을 보내고 응답받는 형식으로 구현했다. 다만… 성공적으로 응답이 요청되면 새로고침이 한 번 되도록 했다. 리스트를 5개까지 밖에 안 만들어 주는데다가 부서명을 기준으로 오름차순으로 가져오고 있어서 그걸 생각해서 화면에 추가를 해줘야하나 고민하다가 <code>location.reload()</code>를 실행시키는 걸로 합의보았다…</p>
<h2 id="👔직책-관리">👔직책 관리</h2>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/b00d7835-697f-40bf-b539-ef615ac31b37/image.png" alt=""></p>
<p>부대표 이상의 직급만 접근 가능한 직책관리 기능을 구현했다. 부서관리와 전체적인 기능은 비슷하지만 각 직책 이름에 맞는 권한을 추가적으로 지정해주어야 한다.</p>
<p>스프링 시큐리티를 이용하고 있었기 때문에 직책명을 가지고 권한을 부여하면 새로 추가된 직급들은 개발자가 코드를 추가하지 않는 이상 권한이 없는 상태가 된다는 게 문제점이었다. 그래서 <strong>직급 테이블에 권한에 대한 컬럼을 추가하고 해당 컬럼을 기준으로 권한을 부여해두었다.</strong></p>
<p>그래서 강사, 사원, 매니저 등의 다른 이름의 직책이 모두 일반 사원이라는 권한을 가지고 있을 수 있도록 추가, 변경하려는 직책에 따라 그에 맞는 권한을 설정해주면 <strong>이름이 바뀌고 새로 생성되더라도 코드의 변경 없이 동일한 권한을 부여할 수 있게 된다.</strong></p>
<h2 id="👌정보-수정-요청-승인">👌정보 수정 요청 승인</h2>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/b58f0ba6-0ca6-4ad4-8451-4326435175f4/image.png" alt=""></p>
<p>마이페이지에서 개인 정보 수정 요청을 보내면 인사팀 혹은 부대표 이상의 권한을 가진 사용자가 확인하고 승인, 삭제할 수 있는 수정 신청 목록 페이지를 구현했다.</p>
<p>수정 신청 목록을 클릭하면 <code>AJAX</code>로 처리해서 새로고침 없이 바로 신청 내역에 해당하는 정보가 표시된다.</p>
<p>캡쳐에서는 이미 수정 완료된 상태에서 불러왔기 때문에 수정 신청 내역과 현재 사원 정보가 동일하게 뜬다. (+신청일은 날짜가 출력되는데 출력 형식을 바꿔주려다가 아직 수정을 못했다…)</p>
<h3 id="후기">후기!</h3>
<p>프로젝트 진행 중에는 학원에서 살짝 맛만 봤던 Security를 직접 프로젝트에 입맛대로 적용하려고 하니까 힘들기도 하고... 그래도 지난 번 프로젝트에 비해서 많이 성장했다는 게 느껴져서 뿌듯하기도 했다!
2주 정도 지나서 보니까 부족했던 부분이 눈에 확 들어오기도 해서 슬슬 업그레이드를 준비해야할 거 같다...ㅎㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpringBoot]IT학원 그룹웨어 - 로그인 및 마이페이지 기능 구현]]></title>
            <link>https://velog.io/@lee_bb/SpringBootIT%ED%95%99%EC%9B%90-%EA%B7%B8%EB%A3%B9%EC%9B%A8%EC%96%B4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%B0%8F-%EB%A7%88%EC%9D%B4%ED%8E%98%EC%9D%B4%EC%A7%80-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@lee_bb/SpringBootIT%ED%95%99%EC%9B%90-%EA%B7%B8%EB%A3%B9%EC%9B%A8%EC%96%B4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%B0%8F-%EB%A7%88%EC%9D%B4%ED%8E%98%EC%9D%B4%EC%A7%80-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Sat, 02 Sep 2023 07:30:53 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-소개">프로젝트 소개</h1>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/ea302d55-6568-4b93-9f9e-9cc21aaa16b3/image.png" alt=""></p>
<p>IT 학원에서 사용하는 그룹웨어.</p>
<p>일반적인 그룹웨어와 동일하게 사원 생성, 로그인, 인사 관리, 근태 관리, 전자결재, 채팅 기능을 제공하고 별도로 강의 등록, 회의실 및 학생 상담실 예약 등의 기능을 제공한다.</p>
<blockquote>
<p>자세한 소스 코드는 GitHub에서 확인 가능합니다!</p>
<p><a href="https://github.com/leebib1/FilnalProject_workit">https://github.com/leebib1/FilnalProject_workit</a></p>
</blockquote>
<h1 id="담당-기능---로그인">담당 기능 - 로그인</h1>
<h2 id="👥사원-생성">👥사원 생성</h2>
<p>이전 회사 생활을 하면서 사용했던 그룹웨어를 떠올렸을 때 일반적인 웹사이트와 다른 점이 있다면 <strong>계정을 사용자가 각자 생성해서 사용하지 않는다</strong>는 점이었다. 일반적인 웹사이트의 경우 사용자가 인증 과정을 거치면서 계정을 생성하고 아이디 중복 체크를 하지만 그룹웨어의 경우 <strong>인사 담당자가 계정을 만들면 입사날짜+임의 번호가 합쳐져서 “사번”이 부여</strong>된다.</p>
<p>“사번”과 “초기 비밀번호”는 인사 담당자(클라이언트)가 입력하는 부분은 아니고, 기본적인 개인정보(이름, 연락처, 부서, 직책)만 입력한다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/383582ac-d437-4390-bfa0-951c9430715a/image.png" alt=""></p>
<pre><code class="language-xml">&lt;insert id=&quot;insertEmployee&quot; parameterType=&quot;map&quot;&gt;
    INSERT INTO MEMBER_TB
    VALUES((TO_CHAR(TO_DATE(#{enrollDate},&#39;YYYY-MM-DD&#39;),&#39;YYMMDD&#39;)||SEQ_MEMBER_ID.NEXTVAL)
        ,#{memberName},DEFAULT,#{password},#{memberNo},#{phone},#{mainAddress},NULL
        ,#{enrollDate},NULL,#{salary},DEFAULT,#{deptCode},#{jobCode},#{detailAddress})
&lt;/insert&gt;</code></pre>
<p><code>(TO_CHAR(TO_DATE(#{enrollDate},&#39;YYYY-MM-DD&#39;),&#39;YYMMDD&#39;)||SEQ_MEMBER_ID.NEXTVAL)</code>로 입사년월일+시퀀스번호를 부여한다. 시퀀스는 1~99사이로 순회하도록 생성했다.</p>
<h2 id="💻로그인">💻로그인</h2>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/6d9cd681-4204-4d38-a420-49d5cb05b6ce/image.png" alt=""></p>
<h3 id="🍪아이디-저장">🍪아이디 저장</h3>
<pre><code class="language-jsx">      //저장된 아이디
        var id=getCookie(&quot;rememberId&quot;);
        $(&quot;#id&quot;).val(id);
        if($(&quot;#id&quot;).val()!=&quot;&quot;){
            $(&quot;#remember-id&quot;).attr(&quot;checked&quot;,true);
        }

        //아이디 저장 체크박스
        $(&quot;#remember-id&quot;).change(function(){
            if($(&quot;#remember-id&quot;).is(&quot;:checked&quot;)){
                setCookie(&quot;rememberId&quot;,$(&quot;#id&quot;).val(),7);
            }else{
                deleteCookie(&quot;rememberId&quot;);
            }
        });

        //아이디 저장이 선택된 상태로 아이디 입력시
        $(&quot;#id&quot;).keyup(function(){
            if($(&quot;#remember-id&quot;).is(&quot;:checked&quot;)){
                setCookie(&quot;rememberId&quot;,$(&quot;#id&quot;).val());
            }
        });</code></pre>
<p>ready 함수 내부에 작성된 자바스크립트 코드는 위와 같다. 쿠키에 저장된 아이디가 있는 경우 바로 불러와서 <code>input</code> 태그에 값을 넣어주고 체크박스를 checked로 만들어줘야 하기 때문에 화면이 불러와졌을 때 한 번 실행되야 한다.</p>
<p>위 코드에 작성된 <code>getCookie()</code>, <code>setCookie()</code>, <code>deleteCookie()</code> 함수는 따로 선언한 함수다.</p>
<h3 id="🔐시큐리티를-이용한-로그인-처리">🔐시큐리티를 이용한 로그인 처리</h3>
<p>보통 JPA를 같이 사용하는 것 같지만... 이번 프로젝트에서 JPA를 사용하지 않았기 때문에 일반 객체(MemberVO 클래스)에 <strong>UserDetails를 상속 받아서 인증 객체로 이용했다.</strong></p>
<pre><code class="language-java">@Override //권한 판단
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        List&lt;GrantedAuthority&gt; auth=new ArrayList&lt;&gt;();
        if(dept.getDeptCode().equals(&quot;D3&quot;)) auth.add(new SimpleGrantedAuthority(MemberAuthority.DEPT_EMP.name()));
        if(job.getJobAuth().equals(&quot;MASTER&quot;)) auth.add(new SimpleGrantedAuthority(MemberAuthority.MASTER.name()));
        if(job.getJobAuth().equals(&quot;SUBMASTER&quot;)||job.getJobAuth().equals(&quot;MASTER&quot;)) auth.add(new SimpleGrantedAuthority(MemberAuthority.SUBMASTER.name()));
        if(job.getJobAuth().equals(&quot;TEAMMASTER&quot;)||job.getJobAuth().equals(&quot;MASTER&quot;)
                ||job.getJobAuth().equals(&quot;TEAMMASTER&quot;)) auth.add(new SimpleGrantedAuthority(MemberAuthority.TEAMMASTER.name()));
        if(job.getJobAuth().equals(&quot;EMP&quot;)||job.getJobAuth().equals(&quot;MASTER&quot;)
                ||job.getJobAuth().equals(&quot;TEAMMASTER&quot;)||job.getJobAuth().equals(&quot;EMP&quot;)) {
            auth.add(new SimpleGrantedAuthority(MemberAuthority.EMP.name()));
        }
        return auth;
    }

@Override  //퇴사 기간이 지났거나 입사 전이면 권한 없음
    public boolean isCredentialsNonExpired() {
        Date today=new Date();
        return entDate==null||(today.before(entDate)&amp;&amp;today.after(hireDate));
    }</code></pre>
<p>위 코드는 MemberVO 클래스의 일부이다. UserDetails를 상속 받으면서 <code>getAuthorities()</code> 메소드 내부에 인증된 객체가 가지고 있을 권한들을 부여해주었다.</p>
<p>직책, 부서 별로 접근할 수 있는 페이지가 달라지기 때문에 권한에 따라 작성해주되, 대표가 그 아래 부대표, 팀장, 사원 등의 권한에도 사용할 수 있도록 <strong>가장 큰 권한부터 위에서 작성하여 하위 권한들을 상위 권한이 가질 수 있도록 코드를 작성했다.</strong></p>
<p><strong>추가로 그룹웨어이기 때문에 퇴사한 계정이나 입사일이 지정되어 있어 미리 계정은 만들어졌지만 입사 전인 계정은 권한을 받지 못하도록 추가적인 설정도 해주었다.</strong></p>
<blockquote>
<p>인증 객체에 대한 다른 설정은 이전 포스팅을 참고</p>
<p><a href="https://velog.io/@lee_bb/Spring-Security-2%EC%9D%B8%EC%A6%9D-%EA%B0%9D%EC%B2%B4-config-%EC%84%A4%EC%A0%95">Spring Security -2(인증 객체, config 설정)</a></p>
</blockquote>
<pre><code class="language-java">@Configuration
@EnableWebSecurity
public class SecurityConfig{
    @Autowired
    private DBConnectProvider provider;

    @Bean
    public SecurityFilterChain authenticationPath(HttpSecurity http) throws Exception{
        return http.csrf().disable()
                .formLogin()
                    .successForwardUrl(&quot;/login/success&quot;)
                    .failureForwardUrl(&quot;/login/fail&quot;)
                    .passwordParameter(&quot;password&quot;)
                    .usernameParameter(&quot;memberId&quot;)
                    .loginProcessingUrl(&quot;/login&quot;)
                    .loginPage(&quot;/loginpage&quot;)
                .and()
                .authorizeHttpRequests()
                    .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                    .antMatchers(&quot;/error/**&quot;).permitAll()
                    .antMatchers(&quot;/loginpage&quot;).permitAll()
                    .antMatchers(&quot;/logout&quot;).permitAll()
                    .antMatchers(&quot;/resources/**&quot;).permitAll()
                    .antMatchers(&quot;/login/**&quot;).permitAll()
                    .antMatchers(&quot;/email/**&quot;).permitAll()
                    .antMatchers(&quot;/employee/job&quot;).hasAnyAuthority(MemberAuthority.SUBMASTER.name(), MemberAuthority.MASTER.name())
                    .antMatchers(&quot;/employee/enroll&quot;).hasAnyAuthority(MemberAuthority.TEAMMASTER.name(),
                            MemberAuthority.SUBMASTER.name(), MemberAuthority.MASTER.name())
                    .antMatchers(&quot;/employee/**&quot;).hasAnyAuthority(MemberAuthority.DEPT_EMP.name(), MemberAuthority.SUBMASTER.name(),
                            MemberAuthority.MASTER.name())
                    .antMatchers(&quot;/**&quot;).hasAuthority(MemberAuthority.EMP.name())
                .and()
                .logout()
                    .logoutSuccessUrl(&quot;/loginpage&quot;)
                    .logoutUrl(&quot;/logout&quot;)
                    .invalidateHttpSession(true)
                .and()
                .sessionManagement()
                    .invalidSessionUrl(&quot;/loginpage&quot;)
                .and()
                .exceptionHandling()
                    .authenticationEntryPoint(new AuthenticationEntryPoint() {
                        @Override
                        public void commence(HttpServletRequest request, HttpServletResponse response,
                                AuthenticationException authException) throws IOException, ServletException {
                            response.sendRedirect(request.getContextPath()+&quot;/error/login&quot;);
                        }
                    }).accessDeniedHandler(new AccessDeniedHandler() {
                        @Override
                        public void handle(HttpServletRequest request, HttpServletResponse response,
                                AccessDeniedException accessDeniedException) throws IOException, ServletException {
                            response.sendRedirect(request.getContextPath()+&quot;/error/auth&quot;);
                        }
                    })
                .and()
                .headers()
                .frameOptions().sameOrigin()
                .and()
                .authenticationProvider(provider)
                .build();
    }
}</code></pre>
<p>위 코드는 Security Configuration인데 마찬가지로 기본적인 설정 방법은 위 포스팅 링크를 참고하면 된다. 권한에 따라 접근할 부분을 나눠서 작성하고 <strong>가장 넓은(권한이 작은) 범위의 요청 주소를 마지막에 적어주면 된다.</strong> 작성한 순서대로 막아주기 때문!</p>
<p>그리고 <code>exceptionHandling()</code>을 이용해서 <strong>권한 예외가 발생하는 포인트나 인증 예외가 발생하는 포인트에 대해서 처리를 해줬다.</strong> 로그인이 안 돼서 접근에 예외가 발생하는 경우나 권한이 없는데 요청을 보내려고 하면 그냥 에러 페이지가 뜨기 때문에 내가 만들어둔 요청 주소로 응답을 보내버렸다.</p>
<p><code>/error/login</code>으로 요청이 가면 “로그인 후 사용 가능합니다.” 라는 알림창을 띄워주고 로그인 페이지로 이동 시키고, <code>/error/auth</code>로 요청이 가면 “권한이 없습니다.”라는 알림창을 띄워주고 메인 페이지로 이동시켜준다.</p>
<h2 id="📧이메일-인증">📧이메일 인증</h2>
<h3 id="☝️첫-로그인-시-이메일-인증-및-비밀번호-변경">☝️첫 로그인 시 이메일 인증 및 비밀번호 변경</h3>
<p>초기 비밀번호는 단순하게 설정되어있고 사원 생성 시 이메일을 입력받지 않았기 때문에 이메일 인증 기능과 비밀번호 변경을 함께 구현했다.</p>
<p>아래 화면은 이메일 인증이 완료되지 않은 사원이 로그인 시 메인 페이지가 아닌 인증 페이지로 전환시킨 화면이다. url을 수정해서 인증 처리를 건너뛰고 메인 페이지로 이동할 수 있는 것은 따로 막아두지 않았지만 재로그인 하면 결국 또 같은 화면으로 넘어가게 된다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/87449250-f61c-4555-9ad7-78db0db6e863/image.png" alt=""></p>
<pre><code class="language-java">//email 인증
    @PostMapping(&quot;/email&quot;)
    @ResponseBody
    public String sendEmail(@RequestParam(value=&quot;email&quot;) String email) {
        String key=&quot;&quot;;
        Random random=new Random();
        SimpleMailMessage message=new SimpleMailMessage();
        message.setTo(email);
        for(int i=0;i&lt;4;i++) {
            int alpa=random.nextInt(25)+65; //A~Z 랜덤 알파벳 생성
            key+=(char)alpa;
        }
        int number=random.nextInt(9999)+1000; //4자리 랜덤 숫자
        key+=number;
        message.setSubject(&quot;workit 이메일 인증 코드입니다.&quot;); //메일 제목
        message.setText(&quot;인증 번호 : &quot;+key);
        javaMailSender.send(message);
        return key;
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/8d392436-8df4-4d95-8aeb-128caae10e72/image.png" alt=""></p>
<p>이메일을 정규식 표현에 맞춰서 올바르게 작성 후 인증 버튼을 누르면 위 코드로 요청이 넘어가게 된다. <strong>javaMail API를 사용</strong>했는데 기본적인 설정 방법과 구글 이메일 2차 인증 방법 등은 아래 티스토리 링크를 참고했다.</p>
<blockquote>
<p><a href="https://jforj.tistory.com/307">[SpringBoot] 이메일 발신하기</a></p>
</blockquote>
<h3 id="🔐비밀번호-재발급">🔐비밀번호 재발급</h3>
<p>로그인 화면에서 <em>forgot your password?</em> 를 클릭 시 비밀번호 재발급 화면으로 이동할 수 있게 된다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/4f0b77e7-c9f5-44d3-a83b-4ab2deef39b9/image.png" alt=""></p>
<p>스프링 시큐리티를 이용하고 있기 때문에 <strong>비밀번호 역시 단방향 암호화해서 관리하고 있어서 비밀번호를 잃어버렸다고 해도 기존 비밀번호를 찾을 수 없다.</strong></p>
<p>때문에 영문자+숫자가 랜덤으로 조합된 임시 비밀번호를 이메일로 발급해주면서 해당 사용자의 비밀번호를 임시 비밀번호로 변경해주도록 만들었다.</p>
<pre><code class="language-java">//Cotroller
//비밀번호 재발급
    @PutMapping(&quot;/email/password&quot;)
    @ResponseBody()
    public int passwordReissue(@RequestBody Map&lt;String,Object&gt; param) {
        String key=&quot;&quot;;
        Random random=new Random();
        SimpleMailMessage message=new SimpleMailMessage();
        message.setTo((String)param.get(&quot;email&quot;));
        for(int i=0;i&lt;2;i++) {
            key+=(char)((int)random.nextInt(25)+65); //A~Z 랜덤 알파벳 생성
            key+=(int)random.nextInt(); //랜덤 숫자
            key+=(char)((int)random.nextInt(25)+ 97); //a~z 랜덤 알파벳 생성
        }
        message.setSubject(&quot;workit 임시 비밀번호입니다.&quot;); //메일 제목
        message.setText(&quot;임시 비밀번호 : &quot;+key);
        param.put(&quot;newPwd&quot;, key);
        if(service.updateMember(param)&gt;0) { //임시 비밀번호로 변경이 완료 되면 이메일 발송
            javaMailSender.send(message);
            return 1;
        }
        return 0;
    }
--------------------------------------------------------------------------
//Service
@Override
    public int updateMember(Map&lt;String, Object&gt; param) {
        if(param.get(&quot;password&quot;)==null) { //임시 비밀번호 발급 시
            param.put(&quot;password&quot;, dao.selectMemberByParam(param).getPassword()); //기존 비밀번호        
        }else {//비밀번호 변경 시
            param.put(&quot;password&quot;, encoder.encode((String)param.get(&quot;password&quot;))); //입력한 번호 암호화            
        }
        param.put(&quot;newPwd&quot;, encoder.encode((String)param.get(&quot;newPwd&quot;))); //신규 비밀번호 암호화
        if(loadUserByUsername((String)param.get(&quot;memberId&quot;))!=null) { //비밀 번호 변경 시 현재 비밀번호 일치하는 데이터가 있는지 확인
            return dao.updateMember(param);            
        }else {
            return -1; //없을 때 -1 반환
        }
    }</code></pre>
<p>Service에서 <code>updateMember()</code> 메소드를 사원이 마이페이지에서 비밀번호를 변경하는 경우, 비밀번호 재발급하는 경우. 이 두 가지 경우에서 같이 사용 중이기 때문에 임시 번호 발급 시와 아닌 경우를 나눠서 넘길 값을 지정해줬다.</p>
<p>return -1을 하는 경우는 비밀번호 변경 시 현재 비밀번호와 일치하지 않으면 값을 사용자에게 현재 비밀번호가 일치하지 않다고 출력해주기 위해 구분 값으로 -1을 넘겼다.</p>
<h2 id="😊마이페이지">😊마이페이지</h2>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/67f72fcd-d18a-44e8-aa16-992ff5b070ad/image.png" alt=""></p>
<h3 id="💾프로필-이미지-업로드">💾프로필 이미지 업로드</h3>
<p>프로필 이미지를 누르면 파일 선택이 가능하도록 하고 수정 버튼을 누르면 바로 파일 업로드가 되면서 데이터가 수정되도록 했다.
만약 파일이 선택되지 않은 상태로 수정 버튼 클릭 시 파일을 선택하라는 알림창이 한 번 뜬 후, 알아서 파일 선택창을 띄워준다.</p>
<h3 id="🔐개인-정보-수정">🔐개인 정보 수정</h3>
<p>비밀번호나 프로필 이미지는 사원이 자유롭게 수정할 수 있지만 그 외의 개인 정보는 인사팀에 수정 요청을 보내고 요청이 승인되면 변경되었으면 좋겠다는 의견이 있어서 그렇게 구현하기로 했던 기능이다.</p>
<p>변경 전 데이터를 <code>input</code> 값에 넣어준 상태로 화면을 불러오고 사원이 데이터를 전부 입력하면 사원 정보 요청 테이블에 데이터를 따로 저장해둔다. 그리고 인사팀에서 승인을 하면 저장된 데이터를 불러와서 사원 테이블의 해당 데이터를 수정하도록 했다.</p>
<p>수정 대기 현황이 존재하면 더 이상 추가 요청을 보낼 수 없도록 막아뒀다.</p>
<p>생각보다 글이 길어져서 인사관리 부분은 따로 나눠서 포스팅하겠습니다!ㅎㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래밍 패러다임]]></title>
            <link>https://velog.io/@lee_bb/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8C%A8%EB%9F%AC%EB%8B%A4%EC%9E%84</link>
            <guid>https://velog.io/@lee_bb/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8C%A8%EB%9F%AC%EB%8B%A4%EC%9E%84</guid>
            <pubDate>Sat, 26 Aug 2023 06:46:52 GMT</pubDate>
            <description><![CDATA[<h1 id="프로그래밍-패러다임">프로그래밍 패러다임</h1>
<p><span style="background-color:#fff5b1; color:black"><strong>프로그래밍의 관점을 갖게 해주는 역할을 하는 개발 방법론을 프로그래밍 패러다임이라고 한다.</strong></span> Java 같은 객체지향 프로그래밍은 개발자가 프로그램을 상호 작용하는 객체들의 집합으로 볼 수 있게 하는 것을 말한다.</p>
<p>언어에 따라 특정 패러다임을 지원하기도 한다. 예를 들어 jdk 1.8 이전의 자바는 객체지향 프로그래밍을 지원하고 jdk 1.8 이후로는 함수형 프로그래밍을 지원하면서 람다식 등을 지원하고 선언형 프로그래밍을 위해 stream같은 표준 API를 제공하기도 한다. 하스켈은 함수형 프로그램을 지원하며 C++, 파이썬, 자바스크립트처럼 여러 패러다임을 지원하는 언어들도 있다.</p>
<h2 id="선언형과-함수형-프로그래밍">선언형과 함수형 프로그래밍</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>선언형 프로그래밍은 무엇을 풀어내는가에 집중한다.</strong></span> 함수형 프로그래밍은 선언형 패러다임의 일종이다. 함수형 프로그래밍은 순수 함수들을 블록처럼 쌓아 로직을 구현하고 고차 함수를 통해서 재사용성을 높인 프로그래밍 패러다임이다.</p>
<p>때문에 자바스크립트처럼 단순하고 유연한 함수에서는 함수를 일급 객체로 보기 때문에 객체지향 프로그래밍보다 함수형 프로그래밍 방식을 선호하게 된다.</p>
<p>💡 <span style="color:orange"><strong>순수 함수?</strong></span>
아래 코드처럼 출력이 입력에만 의존하는 것을 말한다. a 나 b 외에 다른 전역 변수 c같은 값이 출력에 영향을 주지 않아야 한다.</p>
<pre><code class="language-jsx">const test=(a, b)=&gt;{
    return a+b;
}</code></pre>
<p>💡 <span style="color:orange"><strong>고차 함수?</strong></span>
함수는 변수에 저장해서 사용할 수 있고 함수는 함수를 저장한 변수를 인자로 전달 받을 수 있다. 그리고 함수는 이 변수를 리턴할 수도 있다. 이렇게 함수의 형태로 리턴할 수 있는 함수를 고차 함수라고 부른다.</p>
<pre><code class="language-jsx">function test(num){
    return num*2;
}

function test2(num, func){ //다른 함수를 인자로 받음
    const testFunc=func(num)
    return function(num){ //결과로 함수 반환
        return num-testFunc;
    };
}

test2(5, test)(11); //test2 함수가 실행되고 반환된 함수에 인자로 10을 넘겨서 실행
//결과 값은 1이된다.</code></pre>
<h2 id="객체지향-프로그래밍">객체지향 프로그래밍</h2>
<p>객체지향 프로그래밍(OOP)은 <span style="background-color:#fff5b1; color:black"><strong>객체들의 집합으로 프로그래밍의 상호 작용을 표현하면서 데이터를 객체로 취급하여 객체 내부에 선언된 메소드를 활용하는 방식이다.</strong></span> 설계에 많은 시간이 필요하고 처리 속도가 다른 프로그래밍 패러다임에 비해 느리다는 단점이 있다.</p>
<h3 id="객체지향-프로그래밍-특징">객체지향 프로그래밍 특징</h3>
<p>✅ <strong>추상화</strong></p>
<p>복잡한 시스템으로부터 핵심적인 개념, 기능을 간추려내는 것을 말한다. </p>
<p>✅ <strong>캡슐화</strong></p>
<p>객체의 속성과 메소드를 하나로 묶고 일부를 외부에 감추어 은닉하는 것을 말한다.</p>
<p>✅ <strong>상속성</strong></p>
<p>상위 클래스의 특성을 하위 클래스가 이어받아서 재사용하거나 추가, 확장하는 것을 말한다. 코드 재사용, 계층 관계 생성, 유지 보수에서 굉장히 중요한 요소가 된다.</p>
<p>✅ <strong>다형성</strong></p>
<p>하나의 메소드나 클래스가 다양한 방법으로 동작하는 것을 이야기한다. 오버로딩, 오버라이딩이 대표적이다.</p>
<p>💡 <span style="color:orange"><strong>오버로딩(Overloading)?</strong></span>
같은 이름을 가진 메소드를 여러 개 두는 것을 의미한다. 메소드의 타입, 매개변수 유형, 개수 등으로 여러 개의 같은 이름의 메소드를 만들 수 있다. → 컴파일 중에 발생하는 정적 다형성</p>
<pre><code class="language-java">public void test(String str){
    System.out.println(str);
}

public void test(int num){
    System.out.println(num);
}</code></pre>
<p>💡 <span style="color:orange"><strong>오버라이딩(Overriding)?</strong></span>
주로 메소드 오버라이딩을 이야기한다. 상위 클래스로부터 상속 받은 메소드를 하위 클래스가 재정의해서 사용하는 것을 말한다. → 런타임 중에 발생하는 동적 다형성.
<code>equals()</code> <code>hascode()</code> 메소드 등을 재정의 해서 사용할 때 자주 볼 수 있다.</p>
<h3 id="객체지향-프로그래밍-설계-원칙solid-원칙">객체지향 프로그래밍 설계 원칙(SOLID 원칙)</h3>
<p>✅ <strong>단일 책임 원칙(SRP : Single Responsibility Principle)</strong></p>
<p>모든 클래스는 각각 하나의 책임만을 가져야 한다는 원칙이다.</p>
<p>✅ <strong>개방-패쇄 원칙(OCP : Open Closed Principle)</strong></p>
<p>유지 보수 사항이 생기면 코드를 쉽게 확장할 수 있도록 하고 수정할 때는 닫혀있어야 한다는 원칙이다. 기존의 코드는 자주 수정하지 않도록 하면서 확장은 수월하게 할 수 있도록 해야한다.</p>
<p>✅ <strong>리스코프 치환 원칙(LSP : Liskov Substitution Principle)</strong></p>
<p>프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않고 하위 타입의 인스턴스로 바꿀 수 있어야 한다. 클래스를 상속하면서 부모, 자식 관계가 만들어질 때, 부모 객체에 자식 객체를 넣어도 문제 없이 실행되도록 만들어야 한다는 것이다.</p>
<p>과일 클래스가 부모 클래스이고 사과 클래스가 자식 클래스일 때 사과를 과일에 넣는다고 문제가 없어야된다는 것이다.</p>
<p>✅ <strong>인터페이스 분리 원칙(ISP : Interface Segregation Principle)</strong></p>
<p>하나의 일반적인 인터페이스보다 구체적인 여러개의 인터페이스를 만들어야 한다는 원칙이다.</p>
<p>✅ <strong>의존 역전 원칙(DIP : Dependency Inversion Principle)</strong></p>
<p>자신보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스에 두어 변하기 쉬운 것의 변화에 영향을 받지 않게 하는 원칙이다. 상위 걔층은 하위 걔층의 변화에 대한 구현으로부터 독립적이여야 한다.</p>
<h2 id="절차형-프로그래밍">절차형 프로그래밍</h2>
<p>절차형 프로그래밍은 로직이 수행되야할 연속적인 계산 과정으로 이루어져있다. <span style="background-color:#fff5b1; color:black"><strong>일이 진행되는 절차대로 로직을 구현하기 때문에 코드의 가독성이 좋고 실행 속도가 빠르다.</strong></span> 때문에 계산이 많이 필요한 작업에 자주 사용한다. 대신 모듈화 하기 어렵고 유지 보수성이 떨어진다는 단점이 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[디자인 패턴]]></title>
            <link>https://velog.io/@lee_bb/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@lee_bb/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Wed, 23 Aug 2023 14:39:23 GMT</pubDate>
            <description><![CDATA[<h1 id="디자인-패턴">디자인 패턴</h1>
<p><span style="background-color:#fff5b1; color:black"><strong>프로그램 설계 시 발생했던 문제점들을 객체 간의 상호 관계 등을 이용해서 해결할 수 있도록 “규약” 형태로 만들어 놓은 것을 의미한다.</strong></span></p>
<p>설계 문제의 해결책으로 디자인 패턴을 이용하여 프로그램을 설계하면 협업 시 팀원 간의 소통을 원활하게 할 수 있고 일정 패턴을 가지고 프로그램을 설계하기 때문에 유지 보수에 있어서도 장점을 보인다.</p>
<h2 id="싱글톤-패턴singleton-pattern">싱글톤 패턴(Singleton pattern)</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>싱글톤 패턴은 하나의 클래스에 하나의 인스턴스만 가지는 패턴을 이야기한다.</strong></span> 하나의 인스턴스만 만들어서 이것을 기반으로 로직을 작성하는데 쓰이고 보통 DB 연결 모둘에서 많이 사용하는 디자인 패턴이다.</p>
<p>싱글톤 패턴을 이용하면 하나의 인스턴스를 다른 모듈이 공유하면서 사용하기 때문에 인스턴스 생성 비용이 줄어든다는 장점이 존재하지만 의존성이 높아진다는 단점도 있다.</p>
<pre><code class="language-java">Class Test{
    prviate static class singleInstanceHolder{
        private static final Test INSTANCE=new Test();
    }
    public static Test getInstance(){//객체의 인스턴스를 반환하는 메소드 작성
        return singleInstanceHolder.INSTANCE;
    }

}

public class HelloSingle{
    public static void main(String[] args){
        Test a=Test.getInstance();
        Test b=Test.getInstance();
        if(a==b){ //static영역의 변수로 같은 해시코드를 가짐
            System.out.println(true); //true 출력
        }
    }
}</code></pre>
<h3 id="싱글톤-패턴-단점">싱글톤 패턴 단점</h3>
<p>TDD(Test Driven Development : 태스트 주도 개발)를 할 때 싱글톤 패턴은 문제가 된다. TDD를 할 때 단위 테스트를 주로 하게 되는데 단위 테스트는 서로 독립적이여야 어떤 순서로 진행하더라도 테스트할 때 문제가 되지 않는다.</p>
<p>하지만 <strong>싱글톤 패턴은 하나의 인스터스를 기반으로 구현하고 있는 디자인 패턴으로 각 테스트 마다 독립적인 인스턴스를 가질 수 없다.</strong></p>
<p>💡 <span style="color:orange"><strong>TDD?</strong></span>
Test Driven Development. 테스트 주도 개발이라고 한다. 프로그램 개발 시 불확실성이 높을 때 개발 방법이다.
TDD 사이클은 실패할 테스트 구현&gt;성공할 프로덕션 테스트 코드&gt;테스트 코드와 프로덕션 코드의 중복 제거 리팩토링 순서로 이루어진다.
 TDD를 이용하면 개발이 끝난 뒤 테스트 코드를 작성하지 않아도 되고 단위 테스트를 진행 시 전체 코드를 돌리지 않기 때문에 개발 중인 코드를 테스트하기에 적합하다는 장점이 있다.</p>
<h2 id="팩토리-패턴factory-pattern">팩토리 패턴(Factory pattern)</h2>
<p>객체를 사용하는 코드에서 객체 생성 부분을 떼어내고 추상화한 패턴이다. <span style="background-color:#fff5b1; color:black"><strong>상속 관계에 있는 두 클래스 사이에서 상위 클래스가 뼈대를 결정하고 하위 클래스가 객체에 필요한 구체적인 내용을 작성하는 것을 팩토리 패턴이라고 한다.</strong></span></p>
<p>부모, 자식 클래스가 분리되어 있기 때문에 느슨한 결합도를 가지고 있고 부모 클래스는 <strong>인스턴스 생성 방식에 관여하지 않게 되기 때문에 더 많은 유연성을 갖는다</strong>는 장점이 있다. 또한, 객체 생성 로직이 따로 분리되어 있기 때문에 코드를 리팩터링 해도 <strong>한 부분만 고칠 수 있기 때문에 유지 보수에 유리하다.</strong></p>
<h3 id="🤔-왜-사용할까">🤔 왜 사용할까?</h3>
<p>예를 들어 빵가게라는 프로그램을 만들기 위해서 다양한 종류의 빵(객체)를 만든다고 가정해보자. 마늘빵, 소금빵, 피자빵 … 다양한 빵 클래스를 만들어서 팔던 도중 빵가게에서 들어오는 밀가루가 쌀가루로 변경됐다. 그러면 나는 내가 만들어둔 모든 빵(클래스)에 대해서 밀가루를 하나씩 수정해야 하는 대참사가 일어난다.</p>
<p>이때 Bread라는 부모 클래스가 밀가루를 갖고 있고 마늘빵, 소금빵, 피빵이 Bread를 상속 받고 있는 상태라면 어떨까?</p>
<p>Bread에 있는 밀가루만 수정하면 Bread를 상속 받고 있는 자식 클래스들을 하나씩 수정해주지 않아도 된다! 이렇게 부모 클래스를 상속 받고 있지만 빵을 만들기 위해서 필요한 뼈대만 가지고 있기 때문에 구체적인 재료는 자식 클래스가 입맛대로 만들어낼 수 있게 된다.</p>
<h2 id="전략-패턴strategy-pattern">전략 패턴(Strategy pattern)</h2>
<p>정책 패턴(policy pattern)이라고도 부르는 전략 패턴은 객체의 행위를 바꾸고 싶은 경우 직접 수정하는 것이 아니라 <span style="background-color:#fff5b1; color:black"><strong>“전략”이라고 부르는 “캡슐화한 알고리즘”을 컨텍스트 안에서 바꾸면서 교체할 수 있도록 만드는 패턴이다.</strong></span></p>
<p>예를 들어 어떤 물건을 사고 결제하려고 할 때 애플페이, 카카오페이, 삼성페이 등 여러가지 방법으로 결제할 수 있는 것처럼 물건을 사려고 할 때 결제 방식, “전략”만 바꿔서 결제하도록 하는 것을 말한다.</p>
<h3 id="🤔-왜-사용할까-1">🤔 왜 사용할까?</h3>
<p><strong>전략 패턴을 이용하면 원하는 알고리즘을 선택적으로 교환할 수 있다.</strong> 상속을 이용하는 경우 공통된 내용을 분리해서 처리할 수 있지만 클래스간의 결합도(의존성)을 증가 시키고 인터페이스는 알고리즘의 구현 클래스를 적용해야하면서 사용하지 않을 메소드도 반드시 구현해야 하기 때문에 오버헤드가 발생하게 된다.</p>
<p>전략 패턴을 이용하면 알고리즘의 객체를 교환하여 사용한다는 점에서 유용하게 된다. 또한, 행동 변경 시 조건문을 사용하지 않기 때문에 알고리즘 캡슐화를 통해서 조건문 없이 원하는 행동으로 교체할 수 있게 된다.</p>
<h2 id="옵저버-패턴observer-pattern">옵저버 패턴(Observer pattern)</h2>
<p><strong>주체가 어떤 객체의 상태 변화를 관찰하고 있다가 <span style="background-color:#fff5b1; color:black">상태 변화가 있을 때마다 메소드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴을 말한다.</strong></span></p>
<p>옵저버 패턴은 상태 변화에 따라 변화를 알리기 때문에 주로 이벤트 기반 시스템에서 사용하게 된다. 주체에 변화가 생겨 옵저버의 뷰에 변경 데이터를 알려주기 위해 컨트롤러가 작동하는 것을 보면 MVC 패턴에서도 사용된다 볼 수 있다.</p>
<p>💡 <span style="color:orange"><strong>옵저버?</strong></span>
<strong>객체의 상태 화에 따라 전달되는 메소드 등을 기반으로 추가 변화 사항이 생기는 객체들을 의미한다.</strong>
예를 들면 인스타에서 내 계정(주체)를 팔로우하고 있는 계정들을 옵저버라고 말할 수 있다. 내가 인스타 게시글을 올리거나 스토리를 업로드하면 그 변화를 감지하고 옵저버(팔로워)의 피드에 내가 업로드한 게시글이 떠야 한다.</p>
<h2 id="프록시-패턴proxy-pattern">프록시 패턴(Proxy pattern)</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>대상 객체에 접근하기 전에 접근에 대한 흐름을 가로채서 대상 객체 앞에 인터페이스 역할을 하는 디자인 패턴을 말한다.</strong></span> 객체의 속성, 변환 등을 보완하고 보안, 데이터 검증, 캐싱, 로깅에 사용한다.</p>
<p><strong>프록시의 특징은 하나의 객체를 두 가지로 나눠서 재구성한다는 점에 있다.</strong> 직접적인 접근을 막고 대리할 객체를 구현하기 위하기 위해서다. 프록시 패턴은 응용되는 범위가 넓어서 원격 프록시, 가상 프록시, 보호 프록시, 스마트 프록시 등 다양하게 파생된 프록시들이 존재한다.</p>
<h3 id="프록시-서버">프록시 서버</h3>
<p>서버와 클라이언트 사이에서 클라이언트를 통해서 <strong>다른 네트워크 서비스에 간접적으로 접속할 수 있도록 해주는 컴퓨터 시스템이나 응용 프로그램을 말한다.</strong></p>
<ol>
<li><p>nginx</p>
<p> 프록시 서버로 사용하는 nginx(엔진엑스)는 비동기 이벤트 기반의 구조와 다수의 연결을 효과적으로 처리하는 웹 서버이다. 주로 Node.js 서버 앞에 프록시 서버로 활용한다. nginx를 프록시 서버로 두고 실제 포트를 숨기며 정적 자원을 gzip 압축하거나 메인 서버 앞에서 로깅을 하기도 한다. 이렇게 익명 사용자의 직접적인 서버 접근을 차단하고 간적접으로 한 단계를 거치면서 보안성을 강화시킨다.</p>
</li>
<li><p>CloudFlare</p>
<p> CloudFlare는 시스템 콘텐츠를 전 세계적으로 분산된 서버에 빠르게 전달할 수 있는 CDN 서비스이다. CDN 서비스 외에도 DDOS 공격 방어, HTTPS 구축을 제공한다.</p>
</li>
</ol>
<h2 id="이터레이터-패턴iterator-pattern">이터레이터 패턴(Iterator pattern)</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>이터레이터(interator)를 사용해서 컬렉션 요소들에 접근하는 디자인 패턴을 말한다.</strong></span> 자료형의 구조와 상관없이 이터레이터라는 하나의 인터페이스를 이용해서 순회할 수 있다.</p>
<blockquote>
<p>컬렉션 요소를 가지고 interator() 메소드 사용하기</p>
<p><a href="https://velog.io/@lee_bb/%EC%BB%AC%EB%A0%89%EC%85%98ArrayList-Set-Map-Tree">[컬렉션]ArrayList, Set, Map, Tree</a></p>
</blockquote>
<h2 id="노출모듈-패턴revealing-module-pattern">노출모듈 패턴(Revealing module pattern)</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>즉시 실행 함수를 통해서 private, public 같은 접근 제어자를 만드는 패턴을 이야기한다.</strong></span> 자바스크립트에서는 접근 제어자가 존재하지 않고 전역 범위에서 스크립트를 실행하기 때문에 노출모듈 패턴을 이용해서 private, public 같은 접근 제어자를 구현하기도 한다.</p>
<p>💡 <span style="color:orange"><strong>즉시 실행 함수?</strong></span>
함수를 정의하자마자 바로 호출되는 함수로 초기화 코드, 라이브러리 내 전역 변수의 충돌 방지 등에 사용한다.</p>
<h2 id="mvc-패턴">MVC 패턴</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>MVC 패턴은 Model, View, Controller로 이루어진 디자인 패턴을 말한다.</strong></span> 애플리케이션의 구성 요소를 3가지로 구분해서 <strong>각 구성 요소에만 집중해서 개발할 수 있도록 한다.</strong> 재사용과 확장성이 용이하다는 장점을 갖고 있지만 애플리케이션이 복잡해질수록 Model과 View의 관계가 복잡해진다는 단점을 갖는다.</p>
<h3 id="model">Model</h3>
<p>모델은 애플리케이션의 데이터인 DB, 상수, 변수 등을 뜻하는데 뷰에서 데이터를 생성하거나 수정하면 컨트롤러를 통해 모델을 생성하거나 갱신하게 된다.</p>
<h3 id="view">View</h3>
<p>뷰는 사용자 인터페이스 요소를 나타낸다. 모델을 기반으로 사용자가 볼 수 있는 화면을 말하는데 모델이 갖는 정보를 따로 저장하지 않고 단순히 화면에 표시하는 정보만을 갖는다. 또한 화면에서 어떤 변경이 일어나는 경우 컨트롤러에 이 변경을 전달한다.</p>
<h3 id="controller">Controller</h3>
<p>컨트롤러는 모델과 뷰를 이어주는 다리 역할을 한다. 또한 이벤트 등 메인 로직을 담당하고 있다. 모델과 뷰의 생명 주기를 관리하기도 하며 모델이나 뷰에서 변경 사항을 받으면 해당 사항에 대해서 해석하여 각 구성 요소에 해당 내용을 전달한다. 즉, 어떤 요청이 왔을 때 어떤 동작을 행동할지 컨트롤하는 역할을 한다.</p>
<h2 id="mvp-패턴">MVP 패턴</h2>
<p>MVC 패턴에서 파생된 패턴으로 컨트롤러가 프레젠터(presenter)로 교체된 패턴을 말한다. <span style="background-color:#fff5b1; color:black"><strong>뷰와 프레젠터가 1:1 관계를 가지고 있기 때문에 MVC 패턴보다 더 강한 결합도를 지닌 디자인 패턴이다.</strong></span></p>
<h2 id="mvvm-패턴">MVVM 패턴</h2>
<p>마찬가지로 <span style="background-color:#fff5b1; color:black"><strong>MVC 패턴에서 파생된 패턴으로 컨트롤러가 뷰모델(view model)로 교체된 패턴이다.</strong></span> 뷰모델은 UI 관련 데이터를 저장, 관리하며 뷰를 더 추상화한 계층이다.</p>
<p>뷰와 뷰모델 사이에서 <strong>양방향 데이터 바인딩을 지원</strong>하며 <strong>UI를 별도의 코드 수정 없이 재사용할 수 있고 단위 테스팅이 쉽다</strong>는 장점을 가진다. 대표적인 프레임워크로 Vue.js가 있다.</p>
<p>💡 <span style="color:orange"><strong>데이터 바인딩?</strong></span>
화면(UI)에 보이는 데이터와 웹 브라우저의 메모리 데이터를 일치시키는 기법. 뷰모델을 변경하면 뷰가 변경된다. MVVM 패턴을 이용하면 코드량이 감소한다는 장점이 있지만 이처럼 데이터 바인딩을 필수로 갖고 설계가 어렵다는 단점이 있다.</p>
<blockquote>
<p>참고 서적 :  면접을 위한 CS 전공지식 노트 - 주홍철</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Security -2(인증 객체, config 설정)]]></title>
            <link>https://velog.io/@lee_bb/Spring-Security-2%EC%9D%B8%EC%A6%9D-%EA%B0%9D%EC%B2%B4-config-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@lee_bb/Spring-Security-2%EC%9D%B8%EC%A6%9D-%EA%B0%9D%EC%B2%B4-config-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Mon, 21 Aug 2023 02:39:28 GMT</pubDate>
            <description><![CDATA[<h1 id="userdetails-인터페이스를-상속-받은-인증-객체">UserDetails 인터페이스를 상속 받은 인증 객체</h1>
<p>스프링 시큐리티를 사용하기 위해서 <span style="background-color:#fff5b1; color:black"><strong>인증된 사용자에 대한 정보를 담을 인증 객체</strong></span>를 만들 때 UserDetails 인터페이스를 상속 받아서 사용한다. 사용할 Entity 혹은 VO객체에 UserDetails 인터페이스를 상속 받아 구현하면 아래 코드와 같다.</p>
<p>💡읽기 전용 객체인 VO객체를 이용해서 중간에 값이 변경되지 않도록 한다.</p>
<pre><code class="language-java">public class Member implements UserDetails{ //UserDetails를 상속 받음
    private String userId;
    private String password;
    private String name;
    private int age;
    private String email;
    private String gender;
    private String phone;
    private String address;
    private String hobby;
    private Date enrollDate;

    @Override //권한 반환
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        List&lt;GrantedAuthority&gt; auth=new ArrayList&lt;&gt;();
        auth.add(new SimpleGrantedAuthority(MyAuthority.USER.name()));
        if(userId.equals(&quot;admin&quot;)) {
            auth.add(new SimpleGrantedAuthority(MyAuthority.ADMIN.name()));
        }
        return auth;
    }

    @Override //사용자 id 반환
    public String getUsername() {
        return userId;
    }

    @Override //사용자 pwd 반환
    public String getPassword() {
        return password;
    }

    @Override //계정 만료 여부 반환
    public boolean isAccountNonExpired() {
        //계정 만료 여부를 확인하는 로직 작성
        return true; //true : 만료되지 않음, false : 만료됨
    }

    @Override //계정 잠금 여부 반환
    public boolean isAccountNonLocked() {
        //계정 잠금 여부를 확인하는 로직 작성
        return true; //true : 잠금되지 않음, false : 잠김
    }

    @Override //패스워드 만료 여부 반환
    public boolean isCredentialsNonExpired() {
        // 패스워드 만료 여부를 확인하는 로직 작성
        return true; //true : 만료되지 않음, false : 만료됨
    }

    @Override //계정 사용 가능 여부 반환
    public boolean isEnabled() {
        //계정 사용 가능 여부를 확인하는 로직 작성
        return true; //true : 사용 가능, false : 사용 불가
    }
}</code></pre>
<h2 id="getauthorities">getAuthorities()</h2>
<p><strong>사용자가 가지고 있는 권한의 목록을 반환하는 메소드</strong>로 <code>Collection&lt;? extends GrentedAuthority&gt;</code> 타입을 반환 타입으로 갖는다. 위에 작성한 코드로 보면 기본적으로 인증 받으면 USER 권한을 주고 계정의 아이디가 admin인 경우 ADMIN 권한을 같이 줄 수 있도록 작성했다.</p>
<h2 id="getusername">getUsername()</h2>
<p><strong>사용자를 식별할 아이디를 String 타입으로 반환하는 메소드이다.</strong> 이때 사용자로 아이디로 사용하는 값은 사용자를 식별할 값이기 때문에 고유한 값을 사용해야 한다. 보통 PK를 사용하고 아닌 경우라도 유니크 속성이 있는 값을 이용한다.</p>
<h2 id="getpassword">getPassword()</h2>
<p>사용자의 비밀번호를 String 타입으로 반환하는 메소드이다. 저장된 비밀번호는 반드시 암호화 해서 저장해야 한다.</p>
<h2 id="is">is~()</h2>
<p>그 외에 is로 시작하는 메소드는 필요에 따라 사용할 수 있다. 기능 필요에 따라 로직을 구현한 뒤 boolean 타입으로 반환 값을 넘겨주면 된다. 사용 안 하는 경우에는 <strong>기본적으로 true를 줘서 사용할 수 있도록 한다.</strong></p>
<h1 id="userdetailsservice-인터페이스를-구현한-service">UserDetailsService 인터페이스를 구현한 Service</h1>
<p>스프링 시큐리티에서 <span style="background-color:#fff5b1; color:black"><strong>로그인을 할 때 사용자 정보를 가져오는 코드를 작성</strong></span>하기 위해서  UserDetailsService 인터페이스를 구현한 service 클래스를 만들어준다.</p>
<pre><code class="language-java">public class SecurityLoginService implements UserDetailsService{
    @Autowired
    private MemberDao dao;

    //UserDetailsService 인터페이스의 추상 메소드 loadUserByUsername()를 구현
    @Override
    public UserDetails loadUserByUsername(String username)
                                                throws UsernameNotFoundException {
        //parameter 중에서 username에 작성된 key값을 매개변수로 받아온다.
        Member loginMember=dao.selectMember(session, Map.of(&quot;userId&quot;,username));
        //DAO에 selectMember() 메소드의 파라미터를 Map타입으로 했기 때문에
        //username을 Map에 담아서 보낸다
        return loginMember;
    }
}</code></pre>
<h1 id="authenticationprovider를-구현해서-db-연동-방식으로-인증">AuthenticationProvider를 구현해서 DB 연동 방식으로 인증</h1>
<p>인증 처리 시 인증 방법에 대한 설정을 하는 클래스를 등록해야 하는데 이 때 인메모리 방식과 DB 연동 방식 두가지로 나뉜다. 보통 인메모리 방식을 사용하지 않기 때문에 DB 연동 방식을 이용한다.</p>
<pre><code class="language-java">@Component
public class DBConnectProvider implements AuthenticationProvider{
    @Autowired
    private MemberDao dao;
    //패스워드 인코더
    private BCryptPasswordEncoder encoder=new BCryptPasswordEncoder();

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String userId=authentication.getName(); //인증 객체에서 id를 가져옴
        String password=(String)authentication.getCredentials(); //인증 객체에서 비밀번호 가져옴

        Member loginMember=dao.selectMemberById(userId);
        if(loginMember==null||!encoder.matches(password, loginMember.getPassword())) {
            //로그인 실패
            throw new BadCredentialsException(&quot;인증 실패&quot;);
        }
        //인증된 객체를 생성해서 반환
        return new UsernamePasswordAuthenticationToken(
                loginMember, loginMember.getPassword(), loginMember.getAuthorities());
    }

    @Override
    public boolean supports(Class&lt;?&gt; authentication) {
        //UsernamePasswordAhthenticationToken을 사용할 수 있게 함
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }

}</code></pre>
<p><code>getName()</code> 메소드는 String 타입을 반환하지만 <code>getCredentials()</code> 메소드는 Object 타입을 반환하므로 형변환해서 String 타입으로 패스워드를 저장해서 사용했다.</p>
<h2 id="bcryptpasswordencodermatches">BCryptPasswordEncoder.matches()</h2>
<p><strong>BCryptPasswordEncoder가 제공하는 *<em><code>matches()</code> *</em>메소드를 이용해서 사용자가 입력한 비밀번호와 DB에 저장된 비밀번호가 일치하는지 확인한다.</strong> 일치하면 true를 반환하고 일치하지 않으면 false를 반환한다. 따로 디코딩 할 수 없다.</p>
<p>💡 <code>matches()</code> 메소드 사용 시 첫 번째 인수 값으로 평문으로 된 사용자가 입력한 패스워드 값을 넣고 두 번째 인수로  암호화된 비밀번호를 작성해줘야 한다.</p>
<h1 id="securityconfig로-시큐리티-설정">SecurityConfig로 시큐리티 설정</h1>
<p><span style="background-color:#fff5b1; color:black"><strong>인증 처리를 위한 인증 객체와 서비스가 완성되었으면 실제로 인증 처리를 진행하는 시큐리티 설정 파일을 작성한다.</strong></span> 보통 같은 패키지 내에 config 패키지를 만들어서 클래스 파일을 새로 만들어서 작성한다.</p>
<p>스프링 부트에서 security를 적용하기 위해서는 우선 인증 처리할 빈을 등록하게 되는데 이 역할을 하는 빈을 SecurityConfig의 메소드로 지정한다. 해당 빈은 SecurityFilterChain 객체를 반환한다.</p>
<pre><code class="language-java">@Configuration //config 클래스로 등록
@EnableWebSecurity //security 설정
public class SecurityConfig {
    @Autowired
    private DBConnectProvider provider;

    @Bean
    public SecurityFilterChain authenticationPath(HttpSecurity http) throws Exception {
        //HttpSecurity를 셋팅해서 SecurityFilterChain을 반환
        return http.csrf().disable() //csrf 비활성화
                //토큰을 사용하는 방식이기 때문에 csrf를 비활성화 헤야한.
                .formLogin() //로그인 폼 관련 설정
                    .successForwardUrl(&quot;/successLogin&quot;) //성공했을 때 실행할 url 주소
                    .failureForwardUrl(&quot;/errorLogin&quot;) //실패 시 실행할 url 주소
                    .passwordParameter(&quot;pw&quot;) //패스워드의 파라미터 이름
                    .loginProcessingUrl(&quot;/login.do&quot;) //로그인 처리할 url
                    .loginPage(&quot;/loginpage&quot;) //로그인 페이지 url
                .and()
            //and()메소드를 사용하면 다시 HttpSecurity를 반환해서 접근해서 이어서 사용할 수 있다.
                .authorizeHttpRequests() //인증, 인가 관련 설정
                    .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                    .antMatchers(&quot;/loginpage&quot;).permitAll()
                    .antMatchers(&quot;/errorLogin&quot;).permitAll()
                    //loginpage, errorLogin는 권한이 없어도 사용할 수 있게 함
                    .antMatchers(&quot;/**&quot;).hasAnyAuthority(MyAuthority.USER.name())
                    //그 외 모든 url 주소에 대해서 USER 권한이 있어야 사용할 수 있게 함
                .and()
                .logout() //로그아웃 관련 설정
                    .logoutSuccessUrl(&quot;/logout&quot;)
                    .logoutUrl(&quot;/logout.do&quot;)
                .and()
                .authenticationProvider(provider)
                .and()
                .exceptionHandling()//에러 발생 시 핸들링 처리
                    .authenticationEntryPoint(new AuthenticationEntryPoint() {
                        @Override //로그인 안 하고 접근하는 경우
                        public void commence(HttpServletRequest request, HttpServletResponse response,
                                AuthenticationException authException) throws IOException, ServletException {
                            response.sendRedirect(request.getContextPath()+&quot;/error/login&quot;);
                        }
                    }).accessDeniedHandler(new AccessDeniedHandler() {
                        @Override //권한이 없는 페이지 접근하는 경우
                        public void handle(HttpServletRequest request, HttpServletResponse response,
                                AccessDeniedException accessDeniedException) throws IOException, ServletException {
                            response.sendRedirect(request.getContextPath()+&quot;/error/auth&quot;);
                        }
                    })
                .build();
    }
}</code></pre>
<h2 id="formlogin">.formLogin()</h2>
<p><strong>form 기반으로 로그인할 경우 필요한 설정에 대해서 작성할 수 있다.</strong> FormLoginConfigurer를 불러온다. </p>
<p><code>successForwardUrl()</code> : 로그인 성공 시 실행할 url 주소를 인수로 작성한다.</p>
<p><code>failureForwardUrl()</code> : 로그인 실패 시 실행할 url 주소를 인수로 작성한다.</p>
<p><code>passwordParameter()</code> : password로 사용할 파라미터의 key값을 작성한다.</p>
<p><code>loginProcessingUrl()</code> : 로그인 처리 시 별도로 실행할 로직이 있을 경우 작성한다.</p>
<p><code>loginPage()</code> : 시큐리티가 제공하는 로그인 페이지 외에 별도로 만든 로그인 페이지를 이용할 때 사용한다.</p>
<h2 id="authorizehttprequests">authorizeHttpRequests()</h2>
<p><strong>특정 HTTP 요청에 대한 엑세스 설정을 할 때 사용한다.</strong> 각 설정은 아래와 같다.</p>
<h3 id="요청-구분">요청 구분</h3>
<p><code>requestMatchers()</code> : 인수로 다수의 요청 주소를 작성할 수 있다. 작성한 인수들의 요청에 동일한 권한 제한을 두고 싶을 때 사용할 수 있다.</p>
<p><code>andMatchers()</code> : 인수로 작성한 url 요청에 대한 설정을 한다.</p>
<p><code>anyRequest()</code> : 설정한 경로 외에 모든 경로에 대한 권한을 지정한다.</p>
<h3 id="허가-구분">허가 구분</h3>
<p><code>hasRole()</code> : 해당 Role(역할)을 가지고 있는 경우 요청 가능하도록 허용한다.</p>
<p><code>hasAnyRole()</code> : 인수로 작성한 Role 중 하나라도 가지고 있으면 허용한다.</p>
<p><code>permitAll()</code> : 해당 요청에 별다른 인증 없이 이용 가능하도록 한다.(무조건 true를 반환)</p>
<p><code>denyAll()</code> : 어떤 권한 상태라도 무조건 허용하지 않을 때 사용한다.(무조건 false를 반환)</p>
<p><code>hasAnyAuthority()</code> : 해당 요청에 인수로 작성한 권한이 있어야 이용 접근 가능하게 한다.</p>
<p><code>isAnonymous()</code> : 익명 사용자를 허용한다.</p>
<p><code>isRememberMe()</code> : Remember Me 인증을 통해 인증된 계정인 경우 허용한다.</p>
<p><code>isFullyAuthenticated()</code> : Remember Me 인증이 아닌 일반 인증인 경우 허용한다.</p>
<p><code>isAuthenticated()</code> : 이미 인증 받은 사용자인 경우 허용한다.</p>
<h2 id="exceptionhandling">exceptionHandling()</h2>
<p>인증, 인가 설정 후 허용되지 않은 요청에 접근하려고 했을 때 예외가 발생하는데 이 때 핸들러를 이용해서 예외 발생 시 처리할 로직을 작성할 수 있다.</p>
<h3 id="authenticationentrypoint">authenticationEntryPoint()</h3>
<p>로그인 후 접근 가능한 url에 요청을 보냈을 경우 실행되는 메소드로 AuthenticationEntryPoint 객체를 생성 해서 해당 객체 안에 <code>commence()</code> 메소드를 오버라이딩 한다.</p>
<pre><code class="language-java">@Override //로그인 안 하고 접근하는 경우
public void commence(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException authException) throws IOException, ServletException {
        response.sendRedirect(request.getContextPath()+&quot;/error/login&quot;);
}</code></pre>
<p>위 코드에서는 요청을 <code>sendRedirect()</code>로 넘겨서 내가 지정한 로그인 에러 url로 응답시켰다.</p>
<h3 id="accessdeniedhandler">accessDeniedHandler()</h3>
<p>접근하려고 하는 url에 인증된 객체가 필요한 권한이 없는 경우 실행되는 메소드로 AccessDeniedHandler 객체를 생성해서 <code>handle()</code> 메소드를 오버라이딩 한다.</p>
<pre><code class="language-java">@Override //권한이 없는 페이지 접근하는 경우
public void handle(HttpServletRequest request, HttpServletResponse response,
    AccessDeniedException accessDeniedException) throws IOException, ServletException {
    response.sendRedirect(request.getContextPath()+&quot;/error/auth&quot;);
}</code></pre>
<p>위 코드에서는 권한 불일치 예외 발생 시 <code>sendRedirect()</code>로 지정한 권한 에러 url로 응답시켰다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Security - 1( Filter들, 동작 구조)]]></title>
            <link>https://velog.io/@lee_bb/Spring-Security-1-Filter%EB%93%A4-%EB%8F%99%EC%9E%91-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@lee_bb/Spring-Security-1-Filter%EB%93%A4-%EB%8F%99%EC%9E%91-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Sat, 05 Aug 2023 15:57:17 GMT</pubDate>
            <description><![CDATA[<h1 id="스프링-시큐리티spring-security">스프링 시큐리티(spring security)</h1>
<p><span style="background-color:#fff5b1; color:black"><strong>스프링 시큐리티는 스프링 기반의 애플리케이션 보안을 담당하는 스프링의 하위 프레임워크다.</strong></span> 보안(인증, 인가, 권한)에 대한 많은 옵션을 제공하며 애너테이션으로 설정하기도 간편하다는 장점이 있다.</p>
<p>CSRF 공격(사용자 권한을 갖고 특정 동작을 유도), 세션 고정 공격(사용자 인증 정보 탈취 및 변조)을 방어할 수 있고 요청 헤더 또한 보안 처리를 해주기 때문에 <strong>개발자가 프로젝트 진행 시 보안에 대한 부담감을 줄이는데 크게 기여한다.</strong></p>
<p>💡 <span style="color:orange"><strong>인증, 인가</strong></span>
사용자의 신원을 입증하는 즉 로그인 과정을 인증이라고 하면 인가는 사이트의 관리자 페이지처럼 특정 권한을 확인하는 과정을 말한다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/b409f0ad-670a-484e-a0d4-767d3ac9bc9b/image.png" alt=""></p>
<p>STS에서 스프링 시큐리티를 사용하기 위해 프로젝트 생성시 Spring Security를 추가하거나 이미 작업 중이던 프로젝트라면 프로젝트 폴더 마우스 우클릭&gt;Srping&gt;Add Staters를 눌러서 Spring Security를 추가하도록 한다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/9a02278e-d2ac-42a2-8c0a-2b3e6f94c07e/image.png" alt=""></p>
<p>추가하면 pom.xml에서 위 사진의 해당 코드를 확인할 수 있고, JRE System Library를 확인해도 시큐리티 관련 자료 파일들이 들어가 있는 것을 확인할 수 있다.</p>
<h1 id="스프링-시큐리티-필터들">스프링 시큐리티 필터들</h1>
<h3 id="securitycontextpersistencefilter">SecurityContextPersistenceFilter</h3>
<p>SecurityContextRepository에서 SecurityContext를 가져오거나 저장하는 역할을 한다. SecurityContext에는 접근 주체와 인증에 대한 정보를 담고 있다.</p>
<h3 id="logoutfilter">LogoutFilter</h3>
<p>설정해둔 로그아웃 url에 요청을 확인하고 사용자를 로그아웃 처리하는 역할을 한다.</p>
<h3 id="usernamepasswordauthenticationfilter">UsernamePasswordAuthenticationFilter</h3>
<p>인증 관리자로 폼 기반 로그인을 사용할 때 사용되는 필터다. 필터로 아이디, 패스워드 데이터를 파싱하여 인증 요청을 위임하고 인증 성공 여부에 따라서 핸들러를 실행한다.</p>
<h3 id="defaultloginpagegenerationfilter">DefaultLoginPageGenerationFilter</h3>
<p>개발자가 로그인 페이지를 따로 지정하지 않았을 때 기본적으로 설정하는 로그인 페이지에 관련된 필터이다. 스프링에서 로그인 페이지를 제공하므로 별도의 로그인 페이지를 만들지 않았다면 기본 로그인 페이지를 이용할 수 있다.</p>
<h3 id="basicauthenticationfilter">BasicAuthenticationFilter</h3>
<p>요청 헤더에 있는 아이디와 패스워드를 파싱해서 인증 요청을 위임하는 필터다. 인증에 성공 여부에 따라 핸들러를 실행한다.</p>
<h3 id="requestcacheawarefilter">RequestCacheAwareFilter</h3>
<p>로그인(인증)이 성공하면 관련 있는 캐시 요청이 있는지 확인하고 캐시 요청을 처리해주는 필터다. 로그인하지 않은 상태에서 방문했던 마지막 페이지를 기억해두었다가 로그인에 성공하면 해당 페이지로 이동시켜주는 등의 처리를 할 수 있다.</p>
<h3 id="securitycontextholderawarerequestfilter">SecurityContextHolderAwareRequestFilter</h3>
<p>HttpServletRequest 정보를 감싸고 있는 필터로 필터 체인 상의 다음 필터들에게 부가적인 정보를 제공하기 위해서 사용한다.</p>
<h3 id="anoymousauthenticationfilter">AnoymousAuthenticationFilter</h3>
<p>필터가 호출되는 시점까지 인증이 되지 않았을 때 익명 사용자 객체(AnonymousAuthentication)를 만들어서 SecurityContext에 넣어주기 위해 사용되는 필터다.</p>
<h3 id="sessionmanagementfilter">SessionManagementFilter</h3>
<p>인증된 사용자와 관련된 세션 작업을 실행할 때 사용하는 필터로 세션 변조 방지 전략에 대한 설정과 유효하지 않은 세션에 대한 처리, 세션 생성 전략을 세우는 등의 작업을 수행한다.</p>
<h3 id="exceptiontranslationfilter">ExceptionTranslationFilter</h3>
<p>요청 처리 중 발생하는 Exception을 위임하거나 전달하는 필터다.</p>
<h3 id="filtersecurityintercetor">FilterSecurityIntercetor</h3>
<p>접근 결정 관리자 필터로 AccessDecisionManager애개 권한 부여 처리를 위임하여 접근 제어 결정을 쉽게 할 수 있도록 도와준다. 이미 사용자가 인증되어 있는 상태에서 사용하기 때문에 유효한 사용자인지 권한에 대한 확인을 한다.</p>
<h1 id="스프링-시큐리티-동작-과정">스프링 시큐리티 동작 과정</h1>
<p><span style="background-color:#fff5b1; color:black"><strong>스프링 시큐리티는 필터 기반으로 동작한다.</strong></span> 각 필터에서 인증, 인가와 관련된 작업들을 처리한다. 특정 필터를 제거 하거나 필터 뒤에 다른 필터를 넣는 설정도 가능하다. </p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/8fdd0810-206f-4035-a925-55aacbb23a43/image.jpg" alt=""></p>
<p>스프링 시큐리티의 동작 과정은 위와 같다.</p>
<ol>
<li>사용자가 인증에 필요한 정보(아이디, 패스워드)를 입력하면 HTTPServletRequest에 데이터가 전달된다. 이 때 <strong>AuthenticationFilter가 데이터의 유효성 검사를 실시</strong>한다.</li>
<li>유효성 검사를 한 뒤에 UsernamePasswordAuthenticationToken에 넘겨준다.</li>
<li><strong>인증용 객체인 UsernamePasswordAuthenticationToken</strong>을 AuthenticationManager에게 보낸다.</li>
<li>UsernamePasswordAuthenticationFilterToken을 AuthenticationProvier에게 보낸다.</li>
<li>이 중 사용자 아이디를 UserDetailService에 보내고 여기서 <strong>사용자 아이디를 가지고 찾은 정보를 UserDetails 객체로 만들어서 AuthenticationProvider에게 전달</strong>한다.</li>
<li>DB에 있는 사용자 정보를 불러온다.</li>
<li><strong>입력된 정보와 UserDetails의 정보를 비교해서 인증 처리를 진행</strong>한다.</li>
<li>~10. 까지 인증이 완료되고 나면 <strong>인증 여부에 따라 AuthenticationSuccessHandler, AuthenticationFilureHandler를 실행한다.</strong></li>
</ol>
<blockquote>
<p>참고
<a href="https://velog.io/@girgir/Spring-Security">Spring Security야! 일단 이론만 정리해보자고! -girgir 벨로그</a>
서적 : 스프링 부트 3 백엔드 개발자 되기(자바 편)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 부트 계층 구조]]></title>
            <link>https://velog.io/@lee_bb/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EA%B3%84%EC%B8%B5-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@lee_bb/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EA%B3%84%EC%B8%B5-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Sat, 05 Aug 2023 13:46:13 GMT</pubDate>
            <description><![CDATA[<h1 id="스프링-부트의-계층-구조">스프링 부트의 계층 구조</h1>
<p><strong>스프링 부트는 각 계층이 다른 계층과 통신하는 구조를 따른다.</strong> 계층은 각 역할과 책임이 있는 소프트웨어의 구성 요소를 의미하는데 계층끼리 소통은 할 수 있지만 각 계층이 다른 계층에 직접 영향을 미치진 않는다.</p>
<p>스프링 부트에서는 프레젠테이션, 비즈니스, 퍼시스턴스 계층이 있다. 계층은 개념의 영역이기 때문에 실제로 구현하는 컨트롤러, 서비스, 리포지토리가 해당 계층의 역할을 수행하는 영역이 된다.</p>
<h2 id="프레젠테이션-계층">프레젠테이션 계층</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>HTTP 요청을 받고 요청을 비즈니스 계층으로 전송하는 역할을 하는 계층을 말한다.</strong></span> 즉 요청에 대해서 어디로 처리할지 컨트롤하는 역할을 하게 되므로 컨트롤러라고 한다. 컨트롤러는 스프링 부트 내에서 여러 개가 존재할 수 있고 역할에 따라 구분한다.</p>
<pre><code class="language-java">@Controller
@RequestMapping(&quot;/member&quot;) //클래스 선언부에 작성 시 해당 매핑주소 생략 후 적을 수 있게함
public class MemberController {

    @Autowired
    private MemberService service; //MemberService 의존성 주입

    @GetMapping(&quot;/memberAll&quot;) /// url주소가 /member/memberAll이지만 클래스 선언부에
    //RequestMapping으로 /member를 작성했으므로 미리 매핑한 주소부분은 빼고 작성
    public String selectMemberAll(Model model) {
        model.addAttribute(&quot;members&quot;,service.selectMemberAll());
        //요청을 처리하기 위해 service를 불러와 이용한다.
        return &quot;member/memberList&quot;;
    }
}</code></pre>
<h2 id="비즈니스-계층">비즈니스 계층</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>모든 비즈니스 로직을 처리하는 계층으로 서비스를 말한다.</strong></span> 비즈니스 로직이란 서비스를 만들기 위한 서비스 로직을 말하는데 웹 사이트에서 일어나는 작업을 처리하기 위한 로직, 처리하다가 발생하는 예외에 대해 처리하는 로직, 서비스를 취소하는 로직 등 프로세스를 구현하기 위한 로직들을 전부 서비스인 비즈니스 계층이 담당한다.</p>
<pre><code class="language-java">@Service
public class MemberServiceImpl implements MemberService { 
//MemberService 인터페이스를 구현한 클래스
    @Autowired
    private SqlSession session;
    @Autowired
    private MemberDao dao;

    @Override
    public List&lt;Member&gt; selectMemberAll() {
        return dao.selectMemberAll(session);
        //필요한 서비스를 제공하기 위해 DB와 접근해야하면 DAO 객체를 이
    }
}</code></pre>
<h2 id="퍼시스턴스-계층">퍼시스턴스 계층</h2>
<p><span style="background-color:#fff5b1; color:black"><strong>모든 데이터베이스 관련된 로직을 처리하는 계층을 말한다.</strong></span> 데이터베이스에 접근하는 DAO 객체를 사용할 수 있고 DAO는 DB 계층과 상호작용하기 위한 객체로 이용한다. <code>@Repository</code>를 사용하는 DAO가 퍼시스턴스 계층 역할을 하고 있다고 이해하면 된다.</p>
<pre><code class="language-java">@Repository
public class MemberDaoImpl implements MemberDao {
    @Autowired
    private MemberMapper mapper;

    @Override
    public List&lt;Member&gt; selectMemberAll(SqlSession session) {
        //return session.selectList(&quot;member.selectMemberAll&quot;); -&gt;session을 이용해서 처리 가능
        return mapper.selectMemberAll(); //mapper 인터페이스를 생성해서 처리 가
    }
}</code></pre>
<h1 id="스프링-부트-프로젝트-구성">스프링 부트 프로젝트 구성</h1>
<p>스프링 부트 프로젝트는 정해진 구성은 없지만 추천하고 있는 구성이 존재하기 때문에 많은 개발자들이 추천된 구성으로 프로젝트를 구성한다.</p>
<h2 id="srcmain">src/main</h2>
<p>실제 코드를 작성하는 공간으로 프로젝트 실행에 필요한 소스 코드나 리소스 파일을 해당 폴더에 만든다.</p>
<h3 id="srcmainjava">src/main/java</h3>
<p>main 디렉터리를 열면 java 디렉터리를 확인할 수 있다. 필요한 소스 코드 파일을 java 디렉터리 안에 생성해서 사용한다.</p>
<h3 id="srcmainresources">src/main/resources</h3>
<p>HTML 같은 뷰 관련 파일을 넣을 templates 디렉터리와 JS, CSS, images 같은 정적 파일을 넣을 static 디렉터리를 resources 디렉터리 안에 생성해서 사용한다. 스프링 부트에 대한 설정을 할 수 있는 application.yml 혹은 application.properties 파일도 해당 디렉터리에 작성한다.</p>
<p>application.yml 파일은 스프링 부트 서버가 실행되면서 자동으로 로딩되는 파일로 DB 설정 정보, 로깅 설정 정보 등을 작성할 수 있다.</p>
<h2 id="srctest">src/test</h2>
<p>프로젝트의 소스 코드를 테스트 하기 위해서 사용하는 폴더로 테스트할 코드나 리소스 파일들을 넣어서 사용한다.</p>
<h2 id="gradle-사용-시">gradle 사용 시</h2>
<p><code>build.gradle</code> : 빌드를 설정하는 파일로 의존성이나 플러그인 설정 등 빌드에 필요한 설정을 작성한다.</p>
<p><code>setting.gradle</code> : 필드할 프로젝트의 정보를 설정하는 파일이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[@Componet 애너테이션]]></title>
            <link>https://velog.io/@lee_bb/Componet-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@lee_bb/Componet-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98</guid>
            <pubDate>Fri, 04 Aug 2023 14:54:58 GMT</pubDate>
            <description><![CDATA[<h1 id="restcontroller와-componet">@RestController와 @Componet</h1>
<p>스프링 부트에서 컨트롤러 역할을 하는 클래스에 선언하는 <code>@RestController</code>는 <code>@Componet</code>처럼 취급한다. 이전 포스팅에서 <code>@Componet</code> 애너테이션이 선언된 클래스들을 찾아서 전부 빈으로 등록하는 역할을 하고 <code>@Componet</code> 애너테이션이 <code>@RestController</code>를 감싸고 있다고 했다. 그래서 다른 두 개의 애너테이션이 어떻게 <code>@Componet</code> 역할을 하는지 얘기해보려고 한다.</p>
<blockquote>
<p><a href="https://velog.io/@lee_bb/Spring-Boot">Spring Boot 포스팅 바로 가기</a></p>
</blockquote>
<h2 id="restcontroller">@RestController</h2>
<p>라우터 역할을 하는 애너테이션으로<code>@RestController</code> 애너테이션을 작성해야 요청에 맞는 메소드를 실행할 수 있다.</p>
<blockquote>
<p>💡 라우터(router)
HTTP 요청과 메소드를 연결하는 장치를 말한다.
<a href="https://i5i5.tistory.com/455">라우터란? -티스토리 링크</a></p>
</blockquote>
<pre><code class="language-java">@RestController
public class BasicController {
    @GetMapping(&quot;/test&quot;)
    public String test() {
        //매핑된 주소로 요청이 들어오면 실행할 로직 작성
        return &quot;test/test&quot;; //test 폴더의 test.jsp 화면을 여는 응답 
    }
}</code></pre>
<p>위와 같은 기본 컨트롤러 클래스에서 ctrl을 누른 상태로 <code>@RestController</code>를 클릭하면 아래와 같은 코드를 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/e65cfa75-0809-4b7f-b7b5-359cef1f3790/image.png" alt=""></p>
<p><code>@Controller</code>와 <code>@ResponsBody</code> 애너테이션이 함께 선언되어 있는 모습을 확인할 수 있는데 이는 <code>@RestController</code>가 <code>@Controller</code>와 <code>@ResponsBody</code>를 포함하고 있다는 것을 알려준다.</p>
<p>여기서 <code>@Controller</code> 애너테이션에 ctrl을 누른 상태로 클릭해서 <code>@Controller</code> 애너테이션을 확인해보면 아래와 같은 코드를 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/0d5637d0-cef3-425e-b043-27d5836fadae/image.png" alt=""></p>
<p>코드를 보면 바로 알 수 있지만 <code>@Controller</code>는 <code>@Componet</code> 애너테이션을 선언하고 있다. 앞서 포스팅했을 때 말했던 <code>@Configuration</code>, <code>@Repository</code>, <code>@Controller</code>, <code>@RestController</code>, <code>@Service</code> 애너테이션들은 전부 <code>@Componet</code> 애너테이션을 갖고 있는 애너테이션들이었다.</p>
<p>그래서 필요에 따라 어떤 애너테이션을 작성해도 빈으로 등록되어 스프링이 관리하게 된다. 애너테이션들의 이름은 해당 클래스를 빈으로 등록하면서 무슨 역할을 하는지 명확하게 구분하기 위해 나뉘어있다고 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot]]></title>
            <link>https://velog.io/@lee_bb/Spring-Boot</link>
            <guid>https://velog.io/@lee_bb/Spring-Boot</guid>
            <pubDate>Thu, 03 Aug 2023 15:04:09 GMT</pubDate>
            <description><![CDATA[<h1 id="스프링-부트">스프링 부트?</h1>
<p><strong>스프링은 애플리케이션을 위한 개발 환경을 제공해서 기능 개발에만 집중할 수 있게 나온 개발 도구지만 설정하기 매우 복잡하다는 단점이 있다.</strong> 실제로 스프링을 처음 접하면서 가장 어려웠던 점은 이것 저것 셋팅할 게 생각보다 많아서 배웠는데도 다시 설정하면서 어디까지 제대로 설정했는지 몰라서 어려움이 있었다.</p>
<p>그래서 스프링에서 이런 단점을 보완하고자 스프링 부트를 만들었다. <span style="background-color:#fff5b1; color:black"><strong>스프링 부트는 스프링 프레임워크를 더 쉽고 빠르게 이용할 수 있도록 만들어진 도구</strong></span>로 스프링 프로젝트를 설정을 더 쉽게 할 수 있고 “의존성 세트”라고 불리는 스타터(starter)를 사용해서 의존성 주입(DI)를 간편하게 이용할 수 있다.</p>
<h1 id="스프링-부트의-특징">스프링 부트의 특징</h1>
<p>✅ 톰캣 같은 웹 애플리케이션 서버(WAS)를 내장하고 있어 따로 설치하지 않아도 독립적으로 실행할 수 있다.</p>
<p>✅ 빌드 구성을 단순화하는 스프링 부트 스타터(spring boot starter)를 제공한다.</p>
<p>✅ xml 설정을 하지 않고 자바 코드로 모두 작성 가능하다.</p>
<p>✅ jar를 이용해서 자바 옵션만으로 배포가 가능하다.</p>
<p>✅ 애플리케이션의 모니터링 및 관리 도구인 스프링 액츄에이터(spring actuator)를 제공한다.</p>
<h1 id="그래서-스프링이랑-스프링-부트의-차이점은">그래서 스프링이랑 스프링 부트의 차이점은?</h1>
<h2 id="💡-구성의-차이">💡 구성의 차이</h2>
<p>스프링은 애플리케이션 개발에 필요한 환경을 직접 구성하고 정의해서 사용하지만 <span style="background-color:#fff5b1; color:black"><strong>스프링 부트는 스프링 코어와 스프링 MVC의 모든 기능을 자동으로 설정하므로 개발 환경을 따로 구성할 필요가 없다.</strong></span></p>
<h2 id="💡-내장-was의-유무">💡 내장 WAS의 유무</h2>
<p>스프링 애플리케이션은 보통 톰캣 같은 WAS를 이용해서 배포하는데 <span style="background-color:#fff5b1; color:black"><strong>스프링 부트에서는 WAS를 내장하고 있기 때문에 jar파일만 만들면 별도로 설치하지 않아도 된다.</strong></span> 스프링 부트는 톰캣만 갖고 있는 건 아니고 제티, 언더토우도 있어서 <strong>필요에 따라 WAS를 선택</strong>해서 사용할 수 있다.</p>
<h2 id="💡-xml-사용-유무">💡 XML 사용 유무</h2>
<p>스프링은 일부 파일을 XML 파일로 직접 생성해서 관리하지만 <span style="background-color:#fff5b1; color:black"><strong>스프링 부트에서는 사용하지 않고 자바 코드로 전부 구현할 수 있다.</strong></span> XML을 사용 못하는 것은 아니지만 버전이 업데이트 될수록 XML 사용을 안하도록 걷어내고 있다.</p>
<h2 id="💡-인메모리">💡 인메모리</h2>
<p>스프링은 인메모리 데이터베이스를 지원하지 않지만 <span style="background-color:#fff5b1; color:black"><strong>스프링 부트에서는 인메모리 데이터베이스를 자동으로 지원하고 있다.</strong></span></p>
<h3 id="💡-span-stylecolororange인메모리-데이터베이스imdb--in-memory-datebasespan">💡 <span style="color:orange"><strong>인메모리 데이터베이스(IMDB : In-Memory Datebase)</strong></span></h3>
<p>IMDB 혹은 MMDB(Main Memory Datebase)라고도 하는 <strong>인메모리 데이터베이스는 디스크가 아닌 주 메모리에 모든 데이터를 보유하고 있는 데이터베이스를 말한다.</strong> <strong>자료 접근이 빠르다는 것이 큰 장점</strong>으로 메모리상에 색인을 넣어서 필요한 모든 정보를 메모리상의 색인으로 빠르게 탐색 가능하다.</p>
<p>DB 서버 전원이 갑자기 꺼지면 자료들이 다 삭제되는 <strong>휘발성 매체라는 것이 단점이다.</strong> 때문에 로그인 정보 등 데이터가 날아가도 상관 없는 임시 데이터들을 주로 인메모리 데이터베이스에 저장하고 사용한다.</p>
<h1 id="springbootapplication">@SpringBootApplication</h1>
<pre><code class="language-java">@SpringBootApplication
public class HellobootApplication {
    public static void main(String[] args) {
        SpringApplication.run(HellobootApplication.class, args);
    }
}</code></pre>
<p>스프링 부트 프로젝트를 생성하면 자동으로 생기는 <code>@SpringBootApplication</code> 어노테이션이 작성된 Application 클래스가 생긴다. <span style="background-color:#fff5b1; color:black"><strong>이 클래스는 자바의 main() 메소드 역할을 한다.</strong></span> 스프링 부트는 자바 중점으로 개발된 도구로 동작 방식이 일반 자바 프로젝트처럼 구성되어있다. 때문에 <strong>해당 클래스가 스프링 부트 프로젝트의 시작점이 된다.</strong></p>
<p><code>SpringApplication.run()</code> 메소드로 애플리케이션을 실행하는데 첫 번째 인수로 스프링 부트의 애플리케이션의 메인 클래스를 작성하고 두 번째 인수로 커맨드 라인의 인수를 작성한다.</p>
<p><code>@SpringBootApplication</code> 어노테이션을 ctrl을 누른 상태로 클릭하면 아래와 같은 코드를 클래스를 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/2212d83e-0a60-440e-8a96-9d12cf2f4219/image.png" alt=""></p>
<h2 id="springbootconfiguration">@SpringBootConfiguration</h2>
<p><strong>스프링 부트 관련 설정을 나타내는 어노테이션이다.</strong> 스프링에서 사용하던 <code>@Configuration</code>을 상속해서 만든 것으로 개발자가 직접 사용하진 않는다.</p>
<h2 id="componentscan">@ComponentScan</h2>
<p><strong>개발자가 등록한 빈들을 읽고 등록하는 어노테이션이다.</strong> <code>@Component</code> 어노테이션을 가진 클래스들을 찾아서 전부 빈으로 등록하는 역할을 하는데 <code>@Component</code> 어노테이션을 감싸는 <code>@Configuration</code>, <code>@Repository</code>, <code>@Controller</code>, <code>@RestController</code>, <code>@Service</code> 어노테이션들도 포함한다.</p>
<h2 id="enableautoconfiguration">@EnableAutoConfiguration</h2>
<p><strong>스프링 부트에서 자동 구성을 활성화하기 위한 어노테이션</strong>으로 스프링 부트 서버가 실행될 때 스프링 부트의 메타 파일을 읽고 정의된 설정을 자동으로 구성하는 역할을 한다. spring.factories 파일의 클래스들이 해당 어노테이션을 사용할 때 자동 설정된다.</p>
<h3 id="💡-자동-구성">💡 <strong>자동 구성</strong></h3>
<p>스프링 부트는 서버를 시작할 때 구성 파일을 읽어와서 설정을 하는데(<code>@EnableAutoConfiguration</code> 이용) META-INF에 있는 spring.factories 파일에 자동 설정에 대한 내용이 담겨있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Result Maps collection does not contain value for Error]]></title>
            <link>https://velog.io/@lee_bb/Result-Maps-collection-does-not-contain-value-for-Error</link>
            <guid>https://velog.io/@lee_bb/Result-Maps-collection-does-not-contain-value-for-Error</guid>
            <pubDate>Sun, 30 Jul 2023 06:39:44 GMT</pubDate>
            <description><![CDATA[<h1 id="⚠️javalangillegalargumentexception-result-maps-collection-does-not-contain-value-for-">⚠️java.lang.IllegalArgumentException: Result Maps collection does not contain value for …</h1>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/1be92ea0-0986-4105-b5c8-72a0faaf3565/image.png" alt=""></p>
<p>프로젝트 진행 도중 위와 같은 에러가 발생했다.</p>
<p>스프링 동작 시에는 에러 없이 서버가 올라가다가 작업 중인 페이지를 불러오려고 MyBatis가 실행되면서 DB에 접근하려고 하니까 에러가 발생했다.</p>
<p><strong>mapper에서 resultMap이 알맞게 매핑 되지 않았을 때 발생하는 에러</strong>라서 내가 작성 중인 mapper만 확인했는데 매핑은 다 알맞게 되어있어서 인터넷에 검색해봐도 매핑 문제가 맞았다.</p>
<p>에러 메시지를 다시 보니까 내가 작업 중인 게 아닌 다른 객체 경로가 떠서 다른 mapper 파일을 뒤적거리다 보니 원인을 찾았다.</p>
<pre><code class="language-xml">&lt;!-- 에러 발생시킨 부분 --&gt;
&lt;select id=&quot;selectAllBooking&quot; resultMap=&quot;com.workit.booking.model.dto.Booking&quot;&gt;
        SELECT * FROM BOOKING ORDER BY BOOKING_DATE DESC
&lt;/select&gt;
&lt;!-- 반환 타입 수정한 부분 --&gt;
&lt;select id=&quot;selectAllBooking&quot; resultType=&quot;com.workit.booking.model.dto.Booking&quot;&gt;
        SELECT * FROM BOOKING ORDER BY BOOKING_DATE DESC
&lt;/select&gt;</code></pre>
<p>해당 com.workit.booking.model.dto.Booking는 resultType 속성으로 작성해줘야 하는데 resultMap으로 작성되어있어서 문제가 발생한 거였다.</p>
<p>그 외에도 MyBatis config.xml 파일에 mapper가 정의되어 있지 않거나 스펠링이 틀린 경우, Mapper에 정의된 namespace 명칭이 Application내에 중복될 경우 등 매칭을 제대로 시켜주지 않은 경우에 발생할 수 있으니 프로그램이 알아서 동작할 수 있도록 제대로 매핑시켜줬는지 잘 확인해줘야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring IoC, DI]]></title>
            <link>https://velog.io/@lee_bb/Spring-IoC-DI</link>
            <guid>https://velog.io/@lee_bb/Spring-IoC-DI</guid>
            <pubDate>Sat, 29 Jul 2023 16:16:35 GMT</pubDate>
            <description><![CDATA[<h1 id="iocinversion-of-controller">IoC(Inversion of Controller)</h1>
<p><span style='background-color: #fff5b1; color:black'><strong>IoC는 프로그램이 동작하는데 메소드나 객체의 호출 작업을 개발자가 아닌 프로그램이 작동하는 컨테이너에서 직접 관리하는 것을 말한다.</strong></span> Spring은 IoC 구조를 통해서 프로그램 동작 시 필요한 객체의 생성부터 생명 주기까지를 직접 수행한다.</p>
<p>단순하게 <strong>제어의 역전</strong>이라고 하며 객체의 의존성을 역전시켜서 객체 간이 결합도를 낮추고 유연한 코드를 작성할 수 있도록 한다. <strong>IoC 방식으로 프로그램이 동작하면 코드의 중복이 줄어 가독성을 높일 수 있고 유지 보수 또한 수월해진다는 장점이 있다.</strong></p>
<h2 id="ioc-컨테이너-역할">IoC 컨테이너 역할</h2>
<ol>
<li>객체의 생명주기와 의존성 관리</li>
<li>VO(DTO, POJO) 객체의 생성, 초기화, 소멸 등 처리를 담당</li>
<li>개발자가 직접 객체를 생성할 수 있지만 권한을 컨테이너 넘김으로써 소스 코드 주현 시간을 단축</li>
</ol>
<h3 id="bean">Bean</h3>
<p><strong>스프링이 IoC 방식으로 관리하는 클래스들을 의미한다.</strong> 빈은 스프링이 직접 생성과 제어를 담당하게 된다.</p>
<h3 id="bean-factory">Bean Factory</h3>
<p>스프링의 IoC를 담당하는 핵심이 되는 컨테이너로 Bean을 등록, 생성, 조회, 반환하는 기능을 담당한다. <strong>Bean들을 관리하는 역할을 담당</strong>하는 컨테이너라고 할 수 있다. 사용할 Bean을 구하는 메소드로<code>getBean()</code>를 제공한다. 등록된 Bean 객체를 반환하며 첫 번째 파라미터로 Bean의 이름, 두 번째 파라미터로 Bean의 타입을 작성할 수 있고 둘 중 하나만 작성해도 해당하는 Bean을 반환한다.</p>
<h3 id="applicationcontext">ApplicationContext</h3>
<p><strong>BeanFactory를 확장한 IoC 컨테이너</strong>로 Bean을 등록하고 관리하는 기능은 BeanFactory와 동일하지만 스프링이 제공하는 각종 부가 서비스를 추가로 제공한다. BeanFactory 인터페이스를 구현한 최상위 구현체이다.</p>
<h3 id="genericxmlapplicationcontext">GenericXmlApplicationContext</h3>
<p>ApplicationContext를 구현한 클래스로 일반적인 xml 형태의 문서를 읽어서 해당 문서에 등록된 Bean들을 관리하는 컨테이너 역할을 한다.</p>
<pre><code class="language-java">GenericXmlApplicationContext context
    =new GenericXmlApplicationContext(&quot;classpath:applicatonContext.xml&quot;);
Student student=context.getBean(&quot;student&quot;,Student.class);
//해당 이름의 Bean을 해당 클래스 타입으로 반환

Student student=context.getBean(Student.class);
//해당 클래스 타입의 Bean을 반환

Student student=(Student)context.getBean(&quot;student&quot;);
//해당 이름의 Bean을 Object 타입으로 반환</code></pre>
<h3 id="configuration-metadata">Configuration metadata</h3>
<p>ApplicationContext 혹은 BeanFactory가 IoC를 적용하기 위해서 사용하는 설정 정보들을 말한다. Configuration metadata(설정 메타 정보)는 IoC 컨테이너에 의해 관리되는 Bean 객체를 생성하고 구성할 때 사용한다.</p>
<h1 id="spring-di의존성-주입">Spring DI(의존성 주입)</h1>
<p>DI(Dependency Injection)은 IoC 구현의 핵심 기술이라고 할 수 있는데 사용하는 객체를 직접 생성해서 만드는 것이 아니라 <span style='background-color: #fff5b1; color:black'><strong>컨테이너가 Bean의 설정 정보를 읽어와서 자동으로 해당 객체에 연결하는 것을 의미한다.</strong></span></p>
<p>즉 등록된 Bean들을 필요한 시점에 저장해두는 것을 말하는데 이렇게 의존성 주입을 하게 되면 해당 객체를 수정해야 할 상황이 생겼을 때 소스 코드의 수정을 최소화할 수 있게 된다.</p>
<h2 id="di의-장점">DI의 장점</h2>
<ol>
<li>개발자가 작성해야 할 코드가 단순해진다.</li>
<li>객체 간의 결합도(종속 관계)가 낮아지면서 유연성이 높아진다.</li>
</ol>
<h2 id="spring-di-종류">Spring DI 종류</h2>
<h3 id="setter-메소드">Setter 메소드</h3>
<p>의존성 주입을 받는 Setter 메소드를 만들고 이를 통해서 의존성 주입한다. Setter 메소드를 통해서 의존 관계가 있는 Bean을 주입하기 위해서는 <code>&lt;property&gt;</code>태그를 이용할 수 있다.</p>
<pre><code class="language-xml">&lt;bean id=&quot;ref에 들어갈 이름&quot; class=&quot;주입할 객체(패키지명 포함)&quot;/&gt;
&lt;bean id=&quot;객체 이름&quot; class=&quot;Bean으로 등록할 클래스(패키지명 포함)&quot;&gt;
    &lt;property name=&quot;사용할 이름&quot; value=&quot;이름에 들어갈 값&quot;/&gt;
    &lt;property name=&quot;사용할 이름&quot; ref=&quot;의존성 주입할 객체&quot;/&gt;
&lt;bean&gt;</code></pre>
<pre><code class="language-xml">&lt;!-- example --&gt;
&lt;beans:bean id=&quot;dept&quot; class=&quot;com.bs.spring.beantest.Department&quot;/&gt;
&lt;beans:bean id=&quot;emp&quot; class=&quot;com.bs.spring.beantest.Employee&quot; 
         init-method=&quot;initialMethod&quot; destroy-method=&quot;destroyMethod&quot;&gt;
     &lt;beans:property name=&quot;name&quot; value=&quot;Lee&quot;/&gt;
     &lt;beans:property name=&quot;age&quot; value=&quot;26&quot;/&gt;
     &lt;beans:property name=&quot;address&quot; value=&quot;서울&quot;/&gt;
     &lt;beans:property name=&quot;salary&quot; value=&quot;3000000&quot;/&gt;
     &lt;beans:property name=&quot;dept&quot; ref=&quot;dept&quot;/&gt;
&lt;/beans:bean&gt;</code></pre>
<h3 id="생성자-이용">생성자 이용</h3>
<p>필요한 의존성을 포함하는 클래스에 생성자를 만들어놓고 이를 통해서 의존성을 주입한다. <code>&lt;constructor-arg&gt;</code> 태그를 이용해서 의존성을 주입할 수 있고 해당 방식은 생성자의 파라미터를 이용하기 때문에 한 번에 여러개의 객체 주입이 가능하다. 필드 선언 순서에 따라 index 속성을 통해서도 접근할 수 있다.</p>
<pre><code class="language-xml">&lt;bean id=&quot;ref에 작성할 이름&quot; class=&quot;해당 객체 이름(패키지 포함)&quot;/&gt;
&lt;bean id=&quot;불러올 객체 이름&quot; class=&quot;클래스명(패키지 포함)&quot;&gt;
    &lt;constructor-arg index=&quot;0&quot; value=&quot;값&quot;/&gt;
    &lt;constructor-arg index=&quot;1&quot; ref=&quot;주입할 객체&quot;/&gt;
&lt;/bean&gt;</code></pre>
<pre><code class="language-xml">&lt;!-- example --&gt;
&lt;beans:bean id=&quot;dept&quot; class=&quot;com.bs.spring.beantest.Department&quot;&gt;
     &lt;beans:constructor-arg index=&quot;0&quot; value=&quot;1&quot;/&gt;
     &lt;beans:constructor-arg index=&quot;1&quot; value=&quot;개발부&quot;/&gt;
     &lt;beans:constructor-arg index=&quot;2&quot; value=&quot;판교&quot;/&gt;
&lt;/beans:bean&gt;
&lt;beans:bean id=&quot;emp2&quot; class=&quot;com.bs.spring.beantest.Employee&quot;&gt;
     &lt;beans:constructor-arg index=&quot;0&quot; ref=&quot;dept&quot;/&gt;
&lt;/beans:bean&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[fork, clone]]></title>
            <link>https://velog.io/@lee_bb/fork-clone</link>
            <guid>https://velog.io/@lee_bb/fork-clone</guid>
            <pubDate>Tue, 25 Jul 2023 15:49:01 GMT</pubDate>
            <description><![CDATA[<h1 id="fork-clone">fork? clone?</h1>
<p>git에서 프로젝트를 복사해서 가져오는 방법에는 두 가지가 존재한다. 어떤 경우에는 그냥 프로젝트를 clone해서 사용하기도 하고 어떤 경우에는 fork해서 사용하기도 하는데 그 차이점을 구분해두면 좋을 거 같아서 정리를 하게 되었다.</p>
<h1 id="fork">Fork</h1>
<p>우선 <span style='background-color: #fff5b1; color:black'><strong>fork는 원본 레포지토리에서 내 레포지토리로 그대로 복제하는 것인데, 작업하는 파일을 그대로 복사해서 가져오면서 원본과 연결된다.</strong></span> 때문에 원본 작업 파일의 변화를 알 수 있고 변화 내용을 내 작업에 반영도 가능해진다.</p>
<p>원본 레포지토리에서 버전이 변경되면(commit, push가 발생하면) 그대로 fork 받은 내 레포지토리에 반영할 수 있게 할 때는 fetch, pull의 과정이 필요하고 나의 레포지토리에서 버전이 변경되는 경우에 원본 레포지토리에 변경된 버전을 반영하고 싶을 땐 PR을 보내야 한다. PR이 승인 되면 내가 commit, push한 과정이 원본 레포지토리에 merge 되어 적용된다.</p>
<blockquote>
<p>PR(pull request) 설명 및 사용 방법 참고</p>
<p><a href="https://velog.io/@lee_bb/4mj3zkea">Pull Request</a></p>
</blockquote>
<h2 id="github에서-fork-하기">Github에서 fork 하기</h2>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/ecfdbb1a-1719-4718-86f2-a7252072d88d/image.png" alt=""></p>
<p>fork 받을 원본 레포지토리에서 상단에 보이는 Fork 버튼을 눌러준다.</p>
<p>버튼을 누르면 fork받을 레포지토리를 지정할 수 있도록 하는데 지정할 레포지토리가 없다면 Create a new fork를 눌러주면 된다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/67af5042-858d-4d2b-8a3f-c91998d5d6f3/image.png" alt=""></p>
<p>버튼을 눌렀다면 레포지토리 이름을 지정하고(보통 자동으로 원본 레포지토리 명이 따라오지만 수정 가능하다) Create fork 버튼을 눌러주면 지정한 레포지토리에 원본 레포지토리의 프로젝트가 복사된다.</p>
<p>복사된 프로젝트는 원격 저장소에서만 복사된 상태로 로컬 컴퓨터에 파일을 받아서 이클립스 등에서 사용하기 위해서는 아래 이클립스로 clone 하기를 참고한다.</p>
<h2 id="터미널로-fork-하기">터미널로 fork 하기</h2>
<p><strong>우선 git에는 fork 명렁어가 없기 때문에 명령어로 fork를 하려면 clone, remote, add, commit, push 명령어를 함께 사용해야 한다.</strong></p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/123a0d11-4d5e-43f4-ad46-c5588fdb1154/image.png" alt=""></p>
<p><strong>먼저 로컬 터미널에서 <code>git clone 복사할 레포지토리 주소</code> 명령어를 작성하여 원본 파일을 복사한다.</strong> 주소는 위 사진처럼 원본 레포지토리에 들어가서 Code 버튼을 누르고 아래 주소를 복사해서 붙여넣기 하면 된다.</p>
<p>💡 이때 clone을 하면 default 브랜치만 불러와서 다른 브랜치를 볼 수 없게 된다. 다른 브랜치를 확인하고 싶으면 <code>git branch -a</code> 명령어를 입력하여 원격 저장소에 있는 모든 브랜치를 확인할 수 있다.</p>
<p><strong>복사가 완료되면 <code>git remote add upstream 원본 레포지토리 주소</code>를 실행한다.</strong> 로컬 기준으로 보고 터미널에서 작업하고 있기 때문에 upstream은 원본을 만들어낸 상류 저장소로 원본 레포지토리가 된다.</p>
<p><strong>복사한 파일에서 작업 후 add, commit 명령어를 실행해서 내 컴퓨터에 작업된 내용에 대해 버전을 업그레이드 한다.</strong> <code>git add 저장할 파일들</code>을 실행해서 commit할 파일들을 추가하고 <code>git commit -m “커밋 메시지”</code>를 실행한다.</p>
<p>이제 <strong>내 레포지토리에 업그레이드 된 버전의 파일을 올리기 위해서 push 작업을 진행하기 위해 <code>git push 원격저장소명 브랜치명</code>을 실행한다.</strong></p>
<p>💡 보통 <code>clone</code>을 사용해서 복제해온 원격 저장소 이름은 <code>origin</code>으로 지정되고 <code>git remote</code> 명령어를 실행해서 원격 저장소 이름을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/lee_bb/post/10f4ea25-67cc-4cbe-a382-ade514126fe6/image.png" alt=""></p>
<h1 id="clone">Clone</h1>
<p><span style='background-color: #fff5b1; color:black'><strong>clone은 특정 레포지토리에서 내 로컬 저장소를 연결해 데이터를 복사해서 가져오는 것을 말한다.</strong></span> 내 레포지토리를 생성하여 clone으로 복사한 작업 파일을 내 레포지토리에 올려서 사용할 수 있다. <strong>원본 레포지토리인 곳과는 연결되지 않았으므로 PR를 보내거나 원본 레포지토리의 작업 내용을 알 수 없다.</strong> </p>
<p>clone하는 방법은 fork보다 상대적으로 간단하다. Github에서 clone 받을 때는 특정 레포지토에 들어가 Code 버튼을 눌러서 주소를 복사한 뒤 IDE(이클립스 등)나 터미널에서 해당 주소를 가지고 프로젝트를 복사해서 사용하면 된다.</p>
<h2 id="이클립스에서-clone-하기">이클립스에서 clone 하기</h2>
<p>IDE에서 복사한 주소가지고 프로젝트를 임포트 할 수 있다. Project Explorer에서 마우스 우클릭&gt;import&gt;Projects from Git(검색창에 git을 치면 바로 나온다) 를 실행한다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/c0d69e7e-c29b-47f5-95f3-4b1e62eb5c75/image.png" alt=""></p>
<p>누르면 위와 같은 화면이 뜨는데 Clone URI를 눌러준다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/980c370d-0b44-4a0a-9741-6c871d4649e2/image.png" alt=""></p>
<p>해당 화면이 뜨면 복사한 주소를 URI에 붙여넣기 하면 Host나 Repository path는 자동으로 들어간다. 아래 유저와 비밀번호가 자동으로 뜨지 않는다면 토큰 발급 받은 후 로컬 컴퓨터에서 인증하고 나면 다음부터는 자동으로 적용된다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/be560cc3-459b-416c-8479-fc19695f5bdb/image.png" alt=""></p>
<p>다음으로 넘기면 위와 같은 화면이 뜨는데 복사할 브랜치를 선택하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/da43aa0b-e384-4ec6-ae64-28a02c894f9a/image.png" alt=""></p>
<p>위는 복사된 프로젝트 파일을 로컬 컴퓨터에 저장할 경로를 지정하는 것으로 자동으로 git 폴더 내부에 위치가 잡히긴 하지만 원하면 Browse 버튼을 클릭해서 위치를 변경할 수 있다.</p>
<p>💡 이때도 clone된 원격 저장소 이름은 기본적으로 origin으로 셋팅된다.</p>
<p><img src="https://velog.velcdn.com/images/lee_bb/post/23af6110-1f02-4eb9-a948-d3bae54ef308/image.png" alt=""></p>
<p> * 원래 위처럼 하나의 레포지토리 안에 여러 개의 프로젝트를 관리하는 일은 없겠지만…😅  만약 프로젝트가 여러개 존재한다면 원하는 프로젝트 폴더만 선택해서 받을 수 있다.</p>
<p>Finish 버튼을 누르면 clone이 완료된다.</p>
<p>터미널에서 clone하는 방법은 fork 방법과 비슷하지만 remote 명렁어만 제외시키면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Pull Request]]></title>
            <link>https://velog.io/@lee_bb/4mj3zkea</link>
            <guid>https://velog.io/@lee_bb/4mj3zkea</guid>
            <pubDate>Mon, 24 Jul 2023 07:16:23 GMT</pubDate>
            <description><![CDATA[<h1 id="pull-request">Pull Request</h1>
<p><strong>pull request는 내가 작업한 새로운 버전 즉 소스 코드 작성 후 commit 했을 경우 내 브런치를 당겨(pull) 달라는 요청을 보내는 것이다.</strong> 줄여서 PR이라고 부른다.</p>
<p>보통은 개인 레포지토리(Repository)에 팀 레포지토리의 프로젝트를 <code>Fork</code> 받아서 협업하는 프로젝트에 대해서 개인이 작업하고 있는 기능을 구현하다가 팀 레포지토리에 <code>pull request</code>를 하는 방식으로 진행된다.</p>
<h2 id="각각의-repository에서-작업">각각의 Repository에서 작업</h2>
<ol>
<li><p><strong>타켓이 되는 팀 프로젝트의 레포지토리에서 프로젝트를 개인 레포지토리로 <code>Fork</code> 받는다.</strong></p>
<p> <img src="https://velog.velcdn.com/images/lee_bb/post/34430f9a-f931-449e-ad6e-1c3cb8fd0c54/image.png" alt=""></p>
</li>
</ol>
<pre><code>`Fork`를 누르면 `new repository` 버튼을 누를 수 있고 해당 버튼을 누르면 repository 생성 화면으로 넘어간다.


💡 **git에는 `Fork` 명령어가 없다.** 때문에 cli에서 한 번에 실행할 수 없고 필요하다면 clone해서 프로젝트를 본인 레포지토리에 복사한 다음 팀 레포지토리에 연결할 수 있도록 따로 지정해야 한다.</code></pre><ol start="2">
<li><p><strong>개인 레포지토리에서 작업할 개인 브런치 생성</strong></p>
<p> 프로젝트 폴더에서 마우스 우클릭&gt;Team&gt;Switch To&gt;New Branch에서 생성한다.</p>
</li>
<li><p><strong>버전 업그레이드 후 <code>pull request</code></strong></p>
<p> 로컬 저장소에서 작업 후 <code>commit</code>, 개인 레포지토리에 <code>push</code> 하고 난 뒤 본인 계정의 github 저장소(remote repository)에 들어오면 <code>Compare &amp; pull request</code> 버튼이 활성화 된다. 해당 버튼 클릭 후 PR 메시지를 작성하고 PR을 생성하면 요청이 팀 레포지토리에 넘어간다.</p>
</li>
<li><p><strong>팀 레포지토리에서 요청 확인 후 <code>Merge</code> 여부 확인</strong></p>
<p> PR을 받은 팀 레포지토리(원본 저장소) <strong>관리자는 코드의 변경 내역을 확인 한 뒤 <code>Merge</code> 여부를 결정한다.</strong> Merge가 완료되면 개인 레포지토리와 팀 레포지토리의 코드를 동기화 한다.</p>
</li>
</ol>
<h2 id="하나의-repository에서-작업">하나의 Repository에서 작업</h2>
<ol>
<li><p><strong>메인 브런치가 생성된 팀 레포지토리를 생성한다.</strong></p>
</li>
<li><p><strong>각 팀원이 레포지토리의 프로젝트를 <code>pull</code> 받고 개별 브런치를 생성한다.</strong></p>
<p> 프로젝트 폴더에서 마우스 우클릭&gt;Team&gt;Switch To&gt;New Branch에서 생성한다.</p>
</li>
<li><p><strong>버전 업그레이드 후 <code>pull request</code></strong></p>
<p> 개인 브런치에서 작업 후 버전 업그레이드(commit) 하고 개별 브런치에 <code>push</code>한다. 해당 브런치로 접속 후 PR 버튼을 클릭해서 요청을 보낸다. (보통 default 브런치를 메인 브런치로 사용하고 이름을 dev라고 정한다 사진은 defaul 브런치가 따로 있고 dev 브런치가 메인 브런치로 사용 중이었을 때)</p>
<p> <img src="https://velog.velcdn.com/images/lee_bb/post/d0030a65-78e4-492b-b9ea-fe360cca86be/image.png" alt=""></p>
</li>
</ol>
<ol start="4">
<li><p><strong>레포지토리의 관리자가 요청 확인 후 <code>Merge</code> 여부 확인</strong></p>
<p> PR을 받은 레포지토리 관리자는 코드의 변경 내역을 확인 한 뒤 <code>Merge</code> 여부를 결정한다. Merge가 완료되면 개인 레포지토리와 팀 레포지토리의 코드를 동기화 한다.</p>
</li>
</ol>
<blockquote>
<p>git 관련 용어 정리</p>
<p><a href="https://velog.io/@lee_bb/git-github">git? github?</a></p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>