<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>liy_se00.log</title>
        <link>https://velog.io/</link>
        <description>개발공부하는 잠만보</description>
        <lastBuildDate>Fri, 18 Jul 2025 10:07:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>liy_se00.log</title>
            <url>https://velog.velcdn.com/images/liy_se00/profile/3653811d-d749-4be2-854a-9b0891dbc8e4/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. liy_se00.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/liy_se00" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[자바웹개발 워크북] 2. 웹과 데이터베이스]]></title>
            <link>https://velog.io/@liy_se00/%EC%9E%90%EB%B0%94%EC%9B%B9%EA%B0%9C%EB%B0%9C-%EC%9B%8C%ED%81%AC%EB%B6%81-2.-%EC%9B%B9%EA%B3%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@liy_se00/%EC%9E%90%EB%B0%94%EC%9B%B9%EA%B0%9C%EB%B0%9C-%EC%9B%8C%ED%81%AC%EB%B6%81-2.-%EC%9B%B9%EA%B3%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Fri, 18 Jul 2025 10:07:46 GMT</pubDate>
            <description><![CDATA[<h2 id="21-jdbc-프로그래밍-준비">2.1 JDBC 프로그래밍 준비</h2>
<hr>
<blockquote>
<p>DB 종류: MSSQL, MySQL, MariaDB, Postgresql, Oracle DB....
이 책에서는 MariaDB를 사용합니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/d6cbff33-e6e9-4311-9268-4437dc33af56/image.png" alt=""></p>
<p>mariadb의 포트 설정 부분에서 <code>3306</code>으로 기본 설정이 되어있는데, 나는 워낙 컴퓨터에 깔려있는게 많으므로 <code>3306</code>포트를 사용하고있는 db가 있는지 확인해주었다.</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/2aa1509e-ecba-42a8-9708-246b56c80344/image.png" alt=""></p>
<p>터미널에서 조회해보니, MySQL 서버가 점유 중이라고 뜬다.</p>
<p>따라서, mariadb는 <code>3307</code>로 설정해주었다.</p>
<pre><code class="language-java">public class ConnectTest {

    @Test
    public void testConnection() throws Exception {

        Class.forName(&quot;org.mariadb.jdbc.Driver&quot;);

        Connection connection = DriverManager.getConnection(
            &quot;jdbc:mariadb://localhost:3307/webdb&quot;,
            &quot;webuser&quot;,
            &quot;webuser&quot;);

        Assertions.assertNotNull(connection);

        connection.close();
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바웹개발 워크북] 1. 웹 프로그래밍의 시작]]></title>
            <link>https://velog.io/@liy_se00/%EC%9E%90%EB%B0%94-%EC%9B%B9%EA%B0%9C%EB%B0%9C-%EC%9B%8C%ED%81%AC%EB%B6%81-1.-%EC%9B%B9-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%98-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@liy_se00/%EC%9E%90%EB%B0%94-%EC%9B%B9%EA%B0%9C%EB%B0%9C-%EC%9B%8C%ED%81%AC%EB%B6%81-1.-%EC%9B%B9-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%98-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Thu, 10 Jul 2025 10:27:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<p>1.1 자바 웹 개발 환경 만들기
1.2 웹 기본 동작 방식 이해하기
1.3 Web MVC 방식
1.4 HttpServlet
1.5 모델(Model)</p>
<h2 id="11-자바-웹-개발-환경-만들기">1.1 자바 웹 개발 환경 만들기</h2>
<hr>
<h3 id="📌span-stylebackground-colorb5f9c0jspjava-server-pagespan">📌<span style="background-color:#B5F9C0">JSP(Java Server Page)</span></h3>
<ul>
<li>JSP 파일은 Java Server Pages의 약자로, 동적인 웹 페이지를 만들기 위해 HTML 코드 내에 Java 코드를 삽입하여 사용하는 기술입니다.</li>
<li>JSP 활용 시 <code>&lt;% %&gt;</code>를 사용하면 HTML문서 중간에 Java코드를 삽입하여 동적인 코드로 변경이 가능</li>
</ul>
<br>
<br>

<h2 id="12-웹-기본-동작-방식-이해하기">1.2 웹 기본 동작 방식 이해하기</h2>
<hr>
<blockquote>
</blockquote>
<p>🔸브라우저와 서버의 관계 이해
🔸서블릿과 JSP가 브라우저에 데이터를 전달하는 과정, 
🔸브라우저에서 사용되는 과정</p>
<h3 id="📌-request요청response응답">📌 Request(요청)/Response(응답)</h3>
<hr>
<p>브라우저에 원하는 정보를 전달하는 방식 GET, POST</p>
<p><strong>GET</strong>
주소와 필요한 데이터를 한번에 같이 보내기 때문에 단순 링크로 처리된다.
ex) <a href="https://velog.io/@liy_se00/posts?tag=000000">https://velog.io/@liy_se00/posts?tag=000000</a></p>
<p><strong>POST</strong>
주소와 데이터를 따로 보내는 방식.
ex) /api/members/news/{newsId}/read</p>
<br>

<p>✨<em><strong>브라우저와 서버의 데이터 전달 과정</strong></em></p>
<ol>
<li>브라우저에서 데이터를 요구 = 요청(Request)</li>
<li>서버는 이에 대한 응답(Response) 데이터를 만들어 브라우저로 보낸다.</li>
<li>서버에서는 이를 정적인 데이터인지 동적인 데이터인지에 따라 다르게 처리.</li>
</ol>
<ul>
<li><span style="background-color:#B5F9C0">웹 서버(Web Server)</span>: 항상 같은 정적 데이터만 보내는 역할만을 수행하는 서버</li>
<li><span style="background-color:#B5F9C0">웹 애플리케이션 서버(Web Application Server; <strong>WAS</strong>)</span>: 동적 데이터를 만들어 보내는 경우의 서버. 대부분의 WAS는 웹 서버 기능도 같이 포함하므로 이미지, CSS/JS, 서블릿/JSP 모두 처리 가능.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/b1a5cfdc-29e6-4fdb-9017-0d6862117e82/image.png" alt=""></p>
<br>

<h3 id="📌-httphyper-text-transfer-protocol">📌 HTTP(Hyper Text Transfer Protocol)</h3>
<hr>
<p>✨<strong>프로토콜(protocol)</strong> : 브라우저의 요청과 서버의 응답 사이에서 한 데이터 교환 약속. 웹에서는 <strong>HTTP</strong>나 보안이 강화된 프로토콜인 <strong>HTTPS</strong> 프로토콜 방식으로 데이터를 주고받는다.</p>
<p><span style="color:gray">개발자도구 -&gt; Network -&gt; 오가는 데이터 확인 가능(Headers, Response, Cookies...)</span></p>
<br>

<p>✨<strong>비연결성(Connectionless)</strong>
웹의 특성상 여러 사용자가 브라우저를 통해 서버를 호출하는 구조이기 때문에 서버에서는 최대한 많은 사용자에게 서비스를 제공하기 위한 고민이 필요하다.
-&gt; HTTP는 이를 위해 &#39;비연결성&#39; 방식을 택하는데, 이는 <span style="background-color:#B5F9C0">요청과 응답을 처리한 후 연결을 종료하는 것</span>이다. ex) 새로고침</p>
<br>

<h3 id="📌-자바-서버-사이드-프로그래밍">📌 자바 서버 사이드 프로그래밍</h3>
<hr>
<p><strong>서버 사이드 프로그래밍</strong>: 서버 쪽에서 프로그래밍을 통해 데이터를 처리할 수 있도록 구성하는 것을 의미</p>
<ul>
<li>동시에 여러 요청 어떻게 처리?</li>
<li>서버 문제 발생 시 어떻게 처리?</li>
<li>어떤 방식으로 데이터 전송 최적화?</li>
<li>분산 환경이나 분산 처리 문제?</li>
</ul>
<p>자바의 경우 위와 같은 처리를 <strong>JavaEE</strong>라는 기술 스펙으로 정리해 두었고, <span style="background-color:#B5F9C0"><strong>Servlet과 JSP는 JavaEE의 여러 기술 중 가장 기본적인 기술이다.</strong></span></p>
<br>

<h3 id="-servlet-">[ Servlet ]</h3>
<p>✨<strong>서블릿(Servlet) 기술</strong>
서버에서 동적으로 요청과 응답을 처리할 수 있는 API들을 정의한 것</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/4836b2d6-0ead-431f-b5e1-4d950cc316b2/image.png" alt=""></p>
<blockquote>
<p>🔸<strong>톰캣이란(Tomcat)?</strong>
톰캣은 아파치 소프트웨어 재단의 웹 어플리케이션 서버(와스)로서, 자바 서블릿을 실행키고 JSP코드가 포함되어 있는 웹 페이지를 만들어준다.
출처: <a href="https://cheershennah.tistory.com/54">https://cheershennah.tistory.com/54</a></p>
</blockquote>
<p>JSP는 근본적으로 서블릿과 같은 원리이지만 좀 더 HTML을 쉽게 이용할 수 있는 방식으로 코드를 작성할 수 있기 때문에 &#39;<strong>서블릿으로는 코드를 이용한 처리, JSP로는 화면 개발</strong>&#39;과 같이 역할을 분담해서 개발하는 것이 일반적이다.</p>
<p><span style="background-color:#B5F9C0"><strong>서블릿 컨테이너</strong></span>: 서블릿의 실행은 서블릿을 실행할 수 있는 환경에서 실행이 가능하다.(톰캣) 이를 &#39;서블릿 컨테이너&#39;라 부르며 과거에는 &#39;서블릿 엔진&#39;이라고 불렀다.</p>
<p>서블릿 코드를 실행하는 주체는 서블릿 컨테이너(톰캣)이므로, 일반 자바 프로그램과 아래와 같은 점들이 달라진다.</p>
<ul>
<li>객체 생성, 호출 주체는 서블릿 컨테이너</li>
<li>객체의 관리 자체가 서블릿 컨테이너에 의해 관리</li>
<li><em>서블릿/JSP 코드 개발은 기본적인 자바 API와 더불어 서블릿 API도 같이 사용해야 한다.</em>
ex) 서블릿 클래스를 보면 <code>import</code>에 <code>javax</code>로 시작하는 서블릿 관련 API가 <code>java.io.*</code>와 함께 사용된 것을 볼 수 있다.</li>
</ul>
<p>서블릿 라이프 사이클: <code>init()</code>, <code>doGet()</code>, <code>destory()</code>등 서블릿 API에서 지정된 메소드들을 서블릿 컨테이너가 호출하고 관리하는 것</p>
<br>

<h3 id="-jsp-">[ JSP ]</h3>
<p><strong>✨ JSP 기술</strong>
서블릿이 있는데도 JSP가 제공되는 이유?
👉🏻 두 기술의 목적 자체가 다르기 때문</p>
<ul>
<li>JSP 기술은 서블릿과 달리 HTML 코드를 그대로 이요하고 필요할 때 약간의 자바 코드를 넣음</li>
<li>서블릿은 자바 코드를 이용해여 HTML 문자열을 만들어내는 방식의 차이</li>
</ul>
<p>JSP 파일도 서블릿 코드로 변환되어 컴파일되고 실행된다. JSP 파일은 필요한 순간에 자바 파일로 생성되고, 컴파일하여 class 파일로도 만들어진다. 
HTML <code>&lt;head&gt;</code> 코드 -&gt; 모두 <code>out.write(&quot;&lt;head&gt;\n&quot;);</code> 형태의 코드로 변환된다.</p>
<br>

<p>🔸<strong>서블릿/JSP 공통점</strong></p>
<ul>
<li>모두 JavaEE 스팩의 일부</li>
<li>실행하기 위해서 서블릿 컨테이너가 필요함</li>
<li>서블릿 컨테이너가 객체를 생성하고 생명주기 관리</li>
</ul>
<br>

<p>✨<strong>JSP 특징</strong></p>
<ul>
<li><code>${}</code>: JSP에서 사용하는 EL 기술. 서버에서 데이터를 출력하는 용도</li>
<li>웹의 파라미터는 모두 문자열 -&gt; 숫자 계산을 위해서는 <code>Integer.parseInt()</code> 적용</li>
<li>JSP는 기본적으로 GET/POST 방식의 호출을 구분하지 않는다.</li>
</ul>
<p>JSP 올바른 사용법</p>
<ul>
<li>JSP에서 쿼리 스트링이나 파라미터를 처리하지 않는다.
  👉🏻 JSP 대신 서블릿으로 처리</li>
<li>입력 화면을 구성하거나 처리 결과를 보여주는 용으로만 사용</li>
<li>서블릿 경로를 통해 JSP를 보는 방식으로 사용</li>
<li>JSP는 직접 호출하지 않고 Controller를 통해서만 JSP에 접근하도록 구성</li>
<li><strong>JSP는 결과만/처리는 서블릿</strong></li>
</ul>
<br>

<p><em>-&gt; 이러한 문제를 해결하기 위해 등장한 것이 웹 MVC</em></p>
<h2 id="13-web-mvc-방식">1.3 Web MVC 방식</h2>
<hr>
<h3 id="--mvc-구조와-서블릿jsp">- MVC 구조와 서블릿/JSP</h3>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/c58f1c8e-79f7-44d5-9333-8a1813c0aa23/image.png" alt=""></p>
<p>요청이 들어오면
-&gt; 서블릿은 준비한 데이터를 JSP로 전달
-&gt; JSP는 EL을 이용해 최종적인 결과 데이터 생성
-&gt; 생성된 결과 화면은 톰캣을 통해 브라우저로 전송
<br></p>
<h3 id="📌-span-stylebackground-colorb5f9c0웹-mvc-구조span">📌 <span style="background-color:#B5F9C0"><strong>웹 MVC 구조</strong></span></h3>
<p>: <strong>Model - View - Controller</strong> 역할을 분리해서 처리하는 구조</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/6d528929-74ec-40fc-9686-0399d2910f35/image.png" alt=""></p>
<ul>
<li><strong>Controller</strong>: 데이터 처리(서블릿)</li>
<li><strong>View</strong>: 결과 처리(JSP)</li>
<li><strong>Model</strong>: JSP에 필요한 데이터를 가공하는 역할을 하는데 필요한 데이터를 제공하는 객체<br>

</li>
</ul>
<p><strong>- <code>RequestDispatcher</code>를 이용한 요청(Request) 배포</strong></p>
<pre><code class="language-java">@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
        ServletException, IOException {
        System.out.println(&quot;InputController...doGet...&quot;);
        RequestDispatcher dispatcher = req.getRequestDispatcher(&quot;/WEB-INF/calc/input.jsp&quot;);
        dispatcher.forward(req, resp);
    }</code></pre>
<blockquote>
<p>👉🏻 <strong>WEB-INF 란?</strong>
<code>WEB-INF</code>는 브라우저에서 직접 접근이 불가능한 특별한 경로입니다. <code>WEB-INF</code> 밑에 jsp 파일을 둔다는 것은 브라우저에서 jsp로 &quot;직접&quot; 호출이 불가능하다는 것을 의미합니다.(따라서 jsp 파일이 호출될 때는 controller를 필수적으로 거치게 됩니다.)</p>
</blockquote>
<p>✨<strong><code>RequestDispatcher</code></strong></p>
<ul>
<li>서블릿에 전달된 요청을 다른 쪽으로 전달 혹은 배포한다. </li>
<li><code>RequestDispatcher</code>를 이용하면 InputController는 jsp파일로 가는 중간 경유지가 됩니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/96efbec9-edd7-4633-8738-df2dd7f9b8e2/image.png" alt=""></p>
<p>✨** <code>reponse.sendRedirect(&quot;/....&quot;)</code> **</p>
<ul>
<li>POST 방식으로 처리하고 JSP를 이용해서 결과를 보여주는 방식을 이용할 때 HttpServletResponse의 sendRedirect()로 처리가 끝난 후에 다른 경로로 이동하게 하는 것이 일반적이다.</li>
</ul>
<br>

<h3 id="📌-span-stylebackground-colorb5f9c0prg-패턴post-redirect-getspan">📌 <span style="background-color:#B5F9C0">PRG 패턴(Post-Redirect-GET)</span></h3>
<blockquote>
<p>PRG 패턴은 POST 방식의 처리 후에 바로 다른 주소로 브라우저가 이동하기 때문에 반복적으로 POST 호출이 되는 상황을 막을 수도 있습니다.</p>
</blockquote>
<ul>
<li>웹 MVC 구조에서 가장 흔하게 사용하는 패턴</li>
<li>사용자는 컨트롤러에 원하는 작업을 POST 방식으로 처리하기를 요청</li>
<li>POST 방식을 컨트롤러에서 처리 -&gt; 브라우저는 다른 경로로 이동(GET)하라는 응답(Redirect)</li>
<li>브라우저는 GET 방식으로 이동</li>
</ul>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/0e9b4f88-5c59-445a-8463-490c5009afee/image.png" alt=""></p>
<h2 id="14-httpservlet">1.4 HttpServlet</h2>
<hr>
<p>Servlet 클래스의 상속 구조</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/de22f783-2326-45d8-8794-96de33c1253b/image.png" alt=""></p>
<h3 id="httpsevletrequest의-주요-기능">HttpSevletRequest의 주요 기능</h3>
<p>주로 &#39;읽는&#39; 기능 제공</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/ca1123ab-e476-47aa-b098-2dd35c9c4156/image.png" alt=""></p>
<p>🔹 <strong><code>getParameter()</code></strong></p>
<ul>
<li><code>?name=AAA&amp;age=20</code>과 같은 쿼리 스트링에서 키를 이용해 값을 얻을 때 사용</li>
<li><code>getParameter()</code> 결과값은 항상 String </li>
<li>문자열로 반환되기 때문에 숫자를 처리할 때의 예외 발생을 주의해야 한다.</li>
<li>해당 파라미터가 존재하지 않는다면 null 반환 주의</li>
</ul>
<p>🔹 <strong><code>getParameterValues()</code></strong></p>
<ul>
<li>동일한 이름의 파라미터가 여러 개 있는 경우에 사용</li>
<li>String[] 타입으로 반환</li>
</ul>
<p>🔹 <strong><code>setAttribute()</code></strong></p>
<ul>
<li>JSP로 전달할 데이터를 추가할 때 사용한다.</li>
<li>key와 value의 형태로 데이터를 저장할 수 있다.</li>
</ul>
<p>🔹 <strong><code>RequestDispatcher</code></strong></p>
<ul>
<li>현재의 요청을 다른 서버의 자원에게 전달하는 용도로 사용</li>
<li><code>forward()</code> : 현재까지의 모든 응답 내용은 무시하고 JSP가 작성하는 내용만을 브라우저로 전달</li>
</ul>
<h3 id="httpsevletresponse의-주요-기능">HttpSevletResponse의 주요 기능</h3>
<p>주로 &#39;쓰는&#39; 기능 제공</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/660fe911-122f-46e4-a89a-94a045eba88e/image.png" alt=""></p>
<p>웹 MVC 구조에서 주로 JSP에서 처리되기 때문에 sendRedirect()를 이용하는 경우가 많다.</p>
<h2 id="15-모델model">1.5 모델(Model)</h2>
<hr>
<h3 id="모델과-3티어">모델과 3티어</h3>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/5d4860f1-a02b-45e4-a863-d747643b2406/image.png" alt=""></p>
<h3 id="dtodata-transfer-object">DTO(Data Transfer Object)</h3>
<p>3티어와 같이 계층을 분리하는 경우에는 반드시 계층이나 객체들 간에 데이터 교환이 이루어지기 때문에 여러 개의 데이터를 묶어서 하나의 객체로 전달하는 것을 DTO라고 한다.</p>
<ul>
<li>대부분 Java Beans 형태로 구성한다.<blockquote>
<p>Java Beans 구성</p>
</blockquote>
</li>
<li>생성자가 없거나</li>
<li>반드시 파라미터가 없는 생성자 함수를 가지는 형태</li>
<li>속성은 private으로 작성</li>
<li>getter/setter 제공</li>
<li>추가 선택적 규칙) Serializable 인터페이스 구현해야 함</li>
</ul>
<pre><code class="language-java">public enum TodoService {

    INSTANCE;
}</code></pre>
<p>enum 타입으로 클래스를 작성하는 경우, 정해진 수만큼만 객체를 생성할 수 있다는 장점이 있다. <code>TodoService.INSTANCE</code>와 같이 간단하게 객체를 하나만 생성해서 사용 가능하다. 이를 싱글톤 패턴이라고 한다.</p>
<blockquote>
<p><span style="background-color:#B5F9C0"><strong>싱글톤 패턴?</strong></span>
 왜 싱글톤 패턴을 사용할까?<br>
    - <strong>인스턴스가 하나만 필요할 때</strong>
    예를 들어, 설정 정보(환경 설정 클래스), 로깅 클래스, DB 커넥션 풀 관리 클래스 등은 여러 개의 객체가 필요하지 않다.<br>
    - <strong>메모리 낭비 방지</strong>
    매번 new 키워드로 객체를 생성하지 않고 하나의 인스턴스를 재사용함으로써 메모리를 절약할 수 있다.<br>
    - <strong>전역 접근</strong>
    어디서든지 동일한 인스턴스를 사용할 수 있어, 공유 리소스 관리에 유용하다.<br>
 enum class로 INSTANCE를 만든 이유는 Java의 Enum이 JVM 차원에서 싱글톤을 보장하기 때문</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring MVC] 3. HttpServletRequest/Response]]></title>
            <link>https://velog.io/@liy_se00/Spring-MVC-HttpServletRequestResponse</link>
            <guid>https://velog.io/@liy_se00/Spring-MVC-HttpServletRequestResponse</guid>
            <pubDate>Sun, 06 Apr 2025 10:02:43 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/liy_se00/post/e0ebe4e9-1f42-4816-837a-c51fbdc2c049/image.jpg" alt=""></p>
