<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev_sjb.log</title>
        <link>https://velog.io/</link>
        <description>성장하는 개발자가 되겠습니다</description>
        <lastBuildDate>Tue, 14 Apr 2026 09:21:26 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev_sjb.log</title>
            <url>https://velog.velcdn.com/images/dev_sjb/profile/ff41bf97-49a8-49ba-a849-2b22d5808dcf/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev_sjb.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev_sjb" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[HttpServletRequest 객체에서 Parameter와 Attribute의 차이점]]></title>
            <link>https://velog.io/@dev_sjb/HttpServletRequest-%EA%B0%9D%EC%B2%B4%EC%97%90%EC%84%9C-Parameter%EC%99%80-Attribute%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@dev_sjb/HttpServletRequest-%EA%B0%9D%EC%B2%B4%EC%97%90%EC%84%9C-Parameter%EC%99%80-Attribute%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Tue, 14 Apr 2026 09:21:26 GMT</pubDate>
            <description><![CDATA[<p><strong>HttpServletRequest 객체</strong>에서 <strong>Parameter</strong>와 <strong>Attribute</strong>는 서로 다른 용도로 사용되며, 요청(request) 처리 시 다양한 데이터를 저장하고 전달하는 방식입니다. 이 두 개념은 <strong>저장 목적, 데이터의 생명 주기, 사용 방식</strong>에서 차이가 있습니다.</p>
<hr>
<h3 id="parameter와-attribute의-차이">Parameter와 Attribute의 차이</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>Parameter</th>
<th>Attribute</th>
</tr>
</thead>
<tbody><tr>
<td><strong>저장 목적</strong></td>
<td>클라이언트가 요청 시 전송한 파라미터 데이터를 담음</td>
<td>서버에서 요청 처리 중 추가한 데이터를 담음</td>
</tr>
<tr>
<td><strong>데이터 소스</strong></td>
<td>주로 쿼리 문자열, 폼 데이터, URL 파라미터</td>
<td>서버에서 코드로 추가된 객체</td>
</tr>
<tr>
<td><strong>데이터 유형</strong></td>
<td>문자열(String)</td>
<td>객체(Object)</td>
</tr>
<tr>
<td><strong>생명 주기</strong></td>
<td>요청이 시작될 때부터 클라이언트 요청 내에서 유지</td>
<td>요청 객체 내에서 유지되며, 필요 시 추가 및 수정 가능</td>
</tr>
<tr>
<td><strong>사용 메서드</strong></td>
<td><code>getParameter()</code>, <code>getParameterValues()</code></td>
<td><code>setAttribute()</code>, <code>getAttribute()</code>, <code>removeAttribute()</code></td>
</tr>
</tbody></table>
<hr>
<h3 id="1-parameter-파라미터">1. Parameter (파라미터)</h3>
<p><strong>Parameter</strong>는 <strong>클라이언트가 서버로 보낸 요청의 데이터를 포함</strong>하는 값입니다. 예를 들어, <strong>HTML 폼 데이터, 쿼리 문자열, URL 매개변수</strong> 등이 이에 해당합니다. 주로 <code>String</code> 타입으로 다루며, 사용자가 입력한 값을 서버에서 읽어올 때 사용합니다.</p>
<ul>
<li><strong>저장 위치</strong>: 클라이언트가 요청 시 보내는 데이터로, 주로 URL 쿼리 문자열이나 POST 폼 데이터로 전달됩니다.</li>
<li><strong>주요 메서드</strong>:<ul>
<li><code>getParameter(String name)</code>: 지정한 이름의 요청 파라미터 값을 <code>String</code>으로 반환합니다.</li>
<li><code>getParameterValues(String name)</code>: 동일한 이름을 가진 여러 요청 파라미터의 값을 <code>String[]</code> 배열로 반환합니다. (예: 체크박스)</li>
</ul>
</li>
</ul>
<h3 id="예시">예시</h3>
<p>클라이언트가 <code>login.jsp?username=johndoe&amp;password=1234</code> 같은 URL로 요청할 때, <code>username</code>과 <code>password</code>는 요청 파라미터가 됩니다.</p>
<pre><code class="language-java">String username = request.getParameter(&quot;username&quot;);
String password = request.getParameter(&quot;password&quot;);
</code></pre>
<ul>
<li>여기서 <code>username</code>과 <code>password</code>는 클라이언트가 보낸 값이며, <code>request.getParameter()</code>를 통해 <code>String</code>으로 읽어올 수 있습니다.</li>
</ul>
<hr>
<h3 id="2-attribute-속성">2. Attribute (속성)</h3>
<p><strong>Attribute</strong>는 <strong>서버에서 요청(request) 객체에 추가하는 데이터</strong>입니다. 주로 서블릿과 JSP 간의 데이터 전달에 사용됩니다. Attribute는 <strong>객체(Object)</strong> 형태로 저장되며, 서버에서 요청 처리 중 필요한 데이터를 공유하는 용도로 사용됩니다. 클라이언트가 직접 접근하거나 변경할 수 없고, 서버 내에서만 유효합니다.</p>
<ul>
<li><strong>저장 위치</strong>: 서버에서 설정하는 값으로, 주로 서블릿에서 JSP로 데이터를 전달할 때 사용합니다.</li>
<li><strong>주요 메서드</strong>:<ul>
<li><code>setAttribute(String name, Object value)</code>: 지정한 이름으로 객체를 속성으로 저장합니다.</li>
<li><code>getAttribute(String name)</code>: 지정한 이름의 속성 값을 반환합니다.</li>
<li><code>removeAttribute(String name)</code>: 지정한 이름의 속성을 제거합니다.</li>
</ul>
</li>
</ul>
<h3 id="예시-1">예시</h3>
<p>서블릿에서 데이터를 JSP로 전달할 때 Attribute를 사용합니다. 예를 들어, 사용자의 이름과 같은 데이터를 JSP로 넘겨야 할 때 Attribute에 저장하여 전달합니다.</p>
<pre><code class="language-java">// 서블릿에서 설정
request.setAttribute(&quot;username&quot;, &quot;John Doe&quot;);

// JSP에서 접근
String username = (String) request.getAttribute(&quot;username&quot;);
</code></pre>
<p>이 경우, <code>username</code>이라는 속성은 서블릿에서 JSP로 전달되어 JSP에서 사용할 수 있습니다. 클라이언트는 이 데이터를 직접 변경할 수 없으며, 서버 측에서만 유지됩니다.</p>
<hr>
<h3 id="parameter와-attribute의-용도-비교">Parameter와 Attribute의 용도 비교</h3>
<ol>
<li><strong>Parameter는 클라이언트가 전송한 데이터를 읽는 데 사용</strong>됩니다.<ul>
<li>예: 로그인 폼의 사용자명과 비밀번호를 읽어와 인증 처리</li>
</ul>
</li>
<li><strong>Attribute는 서버 내부에서 데이터를 공유하거나 전달하는 데 사용</strong>됩니다.<ul>
<li>예: 서블릿에서 처리한 결과 데이터를 JSP로 전달하여 클라이언트에게 표시</li>
</ul>
</li>
</ol>
<h3 id="요약">요약</h3>
<ul>
<li><strong>Parameter</strong>: 클라이언트 요청 시 보내는 데이터. <code>getParameter()</code>로 문자열 값을 읽습니다.</li>
<li><strong>Attribute</strong>: 서버에서 추가한 데이터로, 요청 처리 중 서버 내에서 데이터 전달에 사용. <code>setAttribute()</code>와 <code>getAttribute()</code>로 객체를 저장하고 읽어옵니다.</li>
</ul>
<p>이처럼 <strong>Parameter는 클라이언트가 보낸 값</strong>을 읽을 때, <strong>Attribute는 서버에서 요청 처리 중 데이터를 전달할 때</strong> 사용하여 요청의 성격에 맞게 데이터를 다루는 것이 중요합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HttpServletRequest, HttpServletResponse]]></title>
            <link>https://velog.io/@dev_sjb/HttpServletRequest-HttpServletResponse</link>
            <guid>https://velog.io/@dev_sjb/HttpServletRequest-HttpServletResponse</guid>
            <pubDate>Tue, 14 Apr 2026 09:20:30 GMT</pubDate>
            <description><![CDATA[<p><strong>HttpServletRequest</strong>와 <strong>HttpServletResponse</strong>는 서블릿 API에서 클라이언트의 요청과 서버의 응답을 처리하는 데 사용되는 주요 인터페이스입니다. 이 두 객체는 서블릿이 요청을 처리하고, 결과를 클라이언트에게 전달하는 데 필요한 데이터를 제공합니다.</p>
<hr>
<h3 id="1-httpservletrequest---클라이언트-요청-정보">1. HttpServletRequest - 클라이언트 요청 정보</h3>
<p><strong>HttpServletRequest</strong> 객체는 클라이언트가 서버에 전송한 <strong>HTTP 요청 정보를 포함</strong>하고 있습니다. 클라이언트가 전송한 요청의 메타데이터와 데이터(쿼리 파라미터, 헤더, 쿠키, 세션 정보 등)에 접근할 수 있도록 다양한 메서드를 제공합니다.</p>
<h3 id="주요-메서드">주요 메서드</h3>
<ol>
<li><strong>getParameter(String name)</strong>:<ul>
<li>클라이언트가 전송한 요청 파라미터를 가져옵니다. 폼 데이터와 쿼리 문자열을 읽을 때 주로 사용됩니다.</li>
<li>예: <code>request.getParameter(&quot;username&quot;)</code></li>
</ul>
</li>
<li><strong>getParameterValues(String name)</strong>:<ul>
<li>동일한 이름을 가진 여러 파라미터의 값을 배열 형태로 가져옵니다. 예를 들어, 체크박스와 같이 여러 값을 선택할 수 있는 요소에서 유용합니다.</li>
<li>예: <code>request.getParameterValues(&quot;hobbies&quot;)</code></li>
</ul>
</li>
<li><strong>getMethod()</strong>:<ul>
<li>요청의 HTTP 메서드(GET, POST 등)를 반환합니다.</li>
<li>예: <code>request.getMethod()</code></li>
</ul>
</li>
<li><strong>getRequestURI()</strong>:<ul>
<li>클라이언트가 요청한 URI를 반환합니다.</li>
<li>예: <code>request.getRequestURI()</code></li>
</ul>
</li>
<li><strong>getHeader(String name)</strong>:<ul>
<li>요청 헤더의 값을 가져옵니다. 예를 들어, <code>User-Agent</code> 헤더를 통해 클라이언트의 브라우저 정보를 얻을 수 있습니다.</li>
<li>예: <code>request.getHeader(&quot;User-Agent&quot;)</code></li>
</ul>
</li>
<li><strong>getSession()</strong>:<ul>
<li>현재 요청과 관련된 세션 객체를 반환합니다. 세션을 통해 사용자의 상태를 유지할 수 있습니다.</li>
<li>예: <code>request.getSession()</code></li>
</ul>
</li>
<li><strong>getRemoteAddr()</strong>:<ul>
<li>클라이언트의 IP 주소를 반환합니다.</li>
<li>예: <code>request.getRemoteAddr()</code></li>
</ul>
</li>
</ol>
<h3 id="예시">예시</h3>
<pre><code class="language-java">@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String username = request.getParameter(&quot;username&quot;);
    String userAgent = request.getHeader(&quot;User-Agent&quot;);
    String requestURI = request.getRequestURI();

    System.out.println(&quot;사용자 이름: &quot; + username);
    System.out.println(&quot;User-Agent: &quot; + userAgent);
    System.out.println(&quot;Request URI: &quot; + requestURI);
}
</code></pre>
<hr>
<h3 id="2-httpservletresponse---서버-응답-정보">2. HttpServletResponse - 서버 응답 정보</h3>
<p><strong>HttpServletResponse</strong> 객체는 서버가 클라이언트로 전송할 <strong>HTTP 응답을 구성하는 데 필요한 정보</strong>를 제공합니다. 이 객체를 통해 응답 헤더, 상태 코드, 컨텐츠 유형 등을 설정하고, 최종적으로 클라이언트에게 데이터를 보낼 수 있습니다.</p>
<h3 id="주요-메서드-1">주요 메서드</h3>
<ol>
<li><strong>setContentType(String type)</strong>:<ul>
<li>응답의 컨텐츠 유형(Content-Type)을 설정합니다. 예를 들어, <code>text/html</code>, <code>application/json</code> 등을 지정할 수 있습니다.</li>
<li>예: <code>response.setContentType(&quot;text/html;charset=UTF-8&quot;);</code></li>
</ul>
</li>
<li><strong>setCharacterEncoding(String charset)</strong>:<ul>
<li>응답의 문자 인코딩을 설정합니다. 주로 UTF-8로 설정하여 다국어 지원을 합니다.</li>
<li>예: <code>response.setCharacterEncoding(&quot;UTF-8&quot;);</code></li>
</ul>
</li>
<li><strong>setStatus(int statusCode)</strong>:<ul>
<li>응답 상태 코드를 설정합니다. 예를 들어, 200(성공), 404(찾을 수 없음), 500(서버 오류) 등의 상태 코드를 지정할 수 있습니다.</li>
<li>예: <code>response.setStatus(HttpServletResponse.SC_OK);</code></li>
</ul>
</li>
<li><strong>getWriter()</strong>:<ul>
<li>출력 스트림을 반환하여, 클라이언트에게 텍스트 데이터를 보낼 수 있도록 합니다. 주로 HTML이나 JSON을 작성할 때 사용됩니다.</li>
<li>예: <code>PrintWriter out = response.getWriter(); out.println(&quot;&lt;h1&gt;Hello, World!&lt;/h1&gt;&quot;);</code></li>
</ul>
</li>
<li><strong>sendRedirect(String location)</strong>:<ul>
<li>클라이언트를 다른 URL로 리다이렉트합니다. 주로 로그인 성공 후 특정 페이지로 이동할 때 사용됩니다.</li>
<li>예: <code>response.sendRedirect(&quot;/home&quot;);</code></li>
</ul>
</li>
<li><strong>addCookie(Cookie cookie)</strong>:<ul>
<li>클라이언트에게 쿠키를 전송합니다. 이를 통해 사용자 세션을 유지하거나 사용자 맞춤 설정을 제공합니다.</li>
<li>예: <code>response.addCookie(new Cookie(&quot;user&quot;, &quot;JohnDoe&quot;));</code></li>
</ul>
</li>
</ol>
<h3 id="예시-1">예시</h3>
<pre><code class="language-java">@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType(&quot;text/html;charset=UTF-8&quot;);
    response.setStatus(HttpServletResponse.SC_OK);

    PrintWriter out = response.getWriter();
    out.println(&quot;&lt;html&gt;&lt;body&gt;&quot;);
    out.println(&quot;&lt;h1&gt;Hello, this is the server&#39;s response!&lt;/h1&gt;&quot;);
    out.println(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
}
</code></pre>
<hr>
<h3 id="httpservletrequest와-httpservletresponse의-활용-예">HttpServletRequest와 HttpServletResponse의 활용 예</h3>
<p>다음은 사용자로부터 요청을 받아 처리하고 응답을 생성하는 간단한 예제입니다.</p>
<pre><code class="language-java">import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(&quot;/greet&quot;)
public class GreetServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 요청 파라미터에서 이름 가져오기
        String name = request.getParameter(&quot;name&quot;);

        // 응답 설정
        response.setContentType(&quot;text/html;charset=UTF-8&quot;);

        // 응답 출력
        PrintWriter out = response.getWriter();
        out.println(&quot;&lt;html&gt;&lt;body&gt;&quot;);
        if (name != null &amp;&amp; !name.isEmpty()) {
            out.println(&quot;&lt;h1&gt;안녕하세요, &quot; + name + &quot;님!&lt;/h1&gt;&quot;);
        } else {
            out.println(&quot;&lt;h1&gt;안녕하세요, 방문자님!&lt;/h1&gt;&quot;);
        }
        out.println(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
    }
}
</code></pre>
<ul>
<li><strong>HttpServletRequest</strong>: <code>request.getParameter(&quot;name&quot;)</code>을 통해 클라이언트가 입력한 이름을 가져옵니다.</li>
<li><strong>HttpServletResponse</strong>: <code>response.setContentType(&quot;text/html;charset=UTF-8&quot;);</code>로 응답 유형과 인코딩을 설정하고, <code>PrintWriter</code>를 통해 HTML 응답을 출력합니다.</li>
</ul>
<hr>
<h3 id="요약">요약</h3>
<ul>
<li><strong>HttpServletRequest</strong>는 클라이언트의 요청 정보를 처리하며, URL, HTTP 메서드, 파라미터, 헤더 정보, 쿠키 등 요청과 관련된 다양한 메서드를 제공합니다.</li>
<li><strong>HttpServletResponse</strong>는 서버의 응답을 구성하며, 컨텐츠 유형, 상태 코드, 리다이렉트, 쿠키 등 응답과 관련된 여러 메서드를 통해 클라이언트에게 응답을 보낼 수 있습니다.</li>
</ul>
<p>이 두 객체는 서블릿에서 요청과 응답을 처리하는 기본적인 도구로, HTTP 통신을 통해 클라이언트와 서버 간에 데이터를 주고받는 데 중요한 역할을 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MVC Model2]]></title>
            <link>https://velog.io/@dev_sjb/MVC-Model2</link>
            <guid>https://velog.io/@dev_sjb/MVC-Model2</guid>
            <pubDate>Tue, 14 Apr 2026 09:19:22 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev_sjb/post/10cd55e4-6718-48aa-a126-c97a94483646/image.png" alt="">
