<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Re_Go's Dev</title>
        <link>https://velog.io/</link>
        <description>인생은 본인의 삶을 곱씹어보는 R과 타인의 삶을 배워 나아가는 L의 연속이다.</description>
        <lastBuildDate>Sat, 05 Oct 2024 08:18:13 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Re_Go's Dev</title>
            <url>https://velog.velcdn.com/images/re_go/profile/bba4f435-fe07-40b7-92ad-66277892417e/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Re_Go's Dev. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/re_go" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[React] 기초 | 리액트에서의 버블링과 캡쳐링 방지법]]></title>
            <link>https://velog.io/@re_go/React-%EA%B8%B0%EC%B4%88-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C%EC%9D%98-%EB%B2%84%EB%B8%94%EB%A7%81%EA%B3%BC-%EC%BA%A1%EC%B3%90%EB%A7%81-%EB%B0%A9%EC%A7%80%EB%B2%95</link>
            <guid>https://velog.io/@re_go/React-%EA%B8%B0%EC%B4%88-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C%EC%9D%98-%EB%B2%84%EB%B8%94%EB%A7%81%EA%B3%BC-%EC%BA%A1%EC%B3%90%EB%A7%81-%EB%B0%A9%EC%A7%80%EB%B2%95</guid>
            <pubDate>Sat, 05 Oct 2024 08:18:13 GMT</pubDate>
            <description><![CDATA[<h1 id="1-버블링과-캡처링의-정의">1. 버블링과 캡처링의 정의</h1>
<p>우선 <code>버블링</code>부터 설명해 보자면, <code>버블링</code>은 <span style="color:olivedrab">DOM 트리에서 이벤트가 발생했을 때, 그 이벤트가 발생한 특정 요소에서부터 상위 부모 요소로 전파되는 방식</span>입니다. 즉, <span style="color:olivedrab">가장 구체적인 요소(이벤트가 발생한 요소)에서부터 시작하여 점점 더 넓은 범위(부모, 조상 요소 등)로 이벤트가 전파</span>되는 현상인데요</p>
<p>예를 들어, <span style="color:olivedrab">클릭 이벤트가 특정 버튼 요소에서 발생</span>하면, <span style="color:olivedrab">그 이벤트는 버튼의 부모 요소, 그 부모의 부모 요소로 차례대로 전파</span>되는데, 이때 <span style="color:olivedrab">부모 요소 중 이벤트 리스너를 등록 하고 있는 부모 요소는 해당 이벤트 리스너가 실행</span>되는 현상인거죠.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/c28bf232-dec0-4fb1-a899-31037e333eeb/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>자료 출처 : <a href="https://velog.io/@falling_star3/JS-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81bubbling%EA%B3%BC-%EC%BA%A1%EC%B2%98%EB%A7%81capturing-event.TargetcurrentTarget">https://velog.io/@falling_star3/JS-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81bubbling%EA%B3%BC-%EC%BA%A1%EC%B2%98%EB%A7%81capturing-event.TargetcurrentTarget</a></p>
<p>반대로 <code>캡처링</code>은 <span style="color:olivedrab">버블링과 반대로 부모 요소의 이벤트 발생이 하위 요소까지 전달되는 현상을 의미</span>하는데요. 이러한 이벤트 전파 방식은 리액트뿐만 아니라, <span style="color:olivedrab">DOM의 기본적인 이벤트 전파 메커니즘으로, 모든 브라우저에서 동작하는 자연스러운 현상</span>입니다.</p>
<blockquote>
</blockquote>
<p>이러한 이벤트 전파는 크게 </p>
<blockquote>
</blockquote>
<p>① 부모에서 타겟으로 내려가면서 등록된 이벤트가 발생되는 캡처 페이즈, 
② 타겟 자체에서 등록된 이벤트가 발생되는 타겟 페이즈, 
③ 다시 타겟에서 부모로 올라가면서 등록된 이벤트가 발생되는 버블링 페이즈</p>
<blockquote>
</blockquote>
<p>로 구성됩니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/59b0e245-592f-4afa-bb32-5430bb47d5a1/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>자료 출처 : <a href="https://velog.io/@falling_star3/JS-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81bubbling%EA%B3%BC-%EC%BA%A1%EC%B2%98%EB%A7%81capturing-event.TargetcurrentTarget">https://velog.io/@falling_star3/JS-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81bubbling%EA%B3%BC-%EC%BA%A1%EC%B2%98%EB%A7%81capturing-event.TargetcurrentTarget</a></p>
<p>그럼 카운터 앱 코드를 예시로 버블링과 캡쳐링이 어떻게 일어나는지를 아래의 코드를 잠깐 살펴볼까요?</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">import { useState } from &quot;react&quot;;
&gt;
function CounterApp() {
  const [count, setCount] = useState(0);
&gt;
  // 캡처링 단계에서 실행될 함수
  const handleParentClickCapture = () =&gt; {
    console.log(&quot;부모 캡처링 단계 발생 (count: &quot; + count + &quot;)&quot;);
  };
&gt;
  // 버블링 단계에서 실행될 함수
  const handleParentClick = () =&gt; {
    console.log(&quot;부모 버블링 단계 발생 (count: &quot; + count + &quot;)&quot;);
  };
&gt;
  // 증가 버튼 클릭 핸들러
  const handleIncrement = () =&gt; {
    setCount((prevCount) =&gt; prevCount + 1);
    console.log(&quot;증가 버튼 클릭: &quot; + count);
  };
&gt;
  // 감소 버튼 클릭 핸들러
  const handleDecrement = () =&gt; {
    setCount((prevCount) =&gt; prevCount - 1);
    console.log(&quot;감소 버튼 클릭: &quot; + count);
  };
&gt;
  return (
    &lt;div
      onClickCapture={handleParentClickCapture}  // 캡처링 단계에서 발생
      onClick={handleParentClick}                // 버블링 단계에서 발생
      style={{
        padding: &quot;50px&quot;,
        border: &quot;2px solid black&quot;,
        textAlign: &quot;center&quot;,
      }}
    &gt;
      &lt;h1&gt;Counter: {count}&lt;/h1&gt;
      &lt;div&gt;
        &lt;button
          onClick={handleIncrement}
          style={{ margin: &quot;10px&quot;, padding: &quot;10px&quot; }}
        &gt;
          Increment
        &lt;/button&gt;
        &lt;button
          onClick={handleDecrement}
          style={{ margin: &quot;10px&quot;, padding: &quot;10px&quot; }}
        &gt;
          Decrement
        &lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
&gt;
export default CounterApp;</code></pre>
<h2 id="--버블링과-캡처링-발생-과정-캡처링---버블링">- 버블링과 캡처링 발생 과정 (캡처링 -&gt; 버블링)</h2>
<ul>
<li><code>increment</code> 버튼을 누르면 최상위 부모 요소인 <code>&lt;div&gt;</code> 태그에 등록된 <code>onClickCapture</code> 속성(태그의 고유 제공 속성)에 등록된 함수인 <code>handleParentClickCapture</code> 가 발동</li>
<li>바로 하위 요소(눌려진 요소)에서 등록된 이벤트인 <code>handleIncrement</code> 실행</li>
<li>하위 요소에서 이벤트가 발생되었으므로 다시 상위로부터 버블링이 전달되어 부모 요소인 <code>&lt;div&gt;</code> 태그에 등록된 <code>onClick</code> 속성(버블링 현상이 일어날 때 발동되는 속성이기도 함)에 등록된 함수인 <code>handleParentClick</code> 가 발동</li>
</ul>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/92236757-efb7-4ed0-b96f-ee5c11e4f11a/image.png" alt=""></p>
<h1 id="2-버블링과-캡처링을-방지하기-위한-방법">2. 버블링과 캡처링을 방지하기 위한 방법</h1>
<p>이렇듯 버블링과 캡처링으로 인해 발생되는 전파 현상, 그러니까 개발자의 컨트롤에서 벗어나는 이벤트의 발생 현상을 막기 위해서는 타겟이 되는 요소에 <code>stopPropagation</code> 함수를 걸어주면 되는데요. 아래의 코드 예시를 살펴볼까요?</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">import { useState } from &quot;react&quot;;
&gt;
function CounterApp() {
  const [count, setCount] = useState(0);
&gt;
  // 캡처링 단계에서 실행될 함수
  const handleParentClickCapture = () =&gt; {
    console.log(&quot;부모 캡처링 단계 발생 (count: &quot; + count + &quot;)&quot;);
  };
&gt;
  // 버블링 단계에서 실행될 함수
  const handleParentClick = () =&gt; {
    console.log(&quot;부모 버블링 단계 발생 (count: &quot; + count + &quot;)&quot;);
  };
&gt;
  // 증가 버튼 클릭 핸들러 (이벤트 전파 중단)
  const handleIncrement = (event) =&gt; {
    event.stopPropagation(); // 이벤트 전파 중단 (버블링 차단)
    setCount((prevCount) =&gt; prevCount + 1);
    console.log(&quot;증가 버튼 클릭: &quot; + count);
  };
&gt;
  // 감소 버튼 클릭 핸들러 (이벤트 전파 중단)
  const handleDecrement = (event) =&gt; {
    event.stopPropagation(); // 이벤트 전파 중단 (버블링 차단)
    setCount((prevCount) =&gt; prevCount - 1);
    console.log(&quot;감소 버튼 클릭: &quot; + count);
  };
&gt;
  return (
    &lt;div
      onClickCapture={handleParentClickCapture} // 캡처링 단계에서 발생
      onClick={handleParentClick} // 버블링 단계에서 발생
      style={{
        padding: &quot;50px&quot;,
        border: &quot;2px solid black&quot;,
        textAlign: &quot;center&quot;,
      }}
    &gt;
      &lt;h1&gt;Counter: {count}&lt;/h1&gt;
      &lt;div&gt;
        &lt;button
          onClick={handleIncrement}
          style={{ margin: &quot;10px&quot;, padding: &quot;10px&quot; }}
        &gt;
          Increment
        &lt;/button&gt;
        &lt;button
          onClick={handleDecrement}
          style={{ margin: &quot;10px&quot;, padding: &quot;10px&quot; }}
        &gt;
          Decrement
        &lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
&gt;
export default CounterApp;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/7ce61329-bd35-4c19-8e4b-0052b2fa153c/image.png" alt=""></p>
<p>위 코드에서는 <span style="color:olivedrab">이벤트가 최초로 발생 되는 타겟 (DOM)의 이벤트에 <code>stopPropagation</code> 함수가 먼저 호출</span>이 되는 것을 확인할 수 있습니다. 이렇게 되면 <span style="color:olivedrab">최초 이벤트 발생 타겟을 중심으로 버블링이 차단</span>되기 때문에 부모로부터 자식에게 전파되는 캡처링과 타겟에서만 발생된 이벤트만 발생할 수 있습니다.</p>
<p>그러나 방금 말씀드린대로 위 코드에서 <span style="color:olivedrab">캡처링 속성은 설정</span>이 되어있기 때문에(<code>onClickCapture</code>) <span style="color:olivedrab">캡처링 전파는 발생</span>이 되는데요. <span style="color:olivedrab">현업에서는 굳이 <code>onClickCapture</code> 속성을 정의하지 않기 때문에 자주 사용되는 <code>onClick</code> 버튼에 등록된 이벤트 전파 발생 (버블링)에 대한 방지 방법만 알고 계셔도 충분</span>합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 기초 | 특정 조건에서의 컴포넌트 렌더링 방법]]></title>
            <link>https://velog.io/@re_go/React-%EA%B8%B0%EC%B4%88-%ED%8A%B9%EC%A0%95-%EC%A1%B0%EA%B1%B4%EC%97%90%EC%84%9C%EC%9D%98-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@re_go/React-%EA%B8%B0%EC%B4%88-%ED%8A%B9%EC%A0%95-%EC%A1%B0%EA%B1%B4%EC%97%90%EC%84%9C%EC%9D%98-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Fri, 04 Oct 2024 14:58:48 GMT</pubDate>
            <description><![CDATA[<h1 id="1-조건부-렌더링">1. 조건부 렌더링</h1>
<p>리액트에서는 기본적으로 <a href="https://velog.io/@re_go/React-2%EC%9E%A5-%EA%B8%B0%EC%A1%B4-DOM%EA%B3%BC-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%9D%98-%EA%B0%80%EC%83%81-DOM#2-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EA%B0%80%EC%83%81-%EB%8F%94%EC%9D%98-%EB%93%B1%EC%9E%A5">페이지의 특정 요소에 변화가 일어날 때 해당 부분만 렌더링</a>이 되어 사용자에게 보여지는데요. </p>
<p>이때 <span style="color:olivedrab">특정 상태나 상황에 따라 렌더링을 시켜 사용자에게 보여지게 하거나, 그 반대로 보여지지 않게 할 수</span> 있습니다. 이는 조건문과 같은 <code>조건부 연산자</code>를 활용하는 방법인데요. 아래에서 한번 그 방법들을 소개해 드리겠습니다.</p>
<h2 id="--if-else-문">- if-else 문</h2>
<p><span style="color:olivedrab">가장 기본적인 방법</span>으로, 조건에 따라 다른 컴포넌트를 렌더링하는 방법인데요. <span style="color:olivedrab">코드의 길이가 길어지기 때문에 잘 사용되지 않는 방법</span>이기도 합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">&gt;
function App() {
  const [count, setCount] = useState(0);
  const [input, setInput] = useState(&quot;&quot;);
&gt;
  const onClickButton = (value) =&gt; {
    setCount(count + value);
  };
&gt;
  // useState의 count 변수를 이용해 조건문을 이용해 반환할 DOM 요소를 결정 짓는 함수를 정의
  const renderMessage = () =&gt; {
    if (count &gt; 0) {
      return &lt;h2&gt;카운터가 증가했습니다! 현재 값: {count}&lt;/h2&gt;;
    } else if (count &lt; 0) {
      return &lt;h2&gt;카운터가 감소했습니다! 현재 값: {count}&lt;/h2&gt;;
    } else {
      return &lt;h2&gt;카운터가 0입니다.&lt;/h2&gt;;
    }
  };
&gt;
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;h1&gt;Simple Counter&lt;/h1&gt;
      &lt;section&gt;
        &lt;input 
          value={input} 
          onChange={(e) =&gt; setInput(e.target.value)} 
        /&gt;
      &lt;/section&gt;
      &lt;section&gt;
        &lt;Controller onClickButton={onClickButton} /&gt;
      &lt;/section&gt;
      {/* 
      페이지가 렌더링 될 때마다 count 변수의 변화에 따라  
      보여지는 DOM 요소도 함수 안에 정의된 각각의 DOM요소들과 
      같아질것임.
      */}
      {renderMessage()}
    &lt;/div&gt;
  );
}
&gt;
export default App;</code></pre>
<h2 id="--논리-연산자">- 논리 연산자</h2>
<p>자바스크립트의 논리 연산자를 활용하여 조건에 따라 컴포넌트를 렌더링할 수 있습니다. 예를 들어, <span style="color:olivedrab">&amp;&amp; 연산자의 <a href="https://velog.io/@re_go/5.-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%A2%85%EB%A5%98-%EB%B0%8F-%EA%B7%9C%EC%B9%99#2-%EB%85%BC%EB%A6%AC-%EC%97%B0%EC%82%B0%EC%9E%90">단락 평가의 특성을</a> 사용하면 조건이 true일 때만 컴포넌트를 렌더링</span>할 수 있는데요. </p>
<p>주로 <span style="color:olivedrab">특정 상태를 조건의 전면 코드로, 해당 특정 상태가 변화될 때마다 렌더링 될 DOM의 요소를 후면 코드로 배치</span>하여 사용되며, 주로 모달창, 온오프와 같은 기능에 많이 활용됩니다. (리액트에서 가장 많이 활용되는 방법이기도 합니다.)</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">function App() {
  const [count, setCount] = useState(0);
&gt;
  const onClickButton = (value) =&gt; {
    setCount(count + value);
  };
&gt;
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;h1&gt;Simple Counter&lt;/h1&gt;
      &lt;section&gt;
        &lt;Controller onClickButton={onClickButton} /&gt;
      &lt;/section&gt;
      {/* count가 0보다 클 때만 메시지를 표시 */}
      {count &gt; 0 &amp;&amp; &lt;h2&gt;카운터가 증가했습니다! 현재 값: {count}&lt;/h2&gt;}
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="--삼항-연산자">- 삼항 연산자</h2>
<p>삼항 연산자를 이용하여 특정 상태가 참의 결과, 또는 거짓의 결과를 보일 때 그에 준하는 DOM 요소를 반환하도록 하는 방법으로, <span style="color:olivedrab">렌더링 될 요소가 두 개 일 때 주로 활용</span>됩니다. </p>
<p>그래서 논리 연산자가 주로 온오프를 담당했다면, 삼항 연산자의 경우 <span style="color:olivedrab">두 개 중 하나의 컴포넌트를 상황에 따라 렌더링 할 때 주로 사용</span>합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">function App() {
  const [count, setCount] = useState(0);
&gt;
  const onClickButton = (value) =&gt; {
    setCount(count + value);
  };
&gt;
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;h1&gt;Simple Counter&lt;/h1&gt;
      &lt;section&gt;
        &lt;Controller onClickButton={onClickButton} /&gt;
      &lt;/section&gt;
      {/* 삼항 연산자를 사용하여 카운터 값에 따른 메시지 표시 */}
      &lt;h2&gt;{count &gt; 0 ? `카운터가 증가했습니다! 현재 값: ${count}` : `카운터가 ${count}입니다.`}&lt;/h2&gt;
    &lt;/div&gt;
  );
}
&gt;
export default App;</code></pre>
<h2 id="--switch-문">- switch 문</h2>
<p>2개 이상의 여러 조건이 필요한 경우 switch 문을 사용할 수 있습니다. if-else문과 사용법은 비슷하며, 리액트에서는 <span style="color:olivedrab">그리 많이 사용되지 않는 방법</span>입니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">function App() {
  const [count, setCount] = useState(0);
&gt;
  const onClickButton = (value) =&gt; {
    setCount(count + value);
  };
&gt;
  const renderMessage = () =&gt; {
    switch (true) {
      case count &gt; 0:
        return &lt;h2&gt;카운터가 증가했습니다! 현재 값: {count}&lt;/h2&gt;;
      case count &lt; 0:
        return &lt;h2&gt;카운터가 감소했습니다! 현재 값: {count}&lt;/h2&gt;;
      default:
        return &lt;h2&gt;카운터가 0입니다.&lt;/h2&gt;;
    }
  };
&gt;
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;h1&gt;Simple Counter&lt;/h1&gt;
      &lt;section&gt;
        &lt;Controller onClickButton={onClickButton} /&gt;
      &lt;/section&gt;
      {/* switch 문을 사용하여 메시지 렌더링 */}
      {renderMessage()}
    &lt;/div&gt;
  );
}</code></pre>
<h1 id="2-목록-렌더링">2. 목록 렌더링</h1>
<p>또한 리액트에서는 배열과 같은 반복적인 요소, 그러니까 <a href="https://velog.io/@re_go/Javascript-%EC%9A%94%EC%86%8C-%ED%99%9C%EC%9A%A9-%EC%B4%9D%EC%A0%95%EB%A6%AC-%EC%9D%B4%ED%84%B0%EB%9F%AC%EB%B8%94-%EC%8A%A4%ED%94%84%EB%A0%88%EB%93%9C-%EB%94%94%EC%8A%A4%ED%8A%B8%EB%9F%AD%EC%B2%98%EB%A7%81#1-%EC%9D%B4%ED%84%B0%EB%9F%AC%EB%B8%94%EC%9D%98-%EC%9D%98%EB%AF%B8">반복자(iterator)</a> 의 특성을 가진 요소를 활용해 <span style="color:olivedrab">요소가 가진 아이템들을 반복적으로 렌더링 하는 방법을 제공</span>하고 있는데요.</p>
<p>이는 주로 서버에서 데이터를 제공 받아 화면에 뿌려줄 때 개발자가 일일이 컴포넌트를 짜야만 하는 불편함을 많이 덜어줄 수 있는, <span style="color:olivedrab">리액트의 핵심 스킬</span>이라고도 할 수 있습니다.</p>
<p>이러한 방법을 사용하기 위해서는 대상이 되는 앞서 말씀드린대로 iteraotr 특성을 가져야 하기에, <span style="color:olivedrab">주로 배열 안에 하나 이상의 객체 정보를 뿌려줄 때 사용</span>됩니다.</p>
<h2 id="--map">- map</h2>
<p>리액트에서 <span style="color:olivedrab">요소의 반복 렌더링에 가장 많이 쓰이는 함수</span>입니다. 특히 map은 <span style="color:olivedrab">매 사이클마다 반환값이 있다는 점</span>에서 반환값이 없는 foreach보다 더 많이 사용되기에, return 키워드를 이용한 리액트 렌더링 방식 특성상 map이 리액트에서 가장 많이 활용되는 목록 렌더링 함수라고 할 수 있습니다.</p>
<p>물론 for문을 돌릴수도 있겠으나, 코드의 간결함과 가독성을 고려했을 때 for문은 거의 쓰이지 않는 방식이기도 하죠.</p>
<p>주의할 점은 map을 이용할때 생성되는, 최종적으로 정보가 표시(렌더링)되는 DOM 요소에는 <span style="color:olivedrab">key 속성을 필수로 지정해 주어야</span> 하며, 이때의 key 속성에는 각 아이템에 대한 <span style="color:olivedrab">고유한 id 값과 같은 변수들이 지정</span>됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">function UserList() {
  // 하드코딩된 사용자 데이터
  const users = [
    { id: 1, name: &#39;Alice&#39;, email: &#39;alice@example.com&#39; },
    { id: 2, name: &#39;Bob&#39;, email: &#39;bob@example.com&#39; },
    { id: 3, name: &#39;Charlie&#39;, email: &#39;charlie@example.com&#39; },
    { id: 4, name: &#39;David&#39;, email: &#39;david@example.com&#39; },
    { id: 5, name: &#39;Eve&#39;, email: &#39;eve@example.com&#39; },
  ];
&gt;
  return (
    &lt;div&gt;
      &lt;h1&gt;User List&lt;/h1&gt;
      &lt;ul&gt;
        {/* users 배열을 반복하여 렌더링 */}
        {users.map((user) =&gt; (
          &lt;li key={user.id}&gt;
            {user.name} - {user.email}
          &lt;/li&gt;
        ))}
      &lt;/ul&gt;
&gt;
      {/* 
      map을 사용하지 않을 경우 
      &lt;h1&gt;User List&lt;/h1&gt;
      &lt;ul&gt;
        &lt;li&gt;{users[0].name} - {users[0].email}&lt;/li&gt; {/* Alice */}
        &lt;li&gt;{users[1].name} - {users[1].email}&lt;/li&gt; {/* Bob */}
        &lt;li&gt;{users[2].name} - {users[2].email}&lt;/li&gt; {/* Charlie */}
        &lt;li&gt;{users[3].name} - {users[3].email}&lt;/li&gt; {/* David */}
        &lt;li&gt;{users[4].name} - {users[4].email}&lt;/li&gt; {/* Eve */}
      &lt;/ul&gt;
      */}
    &lt;/div&gt;
  );
}
&gt;</code></pre>
<h2 id="--filter">- filter</h2>
<p>주로 주어진 조건에 따라 배열의 요소를 필터링하여 새로운 배열을 생성하는 데 사용됩니다. 리액트에서 <code>filter</code>를 사용하여 <span style="color:olivedrab">특정 조건에 맞는 요소만 렌더링</span>할 수 있습니다. </p>
<p>특히 <code>filter</code>는 검색 정보와 같이 <span style="color:olivedrab">특정 키워드로 검색했을때 해당 키워드에 맞는 컴포넌트를 DOM 요소로 렌더링</span> 하여 사용자에게 보여주거나, 삭제할 특정 컴포넌트 정보를 제외한 나머지 컴포넌트의 정보를 저장하는 방식으로 <span style="color:olivedrab">특정 컴포넌트를 삭제하는 효과를 주고자 할 때 사용</span>됩니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-jsx">// 1. 검색 기능을 구현할 때 사용되는 filter 함수 예시
import React, { useState } from &#39;react&#39;;
&gt;&gt;
const users = [
  { id: 1, name: &#39;Alice&#39; },
  { id: 2, name: &#39;Bob&#39; },
  { id: 3, name: &#39;Charlie&#39; },
  { id: 4, name: &#39;David&#39; },
  { id: 5, name: &#39;Eve&#39; },
];
&gt;&gt;
const UserList = () =&gt; {
  const [searchTerm, setSearchTerm] = useState(&#39;&#39;);
&gt;&gt;
  // 사용자가 입력한 키워드로 필터링을 한 뒤 그 결과를 filteredUseres에 저장
  const filteredUsers = users.filter(user =&gt;
    user.name.toLowerCase().includes(searchTerm.toLowerCase())
  );
&gt;&gt;
  return (
    &lt;div&gt;
      &lt;h1&gt;User List&lt;/h1&gt;
      &lt;input
        type=&quot;text&quot;
        placeholder=&quot;Search by name&quot;
        value={searchTerm}
        {/*
        사용자가 input에 매번 타이핑을 칠때마다 setSearchTerm 메서드가 실행되어 상태를 searchTerm에 세팅
        */}
        onChange={(e) =&gt; setSearchTerm(e.target.value)}
      /&gt;
      &lt;ul&gt;
        {/*
        렌더링 될 때마다 searchTerm의 상태(사용자가 입력한 값)에 맞는 
        값들이 filteredUsers에 저장된 후 map 함수를 이용해 사용자에게 표시
        */}
        {filteredUsers.map(user =&gt; (
          &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;
        ))}
      &lt;/ul&gt;
    &lt;/div&gt;
  );
};</code></pre>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-jsx">// 2. 특정 컴포넌트를 삭제하는 예시
const usersData = [
  { id: 1, name: &#39;Alice&#39; },
  { id: 2, name: &#39;Bob&#39; },
  { id: 3, name: &#39;Charlie&#39; },
];
&gt;&gt;
const UserList = () =&gt; {
  const [users, setUsers] = useState(usersData);
&gt;&gt;
  const deleteUser = (id) =&gt; {
    // 클릭한 사용자 id를 제외한 나머지 사용자만 필터링하여 상태 업데이트
    const updatedUsers = users.filter(user =&gt; user.id !== id);
    setUsers(updatedUsers);
  };
&gt;&gt;
  return (
    &lt;div&gt;
      &lt;h1&gt;User List&lt;/h1&gt;
      &lt;ul&gt;
        {/*
        요소를 클릭하면 해당 요소에 저장되어 있는 함수가 발동
        그 해당 요소의 id값을 제외한 나머지 값만 updatedUsers에 저장된 후
        setUsers 함수를 이용해 users에 다시 저장한 뒤 users.map을 이용해 제 렌더링
        */}
        {users.map(user =&gt; (
          &lt;li key={user.id}&gt;
            {user.name} &lt;button onClick={() =&gt; deleteUser(user.id)}&gt;Delete&lt;/button&gt;
          &lt;/li&gt;
        ))}
      &lt;/ul&gt;
    &lt;/div&gt;
  );
};</code></pre>
<h2 id="--fragment">- Fragment</h2>
<p>Fragment는 조건부 렌더링 중 map을 이용한 함수 사용에서 반환하고자 하는 DOM의 요소가 두 개 이상일 때 해당 요소들을 그룹화 하는 용도로 사용됩니다. </p>
<p>물론 Fragment를 사용하지 않고 <code>&lt;div&gt;</code> 태그로도 묶어서 렌더링 할수도 있지만, <code>&lt;div&gt;</code> 태그가 여러 개일 때에는 코드의 가독성이 떨어질 수 있기에, 앞서 말씀드린 경우에는 Fragment를 사용합니다. (경우에 따라 축약형인 <code>&lt;&gt;&lt;/&gt;</code> 태그도 사용할 수 있습니다.)</p>
<blockquote>
</blockquote>
<pre><code class="language-jsx">import React, { Fragment, useState } from &#39;react&#39;;
&gt;
const users = [
  { id: 1, name: &#39;Alice&#39;, email: &#39;alice@example.com&#39; },
  { id: 2, name: &#39;Bob&#39;, email: &#39;bob@example.com&#39; },
  { id: 3, name: &#39;Charlie&#39;, email: &#39;charlie@example.com&#39; },
  { id: 4, name: &#39;David&#39;, email: &#39;david@example.com&#39; },
  { id: 5, name: &#39;Eve&#39;, email: &#39;eve@example.com&#39; },
];
&gt;
const UserList = () =&gt; {
  const [searchTerm, setSearchTerm] = useState(&#39;&#39;);
&gt;
  // 사용자가 입력한 키워드로 필터링
  const filteredUsers = users.filter(user =&gt;
    user.name.toLowerCase().includes(searchTerm.toLowerCase())
  );
&gt;
  return (
    &lt;&gt;
      &lt;h1&gt;User List&lt;/h1&gt;
      &lt;input
        type=&quot;text&quot;
        placeholder=&quot;Search by name&quot;
        value={searchTerm}
        onChange={(e) =&gt; setSearchTerm(e.target.value)}
      /&gt;
      &lt;ul&gt;
        {filteredUsers.length &gt; 0 ? (
          // 필터링된 결과가 있을 때
          filteredUsers.map(user =&gt; (
            {/* 추가 정보를 표시하기 위해 Fragment를 사용 */}
            &lt;Fragment key={user.id}&gt;
              &lt;li&gt;{user.name}&lt;/li&gt;
              {/* 
              user.id가 1이 아닌 경우만 정보가 표시되는데, 
              그 중에서도 user.email이 있는 경우는 그 경우를,
              없는 경우 &#39;No email available&#39;을 렌더링 하게 표시
              */}
              {user.id !== 1 &amp;&amp; &lt;p&gt;Email: {user.email ? user.email : &#39;No email available&#39;}&lt;/p&gt;}
            &lt;/Fragment&gt;
          ))
        ) : (
          // 필터링된 결과가 없을 때
          &lt;li&gt;No users found&lt;/li&gt;
        )}
      &lt;/ul&gt;
    &lt;/&gt;
  );
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] DTO와 Record]]></title>
            <link>https://velog.io/@re_go/JAVA-DTO%EC%99%80-Record</link>
            <guid>https://velog.io/@re_go/JAVA-DTO%EC%99%80-Record</guid>
            <pubDate>Wed, 02 Oct 2024 19:12:49 GMT</pubDate>
            <description><![CDATA[<h1 id="1-dto란">1. DTO란?</h1>
<p>자바에서는 데이터 전송을 위해 DTO(Data Transfer Object)라는 개념을 가지고 있는데요. 이는 <span style="color:olivedrab">다양한 계층 간 데이터 전달의 효율성을 목적으로 극대화 하기 위해 사용되는 클래스 틀을 의미</span>하는데요. </p>
<p>물론 자바에는 DTO만 있는건 아니고, VO(Value Object), DAO(Data Access Object), Entity와 같이 특정 목표를 위해만들어진 그릇? 같은 객체가 존재하는데요. </p>
<p>지금 당장은 이러한 DTO가 와닿지 않지만, 후에 <span style="color:olivedrab">MVC 모델을 이용한 웹 어플리케이션을 개발할 때 DTO 개념을 사용</span>하기 때문에 지금은 개념 정도로만 알아두시는 것을 추천합니다.</p>
<p><span style="color:olivedrab">보안 및 매핑 목적으로 자주 사용</span>되기도 하는 이 DTO의 골자는 다음 코드와 같습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">public class MemberDTO {
    // 상수 필드 선언
    private final String name;
    private final int age;
&gt;    
    // 생성자 생성
    public MemberDTO(String name, int age) {
        this.name = name;
        this.age = age;
    }
&gt;
    //getter 메서드 설정 (setter는 보안 목적으로 사용 x)
    public String getName() {
        return name;
    }
&gt;
    public int getAge() {
        return age;
    }
&gt;
    // 상속받은 Object 클래스의 toString 메서드 재정의
    @Override
    public String toString() {
        return &quot;MemberDTO{name=&#39;&quot; + name + &quot;&#39;, age=&quot; + age + &quot;}&quot;;
    }
&gt;
    // 상속받은 Object 클래스의 hashCode 메서드 재정의
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
&gt;
    // 상속받은 Object 클래스의 equals 메서드 재정의
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        MemberDTO memberDTO = (MemberDTO) obj;
        return age == memberDTO.age &amp;&amp; Objects.equals(name, memberDTO.name);
    }
&gt;
    public static void main(String[] args) {
        MemberDTO member1 = new MemberDTO(&quot;Re_Go&quot;, 30);
        MemberDTO member2 = new MemberDTO(&quot;Re-Go&quot;, 29);
&gt;
        // getter 메서드 호출
        System.out.println(member1.getName());
        System.out.println(member1.getAge());
        // toString 메서드 호출
        System.out.println(member1.toString());
        // hashCode 메서드 호출
        System.out.println(member1.hashCode());
        // equals 메서드 호출
        System.out.println(member1.equals(member2));
    }
}</code></pre>
<h1 id="2-record-키워드">2. Record 키워드</h1>
<p>특히 <span style="color:olivedrab">이러한 코드를 반복적으로 작성하는것은 비효율적</span>이라는 것인데, 이를 지원하기 위해 Java 14부터 <code>record</code> 객체가 추가되었는데(물론 잘 사용은 안됨),</p>
<p>위에서 살펴본 DTO를 자동으로 추가해준다는 장점을 가지고 있어 매우 유용하게 사용되는 객체중 하나입니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// Member 레코드 생성
public record Member(String name, int age) {
&gt; 
    public static void main(String[] args) {
        Member member1 = new Member(&quot;Re_Go&quot;, 30);
        Member member2 = new Member(&quot;Re-Go&quot;, 29);
&gt;        
        // getter 메서드 호출 (접두사를 get으로 쓰지 않고 멤버 이름과 같게 자동 생성됩니다.)
        System.out.println(member1.name); 
        System.out.println(member1.age);
        // toString 메서드 호출
        System.out.println(member1.toString());
        // hashCode 메서드 호출
        System.out.println(member1.hashCode());
        // equals 메서드 호출
        System.out.println(member1.equals(member2));
    }
}</code></pre>
<p>참고로 이 record 객체와 비슷한 지원을 해주는 롬복(Lombok) 이라는 커스텀 라이브러리가 있는데요. 해당 내용은 <a href="https://hstory0208.tistory.com/entry/Spring-Lombok%EB%A1%AC%EB%B3%B5%EC%9D%B4%EB%9E%80-%EC%84%A4%EC%B9%98%EB%B6%80%ED%84%B0-%EC%82%AC%EC%9A%A9%EB%B2%95%EA%B9%8C%EC%A7%80">이곳</a>을 참조하시기 바립니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP/Servlet] JSP Model2 (MVC) 방식을 활용한 회사 사원 조회 홈페이지 제작기 | With Mybatis]]></title>
            <link>https://velog.io/@re_go/JSPServlet-JSP-Model2-MVC-%EB%B0%A9%EC%8B%9D%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%9A%8C%EC%82%AC-%EC%82%AC%EC%9B%90-%EC%A1%B0%ED%9A%8C-%ED%99%88%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%A0%9C%EC%9E%91%EA%B8%B0-With-Mybatis</link>
            <guid>https://velog.io/@re_go/JSPServlet-JSP-Model2-MVC-%EB%B0%A9%EC%8B%9D%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%9A%8C%EC%82%AC-%EC%82%AC%EC%9B%90-%EC%A1%B0%ED%9A%8C-%ED%99%88%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%A0%9C%EC%9E%91%EA%B8%B0-With-Mybatis</guid>
            <pubDate>Fri, 16 Aug 2024 04:48:19 GMT</pubDate>
            <description><![CDATA[<h1 id="1-서론">1. 서론</h1>
<p>이번에 학원에서 마이바티스를 활용한 MVC 패턴을 한 번 짜봤는데요. 이전에 스프링 부트로 간이 프로젝트를 할 때는 몰랐는데, 막상 해당 시간을 가지다보니 &quot;이래서 근본을 아는게 중요하구나&quot; 라는 생각이 막 꽂혀서 되게 의미있는 시간이었다고 생각합니다. </p>
<p>물론 해당 시간에 제가 급하게 병원에 입원을 하는 바람에 코드의 나머지를 병원에서 작성해야만 했지만, 그래도 해당 패턴을 하나 하나 뜯어보고나니 &quot;아... 되게 심오하면서 재밌다 ㅋㅋ&quot; 라는 생각을 지울 수는 없더군요. </p>
<p>아무튼 이번 프로젝트에 사용된 프로젝트 구조인 MVC패턴을 잠깐 소개해 드리자면</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/f46e7539-42aa-4756-9a85-572d0dd188ab/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://developer.mozilla.org/ko/docs/Glossary/MVC">https://developer.mozilla.org/ko/docs/Glossary/MVC</a>)</p>
<blockquote>
</blockquote>
<ol>
<li>사용자의 요청을 VIEW가 받아서 Controller에 전송</li>
<li>Controller는 요청을 정제화 해서 Model에 전송</li>
<li>Model은 전송 받은 요청을 이용해 DB를 업데이트 한 뒤 그 결과를 VIEW에 전달</li>
<li>VIEW는 최종적으로 전달 받은 정보를 화면에 업데이트</li>
</ol>
<p>그리고 위 로직을 좀 더 자세히 설명드리자면 아래의 그림, 설명과 같습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/642af1b0-a133-4e1d-a648-99b2d1bcb9e7/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://hyuntaekhong.github.io/til/TIL-200824/">https://hyuntaekhong.github.io/til/TIL-200824/</a>)</p>
<blockquote>
</blockquote>
<ol>
<li>클라이언트의 요청이 Controller로 제출됨 (submit)</li>
<li>Controller는 요청에 해당하는 Service 객체에 요청</li>
<li>Service 객체는 DAO를 호출하여 DB 데이터를 전달 받도록 요청</li>
<li>DAO객체는 Mybatis를 이용하는 Mapper.xml을 이용해 작업을 수행한 뒤 결과를 반환</li>
<li>데이터는 DAO - Service - Controller로 역순으로 전달</li>
<li>Controller는 요청에 맞는 JSP로 응답</li>
<li>JSP 페이지는 응답 정보를 사용자에게 보여줌</li>
</ol>
<p>여기서 의문이 들었던건 왜 Service를 거쳐 DAO로 가는지? 그냥 Service에서 다 해도 되지 않는지에 대한 의문이었는데요.</p>
<p>도달한 결론만 좀 간추려 정리하자면 DAO는 DB와의 상호작용에 집중하고, Service는 데이터 가공과 같이 특정 비즈니스 로직에 좀 더 집중을 하는 역할을 수행하기 위해 모듈화 했다고 생각이 됩니다. (물론 첫 시간이라 DAO 쪽에서 데이터를 정제하고 있긴 하지만 ㅎ...)</p>
<h1 id="2-프로젝트-파일-구성">2. 프로젝트 파일 구성</h1>
<p>우선 JSP 페이지는 다섯 페이지로 구성이 되어있으며, 라이브러리는 ojdbc와 마이바티스, taglib(아파치 태그 lib을 사용하기 위함. c만 임포트 해도 됨)를 폴더와 빌드 패스에 추가해 줬습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/799d00f9-9f27-4385-87ae-e94756235632/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/cf19aa53-c889-46eb-b544-f86fe0f4f89f/image.png" alt=""></p>
<p>그 다음 자바 파일의 각 패키지에 Controller, VO, Service, DAO 및 마이바티스 환경 설정을 위한 xml 파일과 매퍼 xml 파일, 커넥션 풀 관리 파일을 다음과 같이 만들어 줬습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/3165a282-8259-463a-9f9e-ab667cd54f8b/image.png" alt=""></p>
<h1 id="3-추가-개념글">3. 추가 개념글</h1>
<p>이번 섹션에서는 마이바티스를 활용했는데요. 여기서 마이바티스란 자바 앱에서 DB와의 상호작용을 효율적으로 처리하게 해주는 프레임워크로, 그냥 편하게 JPA와 같은 역할을 하는 친구라고 생각하시면 되는데요.</p>
<p>마이바티스에 대한 개념글 정리를 굉장히 잘한 블로그가 있어서 아래의 블로그를 먼저 한 번 훑어보시고 난 뒤 해당 프로젝트의 설명을 참고하시면 좀 더 이해가 되시지 않을까 하여 소개를 드리고자 합니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/242724c0-1ba2-485a-a8fe-75b1521af1db/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(<a href="https://khj93.tistory.com/entry/MyBatis-MyBatis%EB%9E%80-%EA%B0%9C%EB%85%90-%EB%B0%8F-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC">https://khj93.tistory.com/entry/MyBatis-MyBatis%EB%9E%80-%EA%B0%9C%EB%85%90-%EB%B0%8F-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC</a>)</p>
<h1 id="4-오라클-db-테이블-구성">4. 오라클 DB 테이블 구성</h1>
<p>오라클 DB의 경우 오라클에서 기본적으로 제공 해주는 HR 계정의 employees 테이블을 사용했는데요. 해당 테이블 사용을 위해서는 다음 명령어와 같이 언락을 해주어야 사용이 가능합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-sql">ALTER USER hr IDENTIFIED BY 1234 ACCOUNT UNLOCK;</code></pre>
<p>해당 계정을 언락 해주고 접속 계정을 다음과 같이 만들어 줬습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/c31ba31a-41ce-44b5-998f-e26d88c97217/image.png" alt=""></p>
<p>그리고 해당 계정으로 접속하여 employees 테이블을 조회하면 다음과 같이 구조를 확인할 수 있죠.</p>
<blockquote>
</blockquote>
<pre><code class="language-sql">select * from employees;
desc employees;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/aaf3ecff-6057-47cd-81b1-341baaf65a2d/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/f8ee5493-5efe-4839-ae7e-654437889498/image.png" alt=""></p>
<h1 id="5-전체-로직-설명">5. 전체 로직 설명</h1>
<p>그럼 다음 파일들의 로직에 대해서 설명해 드리고자 하는데요. 제가 현재 병원에 있는 상태로 (마취가 덜 풀렸 ㅇ...) 주석으로 설명을 대체하고자 합니다. </p>
<p>그래도 최소한 주석을 달려고 노력을 했으니 가엾게 봐주시면 감사하겠습니다 ㅜㅜ</p>
<h2 id="jsp-영역">JSP 영역</h2>
<h3 id="index">index</h3>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;style type=&quot;text/css&quot;&gt;
    div{
        width: 800px;
        text-align: center;
        margin: auto;
    }
    table{
        width: 100%;
        border-collapse: collapse;
    }
    th, td{
        border: 1px solid gray;
    }
    th{
        padding: 5px;
        background-color: darkslateblue;
        color: white;
    }
&lt;/style&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
    function search1(){
        location.href=&#39;/chapter16_search/Controller?cmd=allList&#39;;
    }
    function search2(){
        location.href=&#39;/chapter16_search/Controller?cmd=inputDept&#39;;
    }
    function search3(){
        location.href=&#39;/chapter16_search/Controller?cmd=inputDynamic&#39;;
    }
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h1&gt;원하는 검색 버튼 클릭&lt;/h1&gt;
        &lt;form&gt;
            &lt;table&gt;
                &lt;tr&gt;
                    &lt;th&gt;검색 버튼을 클릭하세요.&lt;/th&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;
                        &lt;input type=&quot;button&quot; value=&quot;전체 직원 보기&quot; onclick=&quot;search1()&quot;&gt;
                        &lt;input type=&quot;button&quot; value=&quot;부서별 검색&quot; onclick=&quot;search2()&quot;&gt;
                        &lt;input type=&quot;button&quot; value=&quot;동적 검색&quot; onclick=&quot;search3()&quot;&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            &lt;/table&gt;
        &lt;/form&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<h3 id="inputdept">inputDept</h3>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;style type=&quot;text/css&quot;&gt;
    div{
        width: 800px;
        text-align: center;
        margin: auto;
    }
    table{
        width: 100%;
        border-collapse: collapse;
    }
    th, td{
        border: 1px solid gray;
    }
    th{
        padding: 5px;
        background-color: darkslateblue;
        color: white;
    }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h1&gt;부서를 선택하고 검색 버튼을 누르세요.&lt;/h1&gt;
        &lt;form&gt;
            &lt;table&gt;
                &lt;tr&gt;
                    &lt;th&gt;부서 검색&lt;/th&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;
                        &lt;select name=&quot;department_id&quot;&gt;
                          &lt;option value=&quot;&quot; disabled selected&gt;-부서선택-&lt;/option&gt;
                          &lt;option value=&quot;10&quot; &gt;10&lt;/option&gt;
                          &lt;option value=&quot;20&quot;&gt;20&lt;/option&gt;
                          &lt;option value=&quot;30&quot;&gt;30&lt;/option&gt;
                          &lt;option value=&quot;40&quot;&gt;40&lt;/option&gt;
                          &lt;option value=&quot;50&quot;&gt;50&lt;/option&gt;
                          &lt;option value=&quot;60&quot;&gt;60&lt;/option&gt;
                          &lt;option value=&quot;70&quot;&gt;70&lt;/option&gt;
                          &lt;option value=&quot;80&quot;&gt;80&lt;/option&gt;
                          &lt;option value=&quot;90&quot;&gt;90&lt;/option&gt;
                          &lt;option value=&quot;100&quot;&gt;100&lt;/option&gt;
                          &lt;option value=&quot;110&quot;&gt;110&lt;/option&gt;
                        &lt;/select&gt;
                        &lt;input type = &quot;hidden&quot; value = &quot;deptList&quot; name = &quot;cmd&quot;&gt;
                        &lt;input type=&quot;button&quot; value=&quot;검색&quot; onclick=&quot;dynamicSearching(this.form)&quot;&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            &lt;/table&gt;
        &lt;/form&gt;
        &lt;button style = &quot;margin-top : 20px;&quot; onclick = &quot;location.href=&#39;index.jsp&#39;&quot;&gt;홈으로 돌아가기&lt;/button&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
    function dynamicSearching(f){
        f.action=&quot;/chapter16_search/Controller&quot;
        f.submit();
    }
&lt;/script&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/255d668a-3f79-4688-b3a6-74ac8fc8ce24/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/726e68b0-8eba-4faf-8110-8f85e87fdb43/image.png" alt=""></p>
<h3 id="inputdynamic">inputDynamic</h3>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;style type=&quot;text/css&quot;&gt;
    div{
        width: 800px;
        text-align: center;
        margin: auto;
    }
    table{
        width: 100%;
        border-collapse: collapse;
    }
    th, td{
        border: 1px solid gray;
    }
    th{
        padding: 5px;
        background-color: darkslateblue;
        color: white;
    }
&lt;/style&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
    function search1(){
        location.href=&#39;/chapter16_search/Controller?cmd=allList&#39;;
    }
    function search2(){
        location.href=&#39;/chapter16_search/Controller?cmd=inputDept&#39;;
    }
    function search3(){
        location.href=&#39;/chapter16_search/Controller?cmd=inputDynamic&#39;;
    }
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h1&gt;무엇을 검색 하시겠습니까?&lt;/h1&gt;
        &lt;form&gt;
            &lt;table&gt;
                &lt;tr&gt;
                    &lt;th&gt;검색 옵션&lt;/th&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;
                        선택 검색 :
                        &lt;select name = &quot;type&quot; onchange = &quot;placeholderChanger(this)&quot;&gt;
                            &lt;option value = &quot;employee_id&quot;&gt;직원 ID&lt;/option&gt;
                            &lt;option value = &quot;full_name&quot;&gt;직원 이름&lt;/option&gt;
                            &lt;option value = &quot;job_id&quot;&gt;직급&lt;/option&gt;
                            &lt;option value = &quot;email&quot;&gt;직원 이메일&lt;/option&gt;
                            &lt;option value = &quot;phone_number&quot;&gt;직원 전화번호&lt;/option&gt;
                            &lt;option value = &quot;hire_date&quot;&gt;입사 일자&lt;/option&gt;
                        &lt;/select&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;
                        입력 검색 :
                        &lt;input type = &quot;text&quot; placeholder=&quot;ID를 입력해 주세요.&quot; id = &quot;searching_content&quot; name = &quot;searching_content&quot; onkeydown=&quot;handleKeydown(event, this.form)&quot;&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;
                        &lt;input type = &quot;hidden&quot; value=&quot;dynamicList&quot; name = &quot;cmd&quot;&gt; 
                        &lt;input type = &quot;button&quot; value = &quot;검색&quot; onclick = &quot;dynamicSearching    (this.form)&quot;&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            &lt;/table&gt;
        &lt;/form&gt;
        &lt;button style = &quot;margin-top : 20px;&quot; onclick = &quot;location.href=&#39;index.jsp&#39;&quot;&gt;홈으로 돌아가기&lt;/button&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
&gt;
         // 사용자가 선택한 옵션 값과 입력한 값을 checkInfo 함수로 체킹하고 반환 받은 논리값을 이용해
         // 무사히 통과 받은 상태(true인 상태) 라면 컨트롤러로 폼들을 제출함
    function dynamicSearching(f){
        // select의 value값은 type(type in this case(option)), 
        // 즉 해당 option의 value값으로 정의되어 있다고 함. 
        let option = f.type.value;
        let searchValue = f.searching_content.value;
        let state = checkInfo(option, searchValue);
&gt;
        if(state){
            f.action = &quot;/chapter16_search/Controller&quot;
            f.submit();    
        }
    }
&gt;
    // 각 입력값들이 유효한지를 선택된 select의 옵션 값과 사용자 입력값을 이용하여 비교
    function checkInfo(option, value){
&gt;
        let checkId, checkName, checkEmail, checkNumber, checkJob, checkHireDate;
&gt;
        // 옵션(사용자가 검색하고자 하는 카테고리)에 따라 조건문을 타고 미리 정의해 둔
        // 각 변수들에 정규식을 할당한 뒤 exec 메서드를 호출해 정규식에 해당 사용자의 입력값이 유효한지를 검사
        // 유효하지 않은 경우 경고 문구 후 함수 종료
        if(option === &quot;employee_id&quot;){
            // id는 정수 범위에서 최소1 최대4자리까지 (늘어날  사원들의 id를 생각)
            checkId = /^[0-9]{1,4}$/;
            if(!checkId.exec(value)){
                alert(&quot;올바른 사원의 ID를 입력해 주세요. (예시 : 100, 200)&quot;);
                return false;
            }
        }else if(option === &quot;full_name&quot;){
            // full name은 first_name과 last_name을 공백을 두고 서버에 전달할것이기 때문에
            // 사용자에게 알파벳 범위 내에서 공백을 사이에 두고 한 자 이상씩을 받도록 설정
            let checkName = /^[a-zA-Z]+ [a-zA-Z]+$/;
            if(!checkName.exec(value)){
                alert(&quot;올바른 사원의 이름을 입력해 주세요. (예시 : Steven King, neena kochhar)&quot;)
                return false;
            }
        }else if(option === &quot;email&quot;){
            // 이메일의 경우 단순히 연속되는 문자열로만 구성되어 있기 때문에 1자 - 20자의 알파벳 범위 안에서 체킹
            let checkEmail = /^[a-zA-Z]{1,20}$/;
            if(!checkEmail.exec(value)){
                alert(&quot;올바른 사원의 이메일을 1자 이상 입력해 주세요. (예시 : sking, LDEHAAN)&quot;)
                return false;
            }
        }else if(option === &quot;phone_number&quot;){
            // phone_number의 경우 점(.)을 기준으로 3-3-4 자리와 3-2-4-6자리, 두 종류로 나뉘어짐
            // 그래서 입력되는 값은 점을 사이에 두고 3-3-4 자리의 정수값이거나 3-2-4-6자리의 정수로
            // 선택적으로 사용자에게 받음
            let checkNumber = /^(?:\d{3}\.\d{3}\.\d{4}|\d{3}\.\d{2}\.\d{4}\.\d{6})$/;
            if(!checkNumber.exec(value)){
                alert(&quot;올바른 사원의 전화번호를 입력해 주세요. (예시 : 515.123.7777 또는 011.44.1644.429267)&quot;);
                return false;
            }
        }else if(option === &quot;job_id&quot;){
            // 사원의 직급은 앞이 부서고 뒤가 직급을 의미함. 그래서 무난하게 알파벳 열자리로 입력 받도록 함
            let checkJob = /^[a-zA-Z]{1,10}$/;
            if(!checkJob.exec(value)){
                alert(&quot;올바른 사원의 직급을 입력해 주세요. (예시 : clerk 또는 MGR)&quot;);
                return false
            }
        }else if (option === &quot;hire_date&quot;) {
            // 입사 일자의 경우 RR(세기 구분 연도)/MM(월)/DD(일) 형식으로 저장되어 있는데
            // 이에 대한 정규식으로 슬래쉬(/)로 구분지어 각 두자리씩 00년부터 39년 중 하나, 
            // 01월부터 12월 중, 01일부터 31일 중 하나의 값을 가지도록 함
            let checkHireDate = /^(0[0-9]|1[0-9]|2[0-9]|3[0-9])\/(0[1-9]|1[0-2])\/(0[1-9]|[1-2][0-9]|3[0-1])$/;
            if (!checkHireDate.test(value)) {
                alert(&quot;올바른 사원의 입사 일자를 입력해 주세요. (예시 : 07/01/01)&quot;);
                return false;
            }
        }
&gt;
        return true;
    }
&gt;
    // 검색 창에 Enter 이벤트 키가 발생할 때 기본 동작 방식을 방지하고 dynamicSearching 함수를 실행
    // 검색 버튼을 눌렀을 때와 동일한 과정이 진행
    function handleKeydown(event, form) {
        if (event.key === &#39;Enter&#39;) {
            event.preventDefault(); 
            dynamicSearching(form); 
        }
    }
&gt;
    // 옵션 변경시 입력 내용 초기화하는 함수
    function placeholderChanger(option) {
        let text;
&gt;
        // this(select)의 value(선택된 option 중 하나의 값)에 따라 text에 선택지의 문구를 골라 할당
        switch (option.value) {
            case &quot;employee_id&quot;:
                text = &quot;ID를 입력해 주세요.&quot;;
                break;
            case &quot;full_name&quot;:
                text = &quot;이름을 입력해 주세요.&quot;;
                break;
            case &quot;email&quot;:
                text = &quot;이메일을 입력해 주세요.&quot;;
                break;
            case &quot;phone_number&quot;:
                text = &quot;전화번호를 입력해 주세요.&quot;;
                break;
            case &quot;job_id&quot;:
                text = &quot;직급을 입력해 주세요.&quot;;
                break;
            case &quot;hire_date&quot;:
                text = &quot;입사일자를 입력해 주세요.&quot;;
                break;
        }
&gt;
        // 검색 입력 요소 선택 후 placeholder를 type(선택된 옵션의 name)에 따라 할당 된 text값으로 할당
        // 이때 value(입력 상태)도 빈 문자열로 같이 초기화
        let item = document.querySelector(&#39;#searching_content&#39;)
        item.placeholder = text;
        item.value = &quot;&quot;;
    }
&lt;/script&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/21275f7a-e52f-4f5e-b336-02b21310bad1/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/fe3d0e71-1247-45c4-b582-4c603d4a2616/image.png" alt=""></p>
<h3 id="alllist">allList</h3>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.vo.EmployeeVO&quot;%&gt;
&lt;%@page import=&quot;java.util.List&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;style type=&quot;text/css&quot;&gt;
    div{
        width: 800px;
        text-align: center;
        margin: auto;
    }
    table{
        width: 100%;
        border-collapse: collapse;
    }
    th, td{
        border: 1px solid gray;
    }
    th{
        padding: 5px;
        background-color: darkslateblue;
        color: white;
    }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h1&gt;전체 직원 목록&lt;/h1&gt;
        &lt;table&gt;
            &lt;thead&gt;
                &lt;tr&gt;
                    &lt;th&gt;직원ID&lt;/th&gt;
                    &lt;th&gt;직원이름&lt;/th&gt;
                    &lt;th&gt;직원연락처&lt;/th&gt;
                    &lt;th&gt;직원연봉&lt;/th&gt;
                    &lt;th&gt;부서ID&lt;/th&gt;
                    &lt;th&gt;고용일&lt;/th&gt;
                &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;!-- 
            2명 이상의 직원들의 리스트를 받을 때 출력되는 곳으로 request에 세팅 된 list 속성이 비어있지 않은 상태라면 
            forEach 태그로 하나하나 꺼낸 요소들(vo)의 각 필드값들을 출력하도록함.
            --&gt;
            &lt;tbody&gt;
                &lt;c:choose&gt;
                    &lt;c:when test=&quot;${not empty list}&quot;&gt;
                        &lt;c:forEach var=&quot;vo&quot; items=&quot;${list}&quot;&gt;
                            &lt;tr&gt;
                                &lt;td&gt;${vo.job_id}&lt;/td&gt;
                                &lt;td&gt;${vo.first_name} ${vo.last_name}&lt;/td&gt;
                                &lt;td&gt;${vo.phone_number}&lt;/td&gt;
                                &lt;td&gt;${vo.salary}&lt;/td&gt;
                                &lt;td&gt;${vo.department_id}&lt;/td&gt; 
                                &lt;td&gt;${vo.hire_date}&lt;/td&gt;
                            &lt;/tr&gt;
                        &lt;/c:forEach&gt;
                    &lt;/c:when&gt;
                    &lt;c:otherwise&gt;
                        &lt;tr&gt;
                            &lt;td colspan=&quot;6&quot;&gt;데이터가 존재하지 않습니다. 올바른 값을 입력해 주세요.&lt;/td&gt;
                        &lt;/tr&gt;
                    &lt;/c:otherwise&gt;
                &lt;/c:choose&gt;
            &lt;/tbody&gt;
        &lt;/table&gt;
        &lt;button style = &quot;margin-top : 20px;&quot; onclick = &quot;location.href=&#39;index.jsp&#39;&quot;&gt;홈으로 돌아가기&lt;/button&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/c3719e8f-a629-406c-bd11-b4277c208823/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/51bf8a5f-1252-403b-8473-30d34ba3dd11/image.png" alt=""></p>
<h3 id="searchone">searchOne</h3>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.vo.EmployeeVO&quot;%&gt;
&lt;%@page import=&quot;java.util.List&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Employee List&lt;/title&gt;
&lt;style type=&quot;text/css&quot;&gt;
    div {
        width: 800px;
        text-align: center;
        margin: auto;
    }
    table {
        width: 100%;
        border-collapse: collapse;
    }
    th, td {
        border: 1px solid gray;
    }
    th {
        padding: 5px;
        background-color: darkslateblue;
        color: white;
    }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;!-- 동적으로 전달 받은 한 명의 개인 정보를 출력하는 페이지 --&gt;
        &lt;h1&gt;${person.first_name} ${person.last_name} 직원 목록&lt;/h1&gt;
&gt;
        &lt;table&gt;
            &lt;thead&gt;
                &lt;tr&gt;
                    &lt;th&gt;직원ID&lt;/th&gt;
                    &lt;th&gt;직원이름&lt;/th&gt;
                    &lt;th&gt;직원연락처&lt;/th&gt;
                    &lt;th&gt;직원연봉&lt;/th&gt;
                    &lt;th&gt;부서ID&lt;/th&gt;
                    &lt;th&gt;고용일&lt;/th&gt;
                &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;!-- 전달받은 request의 속성 값(person)이 비어있지 않은 경우 각 값들을 순차적으로 꺼내어 표시 --&gt;
            &lt;!-- 전달받은 request의 속성 값(person)이 비어있는 경우 데이터가 존재하지 않음을 표시 --&gt;
            &lt;tbody&gt;
                &lt;c:choose&gt;
                    &lt;c:when test=&quot;${person != null}&quot;&gt;
                        &lt;tr&gt;
                            &lt;td&gt;${person.employee_id}&lt;/td&gt;
                            &lt;td&gt;${person.first_name} ${person.last_name}&lt;/td&gt;
                            &lt;td&gt;${person.phone_number}&lt;/td&gt;
                            &lt;td&gt;${person.salary}&lt;/td&gt;
                            &lt;td&gt;${person.department_id}&lt;/td&gt;
                            &lt;td&gt;${person.hire_date}&lt;/td&gt;
                        &lt;/tr&gt;
                    &lt;/c:when&gt;
                    &lt;c:otherwise&gt;
                        &lt;tr&gt;
                            &lt;td colspan=&quot;6&quot;&gt;데이터가 존재하지 않습니다. 올바른 값을 입력해 주세요.&lt;/td&gt;
                        &lt;/tr&gt;
                    &lt;/c:otherwise&gt;
                &lt;/c:choose&gt;
            &lt;/tbody&gt;
        &lt;/table&gt;
        &lt;button style = &quot;margin-top : 20px;&quot; onclick = &quot;location.href=&#39;index.jsp&#39;&quot;&gt;홈으로 돌아가기&lt;/button&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<h2 id="controller-controllerjava">Controller (Controller.java)</h2>
<blockquote>
</blockquote>
<pre><code class="language-java">package org.joonzis.controller;
&gt;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
&gt;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
&gt;
import org.joonzis.service.EmployeeService;
import org.joonzis.service.EmployeeServiceImpl;
import org.joonzis.vo.EmployeeVO;
&gt;
@WebServlet(&quot;/Controller&quot;)
public class Controller extends HttpServlet {
    private static final long serialVersionUID = 1L;
&gt;      
    public Controller() {
        super();
    }
&gt;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            request.setCharacterEncoding(&quot;utf-8&quot;);
            response.setContentType(&quot;text/html; charset=utf-8&quot;);
&gt;            
            // 어떤 경로로 이동할건지가 담긴 cmd 형태의 요청 파라미터를 추출하여 cmd 변수에 할당  
            // 이 변수는 switch문의 분기점으로 활용될 예정
            String cmd = request.getParameter(&quot;cmd&quot;);
&gt;            
            //단순 화면 이동인지 데이터를 전달하는 이동인지 구분해야함
            // 그래서 forward 방식일 경우 isForward를 true로 지정한 뒤 
            // 조건값으로 forward문을 태울거고, false일 경우 sendRedirect문을 태울거임
            // 일종의 플래그와도 같음
            boolean isForward = false;
&gt;            
            // forward와 sendRedirect의 경로가 할당될 변수 
            String path = &quot;&quot;;
&gt;    
            // 서비스 인스턴스 생성
            EmployeeService service = new EmployeeServiceImpl();
&gt;            
            // request에 세팅할 다중 속성(EmployeeVO 타입의 List)을 받을 list 변수와
            // 단일 속성(EmployeeVO person 변수를 미리 선엉ㄴ
            List&lt;EmployeeVO&gt; list = null;
            EmployeeVO person = null;
&gt;            
            // cmd 값에 따라 switch문 실행
            switch (cmd) {
            // allList (모든 사용자 조회) 의 경우 service 인스턴스의 getAll 메서드 실행 후 반환값을 list에 할당
            // 할당받은 속성을 request의 속성에 설정 후 isForward(실어나를 값이 있는지)을 true(값이 있음)로 변경
            // path(사용자가 이동할 경로)를 allList.jsp로 지정 후 break(switch문 종료)
            case &quot;allList&quot;:
                list = service.getAll();
                request.setAttribute(&quot;list&quot;, list);
                isForward = true;
                path = &quot;allList.jsp&quot;;
              break;
            // 따로 실어나를(request에 할당할 속성, 즉 전달할 값이 없는 상태) 값이 없는 경우
            // isForward는 놔두고 path(경로)만 지정한 뒤 break
            case &quot;inputDept&quot;:
                path = &quot;inputDept.jsp&quot;;
                break;
            case &quot;inputDynamic&quot;:
                path = &quot;inputDynamic.jsp&quot;;
                break;
            // deptList의 설명은 allList와 동일
            // 단 전달하는 값은 정수형 부서 번호 (오라클에서 department_id가 Number로 되어있기 때문에 맞춰줌)
            // 값을 보낼때에도 service의 getOne 메서드에 지정한 매개변수의 타입대로 int로 변환해 전달
            case &quot;deptList&quot;:
                list = service.getOne(Integer.parseInt(request.getParameter(&quot;department_id&quot;)));
                request.setAttribute(&quot;list&quot;, list);
                isForward = true;
                path = &quot;allList.jsp&quot;;
                break;
&gt;                
            // ★ 동적 생성을 위한 페이지. 여기서 설명이 조금 복잡해지기 때문에 주석을 세세히 나눌 예정 ★
            case &quot;dynamicList&quot;:
                //searching은 사용자의 옵션(option에서 선택한 값)과 searching_content(입력한 값)
                //을 담기 위해 Map을 하나 만들어줌. 그냥 객체 하나 생성했다고 보면 됨
                Map&lt;String, String&gt; searching = new HashMap&lt;&gt;();
                String type = request.getParameter(&quot;type&quot;);
                String searchingContent = request.getParameter(&quot;searching_content&quot;);
                // 위에서 꺼낸 두 파라미터 값을 Map으로 선언한 searching 변수의 put 메서드를 이용해
                // 아래와 같이 문자열 타입의 속성 이름과 문자열 타입의 값(getParameter 한 값들)으로 할당함
                // 사용자가 만약 입력창에서 employee_id 옵션을 선택한 뒤 searching_content의 값으로
                // 135를 입력했다면 첫번째 put메서드에는 &quot;type&quot; : &quot;employee_id&quot;가
                // 두번째 put 메서드에는 &quot;searching_content&quot; : &quot;135&quot;가 입력됨
                searching.put(&quot;type&quot;, type);
                searching.put(&quot;searching_content&quot;, searchingContent);
                // hire_date, job_id, full_name의 경우 반환값이 한 명이 아닐수도 있음. 
                // 입사 일자 이후의 값을 조회하기 때문이고, job_id(직급)이 같은 경우가 있고
                // 풀 네임이 같은 사람이 있을 수도 있기 때문임. 그래서 사용자가 입력한 type(옵션)이 이들 값이 아닌 경우,
                // 즉 단일 쿼리 반환값인 person(EmployeeVO)만 반환하는 상황인 경우
                if(!type.equals(&quot;hire_date&quot;) &amp;&amp; !type.equals(&quot;job_id&quot;) &amp;&amp; !type.equals(&quot;full_name&quot;)) {
                    // EmployeeVO 타입의 Person에 service 인스턴스의 getDynamicResult(단일 VO 반환 함수)
                    // 를 호출하고 매개변수로 put 메서드로 세팅한 searching을 전달함
                    person = service.getDynamicResult(searching);
                    // 그 뒤 반환 받은 person의 값(VO)을 request에 아래의 명칭과 같이 싣고
                    request.setAttribute(&quot;person&quot;, person);
                    // 경로(path)를 단일 VO를 출력하는 페이지로 지정
                    path = &quot;searchOne.jsp&quot;;
                // if조건에 걸리지 않은 타입들, 즉 다중 리스트(List&lt;EmployeeVO&gt;)를 반환 받는 상황인 경우
                }else {
                    // service 인스턴스의 다중 리스트를 반환해주는 메서드를 호출하여 마찬가지로 searching 전달 후
                    // 반환 받은 다중 리스트를 미리 선언해 놓은 list(List&lt;EmployeeVO&gt;)에 할당한 뒤 옵션에 세팅
                    list = service.getDynamicResultList(searching);
                    request.setAttribute(&quot;list&quot;, list);
                    // allList 페이지를 활용해줌 (어차피 다 보여주는 페이지는 동일하되 반환되는 쿼리 값들만 다르므로)
                    path = &quot;allList.jsp&quot;;
                }
                // 
                isForward = true;
                break;
            }
            // 포워드 값이 true(전달할 request 속성이 있는 경우라면)라면 디스패쳐 포워드로 패스와 함께 요청 처리를 날림
            if(isForward) {
&gt;                request.getRequestDispatcher(path).forward(request, response);
            // 전달할 값(request에 세팅이 되어있지 않은 상태)이 없는 경우 그냥 경로(path)로 응답 페이지를 제공
            }else {
                response.sendRedirect(path);
            }
    }
&gt;
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
&gt;
}</code></pre>
<h2 id="vo-employeevojava">VO (EmployeeVO.java)</h2>
<blockquote>
</blockquote>
<pre><code class="language-java">package org.joonzis.vo;
&gt;
import java.util.Date;
&gt;
public class EmployeeVO {
    private String first_name, last_name, email, phone_number, job_id; 
    private int employee_id, salary, commision_pct, manager_id, department_id;
    private Date hire_date;
&gt;    
    public EmployeeVO() {}
&gt;
    public EmployeeVO(String first_name, String last_name, String email, String phone_number, String job_id,
            int employee_id, int salary, int commision_pct, int manager_id, int department_id, Date hire_date) {
        super();
        this.first_name = first_name;
        this.last_name = last_name;
        this.email = email;
        this.phone_number = phone_number;
        this.job_id = job_id;
        this.employee_id = employee_id;
        this.salary = salary;
        this.commision_pct = commision_pct;
        this.manager_id = manager_id;
        this.department_id = department_id;
        this.hire_date = hire_date;
    }
    public String getFirst_name() {
        return first_name;
    }
    public void setFirst_name(String first_name) {
        this.first_name = first_name;
    }
    public String getLast_name() {
        return last_name;
    }
    public void setLast_name(String last_name) {
        this.last_name = last_name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getPhone_number() {
        return phone_number;
    }
    public void setPhone_number(String phone_number) {
        this.phone_number = phone_number;
    }
    public String getJob_id() {
        return job_id;
    }
    public void setJob_id(String job_id) {
        this.job_id = job_id;
    }
    public int getEmployee_id() {
        return employee_id;
    }
    public void setEmployee_id(int employee_id) {
        this.employee_id = employee_id;
    }
    public int getSalary() {
        return salary;
    }
    public void setSalary(int salary) {
        this.salary = salary;
    }
    public int getCommision_pct() {
        return commision_pct;
    }
    public void setCommision_pct(int commision_pct) {
        this.commision_pct = commision_pct;
    }
    public int getManager_id() {
        return manager_id;
    }
    public void setManager_id(int manager_id) {
        this.manager_id = manager_id;
    }
    public int getDepartment_id() {
        return department_id;
    }
    public void setDepartment_id(int department_id) {
        this.department_id = department_id;
    }
    public Date getHire_date() {
        return hire_date;
    }
    public void setHire_date(Date hire_date) {
        this.hire_date = hire_date;
    } 
}</code></pre>
<h2 id="service">Service</h2>
<h3 id="employeeservicejava-인터페이스">EmployeeService.java (인터페이스)</h3>
<blockquote>
</blockquote>
<pre><code class="language-java">package org.joonzis.service;
&gt;
import java.util.List;
import java.util.Map;
&gt;
import org.joonzis.vo.EmployeeVO;
&gt;
public interface EmployeeService {
    public List&lt;EmployeeVO&gt; getAll();
    public List&lt;EmployeeVO&gt; getOne(int department_id);
    public EmployeeVO getDynamicResult(Map&lt;String, String&gt; searching);
    public List&lt;EmployeeVO&gt; getDynamicResultList(Map&lt;String, String&gt; searching);
}</code></pre>
<h3 id="employeeserviceimpljava-구현-클래스">EmployeeServiceImpl.java (구현 클래스)</h3>
<blockquote>
</blockquote>
<pre><code class="language-java">package org.joonzis.service;
&gt;
import java.util.List;
import java.util.Map;
&gt;
import org.joonzis.dao.EmployeeDao;
import org.joonzis.dao.EmployeeDaoImpl;
import org.joonzis.vo.EmployeeVO;
&gt;
public class EmployeeServiceImpl implements EmployeeService{
&gt;    
    private EmployeeDao dao = EmployeeDaoImpl.getInstance();
&gt;
    @Override
    public List&lt;EmployeeVO&gt; getAll() {
        return dao.getAllEmployees();
    }
&gt;    
    @Override
    public List&lt;EmployeeVO&gt; getOne(int department_id) {
        return dao.getOneEmployees(department_id);
    }
&gt;    
    @Override
    public EmployeeVO getDynamicResult(Map&lt;String, String&gt; searching) {
        return dao.getDynamicSearching(searching);
    }
&gt;    
    @Override
    public List&lt;EmployeeVO&gt; getDynamicResultList(Map&lt;String, String&gt; searching) {
        return dao.getDynamicSearchingList(searching);
    }
}</code></pre>
<h2 id="dao">DAO</h2>
<h3 id="employeedaojava-인터페이스">EmployeeDao.java (인터페이스)</h3>
<blockquote>
</blockquote>
<pre><code class="language-java">package org.joonzis.dao;
&gt;
import java.util.List;
import java.util.Map;
&gt;
import org.joonzis.vo.EmployeeVO;
&gt;
public interface EmployeeDao {
    public List&lt;EmployeeVO&gt; getAllEmployees();
    public List&lt;EmployeeVO&gt; getOneEmployees(int department_id);
    public EmployeeVO getDynamicSearching(Map&lt;String, String&gt; searching);
    public List&lt;EmployeeVO&gt; getDynamicSearchingList(Map&lt;String, String&gt; searching);
}</code></pre>
<h3 id="employeedaoimpljava-구현-클래스">EmployeeDaoImpl.java (구현 클래스)</h3>
<blockquote>
</blockquote>
<pre><code class="language-java">package org.joonzis.dao;
&gt;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
&gt;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.joonzis.mybatis.config.DBService;
import org.joonzis.vo.EmployeeVO;
&gt;
public class EmployeeDaoImpl implements EmployeeDao{
&gt;
    // DAO 객체 생성
    private static EmployeeDaoImpl instance = null;
    public EmployeeDaoImpl() {}
    // DAO 획득하는 함수
    public static EmployeeDaoImpl getInstance() {
        if(instance == null) {
            instance = new EmployeeDaoImpl();
        }
        return instance;
    }
&gt;        
    // 필드
    private static SqlSession sqlsession = null;
&gt;
    // 팩토리 얻어오는 함수, 단 동기화를 시켜 여러 접속으로부터의 팩토리 획득을 방지하도록 함
    // 왜냐하면 팩토리는 공유 자원(static)인건 맞으나, 팩토리 세션을 생성하는 작업은 1회만 발생(static 구역)
    // 즉 단 한 번 생성된 팩토리 자원을 안정적으로 다수의 호출자로부터의 호출 충돌을 방지하기 위한 작업이라고 보면 됨
    private synchronized static SqlSession getSqlSession() {
        if(sqlsession == null) {
            sqlsession = DBService.getFactory().openSession(false);
        }
        return sqlsession;
    }
&gt;
    // 테이블의 모든 사원들의 정보를 획득하여 전달하는 역할 
    @Override
    public List&lt;EmployeeVO&gt; getAllEmployees() {
        return getSqlSession().selectList(&quot;select_all&quot;);
    }
&gt;
    // 테이블의 특정 부서 사원들의 정보를 획득하여 전달하는 역할
    @Override
    public List&lt;EmployeeVO&gt; getOneEmployees(int department_id) {
        return getSqlSession().selectList(&quot;select_one&quot;, department_id);
    }
&gt;
    // 동적 검색을 할 때 테이블의 특정 사원의 정보를 획득하여 전달하는 역할 
    @Override
    public EmployeeVO getDynamicSearching(Map&lt;String, String&gt; searching) {
        String type = searching.get(&quot;type&quot;);
        String searching_content = searching.get(&quot;searching_content&quot;);
        //id 전달 전용 변수
        int number_content = 0;
&gt;
        // type의 분기점에 따라 마이바티스에 전달할 searching_content의 값을 number_content(사원 id는 Number이므로)
        // 로 변환하거나, 그대로 냅둬서 selectOne 메서드에 전달함
        if(type.equals(&quot;employee_id&quot;)) {
            number_content = Integer.parseInt(searching.get(&quot;searching_content&quot;));
            System.out.println(number_content);
            return getSqlSession().selectOne(&quot;dynamic_searching_byid&quot;, number_content);
        }else if(type.equals(&quot;email&quot;)){
            return getSqlSession().selectOne(&quot;dynamic_searching_byemail&quot;, searching_content);
        }else{
            return getSqlSession().selectOne(&quot;dynamic_searching_bynumber&quot;, searching_content);
        }
    }
&gt;
    // 동적 검색을 할 때 테이블의 특정 사원들의 정보를 획득하여 전달하는 역할
    public List&lt;EmployeeVO&gt; getDynamicSearchingList(Map&lt;String, String&gt; searching) {
        String type = searching.get(&quot;type&quot;);
        String searching_content = searching.get(&quot;searching_content&quot;);
        // 이름 전달 전용 변수
        Map&lt;String, String&gt; names = new HashMap&lt;&gt;();
&gt;
        // 위 단일 VO를 반환하는 동적 검색 함수와의 차이점은 selectList를 반환, 즉 다수의 VO를 반환한다는
        // 차이점이 존재함, (매개변수는 같기 때문에 오버로딩이 안됨, 어차피 반환 타입도 다름) 
        // 그래서 List&lt;EmployeeVO&gt;를 반환하는 메서드를 하나 더 만들어줌 (설명은 단일 동적 검색 함수 주석과 동일)
        if(type.equals(&quot;job_id&quot;)) {
            return getSqlSession().selectList(&quot;dynamic_searching_byjob&quot;, searching_content);
        }else if(type.equals(&quot;hire_date&quot;)) {
            return getSqlSession().selectList(&quot;dynamic_searching_byhiredate&quot;, searching_content);
        }else {
            // else문에는 type이 full_name인 경우가 되는데, 이때 searching(사용자가 전달한 map)을
            // 공백을 기준으로 나눠 nameArr 배열에 할당함
            // 이럴 경우 공백을 기준으로 앞의 값은 names 맵에 first_name 이름으로 할당하고, 
            // 뒤의 값은 last_name 이름으로 할당한 뒤 마이바티스에 names를 전달
            String[] nameArr = searching.get(&quot;searching_content&quot;).split(&quot; &quot;);
            names.put(&quot;first_name&quot;, nameArr[0]);
            names.put(&quot;last_name&quot;, nameArr[1]);
            return getSqlSession().selectList(&quot;dynamic_searching_byname&quot;, names);
        }
    }
&gt;    
}</code></pre>
<h2 id="mybatis-환경-설정">Mybatis 환경 설정</h2>
<h3 id="dbservicejava-커넥션-풀-구성-클래스">DBService.java (커넥션 풀 구성 클래스)</h3>
<blockquote>
</blockquote>
<pre><code class="language-java">package org.joonzis.mybatis.config;
&gt;
import java.io.InputStream;
&gt;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
&gt;
public class DBService {    // factory를 만드는게 목적
&gt;
    // 필드
    private static SqlSessionFactory factory = null;
&gt;
    // 정적 초기화 변수 (클래스가 로드될때 자동으로 실행되는 코드라고 보면 됨)
    // 아래의 메서드들은 마이바티스를 사용하기 위한 주요 메서드들이라서
    // 마이바티스 사용을 위한 하나의 그림으로 외워둘 필요가 있음.
    static {
        try {
            // 클래스 패스에서 위치한 sqlmap (마이바티스 설정 관련 xml)을 읽어오기 위한 경로를 설정
            String resource = &quot;org/joonzis/mybatis/config/sqlmap.xml&quot;;
            // 설정된 경로의 파일을 읽어들어와 is 스트림에 저장
            InputStream is = Resources.getResourceAsStream(resource);
            // 마이바티스의 세션 팩토리를 생성(빌더의 builder메서드)한 뒤 factory 필드에 할당
            factory = new SqlSessionFactoryBuilder().build(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
&gt;    
    // 사용자가 getFactory 메서드를 호출하면 정적으로 
    public static SqlSessionFactory getFactory() {
        return factory;
    }
}</code></pre>
<h3 id="sqlmapxml-mybatis-환경-설정-xml">sqlmap.xml (Mybatis 환경 설정 xml)</h3>
<blockquote>
</blockquote>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!-- 해당 xml 문서의 구조가 마이바티스의 환경설정을 구성하는 구조로 나타나 있음을 알려주는 설정--&gt;
&lt;!DOCTYPE configuration 
PUBLIC &quot;-//mybatis.org//DTD Config 3.0//EN&quot; 
&quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot; &gt;
&gt;
&lt;!-- 설정값들 --&gt;
&lt;configuration&gt;
&gt;
    &lt;!-- 
    마이바티스 설정 중 표준 출력 로깅을 설정하는 세팅, 이 설정을 세팅해주면
    마이바티스가 실행될때 해당 IDE의 콘솔창에 마이바티스의 실행 과정과 결과가 콘솔에 출력됨
    이 세팅을 이용하면 IDE 환경에서도 쉽게 쿼리문을 디버깅 할 수 있음
     --&gt;
    &lt;settings&gt;
        &lt;setting name=&quot;logImpl&quot; value=&quot;STDOUT_LOGGING&quot;/&gt;
    &lt;/settings&gt;
&gt;
    &lt;!-- 
    마이바티스의 실행 환경을 세팅한 구간
    특히 이 구간에서는 마이바티스가 JDBC 수준에서 트랜잭션을 관리(transactionManager)하게 되며
    데이터 소스의 유형을 POOLED로 설정, 쉽게 말하자면 커넥션 풀의 환경 설정이라고 보면됨
     --&gt;
    &lt;environments default=&quot;development&quot;&gt;
        &lt;environment id=&quot;development&quot;&gt;
            &lt;transactionManager type=&quot;JDBC&quot;/&gt;
            &lt;dataSource type=&quot;POOLED&quot;&gt;
                &lt;property name=&quot;driver&quot; value=&quot;oracle.jdbc.driver.OracleDriver&quot;/&gt;
                &lt;property name=&quot;url&quot; value=&quot;jdbc:oracle:thin:@localhost:1521:xe&quot;/&gt;
                &lt;property name=&quot;username&quot; value=&quot;hr&quot;/&gt;
                &lt;property name=&quot;password&quot; value=&quot;1234&quot;/&gt;
            &lt;/dataSource&gt;
        &lt;/environment&gt;
    &lt;/environments&gt;
&gt;    
    &lt;!-- 
    쿼리를 정의하는 java와 DB 사이에 매핑을 수행하도록 하는 xml이 위치한 경로를 지정하는 곳.
    이 설정을 지정해줘야 
     --&gt;
    &lt;mappers&gt;
        &lt;mapper resource=&quot;org/joonzis/mybatis/mapper/employees.xml&quot;/&gt;
    &lt;/mappers&gt;
&gt;
&lt;/configuration&gt;</code></pre>
<h2 id="마이바티스-매퍼-employeesxml">마이바티스 매퍼 (employees.xml)</h2>
<blockquote>
</blockquote>
<pre><code class="language-sql">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!-- 
해당 xml 문서의 구조가 마이바티스의 매퍼를 구성하는 구조로 나타나 있음을 알려주는 설정
특히 이 설정은 configure 부분의 xml과 다르므로 설정시 주의 필요 
--&gt;
&lt;!DOCTYPE mapper 
PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot; 
&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot; &gt;
&gt;
&lt;mapper namespace=&quot;org.joonzis.mybatis.mapper.employees&quot;&gt; &lt;!-- 마이바티스 환경 설정 xml에 사용할 이름 --&gt;
&gt;
    &lt;!-- 1. 모든 부서 목록 조회 getAllEmployees --&gt;
    &lt;select id=&quot;select_all&quot; resultType = &quot;org.joonzis.vo.EmployeeVO&quot;&gt;
        select * from employees order by employee_id asc
    &lt;/select&gt;
&gt;
    &lt;!-- 2. 부분 부서 목록 조회 getOneEmployees --&gt;
    &lt;select id=&quot;select_one&quot; parameterType=&quot;int&quot; resultType = &quot;org.joonzis.vo.EmployeeVO&quot;&gt;
        select * from employees where department_id = ${department_id} order by department_id asc
    &lt;/select&gt;
&gt;
    &lt;!-- 3-1. 동적 목록 조회 getDynamicSearching (아이디 전용) --&gt;
    &lt;select id=&quot;dynamic_searching_byid&quot; parameterType=&quot;int&quot; resultType = &quot;org.joonzis.vo.EmployeeVO&quot;&gt;
        select * from employees where employee_id = #{number_content}  
    &lt;/select&gt;
&gt;
    &lt;!-- 3-2. 동적 목록 조회 getDynamicSearching (이메일 전용) --&gt;
    &lt;select id=&quot;dynamic_searching_byemail&quot; parameterType=&quot;map&quot; resultType=&quot;org.joonzis.vo.EmployeeVO&quot;&gt;
        select * from employees where email = #{searching_content}  
    &lt;/select&gt;
&gt;
    &lt;!-- 3-3. 동적 목록 조회 (전화번호 전용) getDynamicSearching 오름차순 --&gt;
    &lt;select id=&quot;dynamic_searching_bynumber&quot; parameterType=&quot;map&quot; resultType=&quot;org.joonzis.vo.EmployeeVO&quot;&gt;
        select * from employees where phone_number = #{searching_content} 
&gt;        
    &lt;/select&gt;
&gt;
    &lt;!-- 3-4. 동적 목록 조회 (이름 전용) getDynamicSearching 오름차순 --&gt;
    &lt;select id=&quot;dynamic_searching_byname&quot; parameterType=&quot;map&quot; resultType = &quot;org.joonzis.vo.EmployeeVO&quot;&gt;
        select * from employees where first_name = #{first_name} and last_name = #{last_name} 
    &lt;/select&gt;
&gt;
    &lt;!-- 
    3-4. 동적 목록 조회 (직급 전용) getDynamicSearching 오름차순 (최신 부서 순 대로) 
    참고로 매퍼에서 마이바티스 EL을 사용하는 이유는 동적으로 데이터를 집어 넣기 위함인데
    &#39;%UPPER(#{searching_content})&#39; 이렇게 문자열로 감싸게 되면 문자열로 인식되어 버리기 때문에 동적 생성이 불가능함
    그래서 결합 연산자 (||)를 사용하여 따로 도출된 결과값들을 이어붙인거임.
    덧붙여 오라클의 문자 검색의 경우 칼럼 조회와 달리 대소문자를 구별하기 때문에 사용자의 입력값을 UPPER 메서드로
    대문자화 시켜줘야 칼럼 내의 대문자의 값들 중 일치하는 값을 찾을 수 있음
    --&gt;
    &lt;select id=&quot;dynamic_searching_byjob&quot; parameterType=&quot;string&quot; resultType=&quot;org.joonzis.vo.EmployeeVO&quot;&gt;
        select * from employees where job_id like &#39;%&#39; || UPPER(#{searching_content}) order by department_id asc
    &lt;/select&gt;
&gt;
    &lt;!-- 
    3-5. 동적 목록 조회 (입사일자 전용) getDynamicSearching 내림차순 (입사 일자가 최신인 순서대로 연-월-일)
    날짜 검색의 경우 Extract 함수를 이용해 hire_date 중 연도를 추출해 정렬하고, 연도가 같을시
    월을 추출해 정렬하고, 월도 같으면 일자를 추출해 일로 정렬하도록 함
    --&gt;
    &lt;select id=&quot;dynamic_searching_byhiredate&quot; parameterType=&quot;string&quot; resultType=&quot;org.joonzis.vo.EmployeeVO&quot;&gt;
        select * from employees 
        where hire_date &gt;= TO_DATE(#{searching_content}, &#39;RR/MM/DD&#39;) 
        order by EXTRACT(year from hire_date) desc,
        EXTRACT(month from hire_date) desc,
        EXTRACT(day from hire_date) desc
    &lt;/select&gt;
&lt;/mapper&gt;</code></pre>
<h1 id="6-느낀-점">6. 느낀 점</h1>
<ol>
<li><p>예외 처리를 따로 해주진 않았는데 (안해준게 아니라 그것까지 신경 쓸 겨를이...) 그래도 입력 할 때 정규식으로 검사를 하고 있어서 크게 무리는 없을 것 같습니다. 그래도 할 수 있다면 예외 처리를 또 따로 해줘야 하지 않을까 하는 아쉬움이 드네요.</p>
</li>
<li><p>DAO에서 데이터를 정제하는 부분을 Service로 보내줬어야 하는데 병상에서 코드를 짜는지라 그것까지 미처 생각을 하지 못했습니다. 그래도 이정도면 나름 만족을 하지 않을까 싶고, 다음에는 Service 부분에서 데이터를 정제해 DAO에 보내줘야 겠구나 하는 교훈을 얻었지 않았나 싶습니다.</p>
</li>
<li><p>학원에서 수업 내용을 배우면서 빠르게 전개 되느라 따라가기 좀 벅차긴한데, 그래도 복습을 하면서 정리를 하니까 왜 빠르게 전개가 되는지 알겠더라고요. (요즘은 SPA가 대세라서 이렇게 복잡하게 페이징을 하진 않으니까... 그래도 모듈화 개념은 꼭 익혀야 나중에 컴포넌트나 기능 만들 때 잘 익혀둘 수 있기에 빠르게 훑되 중요한 개념만 공부한 것 같았습니다.) </p>
</li>
<li><p>게시글이나 파일 업로드 부분은 따로 처리하지 하지 않았는데, 나중에 하게 된다면 여지껏 배운 내용으로 스프링을 쓰지 않고 간이 사내 사이트를 하나 만들어보고 싶다는 생각이 들었던 시간이기도 했습니다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP/Servlet] JSP Model1 방식을 활용한 회원 가입 CRUD 홈페이지 제작기 | With JavaBeans, 커스텀 태그 라이브러리, 커넥션 풀]]></title>
            <link>https://velog.io/@re_go/JSPServlet-JSP-Model1-%EB%B0%A9%EC%8B%9D%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%9A%8C%EC%9B%90-%EA%B0%80%EC%9E%85-CRUD-%ED%99%88%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%A0%9C%EC%9E%91%EA%B8%B0-With-JavaBeans-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%83%9C%EA%B7%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80</link>
            <guid>https://velog.io/@re_go/JSPServlet-JSP-Model1-%EB%B0%A9%EC%8B%9D%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%9A%8C%EC%9B%90-%EA%B0%80%EC%9E%85-CRUD-%ED%99%88%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%A0%9C%EC%9E%91%EA%B8%B0-With-JavaBeans-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%83%9C%EA%B7%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80</guid>
            <pubDate>Tue, 13 Aug 2024 10:14:30 GMT</pubDate>
            <description><![CDATA[<h1 id="1-서론">1. 서론</h1>
<p>이번에도 마찬가지로 서블릿을 이용해 <a href="https://velog.io/@re_go/Javascript-JSP%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%9A%8C%EC%9B%90-%EA%B0%80%EC%9E%85-CRUD-%ED%99%88%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%A0%9C%EC%9E%91%EA%B8%B0-With-%EC%86%94%EB%8D%B0%EC%8A%A4%ED%81%AC-Soldesk">이전에 제작했던 홈페이지</a>를 개선해 보았는데요. 때문에 이번 섹션을 먼저 보시기 전에 먼저 이전 제작 홈페이지의 로직을 한 번 보시고 오시는 것을 추천 드립니다. </p>
<p>아무튼 이번에는 저번에 사용하지 않았던 자바빈즈(특정 역할을 대체하는 자바 객체 혹은 클래스)와 표현하기 힘들었던 스크립틀릿을 좀 더 편하게 사용할 수 있게 해주는 커스텀 태그 라이브러리, DB를 좀 더 편하게 접촉하게 할 수 있는 커넥션 풀을 사용해 보았습니다.</p>
<blockquote>
</blockquote>
<center>이번 홈페이지 로직에 사용된 JSP 모델 1 그림과 설명</center>
>
![](https://velog.velcdn.com/images/re_go/post/4cbb0eab-3081-42fd-a3e7-fd183c5d9a86/image.jpg)
>
1. 사용자가 JSP 페이지에 요청을 보냄
2. JSP에서 DB를 중계하는 자바빈즈(DAO)에 사용자의 요청 정보를 전달하면 해당 자바빈즈는 이를 토대로 DB에서 해당 정보를 커넥션 풀을 이용하여  쿼리문을 실행
3. 쿼리 실행(사용자의 정보 저장, 혹은 조회) 후에 결과를 받아온 뒤 서버 측에 해당 사용자의 정보들을 자바 객체로 임시 저장하기 위한 자바빈즈(VO)에 임시 저장하거나 반대로 DB에 업데이트 된 경우 해당 반환 값 (정상 반영 값인 정수값을 반환 받음)을 받은 뒤 VO 객체, 혹은 정수 반환 값을 JSP 페이지에 전달
4. 결과값을 전달 받은 JSP는 해당 결과값을 토대로 JSP 페이지에 사용자의 요청을 화면에 출력(VIEW)으로 응답(response)함.

<h1 id="2-프로젝트-파일-구성">2. 프로젝트 파일 구성</h1>
<p>우선 라이브러리 파일들은 다음과 같이 구성되어 있으며, DB 연동을 위한 ojdbc, 커넥션 풀 사용을 위한 아파치 커넥션풀 라이브러리들, 외부 커스텀 태그 라이브러리와 해당 태그에 EL 표현식을 쓰기 위한 taglibs 라이브러리들을 다운받은 뒤 빌드 패스에 추가해 주었습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/824bc13e-fedc-452d-9c42-083280c5b2d8/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/55717a35-0042-42bf-bdbf-5a02114b77b5/image.png" alt=""></p>
<p>그 다음 JSP 페이지 입니다. 이전에 사용했던 프로젝트의 페이지들을 활용해 주었습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/80c8f2cc-462b-45e1-a8f0-f6bf17e2b6af/image.png" alt=""></p>
<p>마지막으로 자바빈즈 파일입니다. 이곳에는 이전 프로젝트에서 사용자가 JSP 페이지마다 DB를 일일이 연결해서 쿼리를 던져주어야만 했던 수고를 덜하기 위해 커넥션 풀을 자체적으로 연동한 뒤 사용자의 호출이 있을 때마다 정의한 각 DML문을 실행하는 메서드를 호출하여 그 결과값을 반환해 주는 DAO(Data Access Object) 객체 하나와</p>
<p>해당 DB의 테이블과 똑같은 데이터 타입과 이름으로 칼럼들이 정의되어 있어 서버 영역에서 DB의 결과값, 혹은 전달값으로 편하게 받고 전달해줄 VO(Value Object) 객체를 하나 만들어 줍니다. </p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/a3d3ab98-091b-4eae-84cd-54b99b613d74/image.png" alt=""></p>
<h1 id="3-추가-개념글">3. 추가 개념글</h1>
<p>특히 이번 섹션에서는 제 블로그에 소개를 드리지 않은 자바빈즈와 커스텀 태그 라이브러리, 커넥션 풀을 간단히 알아볼 수 있는 블로그 글들을 좀 소개해 드리고자 하는데요. </p>
<p>우선 자바빈즈의 경우 ORM이나 스프링으로 자동 연결 및 관리가 가능하기 때문에 그냥 어떻게 만드는지에 대한 개념이나 방법 정도를 아래의 블로그에서 파악하시는 것으로도 충분합니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/9469015d-ddf2-4588-877f-c7fc01f687c6/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(<a href="https://ccomccomhan.tistory.com/37">https://ccomccomhan.tistory.com/37</a>)</p>
<p>또한 커스텀 태그 라이브러리의 경우 클래스 기반 커스텀 태그는 거의 사장되다시피 한 반면 태그 기반 커스텀 태그는 유지 보수 측면에서 종종 사용되는 상황 (그마저도 요즘 웹페이지 동작은 SPA가 대세라 MPA는 외면 받는 상태라 리액트가 JSP를 대체하긴 하지만...)인지라 이것도 마찬가지로 아래의 아래의 블로그에서 그냥 이런게 있구나 하는 정도로만 파악하시는 것으로도 충분합니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/f0e6b1f1-e268-4ba5-ae6c-44e7e3df2a04/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(<a href="https://blog.naver.com/yunwoosup/222045402611">https://blog.naver.com/yunwoosup/222045402611</a>)</p>
<p>마지막으로 커넥션 풀 또한 사용자가 일일이 연결하지 않아도 된다는 장점이 있으나, 이는 JPA는 물론 마이바티스에서도 훨씬 편하게 가능하기 때문에 이마저도 아래의 블로그에서 참고하시면 좋을듯 합니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/87b32508-4295-4ab2-864b-2b5a42e22c01/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(<a href="https://velog.io/@jaemin05/%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80%EC%9D%84-%EC%99%9C-%EC%95%8C%EC%95%84%EC%95%BC%ED%95%A0%EA%B9%8C">https://velog.io/@jaemin05/%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80%EC%9D%84-%EC%99%9C-%EC%95%8C%EC%95%84%EC%95%BC%ED%95%A0%EA%B9%8C</a>)</p>
<h1 id="4-오라클-db-테이블-구성">4. 오라클 DB 테이블 구성</h1>
<p>오라클 DB의 경우 green 테이블을 다음과 같은 구조로 생성했습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-sql">CREATE TABLE users (
    idx        NUMBER PRIMARY KEY,               
    id         VARCHAR2(20) NOT NULL,            
    pw         VARCHAR2(20) NOT NULL,            
    name       VARCHAR2(20) NOT NULL,            
    age        NUMBER NOT NULL,                  
    addr       VARCHAR2(50),                     
    reg_date   DATE                              
);
&gt;
create sequence green_seq start with 1 increment by 1;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/dd13ff12-978e-4310-9350-15275bd0e084/image.png" alt=""></p>
<h1 id="5-전체-로직-설명">5. 전체 로직 설명</h1>
<h2 id="indexjs">index.js</h2>
<p>가장 기본적인 메인 페이지이며, 이전의 프로젝트와 달라진점이 없는 페이지 입니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
// 스타일
&lt;style type=&quot;text/css&quot;&gt;
    table {
        margin: 0 auto;
        border:  1px solid lightseagreen;
    }
    thead {
        background-color: lightseagreen;
        color: white;
    }
    tfoot{
        background-color: lightseagreen;
    }
    td{
        text-align: center;
        padding: 3px;
    }
    th{
        height: 30px;
        padding: 5px;
    }
    input[type=button], input[type=reset]{
        background-color: lightseagreen;
        border: 1px solid white;
        padding: 5px;
        color: white;
    }
    input[type=button]:hover {
        background-color: white;
        color: lightseagreen;
        font-weight: bold;
        cursor: pointer;
    }
&lt;/style&gt;
&lt;script type=&quot;text/javascript&quot; defer&gt;
    function view_all(){
        location.href = &quot;view_all.jsp&quot;;
    }
&gt;
// 예외 처리
    function view_one(f){
        let regId = /^[0-9a-zA-Z]{8,16}$/;
        if (!regId.exec(f.id.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 아이디를 입력해 주세요.&quot;);
            f.id.value = &quot;&quot;;
            f.id.focus();
            return;
        }
        f.action = &quot;view_one.jsp&quot;;
        f.submit();
    }
&gt;
    function insert_one(f){
        let regId = /^[0-9a-zA-Z]{8,16}$/;
        if (!regId.exec(f.id.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 아이디를 입력해 주세요.&quot;);
            f.id.value = &quot;&quot;;
            f.id.focus();
            return;
        }
        let regPw = /^[0-9a-zA-Z]{8,16}$/;
        if (!regPw.exec(f.pw.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 비밀번호를 입력해 주세요.&quot;);
            f.pw.value = &quot;&quot;;
            f.pw.focus();
            return;
        }
        let koreanRegName = /^[가-힣]{2,6}$/;
        if (!koreanRegName.exec(f.name.value)) {
            alert(&quot;올바른 이름을 입력해 주세요.&quot;);
            f.name.value = &quot;&quot;;
            f.name.focus();
            return;
        }
        let regAge = /^[0-9]{1,3}$/;
        if (!regAge.exec(f.age.value)) {
            alert(&quot;올바른 나이를 입력해 주세요.&quot;);
            f.age.value = &quot;&quot;;
            f.age.focus();
            return;
        }
        let regAddr = /^[가-힣0-9\s]{5,30}$/;
        if (!regAddr.exec(f.addr.value)) {
            alert(&quot;올바른 주소를 5자 이상 입력해 주세요&quot;);
            f.addr.value = &quot;&quot;;
            f.addr.focus();
            return;
        }
        f.action = &quot;insert_one.jsp&quot;;
        f.submit();
    }
&gt;
    function remove_one(f){
        let regId = /^[0-9a-zA-Z]{8,16}$/;
        if (!regId.exec(f.id.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 아이디를 입력해 주세요.&quot;);
            f.id.value = &quot;&quot;;
            f.id.focus();
            return;
        }
        let regPw = /^[0-9a-zA-Z]{8,16}$/;
        if (!regPw.exec(f.pw.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 비밀번호를 입력해 주세요.&quot;);
            f.pw.value = &quot;&quot;;
            f.pw.focus();
            return;
        }
        f.action = &quot;remove_one.jsp&quot;;
        f.submit();
    }
&gt;
    function update_one(f){
        let regId = /^[0-9a-zA-Z]{8,16}$/;
        if (!regId.exec(f.id.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 아이디를 입력해 주세요.&quot;);
            f.id.value = &quot;&quot;;
            f.id.focus();
            return;
        }
        let regPw = /^[0-9a-zA-Z]{8,16}$/;
        if (!regPw.exec(f.pw.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 비밀번호를 입력해 주세요.&quot;);
            f.pw.value = &quot;&quot;;
            f.pw.focus();
            return;
        }
        f.action = &quot;update_one.jsp&quot;;
        f.submit();
    }
&lt;/script&gt;
&lt;/head&gt;
&gt; HTML 코드
&lt;body&gt;
    &lt;form method=&quot;post&quot;&gt;
        &lt;table&gt;
            &lt;thead&gt;
                &lt;tr&gt;
                    &lt;th colspan=&quot;2&quot;&gt;회원 정보 관리&lt;/th&gt;
                &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
                &lt;tr&gt;
                    &lt;td&gt;아이디&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;id&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;비밀번호&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;password&quot; name=&quot;pw&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;이름&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;나이&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;number&quot; name=&quot;age&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;주소&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;addr&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
            &lt;/tbody&gt;
            &lt;tfoot&gt;
                &lt;tr&gt;
                    &lt;th colspan=&quot;2&quot;&gt;
                        &lt;input type=&quot;button&quot; value=&quot;전체보기&quot; onclick=&quot;view_all()&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;button&quot; value=&quot;검색&quot; onclick=&quot;view_one(this.form)&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;button&quot; value=&quot;삽입&quot; onclick=&quot;insert_one(this.form)&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;button&quot; value=&quot;삭제&quot; onclick=&quot;remove_one(this.form)&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;button&quot; value=&quot;수정&quot; onclick=&quot;update_one(this.form)&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;reset&quot; value=&quot;다시 작성&quot;&gt;
                    &lt;/th&gt;
                &lt;/tr&gt;
            &lt;/tfoot&gt;
        &lt;/table&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/5f3c1eba-014f-42f2-9829-fd9a6c37d0fd/image.png" alt=""></p>
<h2 id="전체-출력-영역view_alljsp-getalllist">전체 출력 영역(view_all.jsp, getAllList)</h2>
<p>DB의 모든 사용자를 조회하여 페이지를 출력하는 코드 부분입니다. 특히 이번 JSP 페이지들은 에서 DB 처리는 DAO에서 직접 처리를 해줄것이기 때문에 해당 DAO 객체의 메서드를 호출하고 전달할 값으로 VO 객체에 세팅해 두었던 인스턴스를 전달해 주었으며, </p>
<p>페이징 처리의 경우 이전에 사용하던 스크립틀릿 방식에서 벗어나 자바 코드의 로직 동작이 가능하도록 아파치 측에서 제공하고 있는 커스텀 태그 라이브러리와 EL 조합으로 페이지를 처리해 주었습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>view_all.jsp<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.ex.GreenVO&quot;%&gt;
&lt;%@page import=&quot;java.util.List&quot;%&gt;
&lt;%@page import=&quot;org.joonzis.ex.GreenDao&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
 &lt;!-- 
 ① dao 객체를 얻어와 해당 객체에 정의되어 있는 getAllList 메서드를 호출하면서
 사용자에게 미리 세팅해놓은 파라미터를 던져줍니다. 단 view_all 페이지 에서는
 모든 사용자의 값을 받을 것이기 때문에 파라미터가 없어도 되므로 던질 매개변수는 없으며
 받는 결과값은 List 형태의 GreenVO 객체 집합(나중에 설명할 예정)을 받으므로
 GreenVO 타입의 List의 변수를 하나 선언하여 결과값을 받은 뒤
 해당 페이지 컨텍스트에 속성으로 세팅을 해줍니다.
 --&gt;
&lt;%
  GreenDao dao = GreenDao.getInstance();
  List&lt;GreenVO&gt; list = dao.getAllList();
  pageContext.setAttribute(&quot;list&quot;, list);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;jsp:include page=&quot;index.jsp&quot;/&gt;
&gt;
 &lt;br&gt; &lt;hr&gt; &lt;br&gt;
&gt;    
 &lt;h1 style = &quot;text-align:center&quot; &gt;member 테이블의 전체 데이터&lt;/h1&gt;
 &lt;table&gt;
     &lt;thead&gt;
         &lt;tr&gt;
             &lt;th&gt;회원번호&lt;/th&gt; 
             &lt;th&gt;아이디&lt;/th&gt; 
             &lt;th&gt;비밀번호&lt;/th&gt; 
             &lt;th&gt;이름&lt;/th&gt; 
             &lt;th&gt;나이&lt;/th&gt; 
             &lt;th&gt;주소&lt;/th&gt; 
             &lt;th&gt;가입일&lt;/th&gt; 
         &lt;/tr&gt;
     &lt;/thead&gt;
     &lt;tbody&gt;
     &lt;!-- 
     ② taglib으로 대표적으로 사용되는 라이브러리는 c 태그이며, 
     이 라이브러리는 for문과 if조건문이 대표적으로 사용되기 때문에 
     여기서는 대표적으로 choose(when, otherwise를 포함하는 범위)와
     when(if, else if문 역할), otherwise(else문 역할), 
     forEach(for문 및 forEach 메서드 역할)를 사용하여 List에 담긴
     사용자의 값들을 출력하는 역할로 사용하였습니다.
     --&gt;
         &lt;c:choose&gt;
             &lt;c:when test=&quot;${list.size() &gt; 0}&quot;&gt;
                 &lt;c:forEach var = &quot;vo&quot; items = &quot;${list}&quot;&gt;
                     &lt;tr&gt;
 &lt;!-- 
 ③ DAO의 DB 관련 메서드를 거치면서 vo에 세팅 된 각 로우(행)들의 
 값들을 차례대로 forEach 구문을 사용하면서 꺼내옵니다. 
 단 단일 행만 출력할 경우 forEach는 사용하지 않아도 됩니다. 
 --&gt;
                         &lt;td&gt;${vo.getIdx()}&lt;/td&gt; 
                         &lt;td&gt;${vo.getId()}&lt;/td&gt;
                         &lt;%-- 필드처럼 쓰려면 필드명대로 써야함 (private 이지만 필드를 쓰면 컴파일러는 getter로 자동 인식) --%&gt;
                         &lt;td&gt;${vo.pw}&lt;/td&gt; 
                         &lt;td&gt;${vo.getName()}&lt;/td&gt; 
                         &lt;td&gt;${vo.getAge()}&lt;/td&gt; 
                         &lt;td&gt;${vo.getAddr()}&lt;/td&gt; 
                         &lt;td&gt;${vo.getReg_date()}&lt;/td&gt; 
                     &lt;/tr&gt;
                 &lt;/c:forEach&gt;
             &lt;/c:when&gt;
             &lt;c:otherwise&gt;
                 &lt;tr&gt; 
                     &lt;td colspan=&quot;7&quot;&gt;회원이 존재하지 않습니다.&lt;/td&gt;
                 &lt;/tr&gt;
             &lt;/c:otherwise&gt;
         &lt;/c:choose&gt;
     &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</li>
<li>getAllList 메서드<pre><code class="language-java">public class GreenDao {
 // DB 연결 을 위한 커넥션 API
 private Connection conn = null;
 // 쿼리 실행 준비를 위한 API
 private PreparedStatement ps = null;
 // 쿼리 실행 결과 순회하며 순차적으로 가져오는 대표적인 API
 private ResultSet rs = null;
 //실행할 쿼리문을 담을 변수
 private String sql = &quot;&quot;;
&gt;
 public GreenDao() {}
 // 무분별한 GreenDao 인스턴스 생성 방지를 위해 공유 가능한 하나의 인스턴스만을 미리 생성
 private static GreenDao dao = new GreenDao();
&gt;
 // 생성한 인스턴스를 반환해주는 메서드
 public static GreenDao getInstance() {
     return dao;
 }
&gt;
 // 커넥션 풀을 관리하는 객체(DataSource)
 private static DataSource ds;
 static {
     try {
         // JNDI (Java Naming and Directory Interface) 환경을 초기화하고, 데이터베이스 연결과 같은 리소스를 검색하거나 바인딩 할 수 있는 기본적인 설정을 완료 (그냥 해당 어플리케이션의 DB를 찾기 위한 준비단계라고 생각하시면 편합니다.)
         Context context = new InitialContext();
         // JNDI 환경에서 DataSource 객체를 검색하여 데이터베이스 연결을 관리하기 위한 참조를 얻는 과정
         ds = (DataSource)context.lookup(&quot;java:comp/env/jdbc/oracle&quot;);
     } catch (NamingException e) {
         e.printStackTrace();
     }    
 }
&gt;
 // 조회해서 받아올 값들은 하나 하나의 GreenVO(사용자의 전체 정보 한 줄) 그룹이기 때문에
// List 배열로 받아 사용
 public List&lt;GreenVO&gt; getAllList() {
     List&lt;GreenVO&gt; list = new ArrayList&lt;&gt;();
&gt;
     try {
         // dataSource를 사용하여 DB에 연결을 요청
         conn = ds.getConnection();
         // 쿼리문 작성
         String sql = &quot;select * from green order by idx&quot;;
         //쿼리문 실행 준비
         ps = conn.prepareStatement(sql);
         // 실행 된 쿼리의 결과값을 반환 받음
         rs = ps.executeQuery();
         // resultSet의 next 메서드를 이용해 결과 집합을 순차적으로 접근
         while(rs.next()) {
             // vo 객체를 만들고 해당 객체에 컬럼의 순서대로 순차적으로 값을 세팅(setter)한 뒤
             // List(GreenVO 타입)에 저장
             GreenVO vo = new GreenVO();
             vo.setIdx(rs.getInt(1));
             vo.setId(rs.getString(2));
             vo.setPw(rs.getString(3));
             vo.setName(rs.getString(4));
             vo.setAge(rs.getInt(5));
             vo.setAddr(rs.getString(6));
             vo.setReg_date(rs.getDate(7));
             list.add(vo);
         }
     }catch(Exception e) {
         e.printStackTrace();
     }finally {
         try {
             if(rs != null) {rs.close();}
             if(ps != null) {ps.close();}
             if(conn != null) {ps.close();}
         }catch(Exception e2) {
             e2.printStackTrace();
         }
     }
     // 작업이 rs.next 작업이 끝나면 list를 반환
     return list;
 }
ㆍ
ㆍ
ㆍ
(중략)</code></pre>
</li>
</ol>
<h2 id="회원-가입삽입-영역-insert_onejsp-getinsert-메서드">회원 가입(삽입) 영역 (insert_one.jsp, getInsert 메서드)</h2>
<p>위의 view_all.jsp 페이지와 마찬가지로 이전 프로젝트에서 insert_one.jsp에서 요청을 받은 뒤 VO 객체에 세팅한 후 DAO의 getInsert에 전달하고 난 결과값을 토대로 다음과 같이 페이징 처리를 해줄 것입니다.</p>
<blockquote>
</blockquote>
<ol>
<li>insert_one.jsp<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.ex.GreenVO&quot;%&gt;
&lt;%@page import=&quot;org.joonzis.ex.GreenDao&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;!-- 
받은 요청 정보들을 VO에 세팅하는 영역
--&gt;
&lt;%
request.setCharacterEncoding(&quot;utf-8&quot;);
String id = request.getParameter(&quot;id&quot;);
String pw = request.getParameter(&quot;pw&quot;);
String name = request.getParameter(&quot;name&quot;);
String age = request.getParameter(&quot;age&quot;);
String addr = request.getParameter(&quot;addr&quot;);
&gt;
GreenVO vo = new GreenVO();
&gt;
vo.setId(id);
vo.setPw(pw);
vo.setName(name);
vo.setAge(Integer.parseInt(age));
vo.setAddr(addr);
&gt;
&lt;!-- 
view_all.jsp와 마찬가지로 dao의 인스턴스를 얻어와 getInsert 메서드 호출 후 결과값을 이용
--&gt;
GreenDao dao = GreenDao.getInstance();
int result = dao.getInsert(vo);
pageContext.setAttribute(&quot;result&quot;, result);
&gt;
// 이전 사용 방법
//response.sendRedirect(&quot;view_all.jsp&quot;);
%&gt;
&gt;
&lt;!-- 
스크립틀릿보다 원활한 조건문 작성이 가능하기 때문에, 커스텀 태그를 이용하여 조건문에 따른 페이징 처리를 자바스크립트 태그와 병행해서 처리
--&gt;
&lt;c:choose&gt; 
  &lt;c:when test=&quot;${result &gt;= 0}&quot;&gt;
     &lt;script type=&quot;text/javascript&quot;&gt;
         location.href = &quot;view_all.jsp&quot;
     &lt;/script&gt;             
  &lt;/c:when&gt;
  &lt;c:otherwise&gt;
      &lt;script type=&quot;text/javascript&quot;&gt;
          alert(&quot;회원 가입에 실패했습니다.&quot;)
         location.href = &quot;view_all.jsp&quot;
     &lt;/script&gt;
  &lt;/c:otherwise&gt;
&lt;/c:choose&gt;</code></pre>
</li>
<li>getInsert 메서드 (이전 프로젝트의 getInsert와 크게 다르지 않습니다.)<blockquote>
</blockquote>
<pre><code class="language-java">public int getInsert(GreenVO vo) {
     int result = 0;
&gt;
     try {
         conn = ds.getConnection();
&gt;
         sql = &quot;insert into green(idx, id, pw, name, age, addr, reg_date)&quot; + 
                      &quot;values(green_seq.nextval, ?, ?, ?, ?, ?, sysdate)&quot;;
&gt;
         ps = conn.prepareStatement(sql);
         conn.setAutoCommit(true);
         ps.setString(1, vo.getId());
         ps.setString(2, vo.getPw());
         ps.setString(3, vo.getName());
         ps.setInt(4, vo.getAge());
         ps.setString(5, vo.getAddr());
         result = ps.executeUpdate();
         if(result &gt; 0) {
             System.err.println(&quot;데이터 추가 완료&quot;);
         }else {
             System.out.println(&quot;데이터 추가 실패&quot;);
         }
     } catch (Exception e) {
         e.printStackTrace();    
     }finally {
         try {
             if(ps != null) {ps.close();}
             if(conn != null) {conn.close();}
         } catch (Exception e2) {
             e2.printStackTrace();
         }
     }
&gt;
     return result;
 }</code></pre>
</li>
</ol>
<h2 id="회원-삭제-영역-delete_onejsp-getremove-메서드">회원 삭제 영역 (delete_one.jsp, getRemove 메서드)</h2>
<p>설명은 위 영역의 삽입 영역과 거의 동일하기 때문에 생략하도록 하겠습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>delete_one.jsp<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.ex.GreenDao&quot;%&gt;
&lt;%@page import=&quot;org.joonzis.ex.GreenVO&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%
request.setCharacterEncoding(&quot;utf-8&quot;);
String id = request.getParameter(&quot;id&quot;);
String pw = request.getParameter(&quot;pw&quot;); 
&gt;
GreenVO vo = new GreenVO();
&gt;
vo.setId(id);
vo.setPw(pw);
&gt;
GreenDao dao = GreenDao.getInstance();
int result = dao.getRemove(vo);
pageContext.setAttribute(&quot;result&quot;, result);
&gt;
//response.sendRedirect(&quot;view_all.jsp&quot;);
%&gt;
&gt;
&lt;c:choose&gt; 
  &lt;c:when test=&quot;${result &gt;= 0}&quot;&gt;
     &lt;script type=&quot;text/javascript&quot;&gt;
         location.href = &quot;view_all.jsp&quot;
     &lt;/script&gt;             
  &lt;/c:when&gt;
  &lt;c:otherwise&gt;
      &lt;script type=&quot;text/javascript&quot;&gt;
          alert(&quot;정보 삭제에 실패했습니다.&quot;)
         location.href = &quot;view_all.jsp&quot;
     &lt;/script&gt;
  &lt;/c:otherwise&gt;
&lt;/c:choose&gt;</code></pre>
</li>
<li>getRemove 메서드<blockquote>
</blockquote>
<pre><code class="language-java">public int getRemove(GreenVO vo) {
     int result = 0;
&gt;
     try {
         conn = ds.getConnection();
&gt;            
         sql = &quot;delete from green where id = ? and pw = ?&quot;; 
&gt;            
         ps = conn.prepareStatement(sql);
         conn.setAutoCommit(true);
         ps.setString(1, vo.getId());
         ps.setString(2, vo.getPw());
         result = ps.executeUpdate();
         if(result &gt; 0) {
             System.err.println(&quot;데이터 삭제 완료&quot;);
         }else {
             System.out.println(&quot;데이터 삭제 실패&quot;);
         }
     } catch (Exception e) {
         e.printStackTrace();    
     }finally {
         try {
             if(ps != null) {ps.close();}
             if(conn != null) {conn.close();}
         } catch (Exception e2) {
             e2.printStackTrace();
         }
     }
&gt;
     return result;
 }</code></pre>
</li>
</ol>
<h2 id="회원-업데이트-영역-1-1-update_onejsp-getupdateview-메서드">회원 업데이트 영역 1-1 (update_one.jsp, getUpdateView 메서드)</h2>
<p>로직은 이전에 다루었던 프로젝트의 로직을 따르고 있기 때문에 설명이 필요하다고 판단되는 부분은 간단한 주석 정도로만 코드를 설명하겠습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>update_one.jsp<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.ex.GreenVO&quot;%&gt;
&lt;%@page import=&quot;org.joonzis.ex.GreenDao&quot;%&gt;
&lt;%@page import=&quot;java.sql.ResultSet&quot;%&gt;
&lt;%@page import=&quot;java.sql.PreparedStatement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
 function update(f){
     if(f.pw.value == &#39;&#39; || !f.name.value || f.age.value === null || f.addr.value === null){
         alert(&quot;정보가 비어있습니다. 확인해 주세요.&quot;)
         return;
     }
     f.action = &quot;update.jsp&quot;;
     f.submit();
 }
&lt;/script&gt;
&lt;body&gt;
 &lt;jsp:include page=&quot;index.jsp&quot;/&gt;
&gt;
 &lt;br&gt; &lt;hr&gt; &lt;br&gt;
&gt;    
 &lt;%
 request.setCharacterEncoding(&quot;utf-8&quot;);
 response.setContentType(&quot;text/html; charset=UTF-8&quot;);
 String id = request.getParameter(&quot;id&quot;);
 String pw = request.getParameter(&quot;pw&quot;);
&gt;
 GreenDao dao = GreenDao.getInstance();
 GreenVO vo = dao.getUpdateView(id, pw);
 pageContext.setAttribute(&quot;vo&quot;, vo);    
 %&gt;
&gt;
 &lt;h1 style = &quot;text-align:center&quot;&gt;${vo.getId()} 회원의 데이터&lt;/h1&gt;
 &lt;form method = &quot;post&quot;&gt;
     &lt;table&gt;
         &lt;thead&gt;
             &lt;tr&gt;
                 &lt;th&gt;회원번호&lt;/th&gt; 
                 &lt;th&gt;아이디&lt;/th&gt; 
                 &lt;th&gt;비밀번호&lt;/th&gt; 
                 &lt;th&gt;이름&lt;/th&gt; 
                 &lt;th&gt;나이&lt;/th&gt; 
                 &lt;th&gt;주소&lt;/th&gt; 
                 &lt;th&gt;가입일&lt;/th&gt; 
             &lt;/tr&gt;
         &lt;/thead&gt;
         &lt;tbody&gt;
                  &lt;c:choose&gt;
                     &lt;!-- 
                     태그 라이브러리의 경우 비교 연산자를 기호가 아닌 특정 문구로
                     비교하는 것이 가능하기 때문에 다음과 같이 vo가 비어있는지를
                     판단하기 위해 empty 구문을 사용했습니다. (비어있지 않다면
                     not empty를 사용)
                     --&gt;
                      &lt;c:when test=&quot;${empty vo}&quot;&gt;
                          &lt;tr&gt;
                              &lt;td colspan=&quot;7&quot;&gt;member 데이터가 없습니다.&lt;/td&gt;
                          &lt;/tr&gt;    
                      &lt;/c:when&gt;
                      &lt;c:otherwise&gt;
                          &lt;tr&gt;
                              &lt;td&gt;
                                  ${vo.getIdx()}
                                  &lt;input type = &quot;hidden&quot; name = &quot;idx&quot; value = &quot;${vo.getIdx()}&quot;&gt;  
                              &lt;/td&gt;            
                              &lt;td&gt;
                                  ${vo.getId()}
                              &lt;/td&gt;            
                              &lt;td&gt;
                                  &lt;input type = &quot;password&quot; name = &quot;pw&quot; value = &quot;${vo.getPw()}&quot;&gt; 
                              &lt;/td&gt;            
                              &lt;td&gt;
                                  &lt;input type = &quot;text&quot; name = &quot;name&quot; value = &quot;${vo.getName()}&quot;&gt;
                              &lt;/td&gt;            
                              &lt;td&gt;
                                  &lt;input type = &quot;number&quot; name = &quot;age&quot; value = &quot;${vo.getAge()}&quot;&gt; 
                              &lt;/td&gt;            
                              &lt;td&gt;
                                  &lt;input type = &quot;text&quot; name = &quot;addr&quot; value = &quot;${vo.getAddr()}&quot;&gt;
                              &lt;/td&gt;            
                              &lt;td&gt;
                                  &lt;input type = &quot;text&quot; name = &quot;reg_date&quot; value = &quot;${vo.getReg_date()}&quot; readonly&gt; 
                              &lt;/td&gt;            
                          &lt;/tr&gt;    
                      &lt;/c:otherwise&gt;
                  &lt;/c:choose&gt;
         &lt;/tbody&gt;
         &lt;tfoot&gt;
             &lt;tr&gt;
                 &lt;td colspan = &quot;7&quot;&gt;
                     &lt;input type = button value = &quot;수정&quot; onclick = &quot;update(this.form)&quot;&gt;
                 &lt;/td&gt;
             &lt;/tr&gt;
         &lt;/tfoot&gt;
     &lt;/table&gt;
 &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>getUpdateView 메서드<blockquote>
</blockquote>
<pre><code class="language-java">public GreenVO getUpdateView(String id, String pw){
     GreenVO vo = new GreenVO(); 
&gt;
     try {
         conn = ds.getConnection();
         String sql = &quot;select * from green where id = ? and pw = ?&quot;;
         ps = conn.prepareStatement(sql);
         ps.setString(1, id);
         ps.setString(2, pw);
         rs = ps.executeQuery();
         if(rs.next()) {
             vo.setIdx(rs.getInt(1));
             vo.setId(rs.getString(2));
             vo.setPw(rs.getString(3));
             vo.setName(rs.getString(4));
             vo.setAge(rs.getInt(5));
             vo.setAddr(rs.getString(6));
             vo.setReg_date(rs.getDate(7));
         }
     }catch(Exception e) {
         e.printStackTrace();
     }finally {
         try {
             if(rs != null) {rs.close();}
             if(ps != null) {ps.close();}
             if(conn != null) {ps.close();}
         }catch(Exception e2) {
             e2.printStackTrace();
         }
     }
&gt;
     return vo;
 } </code></pre>
</li>
</ol>
<h2 id="회원-업데이트-영역-1-2-updatejsp-getupdate-메서드">회원 업데이트 영역 1-2 (update.jsp, getUpdate 메서드)</h2>
<p>로직은 이전에 다루었던 프로젝트의 로직을 따르고 있기 때문에 설명이 필요하다고 판단되는 부분은 간단한 주석 정도로만 코드를 설명하겠습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>update.jsp<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.ex.GreenDao&quot;%&gt;
&lt;%@page import=&quot;org.joonzis.ex.GreenVO&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%
 request.setCharacterEncoding(&quot;UTF-8&quot;);
 response.setContentType(&quot;text/html; charset=UTF-8&quot;);
&gt;
 String pw = request.getParameter(&quot;pw&quot;);
 String name = request.getParameter(&quot;name&quot;);
 int age = Integer.parseInt(request.getParameter(&quot;age&quot;));
 String addr = request.getParameter(&quot;addr&quot;);
 int idx = Integer.parseInt(request.getParameter(&quot;idx&quot;));
&gt;
 GreenVO vo = new GreenVO();
 vo.setPw(pw);
 vo.setName(name);
 vo.setAge(age);
 vo.setAddr(addr);
 vo.setIdx(idx);
&gt;
 GreenDao dao =GreenDao.getInstance();
 int result = dao.getUpdate(vo);
 pageContext.setAttribute(&quot;result&quot;, result);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;c:choose&gt;
     &lt;c:when test=&quot;${result &gt; 0}&quot;&gt;
         &lt;script type=&quot;text/javascript&quot;&gt;
             location.href = &quot;view_all.jsp&quot;;
         &lt;/script&gt;
     &lt;/c:when&gt;
     &lt;c:otherwise&gt;
         &lt;script type=&quot;text/javascript&quot;&gt;
             alert(&quot;정보 수정에 실패했습니다.&quot;)
             location.href = &quot;view_all.jsp&quot;;
         &lt;/script&gt;
     &lt;/c:otherwise&gt;
 &lt;/c:choose&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>getUpdate 메서드<blockquote>
</blockquote>
<pre><code class="language-java">public int getUpdate(GreenVO vo) {
     int result = 0;
&gt;        
     try {
         conn = ds.getConnection();
&gt;                
         sql = &quot;update green set pw = ?, name = ?, age = ?, addr = ? where idx = ?&quot;; 
         ps = conn.prepareStatement(sql);
         conn.setAutoCommit(true);
         ps.setString(1, vo.getPw());
         ps.setString(2, vo.getName());
         ps.setInt(3, vo.getAge());
         ps.setString(4, vo.getAddr());
         ps.setInt(5, vo.getIdx());
         result = ps.executeUpdate();
         if(result &gt; 0) {
             System.err.println(&quot;데이터 수정 완료&quot;);
         }else {
             System.out.println(&quot;데이터 수정 실패&quot;);
         }
     } catch (Exception e) {
         e.printStackTrace();    
     }finally {
         try {
             if(ps != null) {ps.close();}
             if(conn != null) {conn.close();}
         } catch (Exception e2) {
             e2.printStackTrace();
         }
     }
&gt;    
     return result;
 }</code></pre>
</li>
</ol>
<h2 id="특정-사용자-조회-영역-view_onejsp--getuserinfobyid-메서드">특정 사용자 조회 영역 (view_one.jsp,  getUserInfoById 메서드)</h2>
<p>로직은 이전에 다루었던 프로젝트의 로직을 따르고 있기 때문에 설명이 필요하다고 판단되는 부분은 간단한 주석 정도로만 코드를 설명하겠습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>view_one.jsp<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.ex.GreenDao&quot;%&gt;
&lt;%@page import=&quot;org.joonzis.ex.GreenVO&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%
request.setCharacterEncoding(&quot;utf-8&quot;);
String id = request.getParameter(&quot;id&quot;);
&gt; 
GreenDao dao = GreenDao.getInstance();
GreenVO result = dao.getUserInfoById(id);
pageContext.setAttribute(&quot;vo&quot;, result);
&gt;
// 이전에 사용했던 방법
//response.sendRedirect(&quot;view_all.jsp&quot;);
%&gt;
&gt;
&lt;body&gt;
 &lt;jsp:include page=&quot;index.jsp&quot;/&gt;
&gt;    
 &lt;br&gt; &lt;hr&gt; &lt;br&gt;
&gt;
 &lt;h1 style=&quot;text-align:center&quot;&gt;green 테이블의 전체 데이터&lt;/h1&gt;
 &lt;table&gt;
     &lt;thead&gt;
        &lt;tr&gt;
            &lt;th&gt;번호&lt;/th&gt;
            &lt;th&gt;아이디&lt;/th&gt;
            &lt;th&gt;비밀번호&lt;/th&gt;
            &lt;th&gt;이름&lt;/th&gt;
            &lt;th&gt;나이&lt;/th&gt;
            &lt;th&gt;주소&lt;/th&gt;
            &lt;th&gt;가입일&lt;/th&gt;
        &lt;/tr&gt;
     &lt;/thead&gt;
     &lt;tbody&gt;
         &lt;c:choose&gt;
             &lt;c:when test=&quot;${not empty vo}&quot;&gt;
                     &lt;tr&gt;
                         &lt;th&gt;${vo.getIdx()}&lt;/th&gt;
                         &lt;th&gt;${vo.getId()}&lt;/th&gt;
                         &lt;th&gt;${vo.getPw()}&lt;/th&gt;
                         &lt;th&gt;${vo.getName()}&lt;/th&gt;
                         &lt;th&gt;${vo.getAge()}&lt;/th&gt;
                         &lt;th&gt;${vo.getAddr()}&lt;/th&gt;
                         &lt;th&gt;${vo.getReg_date()}&lt;/th&gt;
                     &lt;/tr&gt;
             &lt;/c:when&gt;
             &lt;c:otherwise&gt;
                 &lt;tr&gt;
                     &lt;td colspan=&quot;7&quot;&gt;정보 조회에 실패했습니다 &lt;/td&gt;
                 &lt;/tr&gt;
             &lt;/c:otherwise&gt; 
         &lt;/c:choose&gt; 
     &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/body&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>getUserInfoById 메서드<blockquote>
</blockquote>
<pre><code class="language-java">public GreenVO getUserInfoById(String id) {
&gt;
     GreenVO vo = new GreenVO(); 
&gt;        
     try {
         conn = ds.getConnection();
         String sql = &quot;select * from green where id = ?&quot;;
         ps = conn.prepareStatement(sql);
         ps.setString(1, id);
         rs = ps.executeQuery();
         if(rs.next()) {
             vo.setIdx(rs.getInt(1));
             vo.setId(rs.getString(2));
             vo.setPw(rs.getString(3));
             vo.setName(rs.getString(4));
             vo.setAge(rs.getInt(5));
             vo.setAddr(rs.getString(6));
             vo.setReg_date(rs.getDate(7));
         }
     }catch(Exception e) {
         e.printStackTrace();
     }finally {
         try {
             if(rs != null) {rs.close();}
             if(ps != null) {ps.close();}
             if(conn != null) {ps.close();}
         }catch(Exception e2) {
             e2.printStackTrace();
         }
     }
&gt;
     return vo;
 }</code></pre>
</li>
</ol>
<h1 id="6-느낀-점">6. 느낀 점</h1>
<ol>
<li>이번에 여러가지 태그를 쓰면서 확실하게 느꼈던 점은 추가된 기술들을 사용하면서 자바빈즈, 특히 자바의 클래스 객체에 대해서 좀 더 면밀히 와닿을 수 있었던 계기가 되었던 것 같습니다. 물론 이전에도 학원에서 객체를 배우면서 예시를 직접 만들어보며 클래스를 이용한 인스턴스 생성 및 메서드 정의, 사용 방법들을 익힐 수 있었지만, DB를 연동하는 과정에서 사용되는 DAO나 요청 정보를 전달할 때 사용하는 VO 객체를 직접 만들어보고 사용해보며 더욱 확실히 와닿을 수 있어서 해당 프로젝트를 하는 내내 되게 흥미로웠다고 생각됩니다. </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP/Servlet] JSP Model1 방식을 활용한 회원 가입 CRUD 홈페이지 제작기 | with 스크립틀릿 ]]></title>
            <link>https://velog.io/@re_go/Javascript-JSP%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%9A%8C%EC%9B%90-%EA%B0%80%EC%9E%85-CRUD-%ED%99%88%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%A0%9C%EC%9E%91%EA%B8%B0-With-%EC%86%94%EB%8D%B0%EC%8A%A4%ED%81%AC-Soldesk</link>
            <guid>https://velog.io/@re_go/Javascript-JSP%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%9A%8C%EC%9B%90-%EA%B0%80%EC%9E%85-CRUD-%ED%99%88%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%A0%9C%EC%9E%91%EA%B8%B0-With-%EC%86%94%EB%8D%B0%EC%8A%A4%ED%81%AC-Soldesk</guid>
            <pubDate>Tue, 13 Aug 2024 08:21:45 GMT</pubDate>
            <description><![CDATA[<h1 id="1-서론">1. 서론</h1>
<p>학원을 다닌 지 한 달 반이 다 되어갈 무렵 JSP와 서블릿을 거의 마치게 되면서 여태껏 배운 내용을 토대로 간단한 웹 홈페이지를 제작하게 되었습니다. 제작을 해보면서 느낀 점은 &quot;근본이 중요하구나.&quot; 라는 점이었는데, 신기술을 배우기에 앞서 그 기술들의 시작점의 원리를 안다는 것은 앞으로 배워나갈 웹페이지 제작 신기술을 보다 더 잘 이해할 수 있다고 생각되기에 해당 결론에 도달하지 않았나 생각됩니다. </p>
<p>아무튼 해당 홈페이지는 Model1 방식 (JSP에서 프론트, 백, DB를 한꺼번에 처리하는 방식) 으로 제작되었으며 다음과 같은 서블릿 기술들이 사용되었습니다.</p>
<blockquote>
</blockquote>
<center>이번 홈페이지 로직에 사용된 JSP 모델 1 그림과 설명</center>
>
![](https://velog.velcdn.com/images/re_go/post/cbd8d9e0-e235-434f-bd2b-59baa8060e71/image.png)
>
(자료 출처 : https://johnmarc.tistory.com/6)
>
1. 사용자가 JSP 페이지에 요청을 보냄
2. JSP에서 받은 요청 정보를 세팅한 뒤 직접 DB에 쿼리문을 전달하고 결과값을 반환 받음
3. 반환 받은 결과값을 토대로 스크립틀릿으로 직접 출력하거나, 따로 출력만 담당하는 페이지를 리다이렉트, 혹은 인클루드(액션 태그)를 이용해 페이징 처리


<ul>
<li><a href="https://velog.io/@re_go/JSPServlet-%EB%A9%94%EC%86%8C%EB%93%9C-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%94%B0%EB%9D%BC-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EC%9D%98-%EC%A0%95%EB%B3%B4-%EC%B6%94%EC%B6%9C-%EB%A9%94%EC%84%9C%EB%93%9C">데이터 추출 메서드</a></li>
<li><a href="https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BFJSP%EC%9D%98-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EA%B3%BC-%EC%A0%95%EB%B3%B4-%EC%A0%84%EB%8B%AC-%EA%B8%B0%EC%88%A0-sendRedirect-Forword-Session#3-sendredirect%EB%A1%9C-request-%EC%A0%95%EB%B3%B4-%EC%A0%84%EC%86%A1%ED%95%98%EA%B8%B0">리다이렉션 (단순 페이지 이동)</a></li>
<li><a href="https://velog.io/@re_go/JSPServlet-JSP-%ED%8E%98%EC%9D%B4%EC%A7%80%EC%99%80-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EB%B0%98-%ED%83%9C%EA%B7%B8%EB%93%A4">스크립트 태그들</a></li>
<li><a href="https://velog.io/@re_go/JAVA-23.-%EC%9D%B4%ED%81%B4%EB%A6%BD%EC%8A%A4%EC%99%80-SQL-%EC%97%B0%EA%B2%B0-%EB%B0%8F-DB-%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-JDBC-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9E%85%EB%A0%A5">JDBC 드라이버를 이용한 오라클 DB 연동</a> </li>
</ul>
<h1 id="2-프로젝트-파일-구성">2. 프로젝트 파일 구성</h1>
<p>우선 프로젝트는 다음과 같이 구성되어 있으며, 라이브러리리는  DB 연동을 위한 OJDBC6 버전을 사용했습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/a7d300c2-daf5-4746-bea7-0326bb6ae2d3/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/98014c5b-d74b-4794-bc0b-55691fa3ddca/image.jpg" alt=""></p>
<h1 id="3-오라클-db-테이블-구성">3. 오라클 DB 테이블 구성</h1>
<p>오라클 DB의 경우 멤버 테이블과 시퀀스 하나씩 만들어 줬습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-sql">create table member(
    no number primary key,
    id varchar2(20) not null,
    pw varchar2(20) not null,
    name varchar2(20) not null,
    age number not null,
    addr varchar2(50),
    reg_date Date
);
&gt;
create sequence member_seq start with 1 increment by 1;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/f3f6d7b4-f794-4132-924f-e0bd8d515531/image.png" alt=""></p>
<h1 id="4-전체-로직-설명">4. 전체 로직 설명</h1>
<h2 id="indexjs">index.js</h2>
<p>가장 기본적인 메인 페이지이며, 스타일은 table을 중심으로 제작되었습니다. 각 버튼을 클릭하면 예외 처리 함수들이 실행된 뒤 각 JSP 페이지로 이동 처리를 하였습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
// 스타일
&lt;style type=&quot;text/css&quot;&gt;
    table {
        margin: 0 auto;
        border:  1px solid lightseagreen;
    }
    thead {
        background-color: lightseagreen;
        color: white;
    }
    tfoot{
        background-color: lightseagreen;
    }
    td{
        text-align: center;
        padding: 3px;
    }
    th{
        height: 30px;
        padding: 5px;
    }
    input[type=button], input[type=reset]{
        background-color: lightseagreen;
        border: 1px solid white;
        padding: 5px;
        color: white;
    }
    input[type=button]:hover {
        background-color: white;
        color: lightseagreen;
        font-weight: bold;
        cursor: pointer;
    }
&lt;/style&gt;
&lt;script type=&quot;text/javascript&quot; defer&gt;
    function view_all(){
        location.href = &quot;view_all.jsp&quot;;
    }
&gt;
// 예외 처리
    function view_one(f){
        let regId = /^[0-9a-zA-Z]{8,16}$/;
        if (!regId.exec(f.id.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 아이디를 입력해 주세요.&quot;);
            f.id.value = &quot;&quot;;
            f.id.focus();
            return;
        }
        f.action = &quot;view_one.jsp&quot;;
        f.submit();
    }
&gt;
    function insert_one(f){
        let regId = /^[0-9a-zA-Z]{8,16}$/;
        if (!regId.exec(f.id.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 아이디를 입력해 주세요.&quot;);
            f.id.value = &quot;&quot;;
            f.id.focus();
            return;
        }
        let regPw = /^[0-9a-zA-Z]{8,16}$/;
        if (!regPw.exec(f.pw.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 비밀번호를 입력해 주세요.&quot;);
            f.pw.value = &quot;&quot;;
            f.pw.focus();
            return;
        }
        let koreanRegName = /^[가-힣]{2,6}$/;
        if (!koreanRegName.exec(f.name.value)) {
            alert(&quot;올바른 이름을 입력해 주세요.&quot;);
            f.name.value = &quot;&quot;;
            f.name.focus();
            return;
        }
        let regAge = /^[0-9]{1,3}$/;
        if (!regAge.exec(f.age.value)) {
            alert(&quot;올바른 나이를 입력해 주세요.&quot;);
            f.age.value = &quot;&quot;;
            f.age.focus();
            return;
        }
        let regAddr = /^[가-힣0-9\s]{5,30}$/;
        if (!regAddr.exec(f.addr.value)) {
            alert(&quot;올바른 주소를 5자 이상 입력해 주세요&quot;);
            f.addr.value = &quot;&quot;;
            f.addr.focus();
            return;
        }
        f.action = &quot;insert_one.jsp&quot;;
        f.submit();
    }
&gt;
    function remove_one(f){
        let regId = /^[0-9a-zA-Z]{8,16}$/;
        if (!regId.exec(f.id.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 아이디를 입력해 주세요.&quot;);
            f.id.value = &quot;&quot;;
            f.id.focus();
            return;
        }
        let regPw = /^[0-9a-zA-Z]{8,16}$/;
        if (!regPw.exec(f.pw.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 비밀번호를 입력해 주세요.&quot;);
            f.pw.value = &quot;&quot;;
            f.pw.focus();
            return;
        }
        f.action = &quot;remove_one.jsp&quot;;
        f.submit();
    }
&gt;
    function update_one(f){
        let regId = /^[0-9a-zA-Z]{8,16}$/;
        if (!regId.exec(f.id.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 아이디를 입력해 주세요.&quot;);
            f.id.value = &quot;&quot;;
            f.id.focus();
            return;
        }
        let regPw = /^[0-9a-zA-Z]{8,16}$/;
        if (!regPw.exec(f.pw.value)) {
            alert(&quot;대소문자와 소문자를 혼합한 8~16자의 비밀번호를 입력해 주세요.&quot;);
            f.pw.value = &quot;&quot;;
            f.pw.focus();
            return;
        }
        f.action = &quot;update_one.jsp&quot;;
        f.submit();
    }
&lt;/script&gt;
&lt;/head&gt;
&gt; HTML 코드
&lt;body&gt;
    &lt;form method=&quot;post&quot;&gt;
        &lt;table&gt;
            &lt;thead&gt;
                &lt;tr&gt;
                    &lt;th colspan=&quot;2&quot;&gt;회원 정보 관리&lt;/th&gt;
                &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
                &lt;tr&gt;
                    &lt;td&gt;아이디&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;id&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;비밀번호&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;password&quot; name=&quot;pw&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;이름&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;나이&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;number&quot; name=&quot;age&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;주소&lt;/td&gt;
                    &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;addr&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
            &lt;/tbody&gt;
            &lt;tfoot&gt;
                &lt;tr&gt;
                    &lt;th colspan=&quot;2&quot;&gt;
                        &lt;input type=&quot;button&quot; value=&quot;전체보기&quot; onclick=&quot;view_all()&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;button&quot; value=&quot;검색&quot; onclick=&quot;view_one(this.form)&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;button&quot; value=&quot;삽입&quot; onclick=&quot;insert_one(this.form)&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;button&quot; value=&quot;삭제&quot; onclick=&quot;remove_one(this.form)&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;button&quot; value=&quot;수정&quot; onclick=&quot;update_one(this.form)&quot;&gt;&amp;nbsp;&amp;nbsp;
                        &lt;input type=&quot;reset&quot; value=&quot;다시 작성&quot;&gt;
                    &lt;/th&gt;
                &lt;/tr&gt;
            &lt;/tfoot&gt;
        &lt;/table&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/5f3c1eba-014f-42f2-9829-fd9a6c37d0fd/image.png" alt=""></p>
<h2 id="view_alljsp">view_all.jsp</h2>
<p>DB에 등록된 모든 사용자의 정보를 볼 수 있는 곳으로 전체보기 버튼을 눌러서 확인할 수 있을 뿐만 아니라 사용자가 삽입 및 삭제 행위를 할 때에도 해당 페이지로 리다이렉트 되게 되어있는데요. </p>
<p>이때 해당 페이지는 index.js를 include 액션 태그로 포함시켜 등록을 마친 사용자 정보를 바로 한 번에 확인할 수 있도록 구성했으며, 그래서 사용자는 실제로 index.js (정보 입력창)이 포함된 결과 창을 같이 확인할 수 있게 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.db.DBConnect&quot;%&gt;
&lt;%@page import=&quot;java.sql.ResultSet&quot;%&gt;
&lt;%@page import=&quot;java.sql.PreparedStatement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    // index.jsp 페이지 포함
    &lt;jsp:include page=&quot;index.jsp&quot;/&gt;
&gt;    
    &lt;br&gt; &lt;hr&gt; &lt;br&gt;
&gt;    
    &lt;%
    // DB 사용 준비
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        conn = DBConnect.getConnection();
&gt;
        String sql = &quot;select * from member&quot;;
&gt;        
        ps = conn.prepareStatement(sql);
        rs = ps.executeQuery();
&gt;
    %&gt;
    // HTML 화면 처리
    &lt;h1 style = &quot;text-align:center&quot; &gt;member 테이블의 전체 데이터&lt;/h1&gt;
    &lt;table&gt;
        &lt;thead&gt;
            &lt;tr&gt;
                &lt;th&gt;회원번호&lt;/th&gt; 
                &lt;th&gt;아이디&lt;/th&gt; 
                &lt;th&gt;비밀번호&lt;/th&gt; 
                &lt;th&gt;이름&lt;/th&gt; 
                &lt;th&gt;나이&lt;/th&gt; 
                &lt;th&gt;주소&lt;/th&gt; 
                &lt;th&gt;가입일&lt;/th&gt; 
            &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
             &lt;tr&gt;
                // 조건문으로 처리할 데이터가 없다면 없음을, 있으면 데이터를 가져와 출력
                 &lt;%if(!rs.next()){ %&gt;
                     &lt;td colspan=&quot;7&quot;&gt;member 데이터가 없습니다.&lt;/td&gt;
                 &lt;%}else{
                     do{%&gt;
                         &lt;tr&gt;
                             &lt;td&gt;&lt;%= rs.getInt(1) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getString(2) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getString(3) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getString(4) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getInt(5) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getString(6) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getDate(7) %&gt; &lt;/td&gt;            
                         &lt;/tr&gt;
                     &lt;%}while(rs.next());%&gt;
                 &lt;%}%&gt;
             &lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
    &lt;%        
// 예외 처리
    } catch (Exception e) {
        e.printStackTrace();    
        try {
            conn.rollback();
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }finally {
        try {
            if(ps != null) {ps.close();}
            if(conn != null) {conn.close();}
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
    %&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/ffc47e4f-e2d4-4618-a6eb-418c8810fd68/image.png" alt=""></p>
<h2 id="insert_onejsp">insert_one.jsp</h2>
<p>해당 페이지의 경우 &#39;회원가입&#39; 으로 보시는 것도 맞으며, 해당 예외처리를 마치고 submit 되어 넘어온 정보를 토대로 파라미터를 저장한 뒤 오라클 DB와 연결하는 작업을 거쳐 해당 쿼리에 저장문을 작성함으로 DB 등록을 마치도록 했는데요.</p>
<p>리다이렉트로는 view_all 페이지로 이동시켜 그 결과값을 확인할 수 있도록 하였습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.db.DBConnect&quot;%&gt;
&lt;%@page import=&quot;java.sql.PreparedStatement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
    // 정보 전달 값 획득
    request.setCharacterEncoding(&quot;UTF-8&quot;);
    String id = request.getParameter(&quot;id&quot;);
    String pw = request.getParameter(&quot;pw&quot;);
    String name = request.getParameter(&quot;name&quot;);
    int age = Integer.parseInt(request.getParameter(&quot;age&quot;));
    String addr = request.getParameter(&quot;addr&quot;);
&gt;
    // DB 사용 준비
    Connection conn = null;
    PreparedStatement ps = null;
&gt;    
    try {
        conn = DBConnect.getConnection();
        // 쿼리값 설정
        String sql = &quot;insert into member(no, id, pw, name, age, addr, reg_date)&quot; + 
                     &quot;values(member_seq.nextval, ?, ?, ?, ?, ?, sysdate)&quot;;
        &gt; 오라클 DB의 해당 쿼리(테이블의 행)에 전달 받은 파라미터들을 등록
        ps = conn.prepareStatement(sql);
        ps.setString(1, id);
        ps.setString(2, pw);
        ps.setString(3, name);
        ps.setInt(4, age);
        ps.setString(5, addr);
        // 쿼리 실행
        int result = ps.executeUpdate();
        // 콘솔로 결과 출력 후 view_all 페이지 리다이렉트
        if(result &gt; 0) {
            System.err.println(&quot;데이터 추가 완료&quot;);
        }else {
            System.out.println(&quot;데이터 추가 실패&quot;);
        }
        response.sendRedirect(&quot;view_all.jsp&quot;);
    } catch (Exception e) {
        e.printStackTrace();    
        try {
            conn.rollback();
        } catch (Exception e2) {
            e2.printStackTrace();
        }
        response.sendRedirect(&quot;view_all.jsp&quot;);
    }finally {
        try {
            if(ps != null) {ps.close();}
            if(conn != null) {conn.close();}
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
%&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/a858e0d0-6817-4735-acf2-3af6d4be3c82/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/caa538cd-1ab5-40ad-aca4-a8003120a80c/image.png" alt=""></p>
<h2 id="remove_onejsp">remove_one.jsp</h2>
<p>해당 설명은 받아오는 파라미터의 개수와 쿼리 부분만 다르고 나머지는 insert.jsp와 동일합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.db.DBConnect&quot;%&gt;
&lt;%@page import=&quot;java.sql.PreparedStatement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
&gt;
request.setCharacterEncoding(&quot;UTF-8&quot;);
String id = request.getParameter(&quot;id&quot;);
String pw = request.getParameter(&quot;pw&quot;);
&gt;
Connection conn = null;
PreparedStatement ps = null;
&gt;
try {
&gt;        
    conn = DBConnect.getConnection();
    String sql = &quot;delete from member where id = ? AND pw = ?&quot;; 
&gt;
    ps = conn.prepareStatement(sql);
    ps.setString(1, id);
    ps.setString(2, pw);
    int result = ps.executeUpdate();
    if(result &gt; 0) {
        System.err.println(&quot;데이터 삭제 완료&quot;);
    }else {
        System.out.println(&quot;데이터 추가 실패&quot;);
    }
    response.sendRedirect(&quot;view_all.jsp&quot;);
} catch (Exception e) {
    e.printStackTrace();    
    try {
        conn.rollback();
    } catch (Exception e2) {
        e2.printStackTrace();
    }
    response.sendRedirect(&quot;view_all.jsp&quot;);
}finally {
    try {
        if(ps != null) {ps.close();}
        if(conn != null) {conn.close();}
    } catch (Exception e2) {
        e2.printStackTrace();
    }
}
%&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/bb71d18a-682b-49f4-af3b-51136e5c1d9d/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/e3ddc18a-eaef-4d15-842b-cc467961ccec/image.png" alt=""></p>
<h2 id="update_onejsp-updatejsp">update_one.jsp, update.jsp</h2>
<ol>
<li><p>이 부분은 설명할 부분이 좀 긴데요. 일단 사용자한테 전달 받는 값은 아이디와 비밀번호로 동일한데, 해당 값을 받아 DB에 등록되어 있는 해당 사용자의 정보(로우, 행)를 받아옵니다. (select 부분)</p>
</li>
<li><p>그리고 받아온 정보를 토대로 사용자에게 HTML 부분에서 입력을 할 수 있게끔 해당 정보들(rs의 값들)을 스크립틀릿 형태로 input에 뿌려 놓는데요. 변경을 불가능하게 만들 값은 hidden 속성을 주거나, 아니면 그냥 input만 빼거나, readOnly 속성을 주는 방법으로 사용자가 수정할 값을 변경 못하게끔 막아놓습니다.</p>
</li>
<li><p>그 나머지 변경 가능한 정보는 사용자가 인풋 창에 입력하게끔 해놓고 버튼을 누르면 update 함수가 실행되며, 해당 정보들이 모두 존재해 있는 상태인지 아닌지를 예외처리 한 뒤 정보들이 빠짐 없이 입력된 상태라면 (누락이 되지 않은 상태라면) update.jsp를 실행시켜서 insert, remove_one.jsp 와 동일하게 해당 행을 업데이트 시켜주도록 정보를 넘겨줍니다. (action으로 경로를 지정하고 submit으로 정보를 넘겨줌)</p>
<p>이 부분은 주석으로 이해하기에는 순서가 좀 섞여있어서 제가 위에서 설명 드린 내용들을 번호로 지정해서 순서 순으로 보시도록 해놓겠습니다.</p>
</li>
</ol>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.db.DBConnect&quot;%&gt;
&lt;%@page import=&quot;java.sql.ResultSet&quot;%&gt;
&lt;%@page import=&quot;java.sql.PreparedStatement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
// ★ 3번 순서 ★
&lt;script type=&quot;text/javascript&quot;&gt;
    function update(f){
        if(f.pw.value == &#39;&#39; || !f.name.value || f.age.value === null || f.addr.value === null){
            alert(&quot;정보가 비어있습니다. 확인해 주세요.&quot;)
            return;
        }
        f.action = &quot;update.jsp&quot;;
        f.submit();
    }
&lt;/script&gt;
&lt;body&gt;
    &lt;jsp:include page=&quot;index.jsp&quot;/&gt;
&gt;    
    &lt;br&gt; &lt;hr&gt; &lt;br&gt;
&gt;    
    &lt;%
    // ★ 1번 순서 ★
    request.setCharacterEncoding(&quot;utf-8&quot;);
    response.setContentType(&quot;text/html; charset=UTF-8&quot;);
    String id = request.getParameter(&quot;id&quot;);
    String pw = request.getParameter(&quot;pw&quot;);
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
&gt;    
    try {
        conn = DBConnect.getConnection();
&gt;        
        String sql = &quot;select * from member where id = ? and pw = ?&quot;;
&gt;        
        ps = conn.prepareStatement(sql);
        ps.setString(1, id);
        ps.setString(2, pw);
        rs = ps.executeQuery();
&gt;    
    %&gt;
    // ★ 2-1번 순서 ★
    &lt;h1 style = &quot;text-align:center&quot;&gt;&lt;%=id %&gt; 회원의 데이터&lt;/h1&gt;
    &lt;form method = &quot;post&quot;&gt;
        &lt;table&gt;
            &lt;thead&gt;
                &lt;tr&gt;
                    &lt;th&gt;회원번호&lt;/th&gt; 
                    &lt;th&gt;아이디&lt;/th&gt; 
                    &lt;th&gt;비밀번호&lt;/th&gt; 
                    &lt;th&gt;이름&lt;/th&gt; 
                    &lt;th&gt;나이&lt;/th&gt; 
                    &lt;th&gt;주소&lt;/th&gt; 
                    &lt;th&gt;가입일&lt;/th&gt; 
                &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
                 &lt;tr&gt;
                    // ★ 2-2번 순서 ★
                     &lt;%if(!rs.next()){ %&gt;
                         &lt;td colspan=&quot;7&quot;&gt;member 데이터가 없습니다.&lt;/td&gt;
                     &lt;%}else{%&gt;
                             &lt;tr&gt;
                                 &lt;td&gt;
                                     &lt;%= rs.getInt(1) %&gt;
                                     &lt;input type = &quot;hidden&quot; name = &quot;idx&quot; value = &quot;&lt;%= rs.getInt(1) %&gt;&quot;&gt;  
                                 &lt;/td&gt;            
                                 &lt;td&gt;
                                     &lt;%= rs.getString(2) %&gt;
                                 &lt;/td&gt;            
                                 &lt;td&gt;
                                     &lt;input type = &quot;password&quot; name = &quot;pw&quot; value = &quot;&lt;%= rs.getString(3) %&gt;&quot;&gt; 
                                 &lt;/td&gt;            
                                 &lt;td&gt;
                                     &lt;input type = &quot;text&quot; name = &quot;name&quot; value = &quot;&lt;%= rs.getString(4) %&gt;&quot;&gt;
                                 &lt;/td&gt;            
                                 &lt;td&gt;
                                     &lt;input type = &quot;number&quot; name = &quot;age&quot; value = &quot;&lt;%= rs.getInt(5) %&gt;&quot;&gt; 
                                 &lt;/td&gt;            
                                 &lt;td&gt;
                                     &lt;input type = &quot;text&quot; name = &quot;addr&quot; value = &quot;&lt;%= rs.getString(6) %&gt;&quot;&gt;
                                 &lt;/td&gt;            
                                 &lt;td&gt;
                                     &lt;input type = &quot;text&quot; name = &quot;reg_date&quot; value = &quot;&lt;%= rs.getDate(7) %&gt;&quot; readonly&gt; 
                                 &lt;/td&gt;            
                             &lt;/tr&gt;
                     &lt;%}%&gt;
                 &lt;/tr&gt;
            &lt;/tbody&gt;
            &lt;tfoot&gt;
                &lt;tr&gt;
                    &lt;td colspan = &quot;7&quot;&gt;
                        &lt;input type = button value = &quot;수정&quot; onclick = &quot;update(this.form)&quot;&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            &lt;/tfoot&gt;
        &lt;/table&gt;
    &lt;/form&gt;
    &lt;%        
&gt;
    } catch (Exception e) {
        e.printStackTrace();    
        try {
            conn.rollback();
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }finally {
        try {
            if(ps != null) {ps.close();}
            if(conn != null) {conn.close();}
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
    %&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/b8d510bf-16c6-4c98-be43-8e445bab3909/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/de854d81-bf9b-4d44-abb2-ed79f5d99ff8/image.png" alt=""></p>
<h2 id="view_onejsp">view_one.jsp</h2>
<p>특정 사용자를 검색합니다. 설명은 ResertSet 부분(쿼리 셀렉트를 위한 객체)과 executeQuery 부분을 제외한 insert_one.jsp 등과 같은 페이지 설명과 동일합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;org.joonzis.db.DBConnect&quot;%&gt;
&lt;%@page import=&quot;java.sql.ResultSet&quot;%&gt;
&lt;%@page import=&quot;java.sql.PreparedStatement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;jsp:include page=&quot;index.jsp&quot;/&gt;
&gt;    
    &lt;br&gt; &lt;hr&gt; &lt;br&gt;
&gt;
    &lt;%
    String id = request.getParameter(&quot;id&quot;);
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
&gt;
    try {
        conn = DBConnect.getConnection();
&gt;
        String sql = &quot;select * from member where id = ?&quot;;
&gt;
        ps = conn.prepareStatement(sql);
        ps.setString(1, id);
        rs = ps.executeQuery();
&gt;
    %&gt;
    &lt;h1 style= &quot;text-align:center&quot;&gt;member 테이블의 전체 데이터&lt;/h1&gt;
    &lt;table&gt;
        &lt;thead&gt;
            &lt;tr&gt;
                &lt;th&gt;회원번호&lt;/th&gt; 
                &lt;th&gt;아이디&lt;/th&gt; 
                &lt;th&gt;비밀번호&lt;/th&gt; 
                &lt;th&gt;이름&lt;/th&gt; 
                &lt;th&gt;나이&lt;/th&gt; 
                &lt;th&gt;주소&lt;/th&gt; 
                &lt;th&gt;가입일&lt;/th&gt; 
            &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
             &lt;tr&gt;
                 &lt;%if(!rs.next()){ %&gt;
                     &lt;td colspan=&quot;7&quot;&gt;member 데이터가 없습니다.&lt;/td&gt;
                 &lt;%}else{%&gt;
                         &lt;tr&gt;
                             &lt;td&gt;&lt;%= rs.getInt(1) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getString(2) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getString(3) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getString(4) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getInt(5) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getString(6) %&gt; &lt;/td&gt;            
                             &lt;td&gt;&lt;%= rs.getDate(7) %&gt; &lt;/td&gt;            
                         &lt;/tr&gt;
                 &lt;%}%&gt;
             &lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
    &lt;%        
&gt;
    } catch (Exception e) {
        e.printStackTrace();    
        try {
            conn.rollback();
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }finally {
        try {
            if(ps != null) {ps.close();}
            if(conn != null) {conn.close();}
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
    %&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/87c2412a-6bc1-4f38-a278-3f62637774bf/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/94681672-ebf5-4476-990c-d5b8917b2b21/image.png" alt=""></p>
<h1 id="5-느낀-점">5. 느낀 점</h1>
<ol>
<li><p>프로젝트를 진행하면서 참... MVC1 패턴이 저에게는 되게 힘들다는 생각이 많이 들었습니다. (특히 스크립틀릿 언어를 사용하면서....) 무엇보다도 모듈화가 되어있지 않고 단순히 JSP to JSP 형식으로 페이지 이동이 되다보니 프로젝트를 하기 전에는 &quot;깔끔해서 괜찮네?&quot; 라고 생각했는데 막상 하고보니 코드가 막 뒤섞여 있어서 차라리 모듈화를 하는 MVC2 패턴이 훨씬 낫다는 생각이 들었습니다.</p>
</li>
<li><p>예전에 공부 했던 내용들 중에 리다이렉트나 스크립틀릿의 사용에 대해서 예제만 풀어서 정리하다보니 이해가 아쉬웠는데, 이번에 간단한 프로젝트를 해보면서 보다 더 와닿게 알 수 있었습니다. 물론 JSP의 근본을 배우고 있다보니 이것보다 더 훨씬 쉬운 기술들이 깔려있어서 얼른 배워보고 싶다는 느낌도 들었습니다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] JSTL 설치 및 표준 태그 라이브러리 종류와 사용법 알아보기]]></title>
            <link>https://velog.io/@re_go/JSPServlet-JSTL-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%91%9C%EC%A4%80-%ED%83%9C%EA%B7%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%A2%85%EB%A5%98%EC%99%80-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@re_go/JSPServlet-JSTL-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%91%9C%EC%A4%80-%ED%83%9C%EA%B7%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%A2%85%EB%A5%98%EC%99%80-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 07 Aug 2024 09:19:34 GMT</pubDate>
            <description><![CDATA[<h1 id="1-jstl-이란">1. JSTL 이란?</h1>
<p>JSTL (JavaServer Pages Standard Tag Library)이란 JSP에서 사용할 수 있는 표준 태그 라이브러리입니다. 좀 더 구체적으로 말해보자면 JSTL은 JSP 페이지에서 자주 사용되는 스크립틀릿 과 같은 작업을 단순화해서, 더 읽기 쉽고 유지보수하기 쉬운 JSP 페이지를 작성할 수 있도록 도와주는 태그 라이브러리인데요. 여러 라이브러리가 있지만 대표적으로 쓰이는 태그 라이브러리는 코어 라이브러리와 포맷팅 라이브러리가 있습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/5a127c77-556e-4040-9077-4a0e9a51346b/image.webp" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://medium.com/@wjdcjf1229/%EC%BD%94%EC%8A%A4%ED%83%8043%EC%9D%BC%EC%B0%A819-01-03-%EC%BF%A0%ED%82%A4%EC%99%80-%EC%84%B8%EC%85%98-%EC%9D%B5%EC%8A%A4%ED%94%84%EB%A0%88%EC%85%98-%EC%96%B8%EC%96%B4-jstl-%ED%95%84%ED%84%B0-719b8981a466">https://medium.com/@wjdcjf1229/%EC%BD%94%EC%8A%A4%ED%83%8043%EC%9D%BC%EC%B0%A819-01-03-%EC%BF%A0%ED%82%A4%EC%99%80-%EC%84%B8%EC%85%98-%EC%9D%B5%EC%8A%A4%ED%94%84%EB%A0%88%EC%85%98-%EC%96%B8%EC%96%B4-jstl-%ED%95%84%ED%84%B0-719b8981a466</a>)</p>
<h1 id="2-jstl의-사용을-위한-설정-단계">2. JSTL의 사용을 위한 설정 단계</h1>
<p>JSTL을 설정하기 위해서는 해당 라이브러리를 <a href="https://mvnrepository.com/artifact/javax.servlet/jstl/1.2">다음 사이트</a>에서 다운 받아 적용하고자 하는 프로젝트의 빌드 패스에 추가를 해주면 됩니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/12807e40-d339-4ee4-94bd-ba6dec5955fc/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/b76907e8-8c1e-42a6-a19b-1a3be5434703/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/90f2ddf9-6b22-4f58-814b-813dd21e39d5/image.png" alt=""></p>
<p>그 다음 사용하고자 하는 태그의 라이브러리리의 경로를 JSP 페이지 최상단에 지시어로 지정해주면 됩니다. 이때 prefix는 해당 태그의 접두어가 되고, uri는 해당 태그 라이브러리의 주소를 의미합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; %&gt;
&lt;%@ taglib prefix=&quot;fn&quot; uri=&quot;http://java.sun.com/jsp/jstl/functions&quot; %&gt;</code></pre>
<p>만약 여기까지 진행을 하셨다면 이제는 태그들의 종류를 알아볼 차례인데요. 태그는 대략 네 가지 태그가 있으나, 주로 쓰이는 태그는 코어 태그와 포맷팅 태그로서 이번 섹션에서는 코어 태그와 포맷팅 태그를 소개해 드리는 시간을 가져보도록 하겠습니다.</p>
<h1 id="3-코어-태그-종류">3. 코어 태그 종류</h1>
<p>코어 태그는 크게 변수를 생성하거나 삭제하는 태그와 제어문을 지원하는 태그로 나뉘는데요. 이들 태그는 특히 EL과 및 스크립틀릿과 같이 사용되어 상황에 따라 유연한 코드 작성이 가능합니다. </p>
<p>이러한 코어 태그는 접두어로 c: 가 붙으며, 그 뒤에 각 기능과 속성을 작성하여 사용합니다. 특히 속성에 대한 값을 작성할 때에는 문자열로 필히 감싸서 작성을 하여야 하며, 사용할 때에도 EL 처럼 그냥 변수명을 작성해주면 됩니다.</p>
<p>이러한 코어 태그를 사용하기 위해선 다음과 같은 taglib을 JSP 페이지 상단에 임포트 해주어야 합니다. </p>
<blockquote>
</blockquote>
<pre><code>&lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&gt;</code></pre><h2 id="cset-var-변수-명-value-변수-값-">&lt;c:set var =&quot;변수 명&quot; value =&quot;변수 값&quot; /&gt;</h2>
<p>JSP에서 사용할 변수를 만들 때 사용합니다. var은 변수의 이름이고, value는 사용할 값을 의미하는데요. 이때 value의 값에 들어가는 값들은 타입에 따라 자동으로 타입이 적절하게 변환되기 때문에 사용에 크게 신경을 쓰지 않아도 됩니다. 마지막으로 scope는 해당 변수의 유효 범위로 pageContext -&gt; request, session, application 값 중 사용할 수 있는 범위를 작성해 주면 됩니다. </p>
<p>그리고 EL 표기법을 사용할 경우 그 안에 연산까지 가능하며, 특히 변수 명이 중복된 상태에서 값만 다르게 설정한 경우 마지막에 적용 된 c:set 태그의 값이 적용됩니다. (덮어쓰기)</p>
<pre><code class="language-jsp">&lt;c:set var = &quot;num1&quot; value = &quot;50&quot; scope = &quot;request&quot;/&gt;
&lt;c:set var = &quot;num2&quot; value = &quot;60&quot; scope = &quot;request&quot;/&gt;
&gt;
&lt;c:set var = &quot;result&quot; value = &quot;${num1 + num2}&quot; scope = &quot;request&quot;/&gt;</code></pre>
<h2 id="cout-value변수-명-">&lt;c:out value=&quot;${변수 명}&quot; /&gt;</h2>
<p>JSP에서 변수를 화면에 출력할 때 사용합니다. value에는 c:set에서 생성한 변수 이름이나 속성(setAttribute)를 적어주어 화면에 출력하면 됩니다.)</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;c:out value=&quot;${num1}&quot; /&gt; +   
&lt;c:out value=&quot;${num2}&quot; /&gt; =   
&lt;c:out value=&quot;${result}&quot;/&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/0fd982e6-b551-470e-bd31-54dfb38d3657/image.png" alt=""></p>
<h2 id="cremove-value변수-명-">&lt;c:remove value=&quot;${변수 명}&quot; /&gt;</h2>
<p>c:out과 사용법은 동일하며, 삭제할 jstl 변수의 이름을 value 속성의 값으로 작성해주면 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;!-- 변수 선언 --&gt;
&lt;c:set var = &quot;num1&quot; value = &quot;50&quot; scope = &quot;request&quot;/&gt;
&lt;c:set var = &quot;num2&quot; value = &quot;60&quot; scope = &quot;request&quot;/&gt;
&gt;
&lt;c:set var = &quot;result&quot; value = &quot;${num1 + num2}&quot; scope = &quot;request&quot;/&gt;
&lt;c:remove var=&quot;result&quot;/&gt;
&lt;!-- 결과 확인 --&gt;
&lt;c:out value=&quot;${num1}&quot; /&gt; +   
&lt;c:out value=&quot;${num2}&quot; /&gt; =   
&lt;c:out value=&quot;${result}&quot;/&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/5b929c33-8b7f-4e31-bc15-87ce093f4f64/image.png" alt=""></p>
<h2 id="cif-testoperand-관계연산자-operand">&lt;c:if test=&quot;${operand 관계연산자 operand}&quot;&gt;</h2>
<p>프로그래밍 언어에서의 if문과 동일하며, 단일 조건을 비교할 때 사용되는 jstl 태그입니다. 물론 단일 조건을 비교할 때 사용 가능하므로 else if와 같은 다른 조건문을 붙이지는 못합니다. </p>
<p>이때 사용되는 관계 연산자는 엔티티 연산자와 혼용하여 사용 가능합니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/1707c216-9c93-4956-a5ff-f0d7dfbd2eea/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://yunamom.tistory.com/179#google_vignette">https://yunamom.tistory.com/179#google_vignette</a>)</p>
<pre><code class="language-jsp">&lt;form action=jstl_calculator&quot;&gt;
  &lt;p&gt; 첫번째 수 : &lt;input type = &quot;text&quot; name = &quot;num1&quot;&gt; &lt;/p&gt;
  &lt;p&gt; 두번째 수 : &lt;input type = &quot;text&quot; name = &quot;num2&quot;&gt; &lt;/p&gt;
  &lt;input type = &quot;submit&quot; value = &quot;전송&quot;&gt;
&lt;/form&gt;</code></pre>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;c:if test=&quot;${param.num1 - param.num2 gt 0}&quot;&gt;
    ${param.num1}
&lt;/c:if&gt;
&lt;c:if test=&quot;${param.num2 - param.num1 &gt;= 0}&quot;&gt;
    ${param.num2}
&lt;/c:if&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/0b463cf4-d75b-44fb-af1c-43d73c4985df/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/4942460a-620b-4396-b827-c200bcb2ed87/image.png" alt=""></p>
<h2 id="cchoose-cwhen-test조건-cotherwise">&lt;c:choose&gt;, &lt;c:when test=&quot;조건&quot;&gt;, &lt;c:otherwise&gt;</h2>
<p>choose 안에 작성하는 태그들로 choose가 조건문의 모태가 되는 태그이며, when에 조건값을 c:if와 똑같이 넣어줍니다. 이때 when은 if문과 else-if문의 역할을 중복으로 수행햐며, otherwise는 else문의 역할을 수행합니다.</p>
<blockquote>
</blockquote>
<ol>
<li>입력 페이지<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;form action=&quot;Ex04_JSTL2.jsp&quot;&gt;
 &lt;p&gt; 국어 점수 : &lt;input type = &quot;number&quot; name = kor&gt; &lt;/p&gt;
 &lt;p&gt; 영어 점수 : &lt;input type = &quot;number&quot; name = eng&gt; &lt;/p&gt;
 &lt;p&gt; 수학 점수 : &lt;input type = &quot;number&quot; name = mat&gt; &lt;/p&gt;
 &lt;input type = &quot;submit&quot; value = &quot;제출&quot;&gt;
&lt;/form&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>처리 페이지<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; %&gt;
&lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/functions&quot; prefix=&quot;fn&quot; %&gt;
&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;c:set var = &quot;avg&quot; value = &quot;${(param.kor + param.eng + param.mat) / 3}&quot;/&gt;
&gt;
    &lt;c:choose&gt;
           &lt;c:when test=&quot;${avg ge 90}&quot;&gt;
               &lt;c:set var = &quot;grade&quot; value = &quot;A&quot;/&gt;        
           &lt;/c:when&gt;
           &lt;c:when test=&quot;${avg ge 80}&quot;&gt;
               &lt;c:set var = &quot;grade&quot; value = &quot;B&quot;/&gt;        
           &lt;/c:when&gt;
           &lt;c:when test=&quot;${avg ge 70}&quot;&gt;
               &lt;c:set var = &quot;grade&quot; value = &quot;C&quot;/&gt;        
           &lt;/c:when&gt;
           &lt;c:when test=&quot;${avg ge 60}&quot;&gt;
               &lt;c:set var = &quot;grade&quot; value = &quot;D&quot;/&gt;        
           &lt;/c:when&gt;
           &lt;c:otherwise&gt;
               &lt;c:set var = &quot;grade&quot; value = &quot;F&quot;/&gt;
           &lt;/c:otherwise&gt;
       &lt;/c:choose&gt;
&gt;
       &lt;c:choose&gt;
     &lt;c:when test=&quot;${avg &gt;= 60}&quot;&gt;
         &lt;c:set var=&quot;pass&quot; value=&quot;합격&quot; /&gt;
     &lt;/c:when&gt;
     &lt;c:otherwise&gt;
         &lt;c:set var=&quot;pass&quot; value=&quot;불합격&quot; /&gt;
     &lt;/c:otherwise&gt;
 &lt;/c:choose&gt;
&gt;
       &lt;p&gt;국어 : &lt;c:out value = &quot;${param.kor}&quot;/&gt;점&lt;/p&gt;
       &lt;p&gt;영어 : &lt;c:out value = &quot;${param.eng}&quot;/&gt;점&lt;/p&gt;
       &lt;p&gt;수학 : &lt;c:out value = &quot;${param.mat}&quot;/&gt;점&lt;/p&gt;
       &lt;p&gt;평균 : &lt;c:out value = &quot;${avg}&quot;/&gt;&lt;/p&gt;
       &lt;p&gt;학점 : &lt;c:out value = &quot;${grade}&quot;/&gt;&lt;/p&gt;
       &lt;p&gt;합격 여부 : &lt;c:out value = &quot;${pass}&quot;/&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/c6493c4d-f831-40e2-aa24-f07556e72c54/image.png" alt=""><blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/c393cada-8b5b-469a-8e8b-561925e7a030/image.png" alt=""></li>
</ol>
<h2 id="cforeach">&lt;c:forEach&gt;</h2>
<p>forEach는 JS에서의 forEach와 같이 기본적인 for문을 지원해주는 태그인데요. 이때 쓰이는 속성은 임의 속성과 배열 속성에 따라 두 가지 버전으로 나뉩니다.  </p>
<h3 id="cforeach-var변수-명-items-배열-변수">&lt;c:forEach var=&quot;변수 명&quot; items =&quot;${배열 변수}&quot;&gt;</h3>
<p>배열을 전달 받을 경우로, paramValues나 배열 변수의 값이 들어왔을 때 해당 item의 값들을 순차적으로 나열하게 해주는 속성 순서입니다. </p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;body&gt;
    &lt;% 
      String[] names = {&quot;김&quot;, &quot;이&quot;, &quot;박&quot;, &quot;최&quot;, &quot;정&quot;};
      pageContext.setAttribute(&quot;NAMES&quot;, names);
      %&gt;
&gt;
    // 조건문을 배열의 길이만큼 돌면서 이름이 &#39;이&#39; 와 같은 경우만 출력
      &lt;c:forEach var=&quot;name&quot; items =&quot;${NAMES}&quot;&gt;
          &lt;c:if test=&quot;${name == &#39;이&#39;}&quot;&gt;
            ${name} &lt;br&gt;
        &lt;/c:if&gt;
      &lt;/c:forEach&gt;
&lt;/body&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/f8a63908-471c-45c6-9290-a3b1cb908181/image.png" alt=""></p>
<h3 id="cforeach-var--i-begin--1-end--10-step--1">&lt;c:forEach var = &quot;i&quot; begin = &quot;1&quot; end = &quot;10&quot; step = &quot;1&quot;&gt;</h3>
<p> 가장 기본적인 형태의 for문으로 var(변수 선언), begin,end(범위), step(증가 값)으로 구성된 foreach문입니다. </p>
<blockquote>
</blockquote>
<pre><code class="language-jsp"> &lt;body&gt;
 // -2부터 10까지 1씩 증가하되 3의 배수인 경우만 출력
      &lt;c:forEach var = &quot;i&quot; begin = &quot;1&quot; end = &quot;20&quot; step = &quot;1&quot;&gt;
          &lt;c:if test=&quot;${i%3 == 0}&quot;&gt;
              ${i} &lt;br&gt; 
          &lt;/c:if&gt;
      &lt;/c:forEach&gt;
&lt;/body&gt;</code></pre>
<blockquote>
</blockquote>
<p> <img src="https://velog.velcdn.com/images/re_go/post/c603af38-c8d9-4845-a7a6-b93acf948797/image.png" alt=""></p>
<h2 id="cfortokens-items문장-or-배열-명-delims구분자-var개별-이름">&lt;c:forTokens items=&quot;${문장 or 배열 명}&quot; delims=&quot;구분자&quot; var=&quot;개별 이름&quot;&gt;</h2>
<p>특정 구분자(, 또는 ^ 또는 공백이나 ; 등)를 기준으로 문자열이나 배열의 각 단어들을 분리한 뒤 개별 이름으로 꺼내서 출력하거나 사용할 때 사용됩니다. 마치 JS의 split 메서드와도 같은 원리죠.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;body&gt;
    &lt;c:set var=&quot;animals&quot; value=&quot;사자,호랑이;사슴,곰;이구아나^뱀 여우*토끼&quot;/&gt;
&gt;
    &lt;c:forTokens items=&quot;${animals }&quot; delims=&quot;,;^ *&quot; var=&quot;animal&quot;&gt;
        ${animal } &lt;br&gt;
    &lt;/c:forTokens&gt;
&lt;/body&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/5be94fd3-0071-4159-9e5c-62f7922a74b6/image.png" alt=""></p>
<h1 id="4-포매팅-태그-종류">4. 포매팅 태그 종류</h1>
<p>특정 변수를 수학적으로 포맷팅 할 때 사용하는 태그로  대표적인 태그는 <a href="fmt:formatNumber">fmt:formatNumber</a>, <a href="fmt:formatDate">fmt:formatDate</a> 태그가 있습니다. 이 태그를 사용하고자 할경우 다음 taglib을 JSP 페이지 위에 임포트 한 뒤 사용해주면 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; prefix=&quot;fmt&quot; %&gt;    </code></pre>
<h2 id="fmtformatnumber"><a href="fmt:formatNumber">fmt:formatNumber</a></h2>
<p>숫자를 특정 형식으로 포맷합니다. 주로 통화, 퍼센트, 소수점 자리수 조정 등을 위해 사용된다고 하네요.</p>
<ul>
<li>value : 입력 값의 이름을 의미합니다.</li>
<li>groupingUsed : 1000 단위 기준으로 구분 기호를 넣을지 말지를 정할 때 사용하는 속성으로 YES를 입력하면 , 기호로 숫자가 묶이고, NO를 입력하면 구분자가 적용되지 않습니다.</li>
<li>pattern : 선택적 자리 표시와 필수적 자리 표시를 표현할 때 사용합니다. 가령 #,##0,000,00 이라고 한다면 만 단위까지는 선택적으로 넣어도 되나, 그 밑으로는 필수적으로 값이 있어야 함을 의미합니다.</li>
<li>type : 숫자 형식의 타입을 지정할 때 사용하며, number(기본 양식), currency(통화), percent(퍼센트) 등의 숫자 타입을 지정할 수 있습니다.</li>
</ul>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;c:set var=&quot;n&quot; value=&quot;123456789.123456789&quot;/&gt;
&gt;    
    &lt;h3&gt;formatNumber의 groupingUsed&lt;/h3&gt;
    천 단위 구분 기호 + 소수 3자리 : &lt;fmt:formatNumber value=&quot;${n }&quot;/&gt; &lt;br&gt;
    천 단위 구분 기호 없이 소수 3자리 : 
        &lt;fmt:formatNumber value=&quot;${n }&quot; groupingUsed=&quot;no&quot;/&gt; &lt;br&gt;
&gt;
    &lt;hr&gt;
&gt;
    &lt;h3&gt;formatNumber의 pattern&lt;/h3&gt;
    천 단위 구분 기호 + 소수 2자리 : 
        &lt;fmt:formatNumber value=&quot;${n }&quot; pattern=&quot;#,##0.00&quot;/&gt; &lt;br&gt;
    천 단위 구분 기호 + 정수 : 
        &lt;fmt:formatNumber value=&quot;${n }&quot; pattern=&quot;#,000&quot;/&gt; &lt;br&gt;
&gt;
    &lt;hr&gt;
&gt;    
    &lt;h3&gt;formatNumber의 type&lt;/h3&gt;
    퍼센트 : &lt;fmt:formatNumber value=&quot;0.1&quot; type=&quot;percent&quot;/&gt;&lt;br&gt;
    원화 표기 : &lt;fmt:formatNumber value=&quot;${n }&quot; type=&quot;currency&quot;/&gt;&lt;br&gt;
    달러 표기 : &lt;fmt:formatNumber 
                    value=&quot;${n }&quot; 
                    type=&quot;currency&quot; 
                    currencySymbol=&quot;$&quot;/&gt;
&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/688fa895-764b-48ea-8c66-1d9369981918/image.png" alt=""></p>
<h2 id="fmtformatdate"><a href="fmt:formatDate">fmt:formatDate</a></h2>
<p>날짜 형식의 변수를 포매팅 할 때 사용하는 태그 라이브러리로 type 속성과 dateStyle(날짜의 표현 길이 정도), timeStyle(시간의 표현 길이 정도), pattern(사용자가 패턴 지정 가능) 속성으로 구성됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;java.util.Date&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;c:set var=&quot;now&quot; value=&quot;&lt;%=new Date() %&gt;&quot;/&gt;
&gt;    
    &lt;h3&gt;formatDate의 타입&lt;/h3&gt;
    date 타입 : &lt;fmt:formatDate value=&quot;${now }&quot; type=&quot;date&quot;/&gt; &lt;br&gt;
    time 타입 : &lt;fmt:formatDate value=&quot;${now }&quot; type=&quot;time&quot;/&gt; &lt;br&gt;
    both 타입 : &lt;fmt:formatDate value=&quot;${now }&quot; type=&quot;both&quot;/&gt; &lt;br&gt;
&gt;    
    &lt;hr&gt;
&gt;
    &lt;h3&gt;formatDate의 스타일&lt;/h3&gt;
    default : &lt;fmt:formatDate value=&quot;${now }&quot; type=&quot;both&quot;
                dateStyle=&quot;default&quot; timeStyle=&quot;default&quot;/&gt; &lt;br&gt;
    short : &lt;fmt:formatDate value=&quot;${now }&quot; type=&quot;both&quot;
                dateStyle=&quot;short&quot; timeStyle=&quot;short&quot;/&gt; &lt;br&gt;
    medium : &lt;fmt:formatDate value=&quot;${now }&quot; type=&quot;both&quot;
                dateStyle=&quot;medium&quot; timeStyle=&quot;medium&quot;/&gt; &lt;br&gt;
    long : &lt;fmt:formatDate value=&quot;${now }&quot; type=&quot;both&quot;
                dateStyle=&quot;long&quot; timeStyle=&quot;long&quot;/&gt; &lt;br&gt;
    full : &lt;fmt:formatDate value=&quot;${now }&quot; type=&quot;both&quot;
                dateStyle=&quot;full&quot; timeStyle=&quot;full&quot;/&gt; &lt;br&gt;
&gt;                
    &lt;hr&gt;
&gt;                
    &lt;h3&gt;formatDate의 패턴&lt;/h3&gt;
    사용자 패턴 : &lt;fmt:formatDate value=&quot;${now }&quot;
            pattern=&quot;yyyy년 MM월 dd일 E요일 a hh:mm:ss&quot; /&gt;    
&gt;        
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/3444785a-3386-4c6f-85c0-43c41d8b489b/image.png" alt=""></p>
<h1 id="5-그-외-함수들">5. 그 외 함수들</h1>
<p>그 외 함수는 <a href="https://jeusyhk.tistory.com/263">함수 태그</a>와 <a href="https://m.blog.naver.com/joonbread/222164647310">sql 태그</a>는 사용 빈도가 상대적으로 떨어져 &quot;아~ 그냥 이런게 있군요?&quot; 정도만 알아두시면 될 것 같습니다. (궁금하신 분들은 해당 페이지를 서칭하시면 되겠습니다.)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] 자바빈의 정의와 실제 사용을 위한 표준 액션 태그들]]></title>
            <link>https://velog.io/@re_go/JSPServlet-%EC%9E%90%EB%B0%94%EB%B9%88%EC%9D%98-%EC%A0%95%EC%9D%98%EC%99%80-%EC%8B%A4%EC%A0%9C-%EC%82%AC%EC%9A%A9%EC%9D%84-%EC%9C%84%ED%95%9C-%ED%91%9C%EC%A4%80-%EC%95%A1%EC%85%98-%ED%83%9C%EA%B7%B8%EB%93%A4</link>
            <guid>https://velog.io/@re_go/JSPServlet-%EC%9E%90%EB%B0%94%EB%B9%88%EC%9D%98-%EC%A0%95%EC%9D%98%EC%99%80-%EC%8B%A4%EC%A0%9C-%EC%82%AC%EC%9A%A9%EC%9D%84-%EC%9C%84%ED%95%9C-%ED%91%9C%EC%A4%80-%EC%95%A1%EC%85%98-%ED%83%9C%EA%B7%B8%EB%93%A4</guid>
            <pubDate>Tue, 06 Aug 2024 10:57:44 GMT</pubDate>
            <description><![CDATA[<h1 id="1-자바빈이란">1. 자바빈이란?</h1>
<p>자바빈(Java Bean) 이란 서버측에서 특정 형식으로 정보를 저장하고자 개발자가 작성한 클래스를 의미하는데요. 대표적으로 DTO와 VO가 있습니다. 그리고 DTO와 VO 둘 다 서버측에서 객체를 생성하기 위해 정의된 클래스라는 공통점이 있지만, 차이점을 살펴보자면 DTO는 주로 데이터 전송을 목적으로 하고, VO는 값 자체를 표현하고자 할 때 사용한다고 하네요. (뭐... 둘 중 아무거나 상관 없다고 스프링 창시자도 말씀은 하셨다면 이걸로 항상 갑론을박이라고 합니다.)</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/db7b9981-b7c8-44a1-a5e8-d6a88b08f4c6/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://maenco.tistory.com/entry/Java-DTO%EC%99%80-VO%EC%9D%98-%EC%B0%A8%EC%9D%B4">https://maenco.tistory.com/entry/Java-DTO%EC%99%80-VO%EC%9D%98-%EC%B0%A8%EC%9D%B4</a>)</p>
<h1 id="2-자바빈의-필수-구성-요소">2. 자바빈의 필수 구성 요소</h1>
<p>이러한 자바 빈도 결국은 클래스라는 점에서 필수 구성 요소는 다음과 같은데요.</p>
<ul>
<li>반드시 디폴트 패키지를 제외한 특정 패키지에 작성 및 저장해야 한다.</li>
<li>필드 + 생성자 + getter/setter 로 구성되어야 한다.</li>
<li>필드의 프로퍼티는 반드시 private 접근자로 작성한다.</li>
<li>생성자는 jsp:useBean에서 사용을 하기 위해 반드시 디폴트 생성자를 직접 작성해야 한다. </li>
<li>getter/setter는 반드시 public 으로 작성한다.</li>
</ul>
<p>그리고 다음의 규약을 지켜 작성한 자바빈의 예시는 다음과 같습니다. (그냥 <a href="https://velog.io/@re_go/JAVA-0.-%EC%9E%90%EB%B0%94-%EC%9E%91%EC%84%B1%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%82%AC%EC%A0%84-%EC%9E%91%EC%97%85#4-eclipse-%EB%8B%A8%EC%B6%95%ED%82%A4-%EB%AA%A8%EC%9D%8C">단축키 모음</a>에서 우클릭 후 getter와 setter, field를 편하게 생성하면 됩니다.)</p>
<blockquote>
</blockquote>
<pre><code class="language-java">package com.practice.test
&gt;
// 필드 (private)
public class MemberDTO {
    private String id;
    private String pw;
    private String name;
    private int age;
    private String addr;
    private String gender;
    private String[] hobbies;
    private String[] likeFoods;
    private String[] dislikeFoods;
&gt;
    // 기본 생성자
    public MemberDTO() {}
&gt;    // 풀 생성자
    public MemberDTO(String id, String pw, String name, int age, String addr, String gender, String[] hobbies,
            String[] likeFoods, String[] dislikeFoods) {
        super();
        this.id = id;
        this.pw = pw;
        this.name = name;
        this.age = age;
        this.addr = addr;
        this.gender = gender;
        this.hobbies = hobbies;
        this.likeFoods = likeFoods;
        this.dislikeFoods = dislikeFoods;
    }
&gt;    // 게터와 세터(public)
    public String getId() {
        return id;
    }
&gt;
    public void setId(String id) {
        this.id = id;
    }
&gt;
    public String getPw() {
        return pw;
    }
&gt;
    public void setPw(String pw) {
        this.pw = pw;
    }
&gt;
    public String getName() {
        return name;
    }
&gt;
    public void setName(String name) {
        this.name = name;
    }
&gt;
    public int getAge() {
        return age;
    }
&gt;
    public void setAge(int age) {
        this.age = age;
    }
&gt;
    public String getAddr() {
        return addr;
    }
&gt;
    public void setAddr(String addr) {
        this.addr = addr;
    }
&gt;
    public String getGender() {
        return gender;
    }
&gt;
    public void setGender(String gender) {
        this.gender = gender;
    }
&gt;
    public String[] getHobbies() {
        return hobbies;
    }
&gt;
    public void setHobbies(String[] hobbies) {
        this.hobbies = hobbies;
    }
&gt;
    public String[] getLikeFoods() {
        return likeFoods;
    }
&gt;
    public void setLikeFoods(String[] likeFoods) {
        this.likeFoods = likeFoods;
    }
&gt;
    public String[] getDislikeFoods() {
        return dislikeFoods;
    }
&gt;
    public void setDislikeFoods(String[] dislikeFoods) {
        this.dislikeFoods = dislikeFoods;
    }
}</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/79a780ce-9d49-4835-b812-ac93f4238f66/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/2877cbca-c695-4efc-ae5b-e563bcd70dca/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/79637ba3-c2d3-4708-9c49-37ff55eea292/image.png" alt=""></p>
<h1 id="3-표준-액션-태그">3. 표준 액션 태그</h1>
<p>앞서 살펴본 자바빈즈(JavaBeans)를 보다 쉽게 사용하기 위해서 JSP에는 표준 액션 태그는 JSP(JavaServer Pages)라는 태그가 존재하는데요. 그래서 JSP 페이지와 같은 웹 애플리케이션의 구성 요소를 다루기 위해 자주 사용되는 자바빈즈를 쉽게 생성, 수정, 읽기 및 관리를 하기 위해 우리는 표준 액션 태그를 사용하게 되는 거죠. 그럼 JSP에서 사용되는 대표적인 액션 태그들을 하나씩 살펴보도록 하겠습니다.</p>
<p>그럼 그 전에 다음과 같은 자바빈과 JSP 페이지가 있다고 하겠습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>JSP 페이지<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;form action=&quot;userInfoSave.jsp&quot;&gt;
     &lt;div&gt;이름 &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt; &lt;/div&gt;
     &lt;div&gt;나이 &lt;input type=&quot;text&quot; name=&quot;age&quot;&gt; &lt;/div&gt;
     &lt;div&gt;키 &lt;input type=&quot;text&quot; name=&quot;height&quot;&gt; &lt;/div&gt;
     &lt;div&gt;몸무게 &lt;input type=&quot;number&quot; name=&quot;weight&quot;&gt; &lt;/div&gt;
     &lt;div&gt;성별 &lt;input type=&quot;text&quot; name=&quot;gender&quot;&gt; &lt;/div&gt;
     &lt;div&gt;주소 &lt;input type=&quot;text&quot; name=&quot;addr&quot;&gt; &lt;/div&gt;
     &lt;input type=&quot;submit&quot; value=&quot;전송&quot;&gt;
 &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</li>
<li>자바빈즈<blockquote>
</blockquote>
<pre><code class="language-java">package com.practice.test;
&gt;
public class PersonVO {
 private String name;
 private int age;
 private double height;
 private double weight;
 private String gender;
 private String addr;
&gt;    
 public PersonVO() {}
&gt;
 public PersonVO(String name, int age, double height, double weight, String gender, String addr) {
     super();
     this.name = name;
     this.age = age;
     this.height = height;
     this.addr = addr;
     this.gender = gender;
 }
&gt;
 public double getWeight() {
     return weight;
 }
&gt;
 public void setWeight(double weight) {
     this.weight = weight;
 }
&gt;
 public String getGender() {
     return gender;
 }
&gt;
 public void setGender(String gender) {
     this.gender = gender;
 }
&gt;
 public String getName() {
     return name;
 }
&gt;
 public void setName(String name) {
     this.name = name;
 }
&gt;
 public int getAge() {
     return age;
 }
&gt;
 public void setAge(int age) {
     this.age = age;
 }
&gt;
 public double getHeight() {
     return height;
 }
&gt;
 public void setHeight(double height) {
     this.height = height;
 }
&gt;
 public String getAddr() {
     return addr;
 }
&gt;
 public void setAddr(String addr) {
     this.addr = addr;
 }    
}</code></pre>
</li>
</ol>
<h2 id="jspusebean"><a href="jsp:useBean">jsp:useBean</a></h2>
<p>JSP 페이지에서 자바빈즈를 사용하기 위해 필요한 인스턴스를 생성하거나, 이미 생성된 인스턴스를 찾아서 사용할 수 있게 합니다. 설정하는 속성으로는 세 가지가 존재하는데, 첫번째(id)는 JSP 페이지에서 해당 자바빈즈를 식별하기 위한 별명이고, 두번째(class)는 해당 빈즈의 패키지 경로, 세번째(scope) 는 해당 빈즈가 어느 영역까지(pageContext부터 Application까지) 유효한지를 설정할 수 있습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;jsp:useBean id=&quot;info&quot; class=&quot;com.practice.test.PersonVO&quot; scope=&quot;session&quot;/&gt;</code></pre>
<h2 id="jspsetproperty"><a href="jsp:setProperty">jsp:setProperty</a></h2>
<p>사용자가 입력한 값의 속성(name) 중 특정 속성을 특정 자바빈즈 객체의 속성(필드의 값)으로 지정할 때 사용하는 액션 태그로, 이때 자바빈즈에 정의되어 있는 필드의 각 멤버 이름과 사용자가 요청할 때의 파라미터들의 name 속성의 이름은 꼭 일치해야 하며, 개수 또한 일치해야 합니다.</p>
<blockquote>
</blockquote>
<ol>
<li>JSP 페이지에서 사용자가 요청할 값들<pre><code class="language-jsp">&lt;form action=&quot;userInfoSave.jsp&quot; method=&quot;post&quot;&gt;
 &lt;div&gt;이름 &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt; &lt;/div&gt;
 &lt;div&gt;나이 &lt;input type=&quot;text&quot; name=&quot;age&quot;&gt; &lt;/div&gt;
 &lt;div&gt;키 &lt;input type=&quot;text&quot; name=&quot;height&quot;&gt; &lt;/div&gt;
 &lt;div&gt;몸무게 &lt;input type=&quot;number&quot; name=&quot;weight&quot;&gt; &lt;/div&gt;
 &lt;div&gt;성별 &lt;input type=&quot;text&quot; name=&quot;gender&quot;&gt; &lt;/div&gt;
 &lt;div&gt;주소 &lt;input type=&quot;text&quot; name=&quot;addr&quot;&gt; &lt;/div&gt;
 &lt;input type=&quot;submit&quot; value=&quot;전송&quot;&gt;
&lt;/form&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>자바빈즈에 설정 된 멤버 필드들<pre><code class="language-java">private String name;
private int age;
private double height;
private double weight;
private String gender;
private String addr;</code></pre>
</li>
</ol>
<p>이러한 setProperty 액션 태그의 첫번째 속성은 전달할 값을 의미하며(왠만해선 *를 넣어주자), 두번째 속성은 지정된 자바빈즈의 별명, 즉 useBean에 설정된 해당 자바빈즈의 별명(id)를 넣어주면 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">// property에  파라미터를 자바빈즈의 특정 멤버에 명시적으로 넣고자 할 경우 다음과 같이 작성
// 단 이럴 경우 나머지 사용자가 전달한 값들은 무시됨.
&lt;jsp:setProperty property=&quot;name&quot; name=&quot;info&quot;/&gt;
&lt;jsp:setProperty property=&quot;age&quot; name=&quot;info&quot;/&gt;
&gt;
// 그래서 보통은 사용자의 입력 값들(name)과 자바빈즈의 멤버 필드의 이름을 꼭 일치시킨 뒤
// 다음과 같이 아스터리스크를 붙여 한꺼번에 자동으로 입력되게 하는 편임.
&lt;jsp:setProperty property=&quot;*&quot; name=&quot;info&quot;/&gt;</code></pre>
<h2 id="jspgetproperty"><a href="jsp:getProperty">jsp:getProperty</a></h2>
<p>특정 별명으로 지정된 자바빈즈 객체의 특정 속성 값을 읽어와서 JSP 페이지에 출력합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;h1&gt;
    이름 : &lt;jsp:getProperty name=&quot;info&quot; property=&quot;name&quot;/&gt;
    나이 : &lt;jsp:getProperty name=&quot;info&quot; property=&quot;age&quot;/&gt;
    키 : &lt;jsp:getProperty name=&quot;info&quot; property=&quot;height&quot;/&gt;
    몸무게 : &lt;jsp:getProperty name=&quot;info&quot; property=&quot;weight&quot;/&gt;
    성별 : &lt;jsp:getProperty name=&quot;info&quot; property=&quot;gender&quot;/&gt;
    주소 : &lt;jsp:getProperty name=&quot;info&quot; property=&quot;addr&quot;/&gt;
&lt;/h1&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/ea651abf-606a-42f7-94d7-36d1a130011f/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/ff0fc24f-730b-431e-9c79-c41cb65c3129/image.png" alt=""></p>
<h2 id="jspinclude"><a href="jsp:include">jsp:include</a></h2>
<p>현재 페이지에서 다른 페이지를 포함하면서 포함 된 페이지에 파라미터를 전달할 수 있는 액션 태그입니다. 주로 <a href="jsp:param">jsp:param</a>과 자주 쓰이며 해당 include로 지정되어 파라미터를 전달 받은 페이지 에서는 해당 파라미터를 활용하여 사용하거나 출력할 수 있습니다.</p>
<p>특히 이 액션 태그는 include 지시어와 동일하다는 특징과, 데이터를 전달할 수 있고 없고의 차이점 또한 존재합니다. </p>
<blockquote>
</blockquote>
<ol>
<li>JSP 페이지 1<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;h1&gt;페이지 1 입니다.&lt;/h1&gt;
&gt;
 &lt;br&gt;
&gt;   
 &lt;!-- Ex01_include1.jsp 페이지에 Ex01_include2.jsp 페이지를 포함 --&gt;
&gt;
 &lt;!-- 1. include 지시어 --&gt;
 &lt;%@ include file=&quot;page2.jsp&quot; %&gt;
&gt;    
 &lt;!-- 2. include 액션 태그 --&gt;
 &lt;jsp:include page=&quot;page2.jsp&quot;&gt;
     &lt;jsp:param name=&quot;name&quot; value=&quot;Re_Go&quot;/&gt;
     &lt;jsp:param name=&quot;age&quot; value=&quot;30&quot;/&gt;
     &lt;jsp:param name=&quot;addr&quot; value=&quot;서울&quot;/&gt;
 &lt;/jsp:include&gt;
&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>JSP 페이지 2<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
 &lt;%-- params 액션 태그에 의해 전달 받은 파라미터들을 활용 가능 --%&gt;
 request.setCharacterEncoding(&quot;utf-8&quot;);
 String name = request.getParameter(&quot;name&quot;);
 String age = request.getParameter(&quot;age&quot;);
 String addr = request.getParameter(&quot;addr&quot;);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;h1&gt;include2.jsp 페이지&lt;/h1&gt;
 &lt;div&gt;
     이름 : &lt;%=name %&gt;
     나이 : &lt;%=age %&gt;
     주소 : &lt;%=addr %&gt;
 &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/8bdfa417-5859-48fd-8b07-d5e222fc5176/image.png" alt=""></li>
</ol>
<h2 id="jspforward"><a href="jsp:forward">jsp:forward</a></h2>
<p>JSP 페이지에서 다른 JSP 페이지 또는 서블릿으로 요청을 포워딩하는 데 사용됩니다. 요청을 포워딩하면, 현재 페이지의 처리가 종료되고 요청이 다른 페이지로 전달됩니다. 사용법은 include 태그와 동일하며,</p>
<p>단일 태그로 사용할 경우 기존에 요청 받은 정보들을 타겟 페이지로 전달하고 종료하지만, 이중 태그로 사용하여 param 태그와 같이 사용할 경우 기존의 요청 정보 + 새로운 파라미터 값을 추가하여 타겟 페이지에 전달할 수 있습니다.</p>
<blockquote>
</blockquote>
<p>JSP 페이지 1</p>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;% 
request.setCharacterEncoding(&quot;utf-8&quot;);
response.setContentType(&quot;text/html; charset=UTF-8&quot;);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;페이지 1 입니다.&lt;/h1&gt;
    &lt;br&gt;
    &lt;jsp:forward page=&quot;page2.jsp&quot;&gt;
        &lt;jsp:param name=&quot;name&quot; value=&quot;김씨&quot;/&gt;
        &lt;jsp:param name=&quot;age&quot; value=&quot;20&quot;/&gt;
        &lt;jsp:param name=&quot;addr&quot; value=&quot;서울&quot;/&gt;
    &lt;/jsp:forward&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p>JSP 페이지2</p>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
    request.setCharacterEncoding(&quot;utf-8&quot;);
    String name = request.getParameter(&quot;name&quot;);
    String age = request.getParameter(&quot;age&quot;);
    String addr = request.getParameter(&quot;addr&quot;);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;페이지 2 입니다.&lt;/h1&gt;
    &lt;div&gt;
        이름 : &lt;%=name %&gt;
        나이 : &lt;%=age %&gt;
        주소 : &lt;%=addr %&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/a5309c57-961e-4352-9e53-54983121a4ee/image.png" alt=""></p>
<h2 id="jspparam"><a href="jsp:param">jsp:param</a></h2>
<p>include나 forward 액션 태그에 속성과 값으로써 문자열 형식으로 전달하는 액션 태그로, 타겟 페이지에 추가적인 정보를 제공하고자 할 때 사용됩니다. (include 및 forward 액션 태그 설명 참조)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] 스크립틀릿의 새로운 대체제, EL 표기법]]></title>
            <link>https://velog.io/@re_go/JSPServlet-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8B%80%EB%A6%BF%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%8C%80%EC%B2%B4%EC%A0%9C-EL-%ED%91%9C%EA%B8%B0%EB%B2%95</link>
            <guid>https://velog.io/@re_go/JSPServlet-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8B%80%EB%A6%BF%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%8C%80%EC%B2%B4%EC%A0%9C-EL-%ED%91%9C%EA%B8%B0%EB%B2%95</guid>
            <pubDate>Tue, 06 Aug 2024 09:08:59 GMT</pubDate>
            <description><![CDATA[<h1 id="1-기존-스크립틀릿과의-작성-차이점">1. 기존 스크립틀릿과의 작성 차이점</h1>
<p>EL(Expression Language)는 JSP에서 사용하는 새로운 스크립트 언어로서 기존의 복잡했던 자바 코드 표현 체계인 스크립틀릿을 대체하기 위해 만들어졌는데요. 이러한 EL은 쉽게 사용자의 입력 데이터를 가져오고, 연산하여 출력하는 역할을 스크립틀릿보다 더 쉽게 할 수 있는데요.</p>
<p>왜 그런지를 이 둘의 차이점을 알아봅시다. 다음과 같은 코드가 있다고 칩시다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
 &lt;%
 int total = 0;
 for(int i = 1 ; i &lt;= 100 ; i++){
     total += i;
 }
 pageContext.setAttribute(&quot;TOTAL&quot;, total);
 %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>위 코드에는 1부터 100까지를 더해서 body에 출력하려는 코드인데요. 기존의 스크립틀릿 언어로 작성한다면 어떻게 해야할까요?</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;p&gt;&lt;%=total%&gt;&lt;/p&gt;
&lt;p&gt;&lt;%=pageContext.getAttribute(&quot;TOTAL&quot;)%&gt;&lt;/p&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/caac1777-cca1-4eae-948f-ecef80033b54/image.png" alt=""></p>
<p>그리고 그럼 위의 코드를 대신해 EL 표기법으로 코드를 작성해 보면 다음과 같이 작성할 수 있답니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;p&gt;${TOTAL}&lt;/p&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/384a58de-a870-4bc0-8b5f-89d3b705f64d/image.png" alt=""></p>
<p>흠... 이렇게 봐서는 차이점을 느끼지 못하시겠다고요? 좋습니다. 그럼 다음 코드를 한 번 살펴보도록 하죠.</p>
<h1 id="2-코드의-간결화">2. 코드의 간결화</h1>
<p>다음과 같이 사용자의 주문을 받는 특정 화면이 있다고 생각해 봅시다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form action=&quot;showOrder.jsp&quot;&gt;
        &lt;h1&gt;배다른 민족&lt;/h1&gt;
        &lt;h3&gt; 주문자 &lt;input type = &quot;text&quot; name = &quot;id&quot;&gt; &lt;/h3&gt;
        &lt;input type = &quot;checkbox&quot; name = &quot;order&quot; value=&quot;하와이안피자&quot;&gt;하와이안피자
        &lt;input type = &quot;checkbox&quot; name = &quot;order&quot; value=&quot;불고기피자&quot;&gt;불고기피자
        &lt;input type = &quot;checkbox&quot; name = &quot;order&quot; value=&quot;허니콤보치즈피자&quot;&gt;허니콤보치즈피자
        &lt;input type = &quot;checkbox&quot; name = &quot;order&quot; value=&quot;이태리안핫소스피자&quot;&gt;이태리안핫소스피자
        &lt;input type = &quot;checkbox&quot; name = &quot;order&quot; value=&quot;쉬림프바질피자&quot;&gt;쉬림프바질피자
     &lt;p&gt;&lt;button&gt;주문하기&lt;/button&gt;&lt;/p&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/24567855-6804-45da-bccb-add65f62f9e5/image.png" alt=""></p>
<p>그럼 개발자는 사용자의 주문(요청)을 받아서 처리를 해줘야겠죠?</p>
<blockquote>
</blockquote>
<pre><code class="language-java">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&gt;
&lt;% 
    String name = request.getParameter(&quot;name&quot;);
    String[] orders = request.getParameterValues(&quot;order&quot;);
&gt;
    pageContext.setAttribute(&quot;orders&quot;, orders);
%&gt;    
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    주문자 : &lt;%= name %&gt; &lt;br&gt;
    주문하신 음식 : &lt;%= String.join(&quot; &quot;, (String[]) pageContext.getAttribute(&quot;orders&quot;)) %&gt; 
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/3480f64f-3d03-4494-8b5f-f804b62f67ad/image.png" alt=""></p>
<p>음... 꽤 복잡해 보이죠? 그래서 이 코드를 EL 표기법으로 한 번 바꿔보면 다음과 같이 작성할 수 있답니다!</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&gt;
&gt;
&lt;% 
    String name = request.getParameter(&quot;name&quot;);
    String[] orders = request.getParameterValues(&quot;order&quot;);
&gt;
    request.setAttribute(&quot;name&quot;, name);
    request.setAttribute(&quot;orders&quot;, orders);
%&gt;    
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    주문자 : ${name} &lt;br&gt;
    주문하신 음식 : 
    &lt;c:forEach var=&quot;order&quot; items=&quot;${orders}&quot;&gt;
        ${order} 
    &lt;/c:forEach&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h1 id="3-el-작성의-조건">3. EL 작성의 조건</h1>
<p>이처럼 기존의 스크립틀릿을 대체하여 코드를 보다 간결하게 작성할 수 있도록 해주는 EL 표기법에는 그 조건이 따르는데요. 그 조건은 바로 정보 저장 객체들(pageContext, request, session, application) 중에 하나의 속성으로서 저장되어야 사용이 가능하다는 것입니다. 바로 위의 코드를 살펴보면</p>
<blockquote>
</blockquote>
<pre><code class="language-java">request.setAttribute(&quot;name1&quot;, value1);
request.setAttribute(&quot;name2&quot;, value2);</code></pre>
<p>이렇게 되어있는 코드가 보이시죠? 이것이 바로 request 객체에 속성들로 name라는 속성과 그 값(name)을, orders라는 속성과 그 값(orders)을 넣어준게 바로 이 코드였던거죠. </p>
<p>참고로 정보 저장 객체의 저장 및 유효 범위는 해당 <a href="https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BFJSP%EC%9D%98-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EA%B3%BC-%EC%A0%95%EB%B3%B4-%EC%A0%84%EB%8B%AC-%EA%B8%B0%EC%88%A0-sendRedirect-Forword-Session#5-%EC%B6%94%EA%B0%80--session%EA%B3%BC-redirection-%EC%A1%B0%ED%95%A9">링크를 참조</a>해 주시기 바랍니다! </p>
<p>아무튼 이렇게 속성으로서 그 값이 저장이 되어 있어야 사용이 가능한 EL이지만, 물론 사용자가 전달한 파라미터 값, 즉 전달한 값이 있다면 EL로 표기가 가능한데요.  바로 직전에 주문 페이지에서 사용자가 요청한 정보가 request, 즉 파라미터 값으로 들어온다는 특성을 이용해 param과 paramvalues 객체를 다음과 같이 이용하면 되죠!</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    주문자 : ${param.name}&lt;br&gt;
    주문하신 음식 : ${paramValues.order[0]} ${paramValues.order[1]} ${paramValues.order[2]} 
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/629a7699-84a5-4dee-a217-e5cfd33e7643/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/14684f2c-9cc2-40a8-ad58-51c0f91e6f9f/image.png" alt=""></p>
<p>물론 앞전에서 출력 홈페이지에 pageContext로 속성을 설정해 준거라 해당 페이지에서 스크립틀릿을 쓰긴 했지만, 이전 페이지에서 request나 session로 속성을 설정해 줬다면 위의 코드와 같이 굳이 스크립틀릿 태그로 속성을 설정해줄 이유도 없겠지요.</p>
<p>이처럼 EL로 변수들에 접근할 때에는 파라미터의 경우 param(단수)와 paramValues(복수), 혹은 속성으로 설정한 경우 그냥 속성명을 적어주면 되는데요. 물론 속성의 경우 각 정보 저장 객체가 동일한 속성의 이름으로 저장을 했다면 그건 또 문제가 될 수 있겠죠? 그래서 다음 예제를 한 번 살펴보도록 하겠습니다.</p>
<h1 id="4-속성-중복-이름의-방지-scope">4. 속성 중복 이름의 방지, Scope</h1>
<p>스코프는 말 그대로 어떠한 정보 저장 객체에 해당 속성이 있는지를 구별할 때 사용하는 객체로, 정보 저장 객체인 pageContext, request, session, application에게 각각 부여되어 있으며, 사용법은 접미사로 Scope 라는 단어를 작성하면 됩니다. </p>
<p>좀 더 쉬운 이해를 위해 다음 예시 코드를 한 번 살펴볼까요?</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&gt;
&lt;% 
    // 요청 파라미터에서 값 가져오기
    String name = request.getParameter(&quot;name&quot;);
    String order = request.getParameter(&quot;order&quot;);
&gt;
    // 세션과 요청에 속성 설정
    session.setAttribute(&quot;name&quot;, name);
    session.setAttribute(&quot;orders&quot;, order);
&gt;   
    request.setAttribute(&quot;name&quot;, &quot;진상 손님&quot;);
    request.setAttribute(&quot;orders&quot;, &quot;경찰을 불러서 퇴치를 했다!&quot;);
%&gt;    </code></pre>
<p>위 코드에서는 request와 session의 속성으로 각각 같은 이름에 다른 값을 부여했는데요. 그래서 일반적인 접근 방법(그냥 속성의 이름을 쓰는 것)으로 접근한다면 다음과 같이 출력될 것입니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">주문자 : ${name} &lt;br&gt;
주문하신 음식 : ${orders}</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/019f698f-987f-4046-9132-7c5850e44e0e/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/d59c14c7-d178-4555-8792-7b2d89e6bc95/image.png" alt=""></p>
<p>분명 주문자 명으로 하와이안피자를 불렀는데 나타난 정보는 다음과 같이 전혀 딴판인 정보가 나왔죠? 그 이유는 그냥 속성명만 지정할 경우 페이지에서부터 가장 가까운 정보 저장 객체의 속성을 접근하기 때문에 나타난 현상인데요. 이 페이지의 경우 pageContext는 설정되어 있지 않기 때문에 request에 설정된 각 속성들이 출력이 된 것이죠. </p>
<p>그래서 이런 상황을 대비하기 위해 스코프를 사용하면 다음과 같이 정상적으로 코드가 출력되는 모습을 확인하실 수 있으실 겁니다. </p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;!-- 세션 스코프의 속성 접근 --&gt;
    &lt;h2&gt;Session Scope&lt;/h2&gt;
    주문자 : ${sessionScope.name} &lt;br&gt;
    주문하신 음식 : ${sessionScope.orders}
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/6f370507-b9a7-40f1-974c-1a1a8465bede/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] 서블릿 실행 전 정보 처리를 지원하는 필터(Filter) 기능간단 개요]]></title>
            <link>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%8B%A4%ED%96%89-%EC%A0%84-%EC%A0%95%EB%B3%B4-%EC%B2%98%EB%A6%AC%EB%A5%BC-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94-%ED%95%84%ED%84%B0Filter-%EA%B8%B0%EB%8A%A5%EA%B0%84%EB%8B%A8-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%8B%A4%ED%96%89-%EC%A0%84-%EC%A0%95%EB%B3%B4-%EC%B2%98%EB%A6%AC%EB%A5%BC-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94-%ED%95%84%ED%84%B0Filter-%EA%B8%B0%EB%8A%A5%EA%B0%84%EB%8B%A8-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Mon, 05 Aug 2024 12:12:37 GMT</pubDate>
            <description><![CDATA[<h1 id="1-filter란">1. Filter란?</h1>
<p>Filter란 클라이언트(Client)에서 서버(Server)로 요청이 들어오기 전 특정 서블릿을 데이터를 정제하거나 분기를 정하는 등의 목적으로 만들고자 할 때 사용하는 추상 인터페이스로, 클라이언트의 요청을 서버가 받아 메인 서블릿(컨테이너)에 넘기기 전 단계라고 보시면 됩니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/099f933c-5f0b-4b08-a48a-3ab8b27d71f8/image.jpg" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://wch18735.github.io/jsp/JSP_Servlet_Filter/">https://wch18735.github.io/jsp/JSP_Servlet_Filter/</a>)</p>
<p>이러한 필터는 주로 인터넷 뱅킹 사이트 같은 곳에서 민감한 개인 정보를 다루기 위해 사용자에게 추가적인 인증을 별도로 요구하는 용도로, 혹은 사용자가 입력한 로그인 값이 올바른지를 대조하는대에 사용되는 용도로 사용됩니다.</p>
<h1 id="2-filter의-용도--초기값-사용">2. Filter의 용도 : 초기값 사용</h1>
<p>앞서 살펴본 <a href="https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BFJSP%EC%9D%98-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EA%B3%BC-%EC%A0%95%EB%B3%B4-%EC%A0%84%EB%8B%AC-%EA%B8%B0%EC%88%A0-sendRedirect-Forword-Session">리다이렉션</a> 섹션에서 혹시 눈치 채신 분들이 계실수도 있으실텐데요. 저는 그 섹션에서 session을 이용한 정보 전달과 리다이렉션 조합 부분에서 인코딩 세팅 작업을 하지 않고도 sub2 페이지에서 올바른 정보들을 출력할 수 있었습니다. </p>
<blockquote>
</blockquote>
<ol>
<li>sub1.jsp<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
 String name = request.getParameter(&quot;name&quot;);
 String age = request.getParameter(&quot;age&quot;);
&gt;   
 session = request.getSession();
&gt;    
 session.setAttribute(&quot;name&quot;, name);
 session.setAttribute(&quot;age&quot;, age);
&gt;
 response.sendRedirect(&quot;sub2.jsp&quot;);  
%&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>sub2.jsp<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;h1&gt;사용자 정보&lt;/h1&gt;
 &lt;p&gt;name : ${name} &lt;/p&gt;
 &lt;p&gt;age : ${age} &lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/35f91d29-26bd-4051-9c67-b2134ca1aa2c/image.png" alt=""></li>
</ol>
<p>어떻게 가능했던 걸까요? 네. 그렇습니다. filter를 구현하고 그 구현 정보를 web.xml에 등록했기에 가능한 일이였죠.</p>
<p>우선 web.xml에 해당 filter의 정보를 등록하기 위해 다음과 같은 태그 목록들을 web.xml에 삽입해 줍니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-xml">&lt;!-- 필터의 기본적인 정보 --&gt;
&lt;filter&gt;
  &lt;!-- 필터 이름 --&gt;
  &lt;filter-name&gt;EncodingFilter&lt;/filter-name&gt;
  &lt;!-- 필터의 위치 --&gt;
  &lt;filter-class&gt;com.practice.test.EncodingFilter&lt;/filter-class&gt;
  &lt;!-- init 메서드에 사용할 filter 초기 객체의 변수와  값들--&gt;
  &lt;init-param&gt;
    &lt;!-- request.setCharacterEncoding 설정에 사용할 값 설정--&gt;
    &lt;param-name&gt;encoding&lt;/param-name&gt;
    &lt;param-value&gt;utf-8&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
    &lt;!-- response.setContentType 설정에 사용할 값 설정--&gt;
    &lt;param-name&gt;contentType&lt;/param-name&gt;
    &lt;param-value&gt;text/html; charset=UTF=8&lt;/param-value&gt;
  &lt;/init-param&gt;
&lt;/filter&gt;
&gt;
&lt;!-- 해당 필터가 영향을 끼치는 url의 범위를 설정, 참고로 아스터리스크(*)를 표기하면 모든 범위의 서블릿과 JSP 페이지로 이동할 때마다 해당 filter가 적용됨. --&gt;
&lt;filter-mapping&gt;
  &lt;filter-name&gt;EncodingFilter&lt;/filter-name&gt;
  &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;</code></pre>
<p>그 다음 작업으로는 web.xml에 등록했던 filter의 정보를 토대로 다음과 같이 filter를 구현하는 클래스를 아래와 같이 작성해야 하는데요.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">package com.practice.test;
&gt;
import java.io.IOException;
&gt;
import javax.servlet.Filter;
&gt;
public class EncodingFilter implements Filter{
&gt;    
}</code></pre>
<p>filter는 추상 인터페이스이기 때문에 인터페이스를 구현하는 쪽에서 해당 추상 메서드들을 구현해 주어야 하는데요. 보통은 init, doFilter, destroy 메서드를 구현합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">public class EncodingFilter implements Filter{
&gt;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
&gt;
    }
&gt;
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
&gt;
    }
    @Override
    public void destroy() {}
}</code></pre>
<p>그 다음 web.xml에서 정의한 filter 초기 객체의 값들을 사용하기 위해 필드로 다음 코드를 선언한 뒤 생성된 필드들에 init 메서드에 해당 파라미터 값들을 할당해 줍니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">private String encoding;
private String contentType;
&gt;
@Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // encoding 파라미터의 값은 utf-8 이었음
        encoding = filterConfig.getInitParameter(&quot;encoding&quot;);
        // contentType 파라미터의 값은 text/html; charset=UTF=8 이었음
        contentType = filterConfig.getInitParameter(&quot;contentType&quot;);
    }</code></pre>
<p>그 다음 doFilter 메서드에 각 객체로부터 문자 인코딩 방식과 컨텐츠 타입 인코딩 방식을 가져와 null일 경우, 그러니까 set 메서드로 설정을 해주지 않은 경우 파라미터 값들을 할당 받은 encoding과 contentType 변수의 값으로 각각 set 메서드를 호출한 뒤 할당해 줍니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">if(request.getCharacterEncoding() == null) request.setCharacterEncoding(encoding);
if(response.getContentType() == null) response.setContentType(contentType);
// doFilter가 실행 되었다는 것을 확인하기 위한 콘솔 출력문
System.out.println(&quot;필터 초기화 완료&quot;);
// doFilter 작업이 끝난 뒤 request와 response 정보를 그제야 서블릿이나 JSP 페이지로 제어 전달
chain.doFilter(request, response);</code></pre>
<p>그럼 다음과 같이 페이지를 이동할 때마다 filter가 발동 되어 다음과 같은 문구가 콘솔에 출력되는걸 확인할 수 있습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/bda9da70-391d-4dbe-a517-c8a26e7285fa/image.png" alt=""></p>
<h1 id="3-filter의-용도--로그인-유무-확인">3. Filter의 용도 : 로그인 유무 확인</h1>
<p>앞서 살펴본 <a href="https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BFJSP%EC%9D%98-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EA%B3%BC-%EC%A0%95%EB%B3%B4-%EC%A0%84%EB%8B%AC-%EA%B8%B0%EC%88%A0-sendRedirect-Forword-Session">리다이렉션</a> 색션에서 활용한 세션과 리다이렉션의 예제를 활용하여 사용자가 입력한 값 중 하나가 없을 경우 자연스럽게 로그인 창으로 리다이렉션 하는 코드를 만들어 볼겁니다. </p>
<p>우선 사용자에게 보여질 메인 화면을 다음과 같이 만듭니다. 이때 경로는 서버에 요청을 보낼 것이고, 해당 서블릿들은 @WebServlet 어노테이션으로 각자 매핑이 되어있기 때문에 절대경로로 해당 어노테이션 경로를 지정해 줍니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;ul&gt;
        &lt;li&gt;&lt;a href=&quot;showUserInfo&quot;&gt;유저 정보 확인&lt;/a&gt;&lt;/li&gt;    
        &lt;li&gt;&lt;a href=&quot;moveLoginPage&quot;&gt;로그인 화면&lt;/a&gt;&lt;/li&gt;    
    &lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>이제 서블릿을 만들어줄 차례인데요. 서블릿은 경로대로 showUserInfo 하나와 moveLoginPage 서블릿을 각각 만들어 줍니다. 이 서블릿들은 말 그대로 서버에서 다른 페이지로 리다이렉트 시켜주려는 목적의 페이지들이죠.</p>
<blockquote>
</blockquote>
<p>1.showUserInfo 페이지</p>
<pre><code class="language-java">package com.practice.test;
&gt;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
&gt;
@WebServlet(&quot;/showUserInfo&quot;)
public class showUserInfo extends HttpServlet {
    private static final long serialVersionUID = 1L;
&gt;
    public showUserInfo() {
        super();
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendRedirect(&quot;/project/showUserInfo.jsp&quot;);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
&gt;
}</code></pre>
<blockquote>
</blockquote>
<ol start="2">
<li>moveLoginPage<pre><code class="language-java">package com.practice.test;
&gt;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
&gt;
@WebServlet(&quot;/moveLoginPage&quot;)
public class moveLoginPage extends HttpServlet {
 private static final long serialVersionUID = 1L;
&gt;
 public moveLoginPage() {
     super();
 }
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     response.sendRedirect(&quot;/project/loginForm.jsp&quot;);
 }
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     doGet(request, response);
 }
&gt;
}</code></pre>
</li>
</ol>
<p>서블릿들도 만들어 줬으니 이제 해당 서블릿들이 보내고자 하는 페이지도 만들어 줘야겠죠? showUserInfo와 loginform jsp를 만들어 줍니다.</p>
<blockquote>
</blockquote>
<ol>
<li>showUserInfo<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 id : ${id}
 password : ${pw} 
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>loginForm<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;form action=&quot;login&quot;&gt;
     &lt;p&gt; 아이디 : &lt;input type = &quot;text&quot; name = &quot;id&quot;&gt; &lt;/p&gt; 
     &lt;p&gt; 비밀번호 : &lt;input type = &quot;password&quot; name = &quot;pw&quot;&gt; &lt;/p&gt; 
     &lt;input type=&quot;submit&quot; value = &quot;가입하기&quot; &gt;
     &lt;input type=&quot;reset&quot; value = &quot;초기화&quot; &gt;
 &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</li>
</ol>
<p>이제 loginForm을 만들어 줬으니 form 정보를 보내줄 서블릿을 또 하나 만들어 보도록 하겠습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>login<pre><code class="language-java">package com.practice.test;
&gt;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
&gt;
@WebServlet(&quot;/login&quot;)
public class login extends HttpServlet {
 private static final long serialVersionUID = 1L;
&gt;   
 public login() {
     super();
 }
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
&gt;        
     response.setContentType(&quot;text/html; charset=utf-8&quot;);
     String id = request.getParameter(&quot;id&quot;);
     String pw = request.getParameter(&quot;pw&quot;);
     System.out.println(id);
     System.out.println(pw);
     HttpSession session = request.getSession();
&gt;        
     session.setAttribute(&quot;id&quot;, id);
     session.setAttribute(&quot;pw&quot;, pw);
&gt;        
     response.sendRedirect(&quot;/project/showUserInfo.jsp&quot;);
&gt;
 }
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     doGet(request, response);
 }
}</code></pre>
</li>
</ol>
<p>겉으로 보기에는 완성되어 보이는 듯 하군요. 근데 문제가 있습니다. 로그인 폼이라고 한다면 둘 중 하나의 값이 입력된 경우와, 아무것도 입력을 안한 경우 로그인을 하도록 해야하는데, 그 처리를 안해줬습니다. </p>
<p>그래서 우리는 이 처리를 filter를 이용해서 진행을 해볼겁니다. </p>
<p>우선 filter를 하나 만들어 보도록 하겠습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">package com.practice.filter;
&gt;
import java.io.IOException;
&gt;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
&gt;
@WebFilter(&quot;&quot;)
public class LoginFilter implements Filter{
    @Override
    public void init(FilterConfig filterConfig) 
            throws ServletException {}
&gt;    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
&gt;
    }
    @Override
    public void destroy() {}
}</code></pre>
<p>우리가 해야할 작업은 우선 사용자가 넣은 값, 즉 입력한 값이 잘 들어왔는지를 확인하는 법입니다. 그럴려면 login에서 사용자가 입력한 값을 세션에 저장한 값을 사용해야겠죠?</p>
<blockquote>
</blockquote>
<pre><code class="language-java">HttpSession session = request.getSession();</code></pre>
<p>앗! 근데 문제가 있습니다. filter의 doFilter 부분을 잘 보시면 request 부분은 ServletRequest 클래스 타입인걸 확인할 수 있는데요. getSession 메서드는 HTTPServletRequest 클래스 타입에게 있는 전용 메서드로, 부모인 ServletRequest 클래스에는 해당 메서드가 없어서 사용을 못합니다. </p>
<p>그래서 우리는 doFilter의 request를 다음과 같이 다운그레이드 시켜줄겁니다. 그럼 무사히 다운 그레이드 된 인스턴스로부터 getSession 메서드를 호출해 사용자가 입력한 값을 가져올 수 있겠죠.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">HttpServletRequest req = (HttpServletRequest)request;
HttpSession session = req.getSession();</code></pre>
<p>이제 우리가 해야할건 사용자가 값을 하나라도 안 넣은 경우 로그인 창을 다시 이동하도록 하는 작업입니다. 그럴려면 다음과 같은 조건이 필요하겠죠?</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// 모든 속성을 가져올때는 타입이 오브젝트 이므로 적절한 형변환이 필요함
String id = (String)session.getAttribute(&quot;id&quot;);
String pw = (String)session.getAttribute(&quot;pw&quot;);
&gt;
if( (id == null || pw == null) || (id == &quot;&quot; || pw == &quot;&quot;) ){
    //request와 마찬가지로 response도 다운그레이드
    HttpServletResponse rep = (HttpServletResponse)response;
    // 현재 웹 어플리케이션의 컨텍스트패스인 project를 반환 받고, 그 바로 밑의 loginForm으로 돌아가 올바르게 입력하도록 함
    String cPath = req.getContextPath();
    rep.sendRedirect(cPath + &quot;/loginForm.jsp&quot;);
}else{
    // 올바르게 입력한 경우라면 그대로 서블릿을 진행 
    chain.doFilter(request, response);
}</code></pre>
<p>자, 이제 가장 중요한 WebFilter가 남았습니다. WebFilter는 해당 서블릿이 실행하기 전에 거쳐가도록 하는 경로를 지정하는데요. 우린 이 필터를
showUserInfo 서블릿에 걸어줄 겁니다. 그래야 사용자가 값을 입력한 뒤에 서블릿에 의해 showUserInfo에 진입하기 전에 filter를 걸어줘 만약 둘 중 하나의 값이라도 없는 경우 다시 로그인을 하게끔 만들고, </p>
<p>두 값을 다 입력했다면 그대로 서블릿을 다시 재개하게끔 만들어야 하니까요. 그래서 showUserInfo의 어노테이션에 다음과 같은 매핑 주소를 추가해 줍니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">@WebServlet(&quot;/private/showUserInfo&quot;)</code></pre>
<p>어차피 각각의 고유의 절대 경로를 가질 수 있기 때문에 가능한 작업이죠. 그리고 WebFilter의 주소도 다음과 같이 작성을 해줍니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">@WebFilter(&quot;/private/*&quot;)</code></pre>
<p>이 의미는, 절대 경로로 private이 붙은 다음의 모든 경로에 있는 파일들에 이 filter를 적용하겠다는 의미입니다. 그래서 showUserInfo 또한 해당 filter를 거치게 되는거죠. 자 이제 그럼 진짜 시연을 해볼까요?</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/28401188-2f02-4d55-8a0d-ac21b489930a/image.gif" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/e3cf0df0-6757-430a-8769-bdfd50b2c621/image.gif" alt=""></p>
<blockquote>
</blockquote>
<p>시연을 하고 보니 약간 아쉬운건 있지만 그래도 의도 대로 작동되는군요...?</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] 서블릿&JSP의 리다이렉션과 정보 전달 기술 (sendRedirect, Forword, Session)]]></title>
            <link>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BFJSP%EC%9D%98-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EA%B3%BC-%EC%A0%95%EB%B3%B4-%EC%A0%84%EB%8B%AC-%EA%B8%B0%EC%88%A0-sendRedirect-Forword-Session</link>
            <guid>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BFJSP%EC%9D%98-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EA%B3%BC-%EC%A0%95%EB%B3%B4-%EC%A0%84%EB%8B%AC-%EA%B8%B0%EC%88%A0-sendRedirect-Forword-Session</guid>
            <pubDate>Mon, 05 Aug 2024 09:19:18 GMT</pubDate>
            <description><![CDATA[<h1 id="1-리다이렉션-기술의-중요성">1. 리다이렉션 기술의 중요성</h1>
<p>데이터 처리를 클라이언트 측에서 요청받은 후 서버 측에서 처리하고, 클라이언트에게 다시 페이지를 로드하거나 새로운 페이지로 리다이렉트하는 것입니다. 이는 서버측에서 데이터를 처리한 후 처리 결과를 클라이언트에게 다른 페이지로 보여주는 역할이나, 혹은 사용자가 새로고침을 눌렀을 때 동일한 정보가 서버로 요청되는 불필요한 작업을 방지하고자 하는 역할에 주로 사용되는데요.</p>
<p>이러한 리다이렉션은 크게 리다이렉션을 실행할 때 정보가 유지되느냐, 유지되지 않느냐의 차이를 두면서 두 가지 메서도르 나뉘는데요. 그것이 바로 RequestDispatcher 객체의 forward 메서드와, HttpServletRequest 객체의 sendRedirect 메서드입니다.</p>
<h1 id="2-동작-원리-간단-요약">2. 동작 원리 간단 요약</h1>
<p>request의 정보는 클라이언트의 요청과 그 요청을 받은 JSP 페이지 혹은 서블릿 서버에서만 유효합니다. 그래서 해당 페이지나 서버에서 사용자의 정보를 처리한 뒤 그 결과를 사용자에게 응답으로 보내게 되죠. 물론 편법으로 URL에 GET 방식 처럼 사용자의 요청 값들을 묶어 보내는 방법도 있지만 좀 비효율적일 수 밖에 없겠죠.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/a7af389c-1780-4251-b693-ddb8d5aae815/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://mangkyu.tistory.com/51">https://mangkyu.tistory.com/51</a>)</p>
<p>하지만 forward의 경우 reuqest 객체에 사용자의 요청 값들을 저장한 뒤 dispatcher에 forward 메서드로 보내고자 하는 페이지에 request와 response를 보내는 방식으로 처리가 가능하기 때문에 정리하자면 정보를 별도의 인코딩 과정 없이 원형으로 보내는 것이 가능합니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/6be9b3e0-ab44-4bf3-96d6-65f86a673768/image.png" alt=""></p>
<h1 id="3-sendredirect로-request-정보-전송하기">3. sendRedirect로 request 정보 전송하기</h1>
<p>해당 메서드를 사용하기에 앞서, 다음과 같은 JSP 구조가 있다고 생각해 봅시다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/1b3eeea2-3856-4706-8d51-1ea0ceb1e60a/image.png" alt=""></p>
<p>main.jsp에서는 사용자가 입력을 하여 요청을 하게 되는 구조이고요.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Request 정보 전달 해보기&lt;/h1&gt;
    &lt;form action=&quot;sub1.jsp&quot; method = &quot;get&quot;&gt;
        &lt;label&gt;이름 : &lt;input type = &quot;text&quot; name = &quot;name&quot;&gt;&lt;/label&gt;
        &lt;label&gt;나이 : &lt;input type = &quot;number&quot; name = &quot;age&quot;&gt;&lt;/label&gt;
        &lt;input type=&quot;submit&quot; value=&quot;제출&quot;&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/9acace93-1c12-4eb5-8407-c95358e685a1/image.png" alt=""></p>
<p>이 정보를 sub1.jsp가 받아서 sub2.jsp로 리다이렉트 하는 코드로 작성되었습니다. 이때 sendRedirect로 전달할 때는 페이지의 URL을 전송해야 하기 때문에 get 방식으로 조합을 해 sub2.jsp 페이지와 정보를 함께 묶은 url을 전송해야 하는데요.</p>
<p>거기에 한자나 한글과 같은 비아스키 코드 언어는 전달 받는 쪽에서 제대로 식별을 하지 못하기 때문에 URLEncoder 객체의 메서드로 인코딩 처리를 해주어야 하는 불편함도 존재하죠.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@page import=&quot;java.net.URLEncoder&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
    String name = request.getParameter(&quot;name&quot;);
    String age = request.getParameter(&quot;age&quot;);
&gt;
    /*
        db에 저장하거나, 섹션에 저장하는 용도로 작업 수행
    */
&gt;   
    // 조합을 해보면 url은 &quot;sub2.jsp?name=Re_Go&amp;age=30&quot; 이 됩니다.
    String redirectURL = &quot;sub2.jsp?name=&quot; + URLEncoder.encode(name, &quot;UTF-8&quot;) + &quot;&amp;age=&quot; + age;
    response.sendRedirect(redirectURL);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>그렇게 어찌어찌 전달 받은 sub2.jsp는 해당 정보를 이용해 사용자의 정보를 다음 코드를 통해 출력하게 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
    String name = request.getParameter(&quot;name&quot;);
    int age = Integer.parseInt(request.getParameter(&quot;age&quot;));
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;정보가 정상적으로 저장되었습니다.&lt;/h1&gt;
    &lt;p&gt;사용자님의 아이디는 &lt;%=name%&gt; 입니다.&lt;/p&gt;
    &lt;p&gt;사용자님의 나이는 &lt;%=age%&gt; 입니다.&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/4f7ba194-47ca-4fc9-a0c1-9c1da3669dbe/image.png" alt=""></p>
<p>이러한 데이터 전송 방식은 정보를 클라이언트와 단순히 페이지를 보여주는 서버가 직접적으로 주고 받는 1:1 방식에는 유용하나, 서버가 페이지를 보여주는 것이 아닌, 서버 본연의 역할 (db나 다른 객체를 만드는 작업)을 수행하는 경우 매우 비효율적이기 때문에, 이럴 경우 사용할 수 있는 옵션이 바로 RequestDispatcher 객체의 메서드인 forward 것이죠.</p>
<h1 id="4-forward로-request-정보-전송하기">4. forward로 request 정보 전송하기</h1>
<p>RequestDispatcher 객체의 메서드인 forward로 정보를 전송하게 되면 POST 방식에서도 효과적으로 정보를 전송할 수 있는데요. 사용자에게 입력 받는 http 요청 양식을 post 방식으로 지정했다고 하고 다음과 같이 sub1.jsp의 코드를 다음과 같이 작성해 보겠습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
    request.setCharacterEncoding(&quot;UTF-8&quot;);
    String name = (String) pageContext.getAttribute(&quot;name&quot;);
    int age = Integer.parseInt((String) pageContext.getAttribute(&quot;age&quot;));
&gt;
    // RequestDispatcher를 사용하여 sub2.jsp로 요청을 포워딩
    RequestDispatcher dispatcher = request.getRequestDispatcher(&quot;sub2.jsp&quot;);
    request.setAttribute(&quot;name&quot;, name);
    request.setAttribute(&quot;age&quot;, age);
    dispatcher.forward(request, response);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/f7b2c0b2-6f5f-4956-a3b2-6bed3aaf14ca/image.png" alt=""></p>
<p>어떻습니까? request의 sendRedirect 메서드보다 간결하고 더 깔끔하게 데이터를 보낼 수 있지 않습니까? 하지만 이러한 데이터 전송도 결국 request의 정보 범위 (하나의 요청에만 유효)의 한계 때문에 사용자로부터 받은 중요한 데이터들은 결국 session에 보관을 하여 sendRedirect와 함꼐 사용하는 편이 좀 더 나은 선택지이며, 기본 검색과 같은 역할에는 request(pageContext)를 주로 사용하게 됩니다.</p>
<p>참고로 위의 코드처럼 전송할 때 포워딩을 이용하는 방법도 존재하는데, 이때 request 객체에 속성과 값을 설정했다면, 전달 받은 쪽에서는 굳이 getAttribute 메서드를 쓰지 않고도 EL 표기법으로 아래와 같이 접근이 가능합니다.</p>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;사용자 정보&lt;/h1&gt;
    &lt;p&gt;name : ${name} &lt;/p&gt;
    &lt;p&gt;age : ${age} &lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h1 id="5-추가--session과-redirection-조합">5. 추가 : session과 redirection 조합</h1>
<p>session은 앞서 소개한 session 섹션에서도 말씀 드렸듯이, 고객의 주요 정보를 유효 기간 내에 저장하는 용도로 사용한다고 했는데요. 그래서 주요 정보는 보통 session으로 저장을 하는 작업이 대부분인 만큼 사용자의 정보를 받은 뒤 session에 저장한 후 filter 작업을 거쳐 제공 세션과 입력 값이 같을 경우와 다를 경우를 각각 처리하는 용도로 사용이 가능합니다.</p>
<p>특히 session을 사용하면 get이든 post 방식이든 get 방식의 쿼리 스트림을 사용하여 정보를 전송하는대에 국한 되어있던 sendRedirection 방법을 좀 더 유연하게 사용할 수 있게 되기에, session과 sendRedirection 조합은 궁합이 잘 맞는 페이지 정보 전달 기술이라고 할 수 있겠습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
    String name = request.getParameter(&quot;name&quot;);
    String age = request.getParameter(&quot;age&quot;);
&gt;    
    session = request.getSession();
&gt;   
    session.setAttribute(&quot;name&quot;, name);
    session.setAttribute(&quot;age&quot;, age);
&gt;    
    response.sendRedirect(&quot;sub2.jsp&quot;);
&gt;
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/07c27a15-0f6e-497c-90f5-630e1431143f/image.png" alt=""></p>
<p>특히 request의 범위는 클라이언트의 1회 요청에만 유효한 반면, session은 정해진 유효기간 내라면 어플리케이션 내에서 어느 페이지든 유효하기에 그 쓰임새가 더 넓다고 할 수 있겠죠. </p>
<p>참고로 EL (${})로 접근할때에 각 객체의 속성 이름이 중복되는 경우 접근 우선 순위는 페이지 컨텍스트 =&gt; request =&gt; session =&gt; application 순이라고 합니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/619a9377-fa6c-4fce-90c3-7d457b93ce96/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/3f204164-996a-40d9-af40-c2f7a82656c9/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://velog.io/@ggg4155/22.03.03-%EC%84%B8%EC%85%98-%EC%A0%95%EB%B3%B4-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0">https://velog.io/@ggg4155/22.03.03-%EC%84%B8%EC%85%98-%EC%A0%95%EB%B3%B4-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</a>)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] 서블릿의 상태 저장 및 유지 객체들 : Session
]]></title>
            <link>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%98-%EC%83%81%ED%83%9C-%EC%A0%80%EC%9E%A5-%EB%B0%8F-%EC%9C%A0%EC%A7%80-%EA%B0%9D%EC%B2%B4%EB%93%A4-session</link>
            <guid>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%98-%EC%83%81%ED%83%9C-%EC%A0%80%EC%9E%A5-%EB%B0%8F-%EC%9C%A0%EC%A7%80-%EA%B0%9D%EC%B2%B4%EB%93%A4-session</guid>
            <pubDate>Thu, 01 Aug 2024 14:58:21 GMT</pubDate>
            <description><![CDATA[<h1 id="1-인터넷에서의-세션의-역할">1. 인터넷에서의 세션의 역할</h1>
<p>서블릿에서 세션(Session)은 클라이언트와 서버 간의 상태 정보를 유지하기 위한 중요한 개념이라고 하는데요.</p>
<p>그 이유는 웹 애플리케이션에서 클라이언트와 서버 간의 상호작용은 보통 HTTP 프로토콜을 통해 이루어지는데, HTTP는 기본적으로 상태가 없는(stateless) 프로토콜입니다. 이는 각 요청이 독립적이며 이전 요청과의 연결이 없다는 것을 의미합니다. 이를 두고 무상태성, 비상태성이라고도 하는데요.</p>
<blockquote>
</blockquote>
<center>프로토콜의 무상태성을 잘 설명한 예시</center>
>
![](https://velog.velcdn.com/images/re_go/post/6372396e-d110-48e2-a34f-69286e9bf4b2/image.png)
>
(자료 출처 : https://velog.io/@beneficial/Session%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80)

<p>세션은 이러한 상태 비유지의 문제를 해결하기 위해 사용되는데요. 서버는 클라이언트가 처음 요청을 보낼 때 새로운 세션을 생성하고, 이를 식별하기 위한 고유한 세션 ID를 부여합니다. 이 세션 ID는 클라이언트에게 쿠키를 통해 전달됩니다. 이후 클라이언트는 서버에 요청을 보낼 때마다 이 쿠키에 세션 ID를 포함하여 보냅니다.</p>
<p>그럼 서버는 해당 쿠키에서 세션 ID를 대조해 세션 별로 할당 된 메모리 적재 공간에 사용자의 정보를 저장하여 정제 및 활용한 뒤 사용자에게 응답을 하게 되는 방식으로 활용되는 것이죠.</p>
<blockquote>
</blockquote>
<center>쿠키와 세션의 연결성을 잘 보여준 예시</center>
>
![](https://velog.velcdn.com/images/re_go/post/a97f06f1-8a8a-4771-88d3-82d52c4dd3cb/image.png)
>
![](https://velog.velcdn.com/images/re_go/post/52c4d4aa-c27c-4732-b098-a65d3bb264db/image.png)
>
(자료 출처 : https://velog.io/@hyun6ik/%EC%84%B8%EC%85%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D)

<h1 id="2-서블릿에서의-세션과-메서드들">2. 서블릿에서의 세션과 메서드들</h1>
<p>물론 서블릿에서도 쿠키를 사용 가능하기에 당연히 세션도 사용 가능한데요. 이러한 세션은 HttpSession 객체에 의해 관리되며, HttpServletRequest 인스턴스를 이용해 세션의 정보를 획득할 수 있게 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">HttpSession session = request.getSession();</code></pre>
<p>그럼 해당 세션 객체가 제공하는 대표적인 메서드들을 한 번 볼까요?</p>
<h2 id="getsession">getSession()</h2>
<p>현재 요청과 연관된 세션을 반환합니다. 세션이 없다면 세션을 새로 생성하는데요. 이때 매개변수로 true, false를 넣게 되면 그 의미는 좀 달라지는데, </p>
<p>true를 전달하게 되면 기존 세션이 있다면 기존 세션을 반환하고, 없으면 새로운 세션을 생성하는 반면 </p>
<p>false를 전달하게 되면 기존 세션이 있다면 기존 세션을 반환하고, 없으면 null을 반환하게 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// 세션을 확인해 null(없는 상태) 일 경우 새로운 세션을 생성
HttpSession session = request.getSession(false);
if (session == null) {
    session = setSession(&quot;newSession&quot;, &quot;SessionValue&quot;)
    session.setAttribute(name, value);
}</code></pre>
<h2 id="isnew">isNew()</h2>
<p>세션이 새로 생성된 것인지 기존에 존재하던 것인지 확인합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">String checked = session.isNew() ? &quot;새로 생성&quot; : &quot;기존의 것&quot;</code></pre>
<h2 id="getcreationtime-getlastaccessedtime">getCreationTime(), getLastAccessedTime()</h2>
<p>세션이 처음 생성된 시간과 마지막으로 엑세스된 시간을 반환합니다. 이때 반환되는 시간은 타임스탬프 형식이므로 적당한 포맷 작업이 필요합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// 각각의 세션 시간들을 Date 객체로 포맷
long creationTime = new Date(session.getCreationTime());
long lastAccessedTime = new Date(session.getLastAccessedTime());</code></pre>
<h2 id="invalidate">invalidate()</h2>
<p>현재 세션을 무효화하여 모든 세션 데이터를 삭제합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">session.invalidate();</code></pre>
<h2 id="setattributestring-name-object-value">setAttribute(String name, Object value)</h2>
<p>세션에 이름-값 쌍으로 속성을 저장합니다. 이때 세션의 속성 중 이름은 반드시 문자열이여야 하나, 값의 경우 다양한 데이터 타입을 허용합니다.</p>
<p>또한 이미 있는 속성의 이름을 지정하여 값을 변경한 상태로 넣을 경우, 해당 속성의 값만 바뀐채 재정의 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// session에 해당 속성의 이름과 값을 지정하여 생성
session.setAttribute(&quot;attributeName&quot;, 500);
// 이미 있는 속성에 값을 변경하여 재정의
session.setAttribute(&quot;attributeName&quot;, 1500);</code></pre>
<h2 id="getattributestring-name">getAttribute(String name)</h2>
<p>세션에 저장된 특정 속성의 값을 가져옵니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// attributeName 속성의 값을 가져옴
session.getAttribute(&quot;attributeName&quot;);</code></pre>
<h2 id="getattributenames">getAttributeNames()</h2>
<p>세션에 저장된 모든 속성의 이름을 Enumeration 객체로 반환합니다. 이때 Enumeration은 열거형 타입이므로 hasMoreElements 메서드와 연계하여 사용할 수 있으며, 한 번 사용된 경우 해당 타입의 변수로 재사용이 불가능합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// 해당 메서드를 이용해 이름과 속성의 이름과 값을 얻어옴
Enumeration&lt;String&gt; attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()) {
    String name = attributeNames.nextElement();
    Object value = session.getAttribute(name);
    // 속성 이름과 값을 사용
}</code></pre>
<h2 id="removeattributestring-name">removeAttribute(String name)</h2>
<p>세션에 저장된 특정 속성을 제거합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// attributeName 속성을 session에서 제거
session.removeAttribute(&quot;attributeName&quot;);</code></pre>
<h1 id="3-실습-예제---세션을-이용한-모의-crud">3. 실습 예제 - 세션을 이용한 모의 CRUD</h1>
<p>위에서 소개해드린 세션 메서드들을 이용해 세션을 이용해서 CRUD 프로그램을 한 번 만들어 봤는데요. 다소 짧은 시간에 만든 예제라 엉성하긴 하지만, 그래도 세션을 이렇게도 활영할 수 있다는 것을 보여드리고자 간단하게 설명을 드려보도록 하겠습니다.</p>
<p>각각의 폼들은 세션 생성, 삭제, 속성 생성, 삭제, 속성 조회 다섯가지 폼으로 구성했으며, 각 영역 당 input의 hidden 속성을 이용해 고유의 선택값들을 넣어줬습니다. 이 값들을 서블릿의 switch문의 분기로 활용할 거고요.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Request Form&lt;/title&gt;
    &lt;style&gt;
        body { font-family: Arial, sans-serif; margin: 20px; }
        label { display: block; margin-bottom: 10px; }
        input, button { margin-top: 5px; }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;세션 명령을 입력해 주세요&lt;/h1&gt;
    &lt;h3&gt;1.세션 생성 2.세션 삭제&lt;/h3&gt;
&gt;
    &lt;!-- 세션 명령 폼 --&gt;
    &lt;form action=&quot;SessionServer&quot; method=&quot;get&quot;&gt;
        &lt;label for=&quot;choice&quot;&gt;명령어 선택:&lt;/label&gt;
        &lt;input type=&quot;text&quot; id=&quot;choice&quot; name=&quot;choice&quot; placeholder=&quot;명령어 입력&quot;&gt;
        &lt;input type=&quot;submit&quot; value=&quot;전송&quot;&gt;
    &lt;/form&gt;
&gt;
    &lt;br /&gt;
&gt;
    &lt;!-- 속성 추가 폼 --&gt;
    &lt;h3&gt;속성 추가&lt;/h3&gt;
    &lt;form action=&quot;SessionServer&quot; method=&quot;get&quot;&gt;
        &lt;input type=&quot;hidden&quot; name=&quot;choice&quot; value=&quot;3&quot;&gt;
        &lt;label for=&quot;itemName&quot;&gt;속성 이름:&lt;/label&gt;
        &lt;input type=&quot;text&quot; id=&quot;itemName&quot; name=&quot;name&quot; placeholder=&quot;속성 이름&quot;&gt;
        &lt;label for=&quot;itemValue&quot;&gt;속성 값:&lt;/label&gt;
        &lt;input type=&quot;text&quot; id=&quot;itemValue&quot; name=&quot;value&quot; placeholder=&quot;속성 값&quot;&gt;
        &lt;input type=&quot;submit&quot; value=&quot;속성 추가&quot;&gt;
    &lt;/form&gt;
&gt;
    &lt;br /&gt;
&gt;
    &lt;!-- 속성 제거 폼 --&gt;
    &lt;h3&gt;속성 제거&lt;/h3&gt;
    &lt;form action=&quot;SessionServer&quot; method=&quot;get&quot;&gt;
        &lt;input type=&quot;hidden&quot; name=&quot;choice&quot; value=&quot;4&quot;&gt;
        &lt;label for=&quot;removeName&quot;&gt;제거할 속성 이름:&lt;/label&gt;
        &lt;input type=&quot;text&quot; id=&quot;removeName&quot; name=&quot;name&quot; placeholder=&quot;속성 이름&quot;&gt;
        &lt;input type=&quot;submit&quot; value=&quot;속성 제거&quot;&gt;
    &lt;/form&gt;
&gt;
    &lt;br /&gt;
&gt;
    &lt;!-- 속성 조회 폼 --&gt;
    &lt;h3&gt;속성 조회&lt;/h3&gt;
    &lt;form action=&quot;SessionServer&quot; method=&quot;get&quot;&gt;
        &lt;input type=&quot;hidden&quot; name=&quot;choice&quot; value=&quot;5&quot;&gt;
        &lt;label for=&quot;getName&quot;&gt;조회할 속성 이름:&lt;/label&gt;
        &lt;input type=&quot;text&quot; id=&quot;getName&quot; name=&quot;name&quot; placeholder=&quot;속성 이름&quot;&gt;
        &lt;input type=&quot;submit&quot; value=&quot;속성 조회&quot;&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>다음은 인코딩 설정과 choice 파라미터를 가져오고, 세션 객체 하나와 데이터 포맷 양식 제공을 위한 simpleDateFormat 타입의 인스턴스를 하나 생성해 줍니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">res.setContentType(&quot;text/html;charset=UTF-8&quot;);
req.setCharacterEncoding(&quot;UTF-8&quot;);
PrintWriter out = res.getWriter();
int choice = Integer.parseInt(req.getParameter(&quot;choice&quot;));
HttpSession session = null;
SimpleDateFormat formatTime = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);</code></pre>
<p>switch문의 첫번째 구간입니다. getsession과 isNew 조합을 이용해 있을 경우 해당 세션을 생성 (값이 비었을 경우 true가 default 값이므로)하고, 기존 세션 반환된다면 아래 문구를 출력후 각각의 생성 시간과 마지막 섹션 엑세스 시간을 출력합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">case 1:
    // 세션을 생성하거나 기존 세션을 가져옵니다.
    session = req.getSession();
    if (session.isNew()) {
        out.print(&quot;세션이 새로 생성되었습니다.&quot;);
    } else {
        out.print(&quot;기존 세션이 존재합니다.&quot;);
    }
    out.print(&quot;&lt;p&gt;생성 시간 : &quot; + formatTime.format(new Date(session.getCreationTime())) + &quot;&lt;/p&gt;&quot;);
    out.print(&quot;&lt;p&gt;마지막 접속 시간 : &quot; + formatTime.format(new Date(session.getLastAccessedTime())) + &quot;&lt;/p&gt;&quot;);
    break;</code></pre>
<p>두번째 구간입니다. 기존의 세션을 가져오되 false 값을 전달해 만약 null을 반환 받지 않을 경우, 즉 기존의 세션을 가져올 경우 해당 세션의 생성 시간과 마지막 접속 시간을 보여준 뒤 삭제하고, null, 즉 이미 없는 상태라면 섹션이 없다고 출력합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">case 2:
    // 기존 세션을 가져옵니다 (존재하지 않으면 null).
    session = req.getSession(false);
    if (session != null) {
        out.print(&quot;&lt;p&gt;생성 시간 : &quot; + formatTime.format(new Date(session.getCreationTime())) + &quot;&lt;/p&gt;&quot;);
        out.print(&quot;&lt;p&gt;마지막 접속 시간 : &quot; + formatTime.format(new Date(session.getLastAccessedTime())) + &quot;&lt;/p&gt;&quot;);
        session.invalidate(); // 세션을 무효화합니다.
        out.print(&quot;세션이 삭제되었습니다.&quot;);
    } else {
        out.print(&quot;삭제할 세션이 존재하지 않습니다.&quot;);
    }
    break;</code></pre>
<p>세번째 구간입니다. 사용자가 폼에 세션의 속성 이름과 값을 입력하면 각각 그 값들이 넘어올텐데요. 두 값이 모두 입력된 경우에 setAttribute로 아이템을 추가하고, 두 값 중 하나가 빠진 경우라면 주의 문구를 출력합니다.</p>
<p>그리고 attributeNames 메서드를 이용해 속성의 이름과, 이름을 이용해 getAttribute 메서드로 값을 함께 보여줍니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">case 3:
    // 속성 값을 추가합니다.
    session = req.getSession();
    String itemName = req.getParameter(&quot;name&quot;);
    String itemValue = req.getParameter(&quot;value&quot;);
&gt;
    if (itemName != null &amp;&amp; itemValue != null) {
        session.setAttribute(itemName, itemValue);
        out.print(&quot;속성이 추가되었습니다.&quot;);
    } else {
        out.print(&quot;속성 이름과 값을 제공해야 합니다.&quot;);
    }
&gt;
    // 추가된 속성 출력
    out.print(&quot;&lt;p&gt;속성 목록 : &quot;);
    Enumeration&lt;String&gt; attributeNames1 = session.getAttributeNames();
    while (attributeNames1.hasMoreElements()) {
        String name = attributeNames1.nextElement();
        out.print(&quot;&lt;li&gt;&quot; + name + &quot;: &quot; + session.getAttribute(name) + &quot;&lt;/li&gt;&quot;);
    }
    out.print(&quot;&lt;/p&gt;&quot;);
    break;</code></pre>
<p>네번째 구간입니다. 세번째 구간에서의 설명과 동일합니다. 메서드 이름만 다르고 원리는 거의 비슷하기 때문이죠.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">case 4:
    // 속성 값을 제거합니다.
    session = req.getSession();
    String attributeName = req.getParameter(&quot;name&quot;);
&gt;
    if (attributeName != null) {
        session.removeAttribute(attributeName);
        out.print(&quot;속성이 제거되었습니다.&quot;);
    } else {
        out.print(&quot;제거할 속성 이름을 제공해야 합니다.&quot;);
    }
&gt;
    out.print(&quot;&lt;p&gt;속성 목록 : &quot;);
    Enumeration&lt;String&gt; attributeNames2 = session.getAttributeNames();
    while (attributeNames2.hasMoreElements()) {
        String name = attributeNames2.nextElement();
        out.print(&quot;&lt;li&gt;&quot; + name + &quot;: &quot; + session.getAttribute(name) + &quot;&lt;/li&gt;&quot;);
    }
    out.print(&quot;&lt;/p&gt;&quot;);
    break;</code></pre>
<p>다섯 번째 구간입니다. 세션을 가져온 후 사용자로부터 찾을 속성의 값을 받아온 뒤 그 값을 이용해 속성을 받아옵니다. 이때 전달되는 타입은 오브젝트 이므로 string으로 변환한 뒤 해당 속성의 값을 보여줍니다. </p>
<p>만약 존재하지 않는 속성의 이름일 경우 else문과 같이 출력해 주고 열거형으로 속성 목록을 보여줍니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">case 5:
    // 속성 값을 조회합니다.
    session = req.getSession();
    String queryName = req.getParameter(&quot;name&quot;);
&gt;
    if (queryName != null) {
        Object value = session.getAttribute(queryName);
        if (value != null) {
            out.print(&quot;&lt;p&gt;속성 이름 &#39;&quot; + queryName + &quot;&#39;의 값: &quot; + value.toString() + &quot;&lt;/p&gt;&quot;);
        } else {
            out.print(&quot;&lt;p&gt;속성 이름 &#39;&quot; + queryName + &quot;&#39;은 존재하지 않습니다.&lt;/p&gt;&quot;);
        }
    } else {
        out.print(&quot;조회할 속성 이름을 제공해야 합니다.&quot;);
    }
    out.print(&quot;&lt;p&gt;속성 목록 : &quot;);
    Enumeration&lt;String&gt; attributeNames3 = session.getAttributeNames();
    while (attributeNames3.hasMoreElements()) {
        String name = attributeNames3.nextElement();
        out.print(&quot;&lt;li&gt;&quot; + name + &quot;: &quot; + session.getAttribute(name) + &quot;&lt;/li&gt;&quot;);
    }
    out.print(&quot;&lt;/p&gt;&quot;);
    break;</code></pre>
<h2 id="시연-영상">시연 영상</h2>
<p>만들고보니 삭제 예외 처리를 안해줬네요 ㅎ...</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/95e516ea-697c-49aa-b720-e8a10b24c5bc/image.gif" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/8bb25a75-d15c-42ed-abdd-f8cc91c05ae9/image.gif" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/aacc667c-dabe-40d3-a7b8-6ade44b22966/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] JSP 페이지와 스크립트 기반 태그들 ]]></title>
            <link>https://velog.io/@re_go/JSPServlet-JSP-%ED%8E%98%EC%9D%B4%EC%A7%80%EC%99%80-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EB%B0%98-%ED%83%9C%EA%B7%B8%EB%93%A4</link>
            <guid>https://velog.io/@re_go/JSPServlet-JSP-%ED%8E%98%EC%9D%B4%EC%A7%80%EC%99%80-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EB%B0%98-%ED%83%9C%EA%B7%B8%EB%93%A4</guid>
            <pubDate>Thu, 01 Aug 2024 09:43:31 GMT</pubDate>
            <description><![CDATA[<h1 id="jsp란">JSP란?</h1>
<p>JSP(JavaServer Pages)는 웹 개발에서 서버 측에서 동적으로 컨텐츠를 생성하기 위해 사용되는 기술입니다. JSP는 HTML에 Java 코드를 포함시켜 웹 페이지를 작성할 수 있게 되는데요. 쉽게 얘기하면 페이지에 자바 코드를 사용할 수 있는 페이지가  JSP인 셈이죠.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%--1. JAVA 코드 영역--%&gt;
&lt;%@page import=&quot;java.text.SimpleDateFormat&quot;%&gt;
&lt;%@page import=&quot;java.util.Date&quot;%&gt;
&lt;%@page import=&quot;java.util.Calendar&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&gt;
&lt;!--2. HTML 코드 영역 --%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form action=&quot;&quot; method = &quot;get&quot;&gt;
    &lt;fieldset&gt;
        &lt;legend&gt;개인 정보 전송&lt;/legend&gt;
        &lt;p&gt;이름 &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;&lt;/p&gt;
        &lt;p&gt;나이 &lt;input type=&quot;text&quot; name=&quot;age&quot;&gt;&lt;/p&gt;
        &lt;p&gt;
            반려동물(
                &lt;input type=&quot;checkbox&quot; name=&quot;pet&quot; value=&quot;강아지&quot;&gt;강아지
                &lt;input type=&quot;checkbox&quot; name=&quot;pet&quot; value=&quot;고양이&quot;&gt;고양이
                &lt;input type=&quot;checkbox&quot; name=&quot;pet&quot; value=&quot;돼지&quot;&gt;돼지
            )
        &lt;/p&gt;
        &lt;p&gt;&lt;textarea rows=&quot;5&quot; cols=&quot;30&quot; name=&quot;info&quot;&gt;&lt;/textarea&gt;&lt;/p&gt;
        &lt;input type=&quot;button&quot; value=&quot;전송&quot; onclick=send(this.form)&gt;
    &lt;/fieldset&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
    function send(f){
        f.action = &#39;Ex03_02_receive.jsp&#39;;
        f.submit();
    }
&lt;/script&gt;
&lt;/html&gt;</code></pre>
<h1 id="스크립트-기반-태그">스크립트 기반 태그</h1>
<p>이러한 JSP 페이지는 스크립트 기반 태그를 이용해 자바의 선언문, 표현식등의 다양한 자바 전용 스크립트 태그를 페이지 내에 삽입하여 페이지에 동적인 기능 및 UI를 생성하는 것이 가능한데요. 이러한 자바 코드를 JSP 페이지에서 사용할 수 있게 해주는 태그를 스크팁트 기반 태그라고 하면, 해당 태그들의 종류들은 다음과 같습니다.</p>
<h2 id="지시어directive--">지시어(Directive) &lt;%@ %&gt;</h2>
<p>주로 해당 JSP 페이지의 설정 정보를 제공하거나 페이지 속성을 정의할 때 사용됩니다. 주로 JSP 페이지의 최상단에 위치해 있습니다. 지시어의 속성은 다양한데요. 우선 page 지시어의 속성들부터, 그러니까 가장 많이 쓰이니는 지시어인 page의 속성들을 소개해 드리도록 하겠습니다.</p>
<p>다음은 page 지시어에서 주로 쓰이는 속성들입니다.</p>
<blockquote>
</blockquote>
<h3 id="language">language</h3>
<p>스크립트 코드에서 사용되는 언어를 지정합니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; %&gt;         </code></pre>
<blockquote>
</blockquote>
<h3 id="contenttype">contentType</h3>
<p>컨텐츠 타입의 형식과 인코딩 방식을 지정합니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page contentType=&quot;text/html; charset=UTF-8&quot; %&gt;</code></pre>
<blockquote>
</blockquote>
<h3 id="pageencoding">pageEncoding</h3>
<p>JSP 파일 자체의 문자 인코딩을 지정합니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-JSP">&lt;%@ page pageEncoding=&quot;UTF-8&quot; %&gt;</code></pre>
<blockquote>
</blockquote>
<h3 id="import">import</h3>
<p>JSP 페이지 내에서 사용할 자바클래스나 라이브러리를 임포트 하는 용도로 사용합니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-JSP">&lt;%@ page import=&quot;java.util.Calendar&quot; %&gt;</code></pre>
<blockquote>
</blockquote>
<h3 id="session">session</h3>
<p>해당 페이지 내에서 세션 객체를 사용할 것인지에 대한 여부를 지정하는데 사용됩니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-JSP">&lt;%@ page session=&quot;true&quot; %&gt;</code></pre>
<blockquote>
</blockquote>
<h3 id="buffer">buffer</h3>
<p>콘텐츠를 클라이언트에게 전달하기 전에 메모리 내에서 임시로 저장하는 과정을 가지는데요. 이 메모리의 버퍼(크기)를 정할 때 사용합니다. 보통 기본값은 8kb 이며 웹페이지의 규모에 따라 64kb까지 버퍼를 설정하기도 합니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-JSP">&lt;%@ page buffer=&quot;8KB&quot; %&gt;</code></pre>
<blockquote>
</blockquote>
<h3 id="autoflush">autoFlush</h3>
<p>보통 버퍼가 가득 차면 자동으로 컨텐츠를 클라이언트에게 출력(응답) 하게 되는데요. 그 말인 즉 버퍼가 차지 않을 경우 클라이언트에게 출력을 하지 않는다는 의미인데, autoFlush 속성을 true로 설정하면 버퍼가 다 차지 않아도 자동으로 클라이언트에게 컨텐츠를 응답하게 됩니다. </p>
<blockquote>
</blockquote>
<p>기본적으로 이 속성은 false로 설정되어 있기 때문에 만약 메모리 효율을 높이고 싶을 경우  true로 설정하면 되나, 응답 속도를 느리게 하는데 영향을 줄 수 있기 때문에 상황에 따라 설정값을 주어야 하는 속성입니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-JSP">&lt;%@ page autoFlush=&quot;true&quot; %&gt;</code></pre>
<blockquote>
</blockquote>
<h3 id="errorpage">errorPage</h3>
<p>에러가 발생하면 호출할 오류 전용 페이지의 위치를 지정하는데 사용됩니다. </p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-JSP">&lt;%@ page errorPage=&quot;이동할 위치&quot; %&gt;</code></pre>
<blockquote>
</blockquote>
<h3 id="iserrorpage">isErrorPage</h3>
<p>해당 JSP 페이지가 오류 처리 페이지임을 명시하는데 사용됩니다. 이곳에서는 주로 예외 처리 과정을 거쳐 도출된 결과를 사용자에게 적절하게 보여주는 역할의 페이지로 사용됩니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-JSP">&lt;%@ page isErrorPage=&quot;false&quot;%&gt;    </code></pre>
<blockquote>
</blockquote>
<p>다음은 JSP 페이지 내의 최상단에 위치하는 지시어의 예시입니다.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%@ page 
    language=&quot;java&quot;                    &lt;%-- JSP 페이지에서 사용할 프로그래밍 언어를 설정합니다. 기본값은 &quot;java&quot;입니다. --%&gt;
    contentType=&quot;text/html; charset=UTF-8&quot; &lt;%-- 페이지의 콘텐츠 타입과 문자 인코딩을 설정합니다. HTML과 UTF-8 인코딩을 사용합니다. --%&gt;
    pageEncoding=&quot;UTF-8&quot;               &lt;%-- JSP 페이지 소스 파일의 문자 인코딩을 설정합니다. 여기서는 UTF-8을 사용합니다. --%&gt;
    import=&quot;java.util.*, java.text.*&quot; &lt;%-- JSP 페이지에서 사용할 Java 클래스들을 임포트합니다. --%&gt;
    session=&quot;false&quot;                    &lt;%-- 이 JSP 페이지가 세션을 사용하지 않도록 설정합니다. --%&gt;
    isThreadSafe=&quot;true&quot;                &lt;%-- 페이지가 스레드 안전하다고 설정합니다. 기본값은 true이며, 페이지가 동시 요청을 안전하게 처리할 수 있음을 의미합니다. --%&gt;
    buffer=&quot;8kb&quot;                       &lt;%-- 페이지의 버퍼 크기를 설정합니다. 여기서는 8KB로 설정합니다. --%&gt;
    autoFlush=&quot;true&quot;                  &lt;%-- 버퍼가 가득 차면 자동으로 클라이언트에게 내용을 플러시하도록 설정합니다. --%&gt;
    errorPage=&quot;error.jsp&quot;              &lt;%-- 이 JSP 페이지에서 예외가 발생하면 &quot;error.jsp&quot;로 이동하여 예외를 처리하도록 설정합니다. --%&gt;
    isErrorPage=&quot;false&quot;               &lt;%-- 이 JSP 페이지가 오류 처리 페이지가 아님을 명시합니다. 이 속성은 주로 오류 처리 페이지에서 사용됩니다. --%&gt;
%&gt;</code></pre>
<blockquote>
</blockquote>
<h3 id="include-지시어">include 지시어</h3>
<blockquote>
</blockquote>
<p>include 지시어는 말 그대로 파일 내에 또 다른 JSP 파일의 HTML 영역을 삽입할 때 사용합니다. 예시로 해당 페에지의 헤더나 푸터로서 사용할 수 있기에 자주 사용되는 지시어이기도 하죠.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<ol>
<li>Date 정보를 담고 있는 JSP 페이지<pre><code class="language-jsp">&lt;%@page import=&quot;java.text.SimpleDateFormat&quot;%&gt;
&lt;%@page import=&quot;java.util.Date&quot;%&gt;
&lt;%@page import=&quot;java.util.Calendar&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%Calendar cal = Calendar.getInstance(); %&gt;
&lt;%=cal.get(Calendar.YEAR)%&gt;년
&lt;%=cal.get(Calendar.MONTH) %&gt;월
&lt;%=cal.get(Calendar.DATE) %&gt;년일
&lt;%Date today = new Date();
 SimpleDateFormat sdf = new SimpleDateFormat(&quot;HH시 mm분 ss초&quot;);     
%&gt;
&lt;%= sdf.format(today) %&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
</li>
<li>해당 Date 정보를 담고 있는 JSP 파일을 include 지시어로 포함시킨 페이지<pre><code class="language-jsp">&lt;%@page import=&quot;java.text.SimpleDateFormat&quot;%&gt;
&lt;%@page import=&quot;java.util.Date&quot;%&gt;
&lt;%@page import=&quot;java.util.Calendar&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
 pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;현재 상황 &lt;%@ include file = &quot;Date.jsp&quot; %&gt; 날씨는 무진장 화창합니다!&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/761f5635-6984-418e-929b-279a8ec5dc7a/image.png" alt=""></p>
</blockquote>
</li>
</ol>
<h2 id="스크립틀릿scriptlet--">스크립틀릿(Scriptlet) &lt;% %&gt;</h2>
<p>Java 코드를 JSP 페이지에 삽입할 때 사용됩니다. 쉽게 이야기해서 자바의 코드는 대부분 이곳에서 작성된다고 생각하시면 됩니다. </p>
<blockquote>
</blockquote>
<pre><code class="language-java">&lt;%
// 자바의 서블릿 객체 중 request(사용자 요청 정보 저장) 객체의 특정 인코딩 속성을 세팅
request.setCharacterEncoding(&quot;UTF-8&quot;);
// 각 변수들에 request의 특정 메서드(getter 메서드)를 이용해 사용자의 응답 정보를 추출하여 할당
String name = request.getParameter(&quot;name&quot;);
int age = Integer.parseInt(request.getParameter(&quot;age&quot;));
String[] pet = request.getParameterValues(&quot;pet&quot;);
String info = request.getParameter(&quot;info&quot;);
%&gt;</code></pre>
<h2 id="표현식-expression--">표현식 (Expression) &lt;%= %&gt;</h2>
<p>Java 표현식을 평가하고 결과를 출력할 때 사용됩니다. 주로 JSP 페이지 내에서 HTML 코드 내에 함께 사용하며, 스크립틀릿에 작성된 변수를 문서 내에 표현하고자 할 때 사용합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%-- HTML 코드 내에 자바의 변수들을 표현하기 위해 표현식을 사용한 모습 --%&gt;
&lt;li&gt;이름 :&lt;%= name %&gt;&lt;/li&gt;
&lt;li&gt;나이 :&lt;%= age %&gt;&lt;/li&gt;
&lt;li&gt;애완동물 :&lt;%= pet == null ? &quot;없음&quot; : Arrays.toString(pet) %&gt;&lt;/li&gt;
&lt;li&gt;자기소개 :&lt;%= info %&gt;&lt;/li&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/c9b24ef7-e0a2-4fe8-ac9b-5321a8c235f0/image.png" alt=""></p>
<h2 id="선언문-declaration--">선언문 (Declaration) &lt;%! %&gt;</h2>
<p>메서드나 변수를 선언할 때 사용되는 태그로, 선언문이 아니더라도 더욱 포괄적인 개념에서의 스크립틀릿 태그가 존재하기 때문에 잘 사용되지 않는 태그이기도 합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;%-- 사용자의 요청 중 name 속성이 name인 값을 얻어와 --%&gt;
&lt;%-- 해당 값의 상태에 따라 출력문을 다르게 하는 함수를 정의한 후 호출 결과를 표현식으로 사용 --%&gt;
&lt;%! 
    String name = request.getParameter(&quot;name&quot;);
    private String getGreeting(String name) {
        if (name == null || name.isEmpty()) {
            return &quot;Hello, Guest!&quot;;
        } else {
            return &quot;Hello, &quot; + name + &quot;!&quot;;
        }
    }
%&gt;
&lt;%= getGreeting(name) %&gt;</code></pre>
<h2 id="주석문-comment------">주석문 (comment) &lt;%-- --%&gt;</h2>
<p>주석문은 JSP 페이지 내에서 달 수 있는 설명글로, 그냥 편하게 주석이라고 생각하시면 되는데요. JSP 에서는 주석을 두 가지, 그러니까 HTML 주석과 JSP 주석을 달 수 있습니다.</p>
<p>공통점은 코드 실행에 영향을 주지 않는다는 점이지만, 차이점은 HTML 주석의 경우 페이지 소스 코드에서 보이는 반면, JSP 주석은 페이지 소스 코드에서도 보이지 않는다는 점을 들 수 있습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-jsp">&lt;!-- 이 부분은 HTML 주석입니다. 브라우저가 렌더링할 때 무시되지만, 소스 코드에는 표시됩니다. --&gt;
&lt;p&gt;이 부분은 HTML 주석입니다.&lt;/p&gt;
&gt;
&lt;%-- 이 부분은 JSP 주석입니다. 클라이언트에게 전달되지 않습니다. --%&gt;
&lt;p&gt;이 부분은 JSP 주석입니다.&lt;/p&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] 서블릿의 상태 저장 및 유지 객체들 : Cookie]]></title>
            <link>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%98-%EC%83%81%ED%83%9C-%EC%A0%80%EC%9E%A5-%EB%B0%8F-%EC%9C%A0%EC%A7%80-%EA%B0%9D%EC%B2%B4%EB%93%A4-Cookie-cwwmqqvd</link>
            <guid>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%98-%EC%83%81%ED%83%9C-%EC%A0%80%EC%9E%A5-%EB%B0%8F-%EC%9C%A0%EC%A7%80-%EA%B0%9D%EC%B2%B4%EB%93%A4-Cookie-cwwmqqvd</guid>
            <pubDate>Wed, 31 Jul 2024 14:02:24 GMT</pubDate>
            <description><![CDATA[<h1 id="1-cookie-객체와-관련-메서드들">1. Cookie 객체와 관련 메서드들</h1>
<p>서블릿에서도 쿠키를 추출하는 것이 가능한데요. 그 전에 쿠키에 대해서 모르시는 분들이 계시다면 <a href="https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4-HTTP-%EC%9A%94%EC%B2%AD-%EC%A0%95%EB%B3%B4-%ED%99%95%EC%9D%B8-%ED%95%B4%EB%B3%B4%EA%B8%B0#cookie">다음의 링크</a>에서 한 번 확인하시고 와보시는것을 추천드립니다. 아무튼 이러한 쿠키는 특히 사용자를 인증하는 정보로 주로 사용하고 있는 만큼 없어서는 안될 중요한 개념인데요. 이러한 쿠키는 서버가 사용자한테 지급을 해주어야 하는 만큼 세팅하는 메서드에 대해서도 알 필요가 있습니다. 이번 섹션에서는 그 사용법을 한 번 익혀보도록 하겠습니다.</p>
<h2 id="cookie">Cookie()</h2>
<p>Cookie 클래스는 클라이언트와 서버 간의 상태 정보를 저장하기 위해 사용됩니다. 클라이언트으ㅢ HTTP 응답에서 서버는 클라이언트에게 쿠키를 보내고, 클라이언트는 이후 요청에서 해당 쿠키를 서버로 다시 전송합니다. 그러면 서버는 클라이언트로부터 받은 요청에서 쿠키를 읽고, 이를 통해 클라이언트의 상태를 관리하거나 맞춤형 응답을 생성하는 용도로서 쿠키를 사용하게 되죠.</p>
<p>이러한 쿠키는 기본적으로 인스턴스를 생성하듯이 new 키워드와 함께 기입하면 되는데, 이때 첫번째 매개변수는 쿠키의 이름으로 오직 문자열 타입만 가능하고, 두번째는 쿠키의 값도 마찬가지로 문자열 타입만 저장할 수 있습니다.</p>
<p>또한 이때 쿠키의 이름은 처음 세팅할 때에만 지정 가능하고 수정은 불가능 하다는 점도 기억해야 합니다.</p>
<pre><code class="language-java">Cookie cookie1 = new Cookie(&quot;onlyStringCookieName&quot;, &quot;onlyStringCookieValue&quot;);</code></pre>
<blockquote>
</blockquote>
<center>이클립스에서 확인해보는 쿠키 클래스의 내부 구조</center>
>
![](https://velog.velcdn.com/images/re_go/post/1bbc1849-39a9-4b59-9a35-db119a74139a/image.png)

<h2 id="setmaxageint-expiry">setMaxAge(int expiry)</h2>
<p>쿠키가 해당 앱 어플리케이션에 남아있는 유효 기간을 설정합니다. 이때 설정하는 매개변수는 정수형이며, 초 단위로 설정이 가능합니다. 그래서 0을 전달하면 즉시 쿠키가 없어지고, 60을 전달하면 60초 동안 해당 쿠키가 유효한데요. </p>
<p>다만 음수를 설정하면 브라우저가 종료될 때 해당 쿠키를 삭제하게 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// cookie1의 유효 시간을 60초 * 60초, 즉 60분인 1시간 동안으로 설정함.
cookie1.setMaxAge(60*60)</code></pre>
<h2 id="setpathstring-uri">setPath(String uri)</h2>
<p>쿠키가 해당 어플리케이션의 특정 url에서 유효한지를 지정할 때 사용하는 메서드로, / 기호를 단독으로 사용할 경우 해당 어플리케이션 내에서는 어느 url이든 해당 쿠키가 유효하다는 의미입니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// cookie1의 유효 경로는 해당 어플리케이션의 모든 곳에서 유효함
cookie1.setPath(&quot;/&quot;)</code></pre>
<h2 id="setdomainstring-pattern">setDomain(String pattern)</h2>
<p>해당 쿠키가 다른 어플리케이션, 즉 다른 서버에서도 유효한 경우로 사용하고자 할 때 지정하는 메서드입니다. 예를 들어 (&quot;<a href="http://www.naver.com&quot;">www.naver.com&quot;</a>) 으로 지정했다면 해당 쿠키는 생성된 어플리케이션 뿐만 아니라 네이버, 즉 다른 서버에서도 유효한 쿠키가 됩니다. 물론 (&quot;ver.com&quot;) 으로 지정하면 도메인 중 ver.com 단어가 포함된 도메인에서도 해당 쿠키는 유효하게 됩니다.</p>
<p>그러나 이러한 행위는 쿠키 탈취, 혹은 노출과 같이 보안 상에 큰 타격을 줄 수 있기에 계열사 홈페이지와 같이 연관된 홈페이지가 아니라면 사용을 하지 않는 것을 권장합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// cookie1는 네이버 서버에서도 유효한 쿠키로 지정
cookie1.setDomaion(&quot;www.naver.com&quot;) </code></pre>
<h2 id="httpservletresponseaddcookiecookie-cookie">HttpServletResponse.addCookie(Cookie cookie)</h2>
<p>특정 시점에서 사용자에게 응답을 보낼 때 세팅 된 쿠키를 전송하는 용도로 사용합니다. </p>
<blockquote>
</blockquote>
<pre><code class="language-java">// 응답을 보낼 때 cookie1을 보냄
res.addCookie(cookie1);</code></pre>
<h2 id="httpservletrequestgetcookies">HttpServletRequest.getCookies()</h2>
<p>클라이언트가 전송한 모든 쿠키를 배열로 반환받습니다. 이때 쿠키의 유효기간이 지나 쿠키가 만료된 상태에서 해당 메서드로 요청을 할 때 반환된 배열이 null일 수도 있으므로, null 체크를 넣어줍시다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// 쿠키를 배열로 얻어옴
Cookie[] cookieList = req.getCookies();
// 쿠키를 임시로 만들어 넣을 용도
Cookie cookie1; 
    // 만약 얻어온 쿠키가 없는 상태라면 새로운 쿠키를 만들어 사용자에게 전송
    if (cookieList == null) {
         cookie1 = new Cookie(&quot;CookieName&quot;, &quot;CookieValue&quot;);
          res.addCookie(cookie1);
    }else {
        // 쿠키가 있다면 배열을 돌리며 쿠키를 출력
         for (Cookie cookie : cookieList) {
           out.print(&quot;&lt;p&gt;Cookie Name: &quot; + cookie.getName() + &quot;&lt;/p&gt;&quot;);
           out.print(&quot;&lt;p&gt;Cookie Value: &quot; + cookie.getValue() + &quot;&lt;/p&gt;&quot;);
       }
   }</code></pre>
<h2 id="getname">getName()</h2>
<p>얻어온 쿠키 배열 중에서 특정 쿠키의 이름을 반환받습니다. </p>
<h2 id="getvalue">getValue()</h2>
<p>얻어온 쿠키 배열 중에서 특정 쿠키의 값을 반환받습니다. </p>
<h1 id="2-페이지에-쿠키-출력-해보기">2. 페이지에 쿠키 출력 해보기</h1>
<h2 id="파일-생성">파일 생성</h2>
<p>그럼 지금까지 알아본 내용으로 페이지를 이동할 때마다 쿠키를 생성하고 출력해보는 예제를 만들어 보겠습니다. </p>
<p>우선 서블릿을 총 네 개를 만들어 줍니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/cadc61b1-90b2-4a4d-a4ab-d50f32f41d29/image.png" alt=""></p>
<h2 id="쿠키-생성">쿠키 생성</h2>
<p>메인 페이지에는 쿠키를 생성하지 않을거고, 다른 페이지를 이동할 때마다 쿠키를 생성해서 사용자에게 주도록 할것이므로 한 번 해당 코드를 만들어보도록 하겠습니다.</p>
<blockquote>
</blockquote>
<p>subServlet1의 쿠키 </p>
<pre><code class="language-java">Cookie user1 = new Cookie(&quot;user1&quot;, &quot;guest&quot;);
user1.setPath(&quot;/&quot;); // 쿠키가 웹 애플리케이션 전체에서 유효하도록 설정
user1.setMaxAge(60); // 60초 동안 유효
res.addCookie(user1);</code></pre>
<blockquote>
</blockquote>
<p>subServlet2의 쿠키 </p>
<pre><code class="language-java">Cookie user2 = new Cookie(&quot;user2&quot;, &quot;worker&quot;);
user2.setPath(&quot;/&quot;); // 쿠키가 웹 애플리케이션 전체에서 유효하도록 설정
user2.setMaxAge(60 * 60); // 1시간 동안 유효
res.addCookie(user2);</code></pre>
<blockquote>
</blockquote>
<p>subServlet3의 쿠키 </p>
<pre><code class="language-java">Cookie user3 = new Cookie(&quot;user3&quot;, &quot;operator&quot;);
user3.setPath(&quot;/&quot;); // 쿠키가 웹 애플리케이션 전체에서 유효하도록 설정
user3.setMaxAge(60 * 60 * 24); // 1일 동안 유효
res.addCookie(user3);</code></pre>
<h2 id="쿠키-얻어오기">쿠키 얻어오기</h2>
<p>자, 이제 사용자는 페이지를 방문할 때마다 쿠키를 받게 될텐데요. 받았으니 사용자로부터 한 번 받아봐야겠죠? 해당 코드도 만들어 줍시다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">&gt;
// 쿠키 받아오기
Cookie[] cookieList = req.getCookies();
&gt;
// 만약 쿠키가 남아있으면 다음과 같이 html 요소를 만들고 getName과 getValue로 화면에 표시하기
if (cookieList != null) {
      out.print(&quot;&lt;h4&gt;쿠키 목록:&lt;/h4&gt;&quot;);
      out.print(&quot;&lt;ul&gt;&quot;);
      for (Cookie cookie : cookieList) {
          out.print(&quot;&lt;li&gt;&quot;);
          out.print(&quot;쿠키 이름: &quot; + cookie.getName() + &quot;, &quot;);
          out.print(&quot;쿠키 값: &quot; + cookie.getValue());
          out.print(&quot;&lt;/li&gt;&quot;);
      }
      out.print(&quot;&lt;/ul&gt;&quot;);
    // 쿠키가 없을 경우 없다고 표시하기
} else {
      out.print(&quot;&lt;p&gt;쿠키가 존재하지 않습니다.&lt;/p&gt;&quot;);
}</code></pre>
<h2 id="링크-경로-설정">링크 경로 설정</h2>
<p>이제 사용자가 버튼을 누를 때마다 해당 서블릿 페이지로 이동하도록 a 태그를 활용해 경로를 지정해 줄건데요. 이때 본인이 사용하고 있는 프로젝트부터 web.xml에 매핑한 루트(또는 @WebServlet으로 매핑한 경로)를 올바르게 적어야 하기 때문에 주의가 필요합니다. </p>
<p>제 프로젝트 이름은 project이므로 다음과 같이 적어주도록 하겠습니다.</p>
<blockquote>
</blockquote>
<p>mainServlet 페이지 버튼</p>
<pre><code class="language-java">@WebServlet(&quot;/mainServlet&quot;)
&gt;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    res.setContentType(&quot;text/html;charset=UTF-8&quot;);
    PrintWriter out = res.getWriter();
    out.print(&quot;&lt;h3&gt;여기는 메인 서블릿 입니다.&lt;/h3&gt;&quot;);
    out.print(&quot;&lt;h3&gt;경로를 이동해 보세요.&lt;/h3&gt;&quot;);
    out.print(&quot;&lt;a href=\&quot;/project/subServlet1\&quot;&gt;서브 서블릿1&lt;/a&gt;&quot;);
    out.print(&quot;&lt;br&gt;&quot;);
    out.print(&quot;&lt;a href=\&quot;/project/subServlet2\&quot;&gt;서브 서블릿2&lt;/a&gt;&quot;);
    out.print(&quot;&lt;br&gt;&quot;);
    out.print(&quot;&lt;a href=\&quot;/project/subServlet3\&quot;&gt;서브 서블릿3&lt;/a&gt;&quot;);
}</code></pre>
<blockquote>
</blockquote>
<p>SubServlet1 페이지 버튼 </p>
<pre><code class="language-java">@WebServlet(&quot;/subServlet1&quot;)
&gt;
out.print(&quot;&lt;h3&gt;경로를 이동해 보세요.&lt;/h3&gt;&quot;);
out.print(&quot;&lt;a href=\&quot;/project/mainServlet\&quot;&gt;메인 서블릿&lt;/a&gt;&quot;);
out.print(&quot;&lt;br&gt;&quot;);
out.print(&quot;&lt;a href=\&quot;/project/subServlet2\&quot;&gt;서브 서블릿2&lt;/a&gt;&quot;);
out.print(&quot;&lt;br&gt;&quot;);
out.print(&quot;&lt;a href=\&quot;/project/subServlet3\&quot;&gt;서브 서블릿3&lt;/a&gt;&quot;);</code></pre>
<blockquote>
</blockquote>
<p>SubServlet2 페이지 버튼 </p>
<pre><code class="language-java">@WebServlet(&quot;/subServlet2&quot;)
&gt;
out.print(&quot;&lt;h3&gt;경로를 이동해 보세요.&lt;/h3&gt;&quot;);
out.print(&quot;&lt;a href=\&quot;/project/mainServlet\&quot;&gt;메인 서블릿&lt;/a&gt;&quot;);
out.print(&quot;&lt;br&gt;&quot;);
out.print(&quot;&lt;a href=\&quot;/project/subServlet1\&quot;&gt;서브 서블릿1&lt;/a&gt;&quot;);
out.print(&quot;&lt;br&gt;&quot;);
out.print(&quot;&lt;a href=\&quot;/project/subServlet3\&quot;&gt;서브 서블릿3&lt;/a&gt;&quot;);</code></pre>
<blockquote>
</blockquote>
<p>SubServlet3 페이지 버튼 </p>
<pre><code class="language-java">@WebServlet(&quot;/subServlet3&quot;)
&gt;
out.print(&quot;&lt;h3&gt;경로를 이동해 보세요.&lt;/h3&gt;&quot;);
out.print(&quot;&lt;a href=\&quot;/project/mainServlet\&quot;&gt;메인 서블릿&lt;/a&gt;&quot;);
out.print(&quot;&lt;br&gt;&quot;);
out.print(&quot;&lt;a href=\&quot;/project/subServlet2\&quot;&gt;서브 서블릿1&lt;/a&gt;&quot;);
out.print(&quot;&lt;br&gt;&quot;);
out.print(&quot;&lt;a href=\&quot;/project/subServlet3\&quot;&gt;서브 서블릿2&lt;/a&gt;&quot;);</code></pre>
<p>자, 이제 페이지 작성을 모두 완료했으니 한 번 작동을 시켜보겠습니다.</p>
<h2 id="페이지-동작-과정-그림-설명">페이지 동작 과정 (그림 설명)</h2>
<blockquote>
</blockquote>
<center>최초 방문입니다. 해당 메인 페이지는 쿠키를 설정하지 않았습니다.</center>
>
![](https://velog.velcdn.com/images/re_go/post/98863baa-20c4-472a-bec8-549e4dec5ad1/image.png)
>
<center>서브 서블릿1 페이지로 이동해 보겠습니다. 개발자 도구에는 쿠키가 생성되었군요? 하지만 서브 서블릿1에는 생성된 쿠키가 표시되지 않습니다. 정상입니다. 사용자가 최초로 서브 서블릿1에 진입하는 시점에는 쿠키가 없는 상태이니까요.</center>
>
![](https://velog.velcdn.com/images/re_go/post/3e7b8fc1-5797-4654-84f5-5747fd734982/image.png)
>
<center>서블릿 페이지의 두번째로 이동했습니다. 이 시점에는 사용자가 서브 서블릿 페이지1에서 받은 쿠키가 있으므로 해당 쿠키가 정상적으로 노출되는 모습을 확인할 수 있습니다.</center>
>
![](https://velog.velcdn.com/images/re_go/post/eef05cc1-c89b-4148-8c24-84e7417beec4/image.png)
>
<center>서브 서블릿 페이지 3도 마찬가지죠.</center>
>
![](https://velog.velcdn.com/images/re_go/post/91dd6c44-4db1-4c98-a990-529c0fedc2d1/image.png)
>
<center>다시 다른 페이지로 이동하면 이제 세 개의 서브 서블릿 페이지에서 사용자에게 전달한 쿠키를 사용자로부터 받아 출력할 수 있게 되는거죠!</center>
>
![](https://velog.velcdn.com/images/re_go/post/47ce08f5-68a9-4bcc-a493-699e6268dce5/image.png)
>
<center>물론 서브 서블릿 페이지 1의 쿠키 유효 기간은 60초 이므로 시간이 지나면 삭제되는 모습도 보실 수 있고요!</center>
>
![](https://velog.velcdn.com/images/re_go/post/6b27f6a4-5f94-4d33-b84d-1c9e110c841d/image.png)

<h1 id="3-방문-횟수-세어보기">3. 방문 횟수 세어보기</h1>
<p>쿠키를 이용하면 해당 어플리케이션의 방문을 몇 번 했는지도 알 수 있는데요. 기본적으로 사용자가 방문할 때 해당 방문자의 고유 쿠키를 갖고 있을 것이고, 이 사용자가 매번 어플리케이션, 즉 홈페이지에 접속할 때마다 해당 쿠키의 이름을 대조하여 사용자가 맞을 경우 값(방문 횟수)을 증가시키는 방식으로 활용할 수 있기 때문이죠. </p>
<p>그래서 아래 코드를 살펴보시면 얻어온 쿠키가 널이 아니고 배열의 길이보다 작을 때까지 for문을 돌리면서 특정 사용자일 경우 방문 횟수(값)을 증가시켜주는 코드임을 알 수 있습니다. </p>
<p>물론 doGet 메서드는 사용자의 요청마다 호출되고 종료되므로 cnt 변수도 매번 초기화 되겠으나 매번 사용자로부터 쿠키 값을 가져와 cnt에 담고(물론 문자열을 정수로 변환) 그 값을 증가시켜 다시 사용자의 쿠키로 담아주면 결국 사용자의 쿠키 값은 매번 증가할 수 있는 겁니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">package com.practice.test;
&gt;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
&gt;
@WebServlet(&quot;/mainServlet&quot;)
public class MainServlet extends HttpServlet {
&gt;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
&gt;
        resp.setContentType(&quot;text/html;charset=UTF-8&quot;);
        PrintWriter out = resp.getWriter();
&gt;        
        // cnt 선언
        int cnt = 0;
        // 쿠키 가져오기
        Cookie[] list = req.getCookies();
&gt;
        // 가져온 쿠키가 비어있지 않고 i가 배열의 길이보다 작을 떄까지 반복
        // 하는 동안 만약 사용자의 쿠키 이름이 특정 이름과 같으면
        // 쿠키의 값을 가져와 정수 변환 후 cnt에 저장
        for(int i = 0 ;list!=null&amp;&amp; i &lt; list.length;i++) {
            if(list[i].getName().equals(&quot;cookieName&quot;)) {
                cnt = Integer.parseInt(list[i].getValue());
            }
        }
        // cnt를 증가시킨 후 쿠키 이름과 함께 값으로 새로운 쿠키 생성
        cnt++;
        Cookie cookie = new Cookie(&quot;cookieName&quot;, cnt+&quot;&quot;);
        // 유효 기간 재세팅
        cookie.setMaxAge(60*60);
        // 사용자에게 전달
        resp.addCookie(cookie);
&gt;
        out.print(&quot;&lt;h1&gt; 고객님의 &quot; + cnt + &quot;번째 방문을 환영합니다!&lt;/h1&gt;&quot;);
&gt;
    }
&gt;   
}</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/b761867b-65bb-4ae5-968e-45ebb9cecada/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/05fef384-386c-4e3e-adac-99a91f05bfe1/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] 서블릿의 상태 저장 및 유지 객체들 : ServletConetxt]]></title>
            <link>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%98-%EC%83%81%ED%83%9C-%EC%9C%A0%EC%A7%80-%EA%B0%9D%EC%B2%B4%EB%93%A4%EA%B3%BC-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%98-%EC%83%81%ED%83%9C-%EC%9C%A0%EC%A7%80-%EA%B0%9D%EC%B2%B4%EB%93%A4%EA%B3%BC-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Wed, 31 Jul 2024 10:42:59 GMT</pubDate>
            <description><![CDATA[<h1 id="1-상태-저장의-중요성">1. 상태 저장의 중요성</h1>
<p>네트워크의 연결은 어느 한 쪽의 일방적인 행위에 의해서 발생되지 않습니다. 이를 좀 더 풀어서 설명해 보자면 클라이언트의 요청이라는 행위와, 서버의 응답이라는 행위가 일어나는 일련의 과정을 거친 상태가 네트워크가 연결된 상태라고 할 수 있는데요. </p>
<p>이때 네트워크의 연결은 매번 일어나는 상황이라고 가정 할 때 클라이언트의 제공 정보와 서버측의 제공 정보를 매번 새로 생성하는 것은 매우 비효율적이라고 할 수 있겠는데요. 그래서 클라이언트에서는 서버에 제공했던 정보를 <a href="https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4-HTTP-%EC%9A%94%EC%B2%AD-%EC%A0%95%EB%B3%B4-%ED%99%95%EC%9D%B8-%ED%95%B4%EB%B3%B4%EA%B8%B0#cookie">쿠키(cookie)</a> 형식으로 저장을 하게되고, 서버측은 서버 언어의 각 기술에 따라 서버의 상태 정보를 저장하는데요. </p>
<blockquote>
</blockquote>
<center>웹 페이지의 특정 요소를 클릭 시 서버측에 전달하는 정보를 클라이언트 측에서 쿠키 형태로 저장하고 재전송시 활용하게 되는데, 이때 개발자 도구의 cookies 항목을 보면 해당 정보들의 이름과 값, 데이터 타입, 용량 및 유효기간 등을 확인할 수 있습니다.</center>
>
![](https://velog.velcdn.com/images/re_go/post/5daac957-c8f7-4592-95dd-b340a6c3a191/image.png)



<p>특히 서블릿에서는 쿠키 외에도 ServletContext(웹 어플리케이션의 전반적인 정보를 저장), HttpSession(사용자의 개별 세션 보관 및 관리), HttpServletRequest(사용자의 HTTP 요청 정보 저장) 이렇게 세 가지 객체로 전반적인 서버의 정보들을 저장하고 관리하게 됩니다.</p>
<blockquote>
</blockquote>
<center>각 객체 별 상태 유지 기간</center>
>
![](https://velog.velcdn.com/images/re_go/post/fa22928e-75ca-4a3f-8eb9-3e3b4f5ec637/image.png)
>
(자료 출처 : https://velog.io/@yeppi/%ED%99%94%EB%A9%B4-%EC%9D%B4%EB%8F%99-%EB%B0%A9%EC%8B%9D-Redirect-VS-Forwarding-VS-Session-VS-Context)

<h1 id="2-servletconetxt-개요">2. ServletConetxt 개요</h1>
<p>ServletContext 객체는 위에서 언급했다시피 웹 어플리케이션의 전반적인 정보를 담고 있는 객체인데요. 그와 동시에 서블릿 간에 통신할 수 있는 기능도 제공합니다. </p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/f52f3f89-ad52-4afa-8dc1-b171e47cb590/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://nesoy.github.io/articles/2019-02/Servlet">https://nesoy.github.io/articles/2019-02/Servlet</a>)</p>
<h2 id="servletconetxt-추출-방법">ServletConetxt 추출 방법</h2>
<p>이러한 서블릿을 추출하는 방법은 ServletConfig 객체를 사용할 수 있는 init() 메서드에서 해당 객체를 이용한 메서드로 추출을 하거나, GenericServlet으로부터 상속 받은 서블릿 추출 메서드로 추출을 하면 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// 서블릿 컨텍스트의 정보값을 보관하기 위한 변수
ServletContext scContent;
&gt;
@Override
// 개별 서블릿의 초기화 작업 시 활용되는 servletConfig로도 getServletContext 메서드를 호출할 수 있습니다.
public void init(ServletConfig config) throws ServletException {
    scContent = config.getServletContext();
}
&gt;
&gt;
@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    res.setContentType(&quot;text/html;charset=UTF-8&quot;);
    PrintWriter out = res.getWriter();
    // this는 제네릭서블릿의 메서드를 상속 받은 http서블릿을 상속 받은 현재 서블릿, 즉 현재 서블릿에서 상속 받은 getServletContext 메서드를 호출하겠다는 의미.
    scContent = this.getServletContext();
    out.print(&quot;&lt;h3&gt; 컨텍스트 추출값 &lt;/h3&gt;&quot;);
    out.print(&quot;&lt;p&gt;&quot; + scContent + &quot;&lt;p&gt;&quot;);    
}</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/a18682d2-9659-487f-85ce-e40d080e8bae/image.png" alt=""></p>
<h2 id="servletconetxt-전역-파라미터-할당-방법">ServletConetxt 전역 파라미터 할당 방법</h2>
<p>이러한 서블릿 컨텍스트에는 주로 특정 파라미터, 그러니까 모든 서블릿과 JSP들이 공유가 가능한 전역 파라미터가 저장되는데요. 이 값들은 주로 해당 어플리케이션 전역에 걸쳐 설정해야할 공통적인 값을 지정할 때라든지, 서블릿이 초기화 될 때 사용될 초기값으로서 사용된다든지 하는 경우로서 사용될 수 있습니다. </p>
<p>그리고 이러한  web.xml 파일에 <context-param> 안에서 똑같이 <param-name> 과 <param-value>에 이름과 값을 지정한 뒤 특정 시점에서 해당 이름의 컨텍스트 값을 추출하여 사용할 수 있죠.</p>
<blockquote>
</blockquote>
<ol>
<li>XML 페이지<pre><code class="language-xml">&lt;display-name&gt;project&lt;/display-name&gt;
ㆍ
ㆍ
ㆍ
&lt;!-- 컨텍스트 --&gt;
&lt;context-param&gt;
&lt;param-name&gt;firstContextParam&lt;/param-name&gt;
&lt;param-value&gt;hello&lt;/param-value&gt; 
&lt;/context-param&gt;
&lt;context-param&gt;
&lt;param-name&gt;secondContextParam&lt;/param-name&gt;
&lt;param-value&gt;world&lt;/param-value&gt; 
&lt;/context-param&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>JAVA 페이지<pre><code class="language-java">@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
&gt;
// 서블릿 컨텍스트 정보 획득
ServletContext sc = this.getServletContext();
// 특정 이름의 컨텍스트 파라미터 값을 각각 변수에 할당
String var1 = sc.getInitParameter(&quot;firstContextParam&quot;);
String var2 = sc.getInitParameter(&quot;secondContextParam&quot;);
res.setContentType(&quot;text/html;charset=UTF-8&quot;);
PrintWriter out = res.getWriter();
// 변수들을 화면에 출력
out.print(&quot;&lt;h3&gt; 첫번째 컨텍스트 변수의 값 &lt;/h3&gt;&quot;);
out.print(&quot;&lt;p&gt;&quot; + var1 + &quot;&lt;p&gt;&quot;);
&gt;
out.print(&quot;&lt;h3&gt; 두번째 컨텍스트 변수의 값 &lt;/h3&gt;&quot;);
out.print(&quot;&lt;p&gt;&quot; + var2 + &quot;&lt;p&gt;&quot;);
 }</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/bea0c8f1-fd38-4e15-b18b-f677c19e7f95/image.png" alt=""></li>
</ol>
<p>물론 서블릿 컨텍스트의 전역 파라미터를 web.xml 에서만 만들 수 있는건 아니고, 서블릿 컨텍스트 인스턴스의 setAttribute 메서드를 이용해 만들 수도, getAttribute 메서드를 이용해 받을 수도 있습니다. (단 getAttribute로 해당 파라미터를 받을 시 전달한 값의 타입에 상관 없이 오브젝트로 반환 받으므로 받고자 하는 변수의 타입으로 적절하게 형변환을 해줘야 합니다.)</p>
<blockquote>
</blockquote>
<pre><code class="language-java">// setAttribute로 컨텍스트의 특정 파라미터의 이름과 값을 전달합니다.
sc.setAttribute(&quot;thirdContextParam&quot;, &quot;this is made by Set Method!&quot;);
// getAttribute 메서드로 특정 파라미터의 이름을 전달해주고 값을 받아오는데, 이때 문자열로 받을 거니까 String으로 형변환을 해줍니다.
String var3 = (String) sc.getAttribute(&quot;thirdContextParam&quot;);</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/4de2297b-b8d2-4e47-9594-330978f903e0/image.png" alt=""></p>
<p>물론 값은 문자열 뿐만 아니라 여러 데이터 타입의 값도 할당 가능합니다. 단 주의할 점은 파라미터의 값은 여러 타입이 가능해도, 이름 부분은 문자열만 가능하다는 사실!</p>
<blockquote>
</blockquote>
<ol>
<li>Person 객체<pre><code class="language-java">package com.practice.test;
&gt;
public class Person {
 private String name;
 private int age;
&gt;
 // 기본 생성자
 public Person() {
     this.name = &quot;Unknown&quot;;
     this.age = 0;
 }
&gt;
 // 생성자
 public Person(String name, int age) {
     this.name = name;
     this.age = age;
 }
&gt;
 @Override
 public String toString() {
     return &quot;Name: &quot; + name + &quot;, Age: &quot; + age;
 }
}</code></pre>
</li>
<li>Person 타입의 인스턴스를 하나 생성해 컨텍스트의 특정 전역 변수의 값으로 전달한 뒤 가져와 출력하기<pre><code class="language-java">ServletContext sc = this.getServletContext();
// 인스턴스 선언
Person people1 = new Person(&quot;Re_Go&quot;, 30);
// 값 전달
sc.setAttribute(&quot;myInfo&quot;, people1);
// 가지고 와서 적절하게 형변환 한 뒤 변수에 저장
Person var3 = (Person) sc.getAttribute(&quot;myInfo&quot;);
res.setContentType(&quot;text/html;charset=UTF-8&quot;);
PrintWriter out = res.getWriter();
// 출력
out.print(&quot;&lt;h3&gt; 주인장 인적사항 &lt;/h3&gt;&quot;);
out.print(&quot;&lt;p&gt;&quot; + var3 + &quot;&lt;p&gt;&quot;);</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/4fd0e0b5-8de5-459f-a239-3db65f583ec4/image.png" alt=""></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] 서블릿의 web.xml의 환경 설정 살펴보기]]></title>
            <link>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%98-web.xml-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@re_go/JSPServlet-%EC%84%9C%EB%B8%94%EB%A6%BF%EC%9D%98-web.xml-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 30 Jul 2024 09:52:59 GMT</pubDate>
            <description><![CDATA[<h1 id="1-webxml-설정">1. web.xml 설정</h1>
<p>web.xml은 웹 애플리케이션의 구조와 동작을 정의하는 역할을 하는데요. 서블릿, JSP 페이지, 필터, 리스너 등 웹 애플리케이션의 구성 요소를 정의하고, 이들이 어떻게 매핑되는지를 설정하거나 인증, 권한 부여, 보안 제약 조건 등을 설정하는 코드들이 작성되기도 하는 곳이죠. 이러한 xml 파일은 기본적으로 이클립스에서 Dynamic Web Project를 생성시 존재하지 않을 경우 아래의 사진과 같은 경로로 xml 파일을 생성해주면 됩니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/5319931d-2294-43d1-88e2-bc850466695c/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/58d01470-7ac4-4465-abe7-56a339d02386/image.png" alt=""></p>
<h2 id="xml-파일-기본-구조-살펴보기">xml 파일 기본 구조 살펴보기</h2>
<p>이때 web.xml 파일을 텍스트 파일 형식으로 열게 되면 다음과 같은 기본적인 구조의 코드를 확인할 수 있는데요.</p>
<p>첫번째 줄(?xml)은 해당 문서의 버전과 인코딩 방식을 의미하고, 두번째 줄(web-app)은 해당 문서의 요소(xmlns)와 속성(xmlns:xsi)에 대한 고유의 이름을 지정하고, 문서의 스키마(정의 규칙, xsi:schemaLocation 부분)을 정의하는 코드를 의미하게 됩니다.</p>
<p>display-name은 배포되는 웹 에플리케이션의 이름을, welcome-file은 사용자가 해당 웹 애플리케이션에 접근할 때 보여질 루트 페이지를 의미하게 되죠. 이때 다중으로 지정된 경우 첫번째로 나열 된 태그의 파일이 사용자에게 보여지게 됩니다. (아래의 경우 index.html)</p>
<blockquote>
</blockquote>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;web-app xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns=&quot;http://xmlns.jcp.org/xml/ns/javaee&quot; xsi:schemaLocation=&quot;http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd&quot; version=&quot;4.0&quot;&gt;
  &lt;display-name&gt;project&lt;/display-name&gt;
  &lt;welcome-file-list&gt;
    &lt;welcome-file&gt;index.html&lt;/welcome-file&gt;
    &lt;welcome-file&gt;index.htm&lt;/welcome-file&gt;
    &lt;welcome-file&gt;index.jsp&lt;/welcome-file&gt;
    &lt;welcome-file&gt;default.html&lt;/welcome-file&gt;
    &lt;welcome-file&gt;default.htm&lt;/welcome-file&gt;
    &lt;welcome-file&gt;default.jsp&lt;/welcome-file&gt;
  &lt;/welcome-file-list&gt;
&lt;/web-app&gt;</code></pre>
<p>이 중 우리는 서블릿에 대한 정보를 의미하는 서블릿 태그 작성을 위해 welcome-file 태그를 삭제한 뒤 다음과 같이 작성해 줄겁니다. 새로 생긴 태그를 </p>
<blockquote>
</blockquote>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;web-app xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xmlns=&quot;http://xmlns.jcp.org/xml/ns/javaee&quot;
         xsi:schemaLocation=&quot;http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd&quot;
         version=&quot;4.0&quot;&gt;
&gt;
  &lt;display-name&gt;project&lt;/display-name&gt;
&gt;
  &lt;servlet&gt;
    &lt;servlet-name&gt;myServlet&lt;/servlet-name&gt;
    &lt;servlet-class&gt;com.practice.test.MyServlet&lt;/servlet-class&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;id&lt;/param-name&gt;
      &lt;param-value&gt;Re_Go&lt;/param-value&gt;
    &lt;/init-param&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;password&lt;/param-name&gt;
      &lt;param-value&gt;200,000 subscribers!&lt;/param-value&gt;
    &lt;/init-param&gt;
     &lt;!-- 서블릿이 웹 애플리케이션 시작 시점에서 로드되도록 설정 --&gt;
    &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
  &lt;/servlet&gt;
&gt;
  &lt;servlet-mapping&gt;
    &lt;servlet-name&gt;myServlet&lt;/servlet-name&gt;
    &lt;url-pattern&gt;/servletroute&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;
&gt;
&lt;/web-app&gt;</code></pre>
<ul>
<li>servlet : 서블릿의 정보를 등록하는 태그들이 담긴 상위 태그입니다.</li>
<li>servlet-name : 서블릿의 이름을 의미합니다.</li>
<li>servlet-class : 서블릿의 위치가 작성되는 곳입니다.</li>
<li>servlet-mapping : 서블릿과 url 패턴을 매핑할 때의 태그들이 담긴 상위 태그입니다.</li>
<li>url-pattern : 서블릿에 매핑할 url 이름을 지정합니다.</li>
<li>init-param : xml에서 서블릿의 초기화 변수를 전달할 때 사용합니다. 좀 더 구체적으로 말하자면 서블릿이 초기화될 때 서블릿 인스턴스에 전달되며, 서블릿의 초기 설정이나 구성에 사용되는 태그입니다.</li>
<li>param-name : 전달되는 매개변수의 이름을 지정합니다.</li>
<li>param-value : 전달되는 해당 매개변수의 값을 지정합니다.</li>
<li>load-on-startup : 하나의 xml 파일에 서블릿 태그가 여러개로 지정된 경우 초기화 되는 순서를 지정합니다. 숫자가 작을수록 우선 순위가 높아집니다.</li>
</ul>
<h2 id="param-값을-init-메서드에-활용해보기">param 값을 init 메서드에 활용해보기</h2>
<p>위에서 언급한 param 관련 태그는 서블릿이 초기화 될 때 사용되는 인스턴스라고 했는데요. 그 말인 즉 서블릿의 init 메서드에서 해당 인스턴스를 전달 받아 초기화 작업에 필요한 값을 안정적으로 전달 받을 수 있다는 것을 의미하기도 합니다. 그렇다면 이러한 매개변수를 어떻게 전달 받을까요?</p>
<p>우선 해당 서블릿 컨테이너에서 init 메서드를 작성한 뒤 매개변수로 ServletConfig 인터페이스를 받도록 작성해 줍니다. 이때 이 ServletConfig 객체는 서블릿이 최초로 실행되는 시점에 자동적으로 생성되므로, 만약 xml 파일 내에 param 태그로 매개변수를 지정해 주었다면 이에 대한 처리를 init 메서드에서 해주어야 한다는 것이죠.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/784d3c53-9c50-4692-83d0-c1326c0705fe/image.png" alt=""></p>
<blockquote>
</blockquote>
<pre><code class="language-java">String id, password; 
@Override
public void init(ServletConfig config) throws ServletException {
    id = config.getInitParameter(&quot;id&quot;);
    password = config.getInitParameter(&quot;password&quot;);
}</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/dcd35ee6-0dbb-41b4-9050-8277ff9232cc/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Javascript] jQuery 간단 정리 | DOM 조작을 쉽고 빠르게 도와주는 라이브러리, jQuery 간단 개요]]></title>
            <link>https://velog.io/@re_go/Javascript-jQuery-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC-DOM-%EC%A1%B0%EC%9E%91%EC%9D%84-%EC%89%BD%EA%B3%A0-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%8F%84%EC%99%80%EC%A3%BC%EB%8A%94-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-jQuery-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@re_go/Javascript-jQuery-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC-DOM-%EC%A1%B0%EC%9E%91%EC%9D%84-%EC%89%BD%EA%B3%A0-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%8F%84%EC%99%80%EC%A3%BC%EB%8A%94-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-jQuery-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 30 Jul 2024 08:36:42 GMT</pubDate>
            <description><![CDATA[<h1 id="1-jquery란">1. jQuery란?</h1>
<p>jQuery란 자바스크립트 라이브러리 중 하나로, HTML 문서를 탐색하고 조작하기 쉬운 API를 제공하여 웹 개발을 단순화 하는데 사용되는 라이브러리인데요. 제이쿼리를 이용하면 HTML 문서의 탐색 및 조작, 이벤트 처리, 애니메이션, Ajax와 같은 기능을 쉽게 사용할 수 있기 때문에 리액트의 등장 전까지 근 10년 동안 사용되어온 자바스크립트 라이브러리라고 합니다. (물론 지금도 사용은 되고 있으나 리액트, 앵귤러, 넥스트js 등의 등장으로 약간 주춤하고 있긴 합니다.)</p>
<h1 id="2-jquery-임포트-방법">2. jQuery 임포트 방법</h1>
<p>기본적으로 jQuery는 jQuery 라이브러리 파일 자체를 임포트 하는 방법과, 온라인 CDN(Content Delivery Network)을 사용하면 됩니다. </p>
<blockquote>
</blockquote>
<pre><code class="language-html">// 프로젝트 내 자체 파일의 경로를 지정
&lt;script src=&quot;./js/jquery-3.4.1.js&quot;&gt;&lt;/script&gt;
// 브라우저에서 지원하는 CDN 경로를 지정
&lt;script src=&quot;https://code.jquery.com/jquery-3.6.0.min.js&quot;&gt;&lt;/script&gt;</code></pre>
<h1 id="3-기본-작성-방법">3. 기본 작성 방법</h1>
<p>제이쿼리의 작성 형식은 크게 세가지라 구분이 되는데요. $ 기호, 선택자, action()으로 구분이 되며, 각 대상들의 의미는 다음과 같습니다.</p>
<ul>
<li>$ : jQuery를 정의하고, jQuery를 호출하여 사용할 수 있음</li>
<li>선택자() : 자바스크립트의 쿼리 셀렉터 선택자와 마찬가지로 괄호 안에 찾고자 하는 HTML 요소를 선택</li>
<li>action() : 선택된 요소가 행동할 동작(메서드)를 정의하고 실행</li>
</ul>
<blockquote>
</blockquote>
<pre><code class="language-html">&lt;p id = &quot;pa&quot;&gt;아이디 p 태그&lt;/p&gt;
&lt;p class = &quot;pa&quot;&gt;클래스 p 태그&lt;/p&gt;
&lt;button id = &quot;idBtn1&quot;&gt;아이디 숨기기 버튼&lt;/button&gt;
&lt;button class = &quot;classBtn1&quot;&gt;클래스 숨기기 버튼&lt;/button&gt;
&lt;button class = &quot;tagBtn1&quot;&gt;태그 숨기기 버튼&lt;/button&gt;
&lt;button id = &quot;idBtn2&quot;&gt;아이디 나오기 버튼&lt;/button&gt;
&lt;button class = &quot;classBtn2&quot;&gt;클래스 나오기 버튼&lt;/button&gt;
&lt;button class = &quot;tagBtn2&quot;&gt;태그 나오기 버튼&lt;/button&gt;</code></pre>
<blockquote>
</blockquote>
<pre><code class="language-javascript">// id가 idBtn1인 요소를 클릭하면 id가 pa인 요소를 숨김
$(&quot;#idBtn1&quot;).click(() =&gt; {
  $(&#39;#pa&#39;).hide();
});
// 클래스가 classBtn1인 요소를 클릭하면 클래스가 pa인 요소를 숨김
$(&quot;.classBtn1&quot;).click(() =&gt; {
  $(&#39;.pa&#39;).hide();
})
// 클래스가 tagBtn1인 요소를 클릭하면 태그가 p인 요소를 숨김
$(&quot;.tagBtn1&quot;).click(() =&gt; {
  $(&#39;p&#39;).hide();
})
// id가 idBtn2인 요소를 클릭하면 아이디가 pa인 요소를 선택한 뒤 다시 나타냄
$(&quot;#idBtn2&quot;).click(() =&gt; {
  $(&#39;#pa&#39;).show();
});
// 위 설명과 동일
$(&quot;.classBtn2&quot;).click(() =&gt; {
  $(&#39;.pa&#39;).show();
});
// 위 설명과 동일
$(&quot;.tagBtn2&quot;).click(() =&gt; {
  $(&#39;p&#39;).show();
})</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/2e94db46-f7c1-4424-94ee-8a3b5fb21e0a/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/28ebedf2-f5f8-45d4-95b2-2129e140f2b4/image.png" alt=""></p>
<p>만약 jquery 코드를 순수한 자바스크립트 돔 형태로 작성한다면 어떻게 작성할 수 있을까요?</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript">document.getElementById(&#39;idBtn1&#39;).addEventListener(&#39;click&#39;, () =&gt; {
  document.getElementById(&#39;pa&#39;).style.display = &#39;none&#39;;
});
&gt;
document.querySelectorAll(&#39;.classBtn1&#39;).forEach(element =&gt; {
  element.addEventListener(&#39;click&#39;, () =&gt; {
    document.querySelectorAll(&#39;.pa&#39;).forEach(paElement =&gt; {
      paElement.style.display = &#39;none&#39;;
    });
  });
});
&gt;
document.querySelectorAll(&#39;.tagBtn1&#39;).forEach(element =&gt; {
  element.addEventListener(&#39;click&#39;, () =&gt; {
    document.querySelectorAll(&#39;p&#39;).forEach(pElement =&gt; {
      pElement.style.display = &#39;none&#39;;
    });
  });
});
&gt;
document.getElementById(&#39;idBtn2&#39;).addEventListener(&#39;click&#39;, () =&gt; {
  document.getElementById(&#39;pa&#39;).style.display = &#39;block&#39;;
});
&gt;
document.querySelectorAll(&#39;.classBtn2&#39;).forEach(element =&gt; {
  element.addEventListener(&#39;click&#39;, () =&gt; {
    document.querySelectorAll(&#39;.pa&#39;).forEach(paElement =&gt; {
      paElement.style.display = &#39;block&#39;;
    });
  });
});
&gt;
document.querySelectorAll(&#39;.tagBtn2&#39;).forEach(element =&gt; {
  element.addEventListener(&#39;click&#39;, () =&gt; {
    document.querySelectorAll(&#39;p&#39;).forEach(pElement =&gt; {
      pElement.style.display = &#39;block&#39;;
    });
  });
});</code></pre>
<p>네... 이것이 바로 제이쿼리를 사용하는 이유가 되겠습니다 ... 다중 선택된 요소에 이벤트를 주기 위해 forEach를 쓰느니, 스타일 속성을 주느니 할 필요 없이 이벤트 함수인 hide와 show를 사용하면 되니까요!</p>
<h1 id="4-스타일-지정-방법">4. 스타일 지정 방법</h1>
<p>제이쿼리를 이용해 스타일을 지정할 때는 선택자 뒤쪽에 .css 메서드로 단일, 다중 속성을 지정하면 되는데요. 이때 다중 속성의 경우 객체 형식으로 전달해주면 되기 때문에, 객체를 직접 넣어주거나 객체를 할당 받은 변수를 할당해주면 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-html">&lt;ul class=&quot;wrap&quot;&gt;
  &lt;li class=&quot;html&quot;&gt;html&lt;/li&gt;
  &lt;li class=&quot;css&quot;&gt;css&lt;/li&gt;
  &lt;li class=&quot;javascript&quot;&gt;javascript&lt;/li&gt;
  &lt;li class=&quot;jquery&quot;&gt;jquery&lt;/li&gt;
  &lt;li class=&quot;ajax&quot;&gt;ajax&lt;/li&gt;
&lt;/ul&gt;</code></pre>
<blockquote>
</blockquote>
<pre><code class="language-javascript">// 단일 css 속성을 선택할 때는 속성, 값 순서대로 작성합니다.
$(&quot;.html&quot;).css(&#39;color&#39;,&#39;red&#39;);
$(&quot;.css&quot;).css(&#39;color&#39;,&#39;yellow&#39;);
&gt;
// 다중 속성의 경우 객체 형식으로 전달해주면 됩니다.
let css = {
  &#39;font-size&#39; : &#39;30px&#39;,
  &#39;background-color&#39; : &#39;orange&#39;
}
$(&#39;ul li&#39;).css(css);</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/137c4f06-a3e7-481f-91e7-381d28392532/image.png" alt=""></p>
<h1 id="5-이벤트-지정-방법">5. 이벤트 지정 방법</h1>
<p>이벤트의 경우 자바스크립트의 이벤트들과 크게 다르지 않는데요. 다만 인라인 스타일(접두어로 on을 사용)을 사용하지 않는다는 점만 알아두시면 될텐데, 제이쿼리의 이벤트 종류들은 <a href="https://inpa.tistory.com/entry/jQuery-%F0%9F%93%9A-%EC%A0%9C%EC%9D%B4%EC%BF%BC%EB%A6%AC-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC#on_%EB%A9%94%EC%86%8C%EB%93%9C">해당 블로그</a>에서 깔끔하고 자세하게 알려주고 있기 때문에, 여기서는 간단한 예시를 들어 동작 원리 몇 가지만 소개해 드리겠습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>마우스 이벤트(mouseover, mouseout)<pre><code class="language-html">&lt;h2 id = &quot;header&quot;&gt;MouseOver / MouseOut&lt;/h2&gt;</code></pre>
<blockquote>
</blockquote>
<pre><code class="language-javascript">// 해당 요소에 마우스를 올리면 css 속성을 변경
$(&#39;#header&#39;).mouseover(() =&gt; {
$(&#39;#header&#39;).css(&#39;background-color&#39;, &#39;yellow&#39;)
})
// 해당 요소에 마우스를 떼면 css 속성을 변경
$(&#39;#header&#39;).mouseout(() =&gt; {
$(&#39;#header&#39;).css(&#39;background-color&#39;, &#39;pink&#39;)
})</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/c4b25de9-8f96-40b2-a9f2-c7bbc83d5129/image.png" alt=""><blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/19180bec-5d68-413b-9bb5-7b2be4f07e25/image.png" alt=""><blockquote>
</blockquote>
</li>
<li>입력할 때마다 HTML 요소를 변화(keydown, text)<pre><code class="language-html">타이핑을 해보세요 : &lt;input type=&quot;text&quot; id=&quot;inputName&quot;&gt;
&lt;p style=&quot;margin:0&quot;&gt;
count : &lt;span&gt;0&lt;/span&gt;
&lt;/p&gt;</code></pre>
<blockquote>
</blockquote>
<pre><code class="language-javascript">let content = $(&#39;p&gt;span&#39;).text()
$(&#39;#inputName&#39;).keydown(() =&gt; {
$(&#39;span&#39;).text(++content);
})</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/7684bf78-cc35-4402-97ce-2899a1694ecd/image.png" alt=""></li>
</ol>
<h1 id="6-배열-메서드-사용-방법">6. 배열 메서드 사용 방법</h1>
<p>제이쿼리에서는 다중 태그 요소들을 가져오고, 이를 가공하는 작업을 사용할 때 주로 each, map, grep를 사용하는데요. 해당 메서드들에 대한 설명을 짧게 말씀드려보자면 다음과 같습니다.</p>
<ul>
<li>$.each() : 배열이나 객체를 순회하면서 각 항목에 대해 지정된 작업을 수행합니다.</li>
<li>$.map() : 배열 또는 객체를 순회하면서 각 항목을 변환하고, 변환된 결과로 새 배열을 생성합니다.</li>
<li>$.grep() : 배열에서 조건을 만족하는 항목만 필터링하여 새로운 배열을 생성합니다.</li>
</ul>
<blockquote>
</blockquote>
<p>Q. 중복되는 값을 가진 태그를 제거한 뒤 새로운 값으로 대체하는 코드</p>
<pre><code class="language-html">&lt;ul&gt;
  &lt;li&gt;사과&lt;/li&gt;
  &lt;li&gt;바나나&lt;/li&gt;
  &lt;li&gt;포도&lt;/li&gt;
  &lt;li&gt;딸기&lt;/li&gt;
  &lt;li&gt;귤&lt;/li&gt;
  &lt;li&gt;배&lt;/li&gt;
  &lt;li&gt;참외&lt;/li&gt;
  &lt;li&gt;복숭아&lt;/li&gt;
  &lt;li&gt;사과&lt;/li&gt;
  &lt;li&gt;딸기&lt;/li&gt;
&lt;/ul&gt;</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/20ebda04-7492-4995-ac10-2cb78972a98e/image.png" alt=""></p>
<blockquote>
</blockquote>
<ol>
<li>선택한 li 태그들 중 text 값만 골라서 새로운 배열로 반환 받는 작업(map) <pre><code class="language-javascript">$(document).ready(function() {
// 모든 &lt;li&gt; 요소의 텍스트를 배열로 추출
var items = $(&#39;#fruit-list li&#39;).map(function() {
  // this는 해당 각각의 태그들, 즉 li 요소들을 의미함.
  return $(this).text();
}).get();
console.log(items)</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/16326804-0f29-4d68-a20e-86bad2bf7afb/image.png" alt=""><blockquote>
</blockquote>
</li>
<li>가져온 값들 중 중복을 제거하는 작업(grep)<pre><code class="language-javascript">// 중복 제거를 위한 임시 객체
var seen = {};
&gt;
// $.grep()을 사용하여 중복 제거, 이때 grep 메서드의 첫번째 매개변수는 그룹(배열)이, 두번째 매개변수는 자동으로 각 값들과 인덱스를 제공 받는 콜백 함수를 지정하여 작업을 수행함) 
var uniqueItems = $.grep(items, function(value, index) {
// 
if (!seen[value]) {
 seen[value] = true;
 return true;  // 항목이 중복되지 않았으므로 반환
}
return false;  // 항목이 중복되었으므로 반환하지 않음
});</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/dd402d7b-a603-4551-bb64-ca0aea343131/image.png" alt=""><blockquote>
</blockquote>
</li>
<li>기존의 ul을 제거한 뒤 중복 제거된 배열 정보로 태그를 재생성<pre><code class="language-javascript">// &lt;ul&gt; 요소를 싹 다 비운 후 each를 이용해 중복 제거된 값 &lt;li&gt; 요소 추가
$(&#39;#fruit-list&#39;).empty();
// each 메서드의 콜백 함수의 첫번째 매개변수는 인덱스, 두번째는 값을 나타내므로 요소를 순환하면서 ul 태그에 li에 item을 혼합해 달아주면 됨.
$.each(uniqueItems, function(index, item) {
$(&#39;#fruit-list&#39;).append(&#39;&lt;li&gt;&#39; + item + &#39;&lt;/li&gt;&#39;);
});</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/9244792a-4252-4bec-b7d2-21ac24d8d828/image.png" alt=""></li>
</ol>
<h1 id="7-클래스-추가-및-제거-방법">7. 클래스 추가 및 제거 방법</h1>
<p>자바스크립트에서는 클래스를 추가하거나 제거할 때 classList(클래스이름).add, 혹은 remove를 사용하여 해당 요소의 클래스를 변경했는데요. 제이쿼리에서는 크게 세 가지 메서드로 나뉘어져 있답니다.</p>
<ul>
<li>add-class : 클래스를 추가</li>
<li>remove-class : 클래스를 제거</li>
<li>toggle-class : 두 개 이상의 클래스를 가지고 있을 경우 클래스를 토글</li>
</ul>
<blockquote>
</blockquote>
<p>버튼을 누를 때마다 해당 요소에 클래스를 주어 스타일을 변화시키는 코드</p>
<pre><code class="language-javascript">&lt;h1&gt;jQuery css 변경 방법&lt;/h1&gt;
&lt;p id = &quot;set-css&quot;&gt;jquery CSS&lt;/p&gt;
&lt;hr&gt;
&lt;button id = &quot;add-class&quot;&gt;클래스 추가&lt;/button&gt;
&lt;button id = &quot;remove-class&quot;&gt;클래스 제거&lt;/button&gt;
&lt;button id = &quot;toggle-class&quot;&gt;클래스 토글&lt;/button&gt;</code></pre>
<blockquote>
</blockquote>
<pre><code class="language-javascript">$(&quot;#add-class&quot;).click(() =&gt; {
  $(&quot;#set-css&quot;).addClass(&#39;red&#39;);
})
$(&quot;#remove-class&quot;).click(() =&gt; {
  $(&quot;#set-css&quot;).removeClass(&#39;red&#39;);
})
$(&quot;#toggle-class&quot;).click(() =&gt; {
  $(&quot;#set-css&quot;).toggleClass(&#39;red&#39;);
})</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/fbc30b14-d989-43f9-9de9-3e57fad5027b/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/efe8ad17-13fc-45cd-8f3d-d212ed6a53fd/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/87829bbb-3215-4556-8b7d-18a16ca2c46d/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/daa8f89c-63e2-4746-8f51-0d05ee1bde1e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP&Servlet] 메소드 방식에 따라 클라이언트의 정보 추출 메서드]]></title>
            <link>https://velog.io/@re_go/JSPServlet-%EB%A9%94%EC%86%8C%EB%93%9C-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%94%B0%EB%9D%BC-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EC%9D%98-%EC%A0%95%EB%B3%B4-%EC%B6%94%EC%B6%9C-%EB%A9%94%EC%84%9C%EB%93%9C</link>
            <guid>https://velog.io/@re_go/JSPServlet-%EB%A9%94%EC%86%8C%EB%93%9C-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%94%B0%EB%9D%BC-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EC%9D%98-%EC%A0%95%EB%B3%B4-%EC%B6%94%EC%B6%9C-%EB%A9%94%EC%84%9C%EB%93%9C</guid>
            <pubDate>Mon, 29 Jul 2024 15:19:35 GMT</pubDate>
            <description><![CDATA[<h1 id="1-기본-코드-구조">1. 기본 코드 구조</h1>
<p>GET, POST 방식에 따른 클라이언트의 제공 정보를 추출하기에 앞서 JSP 구조를 다음과 같이 짜보도록 하겠습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>JSP 페이지<pre><code class="language-JSP">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;${pageContext.request.contextPath}/css/style.css&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;h1&gt;회원 가입&lt;/h1&gt;
 &lt;form action=&quot;page&quot; method=&quot;get&quot;&gt;
     &lt;fieldset&gt;
         &lt;legend class=&quot;screen-out&quot;&gt;개인 정보&lt;/legend&gt;
         &lt;ul&gt;
             &lt;li&gt;
                 &lt;label for=&quot;id&quot; class=&quot;title&quot;&gt;아이디 : &lt;/label&gt;
                 &lt;input type=&quot;text&quot; id=&quot;id&quot; name=&quot;id&quot; class=&quot;input&quot;&gt;
             &lt;/li&gt;
             &lt;li&gt;
                 &lt;label for=&quot;pwd&quot; class=&quot;title&quot;&gt;패스워드 : &lt;/label&gt;
                 &lt;input type=&quot;password&quot; id=&quot;pwd&quot; name=&quot;pwd&quot; class=&quot;input&quot;&gt;
             &lt;/li&gt;        
             &lt;li&gt;
                 &lt;label for=&quot;name&quot; class=&quot;title&quot;&gt;이름 : &lt;/label&gt;
                 &lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot; class=&quot;input&quot;&gt;
             &lt;/li&gt;
             &lt;li&gt;
                    &lt;label class=&quot;title&quot;&gt;취미 : &lt;/label&gt;
                 &lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;등산&quot;&gt;등산
                 &lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;운동&quot;&gt;운동
                 &lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;독서&quot;&gt;독서
                 &lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;여행&quot;&gt;여행
                 &lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;없음&quot;&gt;없음
             &lt;/li&gt;
             &lt;li&gt;
                 &lt;label class=&quot;title&quot;&gt;성별 : &lt;/label&gt;
                 &lt;input type=&quot;radio&quot; class=&quot;gender&quot; name=&quot;gender&quot; value=&quot;male&quot;&gt;남자
                 &lt;input type=&quot;radio&quot; class=&quot;gender&quot; name=&quot;gender&quot; value=&quot;female&quot;&gt;여자
             &lt;/li&gt;
             &lt;li&gt;
                 &lt;label for = &quot;religion&quot; class=&quot;title&quot;&gt;종교 : &lt;/label&gt;
                 &lt;select id = &quot;religion&quot; name = &quot;religion&quot;&gt;
                     &lt;option value = &quot;christian&quot;&gt;기독교&lt;/option&gt;
                     &lt;option value = &quot;catholic&quot;&gt;천주교&lt;/option&gt;
                     &lt;option value = &quot;buddhism&quot;&gt;불교&lt;/option&gt;
                     &lt;option value = &quot;wonbuddhism&quot;&gt;원불교&lt;/option&gt;
                     &lt;option value = &quot;none&quot;&gt;없음&lt;/option&gt;
                 &lt;/select&gt;
             &lt;/li&gt;
             &lt;li&gt;
                 &lt;label for = &quot;introduce&quot; class=&quot;title&quot;&gt;자기 소개&lt;/label&gt;
                 &lt;textarea id=&quot;introduce&quot; name=&quot;introduce&quot; rows=&quot;10&quot; cols=&quot;50&quot;&gt;
                 &lt;/textarea&gt;
             &lt;/li&gt;
         &lt;/ul&gt;
         &lt;div class = &quot;button-group&quot;&gt;
             &lt;input type = &quot;submit&quot; id = &quot;submit&quot; value = &quot;전송하기&quot;&gt;
             &lt;input type = &quot;submit&quot; id = &quot;initial&quot; value = &quot;모두 지우기&quot;&gt;
         &lt;/div&gt;
     &lt;/fieldset&gt;
 &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
</blockquote>
</li>
<li>CSS 파일<pre><code class="language-css">h1{
 text-align: center;
}
body{
 width: 100vw;
 height: 100vh;
}
fieldset{
 width: 300px;
 height: 500px;
 margin: auto;
}
legend{
 text-align: center;
}
ul {
padding: 0;
list-style-type: none;
}
ul li {
margin-bottom: 15px;
}
label.title{
 text-align: left;
}
input.input{
 width: 300px;
 float: right;
}
textarea {
 display: block;
 margin: 10px 0; 
}
.button-group{
 text-align: center;
}</code></pre>
</li>
<li>서블릿 파일<pre><code class="language-java">package com.practice.test;
&gt;
import java.io.*;
import java.util.Arrays;
&gt;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
&gt;
@WebServlet(&quot;/page&quot;)
public class ServletContainer extends HttpServlet {
&gt;
 @Override
 public void init() throws ServletException {
     // DB 연결이나 파일 로딩, 기타 작업을 초기화 하는 구역입니다.
     System.out.println(&quot;초기화 완료&quot;);
 }
&gt;
 @Override
 protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
     String method = req.getMethod();
&gt;        
     // GET 요청 처리
     if (&quot;GET&quot;.equalsIgnoreCase(method)) {
         doGet(req, res);
     }
     // POST 요청 처리
     else if (&quot;POST&quot;.equalsIgnoreCase(method)) {
         doPost(req, res);
     }
     // 다른 HTTP 메서드에 대한 처리 추가
     else {
&gt;            res.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
         res.getWriter().println(&quot;HTTP method not supported.&quot;);
     }
 }
&gt;
 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     // 요청 인코딩 설정
     req.setCharacterEncoding(&quot;UTF-8&quot;);
&gt;
     // 보여질 컨텐츠의 인코딩 세팅
     resp.setContentType(&quot;text/html;charset=UTF-8&quot;);
     // 응답 인코딩 세팅
     resp.setCharacterEncoding(&quot;UTF-8&quot;);
&gt;
     // 서블릿에서 사용자에게 데이터 출력을 위해 관례적으로 사용하는 출력 변수를 사용해 코드의 가독성을 높임
     PrintWriter out = resp.getWriter();
&gt;        
     // 요청 파라미터 추출
     String id = req.getParameter(&quot;id&quot;);
     String pw = req.getParameter(&quot;pwd&quot;);
     String name = req.getParameter(&quot;name&quot;);
     String[] hobbies = req.getParameterValues(&quot;hobby&quot;);
     String gender = req.getParameter(&quot;gender&quot;);
     String religion = req.getParameter(&quot;religion&quot;);
     String introduce = req.getParameter(&quot;introduce&quot;);
&gt;
     // 사용자에게 보여질 화면 출력
     out.print(&quot;&lt;head&gt;&lt;title&gt;요청 결과&lt;/title&gt;&lt;/head&gt;&quot;);
     out.print(&quot;&lt;body&gt;&quot;);
     out.print(&quot;&lt;h1&gt;요청 결과&lt;/h1&gt;&quot;);
     out.print(&quot;&lt;p&gt;ID : &quot; + id + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;PASSWORD : &quot; + pw + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;이름 : &quot; + name + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;취미 : &quot; + Arrays.toString(hobbies) + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;성별 : &quot; + gender + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;종교 : &quot; + religion + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;소감 : &quot; + introduce + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;form action=&#39;index.jsp&#39; method=&#39;get&#39;&gt;&quot;);
     out.print(&quot;&lt;button type=&#39;submit&#39;&gt;돌아가기&lt;/button&gt;&quot;);
     out.print(&quot;&lt;/form&gt;&quot;);
     out.print(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
 }
&gt;   
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     // 요청 인코딩 설정
     req.setCharacterEncoding(&quot;UTF-8&quot;);
&gt;        
     // 보여질 컨텐츠의 인코딩 세팅
     resp.setContentType(&quot;text/html;charset=UTF-8&quot;);
     // 응답 인코딩 세팅
     resp.setCharacterEncoding(&quot;UTF-8&quot;);
&gt;
     // 서블릿에서 사용자에게 데이터 출력을 위해 관례적으로 사용하는 출력 변수를 사용해 코드의 가독성을 높임
     PrintWriter out = resp.getWriter();  
&gt;
     String id = req.getParameter(&quot;id&quot;);
     String pw = req.getParameter(&quot;pwd&quot;);
     String name = req.getParameter(&quot;name&quot;);
     String[] hobbies = req.getParameterValues(&quot;hobby&quot;);
     String gender = req.getParameter(&quot;gender&quot;);
     String religion = req.getParameter(&quot;religion&quot;);
     String introduce = req.getParameter(&quot;introduce&quot;);
     String queryInfo = req.getQueryString();
&gt;
     out.print(&quot;&lt;head&gt;&lt;title&gt;요청 결과&lt;/title&gt;&lt;/head&gt;&quot;);
     out.print(&quot;&lt;body&gt;&quot;);
     out.print(&quot;&lt;p&gt;&quot; + &quot;total : &quot; + queryInfo + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;h1&gt;요청 결과&lt;/h1&gt;&quot;);
     out.print(&quot;&lt;p&gt;&quot; + &quot;ID : &quot; + id + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;&quot; + &quot;PASSWORD : &quot; + pw + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;&quot; + &quot;이름 : &quot; + name + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;&quot; + &quot;취미 : &quot; + Arrays.toString(hobbies) + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;&quot; + &quot;취미 : &quot; + String.join(&quot;, &quot;, hobbies) + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;&quot; + &quot;성별 : &quot; + gender + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;&quot; + &quot;종교 : &quot; + religion + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;p&gt;&quot; + &quot;소감 : &quot; + introduce + &quot;&lt;/p&gt;&quot;);
     out.print(&quot;&lt;form action=&#39;index.jsp&#39; method=&#39;get&#39;&gt;&quot;);
     out.print(&quot;&lt;button type=&#39;submit&#39;&gt;돌아가기&lt;/button&gt;&quot;);
     out.print(&quot;&lt;/form&gt;&quot;);
 }
}</code></pre>
</li>
</ol>
<h1 id="2-getparameter">2. getParameter</h1>
<p>getParameter는 사용자가 입력한 단일 정보를 추출하는대 사용되는데요. 서블릿과 연결된 JSP 페이지에서 제출 된 사용자의 정보들 중 단일 정보를 가져오며, 이때 가져오는 정보의 형태는 속성은 name이라는 이름과 value라는 이름의 값으로 구성이 됩니다.</p>
<blockquote>
<p>HTML에서 name은 id이고 value는 사용자의 입력값이므로</p>
</blockquote>
<pre><code class="language-html">&lt;input type=&quot;text&quot; id=&quot;id&quot; name=&quot;id&quot; class=&quot;input&quot;&gt;</code></pre>
<blockquote>
</blockquote>
<p>request 객체의 getParameter 메서드로 input의 name이 id인 요소의 값(value)를 추출해 id 변수에 담아 출력하면</p>
<pre><code class="language-java">String id = req.getParameter(&quot;id&quot;);
System.out.println(id);</code></pre>
<p>다음과 같이 콘솔창과 웹페이지 창에서 해당 정보를 확인할 수 있습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/f73ec7d2-695a-412d-880f-0f53a4cade69/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/aed6165e-b27d-440b-ae83-ae400d2d54b2/image.png" alt=""></p>
<h1 id="3-getparametervalues">3. getParameterValues</h1>
<p>getParameterValues는 사용자가 입력한 다중 정보를 추출하는대 사용되는데요. 이 방식은 name이 같은 이름인 요소들의 값을 배열 형태로 전달 받아 활용할 수 있습니다.</p>
<blockquote>
</blockquote>
<p>checkbox 형태의 input은 다중 선택이 가능하기 때문에 name이 hobby인 것을 기준으로 배열 형태로 받을 수 있습니다.</p>
<pre><code class="language-html">&lt;label class=&quot;title&quot;&gt;취미 : &lt;/label&gt;
&lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;등산&quot;&gt;등산
&lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;운동&quot;&gt;운동
&lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;독서&quot;&gt;독서
&lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;여행&quot;&gt;여행
&lt;input type=&quot;checkbox&quot; class=&quot;habit&quot; name=&quot;hobby&quot; value=&quot;없음&quot;&gt;없음</code></pre>
<blockquote>
</blockquote>
<p>다음과 같이 배열에 값들을 전달한 뒤 배열 형태와 문자열 형태로 반환하면 다음과 같이 확인할 수 있습니다.</p>
<pre><code class="language-java">String[] hobbies = req.getParameterValues(&quot;hobby&quot;);
out.print(&quot;&lt;p&gt;&quot; + &quot;취미 : &quot; + Arrays.toString(hobbies) + &quot;&lt;/p&gt;&quot;);
out.print(&quot;&lt;p&gt;&quot; + &quot;취미 : &quot; + String.join(&quot;, &quot;, hobbies) + &quot;&lt;/p&gt;&quot;);</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/c94dba83-4282-472b-89a4-53106f2a4427/image.png" alt=""></p>
<h1 id="4-getquerystring">4. getQueryString</h1>
<p>getQueryString 메서드는 사용자의 입력값을 주소 형태로 받아오는 메서드인데요. get 방식에는 주소값에 사용자의 입력값이 포함되어 있기 때문에 POST 방식에는 사용이 불가능하고, GET 방식에서만 활용 가능한 메서드입니다.</p>
<blockquote>
</blockquote>
<p>서블릿에 다음과 같이 쿼리를 구해와 출력을 할 경우 </p>
<pre><code class="language-java">String queryInfo = req.getQueryString();
out.print(&quot;&lt;p&gt;&quot; + &quot;total : &quot; + queryInfo + &quot;&lt;/p&gt;&quot;);</code></pre>
<blockquote>
</blockquote>
<p>다음과 같이 쿼리값 자체를 출력할 수 있습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/1c0f4e72-ed0c-4f51-942b-05eb6c5af498/image.png" alt=""></p>
<h1 id="5-getinputstream">5. getInputStream</h1>
<p>getInputStream 메서드는 클라이언트의 요청 정보가 담긴 몸체와 연결된 입력스트림을 생성한 뒤 반환하는 메서드로, 요청정보 몸체에 있는 데이터들을 읽어오고 싶을 때 사용하는데요. 즉 이 메서드는 POST 방식을 위한 메서드라는 말이기도 합니다.</p>
<blockquote>
</blockquote>
<p>getInputStream 인스턴스를 하나 생성한 후 컨텐츠 길이 만큼을 len 변수에 저장을 해줍니다. (getContentLength 메서드 이용)</p>
<blockquote>
</blockquote>
<p>그 다음 byte 타입의 배열을 해당 len의 길이만큼 생성하는데, 여기서 byte 배열을 사용하는 이유는 HTTP 요청 본문은 텍스트 뿐만 아니라 바이너리 데이터도 포함될 수 있기에 텍스트와 이진 데이터를 모두 저장 가능한 byte 배열을 사용한 것입니다. 물론 String으로 변환하기 위한 목적도 가지고 있고요.</p>
<blockquote>
</blockquote>
<p>아무튼 위 작업을 수행한 뒤 read 메서드를 이용해 각각 저장소(b)와 저장 시작 위치(0), 저장할 문자열의 길이(len)을 지정해 읽어줍니다.</p>
<blockquote>
</blockquote>
<p>그 다음 마지막으로 저장소 b를 문자열로 변환 후 출력을 해줍니다.</p>
<pre><code class="language-java">ServletInputStream input = req.getInputStream();
int len = req.getContentLength();
byte[] b = new byte[len];
input.read(b, 0, len);
String str = new String(b)
out.print(&quot;전체 문자열 : &quot; + str);</code></pre>
<blockquote>
</blockquote>
<p>그럼 다음과 같이 데이터가 잘 출력되는데요. 여기서 의외인건 POST 방식에서 getParameter 메서드를 사용하면 null, 즉 값이 없음을 의미한다는 겁니다. </p>
<blockquote>
</blockquote>
<p>그 이유는 getInputStream으로 요청 본문을 직접 읽으면, getParameter 메서드는 더 이상 폼 데이터에 접근할 수 없는데, getInputStream과 getParameter는 서로 다른 데이터 읽기 방식을 사용하기 때문에, 하나의 요청 본문에서 둘 다 사용하려고 하면 충돌이 발생하여 getParameter에서 제대로 값을 읽어오지 못했기 때문이죠.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/888bd332-fd0f-41d6-a50d-1bb14212d52e/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>하지만 그 말인 즉 getInputStream을 사용하지 않으면 getParameter도 정상적으로 사용할 수 있다는 의미이므로 왠만해서는 getParameter로 사용하면 됩니다.</p>
<blockquote>
</blockquote>
<p>getInputStream 부분을 지우고 나니 잘 되는군요!</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/479e49eb-591b-4e04-b0fc-0abd235986d6/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Javascript] 비동기 함수 총정리 | fetch 메서드와 네트워크 통신 활용]]></title>
            <link>https://velog.io/@re_go/Javascript-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-%EC%B4%9D%EC%A0%95%EB%A6%AC-fetch-%EB%A9%94%EC%84%9C%EB%93%9C%EC%99%80-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%86%B5%EC%8B%A0-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@re_go/Javascript-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-%EC%B4%9D%EC%A0%95%EB%A6%AC-fetch-%EB%A9%94%EC%84%9C%EB%93%9C%EC%99%80-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%86%B5%EC%8B%A0-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Mon, 29 Jul 2024 13:11:38 GMT</pubDate>
            <description><![CDATA[<h1 id="1-fetch-메서드란">1. fetch 메서드란?</h1>
<p>fetch 메서드는 앞서 살펴본 비동기 함수들을 이용해 서버와 비동기로 데이터를 주고 받도록 도와주는 브라우저 지원 API로, window 전역 객체의 프로퍼티로서 여러 데이터 형식을 지원하나, 주로 JSON(JavaScript Object Notation) 데이터 통신에 활용되는 메서드입니다.</p>
<p>여기서 JSON이란 JS에서 키와 값으로 이루어진 프로퍼티들의 집합으로, 그냥 객체라고 생각하시면 되는데요. 다만 JS에서 해당 객체의 타입은 Object인 반면, 데이터를 받아올 때에는 우리가 알아볼 수 있도록 JSON 형식이기 때문에 json 메서드를 사용하여 데이터를 JSON 형식으로 변환하는 작업을 해주어야 합니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/02cdd714-7678-4410-a425-95699f06afb3/image.png" alt=""></p>
<blockquote>
</blockquote>
<p>(자료 출처 : <a href="https://commons.wikimedia.org/wiki/File:JSON_vs._XML.png">https://commons.wikimedia.org/wiki/File:JSON_vs._XML.png</a>)</p>
<h1 id="2-json-정보-가져오기-then-catch">2. JSON 정보 가져오기 (then, catch)</h1>
<p>fetch는 다음과 같이 특정 정보를 담고 있는 웹페이지의 url을 매개변수로 전달 받으면 해당 url로 정보를 요청하게 되는데요.</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript">fetch(&#39;https://jsonplaceholder.typicode.com/posts/1&#39;)</code></pre>
<p>위와 같이 fetch 메서드는 해당 url로 데이터 요청을 보내게 됩니다. 이때 해당 메서드에 마우스를 올리면 다음과 같은 정보를 확인할 수 있습니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/38a24184-b429-406b-b735-61435271648a/image.png" alt=""></p>
<p>해당 메서드를 설명하는 부분에 익숙한 키워드가 보이시죠? 네 그렇습니다. 해당 메서드는 바로 프로미스를 반환하는 메서드, 즉 비동기 함수인 것이죠. 그 말인 즉 url 요청에 의한 결과값을 then(성공), 혹은 catch(실패)로 처리할 수 있다는 말입니다. 아래 코드와 같이 말이죠!</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript">fetch(&#39;https://jsonplaceholder.typicode.com/posts/1&#39;)
  .then(response =&gt; {
    console.log(&#39;data received&#39;);
      console.log(response);
  })
  .catch(err =&gt; {
    console.log(&quot;can\n&#39;t acceptid&quot;)
  })</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/105f40b7-e9a2-4d59-939d-6a5b08ff9e06/image.png" alt=""></p>
<p>위의 사진을 보시다시피 해당 데이터를 잘 받아온 모습을 확인하실 수 있는데요. 하지만 해당 데이터를 직접 콘솔에 출력하면 내용이 아니라 바디와 헤더의 특정 정보만 나와있을 뿐이죠. 그래서 우리는 이 정보를 알아보기 쉽게 앞서 언급한 json 메서드로 비동기 작업을 진행해 줄겁니다.</p>
<p>물론 json 메서드도 promise를 반환하는 메서드이니 뭘 사용할 수 있다? then과 catch를 사용할 수 있다~!</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript"> fetch(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;)
   .then((response) =&gt; {
   console.log(&quot;data received&quot;);
   // json 메서드를 호출하여 해당 데이터에 대한 변환 작업을 수행한 뒤 반환 받은 결과값에 따라 then, 혹은 catch문의 코드가 실행됨
   return response.json();
   })
   .then((formattedData) =&gt; {
   console.log(formattedData);
   })
   .catch((err) =&gt; {
   console.log(err);
   });</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/9c18da0f-e121-41b8-88a5-065e98785003/image.png" alt=""></p>
<p>이처럼 위의 사진 처럼 우리가 알아보기 쉽게 데이터가 JSON 형태로 정제 된 모습을 확인할 수 있는데요. 다만 첫번째 then 문에서 예외 처리(catch)를 추가하지 못했기 때문에 마저 예외 처리를 추가해 보도록 하겠습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript">fetch(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;)
  .then((response) =&gt; {
    console.log(&quot;data received&quot;);
    // json 메서드를 호출하여 해당 데이터에 대한 변환 작업을 수행한 뒤 반환 받은 결과값에 따라 then, 혹은 catch문의 코드가 실행됨
    return response.json();
  })
  .then((formattedData) =&gt; {
    console.log(formattedData);
  })
  .catch((err) =&gt; {
    console.log(&quot;파싱 에러:&quot;, err);
  })
  .catch((err) =&gt; {
    console.log(&quot;패치 에러:&quot;, err);
  });</code></pre>
<p>다만 catch문에 육안을 알아보기 힘들기 때문에, 첫번째 then 문에 결과값의 ok 속성(성공하면 true이고, 실패하면 false)의 값을 이용해 에러를 아래 코드와 같이 개별적으로 처리하는 코드도 작성이 가능합니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript">fetch(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;)
  .then((response) =&gt; {
    // 응답이 성공적이지 않은 경우(response.ok가 false인 경우) status(400 에러 코드 등)을 표시하며 에러를 표시합니다.
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    console.log(&quot;data received&quot;);
    return response.json();
  })
  .then((formattedData) =&gt; {
    console.log(formattedData);
  })
  .catch((err) =&gt; {
    console.log(&quot;파싱 에러:&quot;, err);
  });</code></pre>
<h1 id="3-json-정보-가져오기-async-await">3. JSON 정보 가져오기 (async, await)</h1>
<p>앞서 소개했듯이, 비동기 함수는 then, catch 뿐만 아니라 async, await 키워드로도 활용이 가능하다고 했죠? 이번에는 해당 API의 정보를 async와 await을 이용한 fetch 메서드로 처리하는 예제를 보여드리겠습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript">async function fetchData() {
&gt;
  // async, await을 사용할 경우 try로 감싸준 후 catch문을 처리해야함
  try {
    // fetch 호출 및 응답 대기
    const response = await fetch(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;);
&gt; 
    // 응답이 성공적이지 않은 경우 오류를 발생
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
&gt;
    console.log(&quot;async, await을 이용했습니다!&quot;);
&gt;
    // JSON 데이터로 변환
    const formattedData = await response.json();
&gt;
    // 변환된 데이터 출력
    console.log(formattedData);
&gt;    // catch문에서 오류 처리
  } catch (err) {
    console.log(&quot;파싱 에러:&quot;, err);
  }
}
&gt;
// 함수 호출
fetchData();</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/d891ee46-0bb5-4a39-9a51-1bf01377f88c/image.png" alt=""></p>
<h1 id="4-json-정보-보내기">4. JSON 정보 보내기</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Javascript] 비동기 함수 총정리 | async-await 키워드]]></title>
            <link>https://velog.io/@re_go/Javascript-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-%EC%B4%9D%EC%A0%95%EB%A6%AC-async-await-%ED%82%A4%EC%9B%8C%EB%93%9C</link>
            <guid>https://velog.io/@re_go/Javascript-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-%EC%B4%9D%EC%A0%95%EB%A6%AC-async-await-%ED%82%A4%EC%9B%8C%EB%93%9C</guid>
            <pubDate>Mon, 29 Jul 2024 09:34:32 GMT</pubDate>
            <description><![CDATA[<h1 id="1-then-catch를-이용한-비동기-함수-처리">1. then-catch를 이용한 비동기 함수 처리</h1>
<p>앞전에 배운 비동기 함수를 Promise 객체로 선언한 코드가 있다고 쳐보겠습니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript">function myProm(){
  return new Promise(resolve =&gt; {
    resolve(&#39;완료&#39;);
  })
}</code></pre>
<p>이때 반환 받은 Promise의 상태가 정상 상태(resolve)일 경우에 대한 코드로는 다음과 같이 then-catch를 이용해 작성할 수 있는데요.</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript">myProm().then(result =&gt; console.log(result))</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/re_go/post/ace2985b-dd58-489c-ae4e-6c8b6eba3696/image.png" alt=""></p>
<p>JS에서는 이러한 비동기 함수인 Promise의 결과값에 대한 처리를 then,catch문으로 작성하는데요. 이러한 방법 보다도 좀 더 직관적으로 코드를 작성할 수 있게 처리를 해주는 비동기 키워드도 존재합니다. 바로 async와 await 키워드죠.</p>
<h1 id="2-async와-await을-활용한-비동기-함수-실행">2. async와 await을 활용한 비동기 함수 실행</h1>
<p>async와 await은 비동기적인 코드의 흐름을 동기적인 방식으로 흐를 수 있도록 해주는 키워드로, 한마디로 비동기 API나 메서드를 동기적인 흐름을 갖도록 만드는 키워드입니다. 특히 API를 가져올 때 정보의 일관성을 유지하기 위해 사용되죠.</p>
<p>이때 함수의 앞쪽에는 async 키워드를, 비동기 함수의 결과값을 반환 받는 쪽(비동기 함수를 호출한 쪽)에서는 해당 함수의 반환값을 받는 역할을 하는 await 키워드를 아래의 코드와 같이 작성하게 됩니다.</p>
<blockquote>
</blockquote>
<pre><code class="language-javascript">async function print(){
  try{
  const result = await myProm();
  console.log(result);
  }catch(err){
      console.log(err)
  }
}
print();</code></pre>
<p>위와 같이 코드를 작성할 경우 myProm 비동기 함수의 결과값을 result에서 받게 되어 처리를 할 수 있는데요. 즉 then,catch를 사용할 때보다 코드를 좀 더 직관적으로 사용할 수 있습니다. </p>
<p>하지만 단점 또한 존재하는데요. async나 await을 사용할 때에는 함수 안에서 정의를 해주어야 된다는 점, catch문 처리를 위해 try-catch문으로 감싸서 처리를 해주어야 된다는 점이 존재합니다.</p>
<h1 id="then-catch와-async-await-간의-동기-작업-비교">then-catch와 async-await 간의 동기 작업 비교</h1>
<p>다음 코드는 then-catch와 async-await을 이용해 첫번째 작업부터 세 번째 작업까지의 비동기 작업을 동기화 하는 코드인데요. </p>
<p>확실히 async와 await 키워드를 이용한 코드가 좀 더 코드를 이해하는데 쉬운, 직관적인 코드임을 확인할 수 있습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>then-catch<pre><code class="language-javascript">function myProm2() {
 return new Promise(resolve =&gt; {
     setTimeout(() =&gt; {
         resolve(&#39;실행 2번&#39;);
     }, 5000);
 });
}
&gt;
function run() {
 // 첫 번째 비동기 작업
 new Promise(resolve =&gt; {
     setTimeout(() =&gt; {
         resolve(&#39;실행 1번&#39;);
     }, 500);
 })
 .then(result1 =&gt; {
     console.log(result1); // &#39;실행 1번&#39; 출력
&gt;
     // 두 번째 비동기 작업
     return myProm2();
 })
 .then(result2 =&gt; {
     console.log(result2); // &#39;실행 2번&#39; 출력
     console.log(&#39;실행 3번&#39;); // 마지막으로 실행됨
 })
 .catch(error =&gt; {
     console.error(&#39;에러 발생:&#39;, error);
 });
}
&gt;
run();</code></pre>
<blockquote>
</blockquote>
</li>
<li>async-await<pre><code class="language-javascript">function myProm2() {
 return new Promise(resolve =&gt; {
     setTimeout(() =&gt; {
         resolve(&#39;실행 2번&#39;);
     }, 5000);
 });
}
&gt;
async function run() {
 try {
     // 첫 번째 비동기 작업
     const result1 = await new Promise(resolve =&gt; {
         setTimeout(() =&gt; {
             resolve(&#39;실행 1번&#39;);
         }, 500);
     });
&gt;       
     console.log(result1); // &#39;실행 1번&#39; 출력
&gt;
     // 두 번째 비동기 작업
     const result2 = await myProm2();
     console.log(result2); // &#39;실행 2번&#39; 출력
&gt;
     console.log(&#39;실행 3번&#39;); // 마지막으로 실행됨
 } catch (error) {
     console.error(&#39;에러 발생:&#39;, error);
 }
}
&gt;
run();</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/re_go/post/76e7c570-26d3-43d3-a194-386887600761/image.png" alt=""></li>
</ol>
]]></description>
        </item>
    </channel>
</rss>