<blockquote>
<p>이 글은 김영한 강의 - Spring MVC 1편 섹션3.서블릿 강의를 듣고 정리한 글입니다.</p>
</blockquote>
<h2 id="📍httpservletrequest">📍HttpServletRequest</h2>
<p>서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱한다. 그리고 그 결과를 <code>HttpServletRequest</code> 객체에 담아서 제공한다.</p>
<blockquote>
<p>🔑<strong>중요</strong>
HttpServletRequest, HttpServletResponse를 사용할 때 가장 중요한 점은 이 객체들이 HTTP 요청 메시지, HTTP 응답 메시지를 편리하게 사용하도록 도와주는 객체라는 점이다. 따라서 이 기능에 대해서 깊이있는 이해를 하려면 HTTP 스펙이 제공하는 요청, 응답 메시지 자체를 이해해야 한다.</p>
</blockquote>
<p>HttpServletRequest를 사용하면 Http 요청 메세지를 편리하게 조회 가능하다.</p>
<pre><code>POST /save HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

username=kim&amp;age=20</code></pre><p>START LINE(HTTP 메소드, URL, 쿼리스트링, 스키마, 프로토콜), 헤더, 바디</p>
<ul>
<li>임시 저장소 기능</li>
<li>세션(로그인) 관리 기능</li>
</ul>
<h3 id="⭐-주로-3가지-방법의-http-요청">⭐ 주로 3가지 방법의 Http 요청</h3>
<blockquote>
<p>GET - 쿼리 피라미터
POST - HTML Form
HTTP message body</p>
</blockquote>
<ol>
<li><p>GET - 쿼리 피라미터</p>
<ul>
<li><p>메세지 바디 없이 URL의 쿼리파라미터를 사용해서 데이터 전달
  예) 검색, 필터, 페이징 등에서 많이 사용함</p>
</li>
<li><p>복수 파라미터에서 단일 파라미터 조회?
  <code>username=hello&amp;username=kim</code>과 같이 파라미터 이름은 하나, 값이 중복일 경우 -&gt; 이렇게 거의 안쓰긴 함.
  -&gt; 쓴다면 <code>request.getParameterValues()</code>를 사용해야 한다.
  -&gt; <code>request.getParameter()</code>는 하나의 파라미터 이름에 단 하나의 값만 있을 때 사용 (여러개일 경우 첫번째 값만 반환한다)</p>
</li>
</ul>
</li>
</ol>
<ol start="2">
<li><p>POST - HTML Form</p>
<ul>
<li><p>메세지 바디에 쿼리 파라미터 형식으로 전달: <code>username=hello&amp;age=20</code></p>
<ul>
<li>예) 회원가입, 상품주문, HTML Form 사용</li>
</ul>
</li>
<li><p><code>Content-Type</code>은 HTTP message body가 어떤 형식의 데이터인지 알려준다(GET URL 쿼리 파라미터 형식은 content-type이 null이다.)</p>
</li>
<li><p><code>Content-Type: application/x-www-form-urlencoded</code></p>
</li>
<li><p><code>message body</code> : <code>username=kim&amp;age=20</code></p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/4eb2feab-de28-48ee-bf4d-550a8797030e/image.png" alt=""></p>
</li>
<li><p>클라이언트 입장에서는 두 방식에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일하므로, HTML Form 같은 경우도 <code>request.getParameter()</code>로 구분 없이 조회할 수 있다.</p>
</li>
<li><p><strong>Form 데이터를 Body로 전송할 때는 POST방식만 허용됨</strong></p>
</li>
</ul>
</li>
</ol>
<ol start="3">
<li><p>HTTP message body</p>
<ul>
<li><p>Http API에서 주로 사용 (JSON, <del>XML, TEXT</del>)
  : <strong>HTTP message body</strong>에 데이터를 직접 담아서 요청</p>
<ul>
<li>POST, PUT PATCH</li>
</ul>
</li>
<li><p>JSON 형식으로 전송할 때 (실습)</p>
<ul>
<li>POST <a href="http://localhost:8080/request-body-json">http://localhost:8080/request-body-json</a></li>
<li>content-type : <strong>application/json</strong></li>
<li>message body : <code>{&quot;username&quot;: &quot;hello&quot;, &quot;age&quot;: 20}</code></li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h2 id="📍httpservletresponse">📍HttpServletResponse</h2>
<p>HttpServletResponse의 역할</p>
<blockquote>
<p>응답은 크게 3가지
단순 text 보내는 것
html 보내는 것
body에 직접 json 보내는 것</p>
</blockquote>
<h3 id="http-응답-메세지-생성">HTTP 응답 메세지 생성</h3>
<ul>
<li>HTTP 응답코드 지정 (200, 400, 404, 500...)</li>
<li>헤더 생성</li>
<li>바디 생성</li>
</ul>
<p>+) 편의 기능 제공(Content-Type, 쿠키, Redirect)</p>
<pre><code class="language-java">@WebServlet(name = &quot;responseHtmlServlet&quot;, urlPatterns = &quot;/response-html&quot;)
public class ResponseHtmlServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Content-Type: text/html;charset=utf-8
        response.setContentType(&quot;text/html&quot;);
        response.setCharacterEncoding(&quot;UTF-8&quot;);

        PrintWriter write = response.getWriter();
        write.println(&quot;&lt;html&gt;&quot;);
        write.println(&quot;&lt;body&gt;&quot;);
        write.println(&quot;    &lt;div&gt; 안녕! &lt;/div&gt;&quot;);
        write.println(&quot;&lt;/body&gt;&quot;);
        write.println(&quot;&lt;/html&gt;&quot;);
    }
}</code></pre>
<p>http 응답으로 HTML을 반환할 때는 content-type을 <code>text/html</code>로 지정해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring MVC] 2. SSR&CSR]]></title>
            <link>https://velog.io/@liy_se00/Spring-MVC-SSR-CSR</link>
            <guid>https://velog.io/@liy_se00/Spring-MVC-SSR-CSR</guid>
            <pubDate>Thu, 03 Apr 2025 06:20:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/liy_se00/post/89f684fc-0eb8-4134-b265-1b8ea7b06e47/image.png" alt=""></p>
<blockquote>
<p>이 세가지를 어떻게 제공할까를 고민해야 한다.</p>
</blockquote>
<ol>
<li>정적 리소스</li>
<li>동적 HTML 페이지</li>
<li>HTTP API</li>
</ol>
<ul>
<li><p>정적 리소스 : 웹브라우저-Web Server-리소스파일-웹브라우저</p>
</li>
<li><p>HTML 페이지 : 웹브라우저-WAS-HTML-웹브라우저</p>
</li>
<li><p>HTTP API : HTML이 아니라 데이터를 전달(주로 JSON형식)
  (웹브라우저-WAS-DATA-웹브라우저)</p>
<ol>
<li>데이터만 주고받고, UI화면이 필요하면 클라이언트가 별도로 처리</li>
<li>다양한 시스템에서 연동</li>
<li>UI 클라이언트 접점<ul>
<li>앱 클라이언트</li>
<li>웹 브라우저에서 JS를 통한 HTTP API 호출</li>
<li>React, Vue.js 같은 웹 클라이언트</li>
</ul>
</li>
<li>서버 to 서버<ul>
<li>주문 서버 -&gt; 결제 서버</li>
<li>기업 간 데이터 통신</li>
</ul>
</li>
</ol>
</li>
</ul>
<h2 id="ssr-csr">SSR, CSR</h2>
<p>서버 사이드 랜더링, 클라이언트 사이드 랜더링</p>
<h3 id="ssr---서버-사이드-랜더링">SSR - 서버 사이드 랜더링</h3>
<p>서버에서 최종 HTML을 생성해서 클라이언트에 전달
DB조회, 최종 HTML 만드는 것을 모두 서버에서 함
웹 브라우저는 다 만들어진 것을 띄우기만 함</p>
<p>툴: <del>JSP</del>, 타임리프(필수) -&gt; 백엔드 개발자
타임리프 - 화면이 정적이고, 복잡하지 않을 때</p>
<p>Ex)</p>
<ul>
<li><p>정적 성격이 강한 페이지 (에러 페이지, 로그인 페이지) 는 
백엔드 SSR로 빠르게 처리</p>
</li>
<li><p>kakao
  로그인/회원가입/인증 페이지는 서버 사이드 렌더링(SSR) 방식 사용
  이유: 빠른 초기 응답, SEO 대응, 보안 이유 등</p>
</li>
<li><p>배달의 민족
  배달 가게 사장님용 관리자 페이지(사장님 사이트) 일부
  이유: 특정 기능은 빠른 접근성과 관리 편의성이 중요해서 SSR로 처리
  그리고 관리자 페이지는 SEO나 초기 로딩 속도가 중요한 경우가 많음</p>
</li>
<li><p>기업 내부 시스템
대부분의 대기업, 공공기관 백오피스는 하이브리드 방식
📌 사내 시스템이라 UX보다 개발 속도, 안정성, 배포 간소화를 중요하게 여김</p>
</li>
</ul>
<h3 id="csr---클라이언트-사이드-랜더링">CSR - 클라이언트 사이드 랜더링</h3>
<p>HTML 결과를 JS를 사용해 웹 브라우저에서 동적으로 생성해서 사용
웹 환경을 마치 앱처럼 필요한 부분부분 변결 가능
Ex) 구글지도, Gmail, 구글 캘린더</p>
<p>툴: React, Vue.js -&gt; 웹 프론트엔드 개발자</p>
<ol>
<li>HTML 요청(/orders.html) - js링크를 내려줌</li>
<li>js 코드 요청 - 서버에서 응답</li>
<li>HTTP API - 데이터 요청(JSON)</li>
<li>웹 브라우저에서 js로 HTML 결과 랜더링</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring MVC] 1. Servlet&Thread]]></title>
            <link>https://velog.io/@liy_se00/Spring-MVC-1.-ServletThread</link>
            <guid>https://velog.io/@liy_se00/Spring-MVC-1.-ServletThread</guid>
            <pubDate>Thu, 03 Apr 2025 05:18:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/liy_se00/post/d8238efd-86c2-4389-96f7-26b724197d94/image.png" alt=""></p>
<h1 id="서블릿servlet">서블릿(Servlet)</h1>
<p>개발자가 비즈니스 로직만 작성할 수 있게 나머지 단계를 자동화해준다.</p>
<pre><code class="language-java">@WebServlet(name=&quot;helloServlet&quot;, urlPatterns=&quot;/hello&quot;)
public class HelloServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) {
    }
}</code></pre>
<p>HTTP 요청 정보를 편리하게 사용할 수 있는 HttpServletRequest
HTTP 응답 정보를 편리하게 사용할 수 있는 HttpServletResponse</p>
<h2 id="📍servlet-http-요청-응답-흐름">📍Servlet HTTP 요청, 응답 흐름</h2>
<ul>
<li>HTTP 요청시</li>
</ul>
<ol>
<li>WAS는 Request, Response 객체를 새로 만들어서</li>
<li>서블릿 객체 호출</li>
<li>개발자는 Request 객체에서 HTTP 요청 정보 편리하게 꺼내서 사용</li>
<li>Response 객체에서 HTTP 응답 정보 편하게 입력</li>
<li>WAS는 Response 객체에 담겨있는 내용으로 HTTP 응답 정보 생성</li>
<li>웹 브라우저에 전송</li>
<li>웹 브라우저가 html 랜더링해서 화면에 띄움</li>
</ol>
<h3 id="servlet-container">Servlet Container</h3>
<p>서블릿을 지원해주는 WAS = 서블릿 컨테이너</p>
<ol>
<li>서블릿 객체를 서블릿 컨테이너가 자동으로 생성해준다.</li>
<li>서블릿 객체 생성, 초기화, 호출, 종료등 생명주기도 관리해줌</li>
<li>서블릿 객체는 <strong>싱글톤으로 관리</strong><ul>
<li>싱글톤: 객체를 하나만 생성해놓고 하나를 모두가 공유해서 쓰는 것</li>
<li>response, request 객체는 요청마다 다 다르게 새로 만들어짐.</li>
<li>그러나 helloServlet이라는 것은 다시 만들 필요가 없음</li>
<li>그래서 최초 로딩 시점에 서블릿 객체를 미리 만들어두고 재활용</li>
<li>모든 고객 요청은 동일한 서블릿 객체 인스턴스에 접근</li>
<li><strong>공유변수(멤버변수) 사용 주의</strong></li>
<li>서블릿 컨테이너 종료시 함께 종료</li>
</ul>
</li>
<li>JSP도 서블릿으로 변환되어 사용</li>
<li><strong>동시 요청을 위한 멀티 쓰레드 처리 지원</strong><ul>
<li>WAS가 멀티쓰레드 자동으로 처리해줌</li>
</ul>
</li>
</ol>
<h2 id="📍동시-요청---멀티-쓰레드">📍동시 요청 - 멀티 쓰레드</h2>
<p>웹 브라우저가 WAS에 요청을 하면 서블릿 호출.
서블릿이라는 것을 도대체 누가 호출하느냐??
-&gt; 쓰레드!</p>
<h3 id="쓰레드">쓰레드</h3>
<ul>
<li>애플리케이션 코드를 하나하나 순차적으로 실행하는 것이 쓰레드</li>
<li>자바 main 메서드 처음 실행 -&gt; main이라는 이름의 쓰레드가 실행</li>
</ul>
<ol>
<li><p>단일 쓰레드?</p>
<p> -&gt; 요청이 여러개 오면 모두 time out</p>
</li>
<li><p>요청마다 쓰레드 생성?</p>
<ul>
<li>쓰레드는 생성 비용이 매우 비쌈</li>
<li>요청 올 때마다 생성하면 응답 속도 늦어짐</li>
<li>쓰레드 간 전환(컨텍스트 스위칭)에 비용 발생</li>
<li>쓰레드 생성에 제한이 없어진다.</li>
<li><blockquote>
<p>고객 요청이 너무 많이 오면, CPU 메모리 임계점을 넘어 서버 죽음.</p>
</blockquote>
</li>
</ul>
</li>
<li><p>✅ <strong>쓰레드 풀!</strong>
<img src="https://velog.velcdn.com/images/liy_se00/post/75ccac99-484c-438c-b6b0-6f39aae1265c/image.png" alt=""></p>
</li>
</ol>
<p>풀 안에 미리 갯수 제한하여 만들어놓음
쓰레드 다 쓰면 다시 풀에 반납
쓰레드 풀에 0개이면 쓰레드 대기, 거절</p>
<p>장점
    - 필요한 쓰레드를 쓰레드 풀에 보관, 관리
    - 쓰레드 풀에 생성 가능한 쓰레드의 최대치를 관리.
    - 톰캣은 최대 200개 기본 설정
    - CPU 절약되고, 응답 시간이 빠름
    - 최대치가 정해져있으므로 많은 요청이 들어와도 안전하게 처리 가능</p>
<h3 id="⭐실무-tip">⭐실무 Tip</h3>
<p><strong>최대 쓰레드(Max thread)</strong>를 튜닝했을때 극적인 효과를 볼 확률이 높음</p>
<p>👉🏻 최대 쓰레드 값이 너무 낮으면?
    - 서버 리소스는 여유롭지만, 클라이언트는 금방 응답 지연</p>
<p>👉🏻 최대 쓰레드 값이 너무 높으면?
    - 동시 요청이 많으면 CPU, 메모리 리소스 임계점 초과로 서버 다운</p>
<p>✅ 클라우드? -&gt; 서버 늘리고 이후에 튜닝, 아니면 그냥 열심히 튜닝
✅ 적정 숫자? -&gt; 애플리케이션 로직의 복잡도, CPU, 메모리 상황에 따라 다름. </p>
<p>-&gt; 최대한 실제 서비스와 유사하게 <strong>성능 테스트</strong>
(툴:아파치, nGrinder, 제이미터)
-&gt; 부하 테스트로 TPS(Transaction Per Second)를 측정한 뒤, 적정 스레드 수를 설정</p>
<table>
<thead>
<tr>
<th>규모</th>
<th align="left">예상 maxThreads</th>
</tr>
</thead>
<tbody><tr>
<td>소규모 서비스</td>
<td align="left">100~200</td>
</tr>
<tr>
<td>일반 사용자 대상 중규모 서비스</td>
<td align="left">200~500</td>
</tr>
<tr>
<td>트래픽 많은 대규모 서비스 (배달의 민족, 쿠팡 등)</td>
<td align="left">400~1000 (하지만 여러 서버로 분산)</td>
</tr>
</tbody></table>
<br>
<br>