<strong>MVC Model 2</strong>는 JSP와 서블릿을 기반으로 하는 <strong>웹 애플리케이션 아키텍처 패턴</strong>으로, <strong>Model-View-Controller(MVC)</strong> 구조를 사용하여 <strong>서버 측에서 요청과 응답을 처리하는 방식</strong>입니다. MVC Model 2는 비즈니스 로직, 데이터, 프레젠테이션(화면) 로직을 명확히 분리하여, 유지보수와 확장성이 높은 구조를 제공합니다. 이 패턴은 Java 웹 애플리케이션에서 <strong>JSP와 서블릿의 역할을 명확히 나누고, 데이터 흐름을 구조화</strong>합니다.</p>
<hr>
<h3 id="mvc-model-2의-주요-구성-요소">MVC Model 2의 주요 구성 요소</h3>
<ol>
<li><strong>Model (모델)</strong>:<ul>
<li>비즈니스 로직과 데이터베이스와의 상호작용을 담당합니다.</li>
<li>DAO(Data Access Object)와 DTO(Data Transfer Object)를 사용하여 데이터를 관리합니다.</li>
<li>서블릿이 처리한 데이터는 모델을 통해 JSP에 전달됩니다.</li>
</ul>
</li>
<li><strong>View (뷰)</strong>:<ul>
<li>클라이언트에게 보여질 화면을 담당하는 부분으로, JSP가 주로 이 역할을 수행합니다.</li>
<li>화면에 표시할 데이터를 모델로부터 받아와 사용자에게 전달합니다.</li>
<li>비즈니스 로직은 포함하지 않고, 데이터 표시와 UI만을 담당합니다.</li>
</ul>
</li>
<li><strong>Controller (컨트롤러)</strong>:<ul>
<li>클라이언트의 요청을 받아서 어떤 작업을 수행할지 결정하고, 필요한 데이터를 준비한 뒤 적절한 뷰로 데이터를 전달합니다.</li>
<li><strong>서블릿</strong>이 컨트롤러 역할을 수행하며, 요청을 받고 모델과 뷰 간의 흐름을 제어합니다.</li>
<li>컨트롤러는 사용자 요청을 해석하고, 비즈니스 로직을 처리하며, 최종적으로 데이터를 뷰에 전달하는 책임을 가집니다.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="mvc-model-2의-흐름">MVC Model 2의 흐름</h3>
<ol>
<li><strong>클라이언트의 요청</strong>: 사용자가 웹 브라우저에서 특정 URL을 통해 요청을 보내면, 이 요청이 컨트롤러(서블릿)로 전달됩니다.</li>
<li><strong>컨트롤러의 요청 처리</strong>: 컨트롤러는 요청을 해석하고, 필요한 경우 모델(비즈니스 로직 또는 데이터베이스 작업)을 통해 데이터를 처리합니다.</li>
<li><strong>뷰 선택 및 데이터 전달</strong>: 컨트롤러는 요청 처리 후, 결과 데이터를 뷰(JSP)에 전달하고, 클라이언트에게 보여질 페이지를 결정합니다.</li>
<li><strong>응답 생성 및 반환</strong>: JSP 뷰는 전달받은 데이터를 사용자에게 보여줄 HTML 페이지로 렌더링하여 클라이언트에게 반환합니다.</li>
</ol>
<hr>
<h3 id="mvc-model-2-간단-예제">MVC Model 2 간단 예제</h3>
<p>아래는 간단한 <strong>게시판 예제</strong>로, 게시글 리스트를 조회하는 흐름을 통해 MVC Model 2의 구조를 이해해 보겠습니다.</p>
<h3 id="구성-파일">구성 파일</h3>
<ol>
<li><strong>BoardDTO.java</strong> - 게시글 데이터를 저장하는 데이터 전송 객체.</li>
<li><strong>BoardDAO.java</strong> - 데이터베이스와 연결하여 게시글을 조회하는 데이터 접근 객체.</li>
<li><strong>BoardListServlet.java</strong> - 클라이언트 요청을 처리하고 뷰로 데이터를 전달하는 컨트롤러.</li>
<li><strong>boardList.jsp</strong> - 클라이언트에게 게시글 목록을 보여주는 뷰.</li>
</ol>
<hr>
<h3 id="1-boarddtojava-model">1. BoardDTO.java (Model)</h3>
<pre><code class="language-java">public class BoardDTO {
    private int id;
    private String title;
    private String content;

    // 기본 생성자 및 getter, setter
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
}
</code></pre>
<ul>
<li><code>BoardDTO</code>는 게시글 데이터를 저장하는 객체로, 데이터베이스에서 조회한 데이터를 JSP로 전달할 때 사용됩니다.</li>
</ul>
<h3 id="2-boarddaojava-model">2. BoardDAO.java (Model)</h3>
<pre><code class="language-java">import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class BoardDAO {
    public List&lt;BoardDTO&gt; getAllPosts() throws Exception {
        List&lt;BoardDTO&gt; postList = new ArrayList&lt;&gt;();
        Connection conn = null;

        try {
            conn = DriverManager.getConnection(&quot;jdbc:mariadb://localhost:3306/boarddb&quot;, &quot;root&quot;, &quot;password&quot;);
            String sql = &quot;SELECT * FROM board ORDER BY id DESC&quot;;
            PreparedStatement pstmt = conn.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();

            while (rs.next()) {
                BoardDTO post = new BoardDTO();
                post.setId(rs.getInt(&quot;id&quot;));
                post.setTitle(rs.getString(&quot;title&quot;));
                post.setContent(rs.getString(&quot;content&quot;));
                postList.add(post);
            }
        } finally {
            if (conn != null) conn.close();
        }
        return postList;
    }
}
</code></pre>
<ul>
<li><code>BoardDAO</code>는 데이터베이스에서 모든 게시글을 조회하여 <code>BoardDTO</code> 리스트로 반환합니다.</li>
</ul>
<h3 id="3-boardlistservletjava-controller">3. BoardListServlet.java (Controller)</h3>
<pre><code class="language-java">import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;

@WebServlet(&quot;/board/list&quot;)
public class BoardListServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BoardDAO dao = new BoardDAO();
        try {
            List&lt;BoardDTO&gt; postList = dao.getAllPosts(); // 데이터 조회
            request.setAttribute(&quot;postList&quot;, postList); // 데이터 뷰로 전달
            request.getRequestDispatcher(&quot;/WEB-INF/views/boardList.jsp&quot;).forward(request, response);
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }
}
</code></pre>
<ul>
<li><code>BoardListServlet</code>은 <code>/board/list</code> URL 요청을 처리하는 서블릿으로, 게시글 리스트를 가져와 <code>boardList.jsp</code>로 전달합니다.</li>
<li><code>request.setAttribute</code> 메서드를 통해 게시글 데이터를 뷰에 전달하고, <code>RequestDispatcher</code>를 사용해 <code>boardList.jsp</code>로 포워딩합니다.</li>
</ul>
<h3 id="4-boardlistjsp-view">4. boardList.jsp (View)</h3>
<pre><code>&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;게시글 목록&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;게시글 목록&lt;/h2&gt;
    &lt;table border=&quot;1&quot;&gt;
        &lt;tr&gt;
            &lt;th&gt;번호&lt;/th&gt;
            &lt;th&gt;제목&lt;/th&gt;
            &lt;th&gt;내용&lt;/th&gt;
        &lt;/tr&gt;
        &lt;c:forEach var=&quot;post&quot; items=&quot;${postList}&quot;&gt;
            &lt;tr&gt;
                &lt;td&gt;${post.id}&lt;/td&gt;
                &lt;td&gt;${post.title}&lt;/td&gt;
                &lt;td&gt;${post.content}&lt;/td&gt;
            &lt;/tr&gt;
        &lt;/c:forEach&gt;
    &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><ul>
<li><code>boardList.jsp</code>는 서블릿에서 전달받은 <code>postList</code> 데이터를 출력하는 역할을 합니다.</li>
<li>게시글의 각 항목은 <code>&lt;c:forEach&gt;</code> 태그를 사용해 테이블 형식으로 화면에 표시됩니다.</li>
</ul>
<hr>
<h3 id="mvc-model-2의-장단점">MVC Model 2의 장단점</h3>
<h3 id="장점">장점</h3>
<ol>
<li><strong>유지보수성 향상</strong>: 비즈니스 로직, 데이터, 프레젠테이션이 각각 Controller, Model, View로 나뉘어 있어 유지보수가 용이합니다.</li>
<li><strong>재사용성 증가</strong>: Model, View, Controller의 역할이 분리되어 재사용성이 높습니다.</li>
<li><strong>코드의 가독성 증가</strong>: 역할이 명확히 나누어져 있어 코드를 쉽게 이해할 수 있습니다.</li>
</ol>
<h3 id="단점">단점</h3>
<ol>
<li><strong>구현 복잡도 증가</strong>: 각 부분이 독립적으로 분리되므로 구조가 복잡해질 수 있습니다.</li>
<li><strong>작은 프로젝트에는 비효율적</strong>: 구조가 분리되기 때문에, 간단한 웹 애플리케이션에서는 구현이 비효율적일 수 있습니다.</li>
</ol>
<hr>
<h3 id="요약">요약</h3>
<ul>
<li><strong>MVC Model 2</strong>는 JSP와 서블릿을 기반으로 하는 구조로, <strong>모델, 뷰, 컨트롤러</strong>를 분리하여 역할을 명확히 하고 유지보수성을 높입니다.</li>
<li><code>Controller</code>(서블릿)는 클라이언트 요청을 처리하고, <code>Model</code>(DAO, DTO)은 데이터 처리를 담당하며, <code>View</code>(JSP)는 사용자에게 화면을 출력합니다.</li>
<li>이 구조는 대규모 애플리케이션에서 관리와 확장이 용이하며, Java 웹 애플리케이션 개발에서 널리 사용되는 패턴입니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[@WebServlet]]></title>
            <link>https://velog.io/@dev_sjb/WebServlet</link>
            <guid>https://velog.io/@dev_sjb/WebServlet</guid>
            <pubDate>Tue, 14 Apr 2026 09:17:10 GMT</pubDate>
            <description><![CDATA[<p>Servlet 애너테이션(@WebServlet)을 사용하면, <code>web.xml</code> 파일에 서블릿 설정을 작성하지 않고도 서블릿의 URL 매핑을 쉽게 지정할 수 있습니다. 애너테이션 방식은 <strong>코드 내에서 직접 서블릿을 설정</strong>할 수 있어 직관적이고, 설정이 간단하다는 장점이 있습니다.</p>
<hr>
<h3 id="webservlet-애너테이션의-기본-사용법">@WebServlet 애너테이션의 기본 사용법</h3>
<p><code>@WebServlet</code> 애너테이션을 통해 서블릿의 URL 패턴을 지정할 수 있습니다. 애너테이션은 서블릿 클래스 위에 추가하며, <code>web.xml</code>에 별도로 서블릿을 등록할 필요 없이 자동으로 매핑됩니다.</p>
<h3 id="기본-예제">기본 예제</h3>
<pre><code class="language-java">import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet(&quot;/hello&quot;)  // 서블릿 URL 매핑 설정
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType(&quot;text/html;charset=UTF-8&quot;);
        resp.getWriter().write(&quot;&lt;h1&gt;Hello, Servlet with Annotation!&lt;/h1&gt;&quot;);
    }
}
</code></pre>
<ul>
<li><strong>@WebServlet(&quot;/hello&quot;)</strong>: <code>/hello</code> 경로로 요청이 오면 <code>HelloServlet</code>이 실행되도록 설정합니다.</li>
<li>서블릿 컨테이너는 <strong>애너테이션을 통해</strong> <code>web.xml</code> 파일 없이도 서블릿을 인식하고, 매핑 설정을 자동으로 처리합니다.</li>
</ul>
<hr>
<h3 id="webservlet-애너테이션의-주요-속성">@WebServlet 애너테이션의 주요 속성</h3>
<p><code>@WebServlet</code> 애너테이션은 다양한 속성을 통해 서블릿의 동작을 세부적으로 설정할 수 있습니다.</p>
<ol>
<li><p><strong>value</strong> 또는 <strong>urlPatterns</strong>: 서블릿이 매핑될 URL 패턴을 지정합니다.</p>
<pre><code class="language-java"> @WebServlet(value = &quot;/hello&quot;)  // 단일 URL 패턴
 @WebServlet(urlPatterns = {&quot;/hello&quot;, &quot;/greet&quot;})  // 여러 URL 패턴</code></pre>
</li>
<li><p><strong>name</strong>: 서블릿 이름을 지정합니다. 기본값은 클래스 이름이 사용됩니다.</p>
<pre><code class="language-java"> @WebServlet(name = &quot;HelloServlet&quot;, urlPatterns = &quot;/hello&quot;)</code></pre>
</li>
<li><p><strong>loadOnStartup</strong>: 서블릿의 로드 우선순위를 설정합니다. 값이 0 이상이면 서버 시작 시 서블릿이 로드됩니다.</p>
<pre><code class="language-java"> @WebServlet(urlPatterns = &quot;/hello&quot;, loadOnStartup = 1)</code></pre>
</li>
<li><p><strong>initParams</strong>: 서블릿에 초기화 매개변수를 전달합니다. 여러 매개변수를 설정할 수 있습니다.</p>
<pre><code class="language-java"> @WebServlet(urlPatterns = &quot;/hello&quot;, initParams = {
     @WebInitParam(name = &quot;param1&quot;, value = &quot;value1&quot;),
     @WebInitParam(name = &quot;param2&quot;, value = &quot;value2&quot;)
 })</code></pre>