<hr>
<h3 id="🔑-was의-멀티-쓰레드-지원">🔑 WAS의 멀티 쓰레드 지원</h3>
<p>멀티쓰레드에 대한 부분은 WAS가 처리
개발자는 멀티 쓰레드 관련 코드를 신경쓰지 않아도 됨
개발자는 마치 싱글 쓰레드 프로그래밍을 하듯이 편리하게 소스코드 개발
멀티 쓰레드 환경이므로 싱글톤 객체(서블릿, 스프링 빈)는 주의해서 사용
-&gt; 싱글톤 객체에 멀티 쓰레드(쓰레드 여러 개)의 호출이 들어옴
-&gt; 멤버변수이기 때문에 주의 필요</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] 깃 명령어 정리]]></title>
            <link>https://velog.io/@liy_se00/Git-%EA%B9%83-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@liy_se00/Git-%EA%B9%83-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 23 Dec 2024 16:45:52 GMT</pubDate>
            <description><![CDATA[<h2 id="--새-브랜치-만들기">- 새 브랜치 만들기</h2>
<pre><code class="language-git">$ git checkout -b feature-week1</code></pre>
<p>-b 플래그는 현재 작업 중인 브랜치에서 새 브랜치를 생성해준다.
새 브랜치의 커밋 히스토리는 작업하고 있던 브랜치의 마지막 커밋에서 시작된다.</p>
<h2 id="--fetch">- fetch</h2>
<p>git fetch origin의 주요 기능</p>
<pre><code>$ git fetch origin</code></pre><p>원격 브랜치 업데이트
원격 저장소(origin)에 있는 브랜치와 관련된 최신 변경 사항(커밋)을 가져옵니다. 이때 가져온 변경 사항은 로컬의 원격 추적 브랜치(예: origin/main, origin/feature-week8)에 저장됩니다.</p>
<p>병합 없이 안전하게 동기화
fetch는 로컬 브랜치에 변경 사항을 바로 적용하지 않기 때문에, 작업 중인 로컬 브랜치를 보호하면서 원격 저장소와의 차이를 확인할 수 있습니다.</p>
<p>원격 브랜치 및 태그 정보 업데이트
원격 저장소에서 새로 추가되거나 삭제된 브랜치 및 태그 정보를 로컬로 동기화합니다.</p>
<h2 id="--브랜치-전환">- 브랜치 전환</h2>
<pre><code class="language-git">$ git checkout main</code></pre>
<h2 id="--브랜치-삭제">- 브랜치 삭제</h2>
<ul>
<li><p>로컬 브랜치 삭제</p>
<pre><code class="language-git">$ git branch -d &lt;로컬_브랜치_이름&gt;</code></pre>
<p>현재 작업중인 브랜치는 삭제 불가능
브랜치에 병합되지 않은 변경사항 및 푸시되지 않은 커밋이 있는 경우 삭제 불가능</p>
</li>
<li><p>로컬 브랜치 <strong>강제 삭제</strong></p>
<pre><code class="language-git">$ git branch -D &lt;로컬_브랜치_이름&gt;</code></pre>
</li>
</ul>
<h2 id="--pull-관련-git">- pull 관련 git</h2>
<h3 id="span-stylecolorindianred⚠️-error-⚠️span"><span style="color:indianred">⚠️ ERROR ⚠️</span></h3>
<pre><code class="language-git">hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use &#39;git pull&#39; before pushing again.
hint: See the &#39;Note about fast-forwards&#39; in &#39;git push --help&#39; for details.</code></pre>
<p>로컬 저장소에 있는 프로젝트를 깃허브에 push하려 할 때, 이런 문구가 떴다.</p>
<p>메세지를 보니,
현재 로컬 브랜치(yoonseo)가 깃허브에 있는 동일한 브랜치(remote branch)보다 옛날 상태라서 깃헙을 보호하기 위해 푸시를 막은 것으로 보인다. </p>
<h3 id="✅-해결">✅ 해결</h3>
<ol>
<li><p>push 전에 pull을 해서 프로젝트를 병합해주어야 한다.
 <code>git push origin yoonseo</code></p>
</li>
<li><p><code>refusing to merge unrelated histories</code> 이런 문구가 뜬다면</p>
</li>
<li><p><code>git pull origin 브런치명 --allow-unrelated-histories</code>
 위 명령 옵션은 서로 관련 없는(공통 조상이 없는) 두 브랜치의 Git 히스토리를 강제로 병합하려고 할 때 사용하는 명령어이다.</p>
</li>
</ol>
<p>참고: 보통 Git은 병합하려는 두 브랜치가 같은 루트(commit)에서 시작된 경우에만 병합을 허용한다. 하지만 아래와 같은 상황에서는 서로 &quot;unrelated&quot;한 것으로 간주하고 병합을 거부한다.</p>
<p>💥 이런 경우!
    1. 두 개의 독립적으로 시작된 Git 저장소를 병합하려고 할 때
    2. 처음에 깃허브에서 <code>README.md</code>만 있는 상태에서 로컬저장소를 push하려고 할때
        - GitHub: README 하나 있음
        - 로컬: 다른 커밋 있음 → 서로 공통 히스토리 없음 → 병합 불가
    3. 이전에 공유한 적 없는 브랜치를 병합할 때
        - ex: 팀원이 만든 브랜치와 내 브랜치가 아예 다른 시작점에서 만들어짐</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC] 5주차. JPA 기초 및 프로젝트 구조]]></title>
            <link>https://velog.io/@liy_se00/UMC-5%EC%A3%BC%EC%B0%A8.-JPA-%EA%B8%B0%EC%B4%88-%EB%B0%8F-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@liy_se00/UMC-5%EC%A3%BC%EC%B0%A8.-JPA-%EA%B8%B0%EC%B4%88-%EB%B0%8F-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Wed, 30 Oct 2024 02:13:33 GMT</pubDate>
            <description><![CDATA[<h1 id="어노테이션-정리">어노테이션 정리</h1>
<hr>
<p>domain 패키지
-&gt; Member.java</p>
<pre><code class="language-java">@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}</code></pre>
<ul>
<li><p>기본키를 만들 때 사용한 어노테이션
<code>@GeneratedValue(strategy = GenerationType.IDENTITY)</code>
: JPA가 통신을 하는 DBMS의 방식을 따른다</p>
</li>
<li><p><code>@Entity</code> : 어노테이션을 통해 해당 클래스가 JPA의 엔티티임을 명시
<code>@Getter</code> : lombok 제공. getter을 만들어주는 어노테이션</p>
</li>
<li><p><code>@Builder</code>
<code>@NoArgsConstructor(access = AccessLevel.PROTECTED)</code>
<code>@AllArgsConstructor</code>
: 위 세 개의 어노테이션은 자바의 디자인 패턴 중 하나인 빌더 패턴을 사용하기 위함. 빌더 패턴을 사용하면 생성자를 사용하는 것보다 더욱 편리하게 코당 가능.</p>
</li>
</ul>
<br>

<p>[ 빌더 패턴 관련 참고자료 ]
<a href="https://mangkyu.tistory.com/163">https://mangkyu.tistory.com/163</a>
<a href="https://readystory.tistory.com/121">https://readystory.tistory.com/121</a>
<a href="https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC">https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC</a></p>
<p><br><br></p>
<h2 id="getter-setter">@Getter, @Setter</h2>
<hr>
<p>[참고자료]: [OOP] Getter와 Setter는 지양하는게 좋다 by 개발하는 곰돌이
<a href="https://colabear754.tistory.com/173">https://colabear754.tistory.com/173</a></p>
<p><code>@Getter</code>와 <code>@Setter</code>의 사용 지양할 것.</p>
<ul>
<li><p>Setter 대신 명확한 의도를 가진 메소드를 사용하라.</p>
</li>
<li><p>Getter로 조건을 검사하지 말고 결과를 반환하게 하라.</p>
</li>
<li><p>Ex) 출금의 경우,
  AccountService에서 계좌 잔액 충분한지 확인 -&gt; 잔액 변경 (X)
  그냥 Account에 출금할 금액을 전달 -&gt; 출금 (O)</p>
<pre><code class="language-java">class Account {
   private long balance;

  public void withdraw(long amount) {
      if (amount &gt; balance) {
          throw new IllegalArgumentException(&quot;잔액이 부족합니다.&quot;);
      }       
      balance -= amount;
  }
}
</code></pre>
</li>
</ul>
<p>@Service
public class AccountService {
       ...<br>    public void withdraw(long id, long amount) {
           Account account = accountRepository.findById(id).orElseThrow();
           account.withdraw(amount);
    }
    ...
}</p>
<pre><code>= 이렇게 하면 코드가 간결해짐과 동시에 의도도 명확해졌다.

&lt;br&gt;&lt;br&gt;

## @Enumerated
---
❗**정해진 값들 중에서 특정 값이 저장되는 경우**,
Ex) gender, status, social_type
String 타입을 사용하기 보다는 **enum**을 사용하는 것이 좋다.

![](https://velog.velcdn.com/images/liy_se00/post/60f17283-9ae0-48f9-bfaf-a60a371fe2cf/image.png)

이때 EnumType은 반드시 **STRING** !!

ORDINAL 사용시 데이터베이스에 enum의 순서가 저장되는데,
만약 enum의 순서를 바꿀 경우 에러가 생김.


## Created_at, Updated_at
---

### @EnableJpaAuditing

❓ **JPA Auditing**(감시,감사)

데이터베이스에서 **누가, 언제** 하였는지 기록을 잘 남겨놓아야 합니다.
때문에 **생성일, 수정일 컬럼**이 대단히 중요합니다.
JPA에서 제공하는 기능인 `Audit`은 Spring Data JPA에서 **시간에 대해서 자동으로 값을 넣어주는 기능**입니다.

1. build.gradle
    `spring-boot-starter-data-jpa`만 추가해도 Audit을 하는데는 문제가 없습니다.

2. BaseTimeEntity.java
```java
@Getter
@MappedSuperclass 
@EntityListeners(AuditingEntityListener.class) 
public abstract class BaseTimeEntity{

    // Entity가 생성되어 저장될 때 시간이 자동 저장됩니다.
    @CreatedDate
    private LocalDateTime createdDate;

    // 조회한 Entity 값을 변경할 때 시간이 자동 저장됩니다.
    @LastModifiedDate
    private LocalDateTime modifiedDate;

}</code></pre><ol start="3">
<li><p>Spring boot Application에 <code>@EnableJpaAuditing</code>추가</p>
<pre><code class="language-java">@SpringBootApplication
@EnableJpaAuditing
public class StudyApplication {

 public static void main(String[] args) {
     SpringApplication.run(StudyApplication.class, args);
 }
</code></pre>
</li>
</ol>
<p>}</p>
<pre><code>
### @MappedSuperclass
JPA Entity 클래스들이 해당 추상 클래스를 상속할 경우 createDate, modifiedDate를 컬럼으로 인식

### @EntityListeners(AuditingEntityListener.class)
해당 클래스에 Auditing 기능을 포함

### @CreatedDate/@LastModifiedDate
Entity가 생성되어 저장될 때 시간이 자동 저장
조회한 Entity의 값을 변경할 때 시간이 자동 저장

&lt;br&gt;
[참고자료] : JPA Auditing 기능이란? - 준영이의 웹 까페
https://webcoding-start.tistory.com/53


&lt;br&gt;&lt;br&gt;

## 연관 관계 매핑
---

### @ManyToOne

: N:1 에서 N에 해당하는 엔티티가 1에 해당하는 엔티티와 연관관계를 매핑할 때 `@ManyToOne` 어노테이션을 씁니다.

`@ManyToOne(fetch = FetchType.LAZY)`
- LAZY(지연): 필요할 때 데이터들을 로딩한다. - 모든 연관관계는 지연관계로 설정할 것.

### @OneToMany

: 1에 해당하는 엔티티가 N에 해당하는 엔티티와 관계가 있음을 명시
    이때, **N에 해당하는 엔티티에서 ManyToOne이 설정 된 멤버변수를 mappedBy** 합니다.

Member.java
`@OneToMany(mappedBy = &quot;member&quot;, cascade = CascadeType.ALL)`
- cascade=CascadeType.ALL
    : 엔티티의 영속성 상태가 변화할 때 연관된 엔티티도 함께 변화한다.
    cascade의 타입에는 PERSIST, MERGE, REMOVE, REFRESH, DETACH, ALL 6가지 속성이 있다.

### @JoinColumn
실제 데이터베이스에서 해당 칼럼(외래키)의 이름을 설정하는 것입니다.

&lt;br&gt;&lt;br&gt;

## 칼럼 별 세부 설정
---
### @Column(...)

Ex.
`@Column(nullable = false, length = 40)`
`@Column(columnDefinition = &quot;VARCHAR(10)&quot;)`
`@Column(columnDefinition = &quot;VARCHAR(15) DEFAULT &#39;ACTIVE&#39;&quot;)`

- mysql은 문자열을 무조건 `&#39;&#39;`로 감싸야 한다.
- 칼럼의 디폴트 값은 `@ColumnDefault(&#39;ACTIVE&#39;)` 같은 형태로도 가능함</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC] 4주차. Spring Boot 코어개념]]></title>
            <link>https://velog.io/@liy_se00/UMC-4%EC%A3%BC%EC%B0%A8.-Spring-Boot-%EC%BD%94%EC%96%B4%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@liy_se00/UMC-4%EC%A3%BC%EC%B0%A8.-Spring-Boot-%EC%BD%94%EC%96%B4%EA%B0%9C%EB%85%90</guid>
            <pubDate>Thu, 24 Oct 2024 05:00:37 GMT</pubDate>
            <description><![CDATA[<p>중간시험이 모두 끝나고 4, 5주차를 한꺼번에 공부하게 되었다...
앞의 1,2,3주차에서는 Spring boot를 배우기 전에 백엔드 개발을 위한 필수적인 사전 지식을 공부하였다면, 이번 주차부터는 본격적으로 Spring boot에 대해 배운다.</p>
<h2 id="🌱spring이란">🌱Spring이란?</h2>
<blockquote>
<p>엔터프라이즈용 Java 애플리케이션 개발을 편하게 할 수 있게 해주는 오픈소스 경량급 애플리케이션 프레임워크</p>
</blockquote>
<h3 id="⭐-프레임워크framework">⭐ 프레임워크(Framework)</h3>
<hr>
<p>프레임워크란, 어떠한 목적을 쉽게 달성할 수 있도록 해당 목적과 관련된 코드의 뼈대를 미리 만들어 둔 것. (프레임 위에서의 작업)
<br>
<br></p>
<h3 id="🧑💻-프레임워크-vs-api-vs-라이브러리">🧑‍💻 프레임워크 vs API vs 라이브러리</h3>
<hr>
<h4 id="span-stylebackground-colorfff5b1api-application-programming-interfacespan"><span style="background-color:#fff5b1">API (Application Programming Interface)</span></h4>
<p>API는 2개 이상의 소프트웨어 컴포넌트 사이에서 상호작용 할 수 있도록 정의된 인터페이스를 말합니다. 즉, 다른 개발자들이 사용할 수 있도록 함수나 메서드, 클래스를 정의하는 것입니다.</p>
<p>라이브러리와 API를 혼동하기 쉬운데, 실제 개발을 할 때는 여러 컴포넌트를 합쳐서 개발을 하게 되고, 각각의 컴포넌트들은 API를 가지고 있습니다. 이때 많은 컴포넌트들이 라이브러리의 형태로 제공되기 때문에 API와 라이브러리를 혼동할 수 있습니다. 하지만, <strong>라이브러리는 컴포넌트 자체</strong>를 의미하고, <strong>API는 그 컴포넌트를 활용하기 위한 규약</strong>입니다.</p>
<h4 id="vs-framework">Vs. Framework</h4>
<p>프레임워크는 응용프로그램이나 소프트웨어 구현을 수월하게 하기 위해 제공된 소프트웨어 환경이다. 라이브러리는 우리가 선택적으로 끌어다 쓰는 것과 달리 프레임워크는 프레임워크에 의존하여 개발해야하고, 프레임워크가 정의한 규칙을 준수해야한다.
<br><br></p>
<h3 id="⭐-spring-ioc-컨테이너">⭐ Spring IoC 컨테이너</h3>
<hr>
<p><span style="background-color:#fff5b1"><strong>Spring IoC</strong></span> (제어의 역전: Inversion of Control) 컨테이너는 <strong>객체의 생성과 관리를 개발자가 아닌 Spring 프레임워크가 직접 담당</strong>하는 개념입니다. 개발자는 필요한 객체만 선언해 두고, Spring이 알아서 적절한 객체를 주입해주는 형태이죠.<br>
👉🏻 그러므로 &#39;제어의 역전&#39;은 객체의 생성, 생명주기의 관리까지 모든 객체에 대한 제어권이 개발자에서 컨테이너에게로 바뀌었다는 뜻입니다.</p>
<blockquote>
<p><strong>컨테이너</strong>: 컨테이너는 보통 객체의 생명주기를 관리, 생성된 인스턴스들에게 추가적인 기능을 제공하도록 하는 것이다.</p>
</blockquote>
<h4 id="--ioc-컨테이너의-작동-방식">- IoC 컨테이너의 작동 방식</h4>
<ol>
<li><p>객체를 <strong>class</strong>로 정의합니다.</p>
</li>
<li><p><strong>객체들 간의 연관성</strong>을 지정: Spring설정 파일(Config) 또는 어노테이션(<code>@Component</code>, <code>@Cinfiguration</code>, <code>@Autowired</code>, <code>@Bean</code>)을 통해 객체들이 어떻게 연결될지 (<strong>의존성 주입</strong>) 지정해줍니다.</p>
</li>
<li><p>IoC 컨테이너가 이 정보를 바탕으로 <strong>객체들을 생성하고 필요한 곳에 주입합니다.</strong></p>
</li>
</ol>
<br>

<p>👉🏻 <span style="background-color:#fff5b1"><strong>POJO (Plain Old Java Object)</strong></span> 기반의 개발을 가능하게 한다!
: 복잡한 라이브러리나 프레임워크에 의존/종속적 X, 순수한 자바 객체</p>
<p><br><br></p>
<h3 id="⭐-빈bean">⭐ 빈(Bean)</h3>
<hr>
<p><strong>spring 컨테이너가 관리하는 자바의 객체</strong>를 의미.
<code>ApplicationContext.getBean()</code> 함수를 호출하면 얻어지는 것이 Sping의 빈이다.</p>
<p>Bean을 통해 객체를 인스턴스화 -&gt; 객체 간의 의존 관계 관리
<br>
객체 생성할 때,</p>
<p><code>new</code> 로 객체 직접 생성 -&gt; 객체 생성과 의존성 관리를 개발자가 전적으로 책임져야 함.</p>
<p>그러므로 아래와 같이 빈으로 등록하여 관리하는 방법이 있다.</p>
<h4 id="✨-bean으로-등록하여-관리하는-방식">✨ Bean으로 등록하여 관리하는 방식</h4>
<p>1) <code>@Component</code> -&gt; <code>@Autowired</code> : <span style="background-color:#fff5b1">묵시적 Bean 정의</span>
    클래스에 Component 어노테이션 추가하여 Spring이 자동으로 해당 클래스를 스캔하고 Bean으로 등록하도록 한다. 그리고 <strong>Autowired로 다른 클래스에서 해당 Bean을 끌어온다.</strong></p>
<p>2) <code>@Configuration</code> -&gt; <code>@Bean</code> : <span style="background-color:#fff5b1">명시적 Bean 정의</span>
    Spring 설정 파일에 Configuration 어노테이션을 추가하고, Bean 어노테이션을 붙여 명시적으로 빈을 지정한다.</p>
<p><br><br></p>
<h2 id="🧑💻-의존성-주입">🧑‍💻 의존성 주입</h2>
<hr>
<h3 id="⭐-spring-di-dependency-injection">⭐ Spring DI (Dependency Injection)</h3>
<p>&#39;의존성 주입&#39;은 객체 인스턴스에 설정된 속성을 통해서만 종속성을 정의하는 프로세스</p>
<p>Spring 컨테이너는 빈을 생성할 때, 이러한 종속성을 주입한다.</p>
<p>이 프로세스는 기본적으로 클래스를 직접 생성, service locater 패턴을 사용하여 종속성의 인스턴스화 또는 위치를 스스로 제어하는 것과는 전혀 반대이다. (Inversion of Control)</p>
<p><em>DI 원칙을 사용하면,</em> </p>
<p>👉🏻 코드가 더 깔끔해지고, 객체에 종속성이 제공되어 분리가 더 효과적이다.</p>
<h3 id="의존성을-왜-외부로부터-주입받아야-할까❓">의존성을 왜 외부로부터 주입받아야 할까❓</h3>
<p>개발을 하며 만드는 객체들은 서로 의존되어있을 수밖에 없다. 필연적으로 서로 의존성을 지닐 수 밖에 없기 때문에 객체들 간에 결합이 생긴다.</p>
<p>CoffeeBeans를 인터페이스로 구현</p>
<pre><code class="language-java">public interface CoffeeBeans {
    void grind();
}</code></pre>
<p>인터페이스를 구현하는 구현체 클래스를 만든다.</p>
<pre><code class="language-java">public static class RegularCoffeeBeans implements CoffeeBeans{
    @Override
    public void grind(){
        System.out.println(&quot;기본 원두로 가는 중&quot;)
    }
}</code></pre>
<p>다른 원두를 쓰고 싶을 때는 &#39;디카페인&#39;이라는 구현체 클래스를 하나 정의</p>
<pre><code class="language-java">public static class DecafCoffeeBeans implements CoffeeBeans {
    @Override
    public void grind() {
        System.out.println(&quot;디카페인 원두로 가는 중&quot;)
    }
}</code></pre>
<br>

<blockquote>
<p>👉🏻 <strong>이처럼 각 객체끼리의 의존성이 존재할 때, 의존성을 외부에서 주입받는 것이 바람직합니다.</strong><br>
그리고, Spring은 이런 의존성 주입을 자동화해줌으로써, 개발자의 부담을 줄여줍니다.</p>
</blockquote>
<br>

<h2 id="⭐-spring의-의존성-주입-방식">⭐ Spring의 의존성 주입 방식</h2>
<blockquote>
<p>1️⃣ 생성자 주입 (Constructor Injection) - 스프링 공식 문서 권장 방식
2️⃣ setter 주입 (Setter Injection) - 의존 관계가 선택적이거나 변경 가능한 경우
3️⃣ 필드 주입 (Field Injection)</p>
</blockquote>
<h3 id="1️⃣-생성자constructor-주입-방식">1️⃣ 생성자(Constructor) 주입 방식</h3>
<hr>
<p>생성자 주입은 <span style="background-color:#fff5b1"><strong>생성자를 통해서 의존 관계를 주입</strong></span>받는 방법이다.</p>
<p>생성자에 <code>@Autowired</code>를 하면 스프링 컨테이너에 <code>@Component</code>로 등록된 빈에서 생성자에 필요한 빈들을 주입한다.</p>
<p>👉🏻 객체가 생성될 때 필요한 의존성을 모조리 설정해버린다.
이는 객체의 불변성(immunability)를 보장해준다.</p>
<pre><code class="language-java">@Service
public class MemberSignupService {
    private final MemberRepository memberRepository;

    @Autowired
    public MemberSignupService(final MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    ...
}</code></pre>
<p>생성자 호출 시점에 1번만 호출되는 것을 보장.
객체 생성될 때 필수적인 의존성(필드값)들을 보장. (의존성 없는 객체 X)
주입받을 필드를 final로 선언 가능하다.
<br></p>
<h3 id="2️⃣-setter-주입-방식">2️⃣ Setter 주입 방식</h3>
<hr>
<p><strong>런타임에 의존성을 주입</strong>하기 때문에, 의존성이 없더라도 객체 생성이 가능.</p>
<ol>
<li>주입 받으려는 빈의 생성자를 호출하여 빈을 찾거나 빈 팩토리에 등록</li>
<li>생성자 인자에 사용하는 빈을 찾거나 생성</li>
<li>Setter의 인자로 주입</li>
</ol>
<p>선택적으로 의존성 주입이 가능
NullPointException 에러가 발생할 수 있다는 매우 치명적 단점!</p>
<br>

<h3 id="3️⃣-필드field-주입-방식">3️⃣ 필드(Field) 주입 방식</h3>
<hr>
<p>Setter 주입 방식과 마찬가지로, <strong>런타임에 의존성을 주입</strong>하기 때문에 의존성이 없더라도 객체가 생성될 수 있다.</p>
<ol>
<li>주입받으려는 빈의 생성자를 호출 -&gt; 빈을 찾거나 빈 팩토리에 등록</li>
<li>생성자 인자에 사용하는 빈을 찾거나 생성</li>
<li>필드에 주입</li>
</ol>
<p>필드에 직접 주입되기 때문에 테스트 중에 의존성을 주입하는 것이 어렵다는 단점이 있다.
의존성이 명시적으로 드러나지 않기 때문에 구조를 이해하기 어렵다.
-&gt; Bean들 간의 <strong>순환 참조 문제</strong>가 발생할 수 있다.</p>
<br>

<p>[참고 자료]
<a href="https://ittrue.tistory.com/227">https://ittrue.tistory.com/227</a></p>
<h2 id="⭐-spring-서블릿servlet">⭐ Spring 서블릿(Servlet)</h2>
<p>웹 어플리케이션에서 클라이언트의 요청을 처리, 그에 대한 응답을 생성</p>
<h3 id="servlet-container">Servlet Container?</h3>
<p>Servlet을 관리해주는 컨테이너 역할</p>
<h3 id="dispatcherservlet">DispatcherServlet?</h3>
<p>Front Controller 패턴을 구현한 서블릿.
모즌 HTTP 요청을 받는 서블릿이다.</p>
<p>[참고 자료]
Spring MVC 동작 구조 : <a href="https://iri-kang.tistory.com/4">https://iri-kang.tistory.com/4</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] List Vs. Mutable List]]></title>
            <link>https://velog.io/@liy_se00/Kotlin-List-Vs.-Mutable-List</link>
            <guid>https://velog.io/@liy_se00/Kotlin-List-Vs.-Mutable-List</guid>
            <pubDate>Wed, 23 Oct 2024 07:57:35 GMT</pubDate>
            <description><![CDATA[<p>참고 문헌
<a href="https://kt.academy/article/ek-mutable-collections">https://kt.academy/article/ek-mutable-collections</a></p>
<blockquote>
</blockquote>
<p> The biggest advantage of using mutable collections instead of immutable collections is that <strong>their performance is faster</strong>. When we add an element to an immutable collection, we need to create a new collection and add all elements to it. Here is how this is currently implemented in Kotlin stdlib<br>
When we deal with bigger collections, adding multiple elements to another collection can be a costly process. This is why using mutable collections, especially if we often need to add elements, is a performance optimization.</p>
<blockquote>
<p>Adding mutable collections to elements is generally faster, <strong>but immutable collections give us more control over how they are changed</strong>. <strong>However, in the local scope we generally do not need this control, so mutable collections should be preferred</strong>, especially in util functions, where element insertion might happen many times. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC] 1주차. DB설계]]></title>
            <link>https://velog.io/@liy_se00/UMC-1%EC%A3%BC%EC%B0%A8.-DB%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@liy_se00/UMC-1%EC%A3%BC%EC%B0%A8.-DB%EC%84%A4%EA%B3%84</guid>
            <pubDate>Sun, 22 Sep 2024 05:31:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>데이터베이스 설계의 방법?</li>
</ul>
</blockquote>
<ul>
<li>상황에 따른 설계</li>
<li>직접 설계해보자!</li>
</ul>
<hr>
<h2 id="들어가기-전에">들어가기 전에</h2>
<h3 id="사전지식">사전지식</h3>
<ul>
<li><strong>데이터의 CRUD</strong> (Create, Read, Update, Delete)
: 데이터를 다루는 가장 기본적인 동작</li>
</ul>
<p>-&gt; MySQL 기초 문법
<code>DESC</code> : describe 테이블 구조 확인
<code>SHOW</code> : 테이블 조회</p>
<pre><code class="language-sql">// Create
CREATE TABLE 테이블명 (
    MYKEY INT,    // 컬럼명 데이터형,
    PRODUCT_ID TEXT,
    TITLE TEXT
    ...
    PRIMARY KEY(MYKEY)    // (기본키가 될 필드명)
);
INSERT INTO ... VALUES ...;

// Read
SELECT ... FROM ...;

// Update
UPDATE ... SET ... WHERE ...;

// Delete
DROP TABLE 테이블명;    // 테이블 삭제
DELETE FROM ... WHERE ...;</code></pre>
<hr>
<blockquote>
<p><strong>⭐ ERD는 프로젝트 시작과 동시에 설계하는 것이 좋습니다.</strong></p>
</blockquote>
<h3 id="erdentity-relationship-diagram-설계">ERD(Entity Relationship Diagram) 설계</h3>
<p>: 데이터 구조를 한눈에 알아보기 위해서 쓰인다.
테이블들의 구조화된 다이어그램</p>
<ul>
<li>Entity (개체)</li>
<li>Attribute (속성)</li>
<li>Relationship (관계)
  관계 형태</li>
</ul>
<hr>
<h3 id="sql과-nosql">SQL과 NoSQL</h3>
<h4 id="sql">SQL</h4>
<p>: 데이터베이스를 사용하는 시스템에서 데이터를 관리하는 데 사용되는 언어
관계가 잘 정의된 구조화된 데이터에 좋습니다.</p>
<h4 id="nosql">NoSQL</h4>
<p>: Not Only SQL의 약자로 SQL을 사용하지 않는 DBMS들을 의미합니다.
다양한 데이터 구조를 지원한다.
데이터 요소 간의 관계가 잘 정의되어 있지 않은 반정형, 비정형 데이터에 적합하다.</p>
<hr>
<blockquote>
<ul>
<li>유저 테이블을 어떻게 설계하는 것이 좋을까? </li>
</ul>
</blockquote>
<ul>
<li>N:M(다대다) 관계는 어떻게 하는 것이 좋을까?</li>
<li>알림을 보내야 하는 경우는 어떻게 하는 것이 좋을까?</li>
</ul>
<h2 id="데이터베이스-설계">데이터베이스 설계</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpringBoot3] JWT 토큰 서비스]]></title>
            <link>https://velog.io/@liy_se00/SpringBoot3-JWT-%ED%86%A0%ED%81%B0-%EC%84%9C%EB%B9%84%EC%8A%A4</link>
            <guid>https://velog.io/@liy_se00/SpringBoot3-JWT-%ED%86%A0%ED%81%B0-%EC%84%9C%EB%B9%84%EC%8A%A4</guid>
            <pubDate>Wed, 04 Sep 2024 07:56:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>👉🏻 이 글은 &lt;스프링부트3 백엔드 개발자 되기-자바편(2판)&gt;의 9장을 공부하며 작성한 글입니다.</p>
</blockquote>
<hr>
<p><strong>JWT (Json Web Token)</strong> : 웹 표준을 따르며 JSON 객체를 사용하여 정보를 전달한다. 토큰 기반의 인증 방법을 사용한다.</p>
<h2 id="📌-토큰-기반-인증">📌 토큰 기반 인증</h2>
<br>

<h4 id="사용자가-서버에-접근할-때-사용자가-인증된-사용자인지-확인하는-방법-두-가지">사용자가 서버에 접근할 때 사용자가 인증된 사용자인지 확인하는 방법 두 가지</h4>
<p><strong>1. 서버 기반 인증</strong></p>
<ul>
<li>세션 기반 인증 : 스프링 시큐리티
  기본적으로 제공해주는 세션 기반 인증을 사용해 사용자마다 사용자의 정보를 담은 세션을 생성하고 서버의 세션 저장소에 저장해서 인증을 함.
<br>세션 개념 참고 : (<a href="https://hudi.blog/cookie-and-session/">https://hudi.blog/cookie-and-session/</a>)<br>

</li>
</ul>
<p><u><strong>2. 토큰 기반 인증</strong></u></p>
<ul>
<li><strong>토큰: 서버에서 클라이언트를 구분하기 위한 유일한 값</strong>
  서버가 토큰을 생성래서 클라이언트에게 제공 
  -&gt; 클라이언트는 이 토큰을 가지고 있다가 여러 토큰과 함께 신청 
  -&gt; 서버가 유효한 사용자인지 검증 </li>
</ul>
<h3 id="📖-토큰을-전달하고-인증받는-과정">📖 토큰을 전달하고 인증받는 과정</h3>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/a38298ff-c94f-4184-94cf-dc3c4dce637a/image.png" alt=""></p>
<p>책의 내용을 위와 같이 그림으로 정리함.</p>
<hr>
<h3 id="📖-토큰-기반-인증의-특징">📖 토큰 기반 인증의 특징</h3>
<ul>
<li><u><strong>무상태성</strong></u><br>
  무상태성은 사용자의 인증 정보가 담겨 있는 토큰이 서버가 아닌 클라이언트에 있으므로 서버에 저장할 필요가 없는 것이다.
  즉, 클라이언트가 토큰을 생성하고 인증, 인증 상태를 유지하면서 요청 처리(=상태 관리) 하므로 <strong>서버 입장에서는 클라이언트의 인증정보를 저장하거나 유지하지 않아도 되기 때문에 완전한 무상태</strong>로 효율적인 검증이 가능하다.<br></li>
<li><u><strong>확장성</strong></u><br>
  서버를 확장할 때 <strong>상태 관리를 신경 쓸 필요가 없으니(무상태성) 서버 확장에도 용이한 것</strong>이다. 세션 기반 인증은 각각 API에서 인증을 해야되는 것과는 달리 토큰 기반 인증에서는 토큰을 가지는 주체는 서버가 아닌 클라이언트이기 때문에 가지고 있는 하나의 토큰으로 결제 서버와 주문 서버에 요청을 보낼 수 있다. 추가로 다른 토큰 기반 인증을 사용하는 다른 시스템에 접근해 로그인 방식을 확장할 수도 있고, 다른 서비스에 권한을 공유할 수도 있다.<br></li>
<li><u><strong>무결성</strong></u><br>
  토큰 방식은 HMAC(hash-based message authenticarion)이라고도 부르는데, <strong>토큰을 발급한 이후에는 토큰 정보를 변경하는 행위를 할 수 없다.</strong> 즉, 토큰의 무결성이 보장되는 것이다. 만약 누군가 토큰을 변경한다면 서버에서는 유효하지 않은 토큰으로 판단한다. </li>
</ul>
<hr>
<h2 id="📌-jwt">📌 JWT</h2>
<p>발급받은 JWT를 이용해 인증을 하려면 HTTP 요청 헤더 중, Authorization 키값에 <code>Bearer + JWT 토큰값</code> 을 넣어 보내야 합니다.</p>
<h3 id="📖-jwt의-구조">📖 JWT의 구조</h3>
<p>JWT는 <code>.</code>을 기준으로 <strong>헤더(header) / 내용(payload) / 서명(signature)</strong> 으로 이루어져 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/ec469c40-ab4b-43d4-8cc2-1129fa528153/image.png" alt=""></p>
<h4 id="--토큰-생성-메서드-예시">- 토큰 생성 메서드 예시</h4>
<p>TokenProvider.java - makeToken 메서드</p>
<pre><code class="language-java">// JWT 토큰 생성 메서드
private String makeToken(Date expiry, User user){
    Date now = new Date();

    // JWT = 헤더.내용.서명
    return Jwts.builder()
            // 헤더 typ : JWT
            .setHeaderParam(Header.TYPE, Header.JWT_TYPE) 
            // 내용 iss : ajufresh@gmail.com(propertise 파일에서 설정한 값)
            .setIssuer(jwtProperties.getIssuer())
            .setIssuedAt(now)   // 내용 iat : 현재 시간
            .setExpiration(expiry)  // 내용 exp : expiry 멤버 변숫값
            .setSubject(user.getEmail())    // 내용 sub : 유저의 이메일
            .claim(&quot;id&quot;, user.getId())  // 클레임 id : 유저 ID
            // 서명 : 비밀값과 함께 해시값을 HS256 방식으로 암호화
            .signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
            .compact();
}
</code></pre>
<br>

<h3 id="📖-토큰-유효기간">📖 토큰 유효기간</h3>
<p>❓ 만약 토큰 자체가 노출된다면? 토큰은 이미 발급되면 그 자체로 인증 수단이 되므로 서버는 토큰과 함께 들어온 요청이 토큰을 탈취한 사람의 요청인지 확인할 수 없다.</p>
<p>❗<u><strong>리프레시 토큰</strong></u>
 보안을 위해서는 토큰의 유효기간이 짧으면 되겠지만, 토큰의 유효기간이 너무 짧으면 사용자 입장에서는 불편할 수 있다.</p>
<p>➡️ <strong>액세스 토큰의 유효기간을 짧게 하고, 리프레시 토큰의 유효기간을 길게 설정!</strong></p>
<p>리프레시 토큰은 액세스 토큰과 별개로 <strong>액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급하기 위해 사용한다</strong>.
<br></p>
<h3 id="📖-액세스-토큰과-리프레시-토큰-전달--인증-과정">📖 액세스 토큰과 리프레시 토큰 전달 &amp; 인증 과정</h3>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/c31210be-fb78-40d1-821e-55d5b9ea86ad/image.png" alt=""></p>
<hr>
<p>이를 이해하고 JWT 서비스를 구현하면 됨.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring-boot3] 스프링 시큐리티]]></title>
            <link>https://velog.io/@liy_se00/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-spring-security</link>
            <guid>https://velog.io/@liy_se00/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-spring-security</guid>
            <pubDate>Thu, 29 Aug 2024 13:25:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>스프링 부트3 백엔드 개발자 되기 (자바편) 07장 : 스프링 부트3 구조 이해하기를 공부하고 작성한 내용입니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/7f1b7950-6a44-419e-8bfb-c697e91f11bd/image.png" alt=""></p>
<hr>
<h2 id="스프링-시큐리티">스프링 시큐리티</h2>
<p>: 스프링 기반의 애플리케이션 보안(인증, 인가, 권한)을 담당하는 스프링 하위 프레임워크이다. 보안 관련 옵션을 제공한다.</p>
<ul>
<li><p>인증: authentication 사용자의 신원을 입증하는 과정. 예를 들어 사이트에 로그인할 때 누구인지 확인하는 과정</p>
</li>
<li><p>인가: authorization 사이트의 특정 부분에 접근할 수 있는지에 권한을 확인하는 작업. (일반 사용자가 관리자 페이지에 들어갈 수 없는 것)</p>
</li>
</ul>
<p>스프링 시큐리티는 필터 기반으로 동작한다.
필터 구조를 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/13e24389-3d2a-4d0f-933f-1e0e6a49df60/image.png" alt=""></p>
<p>SecurityFilterChain 위에서 아래로 순서대로 필터를 거친다. 필터를 실행할 때는 화살표로 연결된 오른쪽의 클래스들을 거치며 실행한다.</p>
<p>⭐ <strong>UsernamePasswordAuthenticationFilter</strong>
: 아이디와 패스워드가 넘어오면 인증 요청을 위임하는 인증 관리자 역할</p>
<p>⭐ <strong>FilterSecurityInterceptor</strong>
: 권한 부여 처리를 위임해 접근 제어 결정을 쉽게 하는 접근 결정 관리자 역할</p>
<hr>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/3ec6083b-dcb2-40d7-a111-cbe580576ee0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring-boot3] ORM]]></title>
            <link>https://velog.io/@liy_se00/spring-boot3-ORM</link>
            <guid>https://velog.io/@liy_se00/spring-boot3-ORM</guid>
            <pubDate>Fri, 09 Aug 2024 07:56:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>스프링 부트3 백엔드 개발자 되기 (자바편) 05장 : 데이터베이스 조작이 편해지는 ORM</strong>을 공부하고 작성한 내용입니다.