</li>
</ol>
<hr>
<h3 id="애너테이션-방식과-webxml-방식의-차이점">애너테이션 방식과 web.xml 방식의 차이점</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>애너테이션 방식</th>
<th>web.xml 방식</th>
</tr>
</thead>
<tbody><tr>
<td><strong>설정 위치</strong></td>
<td>코드 내부</td>
<td>외부 설정 파일(web.xml)</td>
</tr>
<tr>
<td><strong>가독성</strong></td>
<td>코드와 설정이 함께 있어 가독성 증가</td>
<td>설정이 코드와 분리되어 가독성 떨어질 수 있음</td>
</tr>
<tr>
<td><strong>유연성</strong></td>
<td>간단한 설정에 적합</td>
<td>복잡한 설정, 필터, 리스너, 보안 설정에 유리</td>
</tr>
<tr>
<td><strong>변경 용이성</strong></td>
<td>코드 수정 시 서블릿 설정 자동 반영</td>
<td>설정을 수정하려면 web.xml을 따로 수정해야 함</td>
</tr>
<tr>
<td><strong>사용 시기</strong></td>
<td>단일 서블릿이거나 간단한 설정에 적합</td>
<td>대규모 애플리케이션이나 복잡한 설정에 적합</td>
</tr>
</tbody></table>
<hr>
<h3 id="언제-애너테이션을-사용하고-언제-webxml을-사용할까">언제 애너테이션을 사용하고, 언제 web.xml을 사용할까?</h3>
<ol>
<li><strong>애너테이션 사용 시기</strong>:<ul>
<li>서블릿, 필터, 리스너의 설정이 단순할 때.</li>
<li>소규모 애플리케이션에서, 설정이 복잡하지 않고 코드와 설정이 함께 있으면 더 직관적일 때.</li>
<li>빠르게 프로토타입을 개발하거나 테스트하는 경우.</li>
</ul>
</li>
<li><strong>web.xml 사용 시기</strong>:<ul>
<li>복잡한 설정이 필요한 경우(예: 여러 URL 패턴, 보안 설정, 필터 체인 설정 등).</li>
<li>설정 파일을 따로 관리하여 설정의 <strong>유연성과 유지보수성</strong>을 높여야 하는 경우.</li>
<li>환경마다 설정이 달라질 수 있는 대규모 애플리케이션에서, 배포 환경에 따라 설정을 쉽게 변경할 수 있어야 하는 경우.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="애너테이션과-webxml-혼합-사용">애너테이션과 web.xml 혼합 사용</h3>
<p>애너테이션과 <code>web.xml</code>을 함께 사용할 수도 있습니다. 예를 들어, 주로 간단한 서블릿 매핑은 애너테이션을 사용하고, 복잡한 필터와 리스너 설정이나 환경별 설정이 필요한 경우에는 <code>web.xml</code>을 사용하는 방식으로 <strong>혼합 사용</strong>이 가능합니다.</p>
<hr>
<h3 id="요약">요약</h3>
<ul>
<li><strong>@WebServlet 애너테이션</strong>은 서블릿 매핑을 간단하고 직관적으로 설정할 수 있는 방법입니다.</li>
<li>코드와 설정을 한 곳에서 관리할 수 있어, 소규모 프로젝트나 간단한 서블릿 매핑에는 애너테이션이 유리합니다.</li>
<li><code>web.xml</code>은 복잡한 설정이 필요한 경우에 유리하며, 보안 설정, 필터, 리스너 등 전체 애플리케이션 설정이 필요한 경우 여전히 중요한 역할을 합니다.</li>
<li>두 방식을 <strong>혼합하여 사용하는 것</strong>도 가능하며, 애플리케이션의 요구 사항에 따라 유연하게 선택할 수 있습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web.xml]]></title>
            <link>https://velog.io/@dev_sjb/Web.xml</link>
            <guid>https://velog.io/@dev_sjb/Web.xml</guid>
            <pubDate>Tue, 14 Apr 2026 09:16:15 GMT</pubDate>
            <description><![CDATA[<p><strong>web.xml</strong>: 배포 설명자(Deployment Descriptor)로서, <strong>서블릿을 설정하고 웹 애플리케이션의 동작을 정의하는 파일</strong>입니다. 서블릿의 매핑, 초기화 매개변수, 필터, 리스너 등을 설정할 수 있습니다.</p>
<hr>
<h3 id="webxml의-주요-역할">web.xml의 주요 역할</h3>
<ol>
<li><strong>서블릿 매핑</strong>: 서블릿 클래스와 URL 경로를 연결하여 특정 URL 요청이 들어올 때 해당 서블릿이 실행되도록 합니다.</li>
<li><strong>필터와 리스너 설정</strong>: 요청 전/후 작업을 처리하는 필터와 애플리케이션 상태 변화에 대한 이벤트 처리를 담당하는 리스너를 설정할 수 있습니다.</li>
<li><strong>초기화 매개변수 설정</strong>: 애플리케이션과 서블릿에 필요한 초기 매개변수를 설정할 수 있습니다.</li>
<li><strong>보안 설정</strong>: 보안 설정을 통해 특정 URL에 접근 권한을 부여하거나, 인증을 적용할 수 있습니다.</li>
</ol>
<hr>
<h3 id="기본-webxml-구조">기본 web.xml 구조</h3>
<p>web.xml 파일은 <code>WEB-INF</code> 폴더에 위치하며, 웹 애플리케이션의 설정을 XML 형식으로 정의합니다.</p>
<pre><code class="language-xml">&lt;web-app xmlns=&quot;&lt;http://xmlns.jcp.org/xml/ns/javaee&gt;&quot;
         xmlns:xsi=&quot;&lt;http://www.w3.org/2001/XMLSchema-instance&gt;&quot;
         xsi:schemaLocation=&quot;&lt;http://xmlns.jcp.org/xml/ns/javaee&gt;
                             &lt;http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd&gt;&quot;
         version=&quot;3.1&quot;&gt;

    &lt;!-- 서블릿 설정 --&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;HelloServlet&lt;/servlet-name&gt;
        &lt;servlet-class&gt;com.example.HelloServlet&lt;/servlet-class&gt;
    &lt;/servlet&gt;

    &lt;!-- 서블릿 매핑 설정 --&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;HelloServlet&lt;/servlet-name&gt;
        &lt;url-pattern&gt;/hello&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;

    &lt;!-- 초기화 매개변수 설정 (필요시 추가) --&gt;
    &lt;context-param&gt;
        &lt;param-name&gt;configLocation&lt;/param-name&gt;
        &lt;param-value&gt;/WEB-INF/config.properties&lt;/param-value&gt;
    &lt;/context-param&gt;

&lt;/web-app&gt;
</code></pre>
<hr>
<h3 id="webxml의-주요-구성-요소">web.xml의 주요 구성 요소</h3>
<ol>
<li><strong><servlet></strong>: 서블릿 클래스를 정의합니다. 서블릿 이름과 클래스 경로를 설정합니다.<ul>
<li><code>&lt;servlet-name&gt;</code>: 서블릿의 이름을 지정합니다. <code>&lt;servlet-mapping&gt;</code>과 연결됩니다.</li>
<li><code>&lt;servlet-class&gt;</code>: 서블릿 클래스의 전체 경로를 지정합니다.</li>
</ul>
</li>
<li><strong><servlet-mapping></strong>: 특정 URL 요청이 해당 서블릿으로 매핑되도록 설정합니다.<ul>
<li><code>&lt;servlet-name&gt;</code>: 요청을 처리할 서블릿의 이름을 지정합니다. <code>&lt;servlet&gt;</code>의 <code>servlet-name</code>과 일치해야 합니다.</li>
<li><code>&lt;url-pattern&gt;</code>: 서블릿과 연결할 URL 패턴을 지정합니다. 예를 들어, <code>/hello</code> 경로에 매핑하여 <code>http://localhost:8080/프로젝트명/hello</code>로 요청이 들어오면 HelloServlet이 실행됩니다.</li>
</ul>
</li>
<li><strong><context-param></strong>: 웹 애플리케이션 전체에서 사용할 수 있는 전역 매개변수를 정의합니다.<ul>
<li><code>&lt;param-name&gt;</code>과 <code>&lt;param-value&gt;</code>를 통해 전역적으로 필요한 설정 값을 지정할 수 있습니다. 예: 설정 파일 경로.</li>
</ul>
</li>
<li><strong><init-param></strong>: 특정 서블릿에 대한 초기화 매개변수를 설정합니다.<ul>
<li><code>&lt;servlet&gt;</code> 태그 내부에서 설정하여 특정 서블릿에만 적용되는 초기 설정 값을 지정할 수 있습니다.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="webxml을-사용한-서블릿-설정-예제">web.xml을 사용한 서블릿 설정 예제</h3>
<h3 id="helloservlet-서블릿을-webxml에-설정">HelloServlet 서블릿을 web.xml에 설정</h3>
<pre><code class="language-xml">&lt;web-app xmlns=&quot;&lt;http://xmlns.jcp.org/xml/ns/javaee&gt;&quot;
         xmlns:xsi=&quot;&lt;http://www.w3.org/2001/XMLSchema-instance&gt;&quot;
         xsi:schemaLocation=&quot;&lt;http://xmlns.jcp.org/xml/ns/javaee&gt;
                             &lt;http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd&gt;&quot;
         version=&quot;3.1&quot;&gt;

    &lt;!-- HelloServlet 서블릿 설정 --&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;HelloServlet&lt;/servlet-name&gt;
        &lt;servlet-class&gt;com.example.HelloServlet&lt;/servlet-class&gt;
    &lt;/servlet&gt;

    &lt;!-- HelloServlet 매핑 설정 --&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;HelloServlet&lt;/servlet-name&gt;
        &lt;url-pattern&gt;/hello&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;
&lt;/web-app&gt;
</code></pre>
<ul>
<li><strong>HelloServlet 서블릿 설정</strong>: <code>servlet-name</code>과 <code>servlet-class</code>를 사용하여 서블릿 이름과 클래스를 지정합니다.</li>
<li><strong>서블릿 매핑 설정</strong>: <code>url-pattern</code>을 <code>/hello</code>로 지정하여 클라이언트가 <code>/hello</code>로 요청하면 <code>HelloServlet</code>이 실행되도록 설정합니다.</li>
</ul>
<hr>
<h3 id="추가-기능">추가 기능</h3>
<h3 id="1-초기화-매개변수-설정-서블릿에-대한-init-param">1. 초기화 매개변수 설정 (서블릿에 대한 init-param)</h3>
<p>서블릿에 특정 초기 설정 값을 전달하려면 <code>&lt;init-param&gt;</code> 태그를 사용할 수 있습니다. 예를 들어, <code>message</code>라는 초기 매개변수를 설정할 수 있습니다.</p>
<pre><code class="language-xml">&lt;servlet&gt;
    &lt;servlet-name&gt;HelloServlet&lt;/servlet-name&gt;
    &lt;servlet-class&gt;com.example.HelloServlet&lt;/servlet-class&gt;
    &lt;init-param&gt;
        &lt;param-name&gt;message&lt;/param-name&gt;
        &lt;param-value&gt;Hello from init-param!&lt;/param-value&gt;
    &lt;/init-param&gt;
&lt;/servlet&gt;
</code></pre>
<ul>
<li>이 초기화 매개변수는 <code>HelloServlet</code> 내에서 <code>getServletConfig().getInitParameter(&quot;message&quot;)</code>로 접근할 수 있습니다.</li>
</ul>
<hr>
<h3 id="2-필터-설정">2. 필터 설정</h3>
<p><strong>필터</strong>는 요청과 응답의 전후에 처리할 작업을 수행할 수 있게 해줍니다. 예를 들어, 요청 로깅, 응답 압축, 인증 등을 수행할 수 있습니다.</p>
<pre><code class="language-xml">&lt;filter&gt;
    &lt;filter-name&gt;LoggingFilter&lt;/filter-name&gt;
    &lt;filter-class&gt;com.example.LoggingFilter&lt;/filter-class&gt;
&lt;/filter&gt;

&lt;filter-mapping&gt;
    &lt;filter-name&gt;LoggingFilter&lt;/filter-name&gt;
    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;
</code></pre>
<ul>
<li><strong>LoggingFilter</strong>라는 필터를 설정하고, 모든 URL 패턴 (<code>/*</code>)에 대해 요청을 가로채서 필터가 처리하도록 설정합니다.</li>
</ul>
<hr>
<h3 id="3-리스너-설정">3. 리스너 설정</h3>
<p><strong>리스너</strong>는 웹 애플리케이션의 상태 변화를 감지하고 처리할 수 있습니다. 예를 들어, 애플리케이션 시작 시 초기화 작업을 수행하는 리스너를 추가할 수 있습니다.</p>
<pre><code class="language-xml">&lt;listener&gt;
    &lt;listener-class&gt;com.example.AppListener&lt;/listener-class&gt;
&lt;/listener&gt;</code></pre>
<ul>
<li><code>AppListener</code> 클래스는 <code>ServletContextListener</code>를 구현하여, 애플리케이션 시작과 종료 시 특정 작업을 수행할 수 있습니다.</li>
</ul>
<hr>
<h3 id="요약">요약</h3>
<ul>
<li><strong>web.xml</strong>은 서블릿 매핑, 초기화 매개변수, 필터, 리스너 등을 설정하여 <strong>웹 애플리케이션의 전반적인 동작을 정의</strong>하는 역할을 합니다.</li>
<li>*<servlet>와 <servlet-mapping>을 통해 서블릿 클래스를 정의하고, 요청이 들어올 URL 패턴과 연결하여 특정 요청이 어떤 서블릿에서 처리될지 설정합니다.</li>
<li>*<context-param>과 <init-param>을 사용하여 전역 및 서블릿에 대한 초기화 설정을 적용할 수 있습니다.</li>
</ul>
<p><code>web.xml</code>은 특히 대규모 애플리케이션에서 구조적 설정을 관리하는 데 유용하며, 서블릿의 동작과 웹 애플리케이션 전반의 설정을 한 곳에서 관리할 수 있게 해줍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DAO 와 DTO]]></title>
            <link>https://velog.io/@dev_sjb/DAO-%EC%99%80-DTO</link>
            <guid>https://velog.io/@dev_sjb/DAO-%EC%99%80-DTO</guid>
            <pubDate>Tue, 14 Apr 2026 09:14:48 GMT</pubDate>
            <description><![CDATA[<hr>
<h3 id="dto-data-transfer-object---데이터-전송-객체">DTO (Data Transfer Object) - 데이터 전송 객체</h3>
<p><strong>DTO</strong>는 <strong>Data Transfer Object</strong>의 약자로, <strong>데이터를 운반하는 역할</strong>을 하는 객체입니다. DTO는 주로 여러 계층(예: 데이터베이스, 서비스, 웹 등) 간에 데이터를 전달할 때 사용됩니다.</p>
<h3 id="dto의-주요-특징">DTO의 주요 특징</h3>
<ul>
<li><strong>순수한 데이터만 저장</strong>: DTO에는 오직 데이터와 그에 해당하는 <strong>getter/setter 메서드</strong>만 존재합니다.</li>
<li><strong>비즈니스 로직 없음</strong>: 계산이나 처리 등의 복잡한 로직은 포함하지 않으며, 단순히 데이터를 담고 전달하는 역할만 합니다.</li>
<li><strong>데이터 구조를 정의</strong>: 각 필드는 데이터베이스의 컬럼이나 비즈니스 로직에서 필요로 하는 데이터와 매핑됩니다.</li>
</ul>
<h3 id="dto-예시---userdto">DTO 예시 - <code>UserDTO</code></h3>
<p>예를 들어, <code>User</code> 정보(아이디, 이름, 이메일 등)를 담아 전달하는 <code>UserDTO</code> 클래스는 다음과 같습니다.</p>
<pre><code class="language-java">public class UserDTO {
    private int userId;
    private String username;
    private String email;

    // 기본 생성자
    public UserDTO() {}

    // getter와 setter
    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
</code></pre>
<ul>
<li><strong>필드</strong>: <code>userId</code>, <code>username</code>, <code>email</code> 필드가 있으며, 이는 데이터베이스나 애플리케이션에서 필요로 하는 사용자 정보를 담습니다.</li>
<li><strong>getter/setter</strong>: 각 필드에 대한 getter와 setter 메서드를 통해 데이터를 주고받습니다.</li>
</ul>
<p>이렇게 DTO를 통해 데이터를 주고받으면, 서비스 계층이나 다른 클래스에서 데이터를 쉽게 주고받을 수 있습니다.</p>
<hr>
<h3 id="2-dao-data-access-object---데이터-접근-객체">2. DAO (Data Access Object) - 데이터 접근 객체</h3>
<p><strong>DAO</strong>는 <strong>Data Access Object</strong>의 약자로, 데이터베이스와의 상호작용을 담당하는 객체입니다. 주로 데이터베이스에 접근하여 <strong>데이터를 저장하거나 조회, 수정, 삭제</strong>하는 역할을 수행합니다.</p>
<h3 id="dao의-주요-특징">DAO의 주요 특징</h3>
<ul>
<li><strong>데이터베이스 작업 전담</strong>: 데이터베이스와의 연결, SQL 실행 등을 처리합니다.</li>
<li><strong>CRUD 작업 담당</strong>: <code>Create(생성)</code>, <code>Read(조회)</code>, <code>Update(수정)</code>, <code>Delete(삭제)</code> 작업을 수행하는 메서드를 포함합니다.</li>
<li><strong>DTO와 함께 사용</strong>: DAO는 주로 DTO와 함께 사용하여, 데이터베이스로부터 조회한 데이터를 DTO에 담아 반환하거나, DTO에 담긴 데이터를 받아 저장합니다.</li>
</ul>
<h3 id="dao-예시---userdao">DAO 예시 - <code>UserDAO</code></h3>
<p>다음은 <code>UserDAO</code> 클래스에서 <code>UserDTO</code> 객체를 활용하여 데이터베이스에서 사용자 정보를 조회, 삽입하는 예시입니다.</p>
<pre><code class="language-java">import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class UserDAO {
    private Connection conn;

    // 데이터베이스 연결 메서드
    private Connection getConnection() throws Exception {
        String url = &quot;jdbc:mariadb://localhost:3306/mydatabase&quot;;
        String username = &quot;root&quot;;
        String password = &quot;password&quot;;
        conn = DriverManager.getConnection(url, username, password);
        return conn;
    }

    // 사용자 추가
    public void addUser(UserDTO user) throws Exception {
        try {
            conn = getConnection();
            String sql = &quot;INSERT INTO users (user_id, username, email) VALUES (?, ?, ?)&quot;;
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, user.getUserId());
            pstmt.setString(2, user.getUsername());
            pstmt.setString(3, user.getEmail());
            pstmt.executeUpdate();
        } finally {
            if (conn != null) conn.close();
        }
    }

    // 모든 사용자 조회
    public List&lt;UserDTO&gt; getAllUsers() throws Exception {
        List&lt;UserDTO&gt; userList = new ArrayList&lt;&gt;();
        try {
            conn = getConnection();
            String sql = &quot;SELECT * FROM users&quot;;
            PreparedStatement pstmt = conn.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();

            while (rs.next()) {
                UserDTO user = new UserDTO();
                user.setUserId(rs.getInt(&quot;user_id&quot;));
                user.setUsername(rs.getString(&quot;username&quot;));
                user.setEmail(rs.getString(&quot;email&quot;));
                userList.add(user);
            }
        } finally {
            if (conn != null) conn.close();
        }
        return userList;
    }
}
</code></pre>
<ul>
<li><strong><code>getConnection</code> 메서드</strong>: 데이터베이스에 연결합니다.</li>
<li><strong><code>addUser</code> 메서드</strong>: <code>UserDTO</code> 객체를 인자로 받아 데이터베이스에 사용자 정보를 삽입합니다.</li>
<li><strong><code>getAllUsers</code> 메서드</strong>: 데이터베이스에서 모든 사용자 정보를 조회하고, 각 사용자 정보를 <code>UserDTO</code> 객체에 담아 리스트로 반환합니다.</li>
</ul>
<p>이렇게 DAO는 데이터베이스와의 직접적인 상호작용을 처리하여, 다른 클래스에서 데이터베이스와의 의존성을 줄일 수 있습니다.</p>
<hr>
<h3 id="dao와-dto의-역할-요약">DAO와 DTO의 역할 요약</h3>
<table>
<thead>
<tr>
<th>개념</th>
<th>역할</th>
<th>포함 내용</th>
</tr>
</thead>
<tbody><tr>
<td>DTO</td>
<td>데이터 전송을 위한 객체</td>
<td>순수 데이터, getter/setter, 비즈니스 로직 없음</td>
</tr>
<tr>
<td>DAO</td>
<td>데이터베이스 접근과 CRUD 작업 담당</td>
<td>데이터베이스 연결, SQL 실행, DTO와 함께 사용</td>
</tr>
</tbody></table>
<hr>
<h3 id="예제-흐름">예제 흐름</h3>
<ol>
<li><strong>데이터베이스에 접근</strong>할 필요가 있는 서비스나 JSP 페이지에서 <code>UserDAO</code>의 메서드를 호출합니다.</li>
<li><code>UserDAO</code>는 데이터베이스에 연결하고, <strong>데이터 조회</strong>나 <strong>삽입 작업</strong>을 수행합니다.</li>
<li><code>UserDTO</code> 객체를 통해 데이터를 DAO와 서비스, JSP 페이지 간에 주고받습니다.</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring - Slf4j(Simple Logging Facade for Java) 로거]]></title>
            <link>https://velog.io/@dev_sjb/Spring-Slf4J</link>
            <guid>https://velog.io/@dev_sjb/Spring-Slf4J</guid>
            <pubDate>Tue, 07 Apr 2026 14:22:23 GMT</pubDate>
            <description><![CDATA[<h3 id="slf4j-애너테이션이란">@Slf4j 애너테이션이란?</h3>
<p><code>@Slf4j</code>는 Lombok에서 제공하는 애너테이션으로, 클래스에 Logger 객체를 자동으로 생성해주는 역할을 합니다. 이 애너테이션을 사용하면 코드에서 Logger 객체를 생성하는 번거로움을 줄이고 간결하게 로그를 처리할 수 있습니다.</p>
<hr>
<h3 id="slf4j와-logging의-개념-이해">Slf4j와 Logging의 개념 이해</h3>
<h3 id="1-slf4j란">1. Slf4j란?</h3>
<p>Slf4j(Simple Logging Facade for Java)는 Java의 로깅 프레임워크를 통합하기 위한 <strong>추상화 계층</strong>입니다.</p>
<p>Slf4j 자체는 로깅을 처리하지 않고, 실제 로깅 구현체(e.g., Logback, Log4j)를 연결하여 사용합니다.</p>
<blockquote>
<p>장점: Slf4j를 사용하면 로깅 구현체를 변경하더라도 코드 수정 없이 설정만으로 변경이 가능합니다.</p>
</blockquote>
<h3 id="2-로깅이란">2. 로깅이란?</h3>
<p>로깅은 애플리케이션의 실행 과정 중 중요한 정보를 기록하는 작업입니다.</p>
<ul>
<li><strong>왜 로깅이 필요한가?</strong><ul>
<li>디버깅과 문제 해결: 오류 발생 시 원인 파악 가능.</li>
<li>시스템 분석: 실행 흐름과 성능을 분석.</li>
<li>모니터링: 애플리케이션 상태와 동작 확인.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="slf4j의-사용-방식">@Slf4j의 사용 방식</h3>
<ol>
<li><strong>Logger 수동 생성 방식</strong>
일반적으로 Logger를 직접 생성하려면 아래와 같은 코드를 작성해야 합니다.</li>
</ol>
<pre><code class="language-java">import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Example {
    private static final Logger log = LoggerFactory.getLogger(Example.class);

    public void doSomething() {
        log.info(&quot;This is an info log&quot;);
        log.error(&quot;This is an error log&quot;);
    }
}
</code></pre>
<ol>
<li><strong>@Slf4j 애너테이션 사용 방식</strong><code>@Slf4j</code>를 사용하면 Lombok이 Logger 객체를 자동으로 생성합니다.</li>
</ol>
<pre><code class="language-java">import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Example {

    public void doSomething() {
        log.info(&quot;This is an info log&quot;);
        log.error(&quot;This is an error log&quot;);
    }
}
</code></pre>
<blockquote>
<p>자동 생성된 Logger 필드: private static final org.slf4j.Logger log</p>
</blockquote>
<hr>
<h3 id="slf4j-로그-레벨">Slf4j 로그 레벨</h3>
<p>Slf4j를 통해 다양한 로그 레벨로 메시지를 기록할 수 있습니다.</p>
<table>
<thead>
<tr>
<th>로그 레벨</th>
<th>설명</th>
<th>예제</th>
</tr>
</thead>
<tbody><tr>
<td><code>TRACE</code></td>
<td>가장 세부적인 로그. 디버깅에 유용.</td>
<td><code>log.trace(&quot;Trace message&quot;);</code></td>
</tr>
<tr>
<td><code>DEBUG</code></td>
<td>디버깅 정보를 제공. 개발 중 유용.</td>
<td><code>log.debug(&quot;Debug message&quot;);</code></td>
</tr>
<tr>
<td><code>INFO</code></td>
<td>일반적인 실행 정보. 상태 확인 용도.</td>
<td><code>log.info(&quot;Info message&quot;);</code></td>
</tr>
<tr>
<td><code>WARN</code></td>
<td>잠재적인 문제 경고.</td>
<td><code>log.warn(&quot;Warning message&quot;);</code></td>
</tr>
<tr>
<td><code>ERROR</code></td>
<td>오류 발생. 중요한 문제.</td>
<td><code>log.error(&quot;Error message&quot;);</code></td>
</tr>
</tbody></table>
<hr>
<h3 id="slf4j와-slf4j-사용-시-주의할-점">Slf4j와 @Slf4j 사용 시 주의할 점</h3>
<ol>
<li><strong>의존성 확인</strong>
Slf4j를 사용하려면 로깅 구현체(e.g., Logback)를 추가해야 합니다.</li>
</ol>
<pre><code>dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-logging&#39; // 기본적으로 Logback 포함
    compileOnly &#39;org.projectlombok:lombok&#39;
    annotationProcessor &#39;org.projectlombok:lombok&#39;
}
</code></pre><ol>
<li><strong>로그 레벨 설정</strong><code>application.properties</code> 또는 <code>application.yml</code>에서 로그 레벨을 설정합니다.</li>
</ol>
<pre><code>logging.level.root=INFO
logging.level.com.example=DEBUG</code></pre><hr>
<h3 id="추가-이해를-돕는-비유">추가 이해를 돕는 비유</h3>
<ul>
<li>Slf4j는 <strong>전원 멀티탭</strong>처럼 다양한 로깅 구현체(Logback, Log4j, JUL 등)를 연결해주는 역할.</li>
<li>@Slf4j는 <strong>자동으로 플러그를 꽂아주는 비서</strong> 역할을 합니다.
즉, 개발자는 로그를 직접 관리하지 않고 로그를 더 편리하게 사용할 수 있습니다.</li>
</ul>
<h3 id="systemoutprintln과-slf4j-로거의-차이점">System.out.println()과 Slf4j 로거의 차이점</h3>
<p>Java 애플리케이션에서 로그 메시지를 출력하기 위해 <code>System.out.println()</code>과 Slf4j 로거를 사용할 수 있습니다. 하지만 둘 사이에는 기능, 성능, 사용 편의성 등 여러 측면에서 차이가 있습니다.</p>
<hr>
<h3 id="용도"><strong>용도</strong></h3>
<h3 id="systemoutprintln">System.out.println()</h3>
<ul>
<li><strong>용도</strong>: 간단한 디버깅이나 테스트 시 주로 사용.</li>
<li><strong>작동 원리</strong>: 메시지를 표준 출력(콘솔)에 출력.</li>
<li><strong>한계</strong>: 로그 레벨, 출력 포맷, 설정 변경 등의 기능이 없음.</li>
</ul>
<h3 id="slf4j-로거">Slf4j 로거</h3>
<ul>
<li><strong>용도</strong>: <strong>전문적인 로깅</strong>을 위한 도구.</li>
<li><strong>작동 원리</strong>: Slf4j는 추상화 계층으로, Logback, Log4j 같은 로깅 구현체를 통해 다양한 로그 출력 및 관리 가능.</li>
<li><strong>장점</strong>: 로그 레벨, 포맷, 파일 출력, 성능 최적화 등의 고급 기능 제공.</li>
</ul>
<hr>
<h3 id="차이점-비교"><strong>차이점 비교</strong></h3>
<table>
<thead>
<tr>
<th><strong>구분</strong></th>
<th><strong>System.out.println()</strong></th>
<th><strong>Slf4j 로거</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>로그 레벨</strong></td>
<td>없음. 모든 메시지는 동일하게 출력.</td>
<td><code>TRACE</code>, <code>DEBUG</code>, <code>INFO</code>, <code>WARN</code>, <code>ERROR</code> 제공.</td>
</tr>
<tr>
<td><strong>유연성</strong></td>
<td>표준 출력(콘솔)로만 출력.</td>
<td>콘솔, 파일, 네트워크 등 다양한 출력 가능.</td>
</tr>
<tr>
<td><strong>성능</strong></td>
<td>느림. 모든 메시지를 즉시 출력.</td>
<td>메시지 출력 전에 조건을 체크하여 불필요한 연산 최소화.</td>
</tr>
<tr>
<td><strong>포맷팅</strong></td>
<td>기본적으로 문자열 연결 필요.</td>
<td><code>{}</code>를 사용한 효율적인 포맷팅 제공.</td>
</tr>
<tr>
<td><strong>설정</strong></td>
<td>설정 불가.</td>
<td>로그 출력 포맷, 레벨, 파일 경로 등을 설정 가능.</td>
</tr>
<tr>
<td><strong>멀티스레드 지원</strong></td>
<td>지원 부족.</td>
<td>스레드 안정성을 고려하여 설계됨.</td>
</tr>
<tr>
<td><strong>장기적 관리</strong></td>
<td>대규모 애플리케이션에서는 적합하지 않음.</td>
<td>대규모 시스템의 로그 관리에 적합.</td>
</tr>
</tbody></table>
<hr>
<h3 id="성능-차이"><strong>성능 차이</strong></h3>
<h3 id="systemoutprintln-1">System.out.println()</h3>
<ul>
<li><strong>즉각 실행</strong>: 출력할 메시지를 바로 처리하며, 출력이 완료될 때까지 스레드가 대기.</li>
<li><strong>비효율성</strong>: 모든 메시지를 출력하므로 불필요한 출력 작업이 발생.</li>
</ul>
<h3 id="slf4j-로거-1">Slf4j 로거</h3>
<ul>
<li><p><strong>조건부 실행</strong>: 로그 레벨 조건을 체크한 후 필요할 때만 출력.</p>
<pre><code class="language-java">  if (log.isDebugEnabled()) {
      log.debug(&quot;This is a debug log&quot;);
  }
</code></pre>
</li>
<li><p><strong>효율적인 메시지 처리</strong>: 문자열 연결 작업을 줄이고, 실행 시점에만 필요한 계산 수행.</p>
<pre><code class="language-java">  log.debug(&quot;User ID is {}&quot;, userId); // 문자열 연결 작업이 없으므로 성능 최적화.</code></pre>
</li>
</ul>
<hr>
<h3 id="비교"><strong>비교</strong></h3>
<h3 id="systemoutprintln-2">System.out.println()</h3>
<pre><code class="language-java">System.out.println(&quot;Application started&quot;);
System.out.println(&quot;User ID: &quot; + userId);
</code></pre>
<h3 id="slf4j-로거-2">Slf4j 로거</h3>
<pre><code class="language-java">log.info(&quot;Application started&quot;);
log.debug(&quot;User ID: {}&quot;, userId);
</code></pre>
<blockquote>
<p>차이점:</p>
<ul>
<li><code>System.out.println</code>은 문자열 연결 연산이 항상 실행됨.</li>
<li>Slf4j는 로그 레벨 조건에 따라 출력 여부를 결정하며, 문자열 연결 연산을 생략할 수 있음.</li>
</ul>
</blockquote>
<hr>
<h3 id="왜-slf4j를-사용해야-하는가"><strong>왜 Slf4j를 사용해야 하는가?</strong></h3>
<ol>
<li><strong>로그 레벨 관리</strong><ul>
<li>Slf4j는 로그의 중요도에 따라 <code>DEBUG</code>, <code>INFO</code>, <code>WARN</code>, <code>ERROR</code>로 구분하여 관리 가능.</li>
<li>운영 환경에서는 <code>DEBUG</code> 로그를 비활성화하고, <code>ERROR</code> 로그만 남길 수 있음.</li>
</ul>
</li>
<li><strong>출력 대상 설정</strong><ul>
<li>Slf4j는 로그를 콘솔뿐 아니라 파일, 데이터베이스, 원격 서버 등 다양한 출력 대상으로 설정 가능.</li>
</ul>
</li>
<li><strong>성능 최적화</strong><ul>
<li>Slf4j는 불필요한 로그 연산을 피하며, 성능 손실을 최소화.</li>
</ul>
</li>
<li><strong>장기적인 유지보수</strong><ul>
<li><code>System.out.println</code>은 대규모 애플리케이션에서 관리하기 어렵지만, Slf4j는 체계적인 로깅 관리가 가능.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="systemoutprintln를-사용할-때의-한계-사례"><strong>System.out.println()를 사용할 때의 한계 사례</strong></h3>
<h3 id="문제-상황-불필요한-디버깅-출력">문제 상황: 불필요한 디버깅 출력</h3>
<p>운영 환경에서 디버깅 메시지가 계속 출력되면 다음과 같은 문제가 발생:</p>
<ul>
<li><strong>보안 문제</strong>: 민감한 정보가 콘솔에 출력될 위험.</li>
<li><strong>성능 저하</strong>: 디버깅 메시지로 인해 시스템 속도가 느려짐.</li>
<li><strong>로그 과잉</strong>: 중요한 로그를 분석하기 어렵게 만듦.</li>
</ul>
<p>Slf4j를 사용하면 로그 레벨과 설정 파일로 쉽게 제어 가능.</p>
<hr>
<ul>
<li><code>System.out.println()</code>은 <strong>간단한 디버깅</strong> 용도로 사용하기엔 적합하지만, 대규모 애플리케이션에서는 유지보수와 성능 측면에서 한계가 있음.</li>
<li>Slf4j는 로깅을 체계적으로 관리하며 성능과 확장성을 제공하므로, <strong>실제 애플리케이션에서는 Slf4j 사용이 권장</strong>됩니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring - Controller Mapping]]></title>
            <link>https://velog.io/@dev_sjb/Spring-Controller-Mapping</link>
            <guid>https://velog.io/@dev_sjb/Spring-Controller-Mapping</guid>
            <pubDate>Tue, 07 Apr 2026 14:11:30 GMT</pubDate>
            <description><![CDATA[<h2 id="1-http-메서드란">1. HTTP 메서드란?</h2>
<ul>
<li>클라이언트가 서버에게 “무엇을 하고 싶은지”를 나타내는 요청 방식</li>
</ul>
<p>예를 들어</p>
<pre><code>GET /users
POST /users</code></pre><p>같은 URL이라도
HTTP 메서드에 따라 의미가 완전히 달라진다.</p>
<hr>
<h2 id="2-get-조회">2. GET (조회)</h2>
<p>정의</p>
<ul>
<li>서버에서 데이터를 조회할 때 사용</li>
</ul>
<p>특징
    •    데이터를 조회(Read) 할 때 사용
    •    서버 상태를 변경하지 않음 (Safe)
    •    URL에 파라미터를 포함</p>
<p>  예시</p>
<pre><code class="language-http">GET /users
GET /users/1</code></pre>
<h3 id="spring-코드">Spring 코드</h3>
<pre><code class="language-java">@GetMapping(&quot;/users&quot;)
public String getUsers() {
    return &quot;user list&quot;;
}

@GetMapping(&quot;/users/{id}&quot;)
public String getUser(@PathVariable String id) {
    return &quot;user id = &quot; + id;
}</code></pre>
<hr>
<h2 id="3-post-생성">3. POST (생성)</h2>
<p>정의</p>
<ul>
<li>서버에 새로운 데이터를 생성할 때 사용</li>
</ul>
<p>특징
    •    데이터를 생성(Create) 할 때 사용
    •    요청 바디에 데이터 포함
    •    서버 상태 변경 발생</p>
<p>   예시</p>
<pre><code>POST /users</code></pre><h3 id="spring-코드-1">Spring 코드</h3>
<pre><code class="language-java">@PostMapping(&quot;/users&quot;)
public String addUser() {
    return &quot;create user&quot;;
}</code></pre>
<hr>
<h2 id="4--patch-수정---일부-변경">4.  PATCH (수정 - 일부 변경)</h2>
<p>정의</p>
<ul>
<li>기존 데이터의 일부를 수정할 때 사용</li>
</ul>
<p>특징
    •    데이터를 부분 수정(Update 일부) 할 때 사용
    •    변경된 필드만 전달</p>
<p>   예시</p>
<pre><code>PATCH /users/1</code></pre><h3 id="spring-코드-2">Spring 코드</h3>
<pre><code class="language-java">@PatchMapping(&quot;/users/{id}&quot;)
public String updateUser(@PathVariable String id) {
    return &quot;update user id = &quot; + id;
}</code></pre>
<hr>
<h2 id="5-delete-삭제">5. DELETE (삭제)</h2>
<p>정의</p>
<p>데이터를 삭제할 때 사용</p>
<p>특징
    •    데이터를 삭제(Delete) 할 때 사용
    •    서버 상태 변경 발생</p>
<p>   예시</p>
<pre><code>DELETE /users/1</code></pre><h3 id="spring-코드-3">Spring 코드</h3>
<pre><code class="language-java">@DeleteMapping(&quot;/users/{id}&quot;)
public String deleteUser(@PathVariable String id) {
    return &quot;delete user id = &quot; + id;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP/ HTTPS]]></title>
            <link>https://velog.io/@dev_sjb/HTTP-HTTPS</link>
            <guid>https://velog.io/@dev_sjb/HTTP-HTTPS</guid>
            <pubDate>Tue, 07 Apr 2026 08:02:23 GMT</pubDate>
            <description><![CDATA[<h2 id="1-http란">1. HTTP란?</h2>
<p>HTTP (HyperText Transfer Protocol) 는
클라이언트(브라우저)와 서버 간 데이터를 주고받기 위한 <strong>통신 규약(프로토콜)</strong>이다.
    •    기본 포트: 80번
    •    특징: 암호화 없음 (평문 통신)</p>
<h2 id="2-https란https-over-ssl">2. HTTPS란?(HTTPS over SSL)</h2>
<p>HTTPS (HTTP Secure) 는 HTTP에 SSL/TLS 암호화 계층이 추가된 프로토콜이다.
로그인 자격 증명이 필요한 웹사이트는 HTTPS를 사용해야 한다.
HTTP 프로토콜을 사용하기 위해서는 인증기관(CA) 로부터 인증서를 발급받아야 한다.
    •    기본 포트: 443번
    •    특징: 데이터 암호화
<img src="https://velog.velcdn.com/images/dev_sjb/post/4050cc9d-53ef-44a3-8884-1273a8066930/image.png" alt=""></p>
<h2 id="3-암호화-방식">3. 암호화 방식</h2>
<h3 id="대칭키-방식">대칭키 방식</h3>
<p><img src="https://velog.velcdn.com/images/dev_sjb/post/293ed898-95c9-41f3-9a7e-8509d64a674e/image.png" alt="">
하나의 같은 키로 암호화와 복호화를 모두 수행하는 방식 </p>
<p>특징</p>
<ul>
<li>속도가 매우 빠르다.</li>
<li>대량데이터 처리에 적합하다.</li>
</ul>
<p>단점 </p>
<ul>
<li>키를 안전하게 전달하기 어려움 (가장 큰 문제)
ex) AES</li>
</ul>
<h3 id="비대칭키-방식">비대칭키 방식</h3>
<p><img src="https://velog.velcdn.com/images/dev_sjb/post/d9259be2-56c5-4e8d-994b-ccb36df43317/image.png" alt=""></p>
<p>공개키 + 개인키 두 개의 키를 사용하는 방식
    •    공개키: 누구나 볼 수 있음
    •    개인키: 서버만 가지고 있음</p>
<p> 특징
    •    키 교환이 안전함
    •    보안성이 높음</p>
<p> 단점
    •    속도가 느림</p>
<p>ex) RSA</p>
<h3 id="하리브리드-방식">하리브리드 방식</h3>
<p>실제 HTTPS는 이걸 사용합니다.
동작 방식</p>
<ol>
<li>비대칭키로 대칭키를 안전학 전달 </li>
<li>이후 통신은 대칭키로 빠르게 처리 
보안 + 성능 둘 다 잡음</li>
</ol>
<h2 id="4-ssl-인증서-동작-원리">4. SSL 인증서 동작 원리</h2>
<h3 id="1-ssl인증서-발급-과정">1) SSL인증서 발급 과정</h3>
<p>흐름  </p>
<ol>
<li>서버 → 공개키 생성<ol start="2">
<li>서버 → CSR (인증서 요청) 생성</li>
<li>서버 → CA(인증기관)에 요청</li>
<li>CA → 도메인 검증</li>
<li>CA → 디지털 서명 후 인증서 발급</li>
</ol>
</li>
</ol>
<h3 id="2-ca의-서명">2) CA의 서명</h3>
<p>CA는 서버의 공개키를 검증하고
자신의 개인키로 서명을 한다.</p>
<p>클라이언트는 CA 공개키를 검증 </p>
<h3 id="3-self-signed-ssl">3) Self-Signed SSL</h3>
<p>서버가 자기 자신이 서명한 인증서 </p>
<p>특징</p>
<ul>
<li>무료</li>
<li>테스트용으로 사용</li>
</ul>
<p>단점</p>
<ul>
<li>브라우저에서 &quot;신뢰할 수 없음&quot; 경고</li>
</ul>
<h3 id="4-ssl-인증서를-통한-암호화된-통신-원리">4) SSL 인증서를 통한 암호화된 통신 원리</h3>
<p>핵심 흐름
<img src="https://velog.velcdn.com/images/dev_sjb/post/c5667de8-38ce-47ed-b523-fed90a647ff7/image.png" alt=""></p>
<pre><code>1.    클라이언트 → 서버 접속
2.    서버 → SSL 인증서 전달
3.    클라이언트 → CA로 인증서 검증
4.    안전하다고 판단하면 통신 시작</code></pre><h3 id="5-ssl-handshake">5) SSL Handshake</h3>
<p><img src="https://velog.velcdn.com/images/dev_sjb/post/7a7d9cc5-8178-43cd-85d2-30bcdff78806/image.png" alt=""></p>
<p>단계별 흐름
    1.    Client Hello
    •    지원 가능한 암호화 방식 전달
    2.    Server Hello
    •    사용할 암호화 방식 선택
    •    SSL 인증서 전달
    3.    인증서 검증
    •    CA를 통해 서버 신뢰 여부 확인
    4.    키 교환
    •    대칭키 생성 후 공개키로 암호화하여 전달
    5.    세션 생성
    •    이후 통신은 대칭키 사용</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[E-Commerce 프로젝트 - Entity 설계(Member, Product, Order, OrderItem)]]></title>
            <link>https://velog.io/@dev_sjb/E-Commerce-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Entity-%EC%84%A4%EA%B3%84Member-Product-Order-OrderItem</link>
            <guid>https://velog.io/@dev_sjb/E-Commerce-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Entity-%EC%84%A4%EA%B3%84Member-Product-Order-OrderItem</guid>
            <pubDate>Thu, 26 Mar 2026 06:19:58 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-개요">프로젝트 개요</h2>
<p>Spring Boot 기반 이커머스 프로젝트를 진행하면서
핵심 도메인(Entity)을 먼저 설계했다.</p>
<p>단순 CRUD가 아닌 실제 서비스 흐름을 고려하여
회원 → 상품 → 주문 → 주문상품 구조를 설계하였다.</p>
<h2 id="전체-구조-및-관계">전체 구조 및 관계</h2>
<pre><code class="language-text">Member
Product
Order
OrderItem
Cart(나중에)

Member (1) : (N) Order
Order (1) : (N) OrderItem
Product (1) : (N) OrderItem</code></pre>
<h2 id="memberentity">MemberEntity</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.member.entity;

import com.jeongbeom.ecommerce.common.entity.BaseTimeEntity;
import com.jeongbeom.ecommerce.common.entity.Role;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) // 기본 생성자 자동 생성, 외부 생성 차단
public class Member extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; //PK

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String phone;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private Role role;

    public Member(String email, String password, String phone, Role role) {
        this.email = email;
        this.password = password;
        this.phone = phone;
        this.role = role;
    }
}
</code></pre>
<blockquote>
<ul>
<li>Member 엔티티는 사용자 정보를 관리하며,  email에 unique 제약조건을 설정하여 중복 데이터를 방지하였다.</li>
<li>Role을 Enum 타입으로 관리하여 사용자 권한을 명확하게 구분할 수 있도록 설계하였다. </li>
</ul>
</blockquote>
<h2 id="memberrepository">MemberRepository</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.member.repository;

import com.jeongbeom.ecommerce.member.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberRepository extends JpaRepository&lt;Member, Long&gt; {
    Optional&lt;Member&gt; findByEmail(String email);
}
</code></pre>
<h2 id="orderentity">OrderEntity</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.order.entity;

import com.jeongbeom.ecommerce.common.entity.BaseTimeEntity;
import com.jeongbeom.ecommerce.member.entity.Member;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Table(name = &quot;orders&quot;)
@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)
public class Order extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; //PK

    //Member 연결
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;member_id&quot;, nullable = false)
    private Member member;

    @Column(nullable = false)
    private String orderNumber;

    @Column(nullable = false)
    private int totalPrice;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private OrderStatus status;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String phone;

    @Column(nullable = false)
    private String address;

    public Order(Member member, String orderNumber, int totalPrice, OrderStatus status, String name, String phone, String address) {
        this.member = member;
        this.orderNumber = orderNumber;
        this.totalPrice = totalPrice;
        this.status = status;
        this.name = name;
        this.phone = phone;
        this.address = address;
    }
}
</code></pre>
<blockquote>
<ul>
<li>Order 엔티티는 주문 상태를 OrderStatus Enum으로 관리하여<br>주문의 lifecycle을 명확하게 표현할 수 있도록 설계하였다.</li>
<li>배송 정보를 별도로 저장하여 회원 정보 변경과 관계없이 주문 시점의 배송 정보를 유지할 수 있도록 하였다.</li>
<li>Member와의 연관관계를 통해 주문 주체를 명확히 하고 데이터 정합성을 유지하도록 설계하였다.</li>
</ul>
</blockquote>
<h2 id="orderrepository">OrderRepository</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.order.entity.repository;

import com.jeongbeom.ecommerce.order.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository&lt;Order, Long&gt; {
}
</code></pre>
<h2 id="orderstarus">OrderStarus</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.order.entity;

public enum OrderStatus {
    CREATED,    //주문 생성됨 (결제전)
    PAID,       //결제 완료
    PREPARING,  //상품 준비중
    SHIPPED,    //배송 시작
    DELIVERED,  //배송 완료
    CANCELLED   //주문 취소
}
</code></pre>
<h2 id="orderitementity">OrderItemEntity</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.order.entity;

import com.jeongbeom.ecommerce.common.entity.BaseTimeEntity;
import com.jeongbeom.ecommerce.product.entity.Product;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)
public class OrderItem extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; //PK

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;product_id&quot;, nullable = false)
    private Product product;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;order_id&quot;, nullable = false)
    private Order order;

    @Column(nullable = false)
    private int orderPrice;

    @Column(nullable = false)
    private int orderQuantity;

    public OrderItem(Product product, Order order, int orderPrice, int orderQuantity) {
        this.product = product;
        this.order = order;
        this.orderPrice = orderPrice;
        this.orderQuantity = orderQuantity;
    }
}</code></pre>
<blockquote>
<ul>
<li>OrderItem은 주문(Order)과 상품(Product)을 연결하는 핵심 엔티티로 설계하였다. </li>
<li>상품 가격은 변동될 수 있기 때문에 주문 시점의 가격(orderPrice)을 별도로 저장하여, 이후 상품 가격이 변경되더라도 주문 당시의 정보를 유지할 수 있도록 하였다.</li>
<li>주문 수량(quantity)을 함께 저장하여 주문 상세 정보를 구성할 수 있도록 설계하였다.</li>
</ul>
</blockquote>
<h2 id="orderitemrepository">OrderItemRepository</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.order.entity.repository;

import com.jeongbeom.ecommerce.order.entity.OrderItem;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderItemRepository extends JpaRepository&lt;OrderItem, Long&gt; {
}</code></pre>
<h2 id="productentity">ProductEntity</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.product.entity;

import com.jeongbeom.ecommerce.common.entity.BaseTimeEntity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)
public class Product extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; //PK

    @Column(nullable = false)
    private String name; //상품 이름

    @Column(nullable = false)
    private String description; //상품 상세 설명

    @Column(nullable = false)
    private int price;  //상품 가격

    @Column(nullable = false)
    private int stock;  //상품 재고 수량

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private ProductStatus status;  //상품 상태

    // 생성자
    public Product(String name, String description, int price, int stock, ProductStatus status) {
        this.name = name;
        this.description = description;
        this.price = price;
        this.stock = stock;
        this.status = status;
    }

    // 재고 감소
    public void decreaseStock(int quantity){
        if(stock &lt; quantity){
            throw new IllegalArgumentException(&quot;재고 부족&quot;);
        }
        this.stock -= quantity;
    }

    // 재고 증가
    public void increaseStock(int quantity){
        this.stock += quantity;
    }
}</code></pre>
<blockquote>
<ul>
<li>재고 감소 및 증가 로직을 Entity 내부에 포함시켜 비즈니스 로직이 도메인에 집중되도록 설계하였다. 이를 통해 도메인 중심 설계(Domain-driven design)를 적용하였다.</li>
</ul>
</blockquote>
<h2 id="productrepository">ProductRepository</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.product.entity.repository;

import com.jeongbeom.ecommerce.product.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository&lt;Product, Long&gt; {
}
</code></pre>
<h2 id="productstatus">ProductStatus</h2>
<pre><code class="language-java">package com.jeongbeom.ecommerce.product.entity;

public enum ProductStatus {
    ON_SALE, SOLD_OUT, HIDDEN
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[E-Commerce 프로젝트 ERD, 초기셋팅 ]]></title>
            <link>https://velog.io/@dev_sjb/E-Commerce-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-ERD-%EC%B4%88%EA%B8%B0%EC%85%8B%ED%8C%85</link>
            <guid>https://velog.io/@dev_sjb/E-Commerce-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-ERD-%EC%B4%88%EA%B8%B0%EC%85%8B%ED%8C%85</guid>
            <pubDate>Tue, 24 Mar 2026 05:44:33 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-초기-셋팅-erd-작성">프로젝트 초기 셋팅, ERD 작성</h1>
<h3 id="erd">ERD</h3>
<p><img src="https://velog.velcdn.com/images/dev_sjb/post/43b78a5e-e828-430a-9f01-5f0c47ab66d1/image.png" alt=""></p>
<h3 id="dependencies">Dependencies</h3>
<p><img src="https://velog.velcdn.com/images/dev_sjb/post/4644943e-cc08-4c13-9327-a2c4466a4df4/image.png" alt=""></p>
<ul>
<li><p>.gitignore에 # .env 파일 설정해주고, root에 .env파일 만들어서 민감한 부분 .env에 따로 넣어 관리해준다 (부트캠프 당시에 깃을 처음 써봐서 민감한 정보 다올렸던 기억이있다.)</p>
</li>
<li><p>git 레포에 연동 및 초기 셋팅 부분 세분화 Commit</p>
</li>
<li><p>develop 브렌치 생성 및 Git WorkFlow 작성 </p>
</li>
<li><p>Git 레포 주소 <a href="https://github.com/ShinJeongBeom/E-Commerce">https://github.com/ShinJeongBeom/E-Commerce</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[E-commerce 프로젝트]]></title>
            <link>https://velog.io/@dev_sjb/E-commerce-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
            <guid>https://velog.io/@dev_sjb/E-commerce-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</guid>
            <pubDate>Tue, 24 Mar 2026 05:22:28 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 데브코스 과정을 끝내고 개인 역량이 부족하다고 생각되고, 포트폴리오도 특출난 뭔가가 없는것 같아서 개인 프로젝트를 진행 하려고 한다. </p>
<h1 id="프로젝트-선택-이유">프로젝트 선택 이유</h1>
<ul>
<li>결제시스템 및 로그인 등 내가 맡아보지 못한 기능들이 다 들어가 있는것 같아서 선택하게 됬다. ( 이것 저것 추가하다보면 면접에서도 할말이 많아지지 않을까 싶다) </li>
<li>프론트도 취업 준비하는 지인을 꼬셔볼까(?) 했지만 요즘 뜨고있는 LLM MCP Claude 한테 프론트 부분만 맡겨볼까 생각중이다. </li>
</ul>
<h1 id="1-프로젝트-개요">1. 프로젝트 개요</h1>
<p>본 프로젝트는 Spring Boot 기반 이커머스 서비스를 구현하는 것을 목표로 한다.
사용자는 상품을 조회하고 장바구니에 담아 주문할 수 있으며, 관리자는 상품 및 주문을 관리할 수 있다.</p>
<hr>
<h1 id="2-기획-및-설계">2. 기획 및 설계</h1>
<p> 목표
    •    RESTful API 기반 이커머스 서비스 구현
    •    사용자 → 상품 → 장바구니 → 주문 흐름 설계
    •    확장 가능한 구조 설계 (MSA 고려 가능)</p>
<h3 id="핵심-기능-흐름">핵심 기능 흐름</h3>
<blockquote>
<pre><code>   회원가입 → 로그인 → 상품 조회 → 장바구니 담기 → 주문 → 결제(확장 예정)</code></pre></blockquote>
<h3 id="시스템-구조">시스템 구조</h3>
<pre><code>•    Backend: Spring Boot (Java)
•    DB: MariaDB
•    Cache: Redis
•    Infra: Docker
•    인증: JWT (예정)</code></pre><hr>
<h1 id="3-구현-기능">3. 구현 기능</h1>
<h3 id="👤-사용자-user">👤 사용자 (User)</h3>
<pre><code>•    회원가입
•    로그인 (예정)
•    사용자 정보 조회</code></pre><hr>
<h3 id="📦-상품-product">📦 상품 (Product)</h3>
<pre><code>•    상품 등록 (관리자)
•    상품 목록 조회
•    상품 상세 조회
•    카테고리 기반 조회</code></pre><hr>
<h3 id="🗂-카테고리-category">🗂 카테고리 (Category)</h3>
<pre><code>•    카테고리 생성
•    계층형 구조 (parent_id)</code></pre><hr>
<h3 id="🛒-장바구니-cart">🛒 장바구니 (Cart)</h3>
<pre><code>•    장바구니 생성
•    상품 담기
•    수량 변경
•    삭제
•    장바구니 조회</code></pre><hr>
<h3 id="📦-주문-order">📦 주문 (Order)</h3>
<pre><code>•    주문 생성
•    주문 상품 저장
•    총 금액 계산
•    주문 상태 관리</code></pre><hr>
<h3 id="🖼-상품-이미지-product-image">🖼 상품 이미지 (Product Image)</h3>
<pre><code>•    상품별 이미지 관리
•    썸네일 여부 관리
•    정렬 순서 관리</code></pre><hr>
<h1 id="api-명세서">API 명세서</h1>
<table>
<thead>
<tr>
<th>도메인</th>
<th>기능</th>
<th>Method</th>
<th>URL</th>
<th>Request</th>
<th>Response</th>
</tr>
</thead>
<tbody><tr>
<td>User</td>
<td>회원가입</td>
<td>POST</td>
<td>/api/users</td>
<td>email, password, name, phone</td>
<td>userId</td>
</tr>
<tr>
<td>User</td>
<td>사용자 조회</td>
<td>GET</td>
<td>/api/users/{userId}</td>
<td>-</td>
<td>user 정보</td>
</tr>
<tr>
<td>Product</td>
<td>상품 등록</td>
<td>POST</td>
<td>/api/products</td>
<td>name, price, stock, categoryId</td>
<td>productId</td>
</tr>
<tr>
<td>Product</td>
<td>상품 목록 조회</td>
<td>GET</td>
<td>/api/products</td>
<td>-</td>
<td>상품 리스트</td>
</tr>
<tr>
<td>Product</td>
<td>상품 상세 조회</td>
<td>GET</td>
<td>/api/products/{productId}</td>
<td>-</td>
<td>상품 상세</td>
</tr>
<tr>
<td>Cart</td>
<td>장바구니 담기</td>
<td>POST</td>
<td>/api/cart/items</td>
<td>productId, quantity</td>
<td>cartItemId</td>
</tr>
<tr>
<td>Cart</td>
<td>장바구니 조회</td>
<td>GET</td>
<td>/api/cart</td>
<td>-</td>
<td>장바구니 리스트</td>
</tr>
<tr>
<td>Cart</td>
<td>수량 변경</td>
<td>PUT</td>
<td>/api/cart/items/{cartItemId}</td>
<td>quantity</td>
<td>성공 여부</td>
</tr>
<tr>
<td>Cart</td>
<td>장바구니 삭제</td>
<td>DELETE</td>
<td>/api/cart/items/{cartItemId}</td>
<td>-</td>
<td>성공 여부</td>
</tr>
<tr>
<td>Order</td>
<td>주문 생성</td>
<td>POST</td>
<td>/api/orders</td>
<td>receiverName, receiverPhone, address</td>
<td>orderId</td>
</tr>
<tr>
<td>Order</td>
<td>주문 조회</td>
<td>GET</td>
<td>/api/orders/{orderId}</td>
<td>-</td>
<td>주문 상세</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring MVC 내부 구조 정리 (HandlerMapping, HandlerAdapter 이해하기)]]></title>
            <link>https://velog.io/@dev_sjb/Spring-MVC-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC-HandlerMapping-HandlerAdapter-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dev_sjb/Spring-MVC-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC-HandlerMapping-HandlerAdapter-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 17 Mar 2026 02:06:12 GMT</pubDate>
            <description><![CDATA[<p>프론트 컨트롤러 패턴을 직접 구현해보면서 공통 로직을 하나의 컨트롤러로 모을 수 있다는 것을 확인했다.
Spring MVC 역시 동일한 구조를 사용하며, 그 중심에는 DispatcherServlet이 존재한다.</p>
<p>이번 글에서는 Spring MVC가 요청을 처리하는 전체 구조와 핵심 컴포넌트들을 정리한다.</p>
<h2 id="1-dispatcherservlet">1. DispatcherServlet</h2>
<p>Spring MVC의 핵심은 DispatcherServlet이다.</p>
<blockquote>
<p>모든 HTTP 요청은 DispatcherServlet을 통해 처리된다.</p>
</blockquote>
<p>DispatcherServlet은 Front Controller 패턴을 기반으로 만들어진 컴포넌트이며 요청 처리 흐름 전체를 관리한다.</p>
<p>즉,
    •    요청을 받고
    •    컨트롤러를 찾고
    •    컨트롤러를 실행하고
    •    View를 찾아 응답을 반환하는</p>
<p>전체 과정을 조정하는 역할을 한다.</p>
<hr>
<h2 id="2-spring-mvc-전체-요청-흐름">2. Spring MVC 전체 요청 흐름</h2>
<p>Spring MVC에서 요청이 처리되는 전체 흐름은 다음과 같다.</p>
<pre><code class="language-text">Client
  │
  ▼
DispatcherServlet
  │
  ▼
HandlerMapping
  │
  ▼
HandlerAdapter
  │
  ▼
Controller
  │
  ▼
ModelAndView
  │
  ▼
ViewResolver
  │
  ▼
View
  │
  ▼
Response</code></pre>
<p>각 단계의 역할을 살펴보자.</p>
<hr>
<h2 id="3-handlermapping">3. HandlerMapping</h2>
<p><strong>정의</strong></p>
<blockquote>
<p>요청 URL에 맞는 컨트롤러를 찾아주는 역할</p>
</blockquote>
<p>클라이언트 요청이 들어오면 DispatcherServlet은 먼저 HandlerMapping을 통해 실행할 컨트롤러를 찾는다.</p>
<p>예를 들어 다음과 같은 컨트롤러가 있다고 가정하자.</p>
<pre><code class="language-java">@Controller
@RequestMapping(&quot;/members&quot;)
public class MemberController {

    @GetMapping
    public String members() {
        return &quot;members&quot;;
    }
}</code></pre>
<p>요청</p>
<pre><code>GET /members</code></pre><p>HandlerMapping은 @RequestMapping 정보를 기반으로</p>
<pre><code>MemberController.members()</code></pre><hr>
<h2 id="4-handleradapter">4. HandlerAdapter</h2>
<p>HandlerMapping이 컨트롤러를 찾았다고 해서 바로 실행할 수 있는 것은 아니다.</p>
<p>왜냐하면 컨트롤러의 형태가 다양할 수 있기 때문이다.</p>
<p>그래서 등장한 것이 HandlerAdapter이다.</p>
<p><strong>정의</strong></p>
<blockquote>
<p>찾아낸 컨트롤러를 실제로 실행해주는 역할</p>
</blockquote>
<p>HandlerAdapter는 다음 두 가지를 수행한다.</p>
<ol>
<li>해당 컨트롤러를 실행할 수 있는지 확인</li>
<li>컨트롤러 실행<pre><code>RequestMappingHandlerAdapter</code></pre></li>
</ol>
<hr>
<h2 id="5-controller-실행">5. Controller 실행</h2>
<p>HandlerAdapter를 통해 컨트롤러가 실행된다.</p>
<pre><code class="language-java">@GetMapping(&quot;/members&quot;)
public String members(Model model) {

    List&lt;Member&gt; members = memberRepository.findAll();
    model.addAttribute(&quot;members&quot;, members);

    return &quot;members&quot;;
}</code></pre>
<p>컨트롤러는 다음 두 가지 정보를 반환한다.</p>
<ol>
<li>View 이름</li>
<li>View에 전달할 데이터(Model)</li>
</ol>
<hr>
<h2 id="6-modelandview">6. ModelAndView</h2>
<p>컨트롤러의 반환값은 내부적으로 ModelAndView 형태로 변환된다.</p>
<p>ModelAndView는 다음 두 가지 정보를 가진 객체이다.</p>
<pre><code>ModelAndView
 ├ ViewName
 └ Model (데이터)</code></pre><p>즉</p>
<pre><code>어떤 화면(View)을 보여줄지
어떤 데이터(Model)를 전달할지를 함께 담고 있는 객체이다.</code></pre><hr>
<h2 id="7-viewresolver">7. ViewResolver</h2>
<p>컨트롤러는 실제 View 경로를 반환하지 않는다.</p>
<p>예</p>
<pre><code>return &quot;members&quot;;</code></pre><p>이것은 실제 경로가 아닌 <strong>논리적인 View 이름(Logical View Name)</strong>이다.</p>
<p>ViewResolver의 역할은</p>
<blockquote>
<p>논리적인 View 이름을 실제 View 경로로 변환하는 것이다.</p>
</blockquote>
<p>예</p>
<pre><code>members
↓
/WEB-INF/views/members.jsp</code></pre><p>대표 구현체</p>
<pre><code>InternalResourceViewResolver</code></pre><hr>
<h2 id="8-view">8. View</h2>
<p>View는 실제 화면을 생성하는 역할을 한다.</p>
<p>대표적인 View 기술</p>
<pre><code>JSP
Thymeleaf</code></pre><p>예를 들어 JSP에서는 다음과 같이 Model 데이터를 사용할 수 있다.</p>
<pre><code class="language-jsp">&lt;c:forEach var=&quot;member&quot; items=&quot;${members}&quot;&gt;
    &lt;tr&gt;
        &lt;td&gt;${member.username}&lt;/td&gt;
        &lt;td&gt;${member.age}&lt;/td&gt;
    &lt;/tr&gt;
&lt;/c:forEach&gt;</code></pre>
<p>View는 Model 데이터를 기반으로 HTML을 생성한다.</p>
<hr>
<h2 id="9-model-vs-modelandview-vs-modelmap">9. Model vs ModelAndView vs ModelMap</h2>
<p>Spring MVC에서 View에 데이터를 전달하는 방식은 여러 가지가 있다.</p>
<p>Model</p>
<p>가장 많이 사용하는 방식이다.</p>
<pre><code class="language-java">@GetMapping(&quot;/members&quot;)
public String members(Model model) {

    List&lt;Member&gt; members = memberRepository.findAll();
    model.addAttribute(&quot;members&quot;, members);

    return &quot;members&quot;;
}</code></pre>
<p>특징</p>
<pre><code>데이터는 Model에 저장
View 이름은 String으로 반환</code></pre><hr>
<p>*<em>ModelAndView
*</em>
Model과 View를 하나의 객체로 묶는 방식이다.</p>
<pre><code class="language-java">@GetMapping(&quot;/members&quot;)
public ModelAndView members() {

    List&lt;Member&gt; members = memberRepository.findAll();

    ModelAndView mv = new ModelAndView(&quot;members&quot;);
    mv.addObject(&quot;members&quot;, members);

    return mv;
}</code></pre>
<hr>
<p>ModelMap</p>
<p>Model과 비슷하지만 Map 구조로 동작한다.</p>
<pre><code class="language-java">@GetMapping(&quot;/members&quot;)
public String members(ModelMap model) {

    model.addAttribute(&quot;members&quot;, members);

    return &quot;members&quot;;
}</code></pre>
<hr>
<h2 id="10-spring-mvc의-요청-처리-흐름은-다음과-같다">10. Spring MVC의 요청 처리 흐름은 다음과 같다.</h2>
<pre><code>DispatcherServlet
 → HandlerMapping (컨트롤러 찾기)
 → HandlerAdapter (컨트롤러 실행)
 → Controller
 → ModelAndView
 → ViewResolver
 → View
 → Response</code></pre><p>각 구성 요소의 역할</p>
<pre><code>DispatcherServlet → 전체 흐름 제어
HandlerMapping → 실행할 컨트롤러 찾기
HandlerAdapter → 컨트롤러 실행
ViewResolver → View 경로 변환
Model → View에 전달할 데이터</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[서블릿(Servlet) 구조의 한계와 프론트 컨트롤러가 필요한 이유]]></title>
            <link>https://velog.io/@dev_sjb/%EC%84%9C%EB%B8%94%EB%A6%BFServlet-%EA%B5%AC%EC%A1%B0%EC%9D%98-%ED%95%9C%EA%B3%84%EC%99%80-%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@dev_sjb/%EC%84%9C%EB%B8%94%EB%A6%BFServlet-%EA%B5%AC%EC%A1%B0%EC%9D%98-%ED%95%9C%EA%B3%84%EC%99%80-%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Sun, 22 Feb 2026 15:21:28 GMT</pubDate>
            <description><![CDATA[<p>서블릿으로 MVC 구조를 직접 구현하다 보면
코드는 돌아가지만, 구조적으로 계속 찜찜한 지점들이 생긴다.
이번 글에서는 그중에서도 View 이동과 관련된 중복 문제를 중심으로
왜 프론트 컨트롤러가 등장했는지 정리해본다.</p>
<hr>
<ol>
<li>서블릿 MVC에서 발생하는 중복 문제</li>
</ol>
<p>① View로 이동하는 코드의 중복 (forward 중복)</p>
<p>컨트롤러 역할을 하는 모든 서블릿에서
JSP로 이동할 때마다 아래 코드가 반복된다.</p>
<pre><code class="language-java">RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);</code></pre>
<p>회원 목록, 회원 저장, 회원 폼 등
모든 서블릿에서 동일한 코드가 계속 등장한다.
    •    메서드로 분리할 수는 있음
    •    하지만 결국 매번 직접 호출해야 함
    •    컨트롤러의 핵심 로직이 아닌 코드가 계속 눈에 띔</p>
<p> -컨트롤러가 비즈니스 흐름 제어에만 집중하지 못한다.-</p>
<hr>
<p>② View 경로 문자열 중복 (ViewPath 중복)</p>
<pre><code class="language-java">String viewPath = &quot;/WEB-INF/views/new-form.jsp&quot;;</code></pre>
<p>여기에는 두 가지 문제가 숨어 있다.</p>
<p>✔ prefix / suffix가 하드코딩됨
    •    prefix: /WEB-INF/views/
    •    suffix: .jsp</p>
<p>모든 컨트롤러에서 이 문자열을 직접 작성해야 한다.</p>
<p>✔ View 기술 변경에 취약
만약 View를 JSP → Thymeleaf로 바꾸면?</p>
<pre><code class="language-java">// 기존
&quot;/WEB-INF/views/new-form.jsp&quot;

// 변경 후
&quot;/templates/new-form.html&quot;</code></pre>
<p> 모든 컨트롤러 코드를 수정해야 함</p>
<p>즉,
    •    컨트롤러가 View 기술(JSP, Thymeleaf)에 강하게 결합
    •    변경에 매우 취약한 구조</p>
<hr>
<ol start="2">
<li>문제의 본질: 책임이 흩어져 있다</li>
</ol>
<p>현재 구조에서는
    •    요청 분기
    •    비즈니스 로직 호출
    •    View 선택
    •    forward 처리</p>
<p>이 모든 책임이 각 서블릿마다 흩어져 있음,  컨트롤러가 너무 많은 일을 함.</p>
<ol start="3">
<li>해결 아이디어: 공통 로직을 한 곳으로 모으자</li>
</ol>
<p>여기서 자연스럽게 나오는 질문:</p>
<p>“이 공통 코드를 한 곳에서 처리할 수는 없을까?”</p>
<p>그래서 등장하는 개념이 바로 프론트 컨트롤러(Front Controller) 이다.</p>
<ol start="4">
<li>프론트 컨트롤러란?</li>
</ol>
<p>모든 요청을 단 하나의 컨트롤러가 먼저 받는 구조</p>
<pre><code class="language-text">클라이언트 요청
      ↓
[Front Controller]
      ↓
각 기능별 컨트롤러
      ↓
View</code></pre>
<hr>
<ol start="5">
<li><p>프론트 컨트롤러가 해결해주는 것들</p>
<p> 1) forward 로직을 한 곳에서 처리</p>
<pre><code class="language-java">RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);</code></pre>
<p>이 코드는 프론트 컨트롤러에 단 한 번만 존재한다.
개별 컨트롤러에서는
“어떤 뷰를 쓸지”만 반환하면 된다.</p>
</li>
</ol>
<hr>
<p>2) ViewPath 조립 책임 분리</p>
<p>개별 컨트롤러:</p>
<pre><code class="language-java">return &quot;new-form&quot;;</code></pre>
<p>프론트 컨트롤러:</p>
<pre><code class="language-java">String viewPath = &quot;/WEB-INF/views/&quot; + viewName + &quot;.jsp&quot;;</code></pre>
<p>prefix / suffix가 한 곳에만 존재
View 기술 변경 시 수정 포인트 단 하나</p>
<hr>
<p>3) 컨트롤러의 역할이 명확해짐</p>
<p>컨트롤러는 이제 딱 이것만 한다.
    •    요청 파라미터 처리
    •    비즈니스 로직 호출
    •    논리적인 View 이름 반환</p>
<pre><code class="language-text">Controller = 요청 처리 + 흐름 결정
View 처리 = Front Controller</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring - Servlet 기초 ]]></title>
            <link>https://velog.io/@dev_sjb/Spring-Servlet-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@dev_sjb/Spring-Servlet-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sat, 07 Feb 2026 16:16:42 GMT</pubDate>
            <description><![CDATA[<h1 id="서블릿servlet-기초-정리">서블릿(Servlet) 기초 정리</h1>
<h2 id="-http-요청-메시지-받는-방법-중심">— HTTP 요청 메시지 받는 방법 중심</h2>
<p>웹 애플리케이션에서 <strong>클라이언트의 요청(Request)을 서버가 어떻게 받는지</strong>는  
가장 기본이면서도 중요한 개념이다.</p>
<p>서블릿(Servlet)에서는 이 요청 정보를<br><code>HttpServletRequest</code> 객체를 통해 처리한다.</p>
<hr>
<h2 id="1-httpservletrequest란">1. HttpServletRequest란?</h2>
<p><code>HttpServletRequest</code>는 <strong>클라이언트가 서버로 보낸 HTTP 요청 정보를 담고 있는 객체</strong>이다.</p>
<p>이 객체를 통해 다음과 같은 정보를 읽을 수 있다.</p>
<ul>
<li>요청 방식 (GET, POST)</li>
<li>URL, URI</li>
<li>파라미터 값</li>
<li>HTTP 헤더</li>
<li>요청 바디(JSON 등)</li>
<li>세션 정보</li>
</ul>
<p>즉, <strong>사용자가 서버로 보낸 모든 정보의 집합</strong>이다.<br>(Spring에서는 이 객체를 추상화해 더 편하게 사용할 수 있다.)</p>
<hr>
<h2 id="2-요청-메시지를-받는-방법-3가지">2. 요청 메시지를 받는 방법 3가지</h2>
<p>서블릿(또는 Spring MVC)에서 요청 데이터를 받는 방식은<br>크게 <strong>3가지</strong>로 나눌 수 있다.</p>
<hr>
<h3 id="2-1-http-파라미터-방식-query-string--form-data">2-1. HTTP 파라미터 방식 (Query String / Form Data)</h3>
<h4 id="✔-사용-예">✔ 사용 예</h4>
<ul>
<li>GET 쿼리 스트링</li>
<li>POST <code>application/x-www-form-urlencoded</code></li>
</ul>
<pre><code class="language-text">/login?username=kim&amp;age=25
쿼리 파라미터에 정보를 전달하는 부분이다. 

``` java
String username = request.getParameter(&quot;username&quot;);
String age = request.getParameter(&quot;age&quot;);</code></pre>
<p>복수 파라미터</p>
<pre><code>String[] hobbies = request.getParameterValues(&quot;hobby&quot;);</code></pre><p> 특징
    •    항상 String 타입으로 전달됨
    •    단순 데이터 처리에 적합
    •    HTML form 전송 방식</p>
<hr>
<p>② HTTP 메시지 바디(JSON)</p>
<p>REST API에서 가장 많이 사용하는 방식이다.</p>
<p>요청예시 </p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;jeongbeom&quot;,
  &quot;age&quot;: 25
}</code></pre>
<p>서버 처리 코드</p>
<pre><code class="language-java">ObjectMapper objectMapper = new ObjectMapper();

UserDto user = objectMapper.readValue(
    request.getInputStream(),
    UserDto.class
);</code></pre>
<ul>
<li>JSON으로 파라미터 값을 읽으려면 ObjectMapper.readValue(변수명, 클래스이름)을 사용한다.</li>
</ul>
<p>특징
    •    객체 단위로 데이터 처리 가능
    •    타입 안정성 확보
    •    RESTful API에 적합</p>
<hr>
<p>③ Path Variable (경로 변수)</p>
<p>URL 경로 자체에 값을 포함하는 방식이다.</p>
<pre><code>GET /users/10</code></pre><pre><code class="language-java">@GetMapping(&quot;/users/{id}&quot;)
public String find(@PathVariable Long id) {
    return &quot;id = &quot; + id;
}</code></pre>
<p>특징
    •    리소스 식별에 적합
    •    URL 의미가 명확
    •    REST API 설계에 필수</p>
<hr>
<h2 id="3-요청-메시지-방식-한눈에-비교">3. 요청 메시지 방식 한눈에 비교</h2>
<table>
<thead>
<tr>
<th>방식</th>
<th>예시</th>
<th>사용 방법</th>
</tr>
</thead>
<tbody><tr>
<td>HTTP 파라미터</td>
<td><code>?name=kim</code></td>
<td><code>request.getParameter()</code></td>
</tr>
<tr>
<td>JSON 바디</td>
<td><code>{ &quot;name&quot;: &quot;kim&quot; }</code></td>
<td><code>ObjectMapper.readValue()</code></td>
</tr>
<tr>
<td>Path Variable</td>
<td><code>/users/10</code></td>
<td><code>@PathVariable</code></td>
</tr>
</tbody></table>
<hr>
<h2 id="4-request-vs-response">4. request vs response</h2>
<table>
<thead>
<tr>
<th>객체</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>HttpServletRequest</td>
<td>클라이언트 요청 읽기</td>
</tr>
<tr>
<td>HttpServletResponse</td>
<td>클라이언트 응답 보내기</td>
</tr>
</tbody></table>
<blockquote>
<p>요청 데이터는 무조건 <code>request</code>에서 읽는다</p>
</blockquote>
<hr>
<h2 id="5-정리">5. 정리</h2>
<ul>
<li>서블릿에서 요청은 <code>HttpServletRequest</code> 객체를 통해 처리한다</li>
<li>요청 메시지를 받는 방법은 크게 3가지가 있다<ol>
<li>HTTP 파라미터 방식</li>
<li>JSON 메시지 바디 방식</li>
<li>Path Variable 방식</li>
</ol>
</li>
<li>JSON 데이터는 <code>ObjectMapper.readValue()</code>를 사용해 객체로 변환한다</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[ 캐시(Cache)]]></title>
            <link>https://velog.io/@dev_sjb/%EC%BA%90%EC%8B%9CCache</link>
            <guid>https://velog.io/@dev_sjb/%EC%BA%90%EC%8B%9CCache</guid>
            <pubDate>Mon, 08 Dec 2025 15:28:27 GMT</pubDate>
            <description><![CDATA[<p>HTTP 캐시는 웹 성능 최적화의 핵심이다.
같은 리소스를 매번 서버에서 내려받지 않고, 클라이언트나 중간 프록시 서버가 저장한 데이터를 재사용하는 구조이다.</p>
<h2 id="1-캐시가-필요한-이유">1. 캐시가 필요한 이유</h2>
<pre><code>•    네트워크 비용 절약
•    서버 부하 감소
•    웹 페이지 로딩 시간 단축
•    사용자 경험 개선</code></pre><h2 id="2-cache-control-주요-옵션">2. Cache-Control 주요 옵션</h2>
<p><strong>1) max-age</strong></p>
<pre><code>Cache-Control: max-age=60</code></pre><p>•    캐시가 60초 동안 유효하다는 의미
•    유효 시간 동안은 네트워크 요청 없이 캐시 데이터를 바로 사용</p>
<p><strong>2) no-cache</strong></p>
<pre><code>Cache-Control: no-cache</code></pre><p>•    데이터를 캐시할 수는 있음
•    하지만 사용하기 전 반드시 원 서버에 검증(ETag)해야 함</p>
<p><strong>저장은 가능하지만 검증 없이 사용은 불가.</strong></p>
<p>*<em>3) no-store *</em></p>
<pre><code>Cache-Control: no-store</code></pre><p>•    아예 저장조차 하지 않음
•    보안성 높은 데이터(개인정보, 금융 데이터 등)에 필수</p>
<p><strong>4) Expires</strong></p>
<pre><code>Expires: Mon, 01 Jan 2025 00:00:00 GMT</code></pre><p>•    캐시 만료 시간(절대 시간) 지정
•    HTTP/1.1에서는 Cache-Control이 우선순위 더 높음</p>
<h2 id="3-last-modified--if-modified-since">3. Last-Modified / If-Modified-Since</h2>
<p><strong>Last-Modified</strong></p>
<pre><code>Last-Modified: 2024-12-01 00:00:00 GMT</code></pre><p>서버가 리소스가 마지막으로 변경된 시점을 알려줌.</p>
<p>If-Modified-Since</p>
<p>클라이언트가 다음 요청 때:</p>
<pre><code>If-Modified-Since: 2024-12-01 00:00:00 GMT</code></pre><p>서버가 변경된 내용이 없으면…</p>
<p>→ 304 Not Modified 응답
→ 캐시 데이터 재사용</p>
<h2 id="4-etag--if-none-match-더-정교한-캐싱">4. ETag / If-None-Match (더 정교한 캐싱)</h2>
<p>파일 해시 기반으로 변경 여부 판단.</p>
<p>서버:</p>
<pre><code>ETag: &quot;abc123&quot;</code></pre><p>클라이언트:</p>
<pre><code>If-None-Match: &quot;abc123&quot;
</code></pre><p>변경 없음 → 304 Not Modified</p>
<h2 id="5-프록시-캐시">5. 프록시 캐시</h2>
<p>•    브라우저 캐시 → private 캐시
•    프록시(중간 캐시 서버) → public 캐시</p>
<p>public 캐시 허용: </p>
<pre><code>Cache-Control: public</code></pre><p>개인별 캐시만 허용: </p>
<pre><code>Cache-Control: private</code></pre><p><strong>s-maxage</strong>
프록시 캐시만 별도 캐싱 시간 지정</p>
<pre><code>Cache-Control: s-maxage=120</code></pre><h2 id="6-캐시를-완전히-막고-싶을-때">6. 캐시를 완전히 막고 싶을 때</h2>
<p>로그인 정보나 중요한 페이지는 캐시되면 안 된다.</p>
<pre><code>Cache-Control: no-cache, no-store, must-revalidate</code></pre><h2 id="7-no-cache-동작-예시">7. no-cache 동작 예시</h2>
<pre><code>1.    클라이언트 요청 (no-cache + ETag 포함)
2.    캐시 서버는 원 서버에 검증 요청
3.    서버가 304 Not Modified 응답
4.    캐시 서버는 저장해둔 데이터 재사용</code></pre><p>검증 후 캐시 사용 구조이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠키(Cookie) ]]></title>
            <link>https://velog.io/@dev_sjb/%EC%BF%A0%ED%82%A4Cookie</link>
            <guid>https://velog.io/@dev_sjb/%EC%BF%A0%ED%82%A4Cookie</guid>
            <pubDate>Mon, 08 Dec 2025 15:06:56 GMT</pubDate>
            <description><![CDATA[<p>HTTP는 Stateless(무상태) 프로토콜이기 때문에 서버는 요청을 보낸 사용자가 누구인지 알 수 없습니다.
이를 해결하기 위해 가장 널리 사용되는 기술이 바로 <strong>쿠키(Cookie)</strong>입니다.</p>
<h2 id="1-쿠키란">1. 쿠키란?</h2>
<p>쿠키는 웹 브라우저가 저장하는 작은 데이터 조각이며, 브라우저는 매 요청마다 자동으로 해당 쿠키를 서버로 전송한다.</p>
<p>따라서 서버는 브라우저가 보내는 쿠키를 확인해 사용자를 식별할 수 있다.</p>
<h2 id="2-쿠키의-동작-방식">2. 쿠키의 동작 방식</h2>
<p><strong>1.    서버가 응답할 때 쿠키를 설정</strong></p>
<pre><code class="language-http">Set-Cookie: sessionId=adfasdf; Expires=Sat, 26 Dec 2025 00:00:00 GMT; Path=/; Domain=google.com; Secure</code></pre>
<p><strong>2.    브라우저는 쿠키를 저장한다.</strong>
<strong>3.    이후 해당 도메인의 요청은 항상 쿠키를 포함한다.</strong></p>
<pre><code class="language-http">Cookie: sessionId=adfasdf</code></pre>
<h2 id="3-쿠키의-주요-사용처">3. 쿠키의 주요 사용처</h2>
<pre><code>•    로그인 상태 유지(세션 ID 저장)
•    광고/트래킹 정보 저장
•    간단한 사용자 상태 관리
•    장바구니 데이터 유지 등</code></pre><h2 id="4-쿠키-저장-시-주의사항">4. 쿠키 저장 시 주의사항</h2>
<p>쿠키는 매우 중요한 특징이 있다:</p>
<p>브라우저는 모든 요청마다 쿠키를 서버에 자동 포함한다.</p>
<p>→ 결과적으로
    •    네트워크 트래픽 증가
    •    개인 정보 노출 위험 증가</p>
<p>따라서 민감한 정보는 쿠키에 절대 저장하면 안 된다.
(예: 비밀번호, 주민번호, 카드번호 등)</p>
<p>쿠키에는 최소한의 정보(PK, 세션 ID, 인증 토큰 등)만 저장해야 한다.</p>
<p>만약 서버로 보내지 않고 브라우저 내부에만 저장하고 싶다면
→ <strong>Web Storage(LocalStorage / SessionStorage)</strong>를 사용해야 한다.</p>
<h2 id="5-쿠키-속성-정리">5. 쿠키 속성 정리</h2>
<p><strong>Expires / Max-Age</strong>
    •    <strong>Expires:</strong> 절대 시간 (해당 시간이 되면 쿠키 삭제)
    •    <strong>Max-Age:</strong> 쿠키 유지 시간(초 단위)
    •    *<em>Max-Age=0 *</em>또는 음수 → 쿠키 삭제</p>
<p><strong>세션 쿠키(Session Cookie)</strong>
    •    Expires/Max-Age 생략
    •    브라우저 창을 닫을 때 삭제됨</p>
<p><strong>영속 쿠키(Persistent Cookie)</strong>
    •    Expires 또는 Max-Age 지정
    •    지정된 날짜까지 유지됨</p>
<p><strong>Domain</strong>
    •    쿠키가 적용되는 도메인 지정
    •    예:
Domain=google.com → google.com + 모든 서브도메인에서 사용 가능
    •    생략하면 현재 문서 도메인에서만 적용</p>
<p><strong>Path</strong>
    •    쿠키가 전송되는 경로
    •    기본적으로 Path=/ 로 설정하여 전체 경로에서 사용하도록 설정</p>
<p><strong>Secure</strong>
    •    HTTPS에서만 전송됨
    •    HTTP에서는 전송되지 않음</p>
<p><strong>HttpOnly</strong>
    •    JavaScript에서 쿠키 접근 불가
    •    XSS 공격 방지용</p>
<p><strong>SameSite</strong>
    •    CSRF 공격 방지용 정책
    •    Lax / Strict / None 설정 가능</p>
<h2 id="6-쿠키-요약">6. 쿠키 요약</h2>
<pre><code>•    서버가 Set-Cookie로 브라우저에 저장
•    브라우저는 모든 요청에 쿠키를 자동 포함
•    민감 정보 저장 금지
•    로그인/세션유지/트래킹 등에 사용
•    보안 속성(secure, httpOnly, sameSite)을 반드시 설정해야 함</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[M1 Mac(Homebrew)에서 MariaDB 삭제 후 MySQL 재설치 시 3306 포트 충돌 오류 해결 기록]]></title>
            <link>https://velog.io/@dev_sjb/M1-MacHomebrew%EC%97%90%EC%84%9C-MariaDB-%EC%82%AD%EC%A0%9C-%ED%9B%84-MySQL-%EC%9E%AC%EC%84%A4%EC%B9%98-%EC%8B%9C-3306-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@dev_sjb/M1-MacHomebrew%EC%97%90%EC%84%9C-MariaDB-%EC%82%AD%EC%A0%9C-%ED%9B%84-MySQL-%EC%9E%AC%EC%84%A4%EC%B9%98-%EC%8B%9C-3306-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Mon, 08 Dec 2025 14:51:36 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 데브코스에서는 MariaDB를 사용했지만, 최근 김영한 강사님의 강의를 공부하며 MySQL 환경을 사용해야 하는 상황이 생겼습니다.
MariaDB를 삭제하고 MySQL을 재설치하는 과정에서 3306 포트 충돌로 인해 MySQL이 실행되지 않는 문제가 발생했고, 이를 해결한 과정을 단계별로 정리했습니다.</p>
<h2 id="1-문제-발생">1. 문제 발생</h2>
<p>MariaDB 삭제 후 MySQL을 설치하고 실행했지만, 계속해서 MySQL 서버에 연결되지 않는 오류가 발생했습니다.</p>
<p>오류 내용</p>
<pre><code class="language-bash">Error: Can&#39;t connect to local MySQL server through socket &#39;/tmp/mysql.sock&#39; (2)</code></pre>
<p>MySQL 서버가 실행되지 않고, 소켓 파일(mysql.sock) 역시 생성되지 않는 상태였습니다.</p>
<h2 id="2-문제-확인-mysqlerr-로그-분석">2. 문제 확인 (mysql.err 로그 분석)</h2>
<p>단순 소켓 문제가 아닐 수도 있다고 판단해 MySQL 에러 로그를 확인했습니다.</p>
<p><strong>에러 로그 확인</strong></p>
<pre><code class="language-bash">cat /opt/homebrew/var/mysql/*.err</code></pre>
<p>로그를 확인해보니,
    •    Port 3306 is already in use
    •    Can&#39;t start server: Bind on TCP/IP port: Address already in use 와 같은 메시지가 포함되어 있었고,이미 삭제했다고 생각했던 MariaDB(mysqld) 데몬이 3306 포트를 점유 중이라는 사실을 알 수 있었습니다.</p>
<p>즉, MySQL이 실행되지 않은 원인은 소켓 자체 문제가 아니라
3306 포트가 MariaDB 프로세스에 의해 점유되어 있었기 때문이었습니다.</p>
<h2 id="3-문제-해결">3. 문제 해결</h2>
<h3 id="3-1-mariadb-프로세스-종료">3-1. MariaDB 프로세스 종료</h3>
<p>먼저 3306 포트를 점유하고 있는 MariaDB 프로세스를 강제로 종료했습니다</p>
<pre><code class="language-bash">kill -9 &lt;PID&gt;</code></pre>
<p>종료 확인:</p>
<pre><code class="language-bash">lsof -i :3306</code></pre>
<h3 id="3-2-mariadb-완전-삭제">3-2. MariaDB 완전 삭제</h3>
<p>MariaDB가 완전히 제거되도록 아래 순서로 처리했습니다.</p>
<p>1) 서비스 종료</p>
<pre><code class="language-bash">brew services stop mariadb
</code></pre>
<p>2) 패키지 삭제</p>
<pre><code class="language-bash">brew uninstall mariadb</code></pre>
<p>3) 남아 있는 데이터 디렉토리 삭제</p>
<pre><code class="language-bash">rm -rf /opt/homebrew/var/mysql</code></pre>
<p>이 디렉토리가 남아 있으면 MySQL이 소켓 파일을 생성하지 못하고 오류가 반복됩니다.</p>
<h3 id="3-3-mysql-재설치-및-서버-실행">3-3. MySQL 재설치 및 서버 실행</h3>
<p>MySQL 설치</p>
<pre><code class="language-bash">brew install mysql</code></pre>
<p>서버 실행 스크립트로 직접 구동</p>
<pre><code class="language-bash">cd /opt/homebrew/Cellar/mysql/*/support-files
./mysql.server start
</code></pre>
<p>이제 MySQL 서버가 정상적으로 실행되며 mysql.sock 파일도 생성되었습니다.</p>
<h3 id="3-4-mysql_secure_installation-정상-실행">3-4. mysql_secure_installation 정상 실행</h3>
<pre><code class="language-bash">mysql_secure_installation</code></pre>
<p>다음 화면이 뜨면 문제 없이 작동하는 것입니다.</p>
<pre><code class="language-bash">Securing the MySQL server deployment.

Connecting to MySQL using a blank password.</code></pre>
<p>이후 안내에 따라 비밀번호와 보안 설정을 완료했습니다.</p>
<h3 id="3-5-최종-접속-확인">3-5. 최종 접속 확인</h3>
<pre><code class="language-bash">mysql -u root -p</code></pre>
<p>정상적으로 접속되면 모든 과정이 성공적으로 완료된 것입니다.</p>
<p>이번 문제의 핵심은 삭제된 줄 알았던 MariaDB의 데몬 프로세스가 백그라운드에서 실행 중이며 3306 포트를 차지하고 있었던 점이었습니다.
에러 로그(mysql.err)를 직접 확인한 것이 문제 해결의 실마리가 되었습니다.</p>
<p>같은 환경(M1/M2 + Homebrew)에서 MySQL 환경을 구성하는 분들에게 도움이 되었으면 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 웹 기본 지식]]></title>
            <link>https://velog.io/@dev_sjb/HTTP-%EC%9B%B9-%EA%B8%B0%EB%B3%B8-%EC%A7%80%EC%8B%9D</link>
            <guid>https://velog.io/@dev_sjb/HTTP-%EC%9B%B9-%EA%B8%B0%EB%B3%B8-%EC%A7%80%EC%8B%9D</guid>
            <pubDate>Wed, 29 Oct 2025 03:29:18 GMT</pubDate>
            <description><![CDATA[<h1 id="🌐-클라이언트와-서버-통신의-기초-정리">🌐 클라이언트와 서버 통신의 기초 정리</h1>
<blockquote>
<p>백엔드 개발을 하다 보면 “클라이언트가 서버에 요청을 보낸다”라는 말을 매일 듣습니다.<br>그런데, 실제로 <strong>요청이 어떻게 전달되고</strong>, <strong>서버는 어떤 과정을 거쳐 응답을 돌려주는지</strong> 알아보겠습니다. </p>
</blockquote>
<p>이번 글에서는 제가 공부하면서 정리한<br><strong>클라이언트 ↔ 서버 통신의 핵심 개념</strong>을 정리해봤습니다.  </p>
<hr>
<h2 id="🧭-클라이언트---인터넷---서버">🧭 클라이언트 - 인터넷 - 서버</h2>
<p>모든 서버 개발의 출발점은 <strong>요청(Request)</strong> 과 <strong>응답(Response)</strong> 입니다.<br>클라이언트(브라우저)가 서버에 요청을 보내면,<br>인터넷을 거쳐 서버가 응답을 돌려주는 구조죠.</p>
<p>하지만 단순히 &quot;요청 → 응답&quot;만 있는 게 아닙니다.<br>그 뒤에는 IP, TCP, DNS 등 수많은 통신 계층이 존재합니다.</p>
<hr>
<h2 id="💡-ip-internet-protocol">💡 IP (Internet Protocol)</h2>
<p>IP는 인터넷 상의 <strong>주소 체계</strong>입니다.<br>사람에게는 주소가 있고, 컴퓨터에는 IP가 있습니다.</p>
<ul>
<li>예시: <code>192.168.0.1</code></li>
<li>데이터는 <strong>패킷(packet)</strong> 단위로 쪼개져 전송됩니다.</li>
<li>각 패킷은 목적지 IP를 보고 라우터를 통해 전달됩니다.</li>
</ul>
<p>단, IP만으로는 <strong>데이터가 제대로 도착했는지 보장할 수 없습니다.</strong><br>그래서 TCP나 UDP 같은 상위 프로토콜이 등장합니다.</p>
<hr>
<h2 id="⚙️-tcp--udp">⚙️ TCP / UDP</h2>
<h3 id="🔹-tcp-transmission-control-protocol">🔹 TCP (Transmission Control Protocol)</h3>
<blockquote>
<p>“정확하게” 데이터를 주고받는 <strong>연결형 프로토콜</strong></p>
</blockquote>
<p>TCP는 데이터 전송의 안정성을 보장합니다.<br>연결 전에는 다음과 같은 <code>3-way handshake</code> 과정을 거칩니다.</p>
<ol>
<li>클라이언트 → 서버 : <code>SYN</code> (연결 요청)  </li>
<li>서버 → 클라이언트 : <code>SYN + ACK</code> (요청 수락)  </li>
<li>클라이언트 → 서버 : <code>ACK</code> (확인 응답)</li>
</ol>
<p>이제 연결이 완료되면 안전하게 데이터를 전송할 수 있습니다.<br>로그인, 결제, 파일 다운로드 같은 <strong>정확성이 중요한 요청</strong>에서 주로 사용됩니다.</p>
<hr>
<h3 id="🔹-udp-user-datagram-protocol">🔹 UDP (User Datagram Protocol)</h3>
<blockquote>
<p>빠르지만 “확실하지 않은” 비연결형 프로토콜</p>
</blockquote>
<p>UDP는 TCP처럼 연결 과정을 거치지 않습니다.<br>그냥 <strong>“하얀 도화지에 던지는 느낌”</strong>으로 데이터를 전송하죠.</p>
<ul>
<li>연결 없이 빠름 🚀  </li>
<li>대신 손실 가능성 있음 ⚠️  </li>
<li>사용 예: 게임, 실시간 스트리밍, 영상 통화 등</li>
</ul>
<p>UDP는 데이터 전송 시 <strong>포트 번호 + 체크섬</strong> 정보만 포함합니다.</p>
<hr>
<h2 id="🌍-dns-domain-name-system">🌍 DNS (Domain Name System)</h2>
<p>사람은 <code>www.google.com</code>처럼 이름을 기억하지만,<br>컴퓨터는 <code>142.250.196.142</code> 같은 IP 주소로 통신합니다.<br>이 둘을 연결해주는 시스템이 바로 <strong>DNS</strong>입니다.</p>
<blockquote>
<p>예시 흐름:<br>클라이언트 → “google.com 요청”<br>DNS 서버 → “142.250.196.142” IP 응답<br>클라이언트 → 해당 IP로 실제 요청 전송</p>
</blockquote>
<hr>
<h2 id="🔐-http--https">🔐 HTTP / HTTPS</h2>
<p>HTTP는 <strong>웹에서 데이터를 주고받는 프로토콜</strong>입니다.<br>거의 모든 서버 통신은 HTTP를 기반으로 동작합니다.</p>
<ul>
<li><code>HTTP</code> : Hyper Text Transfer Protocol  </li>
<li><code>HTTPS</code> : HTTP + SSL/TLS 보안 계층 (암호화된 통신)</li>
</ul>
<p>즉, HTTPS는 <strong>보안이 강화된 HTTP</strong>라고 생각하면 됩니다.<br>요즘은 대부분 HTTPS를 기본으로 사용하죠.</p>
<hr>
<h2 id="🔄-상태-유지-vs-무상태-stateful-vs-stateless">🔄 상태 유지 vs 무상태 (Stateful vs Stateless)</h2>
<h3 id="🟢-stateful">🟢 Stateful</h3>
<p>서버가 클라이언트의 상태(로그인 등)를 기억하는 구조입니다.<br>예를 들어 로그인 후 &quot;내 정보&quot;를 조회할 때 같은 서버가 계속 나를 알아야 하죠.</p>
<ul>
<li>장점: 사용자의 상태 유지 가능 (세션, 쿠키)</li>
<li>단점: 같은 서버로 요청을 보내야 함 (확장성 낮음)</li>
</ul>
<h3 id="⚪-stateless">⚪ Stateless</h3>
<p>HTTP는 기본적으로 <strong>무상태(Stateless)</strong> 프로토콜입니다.<br>즉, 서버는 클라이언트의 상태를 기억하지 않습니다.</p>
<ul>
<li>장점: 서버 확장(Scale-out) 용이  </li>
<li>단점: 매 요청마다 인증정보를 다시 전달해야 함</li>
</ul>
<p>👉 그래서 JWT 같은 <strong>토큰 기반 인증</strong>이 자주 쓰입니다.</p>
<hr>
<h2 id="📩-http-메시지-구조">📩 HTTP 메시지 구조</h2>
<p>HTTP 통신은 <strong>메시지(Message)</strong> 기반으로 이루어집니다.</p>
<pre><code class="language-http"># 요청 메시지 예시
GET /members HTTP/1.1
Host: example.com
User-Agent: Chrome/127.0.0.1</code></pre>
<h1 id="응답-메시지-예시">응답 메시지 예시</h1>
<pre><code class="language-http">HTTP/1.1 200 OK
Content-Type: application/json

{
  &quot;id&quot;: 1,
  &quot;name&quot;: &quot;정범&quot;
}</code></pre>
<hr>
<h2 id="🚦-http-상태-코드-요약">🚦 HTTP 상태 코드 요약</h2>
<table>
<thead>
<tr>
<th>상태 코드</th>
<th>의미</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1xx (Informational)</strong></td>
<td>정보</td>
<td>요청이 수신되어 처리 중임을 의미</td>
</tr>
<tr>
<td><strong>2xx (Success)</strong></td>
<td>성공</td>
<td>클라이언트 요청이 성공적으로 수행됨</td>
</tr>
<tr>
<td><strong>3xx (Redirection)</strong></td>
<td>리다이렉션</td>
<td>요청을 완료하기 위해 추가 작업(다른 URI 요청 등)이 필요함</td>
</tr>
<tr>
<td><strong>4xx (Client Error)</strong></td>
<td>클라이언트 오류</td>
<td>잘못된 요청 (예: 잘못된 URL, 권한 없음 등)</td>
</tr>
<tr>
<td><strong>5xx (Server Error)</strong></td>
<td>서버 오류</td>
<td>서버에서 요청을 처리하지 못함</td>
</tr>
</tbody></table>
<h3 id="📘-주요-상태-코드-정리">📘 주요 상태 코드 정리</h3>
<h4 id="✅-200-ok">✅ 200 OK</h4>
<p>정상적으로 요청이 성공적으로 처리됨.<br>예시: <code>GET /members</code> 요청 시 회원 목록을 정상적으로 응답받았을 때.</p>
<h4 id="✅-201-created">✅ 201 Created</h4>
<p>요청이 성공적으로 처리되어 리소스가 생성됨.<br>예시: <code>POST /members</code> 요청 시 새로운 회원이 생성된 경우.</p>
<h4 id="⚙️-204-no-content">⚙️ 204 No Content</h4>
<p>요청이 성공했지만, 응답 바디가 없음.<br>예시: <code>DELETE /members/{id}</code> 요청 시 정상 삭제 후 응답 바디가 없을 때.</p>
<h4 id="🚀-301-moved-permanently">🚀 301 Moved Permanently</h4>
<p>요청한 리소스의 URI가 변경되었음을 의미.<br>브라우저는 이후 요청부터 새 URI로 자동 전환함.</p>
<h4 id="🔁-302-found">🔁 302 Found</h4>
<p>임시로 다른 URI로 요청을 보낼 것을 지시.<br>보통 로그인 후 리다이렉트 등에서 사용됨.</p>
<h4 id="🚫-400-bad-request">🚫 400 Bad Request</h4>
<p>클라이언트의 요청이 잘못됨.<br>예시: 필수 파라미터 누락, 잘못된 JSON 포맷 등.</p>
<h4 id="🔐-401-unauthorized">🔐 401 Unauthorized</h4>
<p>인증이 필요함.<br>로그인이나 토큰이 필요한 API 요청 시 인증 누락 시 발생.</p>
<h4 id="🧱-403-forbidden">🧱 403 Forbidden</h4>
<p>인증은 되었으나 접근 권한이 없음.<br>예시: 일반 사용자가 관리자 페이지에 접근하려는 경우.</p>
<h4 id="❓404-not-found">❓404 Not Found</h4>
<p>요청한 리소스를 찾을 수 없음.<br>예시: 존재하지 않는 회원 ID 요청 시.</p>
<h4 id="💣-500-internal-server-error">💣 500 Internal Server Error</h4>
<p>서버 내부 오류.<br>서버 로직, DB 문제 등 서버 측에서 발생한 예외 상황.</p>
<hr>
<h2 id="🧭-restful-api-설계-핵심-정리">🧭 RESTful API 설계 핵심 정리</h2>
<p>RESTful API를 설계할 때는 <strong>리소스 중심(Resource-Oriented)</strong> 으로 생각해야 합니다.<br>즉, <strong>“무엇을”</strong> 다루는지를 URI로 표현하고, <strong>“어떻게”</strong> 다루는지는 HTTP 메서드로 구분합니다.</p>
<p><img src="https://velog.velcdn.com/images/dev_sjb/post/cbbd445e-fd4e-40c0-b558-3655655dd56a/image.png" alt=""></p>
<h3 id="📂-uri-설계-규칙">📂 URI 설계 규칙</h3>
<table>
<thead>
<tr>
<th>행위</th>
<th>HTTP 메서드</th>
<th>URI 예시</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>조회</td>
<td><code>GET</code></td>
<td><code>/members</code></td>
<td>회원 목록 조회</td>
</tr>
<tr>
<td>단일 조회</td>
<td><code>GET</code></td>
<td><code>/members/{id}</code></td>
<td>특정 회원 조회</td>
</tr>
<tr>
<td>등록</td>
<td><code>POST</code></td>
<td><code>/members</code></td>
<td>회원 생성</td>
</tr>
<tr>
<td>수정(전체)</td>
<td><code>PUT</code></td>
<td><code>/members/{id}</code></td>
<td>회원 정보 전체 수정</td>
</tr>
<tr>
<td>수정(부분)</td>
<td><code>PATCH</code></td>
<td><code>/members/{id}</code></td>
<td>회원 정보 일부 수정</td>
</tr>
<tr>
<td>삭제</td>
<td><code>DELETE</code></td>
<td><code>/members/{id}</code></td>
<td>회원 삭제</td>
</tr>
</tbody></table>
<hr>
<h2 id="🧾-알아두면-좋은-uri-설계-개념">🧾 알아두면 좋은 URI 설계 개념</h2>
<p>RESTful 설계를 할 때 URI를 단순히 “주소”로만 보면 부족합니다.<br>리소스의 구조적 의미를 명확히 하기 위해 다음 네 가지 개념을 함께 이해해두면 좋습니다.</p>
<h3 id="📄-문서document">📄 문서(Document)</h3>
<ul>
<li><strong>단일 개념의 리소스</strong>를 의미합니다.<br>(파일 하나, 객체 인스턴스, 데이터베이스의 한 행 등)</li>
<li>주로 고유 ID를 통해 접근합니다.<br>예시:
/members/100
/files/star.jpg</li>
</ul>
<h3 id="📚-컬렉션collection">📚 컬렉션(Collection)</h3>
<ul>
<li><strong>서버가 관리하는 리소스의 디렉터리</strong>입니다.  </li>
<li>서버가 리소스의 URI를 생성하고 관리합니다.<br>(보통 <code>POST</code> 요청으로 새로운 리소스를 생성)</li>
<li>예시:<br>/members
/products</li>
</ul>
<h3 id="🏬-스토어store">🏬 스토어(Store)</h3>
<ul>
<li><strong>클라이언트가 관리하는 리소스 저장소</strong>입니다.  </li>
<li>클라이언트가 직접 URI를 지정하고 관리합니다.<br>(즉, 클라이언트가 리소스의 이름을 정함)</li>
<li>예시:<br>/files
/uploads</li>
</ul>
<h3 id="⚙️-컨트롤러controller-control-uri">⚙️ 컨트롤러(Controller, Control URI)</h3>
<ul>
<li>문서/컬렉션/스토어로 표현하기 어려운 <strong>추가 동작(프로세스)</strong> 을 수행할 때 사용합니다.  </li>
<li>이 경우 <strong>동사(verb)</strong> 를 URI에 직접 사용합니다.<br>예시:<br>/members/{id}/delete
/orders/{id}/cancel
/emails/send</li>
</ul>
<blockquote>
<p>💡 <strong>TIP:</strong><br>RESTful 설계에서는 기본적으로 “명사형 리소스”를 사용하지만,<br>명령형 프로세스가 필요한 경우엔 이렇게 <strong>컨트롤러 URI</strong> 형태로 확장합니다.</p>
</blockquote>
<hr>
<h3 id="🧩-멱등idempotent-개념">🧩 멱등(Idempotent) 개념</h3>
<blockquote>
<p>&quot;같은 요청을 여러 번 보내더라도 결과가 동일해야 한다&quot;</p>
</blockquote>
<table>
<thead>
<tr>
<th>메서드</th>
<th>멱등성</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>GET</code></td>
<td>✅</td>
<td>여러 번 조회해도 결과 동일</td>
</tr>
<tr>
<td><code>PUT</code></td>
<td>✅</td>
<td>동일한 데이터로 여러 번 수정해도 결과 동일</td>
</tr>
<tr>
<td><code>DELETE</code></td>
<td>✅</td>
<td>이미 삭제된 리소스에 다시 요청해도 결과 동일</td>
</tr>
<tr>
<td><code>POST</code></td>
<td>❌</td>
<td>호출할 때마다 새로운 리소스가 생성됨</td>
</tr>
</tbody></table>
<blockquote>
<p>💡 <strong>POST는 멱등하지 않음</strong> — 같은 요청을 반복하면 중복된 데이터가 생길 수 있음.</p>
</blockquote>
<hr>
<h2 id="💬-자바스크립트를-이용한-서버-통신-ajax">💬 자바스크립트를 이용한 서버 통신 (AJAX)</h2>
<h3 id="ajax란">AJAX란?</h3>
<blockquote>
<p><strong>Asynchronous JavaScript And XML</strong> 의 약자로, 페이지 전체를 새로고침하지 않고도 서버와 데이터를 주고받을 수 있게 해주는 기술입니다.</p>
</blockquote>
<pre><code class="language-javascript">fetch(&#39;/members&#39;)
  .then(response =&gt; response.json())
  .then(data =&gt; console.log(data))
  .catch(error =&gt; console.error(error));</code></pre>
<pre><code>•    fetch() 함수는 비동기 통신을 수행합니다.
•    서버로부터 JSON 형식의 응답을 받으면 .then()으로 처리합니다.
•    에러 발생 시 .catch()에서 처리합니다.</code></pre><p>✅ 장점: 페이지 새로고침 없이도 데이터 요청 및 갱신 가능
✅ 활용: 검색 자동완성, 댓글 등록, 좋아요 기능 등</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring]]></title>
            <link>https://velog.io/@dev_sjb/Spring</link>
            <guid>https://velog.io/@dev_sjb/Spring</guid>
            <pubDate>Mon, 11 Aug 2025 16:48:16 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev_sjb/post/325e2336-5320-4ad4-8add-c1ff9d324746/image.png" alt=""></p>
<p><strong>Spring Framework</strong>는 Java 플랫폼을 위한 <strong>오픈 소스 애플리케이션 프레임워크</strong>로, 개발자가 <strong>대규모 엔터프라이즈 애플리케이션</strong>을 효율적으로 개발할 수 있도록 다양한 기능을 제공합니다. <strong>IoC(Inversion of Control)와 DI(Dependency Injection)</strong> 같은 핵심 개념을 기반으로 하여, 복잡한 Java 애플리케이션의 설계와 구성을 간결하게 만들고, 유지보수성을 높이는 데 도움을 줍니다.</p>
<p>Spring은 <strong>웹 애플리케이션, 데이터베이스 연동, 보안, 배치 작업, 클라우드와의 연동</strong> 등 다양한 모듈을 제공하며, 특히 <strong>Spring Boot</strong>의 등장으로 빠르게 애플리케이션을 시작할 수 있는 표준이 되었습니다.</p>
<hr>
<h3 id="spring-framework-개요">Spring Framework 개요</h3>
<ol>
<li><strong>Spring Framework란?</strong><ul>
<li>Java 플랫폼을 위한 오픈 소스 경량 애플리케이션 프레임워크로, <strong>엔터프라이즈급 애플리케이션 개발</strong>을 더 쉽고 빠르게 할 수 있도록 돕습니다.</li>
<li>다양한 모듈을 통해 <strong>IoC (제어의 역전), DI (의존성 주입), AOP (관점 지향 프로그래밍), MVC (모델-뷰-컨트롤러)</strong> 패턴을 지원합니다.</li>
</ul>
</li>
<li><strong>Spring Framework의 주요 특징</strong><ul>
<li><strong>경량 컨테이너</strong>: 객체의 생성과 소멸을 포함한 생명 주기를 관리합니다.</li>
<li><strong>DI 지원</strong>: 외부에서 두 객체 간의 관계를 결정하여 결합도를 낮춥니다.</li>
<li><strong>AOP 지원</strong>: 비즈니스 로직과 공통 관심 사항(로깅, 보안 등)을 분리하여 재사용성을 높입니다.</li>
<li><strong>POJO 지원</strong>: 특정 인터페이스나 상속이 필요 없는 일반 자바 객체를 지원하여 유연성을 제공합니다.</li>
<li><strong>트랜잭션 관리</strong>: 일관된 방법으로 트랜잭션을 처리할 수 있습니다.</li>
<li><strong>데이터 영속성 지원</strong>: 다양한 데이터베이스 접근 기술(JPA, Hibernate, JDBC)과 쉽게 연동됩니다.</li>
<li><strong>API 연동</strong>: JMS, 메일, 스케줄링 등 엔터프라이즈 기능을 쉽게 통합합니다.</li>
</ul>
</li>
<li><strong>Spring MVC</strong><ul>
<li><strong>MVC 패턴</strong>을 기반으로 웹 애플리케이션을 설계할 수 있도록 지원합니다. 클라이언트의 요청을 <strong>Controller</strong>에서 받아 <strong>Model</strong>을 통해 비즈니스 로직을 처리하고, 최종 결과를 <strong>View</strong>로 출력합니다.</li>
</ul>
</li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/dev_sjb/post/57455e40-09b1-4644-947c-3316ee8ee714/image.png" alt=""></p>
<h3 id="spring의-핵심-개념">Spring의 핵심 개념</h3>
<ol>
<li><strong>IoC (Inversion of Control)</strong> - 제어의 역전<ul>
<li>IoC는 <strong>객체의 생성과 관리 권한을 개발자가 아닌 프레임워크가 맡는 것</strong>을 의미합니다. 이를 통해 개발자는 객체 간의 의존성을 직접 설정할 필요 없이, Spring이 이를 관리하도록 맡길 수 있습니다.</li>
<li>Spring에서는 주로 <strong>XML 설정 파일</strong>이나 <strong>애너테이션</strong>을 사용하여 객체의 생성과 의존성 주입을 제어합니다.</li>
</ul>
</li>
<li><strong>DI (Dependency Injection)</strong> - 의존성 주입<ul>
<li>DI는 객체가 다른 객체와의 의존성을 가지는 경우, <strong>필요한 의존성을 외부에서 주입받도록 하는 방식</strong>입니다. 이를 통해 객체 간의 결합도를 낮추고, 코드의 재사용성과 유지보수성을 높일 수 있습니다.</li>
<li>Spring은 <strong>필드 주입, 생성자 주입, 세터 주입</strong> 등 다양한 DI 방식을 지원합니다.</li>
</ul>
</li>
<li><strong>AOP (Aspect-Oriented Programming)</strong> - 관점 지향 프로그래밍<ul>
<li>AOP는 <strong>애플리케이션의 핵심 비즈니스 로직과 부가적인 기능(예: 로깅, 트랜잭션 관리)을 분리</strong>하는 데 사용됩니다.</li>
<li>Spring AOP를 통해 트랜잭션 관리나 보안 등의 기능을 코드에 직접 작성하지 않고, 특정 관심사(Aspect)로 분리하여 관리할 수 있습니다.</li>
</ul>
</li>
<li><strong>MVC (Model-View-Controller)</strong> 패턴<ul>
<li>Spring은 웹 애플리케이션을 구축하기 위해 <strong>Spring MVC</strong> 모듈을 제공합니다. 이는 <strong>모델, 뷰, 컨트롤러</strong>를 분리하여 클라이언트 요청을 효과적으로 처리하고 응답을 생성할 수 있도록 설계되었습니다.</li>
<li>Spring MVC를 사용하면 클라이언트 요청을 컨트롤러에서 받아, 비즈니스 로직을 처리한 후 적절한 뷰로 데이터를 전달하는 구조를 쉽게 구현할 수 있습니다.</li>
</ul>
</li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/dev_sjb/post/1a580a40-bc84-4822-8228-6736b4ab1b21/image.png" alt=""></p>
<h3 id="spring의-주요-모듈">Spring의 주요 모듈</h3>
<p>Spring은 다양한 모듈로 구성되어 있으며, 각 모듈은 특정 기능을 제공하여 <strong>전체 애플리케이션의 효율성과 생산성을 높입니다</strong>.</p>
<ol>
<li><strong>Spring Core</strong>:<ul>
<li>Spring의 기본적인 DI와 IoC 기능을 제공하는 모듈로, Spring Framework의 근간이 됩니다. 모든 Spring 애플리케이션은 이 Core 모듈을 사용하여 객체 생성과 의존성 관리를 수행합니다.</li>
</ul>
</li>
<li><strong>Spring AOP</strong>:<ul>
<li>AOP(Aspect-Oriented Programming) 기능을 제공하여, 비즈니스 로직에 영향을 주지 않고 로깅, 보안, 트랜잭션 관리와 같은 부가 기능을 구현할 수 있습니다.</li>
</ul>
</li>
<li><strong>Spring MVC</strong>:<ul>
<li>웹 애플리케이션을 위한 MVC 패턴을 구현한 모듈로, RESTful 웹 서비스 개발에도 많이 사용됩니다. 클라이언트의 요청을 컨트롤러가 처리하고, 필요한 뷰를 생성하는 구조를 지원합니다.</li>
</ul>
</li>
<li><strong>Spring Data</strong>:<ul>
<li>데이터베이스와의 연동을 쉽게 하기 위한 모듈로, JPA, MongoDB, Redis 등의 데이터베이스를 다루는 기능을 제공합니다. 개발자는 직접 쿼리를 작성하지 않고도 데이터를 CRUD(Create, Read, Update, Delete)할 수 있습니다.</li>
</ul>
</li>
<li><strong>Spring Security</strong>:<ul>
<li>인증과 인가 기능을 제공하는 보안 모듈로, 사용자 인증, 권한 관리, 접근 제어 등을 제공합니다. 이를 통해 안전한 애플리케이션을 구축할 수 있습니다.</li>
</ul>
</li>
<li><strong>Spring Boot</strong>:<ul>
<li>Spring Boot는 Spring Framework를 쉽게 시작할 수 있도록 돕는 모듈입니다. 자동 설정 기능을 제공하여, 최소한의 설정으로 애플리케이션을 빠르게 개발할 수 있습니다. 개발자가 더 이상 복잡한 XML 설정을 작성할 필요 없이, 기본 설정으로 시작할 수 있습니다.</li>
</ul>
</li>
<li><strong>Spring Cloud</strong>:<ul>
<li>클라우드 환경에서 마이크로서비스를 개발할 수 있도록 지원하는 모듈입니다. 마이크로서비스의 구성, 로드 밸런싱, 분산 트랜잭션 등을 관리하기 쉽게 합니다.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="spring을-사용한-애플리케이션-개발-흐름">Spring을 사용한 애플리케이션 개발 흐름</h3>
<ol>
<li><strong>객체 간의 의존성 설정 (IoC와 DI)</strong><ul>
<li>Spring을 통해 애플리케이션에 필요한 객체 간의 의존성을 설정합니다. XML 또는 애너테이션을 통해 객체 간의 관계를 정의하고, Spring이 이를 관리하게 합니다.</li>
</ul>
</li>
<li><strong>컨트롤러 작성 (Spring MVC)</strong><ul>
<li>Spring MVC를 사용하여 클라이언트 요청을 처리할 컨트롤러 클래스를 작성합니다. 각 컨트롤러는 URL 매핑을 통해 클라이언트의 요청을 특정 메서드로 연결하며, 요청을 처리한 후 적절한 뷰로 응답을 보냅니다.</li>
</ul>
</li>
<li><strong>비즈니스 로직 구현 (Service 계층)</strong><ul>
<li>비즈니스 로직을 서비스 계층에 작성하여, 컨트롤러와 분리합니다. 서비스 계층에서는 주로 DAO(Data Access Object)와 협력하여 데이터베이스와의 상호작용을 처리합니다.</li>
</ul>
</li>
<li><strong>데이터 처리 (Spring Data)</strong><ul>
<li>Spring Data JPA를 사용하여 데이터베이스와 상호작용합니다. Spring Data는 CRUD 작업을 추상화하여, 데이터베이스와의 작업을 더 쉽게 수행할 수 있도록 돕습니다.</li>
</ul>
</li>
<li><strong>보안 설정 (Spring Security)</strong><ul>
<li>Spring Security를 사용하여 애플리케이션의 보안을 설정합니다. 권한별 접근 제어를 설정하거나, 인증 절차를 구성하여 애플리케이션을 보호할 수 있습니다.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="spring의-장점">Spring의 장점</h3>
<ol>
<li><strong>의존성 관리의 용이성</strong>: IoC와 DI를 통해 객체 간의 결합도를 낮춰 유연하고 확장성 있는 애플리케이션을 개발할 수 있습니다.</li>
<li><strong>모듈화된 구조</strong>: Spring은 필요한 모듈만 선택적으로 사용하여, 애플리케이션에 필요한 기능만을 구현할 수 있게 합니다.</li>
<li><strong>확장성과 유지보수성</strong>: Spring은 구조가 일관되어 있어 유지보수가 쉽고, 모듈 확장이 용이하여 대규모 애플리케이션에 적합합니다.</li>
<li><strong>방대한 커뮤니티와 지원</strong>: Spring은 방대한 사용자 커뮤니티와 풍부한 문서가 있어 학습과 문제 해결이 비교적 쉽습니다.</li>
<li><strong>Spring Boot로 간편한 설정</strong>: Spring Boot는 자동 설정 기능을 통해 빠르게 애플리케이션을 개발할 수 있도록 지원하며, 특히 간단한 웹 애플리케이션 개발에 유리합니다.</li>
</ol>
<hr>
<h2 id="자료-및-링크">자료 및 링크</h2>
<p><a href="https://spring.io/">Spring</a> </p>
<p><a href="https://spring.io/projects/spring-framework">Spring Framework</a></p>
<p><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html">Spring Documentation - Core Concepts</a>
<a href="https://www.egovframe.go.kr/home/main.do">표준프레임워크 포털 eGovFrame</a></p>
]]></description>
        </item>
    </channel>
</rss>