출처: <a href="https://wikidocs.net/237210">https://wikidocs.net/237210</a></p>
</blockquote>
<hr>
<h3 id="dbms">DBMS</h3>
<p>: DataBase Management System
효율적으로 데이터베이스를 관리하고 운영하는 소프트웨어
( MySQL, Oracle )</p>
<ul>
<li>관계형 DBMS (RDBMS)
: MySQL, H2 (스프링부트가 지원하는 인메모리 관계형 데이터베이스)</li>
</ul>
<hr>
<h3 id="orm">ORM</h3>
<p>: Object-relation Mapping</p>
<ul>
<li><strong>자바의 객체와 데이터베이스를 연결하는 프로그래밍 기법</strong></li>
<li>자바 코드를 통해 데이터베이스의 값을 객체처럼 사용</li>
<li>복잡하고 무거운 쿼리는 ORM로 해결이 불가능한 경우가 있습니다.</li>
</ul>
<h3 id="jpa">JPA</h3>
<p>: Java Persistence API</p>
<ul>
<li><strong>자바 객체와 데이터베이스를 연결해 데이터를 관리한다.</strong></li>
<li>객체지향 도메인 모델과 데이터베이스의 다리 역할을 한다.</li>
<li>ORM의 한 종류로 자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스이다. </li>
<li>인터페이스이므로 실제 사용을 위해서는 ORM 프레임워크를 추가로 선택해야한다. -&gt; 대표적으로 <span style="color:olivedrab">하이버네이트</span>를 사용한다.</li>
</ul>
<h3 id="하이버네이트">하이버네이트</h3>
<p>: hibernate</p>
<ul>
<li>JPA를 구현한 구현체(JPA의 인터페이스를 구현)이자 자바용 ORM 프레임워크</li>
<li>내부적으로 JDBC API를 사용한다.</li>
<li>자바 객체를 통해 데이터베이스 종류에 상관없이 데이터베이스를 자유자재로 사용할 수 있게 한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/82ce1ff4-5d31-4641-95e4-c43c7e1377a7/image.png" alt=""></p>
<hr>
<h3 id="엔티티">엔티티</h3>
<p>: Entity</p>
<ul>
<li>데이터베이스의 테이블과 매핑되는 객체를 의미.
본질적으로 자바 객체이므로 일반 객체와 다르지 않다.</li>
<li><span style="background-color: rgba(242,179,188,0.5)">하지만 데이터베이스의 테이블과 직접 연결된다</span>는 아주 특별한 특징이 있어 구분지어 부릅니다.</li>
</ul>
<h3 id="엔티티-매니저">엔티티 매니저</h3>
<p>: Entity manager</p>
<ul>
<li><strong>엔티티를 관리해 데이터베이스와 어플리케이션 사이에서 객체를 생성, 수정, 삭제하는 등의 역할을 합니다.</strong></li>
<li><blockquote>
<p>이런 엔티티 매니저를 만드는 곳이 <span style="color:olivedrab">엔티티 매니저 팩토리</span>입니다.</p>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/756cd3de-f176-4ed9-b441-2380a2b4025b/image.png" alt=""></p>
<p>✅ 스프링부트에서는 직접 엔티티 매네저 팩토리를 만들어서 관리하지 않고 내부에서 엔티티 매니저 팩토리를 하나만 생성해서 관리하고 <code>@PersistenceContext</code> 또는 <code>@Autowired</code> 애너테이션을 사용해서 엔티티 매니저를 관리한다.</p>
<ul>
<li>기본적으로 빈을 하나만 생성해서 공유하므로 동시성 문제가 발생할 수 있어 실제 엔티티 매니저와 연결하는 프록시(가짜) 엔티티 매니저를 이용한다.</li>
<li>엔티티 매니저는 Spring Data JPA에서 관리하므로 직접 생성하거나 관리할 필요가 없다.</li>
</ul>
<h3 id="영속성-컨텍스트">영속성 컨텍스트</h3>
<p>: <span style="background-color: rgba(242,179,188,0.5)"><strong>엔티티를 관리하는 가상의 공간.</strong> 엔티티 매니저는 엔티티를 영속성 컨텍스트에 저장한다.</span></p>
<h4 id="--1차-캐시">- 1차 캐시</h4>
<p>영속성 컨텍스트는 내부에 1차 캐시를 가지고 있습니다. 이때 캐시의 키는 엔티티의 @Id 애너테이션이 달린 기본키 역할을 하는 식별자이며 값은 엔티티입니다. 엔티티를 조회하면 1차 캐시에서 데이터를 조회하고 값이 있으면 반환합니다. 값이 없으면 데이터베이스에서 조회해 1차 캐시에 저장한 다음 반환합니다. 이를 통해 캐시된 데이터를 조회할 때에는 데이터베이스를 거치치 않아도 되므로 매우 빠르게 데이터를 조회할 수 있습니다</p>
<h4 id="--쓰기-지연">- 쓰기 지연</h4>
<p>쓰기 지연transactional write-behind은 트랜잭션을 커밋하기 전까지는 데이터베이스에 실제로 질의문을 보내지 않고 쿼리를 모았다가 트랜잭션을 커밋하면 모았던 쿼리를 한번에 실행하는 것을 의미합니다. 예를 들어 데이터 추가 쿼리가 3개라면 영속성 컨텍스트는 트랜잭션을 커밋하는 시점에 3개의 쿼리를 한꺼번에 쿼리를 전송합니다. 이를 통해 적당한 묶음으로 쿼리를 요청할 수 있어 데이터베이스 시스템의 부담을 줄일 수 있습니다.</p>
<h4 id="--변경-감지">- 변경 감지</h4>
<p>트랜잭션을 커밋하면 1차 캐시에 저장되어 있는 엔티티의 값과 현재 엔티티의 값을 비교해서 변경된 값이 있다면 변경 사항을 감지해 변경된 값을 데이터베이스에 자동으로 반영합니다. 이를 통해 쓰기 지연과 마찬가지로 적당한 묶음으로 쿼리를 요청할 수 있고, 데이터베이스 시스템의 부담을 줄일 수 있습니다.</p>
<h4 id="--지연-로딩">- 지연 로딩</h4>
<p>지연 로딩lazy loading은 쿼리로 요청한 데이터를 애플리케이션에 바로 로딩하는 것이 아니라 필요할 때 쿼리를 날려 데이터를 조회하는 것을 의미합니다.</p>
<p>⬇️ 이 특징들이 갖는 공통점은 모두 데이터베이스의 접근을 최소화해 성능을 높일 수 있다는 것입니다. 캐시를 하거나, 자주 쓰지 않게 하거나, 변화를 자동 감지해서 미리 준비하거나 한다.</p>
<hr>
<h3 id="엔티티의-상태">엔티티의 상태</h3>
<p>분리 상태 : 영속성 컨테스트가 관리하고 있지 않은 상태
관리 상태 : 영속성 컨테스트가 관리하고 있는 상태
비영속 상태 : 영속성 컨테스트와 전혀 관계가 없는 상태
삭제된 상태 : 삭제된 상태</p>
<p>특정 메서드 호출로 상태 변경 가능.</p>
<hr>
<p>✅ ORM부터 JPA, 하이버네이트, 스프링 데이터 JPA를 알아보았습니다. ORM은 관계형 데이터베이스와 프로그램 간의 통신 개념, JPA는 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 기술 명세, 하이버네이트는 JPA의 구현체, 스프링 데이터 JPA는 JPA를 쓰기 편하게 만들어 놓은 모듈입니다.</p>
<ol>
<li><p>ORM은 객체와 데이터베이스를 연결하는 프로그래밍 기법입니다.</p>
</li>
<li><p>JPA는 자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스입니다.</p>
</li>
</ol>
<ul>
<li>엔티티는 영속성을 가진 객체를 의미합니다.</li>
<li>엔티티 매니저는 엔티티를 관리하며 조회, 삭제, 수정, 생성하는 역할을 합니다.</li>
<li>엔티티 매니저를 만드는 곳이 엔티티 매니저 팩토리입니다.</li>
<li>엔티티 매니저는 엔티티를 영속성 컨텍스트에 저장한다는 특징이 있습니다.</li>
<li>엔티티의 상태는 분리, 관리, 비영속, 삭제 상태로 나뉩니다.</li>
</ul>
<ol start="3">
<li><p>하이버네이트는 JPA의 대표적인 구현체로, 자바 언어를 위한 ORM 프레임워크입니다.</p>
</li>
<li><p>스프링 데이터 JPA는 JPA를 쓰기 편하게 만들어놓은 모듈입니다.</p>
</li>
</ol>
<p>출처: <a href="https://wikidocs.net/237210">https://wikidocs.net/237210</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring-boot3] 테스트 코드]]></title>
            <link>https://velog.io/@liy_se00/spring-boot3-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@liy_se00/spring-boot3-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C</guid>
            <pubDate>Thu, 08 Aug 2024 17:18:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>스프링 부트3 백엔드 개발자 되기 (자바편) 04장 : 스프링 부트 3와 테스트</strong>를 공부하고 작성한 내용입니다.
출처: <a href="https://wikidocs.net/237060">https://wikidocs.net/237060</a></p>
</blockquote>
<h2 id="⭐-테스트-코드">⭐ 테스트 코드</h2>
<p>: 작성한 코드가 의도대로 잘 동작하고 예상치 못한 문제가 없는지 확인할 목적으로 작성하는 코드</p>
<ul>
<li><p>테스트 코드는 <strong>test 디렉터리</strong>에서 작업</p>
</li>
<li><p>다양한 패턴이 존재</p>
<ul>
<li><code>given-when-then</code> 패턴<ol>
<li>given : 테스트 실행을 준비</li>
<li>when : 테스트 진행</li>
<li>then : 테스트 결과를 검증</li>
</ol>
</li>
</ul>
</li>
<li><p>스프링 부트는 <code>spring-boot-starter-test</code>스타터에서 애플리케이션을 테스트하기 위한 도구와 애너테이션을 제공한다. 
(JUnit, Spring Test &amp; Spring Boot Test, AssertJ, Mockito...)</p>
</li>
</ul>
<h3 id="✅-junit-단위-테스트-코드-만들기">✅ JUnit (단위 테스트 코드 만들기)</h3>
<ul>
<li><strong><code>JUnit</code> : 자바 프로그래밍 언어용 단위 테스트 프레임워크</strong><ul>
<li>테스트 방식을 구분할 수 있는 애너테이션을 제공</li>
<li><strong>@Test 애너테이션으로 메서드를 호출할 때마다 새 인스턴스를 생성, 독립 테스트 가능</strong></li>
<li>예상 결과를 검증하는 어설션 메서드 제공</li>
<li>사용 방법이 단순, 테스트 코드 작성 시간이 적음</li>
<li>자동 실행, 자체 결과를 확인하고 즉각적인 피드백을 제공</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/e028c372-efb9-40f2-b957-7549d55aa92d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/74e47681-231d-4af5-8f26-02c4e76feabc/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/d5cfa5bd-55c7-48e8-a8fb-cbdfb2c9bdcf/image.png" alt=""></p>
<h3 id="✅-assertj-검증문-가독성-높이기">✅ AssertJ (검증문 가독성 높이기)</h3>
<p>AssertJ는 JUnit과 함께 사용해 검증문의 가독성을 확 높여주는 라이브러리입니다. 이를테면 앞서 작성한 테스트 코드의 Assertion은 기댓값과 실제 비교값을 명시하지 않으므로 비교 대상이 헷갈립니다. </p>
<p>▼ 기댓값과 비교값이 잘 구분되지 않는 Assertion 예</p>
<p><code>Assertions.assertEquals(sum, a + b);</code></p>
<p>▼ 가독성이 좋은 AssertJ 예</p>
<p><code>assertThat(a + b).isEqualTo(sum);</code></p>
<p>AssertJ에서 다양한 메서드 제공</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/4f357351-21cb-4582-b2d0-0f66955851c0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/03d1318c-058b-453f-a134-fba82d809d99/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring-boot3] 요청-응답 과정]]></title>
            <link>https://velog.io/@liy_se00/spring-boot3-%EC%9A%94%EC%B2%AD-%EC%9D%91%EB%8B%B5-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@liy_se00/spring-boot3-%EC%9A%94%EC%B2%AD-%EC%9D%91%EB%8B%B5-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Thu, 08 Aug 2024 16:44:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>스프링 부트3 백엔드 개발자 되기 (자바편) 03장 : 스프링 부트3 구조 이해하기</strong>를 공부하고 작성한 내용입니다.
출처: <a href="https://wikidocs.net/237058">https://wikidocs.net/237058</a></p>
</blockquote>
<h3 id="33-스프링-부트-요청-응답-과정-한-방에-이해하기">3.3 스프링 부트 요청-응답 과정 한 방에 이해하기</h3>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/05a40d7a-2188-4510-a6dc-85f9e1e02074/image.png" alt=""></p>
<p>① 그림을 보면 포스트맨에서 &quot;톰캣&quot;에 /test GET 요청을 합니다. 
그러면 이 요청은 스프링 부트 내로 이동하는데요. 이때 스프링 부트의 디스패처 서블릿이라는 녀석이 URL을 분석하고, 이 요청을 처리할 수 있는 컨트롤러를 찾습니다. TestController가 /test라는 패스에 대한 GET 요청을 처리할 수 있는 getAllMembers(  ) 메서드를 가지고 있으므로 디스패처 서블릿은 TestController에게 /test GET 요청을 전달합니다. </p>
<p>② 마침내 /test GET 요청을 처리할 수 있는 getAllMembers(  ) 메서드와 이 요청이 매치됩니다. 그리고 getAllMembers(  ) 메서드에서는 비즈니스 계층과 퍼시스턴스 계층을 통하면서 필요한 데이터를 가져옵니다. </p>
<p>③ 그러면 뷰 리졸버는 템플릿 엔진을 사용해 HTML 문서를 만들거나 JSON, XML 등의 데이터를 생성합니다. </p>
<p>④ 그 결과 톰캣이 members를 return하고 그 데이터를 포스트맨에서 볼 수 있게 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Ch.16 네트워킹(Networking)]]></title>
            <link>https://velog.io/@liy_se00/Java-Ch.16-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%82%B9Networking</link>
            <guid>https://velog.io/@liy_se00/Java-Ch.16-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%82%B9Networking</guid>
            <pubDate>Sun, 21 Jul 2024 11:09:08 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>👉🏻 이 글은 자바의 정석(3판) Ch.16을 공부하며 적은 내용입니다.</p>
</blockquote>
<h2 id="url">URL</h2>
<blockquote>
<p><strong>프로토콜://호스트명:포트번호/경로명/파일명?쿼리스트링#참조</strong></p>
</blockquote>
<p>포트번호, 쿼리, 참조는 생략 가능</p>
<p>프로토콜: 자원에 접근하기 위해 서버와 통신하는데 사용되는 통신 규약(http)
호스트명: 자원을 제공하는 서버의 이름
포트번호: 통신에 사용되는 서버의 포트번호
경로명: 접슨하려는 자원이 저장된 서버상의 위치
파일명: 접근하려는 자원의 이름
쿼리(query) 
참조(anchor)</p>
<br>

<ul>
<li>URL 객체 생성
<code>URL url = new URL(&quot;http://.......html&quot;);</code></li>
</ul>
<h3 id="urlconnection">URLConnection</h3>
<p>어플리케이션과 URL간의 통신연결을 나타내는 클래스의 최상위 클래스로 추상클래스이다. (HttpURLConnection, JarURLConnection이 URLConnection을 상속받아 구현하였다.)
<strong>URLConnection을 이용해서 연결하고자 하는 자원에 접근하고 읽고 쓰기를 할 수 있다.</strong></p>
<pre><code class="language-java">public static void main(String args[]){
        URL url = null;
        BufferedReader input = null;
        String address = &quot;http://www.codechobo.com/sample/hello.html&quot;;
        String line = &quot;&quot;;

        try{
            url = new URL(address);
            input = new BufferedReader(new InputStreamReader(url.openStream()));

            while((line = input.readLine()) != null){
                System.out.println(line);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }</code></pre>
<p>openStream()은 openConnection()을 호출해서 URLConnection을 얻은 다름 여기에 다시 getInputStream()을 호출한 것과 같다.즉, URL에 연결해서 InputStream을 얻어온다.</p>
<hr>
<h2 id="📌-소켓-프로그래밍">📌 소켓 프로그래밍</h2>
<p>소켓 프로그래밍 : 소켓을 이용한 통신 프로그래밍을 뜻한다.
<strong>소켓(socket) : 프로세스간의 통신에 사용되는 양쪽 끝단(endpoint).</strong></p>
<p>java.net패키지를 통해 소켓프로그래밍 지원.
<strong>소켓통신에 사용되는 프로토콜에 따라 다른 종류의 소켓을 구현하여 제공.</strong></p>
<h3 id="tcp--udp">TCP / UDP</h3>
<p><strong>TCP/IP 프로토콜</strong>은 이기종 시스템간의 통신을 위한 표준 프로토콜로 프로토콜의 집합이다.</p>
<p>TCP와 UDP는 전송방식이 다르다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>TCP</th>
<th>UDP</th>
</tr>
</thead>
<tbody><tr>
<td>연결방식</td>
<td>연결 기반<br>- 연결 후 통신(전화기)<br>- 1:1 통신 방식</td>
<td>비연결 기반<br>- 연결없이 통신(소포)<br>- 1:1, 1:n, n:n 통신 방식</td>
</tr>
<tr>
<td>특징</td>
<td>데이터의 경계를 구분하지 않음<br>신뢰성 있는 데이터 전송<br>- 전송 순서 보장<br>- 수신 여부 확인함<br>- 패킷 관리 필요 없음<br>UDP보다 전송속도가 느림.</td>
<td>데이터의 경계를 구분함.(datagram)<br>신뢰성 없는 데이터 전송<br>- 전송 순서 바뀔 수 있음<br>- 수신여부 확인 안함<br>- 패킷 관리해주어야 함.<br>TCP보다 전송속도 빠름.</td>
</tr>
<tr>
<td>관련 클래스</td>
<td>Socket<br>ServerSocket</td>
<td>DatagramSocket<br>DatagramPacket<br>MulticastSocket</td>
</tr>
</tbody></table>
<h3 id="tcp소켓-프로그래밍">TCP소켓 프로그래밍</h3>
<ol>
<li>서버 프로그램에서는 서버소켓을 사용해서 서버 컴퓨터의 특정 포트에서 클라이언트의 연결요청 처리 준비한다.</li>
<li>클라이언트 프로그램은 접속할 서버의 IP주소와 포트 정보를 가지고 소켓을 생성해서 클라이언트의 소켓과 연결되도록 한다.</li>
<li>서버소켓은 클라이언트의 연결요청을 받으면 서버에 새로운 소켓을 생성햐서 클라이언트의 소켓과 연결되도록 한다.</li>
<li>이제 클라이언트의 소켓과 새로 생성된 서버의 소켓은 서버소켓과 관계없이 일대일 통신을 한다.</li>
</ol>
<h3 id="ucp소켓-프로그래밍">UCP소켓 프로그래밍</h3>
<p>연결지향적인 프로토콜이 아니기 때문에 ServerSocket이 필요하지 않다.
UCP통신에서 사용하는 소켓은 DatagramSocket이며, 데이터를 DatagramPacket에 담아서 전송한다.
DatagramPacket은 헤더와 데이터로 구성되어 있으며, 헤더에는 수신할 호스트의 정보가 저장되어 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Ch.13 쓰레드]]></title>
            <link>https://velog.io/@liy_se00/Java-Ch.13-%EC%93%B0%EB%A0%88%EB%93%9C</link>
            <guid>https://velog.io/@liy_se00/Java-Ch.13-%EC%93%B0%EB%A0%88%EB%93%9C</guid>
            <pubDate>Fri, 19 Jul 2024 06:51:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>👉🏻 이 글은 자바의 정석(3판) Ch.13을 공부하며 적은 내용입니다.</p>
</blockquote>
<h2 id="📌-프로세스와-쓰레드">📌 프로세스와 쓰레드</h2>
<h3 id="--프로세스process">- 프로세스(process)</h3>
<p>: <span style="color:olivedrab"><strong>&#39;실행 중인 프로그램&#39;</strong></span></p>
<ul>
<li>프로그램을 실행하면 OS로부터 실행에 필요한 자원(메모리)를 할당받아 프로세스가 된다.</li>
<li>프로세스는 프로그램을 실행하는 데 필요한 데이터와 메모리 등의 자원 그리고 쓰레드로 구성되어 있다.</li>
</ul>
<h3 id="--쓰레드thread">- 쓰레드(thread)</h3>
<p>: <span style="color:olivedrab"><strong>프로세스의 자원을 이용해서 실제로 작업을 수행하는 것.</strong></span></p>
<p>모든 프로세스에는 하나 이상의 쓰레드가 존재하며, 둘 이상의 쓰레드를 &#39;멀티쓰레드 프로세스&#39; 라고 부른다.</p>
<h4 id="--멀티-테스킹--멀티-쓰레딩">- 멀티 테스킹 / 멀티 쓰레딩</h4>
<ul>
<li><p><strong>멀티 테스킹</strong> (다중 작업) : 대부분의 OS는 멀티테스킹을 지원하기 때문에 여러 개의 프로세스가 동시에 실행될 수 있다.</p>
</li>
<li><p>⭐ <strong>멀티 쓰레딩</strong> : <span style="color:olivedrab">하나의 프로세스 내에서 둘 이상의 쓰레드가 동시에 작업을 수행하는 것이다.</span> 
CPU의 사용률을 향상시킨다, 각 쓰레드가 프로세스의 메모리를 공유하므로 시스템 자원의 낭비가 적다, 사용자와의 응답성이 좋아진다.
멀티 스레드 프로그램은 공유하는 자원에 대해 동기화 문제가 발생할 수 있다.
CPU의 core는 한 번에 단 하나의 작업만 수행할 수 있으므로 실제로 동시에 처리되는 작업의 개수는 코어의 개수와 일치한다.</p>
</li>
</ul>
<p>-&gt; 여러 쓰레드가 같은 프로세스 내에서 자원을 공유하며 작업을 하기 때문에 동기화, 교착상태 같은 문제들 발생 가능</p>
<hr>
<h2 id="📌-쓰레드의-구현과-실행">📌 쓰레드의 구현과 실행</h2>
<blockquote>
<p>Tread 클래스를 상속받는 방법과 Runnable인터페이스를 구현하는 방법 두 가지가 있다. 차이는 별로 없지만 Thread클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에 <strong>일반적으로 Runnable인터페이스를 구현한다.</strong></p>
</blockquote>
<p>Thread를 상속받을 경우와 Runnable인터페이스를 구현할 때의 인스턴스 생성방법이 다르다.</p>
<h3 id="1-thread클래스-상속받기">1. Thread클래스 상속받기</h3>
<pre><code class="language-java">class MyThread extends Thread {
    public void run() { /*작업내용*/ }    // Thread클래스의 run()을 오버라이딩
}</code></pre>
<h4 id="--인스턴스-생성">- 인스턴스 생성</h4>
<pre><code class="language-java">MyThread t = new MyThread();    // Thread 자손 클래스의 인스턴스 생성</code></pre>
<br>

<h3 id="2-runnable인터페이스-구현하기">2. Runnable인터페이스 구현하기</h3>
<pre><code class="language-java">class MyThread implements Runnable {
    public void run() { /*작업내용*/ }    // Runnable인터페이스의 run()을 구현
}</code></pre>
<p>Runnable인터페이스는 <strong>오직 run()만 정의</strong>되어 있는 간단한 인터페이스이다.</p>
<pre><code class="language-java">// Runnable인터페이스 내용
public interface Runnable{
    public abstract void run();
}</code></pre>
<blockquote>
<p>&quot;쓰레드를 구현한다&quot; = 쓰레드를 통해 작업하고자 하는 내용으로 run()의 몸통 { }을 채운다는 것이다.</p>
</blockquote>
<h4 id="--인스턴스-생성-1">- 인스턴스 생성</h4>
<pre><code class="language-java">class Thread_2 implements Runnable {
    public void run() {
        ...
    }
}

Runnable r = new Thread_2();
Thread t2 = new Thread(r);        //생성자 Thread(Runnable target)

// ⬇️ 위의 두 줄을 간단히
Thread t2 = new Thread(new Thread_2());</code></pre>
<p>Runnable인터페이스를 구현한 Thread_2에는 멤버가 run()밖에 없기 때문에 쓰레드의 이름을 호출할 때 <code>Thread.currentTread().getName();</code>와 같이 해야한다.</p>
<h3 id="👉🏻-쓰레드의-실행">👉🏻 쓰레드의 실행</h3>
<ul>
<li><code>start()</code>를 호출해야만 쓰레드 실행됨.</li>
<li>한 번 실행이 종료된 쓰레드는 다시 시작할 수 없다.</li>
<li>다시 수행하려면 새로운 쓰래드를 new로 다시 생성한다.</li>
</ul>
<ul>
<li><p><code>run()</code>은 쓰레드를 실행시키는 것이 아님. 단순히 클래스에 선언된 매서드를 호출하는 것임.
➡️ 반면 <code>start()</code>는 쓰레드가 작업 실행에 필요한 <strong>호출스택(call stack)을 생성</strong>한 다음에 <code>run()</code>을 호출해서 생성된 스택에 <code>run()</code>이 첫번째로 올라가게 한다. (독립된 공간에서 작업을 수행)</p>
</li>
<li><p><span style="color:olivedrab">실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.</span></p>
</li>
</ul>
<hr>
<h2 id="📌-싱글쓰레드--멀티쓰레드">📌 싱글쓰레드 &amp; 멀티쓰레드</h2>
<p>두 쓰레드가 서로 다른 자원을 사용하는 작업의 경우에는 싱글쓰레드 프로세스보다 멀티쓰레드 프로세스가 더 효율적이다. 
Ex. 사용자로부터 데이터 입력받기, 네트워크로 파일 주고받기....</p>
<pre><code class="language-java">import javax.swing.JOptionPane;

public class TreadEx {
    public static void main(String[] args) throws Exception{
        ThreadEx th = new ThreadEx();
        th.start();

        String input = JOptionPane.showInputDialog(&quot;아무 값이나 입력하세요.&quot;);
        System.out.println(&quot;입력하신 값은 &quot;+input+&quot;입니다.&quot;);
    }

    class ThreadEx extends Thread{
        public void run() {
            for(int i=10; i&gt;0; i--) {
                System.out.println(i);
                try {
                    sleep(10000);
                } catch(Exception e) {}
            }
        }
    }
}</code></pre>
<p>이렇게 하면 사용자가 입력을 마치지 않았어도 숫자가 출력되는 것을 볼 수 있다.</p>
<h2 id="📌-쓰레드의-우선순위">📌 쓰레드의 우선순위</h2>
<ul>
<li>쓰레드의 우선순위는 실행하기 전에만 변경할 수 있다.</li>
<li><code>void setPriority(int newPriority)</code> : 우선순위 변경</li>
<li><code>int getPriority()</code> : 우선순위 반환</li>
<li><code>MAX_PRIORITY = 10</code> : 최대 우선순위</li>
<li><code>MIN_PRIORITY = 1</code> : 최소 우선순위</li>
<li><code>NORM_PRIORITY = 10</code> : 중간 우선순위</li>
</ul>
<br>

<h2 id="📌-쓰레드-그룹">📌 쓰레드 그룹</h2>
<p><strong>모든 쓰레드는 반드시 쓰레드 그룹에 포함되어 있어야 한다.</strong>
-&gt; 만약 쓰레드 그룹 생성자를 사용하지 않으면 자기 자신의 쓰레드 그룹에 포함되어 있는 것으로 된다.</p>
<p>쓰레드 그룹은 서로 관련된 쓰레드를 그룹으로 다루기 위한 것으로, 묶어서 관리할 수 있게 한다.
자신이 속한 쓰레드 그룹이나 하위 쓰레드 그룹은 변경할 수 있지만, 다른 쓰레드 그룹의 쓰레드를 변경할 수 없다.</p>
<p><code>ThreadGroup(String name)</code> : 지정된 이름의 새로운 쓰레드 그룹을 생성</p>
<ul>
<li>쓰레드를 쓰레드 그룹에 포함시키려면 <strong>Thread의 생성자를 이용</strong>해야한다.
<code>Thread(ThreadGroup group, String name)</code>
<code>Thread(ThreadGroup group, Runnable target)</code>
<code>Thread(ThreadGroup group, Runnable target, String name)</code>
<code>Thread(ThreadGroup group, Runnable target, String name, long stackSize)</code></li>
</ul>
<hr>
<h2 id="📌-데몬-쓰레드">📌 데몬 쓰레드</h2>
<p>일반 쓰레드를 보조하는 역할로, 일반 쓰레드가 모두 종료되면 자동으로 종료된다.
Ex. 가비지 컬렉터, 워드 자동저장</p>
<hr>
<h2 id="📌-쓰레드-실행제어">📌 쓰레드 실행제어</h2>
<p>쓰레드 프로그래밍이 어려운 이유는 스케줄링(scheduling)과 동기화(synchronization)때문이다.</p>
<h3 id="쓰레드의-메서드">쓰레드의 메서드</h3>
<p>: sleep(ling millis), join() - 다른 쓰레드의 작업을 기다린다, interrupt() - 깨워서 실행대기상태, [ stop() - 즉시종료, suspend() - 일시정지, resume() - 실행대기상태로 ], yield() - 실행시간을 다른 쓰레드에게 양보하고 자신은 실행대기상태</p>
<p>[ ]는 쓰레드를 교착상태로 만들기 쉽기 때문에 deprecated되었다.</p>
<h3 id="쓰레드의-상태">쓰레드의 상태</h3>
<p><code>NEW</code> : 쓰레드가 생성되고 start()가 호출되지 않은 상태
<code>RUNNABLE</code> : 실행중, 실행가능 상태
<code>BLOCKED</code> : 동기화 블럭에 의해 일시정지된 상태
<code>WAITING</code>, <code>TIMED_WAITTING</code> : 작업이 종료되지는 않았지만 실행가능하지 않은 일시정지 상태 (후자는 정지시간이 지정된 경우)
<code>TERMINATED</code> : 쓰레드의 작업이 종료된 상태</p>
<br>

<h4 id="sleep---일정시간동안-쓰레드를-멈추게한다">*<em>sleep() *</em> : 일정시간동안 쓰레드를 멈추게한다.</h4>
<p>항상 try-catch문으로 예외를 처리해줘야 한다.
매번 처리해주기 번거롭기 때문에 매서드로 만들어서 사용하기도 한다.</p>
<pre><code class="language-java">void delay(ilong millis) {
    try{
        Tread.sleep(millis);
    } catch(InterruptedException e) {}
}</code></pre>
<p>실제로 영향 받는 것은 main메서드를 실행하는 main쓰레드이다.</p>
<h4 id="join--다른-쓰레드의-작업을-기다린다">join() : 다른 쓰레드의 작업을 기다린다.</h4>
<p><code>th1.join();</code>, <code>th1.join(long millis);</code> : 현재 실행중인 쓰레드가 쓰레드 th1의 작업이 끝날 때까지 기다린다.</p>
<p>sleep()처럼 interrupt()에 의해 대기상태에서 벗어날 수 있으며, 호출부분을 try-catch로 감싸야한다.</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/7a384c14-3eb1-446b-90fd-ef6ee794816c/image.png" alt=""></p>
<hr>
<h2 id="📌-쓰레드의-동기화">📌 쓰레드의 동기화</h2>
<p>멀티쓰레드 프로세스의 경우 여러 쓰레드가 같은 프로세스 내의 자원을 공유해서 작업하기 때문에 서로의 작업에 영향을 주게 된다. </p>
<p>그래서 한 쓰레드가 특정 작업을 끝마치기 전까지 다른 쓰레드에 의해 방해받지 않도록 하는 것이 필요하다. 그래서 도입된 개념이 <strong>&quot;임계 영역(critical section)&quot;과 &quot;잠금(lock)&quot;</strong>이다.</p>
<p>공유 데이터를 사용하는 코드 영역을 임계영역으로 지정해놓고, 공유 데이터가 가지고 있는 lock을 획득한 단 하나의 쓰레드만 이 영역 내의 코드를 수행할 수 있게 한다.</p>
<p>이처럼 <span style="background-color:#FFF6B9"><strong>한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는 것을 &#39;쓰레드의 동기화(synchronization)&#39;라고 한다.</strong></span></p>
<br>

<h3 id="1-synchronized를-이용한-동기화">1. synchronized를 이용한 동기화</h3>
<ul>
<li><code>synchronized</code> : 임계영역 설정 <br>
가장 간단한 동기화 방법이다.
메서드 전체를 임계영역으로 지정하거나 특정한 영역을 임계영역으로 지정할 수 있다.</li>
</ul>
<pre><code class="language-java">    public void withdraw(int money){
        if(balance &gt;= money){
            try{ Thread.sleep(1000); } catch(Exception e){}
            balance -= money;
        }
    }</code></pre>
<p>잔고를 확인하는 if문과 출금하는 문장은 하나의 임계영역으로 묶어져야 한다.
⬇️</p>
<ol>
<li>메소드에 synchronized 붙이기
: 한 쓰레드에 의해 먼저 withdraw가 호출되면, 이 메서드가 종료되어 lock이 반납될 때까지 다른 쓰레드는 withdraw를 호출하더라도 대기상태에 머물게 된다.<pre><code class="language-java"> public synchronized void withdraw(int money){
     if(balance &gt;= money){
         try{ Thread.sleep(1000); } catch(Exception e){}
         balance -= money;
     }
 }</code></pre>
</li>
<li>synchronized 블럭 사용<pre><code class="language-java"> public void withdraw(int money){
     synchronized(this){
         if(balance &gt;= money){
             try{ Thread.sleep(1000); } catch(Exception e){}
             balance -= money;
         }
     }
 }</code></pre>
</li>
</ol>
<h3 id="wait과-notify">wait()과 notify()</h3>
<p><code>wait()</code>에 의해 lock을 반납했다가, 나중에 다시 작업을 진행할 수 있는 상황이 되면 <code>notify()</code>를 호출해서 다시 lock을 얻어서 임계영역에 들어와 중단했던 쓰레드를 다시 진행한다. 이것을 재진입(reentrance)이라고 한다.</p>
<p>매개변수가 있는 wait()은 지정된 시간동안만 기다린다.</p>
<h3 id="2-lock과-condition을-이용한-동기화">2. Lock과 Condition을 이용한 동기화</h3>
<p>&#39;java.util.concurrent.locks&#39;패키지가 제공하는 lock클래스들을 이용하는 방법이다. 가은 메서드 내에서만 lock을 걸 수 있다는 제약이 불편할 때 이 lock클래스를 이용한다.</p>
<h4 id="lock-클래스의-종류-3가지">lock 클래스의 종류 3가지</h4>
<ol>
<li><code>ReentrantLock</code> : 재진입 가능 lock. 가장 일반적인 배타 lock</li>
<li><code>ReentranrReadWriteLock</code> : 읽기에는 공유적, 쓰기에는 배타적</li>
<li><code>StampedLock</code> : ReentrantReadWriteLock에 낙관적인 기능 추가</li>
</ol>
<p>-&gt; 무조건 읽기 lock을 걸지 않고, 쓰기와 읽기가 충돌할 때만 쓰기가 끝난 후에 읽기 lock을 거는 것이다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Ch.12 제네릭스, 열거형, 애너테이션]]></title>
            <link>https://velog.io/@liy_se00/Java-Ch.12-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%8A%A4-%EC%97%B4%EA%B1%B0%ED%98%95-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@liy_se00/Java-Ch.12-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%8A%A4-%EC%97%B4%EA%B1%B0%ED%98%95-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98</guid>
            <pubDate>Sun, 14 Jul 2024 14:38:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>👉🏻 이 글은 자바의 정석(3판) Chapter.12을 공부하고 적은 내용입니다.</p>
</blockquote>
<h1 id="📌-1-제네릭스generics">📌 1. 제네릭스(Generics)</h1>
<blockquote>
<p>👉🏻 <strong>제네릭스(Generics)란?</strong>
다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능이다. 컴파일 시에 타입을 체크하기 때문에 </p>
</blockquote>
<ol>
<li><strong>객체의 타입 안정성을 높이고</strong></li>
<li><strong>형변환의 번거로움이 줄어든다</strong>. (코드가 간결해 진다.)</li>
</ol>
<p>👉🏻 다룰 객체의 타입을 미리 멸시해 줌으로써 번거로운 형변환을 줄여준다.</p>
<p>제네릭 타입은 클래스와 메서드에 선언할 수 있다.</p>
<pre><code class="language-java">class Box&lt;T&gt; {    // 제네릭 타입 T 선언
    T item;
    void setItem(T item) { this.item = item; }
    T getItem() { return item; }
}

Box&lt;String&gt; b = new Box&lt;String&gt;();    // 제네릭 타입 호출
b.setItem(&quot;ABC&quot;);</code></pre>
<p>위에서 <code>T</code>는 <span style="background-color:#FFF6B9">타입 변수</span> (타입변수는 T가 아닌 다른 것도 사용 가능)
Ex. <code>ArrayList&lt;E&gt;</code>, <code>Map&lt;K, V&gt;</code></p>
<p>이들은 기호만 다를 뿐 &#39;<strong>임의의 참조형 타입</strong>&#39;을 의미한다는 것은 모두 같다.</p>
<ul>
<li><code>Box&lt;T&gt;</code> : 제네릭 클래스</li>
<li><code>T</code> : 타입 변수, 타입 매개변수 (T는 타입 문자)</li>
<li><code>Box</code> : 원시 타입 (raw type)</li>
<li><code>String</code> : 대입된 타입 (매개변수화된 타입)<br>
### ⚠️ 제네릭스의 제한
</li>
</ul>
<ol>
<li><span style="background-color:#FFF6B9"><code>static</code>멤버에 타입변수 <code>T</code>를 사용할 수 없다.</span>
: static 멤버는 대입된 타입의 종류에 관계없이 동일한 것이어야 하기 때문.<pre><code class="language-java"> static T item;    // Error!
 static int compare(T t1, T t2) { ... }    // Error!</code></pre>
</li>
<li><span style="background-color:#FFF6B9">제네릭 타입의 배열 생성 불가능</span>
: <code>new</code>연산자 때문. 컴파일 시점에 T가 어떤 타입이 될지 전혀 알 수 없기 때문이다. <code>instanceof</code>도 마찬가지이다.<pre><code class="language-java"> T[] itemArr;    // OK. T타입의 배열을 위한 참조변수
 T[] toArray(){
     T[] tmpArr = new T[itemArr.length];    // Error! 생성불가
     ...
     return tmpArr;
 } </code></pre>
</li>
</ol>
<p>--&gt; <code>newInstance()</code>와 같이 동적 객체 생성 메서드 이용, Object배열을 생성해서 복사한 다음 <code>T[]</code>로 형변환 하는 방법 사용</p>
<br>

<h3 id="--제네릭-클래스의-객체-생성과-사용">- 제네릭 클래스의 객체 생성과 사용</h3>
<pre><code class="language-java">class Box&lt;T&gt; {
  ArrayList&lt;T&gt; list = new ArrayList&lt;T&gt;();

  void add(T item)            { list.add(item);        }
  T get(int i)                { return list.get(i);    }
  ArrayList&lt;T&gt; getList        { return list;            }
  int size()                { return list.size();    }
  public String toString()    { return list.toString(); }</code></pre>
<ol>
<li>참조변수와 생성자에 대입된 타입이 일치해야 한다. (상속관계도 X)</li>
<li>단, 제네릭 클래스의 타입이 상속관계에 있는 것은 괜찮다.</li>
<li>JDK1.7부터는 추정이 가능한 경우 생성자에 타입 생략 가능.</li>
<li>대입된 타입과 다른 타입의 객체는 추가 불가능 (자손들은 가능)</li>
</ol>
<br>

<h3 id="--제한된-제네릭스">- 제한된 제네릭스</h3>
<p>👉🏻 <strong>자손타입으로 제한 가능</strong></p>
<pre><code class="language-java">class Fruit&lt;T extends Fruit&gt; {
    ArrayList&lt;T&gt; list = new ArrayList&lt;T&gt;;
    ...
}</code></pre>
<p>⚠️ <span style="background-color:rgb(255, 185, 185)"> <strong>인터페이스</strong>를 구현할 때 또한 <code>implements</code>가 아닌 <code>extends</code>를 사용한다.</span> 
여러 개를 구현할 때는 <code>&amp;</code>기호로 연결.</p>
<br>

<h3 id="--와일드-카드">- 와일드 카드</h3>
<pre><code class="language-java">static Jucie makeJucie(FruitBox&lt;Fruit&gt; box) {
    ...
}
static Jucie makeJucie(FruitBox&lt;Apple&gt; box) {
    ...
}
</code></pre>
<p>⚠️ 위와 같이 오버로딩 하면 컴파일 에러가 발생한다. 제네릭 타입이 다른 것만으로는 <strong>오버로딩(new)이 성립하지 않는다</strong>. 위는 <strong>&#39;메서드 중복 정의&#39;</strong>이다.</p>
<p>----&gt; &quot;와일드 카드(?)&quot; 고안</p>
<p><code>&lt;? extends T&gt;</code> : 와일드카드 상한 제한 (T와 그 자손들)
<code>&lt;? super T&gt;</code> : 와일드카드 하한 제한 (T와 그 조상들)
<code>&lt;?&gt;</code> : 제한 없음. 모든 타입 가능 ( = <code>&lt;? extends Object&gt;</code> )</p>
<h3 id="--제네릭-메서드">- 제네릭 메서드</h3>
<p>메서드의 선언부에 제네릭 타입이 선언된 메서드를 제네릭 메서드라 한다.</p>
<pre><code class="language-java">static &lt;T&gt; void sort (List&lt;T&gt; list, Comparator&lt;? super T&gt; c)</code></pre>
<p>클래스의 타입변수와 메서드의 타입변수는 완전히 별개의 것이다.</p>
<p>static 멤버에는 타입 매개변수를 사용할 수 없지만, 메서드에 제네릭 타입을 선언하고 사용하는 것은 가능하다.</p>
<ul>
<li><p>복잡하게 선언된 제네릭 메서드</p>
<pre><code class="language-java">  public static &lt;T extends Comparable&lt;? super T&gt;&gt; void sort (List&lt;T&gt; list)

  // ⬇️ List &lt;T&gt;의 요소가 Comparable인터페이스를 구현한 것이어야 한다.
  public static &lt;T extends Comparable&lt;T&gt;&gt; void sort (List&lt;T&gt; list)</code></pre>
</li>
</ul>
<ol>
<li>타입 T를 요소로 하는 List를 매개변수로 허용한다.</li>
<li>&#39;T&#39;는 Comparable을 구현한 클래스여야 하며 
&#39;T&#39; 또는 그 조상의 타입을 비교하는 Comparable이어야 한다.</li>
</ol>
<hr>
<h1 id="📌-2-열거형enums">📌 2. 열거형(enums)</h1>
<blockquote>
<p>👉🏻 <strong>열거형(enums)이란?</strong>
JDK1.5부터 새로 추가되었다.
열거형이 갖는 값뿐만 아니라 타입까지 관리. (타입에 안전한 열거형)
: 실제 값이 같아도 타입이 다르면 조건식의 결과가 false가 된다.</p>
</blockquote>
<p>모든 열거형의 조상은 <strong>java.lang.Enum</strong> 이다.</p>
<pre><code class="language-java">class Card{
    static final int CLOVER = 0;
    static final int HEART= 1;
    static final int DIAMOND = 2;
    static final int SPADE = 3;

    static final int TWO = 0;
    static final int THREE = 1;
    static final int FOUR = 2;

    final int kind;
    final int num;
}</code></pre>
<p>⬇️ 열거형으로</p>
<pre><code class="language-java">class Card{
    // 정의 : enum 열거형이름 { 상수명1, 상수명2, ... }
    enum Kind    { COLVER, HEART, DIAMOND, SPADE }
    enum Value    { TWO, THREE, FOUR }

    final Kind kind;    // type이 int가 아님에 유의
    final Value value;
}

if(Card.Kind.CLOVER == Card.Value.TWO)    // false. 타입이 다르기 때문.</code></pre>
<ul>
<li><code>==</code> 사용 가능 (static 상수의 값이 객체의 주소이고 이 값은 바뀌지 않기 때문)</li>
<li>비교연산자 사용 불가능 ( -&gt; <code>compareTo()</code> 사용 )</li>
<li>switch문의 조건식에도 열거형 사용</li>
<li><span style="color:CornflowerBlue">열거형 상수 하나하나가 객체이다.</span></li>
</ul>
<h3 id="--열거형에-멤버-추가">- 열거형에 멤버 추가</h3>
<pre><code class="language-java">enum Direction { 
    EAST(1), SOUTH(5), WEST(-1), NORTH(10);

    private final int value;    // 정수를 저장할 필드(인스턴스 변수) 추가
    Direction(int value) { this.value = value; }    // 생성자

    public int getValue() { return value; }    // 외부 접근을 가능하게 한다.
}</code></pre>
<p>열거형의 생성자는 묵시적으로 <code>private</code>이다.</p>
<h3 id="--열거형에-추상-메서드-추가하기">- 열거형에 추상 메서드 추가하기</h3>
<ul>
<li><strong>열거형에 추상메서드를 선언하면 각 열거형 상수가 이 추상메서드를 반드시 구현해야 한다.</strong></li>
</ul>
<hr>
<h1 id="📌-3-애너테이션annotation">📌 3. 애너테이션(annotation)</h1>
<blockquote>
<p>👉🏻 <strong>애너테이션(annotation)이란?</strong>
애너테이션의 뜻은 주석, 주해, 메모이다.
미리 정의된 <code>@</code>태그들을 이용해서 주석 안에 정보를 저장하고, javadoc.exe라는 프로그램이 이를 읽어서 문서를 작성하는데 사용한다.
👉🏻 이를 이용하여 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것이 애너테이션이다.</p>
</blockquote>
<p>JDK에서 제공하는 애너테이션은 java.lang.annotation 패키지에 포함되어 있다.</p>
<h2 id="📖-표준-애너테이션">📖 표준 애너테이션</h2>
<p>자바에서 기본적으로 제공하는 애너테이션은 몇 개 없다. 그나마 이들의 일부는 메타 애너테이션으로, 애너테이션을 정의하는데 사용되는 애너테이션의 애너테이션이다.</p>
<table>
<thead>
<tr>
<th>애너테이션</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>@Override</td>
<td>컴파일러에게 오버라이딩하는 대상이라는 것을 알린다.</td>
</tr>
<tr>
<td>@Deprecated</td>
<td>앞으로 사용할 것을 권장하는 대상에 붙인다.</td>
</tr>
<tr>
<td>@SuppressWarning</td>
<td>컴파일러의 특정 경고가 나타나지 않게 해준다.</td>
</tr>
<tr>
<td>@FunctionalInterface</td>
<td>함수형 인터페이스라는 것을 알린다.</td>
</tr>
<tr>
<td>@SafeVarargs</td>
<td>제네릭스 타입의 가변인자에 사용.</td>
</tr>
<tr>
<td>@Native</td>
<td>native메서드에서 참조되는 상수 앞에 붙인다.</td>
</tr>
</tbody></table>
<h3 id="override">@Override</h3>
<p>컴파일러가 같은 이름의 메서드가 조상에 있는지 확인하고 없으면 에러 메세지를 출력한다. 이는 알아내기 어려운 실수를 미연에 방지해주므로 반드시 붙이는 것이 좋다.</p>
<h2 id="📖-메타-애너테이션">📖 메타 애너테이션</h2>
<p>애너테이션을 위한 애너테이션으로, 애너테이션을 정의할 때 <strong>애너테이션 적용대상(target)이나, 유지기간(retention)을 지정하는데 사용</strong>된다.</p>
<table>
<thead>
<tr>
<th>애너테이션</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>@Target</td>
<td>애너테이션이 적용가능한 대상을 지정하는데 사용</td>
</tr>
<tr>
<td>@Documented</td>
<td>애너테이션 정보가 작성된 문서에 포함되게 한다.</td>
</tr>
<tr>
<td>@Inherited</td>
<td>애너테이션이 자손 클래스에 상속되도록 한다.</td>
</tr>
<tr>
<td>@Retention</td>
<td>애너테이션이 유지되는 범위를 지정하는데 사용.</td>
</tr>
<tr>
<td>@Repeatable</td>
<td>애너테이션을 반복해서 적용 가능하게 한다.</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Ch.11 컬렉션 프레임워크]]></title>
            <link>https://velog.io/@liy_se00/Java-Ch.11-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@liy_se00/Java-Ch.11-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Fri, 12 Jul 2024 06:40:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>👉🏻 이 글은 자바의 정석(3판) Chapter.11을 공부하고 적은 내용입니다.</p>
</blockquote>
<h1 id="📌-컬렉션-프레임워크">📌 컬렉션 프레임워크</h1>
<ul>
<li>컬렉션 : 다수의 데이터(데이터 그룹)</li>
<li>프레임워크 : 표준화된 프로그래밍 방식</li>
</ul>
<p>➡️ <strong>컬렉션 프레임워크</strong> : 
데이터 군을 다루고 표현하기 위한 단일화된 구조, 
컬렉션과 다수의 데이터를 다루는 데 필요한 다양하고 풍부한 클래스들을 제공</p>
<p>JDK1.2 이전까지의 컬렉션 클래스 - Vector, Hashtable, Properties, Stack
JDK1.2 이후부터 컬렉션 프레임워크 등장 - ArrayList, LinkedList, HashSet, ......</p>
<p>cf) JDK1.5부터 Iterable 인터페이스 추가</p>
<h2 id="📖-핵심-인터페이스">📖 핵심 인터페이스</h2>
<p>컬렉션 데이터 그룹을 크게 3타입이 존재한다고 인식하고 인터페이스 정의</p>
<table>
<thead>
<tr>
<th>인터페이스</th>
<th>구현 클래스</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td><strong>List</strong></td>
<td>ArrayList, LinkedList, Stack, Vector 등</td>
<td>순서가 있는 데이터의 집합. 데이터 중복을 허용.</td>
</tr>
<tr>
<td><strong>Set</strong></td>
<td>HashSet, TreeSet 등</td>
<td>순서를 유지하지 않는 데이터의 집합. 데이터 중복을 허용하지 않는다.</td>
</tr>
<tr>
<td><strong>Map</strong></td>
<td>HashMap, TreeMap, Hashtable, Properties 등</td>
<td>키(key)와 값(value)의 쌍(pair)으로 이루어진 데이터의 집합. 순서유지X, 키는 중복을 허용하지 않는다. (Map : 어떤 두 값을 연결한다.)</td>
</tr>
</tbody></table>
<p>⚠️ Vector나 Hashtable과 같은 기존 컬렉션들은 호환을 위해 남겨두었지만 가능하면 사용하지 않는 것이 좋다. (-&gt; ArrayList, HashMap 사용 권장)</p>
<h3 id="⭐-collection-인터페이스">⭐ Collection 인터페이스</h3>
<p>List와 Set의 부모클래스로, 두 클래스의 공통된 부분을 뽑아 정의한 것이다.</p>
<p>Collection 인터페이스는 컬렉션 클래스에 저장된 데이터를 읽고, 추가, 삭제하는 등 컬렉션을 다루는데 가장 기본적인 메소드들을 저장하고 있다.</p>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>boolean add(Object o)</code></td>
<td>지정된 객체(o) 또는 Collection(c)의 객체들을 Collection에 추가한다.</td>
</tr>
<tr>
<td><code>boolean addAll(Collection c)</code></td>
<td></td>
</tr>
<tr>
<td><code>void clear()</code></td>
<td>Collection의 모든 객체를 삭제한다.</td>
</tr>
<tr>
<td><code>boolean contains(Object o)</code></td>
<td>지정된 객체(o) 또는 Collection의 객체들(c)이 Collection에 포함되어 있는지 확인한다.</td>
</tr>
<tr>
<td><code>boolean containsAll(Collection c)</code></td>
<td></td>
</tr>
<tr>
<td><code>boolean equals(Object o)</code></td>
<td>동일한 Collection인지 비교한다.</td>
</tr>
<tr>
<td><code>int hashCode()</code></td>
<td>Collection의 hash code를 반환한다.</td>
</tr>
<tr>
<td><code>boolean isEmpty()</code></td>
<td>Collection이 비어있는지 확인</td>
</tr>
<tr>
<td><code>Iterator iterator()</code></td>
<td>Collection의 Iterator를 얻어서 반환한다.</td>
</tr>
<tr>
<td><code>boolean remove(Object o)</code></td>
<td>지정된 객체를 삭제한다.</td>
</tr>
<tr>
<td><code>boolean removeAll(Colection c)</code></td>
<td>지정된 Collection에 포함된 객체들을 삭제한다.</td>
</tr>
<tr>
<td><code>boolean retainAll(Collection c)</code></td>
<td>지정된 Collection에 포함된 객체만을 남기고 다른 객체들은 Collection에서 삭제한다. 이 작업으로 인해 Collection에 변화가 생기면 true, 없으면 false를 반환한다.</td>
</tr>
<tr>
<td><code>int size()</code></td>
<td>Collection에 저장된 객체의 개수를 반환한다.</td>
</tr>
<tr>
<td><code>Object[] toArray()</code></td>
<td>Collection에 저장된 객체를 객체배열(Object[])로 반환한다.</td>
</tr>
<tr>
<td><code>Object[] toArray(Object[] a)</code></td>
<td>지정된 배열에 Collection의 객체를 저장해서 반환한다.</td>
</tr>
</tbody></table>
<p>⚠️ <strong>Collection은 인터페이스이고, Collections는 클래스이다.</strong></p>
<hr>
<h1 id="📌-list">📌 List</h1>
<h2 id="📖-list-인터페이스">📖 List 인터페이스</h2>
<p><strong>중복을 허용</strong>하면서 <strong>저장순서가 유지</strong>되는 컬렉션을 구현하는데 사용한다.</p>
<h3 id="⭐-arraylist">⭐ ArrayList</h3>
<p>ArrayList는  List인터페이스를 구현하기 때문에 데이터의 저장순서 유지, 중복 허용</p>
<ul>
<li><strong>기존의 Vector를 개선한것으로 Vector와 구현원리와 기능적인 측면에서 동일하다고 할 수 있다.</strong> Vector는 기존에 작성된 코드와의 호환성을 위해서 계속 남겨 두고 있을 뿐이기 때문에 가능하면 Vector보다는 ArrayList를 사용하자.</li>
</ul>
<p>⬇️ArrayList 소스코드 일부</p>
<pre><code class="language-java">public class ArrayList extends AbstractList 
    implements List, RandomAccess, Cloneable, java.io.Serializable{
        ...
    transient Object[] elementData;    // Object배열
        ...
}</code></pre>
<p><strong>Object 배열</strong>을 이용해서 데이터를 순차적으로 저장한다.
선언된 배열의 타입이 Object이므로 모든 종류의 객체를 담을 수 있다.</p>
<br>

<p>⬇️ ArrayList의 생성자와 매서드</p>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>ArrayList()</td>
<td>크기가 10인 ArrayList 생성</td>
</tr>
<tr>
<td>ArrayList(Collection c)</td>
<td>주어진 컬렉션이 저장된 ArrayList를 생성</td>
</tr>
<tr>
<td>ArrayList(int initialCapacity)</td>
<td>지정된 초기용량을 갖는 ArrayList 생성</td>
</tr>
<tr>
<td>Object clone()</td>
<td>ArrayList를 복제한다.</td>
</tr>
<tr>
<td>void ensureCapacity(int minCapacity)</td>
<td>ArrayList의 용량이 최소한 minCapacity가 되도록 한다.</td>
</tr>
<tr>
<td>List subList(int fromIndex, int toIndex)</td>
<td>fromIndex부터 toIndex 사이에 저장된 객체를 반환한다.</td>
</tr>
<tr>
<td>Object[] toArray()</td>
<td>ArrayList에 저장된 모든 객체들을 객체배열로 반환한다.</td>
</tr>
<tr>
<td>Object[] toArray(Object[] a)</td>
<td>ArrayList에 저장된 모든 객체들을 객체배열 a에 담아 반환한다.</td>
</tr>
<tr>
<td>void trimToSize()</td>
<td>용량을 크기에 맞게 줄인다.(빈 공간을 없앤다.)</td>
</tr>
<tr>
<td>...</td>
<td></td>
</tr>
</tbody></table>
<br>

<p>⬇️ 아래는 list2에서 list1과 공통되는 요소들을 찾아 삭제하는 부분이다.</p>
<pre><code class="language-java">for(int i=list2.size()-1; i&gt;=0; i--) {
    if(list1.contains(list2.get(i));
        list2.remove(i);
}</code></pre>
<p>⚠️ <span style="color:indianred">만일 변수 i를 증가시켜가면서 삭제하면, <strong>한 요소가 삭제될 때마다 빈 공간을 채우기 위해 나머지 요소들이 자리이동</strong>을 하기 때문에 올바른 결과를 얻을 수 없다. </span>
그래서 제어변수를 감소시켜가며 삭제를 해야 자리이동이 발생해도 영향을 받지 않고 작업이 가능하다.</p>
<p>⚠️ ArrayList를 생성할 때, 저장할 요소의 개수를 고려해서 <strong>실제 저장할 개수보다 약간 여유있는 크기로</strong> 하는 것이 좋다. </p>
<pre><code class="language-java">    List list = new ArrayList(length/LIMIT +10);</code></pre>
<p>생성할 때 지정한 크기보다 더 많은 객체를 저장하면 자동적으로 크기가 늘어나기는 하지만 이 과정에서 처리시간이 많이 소요되기 때문이다.</p>
<br>
<br>
Vector의 용량과 크기에 관한 코드

<pre><code class="language-java">class VectorEx1{
    public static void main(String[] args) {
            Vector v = new Vector(5);
            v.add(&quot;1&quot;);
            v.add(&quot;2&quot;);
            v.add(&quot;3&quot;);
            print(v);

            v.trimToSize();    // 빈 공간을 없앤다. (용량과 크기가 같아진다.)
            System.out.println(&quot;=== After trimToSize() ===&quot;);
            print(v);

            v.ensureCapacity(6);
            System.out.println(&quot;=== After ensureCapacity(6) ===&quot;);
            print(v);

            v.setSize(7);
            System.out.println(&quot;=== After setSize(7) ===&quot;);
            print(v);

            v.clear();
            System.out.println(&quot;=== After clear() ===&quot;);
            print(v);
        }

    public static void print(Vector v) {
            System.out.println(v);
            System.out.println(&quot;size :&quot;+v.size());
            System.out.println(&quot;capacity :&quot;+v.capacity());
            System.out.println();
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/41f18dde-1c2d-4d6c-bd71-4d12139201d6/image.png" alt=""></p>
<p>--&gt; <strong>배열은 크기를 변경할 수 없기 때문에</strong> 새로운 배열을 생성해서 그 주소값을 변수 v에 할당한다. 기존의 Vector 인스턴스는 더 이상 사용할 수 없으며, 후에 가비지 컬렉터에 의해 메모리에서 제거된다.</p>
<p>---&gt; 따라서 ArrayList나 Vector과 같이 배열을 이용한 자료구조는 데이터를 읽어오고 저장하는 데는 효율이 좋지만 (접근 시간, access time), <strong>용량을 변경해야 할 때는 데이터 복사 과정을 거치기 때문에 상당히 효율이 떨어진다.</strong></p>
<p>----&gt; 처음에 인스턴스를 생성할 때 <strong>저장할 데이터의 개수를 잘 고려해서 충분한 용량의 인스턴스를 생성</strong>하는 것이 좋다. (이 방법을 이용할 시, 메모리 낭비 가능성이 있는 것이 배열의 단점임.)</p>
<hr>
<h3 id="⭐-linkedlist">⭐ LinkedList</h3>
<p>위의 배열의 단점을 보완하기 위해 <code>LinkedList</code>라는 자료구조가 고안됨.
배열은 모든 데이터가 연속적으로 존재하지만 링크드 리스트는 <span style="background-color:#FFF6B9">불연속적으로 존재하는 데이터를 서로 연결한 형태</span>로 구성되어 있다.</p>
<p>링크드 리스트의 각 요소(node)들은 자신과 연결된 <strong>다음 요소에 대한 참조(주소값)와 데이터로 구성</strong>되어 있다.</p>
<pre><code class="language-java">class Node {
    Node next;        // 다음 요소의 주소를 저장
    Object obj;        // 현 노드의 데이터 저장
}</code></pre>
<h4 id="--요소-삭제--추가">- 요소 삭제 &amp; 추가</h4>
<ul>
<li><strong>삭제</strong> : 삭제하고자 하는 요소의 이전 요소가 삭제하고자 하는 요소의 다음 요소를 참조하도록 변경하기만 하면 된다.</li>
<li><strong>추가</strong> : 새로운 요소를 생성한 다음 추가하고자 하는 위치의 이전 요소의 참조를 새로운 요소에 대한 참조로 변경, 새로운 요소가 그 다음 요소를 참조하도록 변경.</li>
</ul>
<p>이동방향이 단방향 -&gt; 이전요소에 대한 접근은 어려움 --&gt; <span style="background-color:#FFF6B9"><strong>더블 링크드 리스트</strong></span></p>
<h4 id="--더블-링크드-리스트-이중-연결리스트">- 더블 링크드 리스트 (이중 연결리스트)</h4>
<p>: 양방향 가능하도록 앞뒤 연결</p>
<pre><code class="language-java">class Node {
    Node next;        // 다음 요소의 주소를 저장
    Node previous;    // 이전 요소의 주소를 저장
    Object obj;        // 현 노드의 데이터 저장
}</code></pre>
<p>⬇️ 접근성을 더욱 향상시킨 것</p>
<h4 id="--더블-써큘러-링크드-리스트이중-원형-연결리스트">- 더블 써큘러 링크드 리스트(이중 원형 연결리스트)</h4>
<p>: 첫 번째 요소와 마지막 요소를 서로 연결시킨 것.</p>
<p>LinkedList 역시 List인터페이스를 구현했기 때문에 ArrayList와 내부구현방법만 다를뿐 제공하는 메서드의 종류와 기능은 거의 같다.</p>
<hr>
<h3 id="⭐-stack과-queue">⭐ Stack과 Queue</h3>
<p><strong>Stack</strong> : 마지막에 저장한 데이터를 가장 먼저 꺼내는 LIFO구조 (동전통 구조)
Ex) 수식계산, 수식 괄호 검사, 웹 브라우저 뒤로/앞으로....</p>
<ul>
<li>Stack의 메서드</li>
</ul>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>boolean empty()</td>
<td>Stack이 비어있는지 알려준다.</td>
</tr>
<tr>
<td>Object peek()</td>
<td>맨 위에 있는 객체를 반환. pop과 달리 객체를 꺼내지는 않음. <br>(비었을 경우 EmptyStackException 발생함)</td>
</tr>
<tr>
<td>Object pop()</td>
<td>맨 위에 저장된 객체를 꺼낸다. <br>(비었을 경우 EmptyStackException 발생함)</td>
</tr>
<tr>
<td>Object push(Object item)</td>
<td>Stack에 객체를 저장한다.</td>
</tr>
<tr>
<td>int search(Object o)</td>
<td>주어진 객체를 찾아서 그 위치를 반환. 못 찾으면 -1 반환. <br>(배열과 달리 위치는 1부터 시작)</td>
</tr>
</tbody></table>
<p><strong>Queue</strong> : 처음에 저장한 데이터를 가장 먼저 꺼내는 FIFO구조 (파이프 구조)
Ex) 최근 사용 문서, 인쇄작업 대기 목록, 버퍼(buffer)....</p>
<ul>
<li>Queue의 메서드</li>
</ul>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>boolean add(Object o)</td>
<td>지정된 객체를 Queue에 추가한다. 성공하면 true, <br>저장공간이 부족하면 IllegalStateException발생</td>
</tr>
<tr>
<td>Object remove()</td>
<td>객체를 꺼내 반환. 비어있으면 NoSuchElementException 발생</td>
</tr>
<tr>
<td>Object element()</td>
<td>삭제 없이 요소를 읽어온다. <br>peek와 달리 Queue가 비어있을 때 NoSuchElementException 발생</td>
</tr>
<tr>
<td>Object offer(Object o)</td>
<td>Queue에 객체를 저장. 성공하면 true, 실패하면 false</td>
</tr>
<tr>
<td>Object poll()</td>
<td>Queue에서 객체를 꺼내 반환. 비어있으면 null 반환.</td>
</tr>
<tr>
<td>Object peek()</td>
<td>삭제 없이 요소를 읽어 온다. Queue가 비어있으며 null을 반환</td>
</tr>
</tbody></table>
<p>👉🏻 Queue의 인터페이스 기능 사용할 때 아래의 Java API문서 참고
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/Queue.html">https://docs.oracle.com/javase/8/docs/api/java/util/Queue.html</a></p>
<ul>
<li><p><strong>PriorityQueue</strong>
: <strong>Queue의 구현체</strong> 중 하나, 저장한 <strong>순서와 관계없이 우선순위가 높은 것부터 꺼내는</strong> 특징이 있다. ( <code>null</code>은 저장할 수 없다. )
저장공간으로 배열을 사용, 각 요소를 힙이라는 자료구조 형태로 저장.</p>
</li>
<li><p><strong>Deque 덱, 다큐 (Double-Ended Queue)</strong>
: Queue의 변형. 양쪽 끝에 추가/삭제가 가능하다.
Deque의 조상은 Queue, 구현체는 ArrayDeque, LinkedList
스택과 큐를 하나로 합쳐 놓은 것과 같다.</p>
</li>
</ul>
<table>
<thead>
<tr>
<th>Deque</th>
<th>Queue</th>
<th>Stack</th>
</tr>
</thead>
<tbody><tr>
<td>offerLast()</td>
<td>offer()</td>
<td>push()</td>
</tr>
<tr>
<td>pollLast()</td>
<td>-</td>
<td>pop()</td>
</tr>
<tr>
<td>pollFist()</td>
<td>poll()</td>
<td>-</td>
</tr>
<tr>
<td>peekFirst()</td>
<td>peek()</td>
<td>-</td>
</tr>
<tr>
<td>peekLast()</td>
<td>-</td>
<td>peek()</td>
</tr>
</tbody></table>
<hr>
<h2 id="📖-enumeration-iterator-listiterator">📖 Enumeration, Iterator, ListIterator</h2>
<blockquote>
<p>모두 컬렉션에 저장된 요소를 접근하는데 사용되는 인터페이스이다. Enumeration은 Iterator의 구버전이며, ListIterator는 Iterator의 기능을 향상시킨 것이다.</p>
</blockquote>
<h3 id="⭐-iterator">⭐ Iterator</h3>
<pre><code class="language-java">public interface Iterator{
    boolean hashNext();        // 읽어 올 요소가 남아있는지 확인한다.
    Object next();            // 다음 요소를 읽어 온다.
    void remove();            // next()로 읽어 온 요소를 삭제. (선택적 기능)
}

public interface Collention {
    ...
    public Iterator iterator();        // List와 Set에서도 포함되어 있음.
    ...
}

// 리스트에 저장된 요소들을 출력하기 위한 코드
List list = new ArrayList(); // 다른 컬렉션으로 변경할 때는 이 부분만 고치면 된다.
Iterator it = list.iterator();

while(it.hashNext()) {
    System.out.println(it.next());
}

// Map인터페이스에서 Iterator 사용.
Map map = new HashMap();
    ...
    // 키와 값을 Set형태로 받아온 다음 iterator() 호출
    Iterator it = map.keySet().iterator();        // 키를 Set형태로 받아옴
    Iterator list = map.entrySet().iterator();    // 값를 Set형태로 받아옴</code></pre>
<h3 id="⭐-enumeration">⭐ Enumeration</h3>
<p>컬렉션 프레임워크가 만들어지기 전 기능으로, Iterator의 구버전임.
메서드 이름만 다를 뿐 기능은 같다.</p>
<ul>
<li>boolean hashMoreElements() : 읽어 올 요소가 남아있는지 확인
( = hasNext() )</li>
<li>Object nextElement() : 다음 요소를 읽어온다.
( = next() )</li>
</ul>
<h3 id="⭐-listiterator">⭐ ListIterator</h3>
<p>Iterator를 상속받아서 기능을 추가한 것으로, ListIterator는 양방향으로의 이동이 가능하다. 다만, ArrayList나 LinkedList와 같이 List인터페이스를 구현한 컬렉션에서만 사용할 수 있다.</p>
<p><code>add(Object o)</code>, <code>hasNext()</code>,<code>hasPrevious()</code>, <code>next()</code>, <code>previous()</code>, <code>nextIndex()</code>, <code>remove()</code>, <code>set(Object o)</code> 가 메서드로 있음.  </p>
<ul>
<li>이동하기 전에 반드시 hasNext()나 hasPrevious()를 호출해서 이동할 수 있는지 확인해야 한다.</li>
</ul>
<pre><code class="language-java">ListIterator it = list.listIterator();

while(it.hasNext()) {        // 순방향으로 진행하면서 읽어옴
    System.out.println(it.next());
}

while(it.hasPrevious()) {    // 역방향으로 진행하면서 읽어옴
    System.out.println(it.previous());
}</code></pre>
<ul>
<li><code>remove()</code>는 단독으로 쓰일 수 없고, <code>next()</code>를 호출하여 읽어온 것을 삭제하는 역할을 한다. (읽어온 값이 있어야 호출될 수 있다.)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/60a81c66-c878-429c-aa13-ccf631d5e458/image.png" alt=""></p>
<hr>
<h2 id="📖-arrays">📖 Arrays</h2>
<p>Arrays 클래스에는 배열을 다루는데 유용한 메서드(모두 static메서드)가 정의되어 있다. </p>
<h3 id="1-배열의-복사---copyof-copyofrange">1. 배열의 복사 - copyOf(), copyOfRange()</h3>
<p>copyOf()는 배열 전체 복사, copyOfRange()는 일부를 복사하여 새로운 배열을 만든 다음 반환한다.</p>
<pre><code class="language-java">int[] arr = {0, 1, 2, 3, 4};

int[] arr1 = Arrays.copyOf(arr, arr.length);
int[] arr2 = Arrays.copyOf(arr, 3);    // [0,1,2]
int[] arr3 = Arrays.copyOf(arr, 7);    // [0,1,2,3,4,0,0]
int[] arr4 = Arrays.copyOfRange(arr, 2, 4);    // [2,3]</code></pre>
<h3 id="2-배열-채우기---fill-setall">2. 배열 채우기 - fill(), setAll()</h3>
<ul>
<li><code>Arrays.fill(arr, 9);</code>    : 배열의 모든 요소를 9로 채움.</li>
<li><code>Arrays.setAll(arr, ()-&gt;(int)(Math.random()*5)+1);</code> 
: 배열을 채우는데 사용할 함수형 인터페이스를 매개변수로 받는다.
// arr = [1,5,2,1,1]</li>
</ul>
<h3 id="3-배열-정렬과-검색---sort-binarysearch">3. 배열 정렬과 검색 - sort(), binarySearch()</h3>
<ul>
<li><code>sort()</code> : 배열을 정렬</li>
<li><code>binarySearch()</code> : 배열에서 지정된 값이 저장된 위치를 찾아서 반환</li>
</ul>
<p>&#39;순차검색(linear search)&#39;보다 큰 배열의 검색에서 유리하다. 단, <strong>배열이 정렬되어 있는 경우에만 사용할 수 있다</strong>는 단점이 있다.</p>
<h3 id="4-문자열-비교와-출력---equals-tostring">4. 문자열 비교와 출력 - equals(), toString()</h3>
<p>일차원 배열에서의 문자열로 출력은 toString()이용하고,
다차원 배열에서의 문자열로 출력은 deepToString()을 이용한다.</p>
<p>일차원 배열에서 배열 비교는 equals(),
다차원 배열에서 배열 비교는 deepEquals()</p>
<hr>
<h1 id="📌-set">📌 Set</h1>
<h2 id="📖-comparator와-comparable">📖 Comparator와 Comparable</h2>
<blockquote>
<p>모두 인터페이스로 컬렉션을 정렬하는데 필요한 메서드를 정의하고 있음.<br>
Comparable : 기본 정렬기준을 구현하는데 사용. (기본 오름차순)
Comparator : 기본 정렬기준 외에 다른 기준으로 정렬하고자할 때 사용</p>
</blockquote>
<pre><code class="language-java">// java.util
public interface Comparator {
    int compare(Object o1, Object o2);
    boolean equals(Object obj);
}
// java.lang
public interface Comparable {
    public int compareTo(Object o);
}</code></pre>
<hr>
<h2 id="📖-hashset">📖 HashSet</h2>
<blockquote>
<p>HashSet은 Set인터페이스를 구현한 가장 대표적인 컬렉션이며, 중복요소를 저장하지 않는다. 
새로운 요소를 추가할 때는 add메서드나 addAll메서드를 이용하는데, 만일 이미 저장되어 있는 요소이면, false를 반환한다.
저장순서를 유지하지 않으므로 저장순서를 유지하고자 한다면 <strong>LinkedHashSet</strong>을 사용해야한다.</p>
</blockquote>
<ul>
<li><p>객체의 타입이 다르면 중복으로 간주하지 않는다.
<code>(&quot;1&quot;) != Integer(1)</code></p>
</li>
<li><p>랜덤으로 중복되지 않는 숫자 배열을 만드는 방법</p>
<pre><code class="language-java">  for(int i=0; set.size() &lt; 25; i++){
      set.add((int)Math.random()*50)+1+&quot;&quot;);
  }</code></pre>
</li>
<li><p>여러번 생성된 동일한 값의 객체를 같은 것으로 인식하게 하는 방법</p>
<pre><code class="language-java">  class Person{
      String name;
      int age;

      Person(String name, int age){
          this.name = name;
          this.age = age;
      }

      // 
      public boolean equals(Object obj) {
          if(obj instanceof Person){
              Person tmp = (Person)obj;
              return name.equals(tmp.name) &amp;&amp; age==tmp.age;
          }
          return false;
      }

      public int hashCode() {
          return (name+age).hashCode();
      }

      public String toString(){
          return name + &quot;:&quot; + age;
      }
  }</code></pre>
<p>👉🏻 HashSet의 add메서드는 새로운 요소를 추가하기 전에 기존에 저장된 요소와 같은 것인지 판단하기 위해 추가하려는 요소의 <code>equals()</code>와 <code>hashCode()</code>를 호출하므로 목적에 알맞게 <strong>오버라이딩</strong> 해야한다.</p>
</li>
<li><p>String은 문자열의 내용으로 해시코드를 만들어 내고, Object클래스는 객체 주소로 해시코드를 만들어 낸다.</p>
</li>
</ul>
<h2 id="📖-treeset">📖 TreeSet</h2>
<p><strong>이진 검색 트리</strong>라는 자료구조의 형태로 데이터를 저장한다. 
정렬, 검색, 범위 검색에 높은 성능을 보인다.
중복된 데이터의 저장을 허용하지 않으며, 정렬된 위치에 저장하므로 저장순서를 유지하지도 않는다.</p>
<ul>
<li><strong>이진 검색 트리 (binary tree)</strong></li>
</ul>
<ol>
<li>모든 노드는 두 개의 자식 노드를 가질 수 있다.</li>
<li>왼쪽 노드는 부모노드의 값보다 작고, 오른쪽 노드는 크다.</li>
<li>노드의 추가, 삭제에 시간이 걸린다.</li>
<li>(범위)검색과 정렬에 유리하다.</li>
<li>중복된 값은 저장하지 못한다.</li>
</ol>
<ul>
<li>TreeSet에서의 랜덤으로 숫자 배열하기<pre><code class="language-java">  for(int i=0; set.size() &lt; 6; i++) {
      int num = (int)(Math.random()*45) + 1;
      set.add(num);
  }</code></pre>
</li>
<li><blockquote>
<p>TreeSet은 HashSet과 달리 따로 정렬할 필요가 없다.</p>
</blockquote>
</li>
</ul>
<p><code>headSet()</code> : 지정된 기준 값보다 큰 값의 객체들
<code>tailSet()</code> : 지정된 기준 값보다 작은 값의 객체들</p>
<hr>
<h1 id="📌-map">📌 Map</h1>
<h2 id="📖-hashmap">📖 HashMap</h2>
<p>Hashtable은 HashMap의 구버전. 새로운 버전인 HashMap 사용할 것을 권장한다.</p>
<p>Map은 키(key)와 값(value)을 묶어서 하나의 데이터(entry)로 저장한다.</p>
<pre><code class="language-java">public class HashMap extends AbstractMap implements Map,
Cloneable, Seialization {
    transient Entry[] table;
        ...
    static class Entry implements Map.Entry {
        final Object key;
        Object value;
        ...
    }
}</code></pre>
<p>key : 컬렉션의 내의 키 중에서 유일해야 한다.
value : 키와 달리 데이터의 중복을 허용. ( <code>null</code> 허용 )
entry = (Object, Object)</p>
<h2 id="📖-treemap">📖 TreeMap</h2>
<p><strong>이진 검색 트리 형태</strong>로 키와 값의 쌍을 저장한다.
검색과 정렬에 적합한 컬렉션 클래스이다.</p>
<p>검색에 관한한 대부분의 경우에서 TreeMap보다 HashMap이 더 뛰어나므로 HashMap을 사용하는 것이 적합하다.</p>
<p>다만, <strong>범위검색이나 정렬</strong>이 필요한 경우 TreeMap을 사용하자.</p>
<h2 id="📖-properties">📖 Properties</h2>
<p>Hashtable을 상속받음. 
entry = (String, String)
주로 환경설정 관련한 속성을 저장할 때 사용한다. </p>
<br>

<hr>
<h1 id="📌-컬렉션-클래스-정리--요약">📌 컬렉션 클래스 정리 &amp; 요약</h1>
<br>

<p>총정리 필기본</p>
<p><img src="https://velog.velcdn.com/images/liy_se00/post/7b4803fa-cbe7-4e34-8487-6bc741cfd360/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Ch.10 날짜와 시간&형식화]]></title>
            <link>https://velog.io/@liy_se00/Java-Ch.10-%EB%82%A0%EC%A7%9C%EC%99%80-%EC%8B%9C%EA%B0%84%ED%98%95%EC%8B%9D%ED%99%94</link>
            <guid>https://velog.io/@liy_se00/Java-Ch.10-%EB%82%A0%EC%A7%9C%EC%99%80-%EC%8B%9C%EA%B0%84%ED%98%95%EC%8B%9D%ED%99%94</guid>
            <pubDate>Sun, 07 Jul 2024 14:06:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>👉🏻 이 글은 자바의 정석(3판) Chapter10을 공부하며 쓴 글입니다.</p>
</blockquote>
<h1 id="📌-1-날짜와-시간">📌 1. 날짜와 시간</h1>
<h2 id="📖-11-calender와-date">📖 1.1 Calender와 Date</h2>
<ul>
<li><p>java.util.Date
  날짜와 시간을 다룰 목적으로 만들어진 클래스(JDK1.0)
  사용X</p>
</li>
<li><p>java.util.Calendar
  Date클래스를 개선한 새로운 클래스(JDK1.1)
  여전히 단점 존재</p>
</li>
<li><p>java.time패키지
  Date, Calendar의 단점 개선.(JDK1.8)
  날짜와 시간을 따로 다룰 수 있도록 함.
  -&gt; LocalDate / LocalTime / LocalDateTime</p>
</li>
</ul>
<h3 id="calender-클래스">Calender 클래스</h3>
<p>추상 클래스이므로 getInstance()를 통해 구현된 객체를 얻어야 한다.</p>
<pre><code class="language-java">Calendar cal = cal new Calendar();    //Error! 추상클래스는 인스턴스 생성 불가
Calendar cal = Calendar.getInstance();    // Calendar클래스를 구현한 클래스의 인스턴스 반환</code></pre>
<p>get()으로 날짜와 시간 필드 가져오기 <code>int get(int field)</code>
⬇️ <code>int field</code></p>
<ul>
<li>날짜</li>
</ul>
<table>
<thead>
<tr>
<th>필드명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>YEAR</td>
<td>년</td>
</tr>
<tr>
<td>MONTH</td>
<td>월 (0부터 시작)</td>
</tr>
<tr>
<td>WEEK_OF_YEAR</td>
<td>일</td>
</tr>
<tr>
<td>WEEK_OF_MONTH</td>
<td>그 달의 몇 번째 주</td>
</tr>
<tr>
<td>DATE</td>
<td>일</td>
</tr>
<tr>
<td>DAY_OF_MONTH</td>
<td>그 달의 몇 번째 일</td>
</tr>
<tr>
<td>DAY_OF_YEAR</td>
<td>그 해의 몇 번째 일</td>
</tr>
<tr>
<td>DAY_OF_WEEK</td>
<td>요일</td>
</tr>
<tr>
<td>DAY_OF_WEEK_IN_MONTH</td>
<td>그 달의 몇 번째 요일</td>
</tr>
</tbody></table>
<ul>
<li>시간</li>
</ul>
<table>
<thead>
<tr>
<th>필드명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>HOUR</td>
<td>시간(0~11)</td>
</tr>
<tr>
<td>HOUR_OF_DAY</td>
<td>시간(0~23)</td>
</tr>
<tr>
<td>MINUTE</td>
<td>분</td>
</tr>
<tr>
<td>SECOND</td>
<td>초</td>
</tr>
<tr>
<td>MILLISECOND</td>
<td>천분의 일초</td>
</tr>
<tr>
<td>ZONE_OFFSET</td>
<td>GMT기준 시차(밀리세컨 단위)</td>
</tr>
<tr>
<td>AM_PM</td>
<td>오전/오후</td>
</tr>
</tbody></table>
<ul>
<li>set()으로 날짜와 시간 지정하기<pre><code class="language-java">void set(int field, int value)
void set(int year, int month, int date)
</code></pre>
</li>
</ul>
<p>Calendar date1 = Calendar.getInstance();
date1.set(2017, 7, 15);    //2017.08.15</p>
<pre><code>⚠️월(MONTH)이 **0**부터 시작한다는 점 주의!

- 시간 지정
```java
time1.set(Calendar.HOUR_OF_DAY, 10);
time1.set(Calendar.MINUTE, 20);
time1.set(Calendar.SECOND, 30);    // 10시 20분 30초</code></pre><ul>
<li><p><code>clear()</code>는 Calendar객체의 모든 필드를 초기화</p>
</li>
<li><p><code>clear(int field)</code>는 Calendar객체의 특정 필드를 초기화</p>
</li>
<li><p><code>add()</code>는 특정 필드의 값을 증가 또는 감소(다른 필드에 영향 O)
  : 여기서 다른 필드에 영향을 준다는 것을 8월 31일에서 <code>date.add(Calendar.Date, 1)</code>을 하면 자동으로 9월 1일이 되는 것을 의미</p>
</li>
<li><p><code>roll()</code>은 특정 필드의 값을 증가 또는 감소(다른 필드에 영향 X)</p>
</li>
</ul>
<h3 id="date---calendar-간의-변환">Date &lt;-&gt; Calendar 간의 변환</h3>
<ol>
<li>Calendar -&gt; Date<pre><code class="language-java"> Calendar cal = Calendar.getInstance();
 ...
 Date d = new Date(cal.getTimeInMillis());    // Date(long date)</code></pre>
</li>
<li>Date -&gt; Calendar<pre><code class="language-java"> Date d = new Date();
 ...
 Calendar cal = Calender.getInstance();
 cal.setTime(d);
</code></pre>
</li>
</ol>
<h1 id="📌-2형식화-클래스">📌 2.형식화 클래스</h1>
<p>java.text 패키지의 DecimalFormat, SimpleDateFormat</p>
<ul>
<li>숫자와 날짜를 원하는 형식으로 쉽게 출력 가능 (숫자, 날짜 -&gt; 형식 문자열)</li>
<li>형식 문자열에서 숫자와 날짜를 뽑아내는 기능</li>
</ul>
<h2 id="📖-21-decimalformat">📖 2.1 DecimalFormat</h2>
<ul>
<li><p>숫자를 형식화할 때 사용(숫자 -&gt; 형식 문자열)</p>
<pre><code class="language-java">double number = 1234567.89;
DecimalFormat df = new DecimalFormat(&quot;#.#E0&quot;);
String result = df.format(number);    //result = &quot;1.2E6&quot;</code></pre>
<p>0 : 10진수(값이 없을 때는 0)
# : 10진수(값이 없으면 생략)
E : 지수기호</p>
</li>
<li><p>특정 형식의 문자열을 숫자로 변환할 때도 사용(형식 문자열 -&gt; 숫자)
+ ) Integer.parseInt()는 콤마(,)가 포함된 문자열을 숫자로 변환 못함.</p>
<pre><code class="language-java">DecimalFormat df = new DecimalFormat(&quot;#,###.##&quot;);
Number num = df.parse(&quot;1,234,567.89&quot;);
double d = num.doubleValue();    // 1234567.89</code></pre>
</li>
</ul>
<h2 id="📖-22-simpledateformat">📖 2.2 SimpleDateFormat</h2>
<ul>
<li>날짜와 시간을 다양한 형식으로 출력할 수 있게 해준다.<pre><code class="language-java">Date today = new Date();
SimpleDateFormat df = new SimpleDateFormat(&quot;yyyy-MM-dd&quot;);
</code></pre>
</li>
</ul>
<p>String result = df.format(today);</p>
<pre><code>G : 연대(BC, AD)
y : 년도
M : 월
W : 년의 몇 번째 주 (1~53)
w : 월의 몇 번째 주 (1~5)
D : 년의 몇 번째 일 (1~366)
d : 월의 몇 번째 일 (1~31)
F : 월의 몇 번째 요일 (1~5)
E : 요일

- 특정 형식으로 되어 있는 문자열에서 날짜와 시간을 뽑아낼 수도 있다.
```java
Date d = df.parse(&quot;2015년 11월 23일&quot;);    // 문자열을 Date로 변환
String result = df2.format(d);    // format 변경</code></pre>]]></description>
        </item>
    </channel>
</rss>