<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>JCoding.log</title>
        <link>https://velog.io/</link>
        <description>Hello :)</description>
        <lastBuildDate>Thu, 21 Aug 2025 05:04:30 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. JCoding.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/j_code" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[모바일 연결]]></title>
            <link>https://velog.io/@j_code/%EB%AA%A8%EB%B0%94%EC%9D%BC-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@j_code/%EB%AA%A8%EB%B0%94%EC%9D%BC-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Thu, 21 Aug 2025 05:04:30 GMT</pubDate>
            <description><![CDATA[<h2 id="aos">AOS</h2>
<ol>
<li>개발자모드 켜야함</li>
<li>개발자 모드 - USB디버깅 켜기</li>
<li>adb 켜야함 시간이 걸릴 수도 있으니 기다리기</li>
</ol>
<p>드라이브 연결이 안될 경우 
삼성 홈페이지에 삼성 디바이스 통합 모듈 다운받아서 재연결 해보기</p>
<h2 id="ios">IOS</h2>
<ol>
<li><p>리스트에 뜨지 않으면 애플 드라이브 재설치 해보기
1-1. 장치관리자에 들어가서 드라이브 설치 잘 되어있는지 보기</p>
</li>
<li><p>개발자모드가 활성화 안되어 있을 수 있는 경우
설정 - 개인정보 보호 및 보안 안에 <strong>개발자모드</strong> 있음 이거 켜주기</p>
</li>
<li><p>그리고 개발자에서 &#39;UI 자동화 활성화&#39; 버튼 활성화 해줘야 될 수 있음</p>
</li>
<li><p>아이폰 디벨로퍼 추가하기
4-1. <a href="https://developer.apple.com/kr/">https://developer.apple.com/kr/</a> 들어가기
4-2. 계정 클릭
4-3. 아이디 : <a href="mailto:checkmate@symation.co.kr">checkmate@symation.co.kr</a> 비번 : 시~1!인데 ㅅ이 대문자
4-4. 기기(영문) 클릭
4-5. 클릭해서 추가 후 UDID입력 후 등록
<img src="https://velog.velcdn.com/images/j_code/post/99b7bdda-eda7-4a02-9686-1a855b7ecfa2/image.png" alt="">
&lt;UDID 찾는 법&gt; 
보통 아이튠즈에도 뜨지만, 폰 연결 후 cmd에 DeviceConnector 주소창 들어가서 <strong>ios list</strong> 라고 명령어치면 아래 사진처럼 밑에 UDID가 뜬다.
<img src="https://velog.velcdn.com/images/j_code/post/39d47c69-7c5a-4aa2-9a14-a77532ab7097/image.png" alt="">
4-6. profile 클릭 - 보통 POC는 CheckMATE에 있음
<img src="https://velog.velcdn.com/images/j_code/post/9e0dd09e-1e3b-4497-b218-4adab2f2b2ed/image.png" alt="">
4-7.  Edit - 아래쪽  device 중에서 원하는 거 추가 클릭
<img src="https://velog.velcdn.com/images/j_code/post/aa093c1a-c674-4956-a058-5937d2ad54de/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_code/post/d1fd693d-3a3a-4b04-8de9-db33fdaceb01/image.png" alt="">
4-8. 저장 후에 다운로드 하면 provision이 다운 받아짐
<img src="https://velog.velcdn.com/images/j_code/post/d47770ac-f93c-4ca4-8ebf-039cc98f56ee/image.png" alt="">
4-9. provision 다운 다 받아지면, 인증서 등록하기
<img src="https://velog.velcdn.com/images/j_code/post/dedf43ff-8b46-4a15-a91d-90086cdaa7f1/image.png" alt=""></p>
</li>
</ol>
<h3 id="ios-드라이버-문제">ios 드라이버 문제</h3>
<ol>
<li><p>아래와 같이 Apple UsbMux가 뜨지 않으면 드라이버 다시 깔기
<img src="https://velog.velcdn.com/images/j_code/post/4f5e817d-73e5-4b8d-af09-6476376ff509/image.png" alt=""></p>
</li>
<li><p>아래 사진처럼 범용이 두개가 나올 경우에는 드라이브 재설치해야한다.
<img src="https://velog.velcdn.com/images/j_code/post/f8ca5cf9-6a91-47ca-b6e3-fdf30f8472fe/image.png" alt=""></p>
<ul>
<li>드라이브 재설치 하는 법
<img src="https://velog.velcdn.com/images/j_code/post/1b48aec5-80af-49be-838a-88e5bc20b71f/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_code/post/e76eff2c-762a-4cce-8ff3-5c5fdefd9579/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_code/post/fa0b4868-8578-414d-8093-d28bb14cb02a/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_code/post/7788ecde-b10d-4813-9f65-13f08c475afc/image.png" alt=""></li>
</ul>
</li>
</ol>
<ul>
<li>sign이라는 오류 문구 뜨면 인증서쪽 문제</li>
<li>display check 오류 문구 뜨면 tunnel 문제일 가능성 있음
(이때, mobile connector 다 끄고 해야한다)</li>
</ul>
<ol>
<li>관리자권한으로 cmd 실행 </li>
<li>cd C:\CheckMATE\DeviceConnector_iOS 입력 </li>
<li>ios tunnel ls 입력, ios tunnel start 입력 해보기</li>
</ol>
<ul>
<li><p>app install이 잘 안될 경우는 인증서쪽 문제 한번 물어보기</p>
</li>
<li><p>기존 애플드라이버가 설치가 되었는데도 안됐을 경우
iTunes64Setup.exe를 iTunes64Setup.zip으로 확장자를 바꾼다음에 압축을 풀고 폴더에 들어가면 AppleMobileDeviceSupport64.msi 이걸 설치하면 된다</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[성능테스트]]></title>
            <link>https://velog.io/@j_code/%EC%84%B1%EB%8A%A5%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@j_code/%EC%84%B1%EB%8A%A5%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Mon, 29 Jul 2024 08:39:19 GMT</pubDate>
            <description><![CDATA[<h2 id="사전미팅">사전미팅</h2>
<h3 id="알아야-할-단어">알아야 할 단어</h3>
<ul>
<li><strong>TPS</strong> : 초당 처리건수 - 높아야 좋은 거</li>
<li><strong>가상유저</strong></li>
<li><strong>Concurrent user</strong> :동시접속자</li>
<li>부하환경
: 개발 서버(Dev) - 개발자들이 개발하는 서버
: <strong>스테이징 서버(Stg)</strong> - &#39;테스트 서버&#39;, &#39;QA 서버&#39;로도 불림. 운영서버의 클론 형태.
: 운영 서버(Prod) - 실제 사용하는 운영서버</li>
<li>모니터링 </li>
<li><strong>APM</strong> : 어플리케이션 성능 모니터링
APM 종류로는 whatap APM, elastic APM 등이 있다. </li>
</ul>
<h3 id="사전-미팅때-물어봐야-할-것들">사전 미팅때 물어봐야 할 것들</h3>
<ul>
<li>가상유저는? </li>
<li>대상 업무는?</li>
<li>부하환경 구축은?</li>
<li>모니터링은? AWS환경 와탭 등의 단어들이 나올 수 있다.</li>
<li>테스트 후 마무리 작업은? : DB를 날리는 작업 등을 알아서 해달라는 식</li>
<li>부하목표치는?</li>
</ul>
<h2 id="러닝메이트">러닝메이트</h2>
<ul>
<li>Controller : 부하발생, 모니터링 역할 (약간 스케줄 거는 느낌)</li>
<li>Editor : 스크립트 짜는 거</li>
<li>RptMan : 리포트매니저 - 결과파일 확인할 때 사용</li>
<li>Agent : 봇(유저), 포트는 9800,9801</li>
</ul>
<h2 id="성능테스트">성능테스트</h2>
<h3 id="1-스크립트">1. 스크립트</h3>
<ol>
<li><p>먼저 폴더 생성해놓기
(01.script 02.scenario 03.result 04.document 99.etc)</p>
</li>
<li><p>프로젝트 생성 
(<code>SN01_시나리오이름_TC01_업무이름</code>)</p>
</li>
<li><p>녹화하기
녹화할 때 팁! - F12눌러서 확인하는데, 약간 {i} 이 표시되어있는 거 확인하기(캐시메모리 먹는 거 위주로 확인하기)
⭐️녹화 후에 한 단위?마다 FindData(&quot;&quot;) 꼭 해주기!!</p>
</li>
</ol>
<ul>
<li><p>set_autoredirect(0) : 리다이렉션을 안시켜주는 느낌쓰...(로그인할 때 사용하시는 거 봄)</p>
</li>
<li><p>녹화 잘못 했을 때 삭제하는 법
0) 녹화 잘못 되어도 중간에 끄지 말고 우선 파일 만들기
1) 녹화된 그 파일 자체를 삭제
2) 왼쪽에서 두번째꺼 클릭해서 그 전 데이터를 다 삭제
3) 스크립트 아예 껐다가 다시 켜서 녹화 진행하기</p>
</li>
</ul>
<ol start="4">
<li><p>필요한 부분만 Begin()에 넣기</p>
<ul>
<li>로그인 부분을 보통 넣음</li>
<li><code>FindDataSave(&quot;state&quot;, &quot;state=&quot;, &quot;&amp;&quot;, &quot;&quot;);</code>
<code>FindDataSave(&quot;transaction_id&quot;, &quot;transaction_id=&quot;, &quot;&amp;&quot;, &quot;&quot;);</code>
이런 식으로 설정을 해놓으면 들고온 값 중에서  &#39;transaction_id=&#39;로 시작하는 부분에서 &#39;&amp;&#39;로 끝나는 부분까지 값을 &quot;transaction_id&quot;라는 변수에 저장하게 된다.
그러면 나중에 적용하고 싶을 경우에는 <strong><code>{transaction_id}</code></strong>로 작성하면 된다.
<img src="https://velog.velcdn.com/images/j_code/post/6666e17a-886e-42bc-9986-38e910daae8e/image.png" alt=""></li>
</ul>
</li>
<li><p>그리고 cs 파일인 Enerypto.cs에 <strong>메서드</strong>를 만들면 Begin()에서 사용가능.</p>
</li>
<li><p>그리고 <strong>IncludeFile Viewer</strong>에서 변수 user를 만들고 엑셀파일에 첫행에 id입력 후, 2행부터 아이디를 입력하게되면 Begin()에서 <strong><code>{user/id}</code></strong> 형태로 사용 가능.
또는 ⭐️ <strong><code>GetParam(&quot;user/id&quot;)</code></strong> 이렇게 사용하기!! getparam이 더 정확한 방법이다.</p>
<blockquote>
<p>InputData(&quot;&amp;p_lgn_id=&quot; + HtmlEncode(GetParam(&quot;login/id&quot;) + &quot;@naver.com&quot;)); 
이렇게 사용함!!!
<img src="https://velog.velcdn.com/images/j_code/post/849505a5-26c0-47ee-af42-27bdb0eea0f0/image.png" alt=""></p>
</blockquote>
</li>
</ol>
<ul>
<li>아래 사진이 위의 4~6번 설명 한번에 사용한 것 
<img src="https://velog.velcdn.com/images/j_code/post/5d816637-a5a7-48e9-b67e-12e051d8be7c/image.png" alt=""></li>
</ul>
<ol start="7">
<li><p>Script_Action()부분은 제일 처음 만든 TC01을 복사한 후, 사용. 
여기서 왼쪽 Project Viewer에서 프로젝트 이름 변경 해주고,
Action()부분에서 using (new Transaction(this, &quot;여기부분&quot;)) 여기부분을 변경된 프로젝트이름으로 바꿔줘야 한다.</p>
</li>
<li><p>스크립트 들고 오는 법
만약 로그인을 하는데 테스트 하는 것에 주된 것이 로그인이 아닐 경우 진짜 로그인하는 것만 들고오기!
근데 만약 사이트의 특정 페이지가 잘 나오는지 체크하는 거면, 특정 페이지 나오고 그 뒤에 딸려오는 리스트페이지 같은 거까지 들어 가야한다. </p>
</li>
</ol>
<hr>
<h3 id="2-시나리오controller">2. 시나리오(Controller)</h3>
<p>&lt;컨트롤러&gt;에서 작성</p>
<h4 id="scenario-setting-설정">Scenario Setting 설정</h4>
<ol start="0">
<li>라이선스 등록(NewKey) : 인증키 게시판에 올리면 댓글로 수석님 남겨주심.</li>
<li>telnet 확인하기 : <strong>telnet ip port</strong> 입력</li>
<li>시나리오 생성</li>
<li>시나리오 경로 지정</li>
<li>뭔가 설정하는게 나오는듯 </li>
<li>스크립트 드래그 아래 붙이기</li>
<li>Allocate VUsers에서 % 설정해주기</li>
<li>오른쪽 마우스 클릭 후 Play Options 클릭</li>
<li>Play Option(선택) - 에러 발생 시 동작 중단 체크 풀기</li>
<li>Pacing Time : 1초 정도로 설정하는 듯</li>
<li>SSL Setting(필수) - 전체 클릭</li>
<li><strong>Agent 설정</strong>  : local, 봇1, 봇2 이렇게 추가로 만들어서 ip를 설정해줘야 한다. 포트는 9800,9801</li>
<li><strong>VUser 설정</strong> : Controller 왼쪽에 Begin, Action, End 더블 클릭 하기!
Begin : &#39;Action 실행 전 초기화 실행&#39; 설정
Action : &#39;순차 실행&#39;으로 &#39;1초&#39; 간격으로 설정(1초가 통상적인듯)
End : &#39;즉시 실행&#39; 클릭
<img src="https://velog.velcdn.com/images/j_code/post/d714528a-9f2f-4582-a732-94b96f5dbd00/image.png" alt=""></li>
</ol>
<blockquote>
<p>이론적 설명</p>
</blockquote>
<ul>
<li>Pacing Time(선택)- 1회 수행 시간 고정 클릭 3000ms로 고정하게 되면 1회에 3초로 설정됨, 내가 쏘고 나서 3초 걸림
Pacing = 간격 </li>
<li><blockquote>
<p>성능결과로 1초동안 업무가 수행된다고 나옴. 그럼 3초 중 1초업무하고 2초 기다렸다가 한다. 약간 3초안에 업무 다 보는 느낌. 만약 4초가 걸렸다? 그럼 바로 2초 안기다리고 바로 4초뒤에 1초업무 수행함.</p>
<ul>
<li>Think Time(선택) - 업무 요청 응답 후 30초</li>
</ul>
</blockquote>
</li>
<li><blockquote>
<p>3초를 줬다면? 1초 업무를 하고 업무가 끝난 후에 3초를 기다린다. </p>
</blockquote>
</li>
</ul>
<blockquote>
<ul>
<li>VUser 설정 설명</li>
</ul>
</blockquote>
<p>Controller 왼쪽에 Begin, Action 등이 있음.
이천포럼에서는 100VUser 1초 간격으로 설정함.
만약 Begin에서 순차 실행으로 하게 되면, Begin에서만 100유저씩 들어가서 400유저라고 치면 4초만에 로그인이 끝나버린다.
&#39;Action 실행 전 초기화 실행&#39;하면, Action까지 실행 후에 그 담에 또 100유저가 들어간다.
그리고 Begin에서 <strong>using (new Transaction(this, &quot;이름1&quot;))</strong> 과 using (new Transaction(this, &quot;이름2&quot;))를 나눠서 설정하게 되면, 나중에 결과 부분에서 이 부분의 트랜잭션 <strong>이름1과 이름2가 다 나눠져서 나오게 된다</strong>.</p>
<h4 id="run-dashboard-설정">Run DashBoard 설정</h4>
<ol>
<li><p>ServerMonitor 설정 : Add Server 해서 입력하기
-&gt; 필요한 정보 : IP, 계정ID, 계정PW
<img src="https://velog.velcdn.com/images/j_code/post/86a2426d-9586-4eb0-b18e-216d103b30fb/image.png" alt=""></p>
</li>
<li><p>ServerMonitor 설정 : Command List 추가 입력하기
실행명령에 &#39;uname&#39;이라고 치면 result로 LINUX인지, 뭔지 나온다.
결과 보고 템플릿불러오기를 클릭 후 LINUX 등을 선택 후 확인
(사보정은 LINUX CPU 클릭해서 설정함)
<img src="https://velog.velcdn.com/images/j_code/post/4489bf0f-5e20-4c0a-8109-e80a940c277f/image.png" alt=""></p>
</li>
<li><p>Add Chart 추가하기 : 우클릭 후 &#39;차트 종류 선택&#39; 눌러서 맨 밑의 CPU 선택 후에 &#39;CPU Useage&#39; 탭에 추가한다.</p>
</li>
</ol>
<h3 id="3-agentaws-세팅법">3. Agent(AWS 세팅법)</h3>
<p>AWS 상태일 때는 기본적으로 영어로 세팅 되어 있기 때문에, 한글이 깨지는 경우가 있다. 
한글 깨질 때, <strong>관리자권한</strong>으로 꼭 들어가기.
(필기내용그대로라 정확하지 않음)</p>
<ol>
<li>오른쪽 아래 날짜 클릭 -&gt; 지역 -&gt; 오른쪽 -&gt; 관련 설정 -&gt; &#39;추가날짜..&#39; -&gt; 국가 또는 지역 -&gt; 형식(한국어) -&gt; 관리자옵션 -&gt; <strong>시스템 로켈 변경</strong></li>
<li><strong>방화벽 다 내리기</strong>(필수)</li>
<li><strong>cmd에서 &#39;netstat -an|findstr 9800&#39;</strong> or  &#39;netstat -an|findstr 9801&#39; 검색해서 <strong>Listening</strong> 상태 확인하기</li>
</ol>
<h2 id="결과보고서-작성">결과보고서 작성</h2>
<h3 id="부하tps--응답속도">부하TPS / 응답속도</h3>
<blockquote>
<ul>
<li><strong>유저수(명)/부하간격(초) = 부하TPS</strong>
ex) 4000/3 = 1,333.3</li>
<li>응답속도
응답속도는 1초이하가 나와줘야 한다 -&gt; 1초단위이인데 1초보다 늦게 나오면 성능이 안좋은거.</li>
</ul>
</blockquote>
<h3 id="reportmanager-설정">ReportManager 설정</h3>
<ol start="0">
<li><p>NewChart설정(3가지)</p>
<ul>
<li>VUser Count + TPS(Total)</li>
<li>VUser Count + TPS(Transaction) : 개별TPS</li>
<li>VUser Count + Response Time(Transaction) : 평균 응답속도</li>
</ul>
</li>
<li><p>분리 조회 하기(VUser)</p>
</li>
<li><p>범례 정보창 클릭</p>
</li>
<li><p>조회 주기 &#39;1&#39;로 설정</p>
</li>
<li><p>Avg 보기</p>
</li>
<li><p>그림 클립보드 복사할 때는 조회 주기 &#39;5&#39;로 설정</p>
</li>
</ol>
<h3 id="보고서-작성">보고서 작성</h3>
<ol>
<li>로그인 하는 부분은 0초~로그인 끝나는 시간 지정해서 확인하기</li>
<li>차트 정보 클립보드에 복사하기</li>
<li>로그인하는 부분의 값만 정리하기</li>
<li>개별TPS는 Avg에서 소수점 한자리수</li>
<li>응답속도는 Avg에서 1000으로 나누고 소수점 세자리수</li>
<li>WAS, WEB, DB CPU 등 거기서 제공하는 모든 것 다 CPU도 작성해야함</li>
</ol>
<hr>
<h2 id="성능-이론-공부">성능 이론 공부</h2>
<ul>
<li><p>ASYNK와 SYNK
보통 메인페이지에 request가 많은데 그럴 경우 사용된다.
ASYNK냐 아니냐는 담당 개발자에게 물어봐야 한다.
ASYNK는 요청이 멀티
SYNK는 요청이 순차적
ASYNK를 스크립트에서 작성을 하게 되면, RequestAsyncAbortAll() 메서드를 꼭 써줘야하는듯
ASYNK는 그 한 문단? 마다 끝에 작성해주는듯.</p>
</li>
<li><p>Putty
DB정보 불러올 때 사보정에서 사용</p>
</li>
<li><p><em>Putty 명령어 : sar 5 1000*</em>(푸티에서만 사용가능) -&gt; 5초 간격으로 1000개 들고오겠다는 뜻이다. 
값 들고오는 법(정확하지않음) : change setting -&gt; logging -&gt; log file지정 -&gt; printable output -&gt; apply</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[용어 정리]]></title>
            <link>https://velog.io/@j_code/%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@j_code/%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 10 Jul 2023 08:39:13 GMT</pubDate>
            <description><![CDATA[<h2 id="edielectronic-data-interchange">EDI(Electronic Data Interchange)</h2>
<h3 id="개념">개념</h3>
<p>비즈니스 파트너 간에 필요한 각종 서류를 표준화된 상거래 서식이나 합의된 통신 표준에 따라 문서를 컴퓨터간 교환하는 것을 뜻함.</p>
<h3 id="필요성">필요성</h3>
<p>수신자의 컴퓨터에 있는 적절한 어플리케이션으로 문서가 전달되어 바로 데이터 처리가 가능해지게 한다.(구매, 주문, 물류, 배송 등이 자동적으로 수행될 수 있게끔 하는 것)</p>
<h3 id="장점">장점</h3>
<p>운영 비용절감, 비지니스 사이클 속도 향상, 인적 오류 감소 및 기록 정확도 향상, 거래 보완 강화</p>
<h2 id="ssosingle-sign-on">SSO(Single Sign-On)</h2>
<h3 id="개념-1">개념</h3>
<p>1회 사용자 인증으로 다수의 애플리케이션 및 웹사이트에 대한 사용자 로그인을 허용하는 인증 솔루션입니다.</p>
<h3 id="장점-1">장점</h3>
<p>암호보완강화, 비용절감, 보안 태세 개선. </p>
<h2 id="eamextranet-access-management">EAM(Extranet Access Management)</h2>
<h3 id="개념-2">개념</h3>
<ul>
<li><p>서로 다른 플랫폼이나 운영체제, web서버, 어플리케이션 간의 사용자 권한관리를 할 수 있는 솔루션</p>
</li>
<li><p>각 구성원의 데이터 접근권한을 미리 정하고, 업무 변화에 따라 다시 권한을 조정할 수 있는 기술방식.</p>
</li>
<li><p>다양한 응용 플랫폼 환경에서 효과적인 통합인증(SSO)환경과 이를 기반으로 통합된 권한 관리(Access Control)시스템을 구축하기 위한 접근제어시스템.</p>
</li>
</ul>
<h3 id="특징">특징</h3>
<p>sso+권한관리+자원관리기능+보안정책수립지원 기능.
<img src="https://velog.velcdn.com/images/j_code/post/fdc86f02-6535-4356-9b53-3e995de328ab/image.png" alt=""></p>
<h2 id="drmdigital-right-management">DRM(Digital Right Management)</h2>
<h3 id="개념-3">개념</h3>
<p>정보보호 기술 중 하나로 암호화 기술을 이용해서 비허가 사용자로부터 디지털 컨텐츠를 보호하게 하는 기술을 말한다. </p>
<h3 id="종류">종류</h3>
<ol>
<li>문서 DRM : 기업들이 내부에서 사용하는 문서들을 제어하기 위한 기술. 기업 내부에는 보호해야할 중요문서나 개인정보 포함 문서가 많기 때문에 문서관리 시스템과 연동하여 문서에 접근하거나 읽을 수 있는 사용자들에 대해 구별할 수 있도록 도와준다. </li>
<li>멀티미디어 DRM : 동영상이나 음악 컨텐츠에 DRM 기술을 적용하는 것을 말한다. 음악이라는 컨텐츠를 사용자가 자신의 이용권에 따라서 다운받고 듣는 차등적인 정책같은 것이다. </li>
</ol>
<h2 id="was">WAS</h2>
<h3 id="개념-4">개념</h3>
<p>인터넷 상에서 Http 프로토콜을 통해 사용자 컴퓨터나 장치에 애플리케이션을 수행해주는 미들웨어로서, 주로 <strong>동적 서버 컨텐츠를 수행</strong>하는 것으로 <strong>웹 서버와 구별</strong>되며, 주로 데이터베이스 서버와 같이 수행. 
<strong>동적인 페이지를 처리하기위해서.</strong> 
와스는 웹서버와 웹 컨테이너가 합쳐진 말이다. 웹 서버 단독으로는 처리할 수 없는 데이터베이스의 조회나 다양한 로직 처리가 필요한 동적 컨텐츠를 제공한다. jsp, servlet환경을 제공해줘서 웹 컨테이너 혹은 서블릿 컨테이너라고 불린다. </p>
<p>상반되는 개념으로 웹서버가 있는데, <strong>웹서버는 정적인 콘텐츠</strong>를 제공하는 서버이다. 
여기서 말하는 정적인 콘텐츠란 단순 HTML문서, CSS, javascript와 같은 즉시 응답이 가능한 컨텐츠를 말한다. 
<img src="https://velog.velcdn.com/images/j_code/post/f3609529-7962-4f2a-93a5-3b3363eb4b4d/image.png" alt=""></p>
<p>웹서버를 앞에 두고 WAS를 뒤에 둔다면, 정적인 것을 웹서버가 처리해주고 WAS가 동적인 것을 처리해주면 좀 더 효율적으로 분산 처리가 가능해진다. </p>
<blockquote>
</blockquote>
<p>EDI : 표준화된 서식이나 합의된 표준에 따라 문서를 컴퓨터간 교환하는 것
전자 문서 교환. 컴퓨터 대 컴퓨터로 문서를 교환하는 시스템.</p>
<blockquote>
</blockquote>
<p>sso: 1회 사용자 인증으로 다수의 사이트에 로그인을 허용하는 인증솔루션.</p>
<blockquote>
</blockquote>
<p>eam : 각 구성원의 데이터 권한을 미리 정하고, 업무 변화에 따라 다시 권한을 조정할 수 있는 기술방식. 통합된 권한 관리를 하기 위한 접근제어시스템.</p>
<blockquote>
</blockquote>
<p>drm : 정보보호 기술 중 하나로, 암호화 기술사용.  디지털 컨텐츠</p>
<blockquote>
</blockquote>
<p>was : 동적인 서버 컨텐츠를 수행. 웹서버와 함께 사용하는 것이 좋다. </p>
<h2 id="리눅스의-종류">리눅스의 종류</h2>
<ul>
<li>우분투 리눅스(ubuntu linux)</li>
<li>페도라 리눅스(fedora linux)</li>
<li>칼리 리눅스(kali linux)</li>
<li>레드햇(red hat linux) 리눅스</li>
<li>리눅스 민트(linux mint)</li>
<li>센트os(centos) : 레드햇의 계열.</li>
</ul>
<h2 id="adb">ADB</h2>
<p>안드로이드 디버그 브릿지 (adb)는 안드로이드 기반 에뮬레이터 혹은 실제 휴대기기와 통신할 수 있게 해주는 커맨드라인 툴이다. 
앱 설치 / 로그캣 명령어 확인 / 디버깅 등 앱 개발에 유용한 명령어 뿐만아니라 Unix shell에 접근할 수 있게 함으로서 기기 내 파일 관리, 프로세스 확인 등 다양한 작업을 가능하게 한다.</p>
<h2 id="rdpremote-desktop-protocol">RDP(Remote Desktop Protocol)</h2>
<p>말 그대로 원격으로 컴퓨터에 접속할 수 있는 프로토콜.</p>
<p>컴퓨터에 원격으로 접속하여 마치 직접 그 컴퓨터를 사용하는 것처럼 작업할 수 있게 도와줌.</p>
<h2 id="ssh와-rdp의-차이">SSH와 RDP의 차이</h2>
<p><a href="https://spiralwind7.tistory.com/127">https://spiralwind7.tistory.com/127</a>
RDP : 윈도우에서 사용되는 원격.
SSH : 원격 호스트에 접속하기 위해 사용되는 보안 프로토콜. 리눅스에서 사용되는 원격. 보안을 중요시 여긴다. 텔넷과 비교가 되는데, 텔넷보다 훨씬 보안적인 측면에서 더 좋다. </p>
<h2 id="ftp">FTP</h2>
<p>파일전송프로토콜. 파일을 공휴할 때 사용되는 프로토콜.</p>
<h2 id="이더넷">이더넷</h2>
<p>대표적인 컴퓨터 네트워크 기술 중 하나. LAN을 유선으로 구현해내는 방식.
<a href="https://brunch.co.kr/@swimjiy/49">https://brunch.co.kr/@swimjiy/49</a></p>
<h2 id="텔넷">텔넷</h2>
<p>telnet은 클라이언트가 서버의 특정 포트로 잘 연결되는지 확인하기 위한 도구로 가장 많이 활용.포트가 열려있는지 왜 확인하는 것인가? 이런식으로 다른 외부 장비와의 서버 통신 가능 상태를 확인할 때 사용하게된다. 
외부에서 접속이 가능하도록 해주는 것이 포트이다.
포트확인용을 많이 사용하는 프로그램은 tcping이다.</p>
<hr>
<p>인프라
인프라는 크게 3가지로 나눌 수 있다.
네트워크, 서버, DB</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DBCP - Database Connection Pool]]></title>
            <link>https://velog.io/@j_code/DBCP-Database-Connection-Pool</link>
            <guid>https://velog.io/@j_code/DBCP-Database-Connection-Pool</guid>
            <pubDate>Fri, 02 Jun 2023 04:52:28 GMT</pubDate>
            <description><![CDATA[<p><code>클라이언트가 요청을 보낸다 -&gt; 로직(커맨드클래스) -&gt; 연결 -&gt; 데이터베이스(질의연산)</code></p>
<p>=&gt;여기서 연결하는 시간을 제거하려고 함.</p>
<h2 id="dbcp---database-connection-pool">DBCP - Database Connection Pool</h2>
<p>담아두는 공간 : 커넥션을 미리 만들어 놓는거다. 요청이 들어왔을 때, 실시간을 연결하는 것이 아니다. 커넥션객체가 얻어지면 연결이 된거. 그러니까 커넥션을 담아두는 공간이라고 볼 수 있는 듯.</p>
<p>요청하기 전에 미리 커넥션객체를 만들어 놓고 pool에 저장해둔다. 미리 데이터베이스랑 연결하는 걸 유지하고 있어서 만약 요청을 하면 자 미리 연결해놓은 거 써라고 꺼내주고, 다 쓰면 다시 pool에 넣는다. 
어떻게 구현하느냐? 직접만들수도 있지만, 검증이 된 라이브러리에서 많이 쓰는데, 돈주고 사서 쓰는 와스에서 사용함. 우리는 돈없어서 라이브러리를 이용할 거얌.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/7a1f6452-186b-468d-a3d1-549830368400/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_code/post/0bda0073-0a4c-46cb-a323-90e2637f5825/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_code/post/65ff4ff9-f2ee-4c06-8877-263a257c7b34/image.png" alt=""></p>
<p>DBCP가 더 빨리 연결해준다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[sql(셀프조인,LIMIT,EL,JSTL)]]></title>
            <link>https://velog.io/@j_code/sql%EC%85%80%ED%94%84%EC%A1%B0%EC%9D%B8LIMITELJSTL</link>
            <guid>https://velog.io/@j_code/sql%EC%85%80%ED%94%84%EC%A1%B0%EC%9D%B8LIMITELJSTL</guid>
            <pubDate>Thu, 01 Jun 2023 10:03:14 GMT</pubDate>
            <description><![CDATA[<h2 id="셀프조인">셀프조인</h2>
<p>자기 자신의 것을 스스로 참조하는거 = 셀프조인</p>
<p>CREATE TABLE students (
    s_id        INT            PRIMARY KEY        AUTO_INCREMENT,
    s_name        CHAR(20)    NOT NULL,
    s_manitto    INT
);</p>
<p>INSERT INTO students (s_name, s_manitto) values
(&#39;a&#39;,2), (&#39;b&#39;,3), (&#39;c&#39;,null), (&#39;d&#39;,1);</p>
<p>SELECT * FROM students;
<img src="https://velog.velcdn.com/images/j_code/post/6f1def59-3853-411c-8876-45c074ae160e/image.png" alt=""></p>
<p>--각 학생에 이름과 마니또 이름을 표시하라.
SELECT s1.s_name, s2.s_name
FROM sudents s1, student s2    --논리적으로 두개로 본다. 
WHERE s1.s_id = s2.s_manitto;</p>
<p>SELECT s1.s_name, s2.s_name
FROM students s1 INNER JOIN students s2<br>ON s1.s_id = s2.s_manitto;
<img src="https://velog.velcdn.com/images/j_code/post/598868fc-1af4-4f15-b4fe-9a0cfad48fed/image.png" alt=""></p>
<p>SELECT s1.s_name, s2.s_name
FROM students s1 LEFT OUTER JOIN students s2<br>ON s1.s_id = s2.s_manitto
WHERE s2.s_manitto IS NULL;
<img src="https://velog.velcdn.com/images/j_code/post/e03689bb-3fa0-4e27-a8e6-d0b8d3f7dcad/image.png" alt=""></p>
<hr>
<h2 id="limit">LIMIT</h2>
<p>mysql에만 있다. 
앞의 숫자는 시작위치(인덱스처럼 0부터 시작)
뒤의 숫자는 몇개를 보여줄 지 결정하는 숫자.
이걸 가지고 페이징처리를 한다.
SELECT * FROM products LIMIT 3; -- 3넣으니까 3줄만 나온다. 
SELECT * FROM products LIMIT 2, 3; -- 2는 시작위치. 인덱스와 비슷한 느낌. 리밋은 mysql밖에 없다. 이걸 가지고 <strong>페이징처리</strong>를 할 것이다.</p>
<h3 id="bookdaojava">bookDao.java</h3>
<p>여기 안에 2개의 메서드를 적어준다.
getList()는 한페이지에 3개씩 나눠서 list를 구성한다는 것 같고,
getTotalCount()는 테이블의 레코드(행)의 총 개수를 구하는 것.</p>
<pre><code>

public Vector&lt;Book&gt; getList(Connection con, int pageNum, int perPage) {
        Vector&lt;Book&gt; list = new Vector&lt;Book&gt;();
        String sql = &quot;SELECT * FROM a_book ORDER BY b_num DESC LIMIT ?, ?&quot;;
        PreparedStatement pStmt = null;
        ResultSet rs = null;

        try {
            pStmt = con.prepareStatement(sql);
            pStmt.setInt(1, (pageNum -1) * perPage);
            pStmt.setInt(2, perPage);
            rs = pStmt.executeQuery();
            while(rs.next()) {
                list.add(rowMapping(rs));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rs);
            close(pStmt);
        }
        return list;

    }

    public int getTotalCount(Connection con) {
        int count = 0;
        String sql = &quot;SELECT COUNT(*) FROM a_book&quot;;
        PreparedStatement pStmt = null;
        ResultSet rs = null;
        try {
            pStmt = con.prepareStatement(sql);
            rs = pStmt.executeQuery();
            if(rs.next()) {
                count = rs.getInt(1); //열 순서임! 
                //이름으로 들고오면 문제가 있다. 
                //접근하는 속도도 이게 더 빠름. 
                //첫번째 열을 들고오라는 뜻인듯.            
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rs);
            close(pStmt);
        }
        return count;
    }</code></pre><h3 id="listcmdjava">listCmd.java</h3>
<p>그 다음 listCmd를 아래와 같이 바꿔준다.</p>
<pre><code>public class ListCmd implements ICmd { 
//이걸 잘알아야 한다. 페이징하는 연산은 다 이렇게 생김. 
페이징하려면 뭐가 필요한지 알지.
    public void action(HttpServletRequest request) {
        int perPage = 3;
        int pageNum = 1;
        String temp = request.getParameter(&quot;pageNum&quot;);
        if(temp != null) {
            pageNum = Integer.parseInt(temp);
        }
        BookDao dao = BookDao.getInstance();
        Connection con = dao.connect();

        int totalCount = dao.getTotalCount(con); //몇페이지까지 나오는지..
        int pageCount = totalCount / perPage;
        if(totalCount % perPage !=0) {
            pageCount++; //딱 안떨어지면 한페이지 늘여야 하니까.
        }

        request.setAttribute(&quot;list&quot;, dao.getList(con,pageNum, perPage));
        dao.disconnect(con);

        request.setAttribute(&quot;pageNum&quot;, pageNum); 
        request.setAttribute(&quot;pageCount&quot;, pageCount); 
    }
}
</code></pre><h2 id="elexpression-language">EL(Expression Language)</h2>
<p>&lt;%= %&gt; , out.println()과 같은 자바코드를 더 이상 사용하지 않고 좀더 간편하게 출력을 지원하기 위한 도구.
배열, List, map도 가능.</p>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/37d9b1fc-c67a-4319-83ce-70a956dd6f73/image.png" alt=""></p>
<pre><code>&lt;%@page import=&quot;java.util.Hashtable&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@page import=&quot;kr.ac.green.Some&quot; %&gt;
&lt;%@page import=&quot;java.util.*&quot; %&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;elEx.jsp&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
//값을 잡아넣는 것은 jsp에서 하는 일이 아니다. 
%&gt;

&lt;%
    pageContext.setAttribute(&quot;myString&quot;, &quot;aaa&quot;); //영역이 다른데 이름이 같은 속성
    request.setAttribute(&quot;myString&quot;, null);    

    Some s = new Some(100, &quot;myValue&quot;);
    session.setAttribute(&quot;some&quot;, s);


    Vector&lt;String&gt; vec = new Vector&lt;String&gt;();
    vec.add(&quot;a&quot;);
    vec.add(&quot;b&quot;);
    vec.add(&quot;c&quot;);
    vec.add(&quot;d&quot;);

    request.setAttribute(&quot;list&quot;, vec);

    Map&lt;String, String&gt; map = new Hashtable&lt;String,String&gt;();
    map.put(&quot;aKey&quot;, &quot;aValue&quot;);
    map.put(&quot;bKey&quot;, &quot;bValue&quot;);
    map.put(&quot;cKey&quot;, &quot;cValue&quot;);
    map.put(&quot;dKey&quot;, &quot;dValue&quot;);

    request.setAttribute(&quot;map&quot;, map);

    Vector&lt;String&gt; otherList = new Vector&lt;String&gt;();
    request.setAttribute(&quot;otherList&quot;, otherList);

    request.setAttribute(&quot;arr&quot;, new int[]{});
%&gt;

${ list[2] } ${ map[&quot;cKey&quot;] } ${ empty otherList } ${ empty arr }
 &lt;%-- 배열도 되고  list도 된다. --%&gt;
 &lt;%-- ${ empty otherList } null검사가 된다. null도 검사하고 원소가 몇개 들었는지도 알수 있다. 비었습니까? 이런느낌. 
 배열에서 empty는 원소값이 없음에도 false가 나온다. 배열은 안에 비었는지 알지 못한다. 배열이 길이가 0인거 만들면 걍 무조건 0이다. 
 null의 유무만 검사하고, 원소가 있는지 아닌지 배열이 비었는지는 검사하지 못한다./ not empty도 가능  --%&gt;
&lt;hr&gt;
 &lt;%--jstl을 써야 for아니 if문 쓸 수 있다. 코드를 만들 수 있는 일을 태그로 사용한거...jstl은 외부라이브러리다. --&gt;
&lt;%
    String str = (String) request.getAttribute(&quot;myString&quot;);
%&gt;
&lt;%=str %&gt;

${ requestScope.myString } &lt;%--표현식은 null값이 나오지만, requestScope는 null값이 표현되지 않고 결과가 안나온다!!! requestScope는 리퀘스트 객체가 아니라 그냥 속성값만 가져오는 것이다.--%&gt;
&lt;%-- ${ myString }--%&gt; &lt;% //얘는 4개의 객체를 차례로 검사 pageContext-request-session-application 순으로 있는지 없는지 확인하고 page가 먼저 나온다. 4개 다 없으면 null이 뜬다. %&gt;
&lt;hr&gt;
&lt;%
    Some temp = (Some)session.getAttribute(&quot;some&quot;);

%&gt;
&lt;%= temp.getNum()%&gt; &lt;%=temp.getStr()%&gt;
${ sessionScope.some.num }
&lt;%--${ sessionScope.some.str } --%&gt;
${some.str } &lt;%--이렇게 써도 값은 똑같이 나온다. --%&gt;
&lt;%--${some[str] }  --%&gt;&lt;%--이렇게 써도 값은 똑같이 나온다. --%&gt;

&lt;hr&gt;
&lt;%=application.getInitParameter(&quot;pname&quot;) %&gt;
${ initParam.pname }

&lt;%-- http://localhost:8080/06.01/elEx.jsp?myParam=cat --%&gt;
${ param.myParam }

&lt;hr&gt;
${ pageContext.request.contextPath } &lt;%--pageContext.request : 이렇게 쓰면 레퀘스트 객체를 가져오게 된다. el에서 사용할 수 있는 것은 pageContext객체 밖에 안된다. 다른 3가지는 객체 직접 못 들고 온다.--%&gt;
&lt;hr&gt;
${ 4 &lt; 3 } &lt;%-- 참거짓판단가능 --%&gt;
${ 3 == 3 } &lt;%-- 참거짓판단가능 --%&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre><h2 id="jstljsp-standard-tag-library">JSTL(Jsp Standard Tag Library)</h2>
<p>JSP는 자신만의 태그를 추가할 수 있는 기능을 제공하고 있는데요. </p>
<p><a href="jsp:include">jsp:include</a>나 <a href="jsp:usebean">jsp:usebean</a>과 같은 커스텀 태그처럼 연산이나 조건문이나 반복문인 if문, for문, DB를 편하게 처리할 수 있는것이 JSTL.</p>
<blockquote>
</blockquote>
<p><strong>if문 사용 - else if 없음</strong>
<strong>&lt;c:if test = ${}&gt;</strong></p>
<pre><code>&lt;c:if test**=&quot;${4 &gt; 0}&quot;&gt;
    true
&lt;/c:if&gt;</code></pre><blockquote>
</blockquote>
<p><strong>if - else if - else문</strong></p>
<pre><code>&lt;c:choose&gt;
&lt;c:when test=&quot;${ myValue &gt;10 }&quot;&gt; :if느낌. 
        10보다 크다
        &lt;/c:when&gt;
    &lt;c:when test=&quot;${ myValue&gt;5 }&quot;&gt; :else if느낌.
        5보다 크다
    &lt;/c:when&gt;
    &lt;c:otherwise&gt; : else느낌. 
        5 이하이다.
    &lt;/c:otherwise&gt;
&lt;/c:choose&gt;</code></pre><blockquote>
</blockquote>
<p><strong>for구문</strong>
end는 &#39;이하&#39;이다. 10을 포함함. 역순은 안된다.
var : 변수 step : 더해지는 단위</p>
<pre><code>&lt;c:forEach var=&quot;i&quot; begin=&quot;0&quot; end=&quot;10&quot; step=&quot;2&quot;&gt; 
    ${ i }
&lt;/c:forEach&gt;</code></pre><blockquote>
</blockquote>
<p><strong>foreach문</strong>
<strong>varStatus</strong>=&quot;status&quot;:반복문의 정보를 가지고 있다.</p>
<blockquote>
</blockquote>
<p>&lt;c:foreach <strong>items</strong>=&quot;${리스트가 받아올 배열이름}&quot;
&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp <strong>var</strong>=&quot;for문 내부에서 사용할 변수&quot;
&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp <strong>varStatus</strong>=&quot;상태용 변수&quot;&gt;</p>
<pre><code>&lt;c:forEach items=&quot;${ list }&quot; var=&quot;myString&quot; varStatus=&quot;status&quot;&gt; 
    ${ status.count }번째 반복 :  ${ myString } -&gt; ${ status.index }
    &lt;br&gt;
&lt;/c:forEach&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/33e71d8c-4ea2-4496-b71f-ffb7e3382b38/image.png" alt=""></p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ page import =&quot;java.util.*&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
    //int num = 100;    //el에서 참조할 수 없다.${ num } 이렇게 못 쓴다. el는 getAttribute인 느낌인듯.
%&gt;
&lt;c:set var=&quot;num&quot; value=&quot;100&quot; scope=&quot;request&quot; /&gt; &lt;%--이런 거 사용은 되는데 하지마라. jsp는 속성을 정의하는 곳이 아니다. --%&gt;
${ num }
&lt;c:remove var=&quot;num&quot; scope=&quot;request&quot;/&gt; &lt;%--이런 거 사용은 되는데 하지마라. jsp는 속성을 정의하는 곳이 아니다. --%&gt;
${ num }
&lt;hr&gt;&lt;%--여기만 보기--%&gt;
&lt;c:if test=&quot;${4 &gt; 0}&quot;&gt;&lt;%--else if가 없다. --%&gt;
    true
&lt;/c:if&gt;
&lt;c:set var=&quot;myValue&quot; value=&quot;1&quot;/&gt;
&lt;c:choose&gt;
    &lt;c:when test=&quot;${ myValue &gt;10 }&quot;&gt;&lt;%--if느낌. --%&gt;
        10보다 크다
    &lt;/c:when&gt;
    &lt;c:when test=&quot;${ myValue&gt;5 }&quot;&gt;&lt;%--else if느낌. --%&gt;
        5보다 크다
    &lt;/c:when&gt;
    &lt;c:otherwise&gt;&lt;%--else느낌. --%&gt;
        5 이하이다.
    &lt;/c:otherwise&gt;
&lt;/c:choose&gt;

&lt;hr&gt;&lt;%--반복문--%&gt;
&lt;c:forEach var=&quot;i&quot; begin=&quot;0&quot; end=&quot;10&quot; step=&quot;2&quot;&gt; &lt;%--end는 &#39;이하&#39;이다. 10을 포함함. 역순은 안된다.--%&gt;
    ${ i }
&lt;/c:forEach&gt;
&lt;hr&gt;
&lt;%
    ArrayList&lt;String&gt; list = new ArrayList&lt;String&gt;();
    list.add(&quot;a&quot;);
    list.add(&quot;b&quot;);
    list.add(&quot;c&quot;);
    list.add(&quot;d&quot;);

    request.setAttribute(&quot;list&quot;, list);
%&gt;
&lt;c:forEach items=&quot;${ list }&quot; var=&quot;myString&quot; varStatus=&quot;status&quot;&gt; &lt;%-- varStatus=&quot;status&quot;:반복문의 정보를 가지고 있다. --%&gt;
    ${ status.count }번째 반복 :  ${ myString } -&gt; ${ status.index }
    &lt;br&gt;
&lt;/c:forEach&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>걍)
book의 list.jsp를 jstl로 바꾼 거</p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ taglib prefix =&quot;c&quot; uri =&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ page import=&quot;java.util.*&quot; %&gt;
&lt;%@ page import=&quot;kr.ac.green.Book&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;list.jsp&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;list&lt;/h1&gt;
    &lt;a href=&quot;add.book&quot;&gt;책 등록하기&lt;/a&gt;
    &lt;hr&gt;
    &lt;form action=&quot;findByTitle.book&quot; method=&quot;get&quot;&gt;
        제목으로 검색할 수 있어요 : &lt;input type=&quot;text&quot; name=&quot;findTitle&quot; /&gt;
        &lt;input type=&quot;submit&quot; /&gt;
    &lt;/form&gt;
    &lt;hr&gt;
    &lt;table&gt;
        &lt;tr&gt;
            &lt;th&gt;번호&lt;/th&gt;
            &lt;th&gt;제목&lt;/th&gt;
            &lt;th&gt;저자&lt;/th&gt;            
        &lt;/tr&gt;
        &lt;c:choose&gt;
            &lt;c:when test=&quot;${ empty list }&quot;&gt;
                &lt;tr&gt;
                    &lt;td colspan=&quot;3&quot;&gt;no data&lt;/td&gt;
                &lt;/tr&gt;
        &lt;/c:when&gt;
        &lt;c:otherwise&gt;
            &lt;c:forEach items=&quot;${ list }&quot; var=&quot;book&quot;&gt;
                &lt;tr onclick=&quot;showBookInfo(${ book.num})&quot;&gt;
                    &lt;td&gt;${ book.num}&lt;/td&gt;
                    &lt;td&gt;${ book.title}&lt;/td&gt;
                    &lt;td&gt;${ book.writer}&lt;/td&gt;
                &lt;/tr&gt;        
                 &lt;/c:forEach&gt;
            &lt;/c:otherwise&gt;
        &lt;/c:choose&gt;
        &lt;tr&gt;
            &lt;th colspan =&quot;3&quot;/&gt;
            &lt;c:forEach var=&quot;i&quot; begin=&quot;1&quot; end=&quot;${ pageCount }&quot;&gt;
                &lt;c:choose&gt;
                    &lt;c:when test=&quot;${ i == pageNum }&quot;&gt;
                        [${i }]
                    &lt;/c:when&gt;
                    &lt;c:otherwise&gt;
                        &lt;a href=&quot;list.book?pageNum=${i }&quot;&gt; [${i }]&lt;/a&gt;
                    &lt;/c:otherwise&gt;

                &lt;/c:choose&gt;
            &lt;/c:forEach&gt;
        &lt;/tr&gt;

    &lt;/table&gt;
    &lt;script&gt;
        function showBookInfo() {
            location.href=&quot;view.book?num=&quot; + arguments[0];
        }
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><h3 id="배열로-바꾸기">배열로 바꾸기</h3>
<p>public Book[] getAllByArray(Connection con) {
        Book[] arr = null;
        PreparedStatement pStmt = null;
        ResultSet rs =null;    </p>
<pre><code>    String sql = &quot;SELECT * FROM a_book&quot;;
    try {
        pStmt = con.prepareStatement(sql);
        rs = pStmt.executeQuery();
        rs.last();    //마지막으로 옮김. -&gt; 다음게 없음. rs.last()가 제일 마지막 레코드로 이동함.
        int count = rs.getRow();//현재 커서가 몇번째위치에 있는 지 알려주는 게 getRow()
        arr = new Book[count];
        rs.beforeFirst();//위치를 첫번째레코드 위에 있다. 최초위치에 있다.
        int idx = 0;
        while(rs.next()) {//next()의 의미 : 현재위치라는 게 있어야 한다. 다음레코드가 있습니까? 물어볼 수 있는 거. 처음위치가 어디있나? 열이름들 나오는 저기부분. 최초위치는 열이름있는 부분. 현재위치 담고있는 부분을 cursor라고 함.
            arr[idx++] = rowMapping(rs);
        }
        //콜렉션 등도 기반이 배열이다. 길이가 늘어났을 때 배열을 바꾼다. 리스트등은 capacity 용량이라는 것을 가짐. 진짜로 몇개를 넣을 수 있는가의 정보. 용량이 먼저 10으로 잡혀있고, 나중에 10개 넘으면 2배로 양을 늘인다. 넣는 원소개수가 많은면 많을수록 공간의 낭비가 심해진다.
        //가변길이인것처럼 보이게 한다. 
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(rs);
        close(pStmt);
    }
    return arr;
}</code></pre><pre><code>
`클라이언트가 요청을 보낸다 -&gt; 로직(커맨드클래스) -&gt; 연결 -&gt; 데이터베이스(질의연산)`</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[sql 연습문제]]></title>
            <link>https://velog.io/@j_code/sql-%EC%97%B0%EC%8A%B5%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@j_code/sql-%EC%97%B0%EC%8A%B5%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Wed, 31 May 2023 13:35:44 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>모든작물의 이름을 구하라.(crops)
SELECT DISTINCT cname FROM crops;</p>
</li>
<li><p>관리하는 농작물의 총 종류수 구하기
SELECT COUTN (DISTINCT cname) FROM crops;</p>
</li>
<li><p>g로 시작하는 농작물 이름 구하기
SELECT cname FROM crops WHERE cname LIKE &#39;g%&#39;;</p>
</li>
<li><p>작물별 제공 농부수가 2명 이상인 작물을 구하라.
SELECT cname, COUNT(fid) farmers_count FROM crops GROUP BY cname HAVING farmers_count &gt;=2;</p>
</li>
<li><p>3000원 이상하는 작물의 수를 구하라.<br>SELECT COUNT(cname) crops_count FROM crops WHERE cprice &gt;= 3000 ;</p>
</li>
<li><p>작물별로 3000원 이상하는 작물의 수가 2개 이상인 작물을 구하라.<br>SELECT cname, COUNT(cname) crops_count FROM crops WHERE cprice &gt;= 3000 GROUP BY cname HAVING crops_count &gt;=2;</p>
</li>
<li><p>작물별 가장 저렴한 제품의 가격을 구하라. 
SELECT cname, MIN(cprice) FROM crops GROUP BY cname; </p>
</li>
<li><p>작물별 평균가격을 구하라
SELECT cname, AVG(cprice) FROM crops GROUP BY cname;</p>
</li>
<li><p>이름이 4글자가 넘는 작물별 평균가격을 구하라.
SELECT cname, AVG(cprice) FROM crops WHERE CHAR_LENGTH(cname) &gt;4 GROUP BY cname;</p>
</li>
<li><p>이름이 4글자가 넘는 작물별 평균가격을 구하라. 단, 평균가격이 1000원 이상
SELECT cname, AVG(cprice) cprice_avg FROM crops WHERE CHAR_LENGTH(cname)&gt;4 GROUP BY cname HAVING cprice_avg &lt;1000;</p>
</li>
<li><p>각 농부별 재배작물의 총 가격을 구하라. 
SELECT fid, SUM(cprice) total FROM crops GROUP BY fid;
SELECT fname FROM farmers;</p>
</li>
<li><p>-sub query
SELECT fname, c.total FROM farmers, (SELECT fid, SUM(cprice) total FROM crops GROUP BY fid) c WHERE farmers.fid = c.fid;</p>
</li>
<li><p>dainel이 재배하는 작물의 이름과 가격을 구하라. 
SELECT fname, cprice FROM crops WHERE fid = (SELECT fid FROM farmers WHERE fname=&#39;daniel&#39;);</p>
</li>
<li><p>-최신(표준)
SELECT fname, cname, cprice FROM crops INNER JOIN farmers ON crops.fid = farmers.fid WHERE farmers.fname=&#39;daniel&#39;;</p>
</li>
<li><p>kiwi를 생산하는 농부의 이름과 전화번호를 구하라.<br>SELECT fname, ftel FROM crops NATURAL JOIN farmers WHERE cname = &#39;kiwi&#39;;</p>
</li>
<li><p>가장 값이 싼 수박을 납품하는 농부의 이름과 주소를 구하라.
SELECT fid FROM crops WHERE cname =&#39;watermelon&#39; AND cprice=(SELECT MIN(cpirce) FROM crops WHERE cname&#39;watermelon&#39;);
SELECT fname, faddr FROM farmers NATURAL JOIN crops WHERE cname= &#39;watermelon&#39; AND cprice=(SELECT MIN(cprice) FROM crops WHERE cname=&#39;watermelon&#39;);</p>
</li>
<li><p>부산에서 재배되는 작물의 이름, 가격, 농부이름 구하라.
SELECT cname, cprice, fname FROM crops NATURAL JOIN farmers WHERE faddr =&#39;busan&#39;;</p>
</li>
<li><p>옥수수를 재배하지 않는 농부의 이름은?     </p>
</li>
<li><p>-1.옥수수를 재배하는 농부아이디를 구한다.
SELECT fid FROM crops WHERE cname =&#39;corn&#39;;</p>
</li>
<li><p>-2. 1에 포함되지 않는 농부아이디를 구한다.
SELECT DISTINCT fid FROM crops WHERE fid NOT IN (SELECT fid FROM crops WHERE cname =&#39;corn&#39;);</p>
</li>
<li><p>-3. 농부이름을 구한다.
SELECT fname FROM farmers WHERE fid IN (SELECT DISTINCT fid FROM crops WHERE fid NOT IN (SELECT fid FROM crops WHERE cname =&#39;corn&#39;));</p>
</li>
<li><p>-join
SELECT DISTINCT fname FROM farmers NATURAL JOIN crops WHERE farmers.fid NOT IN (SELECT fid FROM crops WHERE cname =&#39;corn&#39;);</p>
</li>
<li><p>납품을 하지않는 농부의 이름을 구하라</p>
</li>
<li><p>-작물을 재배하는 농부 아이디를 구한다.
SELECT DISTINCT fid FROM crops;</p>
</li>
<li><ul>
<li>1을 제외한 농부이름
SELECT fname FROM farmers WHERE fid NOT IN (SELECT DISTINCT fid FROM crops);</li>
</ul>
</li>
<li><ul>
<li>join
SELECT * FROM farmers LEFT JOIN crops ON farmers.fid = crops.fid WHERE cid IS NULL;</li>
</ul>
</li>
</ul>
<hr>
<hr>
<hr>
<p><img src="https://velog.velcdn.com/images/j_code/post/a4317b42-ac49-4a98-b889-a1d731ea0a20/image.png" alt=""></p>
<p>-부산에 사는 고객의 직업을 구하라.<br>SELECT job FROM customers NATURAL JOIN job_list WHERE c_addr = &#39;pusan&#39;;</p>
<p>-이름이 kim인 사람의 직업은?<br>SELECT job FROM customers NATURAL JOIN job_list WHERE c_name = &#39;kim&#39;;</p>
<p>-가장 가격이 싼 제품의 제품명은?<br>SELECT p_name FROM products WHERE p_price = (SELECT MIN(p_price) FROM products);</p>
<p>-고객별 구매한     제품의 이름, 수량, 구매일자를 구하시오. 단, 최신 구매순으로 정렬한다.
SELECT c_name ,p_name, o_count, o_date FROM customers NATURAL JOIN order_list NATURAL JOIN products ORDER BY o_date DESC;</p>
<p>-가장 비싼 제품을 구입한 고객의 이름을 구하시오.
SELECT DISTINCT c_name FROM customers NATURAL JOIN order_list NATURAL JOIN products  WHERE p_price = (SELECT MAX(p_price) FROM products);</p>
<ul>
<li><p>DVD나 TV를 구매한 고객의 이름과 주소, 제품명을 구하시오.
SELECT c_name, c_addr, p_name FROM customers NATURAL JOIN order_list NATURAL JOIN products WHERE p_name =&#39;DVD&#39; OR p_name =&#39;TV&#39;; 
SELECT c_name, c_addr, p_name FROM customers NATURAL JOIN order_list NATURAL JOIN products WHERE p_name IN(&#39;DVD&#39;, &#39;TV&#39;); </p>
</li>
<li><p>doo 가 구매한 가격의 총액을 구하시오.
SELECT SUM(p_price*o_count) FROM products NATURAL JOIN order_list NATURAL JOIN customers WHERE c_name = &#39;doo&#39;;</p>
</li>
<li><p>고객별 구매가격 총액
SELECT SUM(p_price*o_count) FROM products NATURAL JOIN order_list NATURAL JOIN customers GROUP BY c_id;</p>
</li>
<li><p>직업이 teacher인 사람이 구매한 제품목록을 구하시오.
SELECT DISTINCT p_name FROM products NATURAL JOIN order_list NATURAL JOIN customers NATURAL JOIN job_list WHERE job=&#39;teacher&#39;;</p>
</li>
<li><p>직업이 student인 고객의 이름과 구매총액을 구하시오.
SELECT c_name ,SUM(p_price*o_count) FROM products NATURAL JOIN order_list NATURAL JOIN customers NATURAL JOIN job_list WHERE job=&#39;student&#39; GROUP BY c_id;</p>
</li>
<li><p>한번도 구매하지 않은 사람의 직업을 구하시오.
SELECT c_name, job FROM job_list NATURAL JOIN customers    NATURAL JOIN order_list ON customer.c_id = order_list.c_id  WHERE p_id IS NULL;</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[sql문(GROUP BY,집계함수, HAVING, 서브쿼리 와 조인)]]></title>
            <link>https://velog.io/@j_code/sql%EB%AC%B8GROUP-BY%EC%A7%91%EA%B3%84%ED%95%A8%EC%88%98-HAVING-%EC%84%9C%EB%B8%8C%EC%BF%BC%EB%A6%AC-%EC%99%80-%EC%A1%B0%EC%9D%B8</link>
            <guid>https://velog.io/@j_code/sql%EB%AC%B8GROUP-BY%EC%A7%91%EA%B3%84%ED%95%A8%EC%88%98-HAVING-%EC%84%9C%EB%B8%8C%EC%BF%BC%EB%A6%AC-%EC%99%80-%EC%A1%B0%EC%9D%B8</guid>
            <pubDate>Fri, 26 May 2023 04:51:15 GMT</pubDate>
            <description><![CDATA[<p>강사님이 준 sql파일 순서대로 시행하기.</p>
<h3 id="sql-순서">sql 순서!</h3>
<p>from -&gt; where -&gt; select 순으로 시행된다.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/6a95e2cd-e162-4d04-a255-a5f704c7cfed/image.png" alt=""></p>
<hr>
<p>쿼리문!!!!</p>
<blockquote>
</blockquote>
<p><strong>제조업체(vend_id)가 &quot;DLL01&quot;이거나 &quot;BRS01&quot;인 제풍의 이름(prod_name)과 가격(prod_price)을 가져오시오. 단 제품이름은 오름차순으로 정렬.</strong></p>
<ul>
<li>SELECT prod_name, prod_price FROM products WHERE vend_id = &#39;DLL01&#39; OR vend_id = &#39;BRS01&#39; ORDER BY prod_name ASC;</li>
<li>SELECT prod_name, prod_price FROM products WHERE vend_id IN(&#39;DLL01&#39;, &#39;BRS01&#39;) ORDER BY prod_name;</li>
</ul>
<blockquote>
</blockquote>
<p><strong>가격(prod_price)이 5~10인 제품의 가격과 이름을 가져오시오.</strong></p>
<ul>
<li>SELECT prod_name, prod_price FROM products WHERE prod_price BETWEEN 5 AND 10;</li>
<li>SELECT prod_name, prod_price FROM products WHERE prod_price &gt;=5 AND prod_price &lt;=10;</li>
</ul>
<blockquote>
</blockquote>
<p><strong>가격(prod_price)이 입력되지 않은 제품의 이름을 가져오시오.</strong>
SELECT prod_name FROM products WHERE prod_price IS NULL;</p>
<blockquote>
</blockquote>
<p><strong>제조 업체(vend_id)가 &#39;DLL01&#39;이 아닌 제품의 이름을 가져오시오</strong></p>
<ul>
<li>SELECT prod_name FROM products WHERE vend_id &lt;&gt; &#39;DLL01&#39; ORDER BY prod_name;</li>
<li>SELECT prod_name FROM products WHERE <strong>NOT</strong> vend_id=&#39;DLL01&#39; ORDER BY prod_name;</li>
</ul>
<blockquote>
</blockquote>
<p><strong>제품의 이름(prod_name)이 &#39;Fish&#39;로 시작하는 모든 제품의 아이디(prod_id)와 이름(prod_name)을 검색하시오.</strong></p>
<ul>
<li>SELECT prod_id, prod_name FROM products WHERE prod_name <strong>LIKE</strong> &#39;Fish%&#39;;</li>
</ul>
<blockquote>
</blockquote>
<p><strong>제품의 이름(prod_name)이 F로 시작하고 y로 끝나는 제품의 이름(prod_name)을 검색하시오.</strong>
SELECT prod_name FROM products WHERE prod_name LIKE &#39;F%y&#39;;</p>
<h3 id="별칭을-주는-방법--한칸-띄우고-별칭을-적어주기-or-as지원이-되는-것도-있고-안되는-것도-있고-적기">별칭을 주는 방법 : 한칸 띄우고 별칭을 적어주기. or AS(지원이 되는 것도 있고 안되는 것도 있고!) 적기</h3>
<p>as</p>
<blockquote>
</blockquote>
<p><strong>레코드를 하나의 줄씩 연산한다. 레코드별로 연산이 된다! 자기것만 한다. 자가레코드만 연산한다.</strong></p>
<ul>
<li>SELECT prod_id, quantity, quantity*item_price expanded_price FROM orderItems WHERE order_num = 20008</li>
<li>SELECT prod_id, quantity, quantity<em>item_price *</em>AS **expanded_price FROM orderItems WHERE order_num = 20008</li>
</ul>
<h3 id="group-by">GROUP BY</h3>
<p>group by</p>
<blockquote>
</blockquote>
<p><strong>제품별 판매총액을 구하라.</strong></p>
<ul>
<li>SELECT * FROM orderItems ORDER BY prod_id; 
  : 이게 제품별이다. 7가지 종류가 있다. 7가지 종류의 그룹의 총판매액을 구하는 것이다. 이렇게 어떻게 나누는냐? 이걸 할 수 있는 키워드가 GROUP BY이다.</li>
<li>SELECT * FROM orderItems <strong>GROUP BY</strong> prod_id; 
: 이걸 하면 전체 레코드 수와 관련있는게 아니라 그룹수와 관련이 있다. 이것까지 그룹이 된 거.</li>
</ul>
<h3 id="집계함수">집계함수</h3>
<ul>
<li><p><strong>SUM, AVG, COUNT(줄 수가 몇개냐), MAX, MIN</strong> : 여러개의 레코드를 가지고 <strong>값을 한개로</strong> 만드는 놈이다 = 집계함수. </p>
</li>
<li><p><strong>GROUP BY 사용했을 때 셀렉트절에 사용하는 게 가능하다.</strong></p>
</li>
<li><p>집계함수는 WHERE절에 쓸 수 없다. 웨얼절은 하나의 레코드 가지고 연산하는 것이기 때문에. </p>
</li>
<li><p>그룹으로 나눠서 탈락되는 그룹이 있을 수 있다. 조건절이 없을 때는 그룹수가 레코드로 나온다. </p>
</li>
<li><p>요약하자면, 그룹바이는 그룹수에 의해서 결과가 결정난다. 일치하지 않는 값은 사용할 수 없다. 여기서는 id밖에 못쓴다. 그러나 합은 사용할 수 있다. </p>
</li>
<li><p><strong>sum은 세로로 더하는 것</strong>이다. -&gt; 더하면 1개가 나온다. 다 더하면 결과는 1개 나오는데 그걸 스칼라값(집계함수)이라고 한다.</p>
</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>SELECT prod_id, <strong>SUM(quantity*item_price)</strong> FROM orderitems <strong>GROUP BY</strong> prod_id;
prod_id만 살아남고 num,item같은 것을 버려지게 된다. <strong>다 다른 값이기 때문에.</strong>
우연찮게 다 같은 값이 있으면 같이 써도 되는데, 그건 우연일 뿐. </li>
</ul>
<h3 id="having">HAVING</h3>
<ul>
<li>HAVING절에는 집계함수를 조건을 걸 수 있다. 
count()는 null은 카운팅이 되지 않는다. 그래서 <strong>&#39;프라이머리키&#39;</strong>넣던지, 아니면 <strong>&#39;*하기&#39;</strong>. </li>
<li>해빙이 없으면 그룹수랑 레코드수랑 일치한다.</li>
</ul>
<blockquote>
</blockquote>
<p>주문을 2개 이상 받은 제품의 총 판매수량</p>
<ul>
<li>SELECT prod_id, SUM(quantity<em>item_price) FROM orderitems GROUP BY prod_id HAVING COUNT(</em>) &gt;=3 ;</li>
</ul>
<ul>
<li><strong>having은 레코드별이 아니라, 그룹별로 건다</strong>. 이 그룹이 이게 됩니까? 이런 느낌. <strong>where은 레코드 하나씩 검사하는 거.</strong> 
그래서 price가 4가 넘는지는 having에서 알 수 없음</li>
</ul>
<blockquote>
</blockquote>
<p><strong>가격(prod_price)이 4이상인 제품을 두 개 이상 가진 공급업체(vend_id)와 제품수량을 구하라</strong></p>
<ul>
<li>SELECT vend_id, COUNT(<em>) FROM products WHERE prod_price &gt;= 4 GROUP BY vend_id HAVING COUNT(</em>) &gt;= 2;</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>팁
SELECT vend_id, COUNT(*) FROM products를 하면 vend_id가 하나만 나오는데, COUNT때문에 제일 처음인 id가 나오는 것이다. </li>
</ul>
<blockquote>
</blockquote>
<p>예시
SELECT * FROM products WHERE prod_price &gt;= 4 GROUP BY vend_id HAVING COUNT(*) &gt;= 2;</p>
<blockquote>
</blockquote>
<p><strong>제조사(vend_id)별 제품의 판매가격(pod_price) 총 합계(total_price)를 구하라.</strong>
SELECT  vend_id, SUM(prod_price) total_price FROM products GROUP BY vend_id;</p>
<blockquote>
</blockquote>
<p><strong>제품을 3종류 이상 주문한 주문번호와 / 주문가격의 합을 구하시오.</strong>
SELECT oder_num, SUM(item_price * quantity) FROM orderItems GROUP BY order_num HAVING COUNT(*) &gt;=3;</p>
<h3 id="서브쿼리-와-조인">서브쿼리 와 조인</h3>
<p><strong>&#39;King doll&#39;을 생산한 제조사의 이름과 주소를 구하라.</strong> (King doll 얘는 product_name이다.) 
: <strong>사용방법 - 서브쿼리 or 조인 이다. : 연산 방식이 다르다.</strong> </p>
<ul>
<li><strong>서브쿼리</strong>는 우리가 생각하는 방식과 비슷하다.
테이블을 하나씩 처리한다.
그렇지만 쿼리가 길어진다는 단점이 있다. </li>
<li><strong>조인</strong>은 우리가 사용하는 방식은 아니지만, 길이가 상대적으로 짧다. 결합되는 조건에 맞는 열들을 논리적으로 붙여서 하나의 테이블로 본다.</li>
<li>두가지 방식으로 다 풀어보는 걸 권장. <h4 id="서브쿼리">서브쿼리</h4>
쿼리문 안에 쿼리가 있는거 </li>
<li><em>테이블을 하나씩 하나씩 처리한다.*</em></li>
<li><ul>
<li>&#39;=&#39; 라고 사용하면, prod_name은 주키가 아니라서 이게 한개이상 있을 수 있으니까 이름이 여러개 잇을 수 있으니까 오류가 날 수도 있다. </li>
</ul>
</li>
<li><ul>
<li>whrer 다음에 <strong>in으로 바꾼 것은 =라고 하면 1:1의 개념인데, in은 여러개중에 하나와 같은 뜻을 포함하고 잇다. 그래서 =을 사용하면 오류가 난다.</strong> 그래서 안전하게 하기 위해서 in을 사용한다. 일반적으로 연산의 속도는 조인이 더 낫다.</li>
</ul>
</li>
</ul>
<p>서브쿼리방식</p>
<blockquote>
</blockquote>
<p>SELECT vend_id FROM products WHERE prod_name = &#39;King doll&#39;; -- 이거는 FNG01이 나온다.</p>
<blockquote>
</blockquote>
<p>SELECT vend_name, vend_address FROM vendors WHERE vend_id = (SELECT vend_id FROM products WHERE prod_name = &#39;King doll&#39;); 
(X, 틀렸다 밑에 처럼 쓰기)</p>
<blockquote>
</blockquote>
<p>SELECT vend_name, vend_address FROM vendors WHERE vend_id <strong>IN</strong> (SELECT vend_id FROM products WHERE prod_name = &#39;King doll&#39;); 
<strong>IN을 넣어라.</strong></p>
<h4 id="조인">조인</h4>
<p>join : 두개 이상의 테이블을 합치는거. 종류가 많다. 
1.<strong>크로스조인</strong>: 레코들들끼리 조합할 수 있는 모든 경우의 수를 다 합치는거. a레코드 5개, b레코드 5개면, 25개나오는거.</p>
<p>2.<strong>이너조인(보통말하는 조인)</strong> : 결합조건이 일치하는 열끼리만 결합시키는 것이다.
SELECT * FROM vendors v, products p WHERE v.vend_id = p.vend_id; -- 이너조인 예제 : <strong>논리적으로 결합시켜서, 한 테이블처럼 보이게 한다.</strong></p>
<p>3.<strong>아우터조인</strong> : 결합조건맞지않는 애들도 결합시킨다. a와 b의 교집합이 이너조인이면 그 외의 것은 아우터조인 인다.
a만 포함. b만 포함. 차집합만 포함. 교집합도 포함을 하는데, 아우터조인을 할 때는 교집합은 잘 사용하지 않는다.
그렇지만 결국 결합조건이 맞지 않는 부분만 보통 사용한다.
left outerJoin, right outerJoin, full outerJoin..</p>
<blockquote>
</blockquote>
<p><strong>옛날방식</strong>(&#39;King doll&#39;을 생산한 제조사의 이름과 주소를 구하라.)
SELECT vend_name, vend_address FROM vendors v, products p WHERE v.vend_id = p.vend_id AND prod_name = &#39;King doll&#39;;</p>
<blockquote>
</blockquote>
<p><strong>최근 방식1. INNER JOIN</strong>
주키와 외래키로만 묶어야 한다. 그래야지 참조무결성이 지켜진다. 
SELECT vend_name, vend_address FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id WHERE proc_name = &#39;King doll&#39;;</p>
<blockquote>
</blockquote>
<p><strong>최근 방식2. NATURAL JOIN</strong>
이너조인의 중 하나인데, 주키와 외래키의 열이름이 같을 때 사용한다. 
SELECT vend_name, vend_address FROM vendors NATURAL JOIN products WHERE prod_name = &#39;King doll&#39;;</p>
<blockquote>
<p>예제
<strong>&#39;RGAN01&#39;(prod_id)물품을 주문한 모든 고객의 정보를 구하라(cust_name, cust_contact)</strong></p>
</blockquote>
<p><strong>서브쿼리(변화과정 맨 밑에꺼가 정답)</strong></p>
<ul>
<li>SELECT order_num FROM orderItems WHERE prod_id = &#39;RGAN01&#39;;</li>
<li>SELECT cust_id FROM orders WHERE order_num IN(SELECT order_num FROM orderItems WHERE prod_id = &#39;RGAN01&#39;);</li>
<li><strong>SELECT cust_name, cust_contact FROM customers WHERE cust_id IN (SELECT cust_id FROM orders WHERE order_num IN(SELECT order_num FROM orderItems WHERE prod_id = &#39;RGAN01&#39;));</strong><blockquote>
</blockquote>
</li>
<li><em>조인 옛날방식*</em>
SELECT cust_name, cust_contact FROM customers, orders, orderItems WHERE orders.cust_id = customers.cust_id  AND orders.order_num = orderItems.order_num AND prod_id = &#39;RGAN01&#39;;<blockquote>
</blockquote>
</li>
<li><em>조인 최근방식 INNERJOIN*</em>
SELECT cust_name, cust_contact 
FROM customers INNER JOIN orders INNER JOIN orderItems
ON orders.cust_id = customers.cust_id  AND orders.order_num = orderItems.order_num WHERE prod_id = &#39;RGAN01&#39;;<blockquote>
</blockquote>
</li>
<li><em>NATURAL JOIN*</em>
SELECT cust_name, cust_contact 
FROM customers NATURAL JOIN orders NATURAL JOIN orderItems 
WHERE prod_id = &#39;RGAN01&#39;;</li>
</ul>
<h3 id="distinct--중복을-없애준다">DISTINCT : 중복을 없애준다</h3>
<blockquote>
</blockquote>
<p><strong>100개 이상 판매한 제품을 제조한 제조사이름(vend_name)을 구하라</strong></p>
<ul>
<li><strong>서브쿼리</strong>
SELECT vend_name FROM vendors WHERE vend_id IN (SELECT DISTINCT vend_id FROM products WHERE prod_id IN(SELECT prod_id FROM orderItems WHERE quantity&gt;=100));<blockquote>
</blockquote>
</li>
<li><strong>INNERJOIN 조인</strong>
SELECT DISTINCT vend_name
FROM vendors INNER JOIN products INNER JOIN orderItems
ON vendors.vend_id= products.vend_id AND products.prod_id = orderItems.prod_id WHERE quantity &gt;= 100;<blockquote>
</blockquote>
</li>
<li><strong>NATURAL 조인</strong>
SELECT DISTINCT vend_name FROM vendors NATURAL JOIN products NATURAL JOIN orderItems WHERE quantity &gt;= 100;</li>
</ul>
<h4 id="outer-join-예시">outer join 예시</h4>
<p>left outer
<img src="https://velog.velcdn.com/images/j_code/post/0c54640f-3e52-4d0d-b69e-7be47a3cc231/image.jpg" alt=""></p>
<blockquote>
</blockquote>
<p><strong>한번도 주문하지 않은 고객의 이름을 구하라.</strong>
--고객은 있는데, orders에 없는 애를 찾으면 된다.이럴 때 아우터조인이 필요하다. 내가 꺼내오려는 정보가 왼쪽에 있는거라서 LEFT OUTER를 쓴 것이다.
-- 아우터조인은 둘 사이에 겹쳐지지 않는 것도 포함해서 order_num을 사용할 수 있다. 
SELECT * FROM customers LEFT OUTER JOIN orders ON customers.cust_id = orders.cust_id WHERE orders.cust_id IS NULL;
SELECT * FROM customers LEFT OUTER JOIN orders ON customers.cust_id = orders.cust_id WHERE order_num IS NULL;
-- FROM orders RIGHT OUTER JOIN customers</p>
<blockquote>
</blockquote>
<p><strong>제품을 하나도 생산하지 않는 제조사이름(vend_name)을 구하라.</strong>
SELECT vend_name FROM vendors LEFT OUTER JOIN products ON vendors.vend_id = products.vend_id WHERE products.prod_id IS NULL;</p>
<blockquote>
</blockquote>
<p><strong>제조사 이름이 &#39;Bears R Us&#39;인 제조사의 제품을 구매한 모든 고객의 이름을 구하시오. 단 중복은 제거한다.</strong>
SELECT DISTINCT cust_name FROM customers NATURAL JOIN orders NATURAL JOIN orderItems NATURAL JOIN products NATURAL JOIN vendors WHERE vend_name = &#39;Bears R Us&#39;;
SELECT DISTINCT cust_name FROM vendors NATURAL JOIN products NATURAL JOIN orderItems NATURAL JOIN orders NATURAL JOIN customers WHERE vend_name = &#39;Bears R Us&#39;;</p>
<blockquote>
<p><strong>가장 비싼(item_price) 제품을 구매한 구매자의 이름(cust_name)과 판매가격(item_price), 구매수량(quantity)을 구하라.</strong>
SELECT cust_name, item_price, quantity FROM customers NATURAL JOIN orders NATURAL JOIN orderItems WHERE orderItems.item_price = (SELECT MAX(item_price) FROM orderItems); --값을 하나만 들고오는거 : 스칼라</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스(트랜잭션, statement, preparedStatement, ResultSet, 8859_1와 EUC_KR)]]></title>
            <link>https://velog.io/@j_code/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-statement-preparedStatement-ResultSet-88591%EC%99%80-EUCKR</link>
            <guid>https://velog.io/@j_code/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-statement-preparedStatement-ResultSet-88591%EC%99%80-EUCKR</guid>
            <pubDate>Wed, 24 May 2023 10:29:52 GMT</pubDate>
            <description><![CDATA[<h2 id="트랜잭션">트랜잭션</h2>
<p><strong>트랜잭션 : 더이상 쪼갤 수 없는 단위
con.setAutoCommit()</strong>
start --&gt; A-&gt; B -&gt; C -&gt; D ----&gt; end
A :ok
B :ok
C : failed
그럼 작업을 시작하기 전 단계로 돌아간다. (rollback)</p>
<p>A~D : 다 ok
그럼 A부터 D까지 반영해라! 하는게 commit</p>
<p>트랜잭션 속성 : 원자성
A~D까지를 하나로 묶어서 원자로 본다.</p>
<p>데이터베이스에 연결(Connection객체 얻어온거)을 하고 나서, </p>
<p>싱글톤객체 - 공유되는 객체, 상태를 가지면 안된다.
상태는 멤버변수다.</p>
<p>커낵션 기능중에 바로 반영하지말고 내가 반영하라고 할 때 해라. 
만약 insert를 했다가 취소했을 때 데이터는 없지만, 자동적으로 이미 번호를 할당 받았어. 
오토커밋끄고 일이 다 완성되었을 때 본 다음에 처리하는 게 트랜잭션이다!
3개의 일이 따로지만, 하나로 보고 3개 다 성공해야 성공한 것으로 본다.
일이 성공하면 한꺼번에 반영한다. 3개중에 하나라도 성공못하면 rollback한다. </p>
<p>마이그레이션 검색하기.</p>
<h2 id="statement">Statement</h2>
<p>⭐⭐<strong>connection을 통해서 데이터베이스에 질의를 보내는 기능.</strong></p>
<h3 id="메서드">메서드</h3>
<ul>
<li><p><strong>executeUpdate(sql문) : sql실행 -&gt; return int</strong>
int인 이유 : 결과에 따라 몇줄이 바뀌었는 지 알려줌
insert, delete, update할 때 사용.</p>
</li>
<li><p><strong>executeQuery(sql문) : sql실행 -&gt; return ResultSet</strong>
Select에 사용</p>
</li>
<li><p>String sql = &quot;INSERT INTO a_book (b_title, b_writer, b_password, b_price, b_publisher, b_comment) VALUES (&#39;%s&#39;, &#39;%s&#39;, &#39;%s&#39;, &#39;%d&#39;, &#39;%s&#39;, &#39;%s&#39;)&quot;; 라고 가정할 때 아래처럼 사용한다. </p>
</li>
<li><p>sql = <strong>String.format</strong>(sql, book.getTitle(), book.getWriter(), book.getPassword(), book.getPrice(), book.getPublisher(), book.getComment());</p>
</li>
</ul>
<h2 id="preparedstatement">PreparedStatement</h2>
<p> statement가 쿼리를 중간에 String.format()한 것을 얘가 가지고 있다. 날때부터 준비가 되어 있는 애다. 뭘 할지 알고 있다. 쿼리구문을 해석하면서 태어난다.</p>
<h3 id="메서드-1">메서드</h3>
<ul>
<li><p>stmt = con.createStatement() 
:statement 처음 만들 때 이거 사용하는 듯.</p>
</li>
<li><p>pStmt = con.<strong>prepareStatement</strong>(sql) : statement준비해라라는 뜻.</p>
</li>
<li><p>pStmt.<strong>setString</strong>(1, toEn(book.getTitle()));
:해석 - 첫번째 &#39;?&#39;를 뜻하는 게 1이다. 그리고 그 뒤에는 바꿀 값을 넣는다.</p>
</li>
<li><p>result = pStmt.<strong>executeUpdate()</strong>
:statement랑 다르게 파라미터로 쿼리문을 안넣는다.</p>
</li>
<li><p><strong>executeQuery()</strong></p>
</li>
<li><p><strong>pStmt.clearParameters()</strong>
여러개 수정, 삽입, 삭제할 때. 아이디가 여러개 들어올거고 여러개 삭제 될건데 그때 <strong>이걸 꼭 해줘야 한다.</strong> 값들을 전부 다 초기화해준다. 
지금 하나씩 해서 필요없지만 나중에 여러개 할 때 필요함.</p>
</li>
</ul>
<p>String sql = &quot;SELECT * FROM a_book WHERE b_title LIKE ?&quot;;</p>
<p>pStmt = con.prepareStatement(sql);
와일드 카드를 위의 LIKE 뒤에 적는 게 아니다. 쿼리문에 적는 거 아니다! 
-&gt; pStmt.setString(1, <strong>&quot;%&quot; + toEn(b_title) + &quot;%&quot;)</strong>
뜻 : 첫번째 물음표에 title을 넣어라. 인덱스인데 1부터 시작한다.
<strong>그리고 여기에 와이드카드(%)를 넣어줘야 한다. 그래야 오류가 나지 않는다.</strong></p>
<h2 id="resultset">ResultSet</h2>
<p>결과 집합. select문 결과를 담은 객체이다. 표같은 거 나타내는 거.</p>
<h2 id="8859_1언어와-euc_kr">8859_1언어와 EUC_KR</h2>
<p>DB 언어 : 8859_1
java 언어 : EUC_KR</p>
<ul>
<li><p><strong>insert, update</strong> 일 때,  (euc_kr -&gt; 8859_1)로 바꿔야 한다  :이거는 static toEnd(kor:String) : String 메소드로 만든다.</p>
</li>
<li><p><strong>select</strong> 일 때, (8859_1 -&gt; euc-kr) 
이거는 static toKor(en:String) : String</p>
</li>
</ul>
<p>팁) EUC_KR에서 &#39;euc-kr&#39; 이걸로 적으면 안된다. euc_kr 이렇게 &#39;언더바&#39;로 적어야 한다!!</p>
<hr>
<h2 id="juint">JUint</h2>
<p><img src="https://velog.velcdn.com/images/j_code/post/b0fb4acb-3d35-45d0-b578-3102fdbe55ce/image.png" alt=""></p>
<p>JUnit 4로 가기!!
<img src="https://velog.velcdn.com/images/j_code/post/702140a8-7eb2-4b07-b99d-39f149a5d324/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_code/post/380c33ee-c867-4ba7-b248-e0359ca48a97/image.png" alt=""></p>
<hr>
<h2 id="코드">코드</h2>
<h4 id="booksql">book.sql</h4>
<pre><code>CREATE TABLE a_book (
    b_num            INT                PRIMARY KEY     AUTO_INCREMENT,
    b_title            CHAR(200)        NOT NULL,
    b_writer        CHAR(30)        NOT NULL,
    b_password        VARCHAR(10)        NOT NULL,
    b_price            INT                NOT NULL,
    b_publisher     VARCHAR(30)        NOT NULL,
    b_comment        CHAR(200)        NOT NULL
);

-- 전체보기SELECT * FROM a_book
 SELECT * FROM a_book ORDER BY b_num DESC;

--책하나보기
SELECT * FROM a_book WHERE b_num = ? ;

-- 책 추가하기
INSERT INTO a_book (b_title, b_writer, b_password, b_price, b_publisher, b_comment)
VALUES (?,?,?,?,?,?);

-- 책 삭제하기
DELETE FROM a_book WHERE b_num = ? ;

--책 수정
--다 바꾼다고 전제.
UPDATE a_book SET b_title=?, b_writer=?, b_price=?, b_publisher=?, b_comment=? WHERE b_num=? ;


DROP TABLE a_book;</code></pre><h4 id="bookdaojavastatement-버전">bookDao.java(statement 버전)</h4>
<pre><code>package kr.ac.green.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

import kr.ac.green.Book;

/*
 * JDBC(java database connectivity) 
 * 
 * 1. 드라이버 로드(1번만 수행하면 된다.)
 * 2. 연결
 * 3. 질의
 * 4. 자원해제
 */

public class BookDao {
    private static final BookDao INSTANCE = new BookDao();

    private BookDao() {
        // 1. 드라이버 로드
        try {
            Class.forName(&quot;com.mysql.jdbc.Driver&quot;);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static BookDao getInstance() {
        return INSTANCE;
    }

    public Connection connect() {    // import java.sql.Connection... 데이터 베이스에 연결이 되는거야
        Connection con = null;
        try {
            con = DriverManager.getConnection(&quot;jdbc:mysql://localhost:3306/test&quot;, &quot;root&quot;, &quot;1234&quot;);
        } catch(SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    public void disconnect(Connection con) {
        try {
            con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    public int insertBook(Connection con, Book book) {
        int result = 0;
        // statement? 
        // 동시에 접근할 수 있는 세션 수 정해져 있는데 자원해제 안하면 그 세션이 물려서 accessDeny가 발생함..
        // 그럼 프로그램이 동작을 안해 그래서 자원해제 분명히 해야한다.
        Statement stmt = null;    // sql.Statement

        try {
            // executeUpdate :  INSERT, UPDATE, DELETE  =&gt; int 리턴
            // executeQUERY : SELECT 
            stmt = con.createStatement();
            //String sql = &quot;(b_title, b_writer, b_password, b_price, b_publisher, b_comment) &quot;
            //        + &quot;VALUES (&#39;&quot; + book.getTitle() +&quot;&#39;,&#39;&quot; + book.getWriter() + &quot;&#39;,&#39;&quot;+ book.getPassword() + &quot;&#39;,&quot;+book.getPrice() + &quot;,&#39;&quot; + book.getPublisher() + &quot;&#39;,&#39;&quot; + book.getComment() + &quot;&#39;)&quot;;

            String sql = &quot;INSERT INTO a_book (b_title, b_writer, b_password, b_price, b_publisher, b_comment) VALUES (&#39;%s&#39;, &#39;%s&#39;, &#39;%s&#39;, &#39;%d&#39;, &#39;%s&#39;, &#39;%s&#39;)&quot;;
            sql = String.format(sql, book.getTitle(), book.getWriter(), book.getPassword(), book.getPrice(), book.getPublisher(), book.getComment());
            result = stmt.executeUpdate(sql);

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(stmt);
        }
        return result;
    }

    public int deleteBook(Connection con, int num) {
        //몇개가 지워졌는가.
        int result = 0;
        Statement stmt = null;//데이터베이스에 질의를 날리는 거.

        try {
            stmt = con.createStatement();
            String sql = &quot;DELETE FROM a_book WHERE b_num =&quot; + num;
            result = stmt.executeUpdate(sql);    //cud에 해당.

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(stmt);
        }

        return result;
    }


    public int updateBook(Connection con, Book book) {
        int result = 0;
        Statement stmt = null;

        try {
            stmt = con.createStatement();

            String sql = &quot;UPDATE a_book SET b_title=&#39;%s&#39;, b_writer=&#39;%s&#39;, b_price=%d, b_publisher=&#39;%s&#39;, b_comment=&#39;%s&#39; WHERE b_num= %d&quot;;
            sql = String.format(sql, book.getTitle(), book.getWriter(), book.getPrice(), book.getPublisher(), book.getComment(), book.getNum());

            result = stmt.executeUpdate(sql); //일시켜야한다. 
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(stmt);
        }

        return result;
    }

    public Vector&lt;Book&gt; getAll(Connection con) {
        Vector&lt;Book&gt; list = new Vector&lt;Book&gt;();
        //책이 없을 때 빈 벡터를 던질 것이다.
        Statement stmt = null;
        ResultSet rs = null; //결과 집합. select문 결과를 담은 객체이다. 표같은 거 나타내는 거.

        try {
            stmt = con.createStatement();
            String sql = &quot;SELECT * FROM a_book ORDER BY b_num DESC&quot;;
            rs = stmt.executeQuery(sql);
            while(rs.next()) {    //다음 라인이 있습니까? true이면, 위치를 다음 레코드로 이동한다. 
                list.add(rowMapping(rs));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            close(rs);
            close(stmt);
        }

        return list;

    }
    //SELECT * FROM a_book WHERE b_num = ? ;
    public Book getBookByNum(Connection con, int num) {
        Book book = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            stmt = con.createStatement();
            String sql = &quot;SELECT * FROM a_book WHERE b_num =&quot; + num ;
            rs = stmt.executeQuery(sql);
            if(rs.next()) {
                book = rowMapping(rs);
            }

        }catch(SQLException e) {

        }finally {
            close(rs);
            close(stmt);
        }
        return book;
    }

    private Book rowMapping(ResultSet rs) throws SQLException { //spring에 나온다.
        int b_num = rs.getInt(&quot;b_num&quot;);
        String b_title = rs.getString(&quot;b_title&quot;);
        String b_writer = rs.getString(&quot;b_writer&quot;);
        String b_password = rs.getString(&quot;b_password&quot;);
        int b_price = rs.getInt(&quot;b_price&quot;);
        String b_publisher = rs.getString(&quot;b_publisher&quot;);
        String b_comment = rs.getString(&quot;b_comment&quot;);

        return new Book(b_num, b_title, b_writer, b_password, b_price, b_publisher, b_comment);
    }

    private void close(ResultSet rs) {
        try {
            rs.close();
        }catch(Exception e) {}
    }

    private void close(Statement stmt) {
        try {
            stmt.close();
        }catch (Exception e) {
        }
    }
}</code></pre><h4 id="testbookdaojava">TestBookDao.java</h4>
<pre><code>package kr.ac.green.test;

import java.sql.Connection;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import kr.ac.green.Book;
import kr.ac.green.dao.BookDao;

public class TestBookDao {
    // 어떤 상태냐에 따라 골라 사용하면 된다.
    private static BookDao dao;
    private Connection con;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {    // 전체 테스트 전 1번 호출
        dao = BookDao.getInstance();
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {    // 전체 테스트 후 1번 호출
    }

    @Before
    public void setUp() throws Exception {    // 매번 테스트할 때마다
        con = dao.connect();
        //con.setAutoCommit(false); //바로 값이 들어가지 말라고 해놓는 거라서 insert만 시행해도 값이 들어가지 않는다. 
    }

    @After
    public void tearDown() throws Exception {    // 매번 테스트할 때마다
        dao.disconnect(con);
    }

    @Test
    public void testInsertBook() {// 실제 테스트를 담당, 복수개 생성 가능
        //insertBook 테스트

        //가짜개체 만듦
        Book book = new Book(&quot;testTitle&quot;, &quot;testWriter&quot;, &quot;password&quot;, 100, &quot;testPublisher&quot;, &quot;testComment&quot;);
        int result = dao.insertBook(con, book); // 몇개가 인서트 되었는지를 리턴해준다.
        Assert.assertEquals(1, result);    // 예상하는 값(1)대로 나오면 실제 값은 result이니까 둘이 비교를 한다.

        //문제점이 있어 테스트를 하는데 실제로 값이 들어가버림
    }

    @Test
    public void testDeleteBook() {
        int result = dao.deleteBook(con, 9);
        Assert.assertEquals(1, result);
    }

    @Test
    public void testupdateBook() {
        Book book = new Book(16, &quot;title1&quot;, &quot;writer1&quot;, &quot;password1&quot;, 200, &quot;publisher1&quot;, &quot;comment1&quot;);
        int result = dao.updateBook(con, book);
        Assert.assertEquals(1, result);
    }

    @Test
    public void testGetAll() {
        Assert.assertEquals(2, dao.getAll(con).size());
    }

    @Test
    public void testGetBookByNum() {
        int num = 1;
        Book book = dao.getBookByNum(con, num);
        Assert.assertEquals(1, book.getNum());
        Assert.assertEquals(&quot;testComment&quot;, book.getComment());
    }
}
</code></pre><h4 id="bookdaojavapreparedstatement-버전-인코딩">BookDao.java(preparedStatement 버전, 인코딩)</h4>
<pre><code>package kr.ac.green.dao;

import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

import kr.ac.green.Book;

/*
 * JDBC(java database connectivity) 
 * 
 * 1. 드라이버 로드(1번만 수행하면 된다.)
 * 2. 연결
 * 3. 질의
 * 4. 자원해제
 */

public class BookDao {

    private static final BookDao INSTANCE = new BookDao();

    private BookDao() {
        // 1. 드라이버 로드
        try {
            Class.forName(&quot;com.mysql.jdbc.Driver&quot;);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static BookDao getInstance() {
        return INSTANCE;
    }

    public Connection connect() {    // import java.sql.Connection... 데이터 베이스에 연결이 되는거야
        Connection con = null;
        try {
            con = DriverManager.getConnection(&quot;jdbc:mysql://localhost:3306/test&quot;, &quot;root&quot;, &quot;1234&quot;);
        } catch(SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    public void disconnect(Connection con) {
        try {
            con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public int insertBook(Connection con, Book book) {
        int result = 0;
        // statement? 
        // 동시에 접근할 수 있는 세션 수 정해져 있는데 자원해제 안하면 그 세션이 물려서 accessDeny가 발생함..
        // 그럼 프로그램이 동작을 안해 그래서 자원해제 분명히 해야한다.
        //Statement stmt = null;    // sql.Statement
        PreparedStatement pStmt = null;
        String sql = &quot;INSERT INTO a_book (b_title, b_writer, b_password, b_price, b_publisher, b_comment)\r\n&quot; + &quot;VALUES (?, ?, ?, ?, ?, ?)&quot;;


        try {
            // executeUpdate :  INSERT, UPDATE, DELETE  =&gt; int 리턴
            // executeQUERY : SELECT 
            pStmt = con.prepareStatement(sql);
            pStmt.setString(1, toEn(book.getTitle()));
            pStmt.setString(2, toEn(book.getWriter()));
            pStmt.setString(3, book.getPassword());
            pStmt.setInt(4, book.getPrice());
            pStmt.setString(5, toEn(book.getPublisher()));
            pStmt.setString(6, toEn(book.getComment()));

            result = pStmt.executeUpdate();
            pStmt.clearParameters();

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(pStmt);
        }
        return result;
    }

    public int deleteBook(Connection con, int num) {
        //몇개가 지워졌는가.
        int result = 0;
        //Statement stmt = null;//데이터베이스에 질의를 날리는 거.
        PreparedStatement pStmt = null;
        String sql = &quot;DELETE FROM a_book WHERE b_num = ?&quot; ;

        try {
            pStmt = con.prepareStatement(sql);
            pStmt.setInt(1, num);
            result = pStmt.executeUpdate();    
            pStmt.clearParameters();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(pStmt);
        }

        return result;
    }

    public int updateBook(Connection con, Book book) {
        int result = 0;
        //Statement stmt = null;
        PreparedStatement pStmt = null;
        String sql = &quot;UPDATE a_book SET b_title=?, b_writer=?, b_price=?, b_publisher=?, b_comment=? WHERE b_num = ?&quot;;
        try {
            pStmt = con.prepareStatement(sql); //statement준비해라라는 뜻.
            pStmt.setString(1, toEn(book.getTitle()));
            pStmt.setString(2, toEn(book.getWriter()));
            pStmt.setInt(3, book.getPrice());
            pStmt.setString(4, toEn(book.getPublisher()));
            pStmt.setString(5, toEn(book.getComment()));
            pStmt.setInt(6, book.getNum());

            result = pStmt.executeUpdate();
            pStmt.clearParameters(); //여러개 수정, 삽입, 삭제할 때. 아이디가 여러개 들어올거고 여러개 삭제 될건데 그때 이걸 꼭 해줘야 한다. 값들을 전부 다 초기화해준다. 지금 하나씩 해서 필요없지만 나중에 여러개 할 때 필요함. 
            //stmt = con.createStatement();

            //String sql = &quot;UPDATE a_book SET b_title=&#39;%s&#39;, b_writer=&#39;%s&#39;, b_price=%d, b_publisher=&#39;%s&#39;, b_comment=&#39;%s&#39; WHERE b_num= %d&quot;;
//            sql = String.format(
//                    sql, 
//                    toEn(book.getTitle()),
//                    toEn(book.getWriter()), 
//                    book.getPrice(), 
//                    toEn(book.getPublisher()), 
//                    toEn(book.getComment()),
//                    book.getNum());

            //result = stmt.executeUpdate(sql); //일시켜야한다. 
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //close(stmt);
            close(pStmt);
        }

        return result;
    }

    public Vector&lt;Book&gt; getAll(Connection con) {
        Vector&lt;Book&gt; list = new Vector&lt;Book&gt;();
        //책이 없을 때 빈 벡터를 던질 것이다.
        PreparedStatement pStmt = null;
        ResultSet rs = null; //결과 집합. select문 결과를 담은 객체이다. 표같은 거 나타내는 거.
        String sql = &quot;SELECT * FROM a_book ORDER BY b_num DESC&quot;;

        try {
            pStmt = con.prepareStatement(sql);
            rs = pStmt.executeQuery();
            while(rs.next()) {    //다음 라인이 있습니까? true이면, 위치를 다음 레코드로 이동한다. 
                list.add(rowMapping(rs));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            close(rs);
            close(pStmt);
        }

        return list;

    }
    //SELECT * FROM a_book WHERE b_num = ? ;
    public Book getBookByNum(Connection con, int num) {
        Book book = null;
        PreparedStatement pStmt = null;
        ResultSet rs = null;

        String sql = &quot;SELECT * FROM a_book WHERE b_num = ?&quot;;

        try {
            pStmt = con.prepareStatement(sql);
            pStmt.setInt(1, num);
            rs = pStmt.executeQuery();
            pStmt.clearParameters();
            if(rs.next()) {
                book = rowMapping(rs);
            }

        }catch(SQLException e) {

        }finally {
            close(rs);
            close(pStmt);
        }
        return book;
    }

    public Vector&lt;Book&gt; getBooksByTitle(Connection con, String b_title) {
        Vector&lt;Book&gt; list = new Vector&lt;Book&gt;();
        PreparedStatement pStmt = null;    // 쿼리를 중간에 포멧한 것을 얘가 가지고 있다. 날때부터 준비가 되어 있는 애다. 뭘 할지 알고 있다. 쿼리구문을 해석하면서 태어난다. 
        ResultSet rs = null;

        String sql = &quot;SELECT * FROM a_book WHERE b_title LIKE ?&quot;;
        try {
            pStmt = con.prepareStatement(sql);
            //와일드 카드 여기 적기 쿼리문에 적는 게 아니라. 위의 LIKE 뒤에 적는 게 아니라. 
            pStmt.setString(1, &quot;%&quot; + toEn(b_title) + &quot;%&quot;);//첫번째 물음표에 title을 넣어라. 인덱스인데 1부터 시작한다. 여기에 와이드카드를 넣어줘야 한다. 그래야 오류가 나지 않는다. 
            rs = pStmt.executeQuery(); //파라미터 없다. 
            while(rs.next()) {
                list.add(rowMapping(rs));
            }

        }catch(SQLException e) {
            e.printStackTrace();
        }

        return list;

    }

    private Book rowMapping(ResultSet rs) throws SQLException { //spring에 나온다.
        int b_num = rs.getInt(&quot;b_num&quot;);
        String b_title = toKor(rs.getString(&quot;b_title&quot;));
        String b_writer = toKor(rs.getString(&quot;b_writer&quot;));
        String b_password = rs.getString(&quot;b_password&quot;);
        int b_price = rs.getInt(&quot;b_price&quot;);
        String b_publisher = toKor(rs.getString(&quot;b_publisher&quot;));
        String b_comment = toKor(rs.getString(&quot;b_comment&quot;));

        return new Book(b_num, b_title, b_writer, b_password, b_price, b_publisher, b_comment);
    }

    private void close(ResultSet rs) {
        try {
            rs.close();
        }catch(Exception e) {}
    }

    private void close(Statement stmt) {
        try {
            stmt.close();
        }catch (Exception e) {
        }
    }

    /*
     * DB : 8859_1
     * java : EUC-KR
     * insert, update -&gt;  euc_kr -&gt; 8859_1  :이거는 static toEnd(kor:String) : String 메소드로 만든다.
     * select : 8859_1 -&gt; euc-kr : static toKor(en:String) : String
     * */

    public String toEn(String kor) {
        String en = null;
        try {
            en = new String(kor.getBytes(&quot;EUC_KR&quot;), &quot;8859_1&quot;);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return en;
    }

    public String toKor(String en) {
        String kor = null;
        try {
            kor = new String(en.getBytes(&quot;8859_1&quot;), &quot;EUC_KR&quot;);
        }catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return kor;
    }
}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[sql(테이블만들기, SELET문..)]]></title>
            <link>https://velog.io/@j_code/sql-dwy7wig4</link>
            <guid>https://velog.io/@j_code/sql-dwy7wig4</guid>
            <pubDate>Tue, 23 May 2023 10:13:51 GMT</pubDate>
            <description><![CDATA[<h2 id="sql-까는-방법">sql 까는 방법</h2>
<p><img src="https://velog.velcdn.com/images/j_code/post/bbb90f08-d17d-42c7-961d-ce6357ea8b30/image.png" alt="">
1.sql 깐 다음에 HeidiSQL_11.2_64_Portable의 heidisql.exe를 실행
2.신규버튼 누르기
3.세션이름바꿔주기
4.암호 <strong>1234</strong>입력하기</p>
<hr>
<h2 id="sql-기본-설명">sql 기본 설명</h2>
<ul>
<li>데이터베이스 안에는 테이블형태로 저장됨.</li>
<li>키워드는 대문자로. 내가만든 건 소문자로. 근데 대소문 크게 구분없음.</li>
<li>문장의 끝에는 세미콜론.</li>
<li>문자를 <strong>&#39; &#39;</strong>로 표현.</li>
<li>&#39;--&#39; : 한줄주석, &#39;/* */&#39; :여러줄 주석</li>
</ul>
<hr>
<h2 id="sql-코드-설명">sql 코드 설명</h2>
<p><img src="https://velog.velcdn.com/images/j_code/post/3a8caf99-e960-4406-aae8-cd120552a53f/image.png" alt=""></p>
<ul>
<li><p><strong>num INT PRIMARY KEY</strong> : 정수형태이고 인마가 이 테이블의 주키입니다는 뜻.</p>
</li>
<li><p><strong>DESC 테이블명;</strong> : 스키마. 테이블구조 확인하는 거.(아래설명)</p>
</li>
<li><p><strong>DROP TABLE 테이블명;</strong> :  테이블 삭제.</p>
</li>
<li><p><strong>CHAR(10)</strong>: 10은 렝스가 아니고, 바이트이다. 고정길이 문자열</p>
</li>
<li><p><em>VARCHAR(10)*</em> : 가변길이 문자열(바캐릭터라고 읽음.)</p>
</li>
<li><p><strong>DECIMAL</strong> : 실수 나타낼때(데시말)
DECIMAL(5,2) 5: 전체자리수, 2 : 실수로 표현할 자리수(소수이하의 자리)</p>
</li>
<li><p><strong>SELECT * FROM 테이블명</strong> : 전체 레코드 확인, 내가 넣은 코드를 보려면 이걸 부분실행해야한다. </p>
</li>
</ul>
<blockquote>
<p><strong>스키마 설명(DESC 테이블명)</strong>
<img src="https://velog.velcdn.com/images/j_code/post/5fa1ff92-4862-4ffc-923d-96d4f89378cc/image.png" alt="">
테이블안의 int(11)은 값을 설정하는게 아니라 그냥 기본이 11자리라는 뜻. int에 &#39;1&#39;을 넣었는다면, 11자리로 끊어서 앞에는 0으로 10개채우고 1을 보여준다는 뜻.
null이 no되어 있는 건 무조건 값을 넣어야 된다는 뜻.
dfault : 기본값은 다 null.</p>
</blockquote>
<blockquote>
<p><strong>CHAR와 VARCHAR 설명</strong></p>
</blockquote>
<ul>
<li><strong>CHAR(5)</strong>: 무조건 5바이트의 크기를 잡아먹는다. 만약 abc를 넣으면 abcOO이렇게 자리 차지한다. 
따라서, <strong>자주 바뀌는 값들은 여기에 쓴다</strong>. 글자제목 같은 거. 닉네임 같은거. </li>
<li><strong>VARCHAR(5)</strong> : 만약 abc를 넣으면 5칸중에 3칸만 사용한다. 모든 칸을 잡아먹지 않는다. 
따라서, <strong>변경할 수 없는 항목이나 자리수 정해진거, 잘안바꾸는 항목들에 사용</strong> - 아이디. 공간적인 이득은 취하지만, 변경에 대해서 취약하다. </li>
<li>값이 바뀌면, abcd로 바뀌게 되면, CHAR는 그냥 바로 값을 이어서 적을 수 있지만, VARCHAR는 abc뒤에 들어온 애들을 한칸씩 다 밀어내야 한다. 반대로 ab만 넣으면 한칸씩 땡겨와야한다. </li>
</ul>
<blockquote>
</blockquote>
<p><strong>DECIMAL 설명</strong><img src="https://velog.velcdn.com/images/j_code/post/baa347dc-e1de-406b-a0fe-920599433321/image.png" alt="">
<img src="https://velog.velcdn.com/images/j_code/post/afdf8ba4-0c96-450c-a6aa-22bb77525f10/image.png" alt="">
반올림되었다. 
&quot;23.456&quot;은 되는데, &quot;1234.56&quot;은 안된다.
5,2면 정수분은 3자리를 넣을 수 있다는 것이니까. 총 3자리까지 들어갈 수 있다. 근데 이 정수분의 수가 넘어가면 무조건 에러다. 근데 실수분은 2자리 넘어가면 반올림이 된다. 그 자리수에 맞춰서. 
&quot;1.23456&quot;을 넣으면 1.23이라는 값이 나온다. -&gt; 경고는 뜰 수 있으나 에러는 아니다. 
&quot;12345&quot;하면 에러 뜬다. 정수부 자릿수가 넘어갔다. </p>
<blockquote>
<p>영향 받은 행: 1 몇개의 레코드가 영향을 받았는지. 한줄 넣어서 1이 나옴.
<strong>잡아넣는거, 수정하는 거, 삭제하는거 =&gt; 행이 바뀐다.</strong>
3개를 잡아넣엇으면 3이 바뀜.
우리는 root계정을 쓰고 있어서 다 삭제하고 수정하고 하는데, 일하러 가면 다른 계정을 줘서 테이블 삭제같은 거 못하게 해놨을 것이다. </p>
</blockquote>
<hr>
<p>&lt;sql코드&gt;</p>
<pre><code class="language-sql">--한줄주석
/*
여러줄 주석
*/

DROP TABLE a_first;

CREATE TABLE a_first(    
    num            INT            PRIMARY KEY,
    my_value1    CHAR(10)        NOT NULL,
    my_value2    VARCHAR(10),
    my_value3    DECIMAL(5,2)

);
--테이블 구조(스키마) 확인
DESC a_first;

--값입력
INSERT INTO a_first (num,my_value1,my_value2,my_value3)
    VALUES(1, &#39;first&#39;, &#39;choonsik&#39;, 3.14);

INSERT INTO a_first VALUES (2, &#39;second&#39;, NULL, NULL);

INSERT INTO a_first (my_value3, num, my_value2, my_value1)
    VALUES (0.9, 3, &#39;iu&#39;, &#39;third&#39;);

INSERT INTO a_first (num, my_value1, my_value2)
    VALUES (4, &#39;forth&#39;, &#39;foo&#39;);
/*
INSERT INTO a_first (num,my_value1,my_value2,my_value3)
    VALUES(5, &#39;fifth&#39;, &#39;bar&#39;, 23.456);

INSERT INTO a_first (num,my_value1,my_value2,my_value3)
    VALUES(6, &#39;sixth&#39;, &#39;bar&#39;, 1234.56);

-- 실수부 자리수 2를 초과한 경우 3번째 자리에서 반올림한다.
INSERT INTO a_first (num,my_value1,my_value2,my_value3)
    VALUES(7, &#39;a&#39;, &#39;bar&#39;, 1.123456);
-- 정수부 자리수가 넘어갔음.
INSERT INTO a_first (num,my_value1,my_value2,my_value3)
    VALUES(8, &#39;ac&#39;, &#39;bar&#39;, 12345);
*/

INSERT INTO a_first(num, my_value1,my_value2,my_value3)
VALUES
(5, &#39;e&#39;, &#39;muzi&#39;, 100.23);
(6, &#39;f&#39;, &#39;neo&#39;, 22.11);
(7, &#39;g&#39;, &#39;ryan&#39;, 7.23);
(8, &#39;h&#39;, &#39;con&#39;, 9.99);

--삭제
DELETE FROM a_first WHERE num = 2;a_first

-- 수정
-- UPDATE a_first SET my_value3 = 15.0;
-- where안하면 전부 다 바껴버림!!!!
UPDATE a_first SET my_value3 = 15.0 WHERE num = 4;
UPDATE a_first SET my_value3 = 10.04, my_value1=&#39;a&#39; WHERE num= 1;

--
UPDATE a_first SET my_value3 = 11, my_value1 = &#39;f&#39; WHERE num = 1;


--전체 레코드 확인
SELECT * FROM a_first;

SELECT * FROM a_first WHERE num = 2;
SELECT my_value2 FROM a_first WHERE my_value3&gt;=10 AND my_value3&lt;=100;

-- null은 =로 검사하면 안된다. 
SELECT num, my_value2 FROM a_first WHERE my_value3 IS NULL;
SELECT num, my_value2 FROM a_first WHERE my_value3 IS NOT NULL;

--오름차순, 내림차순
SELECT * FROM a_first ORDER BY my_value3 ASC;
SELECT * FROM a_first ORDER BY my_value3 DESC;

-- my_value3이 5이상 100이하인 레코드
SELECT my_value2 FROM a_first WHERE my_value3 BETWEEN 5 AND 100;

--my_value1이 &#39;f&#39;가 아닌 레코드
SELECT * FROM a_first WHERE my_value1 &lt;&gt; &#39;f&#39;;
SELECT * FROM a_first WHERE NOT my_value1 =&#39;f&#39;;

--my_value2가 &#39;iu&#39; 또는 &#39;foo&#39;인 레코드 구하기
SELECT * FROM a_first WHERE  my_value2 =&#39;iu&#39; OR my_value2=&#39;foo&#39;;
SELECT * FROM a_first WHERE  my_value2 IN (&#39;iu&#39;,&#39;foo&#39;);

--my_value2에 &#39;o&#39;가 들어간 레코드를 모두 구하라.
/*
    와일드 카드
    % : 0~무한대
    _ : 1글자
*/
SELECT * FROM a_first WHERE my_value2 LIKE &#39;%o%&#39;
SELECT * FROM a_first WHERE my_value2 LIKE &#39;c%&#39;

--my_value2가 3글자인데 o로 끝나는 거.
SELECT * FROM a_first WHERE my_value2 LIKE &#39;__o&#39;</code></pre>
<blockquote>
</blockquote>
<ul>
<li><strong>INSERT INTO 테이블명</strong> <strong>VALUES</strong> :테이블에 내가 값을 잡아 넣겠다.</li>
<li><strong>DELTE FROM 테이블 명</strong> : 삭제</li>
<li><strong>UPDATE 테이블명 SET 컬럼명=수정할 값 WHHRE 조건</strong> : 수정. where안하면 전부 다 바껴버리기 때문에 where 꼭 넣기!</li>
<li><strong>SELECT * FROM 테이블명</strong> : 전체 레코드 확인, 내가 넣은 코드를 보려면 이걸 부분실행해야한다. </li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><strong>null은 =로 검사하면 안된다</strong>
SELECT num, my_value2 FROM a_first WHERE my_value3 IS NULL;
SELECT num, my_value2 FROM a_first WHERE my_value3 IS NOT NULL;</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><strong>오름차순, 내림차순</strong>
SELECT * FROM a_first ORDER BY my_value3 ASC;
SELECT * FROM a_first ORDER BY my_value3 DESC;</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><strong>my_value3이 5이상 100이하인 레코드</strong></li>
<li><em>SELECT*</em> my_value2 <strong>FROM</strong> a_first <strong>WHERE</strong> my_value3 <strong>BETWEEN</strong> 5 <strong>AND</strong> 100;</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><strong>my_value1이 &#39;f&#39;가 아닌 레코드</strong>
SELECT * FROM a_first WHERE my_value1 <strong>&lt;&gt;</strong> &#39;f&#39;;
SELECT * FROM a_first WHERE <strong>NOT</strong> my_value1 =&#39;f&#39;;
.</li>
<li>** my_value2가 &#39;iu&#39; 또는 &#39;foo&#39;인 레코드 구하기**
SELECT * FROM a_first WHERE  my_value2 =&#39;iu&#39; <strong>OR</strong> my_value2=&#39;foo&#39;;
SELECT * FROM a_first WHERE  my_value2 <strong>IN</strong> (&#39;iu&#39;,&#39;foo&#39;);</li>
</ul>
<blockquote>
</blockquote>
<p><strong>와일드카드</strong> </p>
<ul>
<li>% : 0~무한대</li>
<li>_ : 1글자
.</li>
<li><strong>my_value2에 &#39;o&#39;가 들어간 레코드를 모두 구하라.</strong>
SELECT * FROM a_first WHERE my_value2 LIKE &#39;%o%&#39;
.</li>
<li><strong>my_value2에 &#39;c&#39;로 시작하는 레코드를 모두 구하라.</strong>
SELECT * FROM a_first WHERE my_value2 LIKE &#39;c%&#39;
.</li>
<li><strong>my_value2가 3글자인데 o로 끝나는 거.</strong>
SELECT * FROM a_first WHERE my_value2 LIKE &#39;__o&#39;</li>
</ul>
<hr>
<ul>
<li><p><strong>DML</strong> - 데이터 조작 : insert, select, delete, update(우리가 해야하는거!) - crud만 하면 된다. </p>
</li>
<li><p><strong>DCL</strong> - 데이터 제어 
commit : 내가 이때까지 했던 작업들 적용해
rollback:지금까지 처리했던 거 취소해, 
grant :권한설정하는거, 
revoke :권한을 취소하는 거</p>
</li>
<li><p><strong>DDL</strong> - 데이터 정의 : create, drop, alter</p>
</li>
</ul>
<hr>
<h2 id="이클립스에-sql적용">이클립스에 sql적용</h2>
<p><a href="https://mvnrepository.com/">https://mvnrepository.com/</a> -&gt; sql관련 다운 거 다 받을 수 있다. 
여기에 가서 &#39;connector&#39; 이라고 검색한 후에 
MySQL Connector Java 클릭 후에 5.1.49을 클릭후 jar파일을 다운 받는다.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/8adab518-2615-49ba-8d48-1caa029a4cfe/image.png" alt=""></p>
<p>이클립스로 돌아와서..
톰캣안에 있는 libraries(java resource)에 넣을 수도 있고, 
05.23에서 있는 <strong>WEB-INF안에 있는 lib</strong>에 넣을 수도 있는데, 
05.23에 넣으면 코드에 필요한 것을 넣는 것이다. 
배포할때는 라이브러리 폴더 안에 있는 애들만 나간다.</p>
<ul>
<li>실행 : 드래그 + alt + x</li>
</ul>
<p><img src="https://velog.velcdn.com/images/j_code/post/264f58f7-bd12-4862-89fd-ee969209d8a8/image.png" alt=""></p>
<h3 id="이클립스에서-sql코드-작성">&lt;이클립스에서 sql코드 작성&gt;</h3>
<pre><code class="language-sql">--실행 : 드래그 + alt + x
DROP TABLE a_second;

CREATE TABLE a_second (
    id            INT            PRIMARY KEY         AUTO_INCREMENT,
    user_name    VARCHAR(20)    NOT NULL,    
    user_age    INT            NOT NULL,    
    user_job    VARCHAR(20)    NOT NULL,    
    user_tel    CHAR(11)    NOT NULL
);

INSERT INTO a_second (user_name, user_age, user_job, user_tel)
VALUES 
(&#39;smith&#39;, 30, &#39;singer&#39;, &#39;01033334444&#39;),
(&#39;jane&#39;, 22, &#39;dancer&#39;, &#39;01012345678&#39;),
(&#39;tomas&#39;, 45, &#39;student&#39;, &#39;01088887777&#39;),
(&#39;sam&#39;, 26, &#39;waiter&#39;, &#39;01055556666&#39;);

SELECT * FROM a_second;

--20세 이상 30세 이하인 사람 중 직업이 &#39;dancer&#39;인 사람의 이름과 나이 전화번호를 구하라
SELECT user_name, user_age, user_tel FROM a_second WHERE (user_job =&#39;dancer&#39;)AND (user_age BETWEEN 20 AND 30);

--이름에 &#39;a&#39;가 들어가거나 &#39;s&#39;가 들어가는 사람의 모든 정보를 구하라. 단 나이로 내림차순 정렬한다.
SELECT * FROM a_second WHERE user_name LIKE &#39;%a%&#39;OR user_name LIKE &#39;%s%&#39; ORDER BY user_age DESC;

--tomas의 나이를 38로 변경하시오. 키가 아니기때문에 where을 select문으로 날려주고 맞는지 확인하고 여기에 넣기. 
UPDATE a_second SET user_age = 38 WHERE user_name =&#39;tomas&#39;;
SELECT * FROM a_second WHERE user_name =&#39;tomas&#39;;

</code></pre>
<ul>
<li><p><strong>AUTO_INCREMENT</strong> : 우리가 관리안하고 알아서 자동으로 번호를 발급해준다. mysql에서만 되는 거. 주키에만 사용하가능하고, 한 테이블에 하나씩만 가능하다. </p>
<ul>
<li>*<em>JDBC(java database connectivity) *</em></li>
</ul>
<ol>
<li>드라이버로드    : 딱한번만 하면 된다.
그래서 생성자에 넣어놓는다. 
다음에 데이터베이스 하면 2~4번이 반복되는 것이다. </li>
<li>연결    </li>
<li>질의</li>
<li>자원해제</li>
</ol>
</li>
</ul>
<h4 id="이클립스-코드-적용book코드">이클립스 코드 적용(book코드)</h4>
<h4 id="booksql">book.sql</h4>
<pre><code>CREATE TABLE book (
    b_num            INT                PRIMARY KEY            AUTO_INCREMENT,
    b_title            CHAR(200)        NOT NULL,        
    b_writer        CHAR(30)        NOT NULL,        
    b_password        VARCHAR(10)        NOT NULL,        
    b_price            INT                NOT NULL,
    b_publisher        VARCHAR(30)        NOT NULL,
    b_comment        CHAR(200)        NOT NULL,
);

--전체보기
SELECT * FROM book ORDER BY b_num DESC;

--책 추가하기
INSERT INTO book (b_title, b_writer, b_password, b_price, b_publisher, b_comment)
    VALUES (?,?,?,?,?,?);

--  책 삭제
DELETE FROM book WHERE b_num =?;</code></pre><h4 id="bookdaojava">BookDao.java</h4>
<pre><code>package kr.ac.green;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/*
 * JDBC(java database connectivity)
 * 
 * 1.드라이버로드    
 * 2.연결    
 * 3.질의
 * 4.자원해제
 * */

public class BookDao {
    private static final BookDao INSTANCE = new BookDao();

    private BookDao() {
        //1.드라이버로드
        try {
            Class.forName(&quot;com.mysql.jdbc.Driver&quot;);    //mysql드라이버
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static BookDao getInstance() {
        return INSTANCE;
    }

    //2.연결
    public Connection connect() {
        //java.Connection
        Connection con = null;
        try {
            //jdbc:mysql://localhost:3306/text mysql의 url인듯...
        con = DriverManager.getConnection(&quot;jdbc:mysql://localhost:3306/text&quot;, &quot;root&quot;, &quot;1234&quot;);
        }catch(SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    //4.자원해제
    public void disconnect(Connection con) {
        try {
            con.close();
        }catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public int insertBook(Connection con, Book book) {
        int result = 0;
        //데이터베이스에 질의를 날리는 거. - statement
        Statement stmt = null;
        //import : java.sql.Statement

        try {
            //executeUpdate : insert, update, delete =&gt; int리턴 :3개 지워지면 3이 리턴되는 것이다. ㅌ
            //executeQuery : select
            stmt = con.createStatement();
            /*
            String sql = &quot;INSERT INTO book (b_title, b_writer, b_password, b_price, b_publisher, b_comment)&quot;
                    + &quot;VALUES (&#39;&quot;+book.getTitle() +&quot;&#39;,&#39;&quot;+book.getWriter() +&quot;&#39;,&#39;&quot;+book.getPassword() +&quot;&#39;,&quot;+book.getPrice() +&quot;,&#39;&quot;+book.getPublisher() +&quot;&#39;,&#39;&quot;+book.getComment() +&quot;&#39;)&quot;;
            */
            String sql = &quot;INSERT INTO book (b_title, b_writer, b_password, b_price, b_publisher, b_comment) VALUES (&#39;%s&#39;,&#39;%s&#39;,&#39;%s&#39;,&#39;%d&#39;,&#39;%s&#39;,&#39;%s&#39;)&quot;;
            sql = String.format(sql, book.getTitle(), book.getWriter(), book.getPassword(), book.getPrice(), book.getPublisher(), book.getComment());
            result = stmt.executeUpdate(sql);

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                //자원해제 -  데이터베이스도 max세션이 있어서 자원해제를 안하면 계속 세션이 남아있다. 엑세스디나이가 떠서 안된다. 무조건 필수로 해줘야 한다. 
                stmt.close();
            }catch(Exception e) {

            }
        }
        return result;

    }
}
</code></pre><h4 id="testdaojava">TestDao.java</h4>
<pre><code>package kr.ac.green.text;

import java.sql.Connection;    //이걸로 import해야한다!

import kr.ac.green.Book;
import kr.ac.green.BookDao;

public class TestDao {
    public static void main(String[] args) {
        BookDao dao = BookDao.getInstance();

        Connection con = dao.connect();

        int result = dao.insertBook(con, new Book(&quot;power java&quot;, &quot;inkuk&quot;, &quot;1234&quot;, 28000, &quot;infinity books&quot;, &quot;wow!!&quot;));

        dao.disconnect(con);
        System.out.println(result); //1이 나오면 제대로 작동.
    }
}
</code></pre><p>먜) 
자바에서 sql문 실행하기 위한것 : <strong>Statement객체</strong>
쿼리를 삽입할 때는 Statement객체를 사용하는 것 같음. </p>
<p>Java에서 데이터베이스와의 연결을 나타내는 인터페이스 : <strong>Connection객체</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서블릿]]></title>
            <link>https://velog.io/@j_code/%EC%84%9C%EB%B8%94%EB%A6%BF</link>
            <guid>https://velog.io/@j_code/%EC%84%9C%EB%B8%94%EB%A6%BF</guid>
            <pubDate>Mon, 15 May 2023 08:47:47 GMT</pubDate>
            <description><![CDATA[<h2 id="서블릿">서블릿</h2>
<ul>
<li>init() : 최초로 만들어질때 딱 한번 생성.- 서블릿 객체 생성시 기본 실행 메서드. 생성자대신에 쓰는 것 같음.</li>
<li>destroy() : 종료될 때 딱 한번 생성.</li>
<li>service()메서드로 요청이 들어왔을 때 get방식인지 post방식인지 체크하고 deGet()과 doPost()가 나온다. 
service()를 직접 오버라이드 하지 않고, doGet()과 doPost()를 사용한다. 그냥 service()안에 doGet()과 doPost()가 있는데 service()사용하지 않고 바로 저 두개를 쓴다고 생각하기.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/j_code/post/5a9260e5-8c0b-4394-b961-fca031e70fb3/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_code/post/cd9faa35-3795-42d6-802c-421cb9c54738/image.jpg" alt="">
서블릿 만들 때 <strong>마법사</strong> 이용하면 이렇게 만들어 진다.
마법사는 프레임워크같은 느낌.</p>
<hr>
<p><code>http://localhost:8080/05_15/FirstServlet -&gt; 
urlpattern -&gt;servletname -&gt; &lt;servlet의 태그안의 name&gt; -&gt; servletclass</code>의 순서대로 실행된다.
<img src="https://velog.velcdn.com/images/j_code/post/bd17c87f-814c-4c40-90f1-6653f7c6bc12/image.png" alt=""></p>
<hr>
<p><img src="https://velog.velcdn.com/images/j_code/post/cc5c4ac9-b01e-42d9-99f5-a2c916d30304/image.png" alt=""></p>
<p><strong>url-pattern = 요청.</strong>
&#39;*.do&#39; 뒤에 .do로 끝나면 무조건 쟤가 나온다 -&gt; 전형적인 사용방법
*을 한다라는 뜻. .do앞에 무엇이 오든지 상관없다. </p>
<hr>
<p>서블릿이 여러개 존재할 수 있는데, 
그때마다 <strong>servlet태그</strong>와 <strong>servlet-mapping</strong>이 만들어질 것이다.
서블릿 3.0부터는 web.xml말고 annotation에 하게도 하는데 길이도 길어지고 복잡해짐. </p>
<p><strong>인위적으로 서블릿태그안의 url-pattern를 내가 만들 수 있다.</strong>
url-pattern을 내가 마음대로 바꿀 수 있다. 
<a href="http://localhost:8080/05_15/a/b/c/d/FirstServlet">http://localhost:8080/05_15/a/b/c/d/FirstServlet</a>
05_15안에 a안에 b안에 c안에 d가 실제로 존재하지 않아도 사용할 수 있다.
이것의 좋은점은? 실제로 있는 경우인지 없는 경우인지에 대한 것을 우리만 알 수 있다. 내맘대로 바꿀 수 있다. 보안상에 좋다.
유저별 페이지를 한번에 해결할 수 있다. -&gt; 한번에 처리 가능해짐. 
<code>choonsik.jsp?class=a&amp;age=39&amp;gender=male</code>을
<code>classA/male/39/choonsik.member</code> 이렇게 바꿀 수 있다.</p>
<hr>
<ul>
<li><strong>context_param</strong> : 어플리케이션전체에서.아무데서나 다 쓸 수 있음.</li>
<li><strong>init_param</strong> : FirstServlet서블릿 내부에서만. -config.getInitParameter() or getInitParameter()로 들고 올 수 있다.
같은 게 아니다!! 주체가 다르다.</li>
</ul>
<p>이걸 배우는 이유는 move.jsp(모든 기능이 모여있는 jsp)같은 것들 대신에 서블릿을 사용할 거다.
사용자의 눈에 보이지 않는 jsp는 의미가 없다. </p>
<p>서블릿객체도 서버가 꺼질 때까지 유지가 된다.</p>
<p>init()이 첫 요청이 들어왔을 때 만들어진다.
web.xml에서 
 <code>&lt;load-on-startup&gt;0&lt;/load-on-startup&gt;</code>를 추가하고 다시 실행하면,
init()이 요청을 안해도 init이 생긴다. 
서버가 사작될 때 init으로 간다. 
오래 걸리는 작업들은 미리 처리해놓은 거다. 
서블릿이 여러개 있을 수 있는데 <code>&lt;load-on-startup&gt;0&lt;/load-on-startup&gt;</code> 것이 init되는 순번이다. 
그래서 0을 넣으면 제일 처음으로 시작이 된다. 
init()을 적긴 적어야 하는듯.</p>
<p>인잇끝나면 get, post같은 게 거쳐보게 된다. init이 양이 많으면 오래 기다려야 된다. 인잇은 한번만 하면 되니까 서버가 시작될 때 미리 시작해놓는다.</p>
<p>걍) 세션이랑 어플리케이션 만들기
HttpSession session = request.getSession();<br>ServletContext application = session.getServletContext();</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹(세션)]]></title>
            <link>https://velog.io/@j_code/%EC%9B%B9%EC%84%B8%EC%85%98</link>
            <guid>https://velog.io/@j_code/%EC%9B%B9%EC%84%B8%EC%85%98</guid>
            <pubDate>Wed, 03 May 2023 14:24:00 GMT</pubDate>
            <description><![CDATA[<p>이력서 -&gt; 결과물 -&gt; 포트폴리오(담당파트)스크린샷은 너무 많이 붙이면 안된다. 웹 관련된 것은 crud 4개가 있어야 한다. 화면만 만들어서 캡쳐해서 넣어도 된다. 프로토콜 엑셀파일캡쳐하기.</p>
<hr>
<h2 id="클라이언트와의-대화2--세션⭐⭐⭐">클라이언트와의 대화2 : 세션⭐⭐⭐</h2>
<h3 id="session-기본-객체">session 기본 객체</h3>
<blockquote>
<p><strong>세션</strong> : 클라이언트 상태를 서버측에서 관리하는 거. 
<strong>서버 영역 저장공간 = 서버측의 데이터 보관소</strong></p>
</blockquote>
<p>쿠키와의 차이점은 세션은 브라우저가 아니라 서버에 값을 저장한다는 점이다.
http프로토콜은 계속 유지하는게 아니라, 연결했다 끊겼다를 반복한다.
그래서 상태정보를 저장할 수 없다. 이전에 클라이언트들이 뭘 했는지 어떤 일을 해왔는지 어떤 정보를 가졌는지를 모른다. 
그래서 &#39;상태유지&#39;를 위해서 사용되는 방법이 &#39;세션&#39;이다. 
세션은 브라우저가 시작과 종료를 나타냄.</p>
<p>브라우저는 정보를 저장할 때 쿠키사용, 세션은 웹 컨테이너에 정보를 보관할 때 사용.
<strong>세션은 오직 서버에서만 생성된다.</strong>
클라이언트가 뭘 했는지에 대한 정보를 담당.
<strong>클라이언트(브라우저) 당 한개씩 세션이 할당</strong>된다. 
(클라이언트 담당하는 스레드같은 느낌. 저장소 같은 거.)
쿠키와 마찬가지로 세션도 생성해야만 정보를 저장할 수 있다.</p>
<h3 id="세션-생성하기">세션 생성하기</h3>
<blockquote>
<p>&lt;%@ page session=&quot;true&quot;%&gt;
true가 기본 값이다. false를 하는 걸 본 적이 없다. 걍 무조건 true.
위에 처럼 적으면 세션이 생성된다.
브라우저가 처음 접속할 때 세션을 생성하면 이후로는 이미 생성된 세션을 사용한다.</p>
</blockquote>
<h3 id="세션-기본객체">세션 기본객체</h3>
<p>세션 사용한다 = session 기본 객체를 사용한다
session 기본 객체는 속성을 제공해서
setAttribute(), getAttribute()를 사용해서 속성값을 저장하고 읽어올 수 있다.</p>
<blockquote>
<p><strong>session 메서드</strong></p>
</blockquote>
<ul>
<li>getId() : id구하기</li>
<li>getCreationTime() : 클라이언트가 나에게 첫 요청을 날린 시간 - 바뀌지않는다. 단위 : 1/1000초</li>
<li><strong>getLastAccessedTime()</strong> : 마지막 요청을 날린 시간 - 마지막 요청시간이라서 바뀐다. : 1/1000초</li>
</ul>
<p>각각의 브라우저마다가 갖는 세션을 구분하기 위해서(사용자를 구분하기 위해서) 세션마다 고유 ID를 할당하는데 그 아이디를 세션ID라고 한다. 
서버가 브라우저에게 세션id를 전송하면 브라우저가 쿠키에 세션id를 저장.
그리고 <strong>브라우저가</strong> 서버에 많은 세션들 중에서 어떤게 내 세션인지 찾을수 있는 세션ID를 <strong>요청헤더</strong> 넣어서 서버에 보낸다.
그러면 그 아이디를 브라우저가 쿠키에 JSessionID라는 이름으로 세션id를 저장했다가 보내는 거다.
*<em>JSessionId *</em>: 이 브라우저에게 할당된 세선id같은 거. 세션 식별자.
요청할 때마다 담당하는 세션이 누군지 클라이언트가 보내주는 정보에 의존한다.
쿠키가 차단되어 있으면 로그인을 했음에도 불구하고 다음페이지 가면 로그아웃되어 있고 그럼.. </p>
<h4 id="sessioninfojsp">sessionInfo.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page session =&quot;true&quot; %&gt;
&lt;%@ page import =&quot;java.util.Date&quot; %&gt;
&lt;%@ page import =&quot;java.text.SimpleDateFormat&quot; %&gt;
&lt;%
    Date time = new Date();
    SimpleDateFormat formatter = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;세션 정보&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
세션ID : &lt;%= session.getId() %&gt;&lt;br&gt;
&lt;%
    time.setTime(session.getCreationTime());
%&gt;
세션시간 : &lt;%=formatter.format(time) %&gt;&lt;br&gt;
&lt;%
    time.setTime(session.getLastAccessedTime());
%&gt;
최근 접근시간: &lt;%=formatter.format(time) %&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/a2e31248-183d-4779-bd4d-2e80969504d8/image.png" alt=""></p>
<h3 id="기본객체-속성-사용">기본객체 속성 사용</h3>
<p>한 번 생성된 세션은 지정한 유효 시간동안 유지.
세션은 데이터 저장소 같은 거. 세션은 내부자원이 아니니까 읽고 쓸 때 훨씬 더 부담이 없다.
특히, 사용자의 id같은 것들, 로그인 후에 글쓰기할 때 글쓰는 정보들이 들어간다. 
그래서 여러사이트 같은 데 id가 쓰이면 그걸 세션에 넣어두면 된다. </p>
<h3 id="쿠키-vs-세션">쿠키 vs 세션</h3>
<p>세션의 값은 오직 서버에만 저장.
세션은 쿠키 설정 여부와 관계없이 사용할 수 있다.
하지만 세션은 여러 서버에서 공유할 수 없다. </p>
<h3 id="세션-종료⭐⭐">세션 종료⭐⭐</h3>
<blockquote>
</blockquote>
<p><strong>session.invalidate() *<em>: 세션을 종료시키는 메서드.
*</em>세션객체가 없어진 게 아니다. invalidate()되면 속성에 접근할 수 없게 된다는 뜻이다.</strong> 
set,getAttribute()가 안된다. 
<strong>새로세션이 만들어지는 장소는 invalideate()한 페이지가 아니라 
그 다음 페이지에서 다음요청에서 새롭게 생성된다.</strong> 
그럼 새로운 세션이 생기는 다음페이지 가서 뭔가를 해야한다. 하지만, 다음 페이지에 얻을 세션을 미리 구할 수 있다.
<strong>request.getSession()</strong>를 쓰면 미리 할 수 있다. invalidate()와 같이 기억하기.</p>
<p>세션을 종료하면 현제 사용중엔 session 기본 객체를 삭제하고 session 기본 객체에 저장했던 속성 목록도 함께 삭제한다. </p>
<h4 id="closesessionjsp">closeSession.jsp</h4>
<p>sessionInfo를 열고 closeSession을 연 후에 sessionInfo를 열면 그 전의 세션이 종료되고, 새로운 세션이 생성되는 걸 볼 수 있다. </p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
   &lt;% 
   session.setAttribute(&quot;myName&quot;, &quot;myValue&quot;);
       out.println(&quot;before:&quot; + session.getId());

   session.invalidate(); //이걸 한다고 해서 세션이 사라지는 게 아니라는 얘기다. 
   //새로세션이 만들어지는 장소는 인벨리데이트페이지가 아니라 
   //그 다음 페이지에서 다음요청에서 새롭게 생성된다는 얘기다. 
      out.println(&quot;arter:&quot; + session.getId()); 
   // 세션객체가 없어진 게 아니다. 
      //인벨리디트 되면 속성에 접근할 수 없게 된다. get이 안되면 set도 안된다. 
      //다음페이지 가서 뭔가를 해야한다. 새로운 세션이 생기니까. 
    //다음 페이지에 얻을 세션을 미리 구할 수 있다.
      session = request.getSession();
    // 이아이를 쓰면 미리 할 수 있다. invalidate()와 같이 기억하기.

       out.println(&quot;&lt;br&gt;&quot;);
   out.println(session.getAttribute(&quot;myName&quot;));
   out.println(&quot;&lt;br&gt;&quot;);
   out.println(&quot;new :&quot; + session.getId());
   out.println(&quot;&lt;br&gt;&quot;);
   %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;세션종료&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
세션을 종료하였습니다.

&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="세션-유효-시간">세션 유효 시간</h3>
<p><strong>마지막 요청을 날린 거 기준(session.getLastAccessedTime())</strong>으로 시간을 잡고, 30분(예시)이 지나고 아무런 요청을 안하면 그냥 다른 곳에 갔다고 생각하고 그 전 사이트의 session을 삭제한다. 그러면 로그아웃되어있을 것이다. </p>
<blockquote>
</blockquote>
<p><strong>설정시간 2가지 방법</strong>
1.web.xml에서 <code>&lt;session-timout&gt;</code>태그를 사용 : 단위는 &#39;분&#39;단위
2.setMaxInactiveInterval() 메서드 사용 : 단위는 &#39;초&#39;단위</p>
<h3 id="requestgetsession⭐⭐⭐">request.getSession()⭐⭐⭐</h3>
<p>서블릿 나중에 할 때, service()안에는 request와 response가 기본객체로 있는데, 
이때 request에서 session객체를 구하고 session에서 application객체를 구할 수 있다.
<strong>request.getSession() 메서드는 session이 invalidate()되었느냐의 문제이다.
invalidate()되지 않았으면 session을 리턴하고, invalidate()되었으면 새롭게 session을 생성한다!</strong></p>
<p>getSession() = getSession(<strong>true</strong>)</p>
<hr>
<h2 id="세션을-사용한-로그인-상태-유지">세션을 사용한 로그인 상태 유지</h2>
<p>어제 쿠키대신 세션을 넣은 느낌.</p>
<h4 id="sessionloginjsp">sessionLogin.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%
    String id = request.getParameter(&quot;id&quot;);
    String password = request.getParameter(&quot;password&quot;);

    if(id.equals(password)){
        session.setAttribute(&quot;MEMBERID&quot;, id);

%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;로그인성공&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
로그인에 성공했습니다.
&lt;/body&gt;
&lt;/html&gt;
&lt;%
    }else {//로그인실패시
%&gt;
&lt;script&gt;
alert(&quot;로그인에 실패하였습니다.&quot;);
history.go(-1);
&lt;/script&gt;
&lt;%
    }
%&gt;
</code></pre><h4 id="sessionloginformjsp">sessionLoginForm.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;로그인폼&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form action=&quot;&lt;%= request.getContextPath() %&gt;/member/sessionLogin.jsp&quot; method=&quot;post&quot;&gt;
아이디&lt;input type=&quot;text&quot; name=&quot;id&quot; size=&quot;10&quot;/&gt;
암호 &lt;input type=&quot;password&quot; name=&quot;password&quot; size=&quot;10&quot;/&gt;
&lt;input type=&quot;submit&quot; value=&quot;로그인&quot;&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="sessionlogincheckjsp">sessionLoginCheck.jsp</h4>
<p>멤버id의 유무를 판단. 뭐인지 판단X.</p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%
    String memberId =(String)session.getAttribute(&quot;MEMBERID&quot;);
    boolean login = memberId == null ? false:true;
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;로그인 여부 검사&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
    if(login) {
%&gt;
    아이디&quot;&lt;%=memberId %&gt;&quot;로 로그인한 상태
&lt;%
    }else {
%&gt;
    로그인하지 않은 상태
&lt;%
    }
%&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/e9404f29-b10e-4a96-bf4b-dd96aa8dbab8/image.png" alt=""></p>
<h3 id="서블릿컨텍스트와-세션">서블릿컨텍스트와 세션</h3>
<p>논리적이든 물리적이든 분리가 되면 그냥 다른 것이다.
같은 application에서만 공유된다. 같은 서버에 있다고 해서 같은 세션이 아니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹(경로,쿠키)]]></title>
            <link>https://velog.io/@j_code/%EC%9B%B9%EA%B2%BD%EB%A1%9C%EC%BF%A0%ED%82%A4</link>
            <guid>https://velog.io/@j_code/%EC%9B%B9%EA%B2%BD%EB%A1%9C%EC%BF%A0%ED%82%A4</guid>
            <pubDate>Tue, 02 May 2023 12:13:08 GMT</pubDate>
            <description><![CDATA[<p>파라미터 접근하기 전에 request.setCharacterEncoding()을 해줘야 한다.
&lt;jsp:setProperty name=&quot;memberInfo&quot; property=&quot;*&quot;/&gt; 이거 하기 전에 위에 거를 해줘야 한다.</p>
<hr>
<h3 id="책에는-안나온다">&lt;책에는 안나온다&gt;</h3>
<p><img src="https://velog.velcdn.com/images/j_code/post/32475ca4-03f6-4b99-a130-641793367bd4/image.png" alt="">
a태그가 말하는 루트 : <a href="http://localhost:8080/">http://localhost:8080/</a>
form 루트 :<a href="http://localhost:8080/">http://localhost:8080/</a>
sendRedirect : <a href="http://localhost:8080/">http://localhost:8080/</a>
forward 루트 : <a href="http://localhost:8080/05.02/whatIsRoot.jsp">http://localhost:8080/05.02/whatIsRoot.jsp</a></p>
<p>루트의 기준은 다르다. 어디에서 뭘 쓰느냐에 따라서.
경로가 복잡해질 때, 거기서 어딘가로 찾아가야할 때. 
다른 위치에서 submit해야하는 경우, a태그로 다른 위치로 가야하는 경우, redirect를 이용해서 다른 위차가 가야하는 경우 등등 
-&gt; 절대경로를 사용하는데, <a href="http://localhost:8080/%EB%A1%9C">http://localhost:8080/로</a> 하지말고, 
💡<strong>따라서, getContextPath()를 쓴 다음에 경로를 써주기.</strong></p>
<p><img src="https://velog.velcdn.com/images/j_code/post/80d05f6c-73ea-499a-8552-5725b718ae67/image.png" alt=""></p>
<p>이러면 정상적으로 나온다.
<img src="https://velog.velcdn.com/images/j_code/post/14800efe-1dcc-4834-a023-2de83595cbcf/image.png" alt=""></p>
<p>jsp에서는 if,for문을 잘 사용하지 않고 나중에 배울 태그로 처리를한다.
*<em>jsp에서는 값을 꺼내오는 건 되지만, 값을 바꾸거나 만들거나 하면 안된다. *</em></p>
<hr>
<h2 id="클라이언트와의-대화1쿠키">클라이언트와의 대화1(쿠키)</h2>
<p>클라이언트의 상태정보를 저장하기 위해 2가지
1.클라이언트와의 대화1 : 크게 중요X - 쿠키. 클라이언트에서 상태를 관리. 보조적인 수단이어야한다. 
2.대화2가 중요. - 서버에서 상태를 관리하는 거.</p>
<p>&lt;대화1&gt;
<strong>쿠키</strong>는 웹 브라우저가 보관하는 데이터.
&#39;아이디 기억하기 기능&#39;은 쿠키 이용의 대표 기술이다.
클라이언트에서 생성한 쿠키는 의미가 없다. 상태 정의를 클라이언트가 내릴 수 없다.
클라이언트가 서버한테 뭔가를 할때 계속 이름을 말하게 하면 서버가 클라이언트를 기억할 필요가 없다.
쿠키는 출력버퍼처럼 <strong>헤더</strong>에 담겨서 넘어간다.
웹브라우저는 저장한 쿠키를 요청이 있을 때마다 웹 서버에 전송한다.</p>
<p>모든 브라우저에는 쿠키를 허용하지 않는 옵션이 있다. 
서버측에서는 &#39;냥냥야 이름 말해&#39;라고 하지만, 클라이언트에서 쿠키를 사용하지 않으면 반응이 없을 것이다.
일반적으로 쿠키로 하는 것이 다 되지만 안되는 사람 한두명이 있을 수 있어서 안쓰는 게 좋다. 
그래서 <strong>보조적인 수단</strong>으로 사용한다. 
<strong>쿠키에서는 이름과 값이 중요</strong>. 파라미터와 비슷한 느낌.
이름과 값은 문자열이다. <strong>이름이 식별자</strong>. 알파벳과 숫자만사용.
쿠키값은 서버가 만드는데, 여기에 한글을 넣는다는 발상자체가 잘못된거다. 한글넣는거 아니다.</p>
<h3 id="쿠키-생성하기">쿠키 생성하기</h3>
<p>jsp에서 쿠키 만들 일 없다. 쿠키를 생성할 때에는 Cookie 클래스를 사용한다.
addCookie() 사용하면 응답헤더에 쿠키값이 자동으로 들어간다.</p>
<blockquote>
</blockquote>
<p>Cookie cookie = new Cookie(&quot;cookieName&quot;, &quot;cookieValue&quot;);
<strong>response.addCookie(cookie);</strong></p>
<p>이름하나 값하나만 있다. 속성같은 거 아니다. </p>
<blockquote>
<p><strong>cookie.getName()/cookie.getValue()</strong>    - 중요한 메서드다!!!!
<strong>쿠키에서 값을 뽑아내는 메서드.</strong>
여러개 잡아넣을거면 쿠키를 여러개 만들어야 한다. </p>
</blockquote>
<h4 id="makecookiejsp">makeCookie.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@page import=&quot;java.net.URLEncoder&quot; %&gt;
&lt;%
Cookie cookie = new Cookie(&quot;name&quot;, &quot;myValue&quot;);
response.addCookie(cookie);    
//이것만 해주면 됩니당. 이름과 벨류를 가진 쿠키가 헤더에 추가가 된다. 
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;쿠키생성&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%=cookie.getName() %&gt; 쿠키의 값=&quot;&lt;%= cookie.getValue() %&gt;&quot;    
//중요한 메서드다!!!!
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/95910d4f-1571-4053-9003-5845895c8bd0/image.png" alt=""></p>
<h3 id="쿠키-값-읽어오기">쿠키 값 읽어오기</h3>
<blockquote>
<p>Cookie[] cookie = request.getCookies(); 
쿠키 값을 뽑아온다. 쿠키의 값을 읽어올 수 있는 메서드
위의 makeCookie를 읽어 올 수 있다</p>
</blockquote>
<h4 id="viewcookiejsp">viewCookie.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import =&quot;java.net.URLDecoder&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;쿠키 목록&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
쿠키목록&lt;br&gt;
&lt;% Cookie[] cookies = request.getCookies();    
//브라우저 알아서 헤더에 쿠키정보를 심어준다. 
    if(cookies != null &amp;&amp; cookies.length &gt;0) {    //쿠키가 있냐
        for(int i =0; i&lt;cookies.length; i++) {
%&gt;
    &lt;%=cookies[i].getName() %&gt; = &lt;%=cookies[i].getValue()%&gt;&lt;br&gt;

&lt;%
        }
    }else {
%&gt;
쿠키가 아직 존재하지 않습니다.
&lt;%
    }
%&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/7cf0cbda-bcc9-477c-9b07-0509fc6487ea/image.png" alt=""></p>
<h3 id="쿠키-값-변경-및-쿠키-삭제하기">쿠키 값 변경 및 쿠키 삭제하기</h3>
<p>&lt;변경&gt;
쿠키 심기 전에 setValue()할 수 있는 거고, 
보낸 후에 쿠키를 변경하려면, name은 똑같이 맞춰주고 -&gt; vlaue값을 바꿔주기.
기존의 것을 바꾸는 게 아니라 걍 덮어씌우는거.
쿠키가 여러개일 때 내가 원하는 어떤 것인지 식별을 해야하는데, 그때 이름으로 구분한다. 
이름이 식별자라서, 이름이 같은 게 있으면 덮어씌운다. value는 중복되든 말든 상관없다.
만약 이름이 mimi를 변경하기 위해서는 아래와 같이 적으면 된다.</p>
<blockquote>
</blockquote>
<p>Cookie cookie = new Cookie(&quot;mimi&quot;, &quot;새로운 값&quot;);
response.addCookie(cookie);</p>
<p>&lt;삭제&gt;</p>
<blockquote>
</blockquote>
<p>cookie.setMaxAge(0); : 당장 쿠키를 삭제해준다.
cookie.setMaxAge(-1); : 브라우저가 종료되면 쿠키가 삭제된다.</p>
<h3 id="쿠키의-도메인">쿠키의 도메인</h3>
<p><strong>기본적으로 쿠키는 그 쿠키를 생성한 서버에만 전송된다.</strong>
네이버에서 만든 걸 카카오에서 보낼 리가 없다. </p>
<blockquote>
<p>같은 도메인을 사용하는 모든 서버에 쿠키를 보내야 할 때
setDomain() 을 사용한다.</p>
</blockquote>
<p>2가지 형식으로 도메인을 지정할 수 있다.
1,-.글자 같은 걸로 시작하면 글자 거기만 보낸다.(main.naver.com)
&#39;.&#39;으로 시작하면 .으로 시작하는 거 다 보낸다.(.naver.com)</p>
<p>setDomain()의 값으로 
1.나랑 같은 거(현재 서버의 도메인).
2.상위도메인만 보낼 수 있다.
서버에서 &#39;javacan.naver.com&#39;을 jsp파일에 보내게 된다면.
c1 : .naver.com(o)
c2 : javacan.naver.com(o)
c3 : javacan.tistory.come(X)-전송은 되지만 브라우저에서 저장하지 않는 것.</p>
<p>쿠키 도메인이 쿠키를 생성한 서버의 도메인을 벗어나면 웹 브라우저는 쿠키를 저장하지 않는다.</p>
<p>cf) <a href="http://www.somehost.com%EB%9E%91">www.somehost.com랑</a> .somehost.com이랑 다른 거다.</p>
<p>세션은 브라우저를 켜고 끌때까지인데 그걸 어떻게 알 수 잇을까?
이 클라이언트를 담당하는 세션이 누구냐를 &#39;쿠키&#39;를 이용해서 안다.</p>
<h3 id="쿠키의-경로">쿠키의 경로</h3>
<p>그냥 루트로 걍 경로 보내면 된다. 브라우저가 알아서 분류해서 처리함.
중요X.
setDomain()으로 내가 경로를 지정할 수 있구나 정도만 알기. 
어차피 루트사용할거라서..뭐... 지정안해도 된다.
<strong>경로를 따로 지정해주지 않으면 현재경로로 인식.</strong></p>
<h3 id="쿠키의-유효시간">쿠키의 유효시간</h3>
<p>쿠키는 유효시간을 갖는다. 
유효시간 지정 안하면 브라우저 종료할 때 쿠키 삭제된다.(-1을 주는 것과 같은 효과) </p>
<blockquote>
<p>setMaxAge() :유효기간 지정 메서드. 초단위.</p>
</blockquote>
<h3 id="쿠키와-헤더">쿠키와 헤더</h3>
<p>response.addCookie()로 쿠키를 추가하면 헤더에 들어간다. 
WAS가 알아서 해준다. 우리는 걍 add해주면 된다.</p>
<h2 id="쿠키를-사용한-로그인-상태-유지">쿠키를 사용한 로그인 상태 유지</h2>
<p>여기서 Cookies.java는 쿠키를 편하게 만들 수 있는 util같은 것이다. </p>
<h4 id="cookiesjava">Cookies.java</h4>
<pre><code>package util;

import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

public class Cookies {
    private Map&lt;String,Cookie&gt; cookieMap = new java.util.HashMap&lt;String, Cookie&gt;();

    public Cookies(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if(cookies != null) {
            for(int i=0; i&lt;cookies.length ;i++) {
                cookieMap.put(cookies[i].getName(), cookies[i]);
                //쿠키객체 자체를 value값으로 넣는다. 
            }
        }
    }

    public Cookie getCookie(String name) {
        return cookieMap.get(name);
    }
    public String getValue(String name) throws IOException {
        Cookie cookie = cookieMap.get(name);
        if(cookie == null) {
            return null;
        }
        return URLDecoder.decode(cookie.getValue(), &quot;euc-kr&quot;);
    }

    public boolean exists(String name) {
        return cookieMap.get(name) != null;
    }

    public static Cookie createCookie(String name, String value) throws IOException {
        return new Cookie(name, URLEncoder.encode(value, &quot;euc-kr&quot;));

    }

    public static Cookie createCookie(String name, String value, String path, int maxAge) throws IOException {
        Cookie cookie = new Cookie(name, URLEncoder.encode(value, &quot;euc-kr&quot;));
        // 이 부분 대신에 바로 위의 createCookie()를 쓰는 게 더 좋은 방법이다.
        cookie.setPath(path);
        cookie.setMaxAge(maxAge);
        return cookie;
    }
    public static Cookie createCookie(String name, String value, String domain, String path, int maxAge) throws IOException {
        Cookie cookie = new Cookie(name, URLEncoder.encode(value, &quot;euc-kr&quot;));
        cookie.setDomain(domain);
        cookie.setPath(path);
        cookie.setMaxAge(maxAge);
        return cookie;
    }

}
</code></pre><h4 id="loginformjsp">loginForm.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;로그인폼&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;form action=&quot;&lt;%= request.getContextPath() %&gt;/member/login.jsp&quot; 
    method=&quot;post&quot;&gt;
아이디 &lt;input type=&quot;text&quot; name=&quot;id&quot; size=&quot;10&quot;&gt;
암호 &lt;input type=&quot;password&quot; name=&quot;password&quot; size=&quot;10&quot;&gt;
&lt;input type=&quot;submit&quot; value=&quot;로그인&quot;&gt;    
&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="loginjsp">login.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import = &quot;util.Cookies&quot; %&gt;
&lt;%
    String id = request.getParameter(&quot;id&quot;);
    String password = request.getParameter(&quot;password&quot;);

    if(id.equals(password)) {
        //ID와 암호가 같으면 로그인에 성공한 것으로 판단.
        response.addCookie(
            Cookies.createCookie(&quot;AUTH&quot;, id, &quot;/&quot;, -1)
            //maxAge(0)넣으면 지워진다. 당장지우는거. 
            //-1은 브라우저 끌 때 지우는거. 
        );

%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;로그인 성공&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
로그인에 성공했습니다.
&lt;/body&gt;
&lt;/html&gt;
&lt;%
    } else {//로그인 실패시
%&gt;
&lt;script&gt;
alert(&quot;로그인에 실패하였습니다.&quot;);
//경고창 띄우기
history.go(-1);
// 이전 페이지로 이동하는 JavaScript 함수
&lt;/script&gt;
&lt;%
    }
%&gt;</code></pre><h4 id="logincheckjsp">loginCheck.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import = &quot;util.Cookies&quot; %&gt;
&lt;%
    Cookies cookies = new Cookies(request);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;로그인 여부 검사&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;%
        if(cookies.exists(&quot;AUTH&quot;)) {
    %&gt;
    아이디 &quot;&lt;%= cookies.getValue(&quot;AUTH&quot;) %&gt;&quot;로 로그인 한 상태
    &lt;%
        }else {
    %&gt;
    로그인하지 않은 상태
    &lt;%
        }
    %&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="logoutjsp">logout.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import = &quot;util.Cookies&quot; %&gt;
&lt;%
    response.addCookie(
        Cookies.createCookie(&quot;AUTH&quot;,&quot;&quot;,&quot;/&quot;,0)        
    );
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;로그아웃&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
로그아웃하셨습니다.
&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[웹(forward, 자바빈)]]></title>
            <link>https://velog.io/@j_code/%EC%9B%B9forward-%EC%9E%90%EB%B0%94%EB%B9%88</link>
            <guid>https://velog.io/@j_code/%EC%9B%B9forward-%EC%9E%90%EB%B0%94%EB%B9%88</guid>
            <pubDate>Mon, 01 May 2023 13:46:23 GMT</pubDate>
            <description><![CDATA[<h2 id="jspforward-액션태그"><a href="jsp:forward">jsp:forward</a> 액션태그</h2>
<ul>
<li><p>리다이렉트와 비슷하다.
리다이렉트와 동일한 부분 : 요청한 거랑 다른 거 보여준다.
그러나 동작하는 방식이 다르다. </p>
</li>
<li><p>라다이렉트는 from.jsp에게 응답보내고,다시 요청하는 방식.
forward는 from.jsp에 대한 응답이 나가지 않는다.
to.jsp의 존재여부를 알 수 없다. 
요청 흐름이 이동할 때 from.jsp에서 사용한 request 기본 객체와 response 기본객체가 to.jsp로 전달된다.
리다이렉트는 다시 돌아오는데, forward는 안돌아온다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/j_code/post/7a663738-05bc-481c-9180-4cb314c94d7f/image.jpg" alt=""></p>
<ul>
<li><a href="jsp:forward">jsp:forward</a>액션 태그를 사용하면 &#39;각 조건을 처리하는 jsp&#39;를 분리하여 기능별로 모듈화할 수 있게 된다.</li>
</ul>
<blockquote>
<p>사용법 : &lt;jsp:forward page=&quot;이동할 페이지&quot;/&gt;</p>
</blockquote>
<ul>
<li><p>from.jsp에서 forward를 사용하여 to.jsp를 생성한다면, 웹브라우저 주소는 from.jsp 그대로인다. 라다이렉트처럼 to.jsp로 변경되지 않는다. 웹 컨테이너 내에서 요청 흐름을 이동시키기 때문에, 웹 브라우저는 다른 jsp가 요청을 처리했다는 사실을 알지 못한다.(요청한 페이지를 숨기는 게 목적이다.)</p>
</li>
<li><p>표현식 사용할 수 있다. - forward액션태그에 이동할 페이지를 표현식으로 쓸 수 있다.</p>
</li>
<li><p>라다이렉트보다 포워드가 경제적임. 왔다갔다 하는 것도 낭비라서!</p>
</li>
</ul>
<h3 id="forward-액션태그와-출력-버퍼와의-관계">forward 액션태그와 출력 버퍼와의 관계</h3>
<ul>
<li>리다이렉트는 밑에 적힌 코드까지 다 읽는데 반해서</li>
<li>forward는 포워드 밑의 코드는 읽지않고, 실행 되지 않는다.(버퍼 비우고 걍 딴 데로 간다.)
<a href="jsp:forward">jsp:forward</a> 액션 태그가 올바르게 작동하려면, 이 액션태그를 실행하기 전에 웹 브라우저에 데이터가 전송되면 안된다.=&gt; error페이지랑 똑같다. 정상적으로 나오다가 갑자기 중간에 error문구가 뜨게 된다. 
그래서!! forward 되기 전에 flush 되는 걸 막아야 한다.
그렇게 하려면, 출력버퍼를 늘려놔야한다. 적어도 forward까지 버퍼에 담길 수 있도록 버퍼의 크기를 늘려놔야 한다. </li>
</ul>
<hr>
<h3 id="forward를-사용하는-예시">forward를 사용하는 예시</h3>
<p>조건에 따라서 다른 페이지로 이동하는 구조를 갖는데, forward액션태그를 사용해서 해당 페이지로 이동한다. 근데 만약 forward액션태그를 사용하지 않을 경우, HTML과 스크립트 등이 섞여서 식이 엄청 복잡해진다. 그리고 새로운 조건을 추가할 경우, else-if문 블럭 하나만 추가하면 돼서 forward구문을 쓰는 게 더 간단한 방법이다.</p>
<ul>
<li>&lt;%=request.getContextPath()%&gt;/view.jsp&quot;&gt;    : 많이 쓰이는 표현이다!!! 이렇게 쓰면 실수가 없음. rootContext를 꺼내오는 메서드.</li>
<li>결국 viwe.jsp 페이지의 url이 남는다. 그게 &quot;view&quot;라고 이름 지은 이유이다. </li>
</ul>
<h4 id="select">select</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;옵션화면 선택&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form action= &quot;&lt;%=request.getContextPath()%&gt;/view.jsp&quot;&gt;    
//많이 쓰이는 표현이다!!! 이렇게 쓰면 실수가 없음. 
rootContext를 꺼내오는 메서드.

보고싶은 페이지 선택:
&lt;select name=&quot;code&quot;&gt;
    &lt;option value=&quot;A&quot;&gt;A페이지&lt;/option&gt;
    &lt;option value=&quot;B&quot;&gt;B페이지&lt;/option&gt;
    &lt;option value=&quot;C&quot;&gt;C페이지&lt;/option&gt;
&lt;/select&gt;
&lt;input type=&quot;submit&quot; value=&quot;이동&quot;&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="viewjsp">view.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;% String code = request.getParameter(&quot;code&quot;);
String viewPageURI = null;
if(code.equals(&quot;A&quot;)) {
    viewPageURI = &quot;/viewModule/a.jsp&quot;;
}else if(code.equals(&quot;B&quot;)){
    viewPageURI = &quot;/viewModule/b.jsp&quot;;
}else if(code.equals(&quot;C&quot;)){
    viewPageURI = &quot;/viewModule/c.jsp&quot;;
}
%&gt;
&lt;jsp:forward page=&quot;&lt;%=viewPageURI %&gt;&quot;/&gt;</code></pre><h4 id="viewmoduleajspbjspcjsp">viewModule\a.jsp,b.jsp,c.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
이 페이지는 &lt;b&gt;&lt;font size=&quot;5&quot;&gt;A&lt;/font&gt;&lt;/b&gt; 입니다.
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/741f557c-38bc-4790-9e1c-e1609803ecf8/image.png" alt=""></p>
<h3 id="책에는-안나오는-내용web-inf">책에는 안나오는 내용(WEB-INF)</h3>
<p><code>WEB-INF</code>에서 만든 파일은 외부에서 읽지를 못한다.
근데 만들때가 있다.
클라이언트가 다이렉트로 요청할 수는 없지만, forward로 WEB-INF안에 있는 파일을 불러올 수 있다. 
외부에서는 못들어오는데, 내가 거기로 forward하고 있는 페이지를 거쳐야지만 안의 폴더의 파일을 가져올 수 있다. 
서버 내부에서 이동하는 것은 된다. 외부에서는 요청할 수 없다.
url은 WEB-INF안의 파일말고 요청한 페이지가 보인다.</p>
<h3 id="jspparam액션태그를-이용할-수-있다"><a href="jsp:param">jsp:param</a>액션태그를 이용할 수 있다.</h3>
<p><a href="jsp:param">jsp:param</a>액션 태그를 사용하면 <a href="jsp:forward">jsp:forward</a> 액션 태그로 이동할 페이지에 파라미터를 추가로 전달할 수 있다. </p>
<hr>
<h3 id="jspinclude와-jspforward의-page경로"><a href="jsp:include">jsp:include</a>와 <a href="jsp:forward">jsp:forward</a>의 page경로</h3>
<p>2가지 방식으로 입력할 수 있다.</p>
<ol>
<li>루트에서 출발(웹 어플리케이션 폴더를 기준으로 함) : <strong>절대경로</strong> - 일반적으로 좋다
 : &#39;/&#39;로 시작하면 절대경로가 된다.</li>
<li>현재위치에서 출발  : <strong>상대경로</strong> </li>
</ol>
<ul>
<li>이동할 페이지가 같은 폴더에 있거나, 내가 현재위치하고 있는 바로 밑의 하위폴더에 있는 경우에 상대경로를 쓴다.
  :&#39;..&#39; 한칸 위로 라는 뜻.</li>
</ul>
<p>경로를 보고 절대경로와 상대경로가 무엇인지를 알아야 한다.</p>
<hr>
<h3 id="기본-객체의-속성을-이용해서-값-전달하기">기본 객체의 &#39;속성&#39;을 이용해서 값 전달하기</h3>
<p><a href="jsp:param">jsp:param</a>액션 태그를 이용해서 &#39;파라미터&#39;를 전달하면, String밖에 못넣는데 반해 &#39;속성&#39;은 객체를 넣을 수 있다.
속성을 지원하는 객체 : 4개(pageContext,request,session,application)</p>
<ul>
<li>아래는 request 기본 객체에 속성을 추가한 후에 forward 액션태그를 이용해서 흐름을 이동시키는 예제 코드.</li>
<li>makeTime.jsp를 실행하면 현재 시간을 담고 있는 Calender객체를 생성해서 request 기본 객체의 time속성에 저장한 후 viewTime.jsp로 이동한다. </li>
<li>viewTime.jsp는 request 기본 객체에 저장된 속성인 time 값을 읽어온다. <h4 id="maketimejsp">makeTime.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
  pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import =&quot;java.util.Calendar&quot; %&gt;
&lt;% 
Calendar cal = Calendar.getInstance();
request.setAttribute(&quot;time&quot;, cal);
%&gt;
&lt;jsp:forward page=&quot;/to/viewTime.jsp&quot;/&gt;
</code></pre></li>
</ul>
<pre><code>
#### viewTime.jsp</code></pre><p>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import = &quot;java.util.Calendar&quot; %&gt;
<!DOCTYPE html></p>
<html>
<head>
<meta charset="EUC-KR">
<title>현재시간</title>
</head>
<body>
<%
Calendar cal = (Calendar)request.getAttribute("time");
%>
현재시간은 <%=cal.get(Calendar.HOUR) %>시
<%=cal.get(Calendar.MINUTE) %>분
<%=cal.get(Calendar.SECOND) %>초
</body>
</html>
```
<결과>
![](https://velog.velcdn.com/images/j_code/post/72ffe2b8-66c2-4abc-bc05-8df542d81c35/image.png)

<hr>
<h1 id="자바빈-jspusebean-액션태그">자바빈, <a href="jsp:useBean">jsp:useBean</a> 액션태그</h1>
<p>자바빈 : 속성, 변경 이벤트, 객체 직렬화를 위한 표준이다. 이 중에서 jsp에서는 &#39;속성&#39;을 표현하기 위한 용도로 사용된다.</p>
<p>기본생성자 무조건 있어야 한다.-&gt; 자바빈 규약에 보면 기본생성자가 있어야 한다고 적혀있다.</p>
<h2 id="자바빈-프로퍼티">자바빈 프로퍼티</h2>
<p>프로퍼티 : 자바빈에 저장되는 값.</p>
<ul>
<li>d : 컴파일 결과로 생성될 클래스 파일의 위치를 지정한다. 실제 클래스 파일은 패키지에 맞는 폴더에 생성된다. </li>
</ul>
<h2 id="jspusebean-태그를-이용한-자바-객체-사용"><a href="jsp:useBean">jsp:useBean</a> 태그를 이용한 자바 객체 사용</h2>
<blockquote>
<p>기본 구분 : &lt;jsp:useBeam id=&quot;[빈이름]&quot; class=&quot;[자바빈클래스이름]&quot; scope=&quot;[범위]&quot; /&gt;
속성</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/j_code/post/084c8061-1b50-44c7-90ff-6e420029b44f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/j_code/post/0062be94-f184-4c44-b976-5d6c0a34ae92/image.jpg" alt=""></p>
<p>위의 사진 설명
scope로 정의된 객체안에서 id를 뽑아줘 근데 그 객체는 class에 나오는 객체일거야... 이런 뜻인듯..
저기 안에서 부르는게 기본생성자라서 bean에 기본생성자 꼭 만들어 줘야 한다.</p>
<ul>
<li>id : jsp페이지에서 자바빈 객체에 접근할 때 사용할 이름을 지정.</li>
<li>class : 패키지이름을 포함한 자바빈 클래스의 완전한 이름을 입력. - 없으면 새로 만들어줌</li>
<li>scope : 자바빈 객체를 저장할 영역을 지정. </li>
<li>class속성 대신 type속성을 사용할 수도 있다. 
class는 없으면 새로 만들어줌.
type를 쓰면 꺼내기만 하고 없으면 에러를 발생시킨다.
&lt;jsp:useBeam id=&quot;member&quot; type=&quot;chap08.member.MemberInfo&quot; scope=&quot;request&quot;/&gt;</li>
</ul>
<h4 id="makeobjectjsp">makeObject.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;jsp:useBean id=&quot;member&quot; scope=&quot;request&quot; class=&quot;chap08.member.MemberInfo&quot;/&gt;

&lt;%
member.setId(&quot;madvirus&quot;);
member.setName(&quot;최범균&quot;);
%&gt;
&lt;jsp:setProperty name=&quot;member&quot; property=&quot;id&quot; value=&quot;madvirus&quot;/&gt;
&lt;jsp:setProperty name=&quot;member&quot; property=&quot;name&quot; value=&quot;최범균&quot;/&gt;
&lt;jsp:forward page=&quot;/useObject.jsp&quot;&gt;&lt;/jsp:forward&gt;
</code></pre><h4 id="useobjectjsp">useObject.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;jsp:useBean id=&quot;member&quot; scope=&quot;request&quot; class =&quot;chap08.member.MemberInfo&quot;&gt;&lt;/jsp:useBean&gt;
&lt;%--
    MemberInfo member = (MemberInfo)request.getAttribute(&quot;member&quot;);
    if(member== null) {
        member = new MemberInfo();
        request.setAttribute(&quot;member&quot;, member);
    }
 --%&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;인사말&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%=member.getName() %&gt;(&lt;%=member.getId()%&gt;)회원님
안녕하쇼.
&lt;/body&gt;
&lt;/html&gt;</code></pre><h2 id="jspsetpropertyjspgetproperty"><a href="jsp:setProperty">jsp:setProperty</a>,<a href="jsp:getProperty">jsp:getProperty</a></h2>
<p><a href="jsp:setProperty">jsp:setProperty</a>,<a href="jsp:getProperty">jsp:getProperty</a> -&gt;setter,getter의미.</p>
<h3 id="jspsetproperty"><a href="jsp:setProperty">jsp:setProperty</a></h3>
<blockquote>
</blockquote>
<ul>
<li>&lt;jsp:setProperty name=&quot;[자바빈]&quot; property=&quot;이름&quot; value=&quot;[값]&quot;/&gt;</li>
<li><a href="jsp:setProperty">jsp:setProperty</a>의 속성
• name : 프로퍼티의 값을 변경할 자바빈 객체의 이름을 지정한다. (isp:useBean) 액션 태 그의 서 속성에서 지정한 값을 사용한다.
• property : 값을 지정할 프로퍼티의 이름을 지정한다.
• value : 프로퍼티의 값을 지정한다. 표현식((%- 값 96)이나 EL(S값)을 사용할 수 있다.
EL에 대한 내용은 11장에서 배운다.</li>
</ul>
<p>왜 굳이 이렇게 쓸까? 태그처럼 보이게 하려고.
<code>member.setId(&quot;madvirus&quot;)</code> = <code>&lt;jsp:setProperty name=&quot;member&quot; property=&quot;id&quot; vlaue=&quot;madvirus&quot;/&gt;</code> 같은 것을 뜻한다.</p>
<h3 id="vaue대신-param사용할수-있다">vaue대신 param사용할수 있다.</h3>
<h4 id="param속성--param-속성과-value속성은-함께-사용할-수-없다">&lt;param속성&gt; : param 속성과 value속성은 함께 사용할 수 없다.</h4>
<p>&lt;jsp:setProperty name=&quot;member&quot; property=&quot;id&quot; param=&quot;userId&quot;/&gt;
파라미터 중에 userid를 꺼내와서 멤버변수 id에 넣으라는 뜻.
(String id = request.getParameter(&quot;userId&quot;);
member.setId(id);) 와 같은 말이 된다.</p>
<h4 id="property속성">&lt;property속성&gt;</h4>
<p>파라미터 중에 객체에 멤버변수 이름 같은 게 있으면 걍 알아서 넣어주라는 뜻. 
&lt;jsp:setProperty name=&quot;member&quot; property=&quot;*&quot;/&gt;
이렇게 적으면 알아서 해준다. 
코드가 많을 수록 이렇게 쓰면 큰 효과를 본다.</p>
<p>조건은 멤버변수와 파라미터하고 이름이 같아야 한다는 것이다. </p>
<h3 id="jspgetproperty"><a href="jsp:getProperty">jsp:getProperty</a></h3>
<p>: 자바빈 객체의 프로퍼티 값을 출력할 때 사용.
표현식 대신에 &#39;getProperty&#39;를 사용할 수 있다. 
• name : (ispiuseBean)의 id 속성에서 지정한 자바빈 객체의 이름을 지정한다.
• property : 출력할 프로퍼티의 이름을 지정한다.</p>
<p>&lt;%=member.getName() %&gt;(&lt;%=member.getId()%&gt;)을 
&lt;jsp:getProperty name=&quot;member&quot; property=&quot;id&quot;/&gt; 로 바꿀 수 있다. 
태그처럼 표현식 대신에 쓰는 것이다.
스크립트릿 같은 거 대신에 태그처럼 표현할 수 있어야 한다. </p>
<h3 id="자바빈-프로퍼티-타입에-따른-값-매핑">&lt;자바빈 프로퍼티 타입에 따른 값 매핑&gt;</h3>
<p>문자열이어도 setproperty쓰면 알아서 int로 바꿔준다.</p>
<h3 id="usebean액션태그">&lt;useBean액션태그&gt;</h3>
<p>계산이나 연산하는 게 jsp에서 안나오고, java에서 처리될거라는 뜻.
그냥 useBean은 유용할 수 있다. get,setproperty는 잘 안쓰인다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[include 예시 - guestBook]]></title>
            <link>https://velog.io/@j_code/include-%EC%98%88%EC%8B%9C-guestBook</link>
            <guid>https://velog.io/@j_code/include-%EC%98%88%EC%8B%9C-guestBook</guid>
            <pubDate>Sat, 29 Apr 2023 05:45:34 GMT</pubDate>
            <description><![CDATA[<p>설명 : getList.jsp는 게시글 목록을 보여주기 위해 사용되며, save.jsp는 새로운 게시글을 작성하여 게시글 목록에 추가하기 위해 사용.</p>
<h4 id="webxml">web.xml</h4>
<pre><code>&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://java.sun.com/xml/ns/javaee&quot; xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot; id=&quot;WebApp_ID&quot; version=&quot;2.5&quot;&gt;
  &lt;display-name&gt;guestBook&lt;/display-name&gt;
  &lt;welcome-file-list&gt;
    &lt;welcome-file&gt;template.jsp&lt;/welcome-file&gt;
  &lt;/welcome-file-list&gt;
&lt;/web-app&gt;</code></pre><h4 id="getlistjspf">getList.jspf</h4>
<p>설명 :</p>
<ul>
<li>서버의 어플리케이션의 공용 데이터인 &#39;articleList&#39;.</li>
<li>이 코드는 애플리케이션이 시작될 때마다 articleList가 생성되며, application 객체에 저장됩니다. </li>
<li>application 객체의 articleList 속성에 list 객체를 저장하고 있으므로, list 객체는 해당 웹 애플리케이션의 어느 페이지에서든 접근하여 사용할 수 있습니다.<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
  pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import = &quot;java.util.*&quot;%&gt;
&lt;%@ page import =&quot;org.doo.Article&quot; %&gt;


</code></pre></li>
</ul>
<p>&lt;%</p>
<pre><code>Vector&lt;Article&gt; list = (Vector&lt;Article&gt;) application.getAttribute(&quot;articleList&quot;);
if(list == null) {
    list = new Vector&lt;Article&gt;();
    application.setAttribute(&quot;articleList&quot;, list);

}</code></pre><p>%&gt;</p>
<pre><code>#### write.jsp</code></pre><p>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;</p>
<form action ="save.jsp" method="post">
    <table>
        <tr>
            <th>작성자</th>
            <td><input type="text" name="writer"/></td>
        </tr>
        <tr>
            <th>내용</th>
            <td><input type="text" name="comment"/></td>
        </tr>
        <tr>
            <th colspan="2">
            <input type="submit" value="등록"/>
            </th>
        </tr>

<pre><code>&lt;/table&gt;</code></pre></form>
```

<h4 id="listjsp">list.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ include file =&quot;getList.jspf&quot; %&gt;

&lt;table id=&quot;listTable&quot;&gt;
    &lt;tr id = &quot;header&quot;&gt;
        &lt;th id=&quot;num&quot;&gt;번호&lt;/th&gt;
        &lt;th id=&quot;comment&quot;&gt;내용&lt;/th&gt;
        &lt;th id=&quot;writer&quot;&gt;작성자&lt;/th&gt;
        &lt;th id=&quot;date&quot;&gt;작성일&lt;/th&gt;

    &lt;/tr&gt;
    &lt;%
        if(list.size() ==0){
    %&gt;
        &lt;tr&gt;
            &lt;th colspan =&quot;4&quot;&gt;
                empty
            &lt;/th&gt;
        &lt;/tr&gt;


    &lt;%
        }
        for(int i = list.size()-1; i&gt;=0; i--) {
            Article temp = list.get(i);

    %&gt;
        &lt;tr&gt;
            &lt;th&gt;&lt;%=i+1 %&gt;&lt;/th&gt;
            &lt;th&gt;&lt;%= temp.getComment() %&gt;&lt;/th&gt;
            &lt;th&gt;&lt;%= temp.getWriter() %&gt;&lt;/th&gt;
            &lt;th&gt;&lt;%= temp.getDate() %&gt;&lt;/th&gt;

        &lt;/tr&gt;
    &lt;%
        }
    %&gt;

&lt;/table&gt;</code></pre><h4 id="savejsp">save.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ include file=&quot;getList.jspf&quot; %&gt;

&lt;%
    request.setCharacterEncoding(&quot;euc-kr&quot;);

    String writer = request.getParameter(&quot;writer&quot;);
    String comment = request.getParameter(&quot;comment&quot;);
    Article article = new Article(writer, comment);

    list.add(article);
    response.sendRedirect(request.getContextPath());

%&gt;</code></pre><h4 id="cssmaincss">css/main.css</h4>
<pre><code>@charset &quot;EUC-KR&quot;;

body {
    text-align: center;
}
#mainBox {
    margin: 0 auto;
    border: 2px solid gray;
    width: 80%;
}
tabel {
    margin: 0 auto;
}
#listTable {
    width: 80%;
    border: 1px solid gray;
    border-collapse: collapse;
}
#listTable td, #listTable th {
    border: 1px solid gray;
}
#header {
    background-color: #EEEEEE;
}
#num {
    width: 10%
}
#comment {
    width: 50%
}
#writer {
    width: 20%
}
#top, #bottom {
    margin: 1em;
}
input[type=&quot;text&quot;] {
    width: 20em;
}</code></pre><h4 id="templatejsp">template.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;guestBook&lt;/title&gt;
&lt;link href=&quot;css/main.css&quot; rel=&quot;stylesheet&quot; /&gt;
&lt;/head&gt;

&lt;body&gt;
&lt;h1&gt;Guest Book&lt;/h1&gt;
&lt;div id=&quot;mainBox&quot;&gt;
&lt;div id=&quot;top&quot;&gt;
&lt;jsp:include page=&quot;write.jsp&quot;/&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;div id=&quot;bottom&quot;&gt;
&lt;jsp:include page=&quot;list.jsp&quot; /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/f4c1e906-c62b-42f9-a5e6-bc4b305b149e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹(include 액션태그, include디렉티브)]]></title>
            <link>https://velog.io/@j_code/%EC%9B%B9include-%EC%95%A1%EC%85%98%ED%83%9C%EA%B7%B8-include%EB%94%94%EB%A0%89%ED%8B%B0%EB%B8%8C</link>
            <guid>https://velog.io/@j_code/%EC%9B%B9include-%EC%95%A1%EC%85%98%ED%83%9C%EA%B7%B8-include%EB%94%94%EB%A0%89%ED%8B%B0%EB%B8%8C</guid>
            <pubDate>Sat, 29 Apr 2023 03:00:02 GMT</pubDate>
            <description><![CDATA[<h1 id="페이지-모듈화와-요청-흐름-제어">페이지 모듈화와 요청 흐름 제어</h1>
<p>배울 내용</p>
<ul>
<li><p><a href="jsp:include">jsp:include</a> 액션 태그와 모듈화</p>
</li>
<li><p>include 디렉티브 = &lt;%@ include %&gt;
위의 두 기능을 사용하면 중복된 코드 없이 여러 jsp코드에 공통된 구성요소를 만들 수 있다.</p>
</li>
<li><p><a href="jsp:forword">jsp:forword</a> 액션 태그를 이용한 흐름 이동</p>
<h2 id="jspinclude-액션태그⭐⭐⭐"><a href="jsp:include">jsp:include</a> 액션태그⭐⭐⭐</h2>
<p>&lt;jsp : 이름이 나온다&gt; : 액션태그</p>
</li>
<li><p>*<a href="jsp:include">jsp:include</a> :인클루드 액션태그**</p>
<blockquote>
<p><strong>사용법 : &lt;jsp:include page=&quot;포함할 페이지&quot; flush=&quot;true&quot;/&gt;</strong></p>
</blockquote>
</li>
</ul>
<blockquote>
</blockquote>
<p>💡<strong>&lt;jsp:include page=&quot;sub.jsp&quot; flush=&quot;false&quot;/&gt;</strong><br>sub.jsp를 포함시키겠다는 뜻. 
<code>flush = true</code>라는 뜻은 &#39;a,b,c,d,e&#39;가 있다면 버퍼에 다 차지 않아도 플러시해서 a,b는 일단 브라우저로 응답으로 보내라는 뜻이다.
<code>flush = false</code>는 flush()시키지말고, 우선 버퍼에 &#39;a,b,c&#39;를 담아놔라는 뜻. 
false가 기본값이다.
flush : 플러시하고 합칠거냐 그냥 브라우저에 보낼 것이냐?
결론적으로 사용자는 abcd로 보이기 때문에 두개로 나눠진것을 알 수 없다.
<strong>include는 요청이 들어와서 응답이 나갈때까지 복수개의 jsp를 사용하는 방법이다.</strong>
-&gt;request영역은 요청이 들어와서 응답이 나갈때까지를 말한다.
페이지가 두개 자나가서, 두개의 페이지는 리퀘스트객체를 공유하게 된다. 
sub.jsp에서 리퀘스트 객체가 그대로 유지가 된다.<br>sub.jsp에서 문제생길 때 flsuh는 무조건 false해야한다.
<img src="https://velog.velcdn.com/images/j_code/post/1bd44728-e755-40b5-bb58-2d5a0d25a94c/image.jpg" alt=""></p>
<h4 id="mainjsp">&lt;main.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;main&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
main.jsp에 생성한 내용.

&lt;jsp:include page=&quot;sub.jsp&quot; flush=&quot;false&quot;/&gt;    

include 이후 내용.
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="subjsp">sub.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;

&lt;p&gt;
sub.jsp에서 생성한 내용.
&lt;/p&gt;

&lt;!-- 필요한 태그만 여기에 써준다.  --&gt;
</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/0bb1bca7-b00f-403f-8ac1-5d66e1b9ec53/image.png" alt=""></p>
<h3 id="jspinclude액션-태그를-이용한-중복-영역-처리"><a href="jsp:include">jsp:include</a>액션 태그를 이용한 중복 영역 처리</h3>
<p>상단과 하단의 부분을 공통적인 부분인데, 만약 jsp파일을 40개만든다면 중복되는 부분이 40개 되는 것이다. 그리고 수정을 하려고 한다면, 전부 다 건들여야 한다. 그래서 이렇게 전부 다 건들이는 것을 막기 위해서 만든다.</p>
<p>공통의 부분을 jsp파일로 뽑아서 작성하고 <a href="jsp:include">jsp:include</a>액션태그를 사용해서 공통부분 jsp파일로 페이지를 이동시킨다. 사용자에게는 별도의 구분없이, 보인다. </p>
<p>예시
&lt;layout.jsp&gt;</p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;layout1&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;table width=&quot;400&quot; border=&quot;1&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot;&gt;
    &lt;tr&gt;
        &lt;td colspan=&quot;2&quot;&gt;
            &lt;jsp:include page=&quot;/module/top.jsp&quot; flush=&quot;false&quot;/&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td width=&quot;100&quot; valign=&quot;top&quot;&gt;
            &lt;jsp:include page=&quot;/module/left.jsp&quot; flush=&quot;false&quot;/&gt;
        &lt;/td&gt;
        &lt;td width=&quot;300&quot; valign=&quot;top&quot;&gt;
            &lt;!--  내용부분 : 시작 --&gt;
            레이아웃1
            &lt;br&gt;&lt;br&gt;&lt;br&gt;
            &lt;!--  내용부분 : 끝 --&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td colspan=&quot;2&quot;&gt;
            &lt;jsp:include page=&quot;/module/bottom.jsp&quot; flush=&quot;false&quot;/&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/5cd95042-f8d5-4e3c-8d36-51f6eb761b56/image.png" alt=""></p>
<p>&lt;layout2.jsp&gt;</p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;layout2&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;table width=&quot;400&quot; border=&quot;1&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot;&gt;
    &lt;tr&gt;
        &lt;td colspan=&quot;2&quot;&gt;
            &lt;jsp:include page=&quot;/module/top.jsp&quot; flush=&quot;false&quot;/&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td width=&quot;100&quot; valign=&quot;top&quot;&gt;
            &lt;jsp:include page=&quot;/module/left.jsp&quot; flush=&quot;false&quot;/&gt;
        &lt;/td&gt;
        &lt;td width=&quot;300&quot; valign=&quot;top&quot;&gt;
            이 부분은 layout2.jsp가 생성한다.&lt;br&gt;
            레이아웃2
            &lt;br&gt;&lt;br&gt;&lt;br&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td colspan=&quot;2&quot;&gt;
            &lt;jsp:include page=&quot;/module/bottom.jsp&quot; flush=&quot;false&quot;/&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/96a21314-07f0-4eba-978f-7cf9c6d07e56/image.png" alt=""></p>
<p>상,하, 좌는 고정된 jsp파일을 통해서 작성한다.
그래서 두개의 코드 모두 안의 내용만 다르고 상, 하, 좌는 똑같다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/j_code/post/68dc6fea-e6d3-4a6b-b5b9-3a426c1880ea/image.jpg" alt="">
그럼 안의 내용도 바꾸려면 어떻게 해야 하나?
안의 내용 A와 B도 그냥 jsp파일로 뽑아서 연결하면 된다.</p>
<p>파라미터를 입력해서 start.jsp와 page를 연결하는 듯 -주소창에 연결됨. 처리는 start에서 처리한다. 
이때 파라미터는 &quot;?&quot;로 설정하나? - 물어보기.
이러면 중복이 없어지고 추가만 해주면 된다. 
화면이 바뀌면 start.jsp만 처리하면 된다.</p>
<h4 id="stratjsp">&lt;strat.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%
    String contentsPage = request.getParameter(&quot;contentsPage&quot;);
    if(contentsPage == null) {
        contentsPage = &quot;a&quot;;
    }
    contentsPage += &quot;.jsp&quot;;
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;%-- template page --%&gt;
&lt;title&gt;start.jsp&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;table border=&quot;1&quot;&gt;
        &lt;tr&gt;
            &lt;td colspan=&quot;2&quot;&gt;
                &lt;jsp:include page=&quot;logo.jsp&quot; /&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;&lt;jsp:include page=&quot;menu.jsp&quot; /&gt;&lt;/td&gt;
            &lt;td&gt;&lt;jsp:include page=&quot;&lt;%= contentsPage %&gt;&quot; /&gt;&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="menujsp">&lt;menu.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
Menu
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;start.jsp?contentsPage=a&quot;&gt;page A&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;start.jsp?contentsPage=b&quot;&gt;page B&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;start.jsp?contentsPage=c&quot;&gt;page C&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</code></pre><h4 id="logojsp">&lt;logo.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;h1&gt;Logo&lt;/h1&gt;</code></pre><h4 id="ajsp">&lt;a.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;h2&gt;a.jsp&lt;/h2&gt;</code></pre><h4 id="bjsp">&lt;b.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;h2&gt;b.jsp&lt;/h2&gt;</code></pre><h4 id="cjsp">&lt;c.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;h2&gt;c.jsp&lt;/h2&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/2e94a93b-a56f-44cd-881a-c54a3e526440/image.png" alt=""></p>
<p>a를 누르면 a가 나오고, b,c도 각각 누르면 화면이 바뀐다.</p>
<h3 id="jspparam으로-포함할-페이지에-파라미터-추가하기"><a href="jsp:param">jsp:param</a>으로 포함할 페이지에 파라미터 추가하기.</h3>
<p>형식은 아래의 사진과 같다.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/44aad72c-1a69-42bb-92b9-839e43ecef18/image.png" alt="">
<a href="jsp:param">jsp:param</a>액션 태그는 <a href="jsp:include">jsp:include</a> 의 자식 태그이다.
<a href="jsp:param">jsp:param</a>액션 태그는 파리미터를 인위적으로 추가할 수 있게 해주는 것이다.
name속성과 value속성은 각각 포함할 페이지에 새로 추가할 파라미터의 이름과 값을 지정.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/e89dd91e-2e40-4773-9d07-7567ef8b1b8c/image.jpg" alt=""></p>
<p>원래라면, 리퀘스트로 요청이 들어가면 요청이 나올때까지 같이 값을 공유하는데, 
이렇게 전체적으로 사용하는 파라미터 말고(result.jsp가 포함되는 파라미터말고), 
<strong>특정 하나의 jsp에만 즉, some.jsp에만 쓸 수 있는 파라미터를 추가할 수 있다.</strong> 
include된 파일 안에서만 쓸 수 있는 파라미터를 만들어 준다. 
그 안의 연산이 끝나면 그냥 사라진다. 우리가 인위적으로 만든 것이다. 
메서드 파라미터와 비슷하다. include가 메서드랑 비슷하다.</p>
<h3 id="속성attribute">속성(attribute)</h3>
<p>*<em>근데 만약에 some.jsp와 other.jsp만 가지고 있는 파라미터를 다시 result.jsp에게 전달해주고 싶다면???
이럴 때, attribute를 사용해서 공유한다. *</em>
*<em>속성을 잡아넣으면 어디든 사용할 수 있다. *</em>
*<em>그래서 param대신에 Attribute를 사용하는 것이 더 좋지 않나 싶다. *</em>
문자열뿐만아니라 오브젝트여서 걍 다 던질 수 있기 때문에. </p>
<h4 id="formjsp">&lt;form.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;form.jsp&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form action=&quot;result.jsp&quot; method=&quot;get&quot;&gt;
        age : &lt;input type=&quot;text&quot; name=&quot;age&quot; /&gt;
        &lt;input type=&quot;submit&quot; /&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="resultjsp">&lt;result.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;result.jsp&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    result.jsp : age 파라미터 =&gt; 
    &lt;%= request.getParameter(&quot;age&quot;) %&gt;
    &lt;hr&gt;
    &lt;jsp:include page=&quot;some.jsp&quot;&gt;
        &lt;jsp:param name=&quot;addr&quot; value=&quot;busan&quot; /&gt;
    &lt;/jsp:include&gt;
    &lt;hr&gt;    
    result.jsp : addr 파라미터 =&gt; 
    &lt;%= request.getParameter(&quot;addr&quot;) %&gt;
    result.jsp : myAttr 속성 =&gt;
    &lt;%= request.getAttribute(&quot;myAttr&quot;) %&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="somejsp">&lt;some.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%-- some.jsp --%&gt;
some.jsp : age 파라미터 =&gt; 
    &lt;%= request.getParameter(&quot;age&quot;) %&gt;
&lt;br&gt;
some.jsp : addr 파라미터 =&gt; 
    &lt;%= request.getParameter(&quot;addr&quot;) %&gt;
&lt;hr&gt;
&lt;jsp:include page=&quot;other.jsp&quot; /&gt;</code></pre><h4 id="otherjsp">&lt;other.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%-- other.jsp --%&gt;
other.jsp : age 파라미터 =&gt; 
    &lt;%= request.getParameter(&quot;age&quot;) %&gt;
&lt;br&gt;
other.jsp : addr 파라미터 =&gt; 
    &lt;%= request.getParameter(&quot;addr&quot;) %&gt;
&lt;%
    request.setAttribute(&quot;myAttr&quot;, &quot;myValue&quot;);
%&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/f4bd5fa5-40fc-4272-a1a1-547d0ed72b98/image.png" alt=""></p>
<h2 id="include-디렉티브">include 디렉티브</h2>
<blockquote>
<p><strong>&lt;%@ include %&gt;</strong></p>
</blockquote>
<p>include 액션태그와 include 디렉티브 비교하기</p>
<h3 id="include-액션태그-jspinclude-page-flush">&lt;include 액션태그&gt; :<code>&lt;jsp:include page=&quot;&quot; flush=&quot;&quot;&gt;</code></h3>
<p>목적: <strong>눈에 보이는 중복되는 거 소거하기 위해서 사용</strong>.
html의 구조같은 거 중복되는 거 삭제하려고.</p>
<ul>
<li><p>변환과정
main.jsp-&gt;main_jsp.java -&gt;class파일 
sub.jsp -&gt;sub_jsp.java -&gt;class파일
두개의 jsp가 있으면 각각 독립적으로 변환된다.
그리고 합쳐지는 과정은 출력버퍼를 같이 쓰는 것이다.</p>
</li>
<li><p>main.jsp에서 sub.jsp를 include한다고 하면, 
버퍼에 main의 a <strong>-&gt;</strong> sub의 c <strong>-&gt;</strong> 다시 main의 b 순으로 버퍼에 들어감.
출력버퍼로 합쳐지는 것이다. 
sub.jsp는 걍 혼자서 쓰일 수 있음. 서로 관계없음.
동일한 버퍼를 사용함으로 합쳐진다.</p>
</li>
</ul>
<h3 id="include-디렉티브--한몸이-된다-include-filejspf">&lt;include 디렉티브&gt; : 한몸이 된다. <code>&lt;include file=&quot;~~.jspf&quot;&gt;</code></h3>
<p>목적 : 디렉티브는 <strong>값이나 연산의 중복을 삭제</strong>하려고.
.jspf : jsp의 조각이다.</p>
<p>includer.jsp
includee.jspf
위의 두개가 합쳐져서 -&gt; includer_jsp.java -&gt;class파일 하나만.
아예 합친 내용으로 서블릿을 만든다. 
쓰임새가 좀 다르다. 
인클루드 디렉티브안에 jspf파일이 들어가는 것 같음.</p>
<p>저 합치는 작업은 순전히 WAS한테 의존하다. 하지만 바꿨는데 적용안될수도 있음. 
1.그럴때 localhost파일로 가서 includer_jsp.java와 clas파일을 삭제해야한다. 
2.그리고 브라우저에 캐시를 삭제해라.
2가지하면 해결된다. </p>
<p>목적
1.모든 jsp페이지에서 사용하는 변수 지정
2.저작권 표시와 같이 모든 페이지에서 중복되는 간단한 문장.</p>
<p>매번 getAttribute같은 거 안해줘도 되고, 그냥 include 하면 사용할 수 있다.
반복되는 코드(반복문 같은거) 눈에 안보이게 하려고 사용한다.
코드같은 거 숨기려고도 사용한다.</p>
<h3 id="코드조각자동포함기능">코드조각자동포함기능</h3>
<p>내가 인클루드 디렉티브를 안써도 자동으로 해주는 게 있다.</p>
<blockquote>
</blockquote>
<ul>
<li><p><code>&lt;jsp-property-group&gt;</code> : jsp의 프로퍼티를 포함한다.</p>
</li>
<li><p><code>&lt;url-pattern&gt;</code> : 프로퍼티를 적용할 jsp파일의 url패턴을 지정한다.</p>
</li>
<li><p><code>&lt;include-prelude&gt;</code> : url-pattern 태그에 지정한 패턴에 해당하는 jsp파일의 앞에 삽입할 파일을 지정한다. <strong>최상단</strong>에 자동으로 인클라이드해주는 거. -변수 같은거多</p>
</li>
<li><p><code>&lt;inclued-coda&gt;</code> : url-pattern 태그에 지정한 패턴에 해당하는 jsp 파일의 뒤에 삽입할 파일을 지정한다. <strong>최하단</strong>에 자동 인클루드해주는 거. -공통된 정보多</p>
</li>
<li><p><code>&lt;include-prelude&gt;</code> 와 <code>&lt;inclued-coda&gt;</code> 태그를 모두 설정해야하는 것은 아니며, 필요한 태그만 설정하면 된다.</p>
</li>
<li><p>web.xml에 <code>&lt;jsp-property-group&gt;</code> 태그를 사용해서 2개이상의 파일을 정할 수도 있는데 이때는 내가 적은 순서대로 추가가 된다.</p>
</li>
<li><p>이렇게 자동 되는 것이 인클루드가 &#39;많으면&#39;, 어디서 왔는지, 사용했는지 알기 어렵다. 그래서 무조건 사용하면 안된다. 어디있는 지 찾아야 한다.</p>
</li>
</ul>
<h4 id="webxml">web.xml</h4>
<pre><code>  &lt;jsp-config&gt;
  &lt;jsp-property-group&gt;
      &lt;url-pattern&gt;/view/*&lt;/url-pattern&gt;
    //view에 있는 모든 jsp에 자동으로 들어간다는 뜻.
      &lt;include-prelude&gt;/common/variable.jspf&lt;/include-prelude&gt;
      &lt;include-coda&gt;/common/footer.jspf&lt;/include-coda&gt;
  &lt;/jsp-property-group&gt;
  &lt;/jsp-config&gt;
&lt;/web-app&gt;</code></pre><h4 id="commonvarialbejspf">/common/varialbe.jspf</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%

java.util.Date CURRENT_TIME = new java.util.Date();
%&gt;</code></pre><h4 id="commonfooterjspf">/common/footer.jspf</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!-- 
소스코드 작성 : madvirus.net

 --&gt;</code></pre><h4 id="viewautoincludejsp">/view/autoInclude.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
현재 시간은 &lt;%= CURRENT_TIME %&gt; 입니다.
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>결과
<img src="https://velog.velcdn.com/images/j_code/post/ab74d62f-1fe3-4edb-8864-0c40c97f9596/image.png" alt=""></p>
<p>04.26,04.28수업</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹(에러처리)]]></title>
            <link>https://velog.io/@j_code/%EC%9B%B9%EC%97%90%EB%9F%AC%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@j_code/%EC%9B%B9%EC%97%90%EB%9F%AC%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Wed, 26 Apr 2023 09:35:01 GMT</pubDate>
            <description><![CDATA[<h2 id="에러-처리">에러 처리</h2>
<h3 id="익셉션-직접-처리하기">익셉션 직접 처리하기</h3>
<p>오류가 사용자에게 노출되면 신뢰도가 추락한다. 
<strong>방법1.try-catch</strong> : 좋은 방법이 아니다.
그 전에 jsp파일에서 입출력연산하면, 예외처리 안했다. 예외가 안나온다.
자바파일로 바뀌고 실행된다.
<strong>service()메서드가 이미 예외처리를 throw하고 있어서 jsp파일에서 예외처리를 하지 않아도 된다.</strong></p>
<h3 id="더-좋은-방법">더 좋은 방법</h3>
<p><strong>방법2.에러페이지 지정, 작성하기</strong>
사용자에게 에러처리를 해주는 게 아니라, 준비된 것처럼 대신해서 보여주는 페이지를 설정하는 거. 준비되지 않은 것(막 500숫자 나오는 페이지)을 보여주지 않겠다는 의미.</p>
<blockquote>
<p><strong>에러페이지 지정 : *<em><code>&lt;%@ page errorPage = &quot;jsp파일명&quot; %&gt;</code>
*</em>에러페이지 작성</strong> : <code>&lt;%@ page isErrorPage = &quot;true&quot; %&gt;</code>
: &#39;true&#39;로 지정하면, jsp페이지는 에러 페이지가 된다. 에러페이지로 지정된 jsp파일은 exception 기본 객체를 사용할 수 있다.</p>
</blockquote>
<ul>
<li>응답상태코드가 400, 404나 500 등의 에러코드이고</li>
<li>전체 응답 결과 데이터의 길이가 512바이트보다 작을 때
=&gt; 이러면 익스플로러 자체 에러페이지를 보여준다. </li>
</ul>
<h3 id="응답-상태-코드별로-에러-페이지-지정하기">응답 상태 코드별로 에러 페이지 지정하기</h3>
<p>원래 jsp마다 일일이 지정하는 게 아니라 내가 만든 모든 jsp에서 404 or 500에러가 뜨면 화면을 보여준다. 코드별로 분할해서 정의할 수 있다. 
<img src="https://velog.velcdn.com/images/j_code/post/ed2a38a6-a08b-4c12-8512-c0f73ca2a8f7/image.jpg" alt=""></p>
<p><code>&lt;error-page&gt;</code> 태그는 한개의 에러 페이지를 지정한다. 
<code>&lt;error-code&gt;</code> 태그는 에러 상태 코드를 지정한다.
<code>&lt;location&gt;</code> 태그는 에러 페이지로 사용할 jsp파일의 경로를 지정한다.</p>
<ul>
<li>오류코드
200번대 : 정상
300번대 : 리다이렉트
400번대 : 클라이언트의 요청
500번대 : 서버의 응답</li>
</ul>
<h3 id="익셉션-타입별로-에러-페이지-지정하기">익셉션 타입별로 에러 페이지 지정하기</h3>
<pre><code>&lt;error-page&gt;
  &lt;exception-type&gt; java.lang.NullPointerException&lt;/exception-type&gt;
  &lt;location&gt;/error/errorNullPointer.jsp&lt;/location?
&lt;/error-page&gt;</code></pre><p>위의 코드처럼 적으면, NullPointerException이 발생할 경우 errorNullPointer.jsp를 에러 페이지로 보여준다.</p>
<h3 id="에러-페이지를-여러-방법으로-지정한-경우-우선순위중첩될-때">에러 페이지를 여러 방법으로 지정한 경우 우선순위.(중첩될 때)</h3>
<p>1순위. 페이지디렉티브
2순위. 익셉션타입
3순위. 에러코드</p>
<h3 id="버퍼와-에러페이지의-관계⭐⭐">버퍼와 에러페이지의 관계⭐⭐</h3>
<p>가능하다면, 예외를 올리면 된다. </p>
<p>에러페이지를 지정했음에도 제대로 동작하지 않는 상황.?? 
처음 출력버퍼에는 abc가 담기고 flush되버리면 헤더가 나가면서 브라우저한테 정상처리로 보내게 된다. 그럼 브라우저는 결과화면에 abc를 보내고 → de를 담았는데 e에서 에러가 났다! ⇒ de하다가 문제생겨서 de버리고 에러페이지의 xyz를 출력하게됨</p>
<p>어떤 특정한 jsp에 문제가 생기면 xyz만 깔끔하게 보여야되는데 이렇게 안 나옴. 에러알려주는 페이지랑 정상응답나오는게 섞여서 출력되는 경우가 진짜많다.(수강신청같은거 ) </p>
<p>그럼 이거 어떻게 피해야할까? <strong>페이지 디렉티브에서 출력버퍼를 바꿀 수 있었다.</strong> 밑의 예시 2번줄 처럼 <code>&lt;% page buffer = &quot;1kb&quot; %&gt;</code>!!!!
<strong>예외가 발생했다는 위치까지 한번에 버퍼를 담을 수 있도록 버퍼 크기를 늘려라.</strong> = 버퍼의 크기를 늘려라. 만약에 연산의 수행순서가 상관없다면 코드위치를 바꿔도 되는데(안될 가능성이 높음)</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/e3d5ac6a-f641-44fc-be97-8a77c256a11b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹(JSP기본 객체와 영역)]]></title>
            <link>https://velog.io/@j_code/%EC%9B%B9JSP%EA%B8%B0%EB%B3%B8-%EA%B0%9D%EC%B2%B4%EC%99%80-%EC%98%81%EC%97%AD</link>
            <guid>https://velog.io/@j_code/%EC%9B%B9JSP%EA%B8%B0%EB%B3%B8-%EA%B0%9D%EC%B2%B4%EC%99%80-%EC%98%81%EC%97%AD</guid>
            <pubDate>Tue, 25 Apr 2023 18:03:32 GMT</pubDate>
            <description><![CDATA[<h2 id="jsp기본-객체와-영역⭐⭐⭐⭐⭐">JSP기본 객체와 영역⭐⭐⭐⭐⭐</h2>
<h2 id="네가지-영역">네가지 영역</h2>
<p>네자기 영역으로 구성된다.</p>
<ul>
<li>page영역 : <strong>pageContext를 얘기하는 것이다. page를 나타내는 게 아니다.</strong></li>
<li>request : request 기본객체</li>
<li>session : session 기본객체</li>
<li>application :application 기본객체</li>
</ul>
<p>웹은 상태를 저장할 수 있게 만들어 지지 않았다 -&gt; 사용자가 여러페이지를 거친다면,
이전 페이지에서 뭘 했는 지를 알 수 없다. 
그게 &#39;상태&#39;라는 것이다. 
html은 변수를 만들 수 없어서 값 저장이 안된다.
휘발성 데이터를 영속성 데이터를 저장하는 파일에 저장할 필요가 없다. </p>
<p>4개의 객체는 안에 값을 넣을 수 있는 기능이 있는데 이걸 <strong>attribute</strong>라고 한다. 
일을 진행할 때 이게 없으면 일을 할 수 없다.
9개의 객체중에 &#39;<strong>속성</strong>&#39;을 제공하는 객체가 4개가 있다.
*<em>속성 : 값을 담아놓는 장소. map같은 형식이다. *</em>
4개가 다 속성을 지원하는데, 저 4개가 각각 언제까지 유지가 되느냐의 문제.</p>
<h2 id="page-request-session-application">page, request, session, application</h2>
<p>데이터가 유지되는 범위를 4가지로 나눴다.
<strong>범위 무조건 이해해야한다. 여기에다가 값을 잡아넣을 것이기 때문에!!!!</strong></p>
<blockquote>
</blockquote>
<p><strong>page</strong>
: pageContext객체 </p>
<ul>
<li>해당하는 페이지 안(한페이지 안)에서만 쓸 수 있다. 페이지 내부에서만 쓸 수 있다. 
jsp당 한개. 많이 안쓰임.
하나의 jsp페이지를 범위로 갖는데, jsp가 바뀌면 PageContext가 바뀌고 그 안의 값이 사라진다.</li>
</ul>
<blockquote>
</blockquote>
<p><strong>request</strong> ⭐
: request 기본객체 - HttpServletRequest API사용</p>
<ul>
<li><strong>요청이 들어와서 응답이 나갈 때까지 유지된다</strong>.
요청이 들어와서 <strong>여러 jsp를 거치게 되는 상황</strong>이 있다. 
그때 jsp가 4개라면, 4개의 jsp가 같은 리퀘스트를 공유한다. </li>
<li><em>기능 하나*</em>라고 생각하기. 
하나의 기능이 시작해서 끝날 때까지 약간 메서드에 들어오는 파라미터 같은 거. 
메서드 하나가 기능하나니까. 
개수는 지역변수보다 파라미터가 훨씬 많다. 기능이 수백인데 그때마다 request가 사용된다.
많이 사용됨. </li>
</ul>
<blockquote>
</blockquote>
<p><strong>session</strong>⭐ 
: session 기본객체 - HttpSession API 사용</p>
<ul>
<li><strong>클라이언트 당 하나씩 할당</strong>된다. 
클라이언트= 브라우저라서, <strong>브라우저 당 하나씩 할당.</strong> 
브라우저를 접속했을 때부터 끌 때까지 유지가 된다. 
사용자 별 정보에 사용된다. 
대표적인게 로그인 같은 거. 로그인 한번 하면 네이버 아무때나 가도 네이버 로그인 되어 있다. 사용자 한명의 정보라서. 
크롬, 네이버웨일, 엣지 3개의 브라우저로 들어가면 3명의 클라이언트로 인식. 
많이 사용됨.</li>
</ul>
<blockquote>
</blockquote>
<p><strong>application</strong> 
: application기본객체 - SerlvetContext API 사용</p>
<ul>
<li>전체에서 1개만 있기 때문에, 모두 <strong>공유</strong>한다.  <strong>서버 시작부터 서버 종료까지</strong>. 
특정한 사용자의 정보는 넣을 수 없다. </li>
<li><em>공유하는 곳에는 모든 사용자가, 모든 jsp가 같이 사용하는 정보를 넣어야 한다.*</em> 
예를 들면 오늘의 방문자 수, 설정정보 등.</li>
</ul>
<h2 id="jsp-기본-객체의-속성-사용하기">jsp 기본 객체의 속성 사용하기</h2>
<blockquote>
</blockquote>
<ul>
<li><strong>setAttribute</strong>(String name, Object value) : 이름이 name인 속성의 값을 value로 지정한다. Object는 우리가 원하는 뭐든지 다 담을 수 있게 만든다.</li>
<li><strong>getAttribute</strong>(String name) : 이름이 name인 속성의 값을 구한다. 지정한 이름의 속서잉 존재하지 않으면 null을 리턴한다. 리턴타입- Object</li>
<li><strong>removeAttribute</strong>(String name) : 이름이 name인 속성을 삭제한다.</li>
<li><strong>getAttributeNames</strong>() : 속성의 이름 목록을 구한다.(pageContext 기본 객체는 이 메서드를 제공하지 않는다.) 리턴타입- <code>Enumeration&lt;String&gt;</code></li>
<li>우리가 만들어 내는 값이라서  set도 있고, 지우는 것도 있음.</li>
</ul>
<p>mvc : 세가지 파트가 있는데 하는 역할이 다른거..
이 3가지가 하나로 묶여야지 일이 끝난다. -&gt; 서로 주고 받는 값같은 것들이 존재할 때 request의 속성을 이용한다. 계속 유지되어야 되는 값들. 이 값들을 request에 담는다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[application 예제(Student.jsp)]]></title>
            <link>https://velog.io/@j_code/application-%EC%98%88%EC%A0%9C</link>
            <guid>https://velog.io/@j_code/application-%EC%98%88%EC%A0%9C</guid>
            <pubDate>Mon, 24 Apr 2023 10:48:24 GMT</pubDate>
            <description><![CDATA[<h3 id="application-예제">application 예제</h3>
<h4 id="webxml">&lt;web.xml&gt;</h4>
<pre><code>&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://java.sun.com/xml/ns/javaee&quot; xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot; id=&quot;WebApp_ID&quot; version=&quot;2.5&quot;&gt;
  &lt;display-name&gt;04.24&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;context-param&gt;
        &lt;param-name&gt;dataPath&lt;/param-name&gt;
        &lt;param-value&gt;/data/list.dat&lt;/param-value&gt;
    &lt;/context-param&gt;
&lt;/web-app&gt;</code></pre><h4 id="studentjava">&lt;Student.java&gt;</h4>
<pre><code>package kr.ac.green;

import java.io.Serializable;

public class Student implements Serializable{    //학생정보 담을 거. Stream쓰려고 시리얼라이즈함.
    private String name;
    private String nick;
    private int age;
    public Student() {

    }

    public Student(String name, String nick, int age) {
        super();
        this.name = name;
        this.nick = nick;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNick() {
        return nick;
    }

    public void setNick(String nick) {
        this.nick = nick;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return &quot;Student [name=&quot; + name + &quot;, nick=&quot; + nick + &quot;, age=&quot; + age + &quot;]&quot;;
    }
}
</code></pre><h4 id="studentmanagerjava">&lt;StudentManager.java&gt;</h4>
<pre><code>package kr.ac.green;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Vector;

import javax.servlet.ServletContext;

public class StudentManager {
    public static Vector&lt;Student&gt; loadList(ServletContext application, String paramName) {
         String dataPath = application.getInitParameter(paramName);
         String path = application.getRealPath(dataPath);

         FileInputStream fis = null;
         ObjectInputStream ois= null;
         Vector&lt;Student&gt; list = null;

         try {
             fis = new FileInputStream(path);
             ois = new ObjectInputStream(fis);
             list = (Vector&lt;Student&gt;)ois.readObject();
         }catch(IOException e) {
             list = new Vector&lt;Student&gt;();
         }catch (ClassNotFoundException e) {
             e.printStackTrace();
         }finally {
             try {
                 ois.close();
             }catch(Exception e) {}
             try {
                 fis.close();
             }catch(Exception e) {}
         }
         return list;
    }

    public static void storeStudent(ServletContext application, Vector&lt;Student&gt; list, String paramName) {
        String dataPath = application.getInitParameter(paramName);
        String path = application.getRealPath(dataPath);

        FileOutputStream fos = null;
        ObjectOutputStream oos = null;

        try {
            fos = new FileOutputStream(path);
            oos = new ObjectOutputStream(fos);
            oos.writeObject(list);
            oos.flush();
            oos.reset();
        }catch(IOException e) {

        }finally {
            try {
                oos.close();
            }catch(Exception e) {}
            try {
                fos.close();
            }catch(Exception e) {}
        }
    }
}</code></pre><h4 id="mainjsp">&lt;main.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import=&quot;java.util.*&quot; %&gt;
&lt;%@ page import=&quot;kr.ac.green.*&quot; %&gt;
&lt;%
    Vector&lt;Student&gt; list = StudentManager.loadList(application, &quot;dataPath&quot;);
%&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html&quot;; charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;main.jsp&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form action=&quot;save.jsp&quot; method  =&quot;post&quot;&gt;
        이름 : &lt;input type=&quot;text&quot; name=&quot;studentName&quot; /&gt;
        &lt;br&gt;
        별명 : &lt;input type=&quot;text&quot;  name=&quot;studentNick&quot;/&gt;
        &lt;br&gt;
        나이 : &lt;input type=&quot;text&quot;  name=&quot;studentAge&quot;/&gt;
        &lt;br&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;hr&gt;
    &lt;table&gt;
        &lt;caption&gt;학생목록&lt;/caption&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;/tr&gt;
        &lt;/thead&gt;
        &lt;tfoot&gt;
            &lt;tr&gt;
                &lt;th colspan=&quot;4&quot;&gt;&lt;%=list.size() %&gt; row(s) &lt;/th&gt;
            &lt;/tr&gt;
        &lt;/tfoot&gt;
        &lt;tbody&gt;
            &lt;%
                for(int i = 0; i&lt;list.size(); i++) {
                    Student temp = list.get(i);
            %&gt;
            &lt;tr&gt;
                &lt;td&gt;&lt;%= i+1 %&gt;&lt;/td&gt;
                &lt;td&gt;&lt;%= temp.getName() %&gt;&lt;/td&gt;
                &lt;td&gt;&lt;%= temp.getNick() %&gt;&lt;/td&gt;
                &lt;td&gt;&lt;%= temp.getAge() %&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;%
                }            
            %&gt;
        &lt;/tbody&gt;        
    &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="savejsp">&lt;save.jsp&gt;</h4>
<pre><code>&lt;%@page import=&quot;kr.ac.green.StudentManager&quot;%&gt;
&lt;%@page import=&quot;kr.ac.green.Student&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import=&quot;java.io.*&quot; %&gt;
&lt;%@ page import=&quot;java.util.*&quot; %&gt;
&lt;%@ page import=&quot;kr.ac.green.*&quot; %&gt;
&lt;%
    request.setCharacterEncoding(&quot;euc-kr&quot;);

    String name = request.getParameter(&quot;studentName&quot;);
    String nick = request.getParameter(&quot;studentNick&quot;);
    int age = Integer.parseInt(request.getParameter(&quot;studentAge&quot;));

    Student s = new Student(name, nick, age);

    Vector&lt;Student&gt; list = StudentManager.loadList(application, &quot;dataPath&quot;);
    list.add(s);
    StudentManager.storeStudent(application, list, &quot;dataPath&quot;);
    response.sendRedirect(&quot;main.jsp&quot;);
%&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/080b0943-d686-4558-825c-fd60bb4c8381/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹7(application,web.xml)]]></title>
            <link>https://velog.io/@j_code/%EC%9B%B97applicationweb.xml</link>
            <guid>https://velog.io/@j_code/%EC%9B%B97applicationweb.xml</guid>
            <pubDate>Sat, 22 Apr 2023 20:17:22 GMT</pubDate>
            <description><![CDATA[<h2 id="application-기본-객체">application 기본 객체</h2>
<p>application은 &#39;<strong>기본객체</strong>&#39;중에 하나이다. 기본객체는 따로 만들지 않아도 된다. </p>
<p>application 기본 객체는 웹 어플리케이션 전반에 걸쳐서 사용되는 정보를 담고 있다. 예를 들어, application 기본 객체를 사용해서 초기 설정 정보를 읽어올 수 있고, 서버 정보, 웹 어플리케이션이 제공하는 자원(파일)을 읽어올 수도 있다. </p>
<p>Context Root는 웹 어플리케이션을 웹 서버에서 실행시킬 때, 해당 어플리케이션의 최상위 URL 경로를 말한다. 예를 들어, Context Root가 &quot;/myapp&quot;이면 해당 웹 어플리케이션의 최상위 URL은 &quot;<a href="http://localhost:8080/myapp&quot;%EC%9D%B4">http://localhost:8080/myapp&quot;이</a> 된다. 자동으로 설정되어 있지만 이걸 변경하고 싶을 때 web.xml에서 바꾼다.</p>
<h3 id="webxml--web-application-설정을-위한-deployment-descriptor배포설명자">web.xml : web application 설정을 위한 deployment descriptor(배포설명자)</h3>
<p>: web.xml 파일에 <strong>초기화 파라미터</strong>를 추가하면, JSP는 application 기본 객체가 제공하는 메서드를 사용해서 초기화 파라미터를 사용할 수 있다. jsp에서 어디든 초기화 파라미터를 사용할 수 있다. 초기화 파라미터를 추가하는 방법이 <code>&lt;context-param&gt;</code>.</p>
<p>: <code>WebContent/WEB-INF/web.xml</code> 
위치 :여기 안에 있어야 한다 고정이 되어있다. 약속이 되어 있는 거.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/b36e96ff-98c2-49c8-af5e-6a04df278c37/image.png" alt=""></p>
<ul>
<li><p><strong>welcomfile</strong>
💡 프로젝트 루트로 들어오면 너 처음에 뭐 보여줄래? 가 된다. -&gt; 이 이걸 지정하는게 welcome file인듯.</p>
</li>
<li><p><em>사용자가 url를 넣고 검색했을 때, welcomfile에 넣은 파일을 보여주는 것인듯.*</em>
&#39;처음 시작하는 페이지&#39;라고 생각하면 될 듯.
💡 따로 지정 안하고 루트로 들어왔을 때. restart해줘야 한다. -&gt; 톰캣은 .jsp말고 다른 web.xml, javaclass 얘네가 바뀌었을 때는 수동으로 재시작을 해줘야 한다.</p>
</li>
<li><blockquote>
<p>근데 이클립스에서 자동으로 물어봄
(퍼온거)
일반적으로 웹에서 메인페이지를 보여줄때 메인페이지를 보여주는 url을 &#39;main.do&#39;형태로 설정할경우 사용자가 <a href="http://localhost:8080/main.do">http://localhost:8080/main.do</a> 형태의 url로 접근하기보다는  <a href="http://localhost:8080/">http://localhost:8080/</a> 형태의 주소로 들어오는 경우가 대부분일 텐데 이 경우에는 페이지가 없기 때문에 404에러가 발생할 것입니다. 하지만 <code>&lt;welcome-file-list&gt;</code>를 설정해놓고index.jsp 의 코드를 아래와 같이 작성하여 자연스럽게 main.do페이지로 리다이렉트 시켜준다면 사용자가 비록<a href="http://localhost:8080/">http://localhost:8080/</a> 로 접근했을지라도 자연스럽게 메인페이지로 이동하게 됩니다.
(/퍼온거)
<img src="https://velog.velcdn.com/images/j_code/post/88fa798a-c17a-4f27-8a28-b1053fd3b7ad/image.png" alt=""></p>
</blockquote>
</li>
<li><p><strong>context-param</strong> : 웹 어플리케이션에서 사용할 수 있는 초기화 파라미터는 web.xml에 <code>&lt;context-param&gt;</code> 태그를 사용하여 추가한다.</p>
</li>
<li><p>클래스도 만들 수 있는데 javaREsources - src - 안에 생긴다.</p>
</li>
</ul>
<p>import해서 쓰면 됨. 메소드같은것도 휴먼클래스에 쓴다. -&gt; jsp(서블렛)는 &#39;view&#39;의 역할이기 때문에 여기서 연산을 하면 안된다. 왜냐하면 이 안의 기능들은 재사용이 안된다. (그래서 선언부를 잘 안쓴다고 했던 것)</p>
<h3 id="application-기본객체의-메서드⭐⭐">application 기본객체의 메서드⭐⭐</h3>
<blockquote>
</blockquote>
<ul>
<li>getInitParameter(String name) : 리턴값Stirng - 이름이 name인 파라미터의 값을 읽어온다.</li>
<li>⭐<strong>getInitParameterNames()</strong> : 리턴값Enumeration - 파라미터의 이름 목록을 리턴함.
위의 메서드 사용하기 전에 초기화 파라미터를 web.xml 파일에 추가해야한다.
이 메서드들은 web.xml에서 정의된 파라미터들을 읽어온다!!</li>
</ul>
<h4 id="webxml">&lt;web.xml&gt;</h4>
<pre><code>&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://java.sun.com/xml/ns/javaee&quot; xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot; id=&quot;WebApp_ID&quot; version=&quot;2.5&quot;&gt;
  &lt;display-name&gt;04.21&lt;/display-name&gt;
  👉&lt;welcome-file-list&gt;
    &lt;welcome-file&gt;index.jsp&lt;/welcome-file&gt;
  &lt;/welcome-file-list&gt;

  👉&lt;context-param&gt;    
  // 어플리케이션 객체로 context-param을 꺼내올 수 있다.
      &lt;description&gt;로깅여부&lt;/description&gt;
      &lt;param-name&gt;logEnabled&lt;/param-name&gt;
      &lt;param-value&gt;true&lt;/param-value&gt;
  &lt;/context-param&gt;

  &lt;context-param&gt;
      &lt;description&gt;디버깅 레벨&lt;/description&gt;
      &lt;param-name&gt;debugLevel&lt;/param-name&gt;
      &lt;param-value&gt;5&lt;/param-value&gt;
  &lt;/context-param&gt;
&lt;/web-app&gt;</code></pre><h4 id="readinitparamjsp">&lt;readInitParam.jsp&gt;</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import = &quot;java.util.Enumeration&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;초기화 파라미터 읽어오기&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
초기화 파라미터 목록 :
&lt;ul&gt;
&lt;%
Enumeration&lt;String&gt; initParamEnum = application.getInitParameterNames();👈
// param-name을 다 들고 오는 것이다. 다꺼내오는거
while(initParamEnum.hasMoreElements()){    
// 있습니까? 들고오세용
    String initParamName = initParamEnum.nextElement();    
    %&gt;
&lt;li&gt; &lt;%= initParamName %&gt; = &lt;%= application.getInitParameter(initParamName) %&gt;    
// 이름 알고 있으면 이름이 맞는 value들고오는 거 .다이렉트로 바로 끌고 오는 거. 
//바로 설정했기 때문에 name을 모를 수가 없다. 이거 진짜 많이 쓴다!!!!!
 &lt;%= initParamName %&gt; = &lt;%= application.getInitParameter(&quot;logEnable&quot;) %&gt;    
 // 이렇게 쓰인다.
&lt;%
}
%&gt;
&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/9c1f21f2-9ce2-4cbb-a041-c948858b0ae8/image.png" alt=""></p>
<hr>
<h3 id="웹-어플리케이션-초기화-파라미터는-언제-사용할까">웹 어플리케이션 초기화 파라미터는 언제 사용할까?</h3>
<p>웹 어플리케이션 초기화 파라미터는 이름처럼 웹 어플리케이션을 초기화하는 데 필요한 설정 정보를 저정하기 위해 사용된다. 파일 경로같은 거 지정할 때 초기화 파라미터를 사용한다.</p>
<h3 id="서버-정보-읽어오기">서버 정보 읽어오기</h3>
<p>서버정보 : <code>&lt;%= application.getServerInfo()%&gt;</code>
서블릿 규약 메이저 버전 : <code>&lt;%= application.getMajorVersion()%&gt;</code>
서블릿 규약 마이너 버전 : <code>&lt;%= application.getMinorVersion()%&gt;</code></p>
<p>서블릿 3.1 버전이라는 뜻.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/04497e44-a1b4-4142-9f45-e14dc9d67a8b/image.png" alt=""></p>
<h3 id="--로그메시지기록하기잘-사용되지-않는다-중요x">- 로그메시지기록하기(잘 사용되지 않는다.)-중요X</h3>
<p>로그는 기록하는 것. 로그 남기는 행위 : 로깅
로깅이 무조건 들어가 있다. -&gt; 어플리케이션 차원에서 기본적으로 로그를 제공해준다.
application.log(&quot;application log&quot;)
log(&quot;JSP log&quot;) : jsp로그
로그가 이클립스에서 사용하면 콘솔에 찍히는데 실제로는 파일에 남는다.</p>
<h3 id="-웹어플리케이션-자원구하기127페이지⭐⭐⭐">-웹어플리케이션 자원구하기(127페이지)⭐⭐⭐</h3>
<p>jsp 페이지에서 웹 어플리케이션 폴더에 위치한 파일을 사용해야 할 때가 있다.
개발을 할 때는 내가 어디에 파일이 있는 지 알 수 있는데, 이걸 &#39;배포&#39;했을 때는 어디에 저장이 될 지 알 수 없다.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/3fd41c0e-5405-4eea-aaa7-76fbd6f18a46/image.png" alt=""></p>
<h3 id="application-자원-접근-메서드">application 자원 접근 메서드</h3>
<blockquote>
</blockquote>
<ul>
<li>⭐<strong>getRealPath(String path)</strong> : 웹 어플리케이션 내에서 지정한 경로에 해당하는 자원의 시스템상에서의 경로를 리턴한다. 리턴타입 - String
배포를 하기 전까지는 알 수가 없다. 내부경로를 실제 디스크상의 경로로 구해온다. 읽기쓰기 둘 다 된다. 개발을 할 때 저게 어디있는 지 알아야 해서 이 메서드를 써준다.</li>
<li><strong>getResource(String path)</strong> : 웹 어플리케이션 내에서 지정한 경로에 해당하는 자원에 접근할 수 있는 URL객체를 리턴한다. 리턴타입 - java.net.URL</li>
<li>⭐<strong>getResourceAsStream(String path)</strong> : 웹 어플리케이션 내에서 지정한경로에 해당하는 자원으로부터 데이터를 읽어올 수 잇는 InputStream을 리턴한다. </li>
</ul>
<p>getResourceAsStream() 메소드를 사용하여 리소스 파일을 읽어오면, 파일 시스템에 접근하는 것이 아니므로 보안에 유리하다. 
getRealPath() 메소드를 사용하면 리소스 파일의 실제 경로를 구하지 않아도 되므로, 이식성이 높은 코드를 작성할 수 있습니다.</p>
<ul>
<li>내부경로 : 절대경로같은 거인 듯.</li>
<li>실제경로(디스크상 경로) :  웹 어플리케이션이 배포된 서버의 파일 시스템 상의 경로. 어플리케이션 내부경로와는 달리, 특정 서버의 파일 시스템 구조에 의존하기 때문에 웹 어플리케이션이 배포된 서버에서만 사용할 수 있다.</li>
</ul>
<h4 id="readfiledirectlyjsp---절대경로">&lt;readfileDirectly.jsp&gt; - 절대경로</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import = &quot;java.io.*&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;절대 경로 사용하여 자원 읽기&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;%
    char[] buff = new char[128];
    한번에 128바이트의 문자열을 읽어들이기 위해 char 배열 buff를 선언
    int len = -1;

    String filePath = &quot;C:\\test\\notice.txt&quot;;
    //이건 절대경로.
    // 이렇게 넣음! 근데 배포 후에는 알 수 없다.
    FileInputStream fis = null;
    InputStreamReader isr = null;
    try{
        fis = new FileInputStream(filePath);
        isr = new InputStreamReader(fis);
        while( (len = isr.read(buff)) != -1) {
            out.print(new String(buff,0,len));
        }

    } catch(IOException ex) {
        out.println(&quot;익셉션 발생: &quot; + ex.getMessage());
    } finally {
        try{
            isr.close();
        }catch(Exception e) {}
        try{
            fis.close();
        }catch(Exception e) {}
    }
%&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="readfileusingapplicationjsp---getrealpathgetresourceasstream">&lt;readFileUsingApplication.jsp&gt; - getRealPath(),getResourceAsStream()</h4>
<pre><code>&lt;%@page import=&quot;java.io.IOException&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
    &lt;%@ page import = &quot;java.io.*&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;application 기본 객체 사용하여 자원 읽기&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
    String resourcePath = &quot;/message/notice.txt&quot; ; 
    //이건 절대경로인데 디스상의 경로가 아니라 어플리케이션 내부 경로이다. 
    //문제가 뭐냐면, 디스크상의 경로를 가질 수있으면 좋은데 어디로 배포될지 알 수 없다.
    // &#39;/&#39;앞에는 어디있는지 알 수 없다 그냥 내부에 넣어둔 것이다.
%&gt;
자원의 실제 경로 :&lt;br&gt;
&lt;%= application.getRealPath(resourcePath) %&gt; 👈
// 이게 실제로 디스크상의 경로를 알려준다. 
&lt;br&gt;
------------&lt;br&gt;
&lt;%= resourcePath %&gt;의 내용&lt;br&gt;
------------&lt;br&gt;
&lt;%
    char[] buff = new char[128];
    int len = -1;
    InputStream is = null;
    InputStreamReader isr = null;

    try {
        is = application.getResourceAsStream(resourcePath);👈
        //리소스 파일을 읽어와서 출력하는 코드.
        isr = new InputStreamReader(is);
        while((len = isr.read(buff)) != -1) {
            out.print(new String(buff,0,len));
        }
    }catch(IOException e) {
        out.println(&quot;익셉션 발생:&quot; + e.getMessage());
    }finally {
        try {
            isr.close();
        }catch(Exception e) {}
        try {
            is.close();
        }catch(Exception e) {}
    }
%&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/56cf6c95-9c51-4920-be4e-1ec17a0321d5/image.png" alt=""></p>
<h4 id="readfileusingurljsp---getsource">&lt;readFileUsingURL.jsp&gt; - getSource()</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
    &lt;%@ page import = &quot;java.io.*&quot; %&gt;
    &lt;%@ page import = &quot;java.net.URL&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;application 기본 객체 사용하여 자원 읽기2&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
    String resourcePath = &quot;/message/notice.txt&quot;;
    char[] buff = new char[128];
    int len = -1;
    URL url = application.getResource(resourcePath);👈
    InputStream is = null;
    InputStreamReader isr = null;
    try {
        is = url.openStream();
        isr = new InputStreamReader(is);
        while((len = isr.read(buff)) != -1) {
            out.print(new String(buff, 0 , len));
        }
    }catch(IOException ex) {
        out.println(&quot;익셉션 발생&quot; + ex.getMessage());

    }finally {
        try{
            isr.close();
        }catch(Exception e) {}
        try{
            is.close();
        }catch(Exception e) {}
    }

%&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/dc686144-7aab-4f93-a9f0-a8c8e3d04d8e/image.png" alt="">
&lt;readFileUsingURL.jsp&gt;</p>
<p>D:\am\kjw\web.metadata.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\04.21\message\notice.txt
-&gt; 이게 이클립스에서만 이렇게 나온다.
D:\am\kjw\web.metadata.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\04.21\message 이까지만 적고 탐색기에 넣으면 텍스트파일이 나온다.</p>
<p>실제로는 저 위의 경로가 이클립스의 워크스페이스에 있는 것을 web.metadata여기에 복사를 해놓고 web.metadata여기에서 작업을 수행하다. 서비스 내부적으로 그냥 이렇게 사용한다.</p>
<p>경로와 파일을 분리해놓는다. 나중에 파일같은 것이 바뀔 수도 있으니까.
붙어있으면 경로가 바뀌든 파일이 바뀌든 무조건 수정해야한다. 
경로가 만약 바뀌면 여러개가 똑같은 위치를 갖고 있는데 하나라도 잘못하면 일관성이 무너진다.
경로 하나만 바꾸면 된다. 
경로는 경로대로, 파일은 파일대로 설정하기.
properties로 정보를 따로 두면 컴파일 대상이 아니라서 코드에 부담이 안가고, 코드를 안바꾸고도 값을 구할 수 있다. </p>
<p>web.xml안에 comf/path.txt 를 param-context에 넣고, txt파일의 내용만 바꾸면 restart할 필요없이 경로와 파일을 바꿀 수 잇다. : 설정에 설정을 둔다. </p>
<p>라이브러리,프레임워크 차이는 통제권을 누가 가지고 있느냐?
라이브러리는 순수하게 개발자가 다 결정한다.
프레임워크는 약속대로 뭔가를 해야한다. -&gt; Ioc, 제어의 역전, 통제권을 또 다른 프로그램에게 주는 것. </p>
<hr>
<h3 id="wirte-main-article-dowrite-articleutiljsp">&lt;wirte, main, Article, doWrite, ArticleUtil.jsp&gt;</h3>
<p>먜)
write-&gt; doWrite -&gt; main으로 이어진다.
write에서 적은 값을 doWrite로 넘겨주고, doWrite는 read()해서 그걸 파일에 저장하고 다시 write()해서 main에 값을 넘겨준다. 중간에 doWrite가 들어간 이유는 직접적으로 값을 넘기면 새로고침할 때마다 값이 2번씩 넘어간다. 그래서 중간에 doWrite를 줘서 리다이렉트를 만들어서 넣는다.</p>
<p>&lt;write.jsp&gt;</p>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;write.jsp&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;a href=&quot;main.jsp&quot;&gt;목록보기&lt;/a&gt;
    &lt;hr&gt;
    &lt;form action=&quot;doWrite.jsp&quot; method=&quot;post&quot;&gt;
        내용 : &lt;input type=&quot;text&quot; name=&quot;saying&quot; /&gt;
        &lt;br&gt;
        글쓴이 : &lt;input type=&quot;text&quot; name=&quot;writer&quot; /&gt;
        &lt;br&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>
<p>&lt;main.jsp&gt;</p>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import=&quot;java.io.*&quot; %&gt;   
&lt;%@ page import=&quot;java.util.Vector&quot; %&gt; 
&lt;%@ page import=&quot;kr.ac.green.*&quot; %&gt;

&lt;%
    String fileName = &quot;articles.dat&quot;;
    String tempPath = &quot;/data/&quot; + fileName;
    String realPath = application.getRealPath(tempPath);    

    Vector&lt;Article&gt; list = ArticleUtil.read(realPath);    
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;main.jsp&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;a href=&quot;write.jsp&quot;&gt;글쓰기&lt;/a&gt;
    &lt;hr&gt;
    &lt;table&gt;
        &lt;tr&gt;
            &lt;th&gt;날짜&lt;/th&gt;&lt;th&gt;내용&lt;/th&gt;&lt;th&gt;글쓴이&lt;/th&gt;
        &lt;/tr&gt;
        &lt;%
            if(list != null) {
                for(int i=list.size()-1; i&gt;=0; i--) {
                    Article temp = list.get(i);
        %&gt;
        &lt;tr&gt;
            &lt;td&gt;&lt;%= temp.getDateString() %&gt;&lt;/td&gt;&lt;td&gt;&lt;%= temp.getSaying() %&gt;&lt;/td&gt;&lt;td&gt;&lt;%= temp.getWriter() %&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;%
                }
            } else {
        %&gt;

        &lt;tr&gt;
            &lt;th colspan=&quot;3&quot;&gt;아직 등록한 글이 없어요 ㅠ.ㅠ&lt;/th&gt;
        &lt;/tr&gt;
        &lt;%
            }
        %&gt;
    &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>&lt;dowrite.jsp&gt;</p>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;%@ page import=&quot;java.io.*&quot; %&gt;   
&lt;%@ page import=&quot;java.util.Vector&quot; %&gt; 
&lt;%@ page import=&quot;kr.ac.green.*&quot; %&gt;
&lt;%-- doWrite.jsp --%&gt;
&lt;%
    request.setCharacterEncoding(&quot;EUC-KR&quot;);

    String fileName = &quot;articles.dat&quot;;
    String tempPath = &quot;/data/&quot; + fileName;
    String realPath = application.getRealPath(tempPath);

    // 쓰기        
    String saying = request.getParameter(&quot;saying&quot;);
    String writer = request.getParameter(&quot;writer&quot;);

    Article article = new Article(saying, writer);

    Vector&lt;Article&gt; list = ArticleUtil.read(realPath);
    if(list == null) {
        list = new Vector&lt;Article&gt;();
    }        
    FileOutputStream fos = null;
    ObjectOutputStream oos = null;        
    try {
        fos = new FileOutputStream(realPath);
        oos = new ObjectOutputStream(fos);
        list.add(article);
        oos.writeObject(list);
        oos.flush();
        oos.reset();
    } catch(Exception e) {
        e.printStackTrace();
    } finally {
        try {
            oos.close();
        } catch(Exception e){}
        try {
            fos.close();
        } catch(Exception e) {}
    }

    response.sendRedirect(&quot;main.jsp&quot;);    
%&gt;</code></pre>
<p>&lt;Article.java&gt;</p>
<pre><code class="language-java">package kr.ac.green;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Article implements Serializable{
    private String saying;
    private String writer;
    private Date date;

    public Article(String saying, String writer) {
        setSaying(saying);
        setWriter(writer);
        setDate(new Date());
    }

    public String getSaying() {
        return saying;
    }

    public void setSaying(String saying) {
        this.saying = saying;
    }

    public String getWriter() {
        return writer;
    }

    public void setWriter(String writer) {
        this.writer = writer;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    private static SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy.MM.dd&quot;);

    public String getDateString() {
        return sdf.format(date);
    }
}</code></pre>
<p>&lt;ArticleUtil.java&gt;</p>
<pre><code class="language-java">package kr.ac.green;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Vector;

public class ArticleUtil {
    public static Vector&lt;Article&gt; read(String realPath) {
        FileInputStream fis = null;
        ObjectInputStream ois = null;

        Vector&lt;Article&gt; list = null;
        try {
            fis = new FileInputStream(realPath);
            ois = new ObjectInputStream(fis);
            list = (Vector&lt;Article&gt;)ois.readObject();
        } catch(Exception e) {

        } finally {
            try {
                ois.close();
            } catch(Exception e) {}
            try {
                fis.close();
            } catch(Exception e) {}
        }
        return list;
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/d363972c-4dd0-4c4a-815e-3118a83cef4f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹6(get,post방식/리다이렉트/출력버퍼/getContextPath()/기본객체)]]></title>
            <link>https://velog.io/@j_code/%EC%9B%B96getpost%EB%B0%A9%EC%8B%9D%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%ED%8A%B8%EC%B6%9C%EB%A0%A5%EB%B2%84%ED%8D%BCgetContextPath%EA%B8%B0%EB%B3%B8%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@j_code/%EC%9B%B96getpost%EB%B0%A9%EC%8B%9D%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%ED%8A%B8%EC%B6%9C%EB%A0%A5%EB%B2%84%ED%8D%BCgetContextPath%EA%B8%B0%EB%B3%B8%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Thu, 20 Apr 2023 11:18:51 GMT</pubDate>
            <description><![CDATA[<h2 id="get방식과-post방식리다이렉트">get방식과 post방식(리다이렉트)</h2>
<p>클라이언트는 <strong>&#39;url : 기능&#39;</strong>를 보낸다. 
예를 들어 cafe.naver.com 과 map.naver.com은 url이 아예 다른 것이다. 
<strong>&#39;파라미터&#39;</strong>는 url 즉, <strong>기능을 수행할 때 필요한 정보</strong>이다.
요청은 라인,헤더,바디로 구성되는데 이건 클라이언트가 만든다.</p>
<p><strong>url</strong>은 무조건 <strong>라인</strong>에 들어간다.
<strong>파라미터</strong>는 get방식을 할 때는 라인에 따라 들어가고, 
post방식에서는 바디에 들어간다. </p>
<p>get방식은 모든 정보를 오픈한다. 누군가가 그대로 흉내낼 수 있다.
post방식일때는 사용자 의도를 서버가 알아차리기 어렵다. 그대로 흉내낼 수 없다.
흉내낼 수 있는 지의 여부로 언제 get을 쓰고 언제 post를 쓸 지 결정하면 된다.</p>
<p>&#39;새로고침&#39;은 url과 파라미터를 몰라도 마지막 요청이 그대로 날아가는 거라서 무조건 흉내낼 수 있는 것이다.
그래서 어떻게 보면 ‘새로고침’은 get과 post와 관련이 없을 수도 있다.</p>
<hr>
<p>(퍼온 사진)
<img src="https://velog.velcdn.com/images/j_code/post/1a24a81d-d7aa-4fb2-bce8-545a1851d7fe/image.png" alt="">
(퍼온 내용)
<a href="https://ip99202.github.io/posts/JSP-Servlet-Request,-Response-%EA%B0%9D%EC%B2%B4/">https://ip99202.github.io/posts/JSP-Servlet-Request,-Response-%EA%B0%9D%EC%B2%B4/</a></p>
<p>Request, Response 과정
웹 브라우저에 URL을 입력
웹 브라우저는 도메인과 포트 번호를 이용해서 서버에 접속
path 정보, 클라이언트의 IP 등의 요청 정보를 서버에 전송
WAS가 HttpServletRequest라는 객체와 HttpServletResponse라는 객체를 생성
생성된 두 개의 객체를 요청 정보에 있는 path로 매핑된 서블릿에게 전달
전달된 객체는 service(), doGet(), doPost() 같은 메서드에 파라미터로 전달되서 사용</p>
<p>HttpServletRequest
http 프로토콜의 request 정보를 서블릿에게 전달하기 위한 목적으로 사용한다.
헤더정보, 파라미터, 쿠키, URI, URL 등의 정보를 읽는 메소드를 가지고 있다.
Body의 Stream을 읽어 들이는 메소드를 가지고 있다.</p>
<p>HttpServletResponse
WAS는 요청을 보낸 클라이언트에게 응답을 보내기 위한 HttpServletResponse 객체를 생성하여 서블릿에게 전달한다.
서블릿은 해당 객체를 이용하여 content type, 응답코드, 응답 메시지등을 전송한다.</p>
<hr>
<h2 id="wasrequest객체">WAS(request객체)</h2>
<blockquote>
<p>브라우저(클라이언트)-&gt;웹서버-&gt;WAS로 간다. WAS가 request객체에 감싸서 정보를 주고, response객체도 같이 -&gt;jsp에게 준다. 
그리고 다시 jsp-&gt;WAS-&gt;웹서버(정적데이터)-&gt;클라이언트(브라우저)에게 준다.</p>
</blockquote>
<p>웹서버와 WAS를 묶어서 WAS라고 쳐라.
jsp가 쓸 수 있도록 만들어준다. 
url과 파라미터를 WAS에 넘겨주면 
WAS가 request의 객체로 만들어서 jsp에게 넘겨준다. 
그래서 getParameter()같은 것을 뽑아 올 수 있다. 
우리는 jsp만 하면 된다.
jsp의 실행된 결과를 response객체의 바디에 담아서 보낸다.
어떤 웹서버를 쓰더라도 request는 쓰는 방법이 똑같다.
jsp는 요청한 결과 보게 되는 것이다. </p>
<h2 id="리다이렉트">리다이렉트</h2>
<p><strong>리다이렉트에서는 요청이 두번 생긴다!</strong>!! 이게 제일 중요하다.
요청이 2번으로 끝난 것이다.
*<em>리다이렉트를 하면 마지막요청이 b.jsp가 되기 때문에, 새로고침해도 b.jsp된다. *</em>
요청이 되면 응답은 무조건 일어난다.
새로고침은 브라우저에 저장이 되어 있는 것이라서 서버가 막을 수가 없다.
새로고침을 ‘막는 게 아니라’ 새로고침을 통해서 ‘일어나는 일을 바꾸면 된다.’ -&gt;이게 리다이렉트인듯.
뒤로가기를 두번 눌러도 그냥 만료된 페이지같은 게 나온다.</p>
<hr>
<h2 id="jsp주석">JSP주석</h2>
<p><code>&lt;!-- --&gt;</code>  <strong>html주석</strong> : 사용자가 확인을 할 수 있다. - 이걸 쓸 일이 거의 없다. 
<code>&lt;%-- --%&gt;</code>  <strong>jsp주석</strong> : 이건 남들이 볼 수 없다. 사용자가 확일을 할 수 없다.<br>스크립트릿, 선언부는 자바코드라서<code>//주석처리</code>가 된다. 이것도 사용자가 확인 할 수 없다.</p>
<hr>
<h2 id="jsp처리과정">JSP처리과정</h2>
<p><img src="https://velog.velcdn.com/images/j_code/post/4039e0fd-31ef-4d20-ac0b-7648860a5759/image.jpeg" alt=""></p>
<p>브라우저-&gt;WAS--&gt;a.jsp --&gt; java로 변환 --&gt; jsp로부터 자바 코드 생성 --&gt; 컴파일 --&gt; class파일</p>
<p>서블릿 = jsp에서 변환된 자바소스코드이다.
그래서 WAS가 컴파일을 해준다. 그러면 클래스파일를 만든다.</p>
<p>최초실행과 a.jsp과 변경되었을 때는 저기의 파란색의 순서로 시행된다.
다시 a.jsp를 요청하면(재요청) 바로 clss파일로 가서 그걸 들고 바로 실행한다. 처음 실행하면 서블릿으로 한다.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/65e3432e-7d7e-4df8-a34d-5bb1fe4bde8c/image.png" alt=""></p>
<p>D:\am\kjw\web.metadata.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\04.20\org\apache\jsp</p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;%
    String str = &quot;hi~&quot;;
%&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Hello&lt;/h1&gt;&lt;%= str %&gt;
&lt;/body&gt;
&lt;/html&gt;
&lt;%!
    private void todo() {
        System.out.println(&quot;todo&quot;);
    }

    private int num = 4;
%&gt;</code></pre><h3 id="코드를-바꿨는데-결과가-바뀌지-않을-때">코드를 바꿨는데 결과가 바뀌지 않을 때</h3>
<p>hello_jsp : &#39;jsp&#39;를 &#39;서블릿&#39;(자바코드)으로 표현한 거. 엄청 길다. 
jsp에서 에러가 났을 때 서블릿으로 변환된 부분에서 예외처리를 할 수 있는 경우가 있다.</p>
<p>정적데이터이기 때문에 css,HTML 걔들은 WAS가 인지를 못한다. 코드를 바꿨음에도 불구하고 결과가 바뀌지 않는 경우가 있다. -&gt; 특히 파일을 외부로 꺼내놨을때!</p>
<p>1.그럴 경우에는 서블릿 파일을 지우고 다시 실행한다. 그러면 WAS가 다시 서블릿파일을 만들어준다. hello_jsp를 지워버려도 된다. 어차피 실행하면 다시 만들어진다.</p>
<p>2.브라우저에서 캐시도 삭제해야한다. 서블릿이 자바의 웹표준이다.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/a10dfa8b-1481-4ee1-9047-d5af7211dabf/image.png" alt=""></p>
<p>워크스페이스 : D:\am\kjw\web</p>
<p>D:\am\kjw\web.metadata.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost 이거를 바탕화면 바로가기 만들어놓기</p>
<h3 id="jsp의-기본객체를-선언을-안해도-쓸-수-있다-어떻게">jsp의 기본객체를 선언을 안해도 쓸 수 있다. 어떻게?</h3>
<p>서블릿의 파일에 보면, jspService()의 파라미터에 HttpServletRequest를 넣어준다. 이걸 WAS가 넣어준다.(WAS가 request객체를 만들어 줌) 서블릿 파일의 메소드안에 기본객체가 있다.</p>
<blockquote>
</blockquote>
<p>서블릿은 세가지 메소드로 구성된다. 
1.init() : 서블릿이 생성될 때 딱 한번만 호출됨.
2.destroy() : 그담은 삭제될 때
3.<strong>service()</strong> : 이 안에 request와 response가 들어가 있음.</p>
<ul>
<li><p><strong>스크립트릿, 표현식, HTML</strong> -&gt; <strong>jsp가 서블릿으로 변환될 때 service()메서드 안으로 들어간다.</strong>
service메서드 안에 request와 response가 들어가 있어서 기본객체로서 사용할 수 있다. 
모든 jsp기본객체에 대해서</p>
</li>
<li><p><em>선언, 정의*</em>가 들어가고 그 밑에 <strong>스크립트릿, 표현식, HTML</strong> 가 들어간다. 
그래서 jsp에서는 기본객체라는 애들을 사용할 수 있게 된다.</p>
</li>
<li><p><strong>선언부</strong>로 정의된 부분 -&gt; 서블릿의 멤버변수로 등록되고, 서블릿의 메서드 등록된다. 얘는 서비스의 메서드에 들어가지 않는다.</p>
</li>
</ul>
<p>결국 jsp라고 하는 것도 자바클래스의 일부분일 뿐이다.</p>
<h2 id="출력버퍼와-응답">출력버퍼와 응답</h2>
<p>WAS의 출력버퍼라는 게 있는데 거기에 쓴다.
세칸짜리 출력버퍼가 있다면, class에서 출력버퍼를 하나씩 채우고 3칸이 차면 와스가 브라우저에 보낸다.
jsp가 실행된다는게 버퍼를 하나씩 채운다는 의미이다.</p>
<h3 id="버퍼-저장-후-한꺼번에-브라우저에-전송할-때의-장점">버퍼 저장 후 한꺼번에 브라우저에 전송할 때의 장점</h3>
<blockquote>
<p>1.데이터 전송 성능 향상
2.JSP 실행 도중에 버퍼를 비우고 새로운 내용 전송 가능
3.버퍼가 다 차기 전까지 첫번째 헤더 변경 가능</p>
</blockquote>
<p>&lt;설명&gt;
2. JSP 실행 도중에 버퍼를 비우고 새로운 내용 전송 가능
버퍼가 있다는 얘기는 다이렉트로 바로 안간다는 의미. 버퍼가 있음으로 인해서 다이렉트로 전송 안된다. 
버퍼라는 게 다 차야지 나간다. 
그것때문에 flush()를 한다.
<strong>3칸중에 2칸이 찼을 경우에 아직 브라우저에 안나갔는데, 그때 그 안의 2칸의 내용을 없앨 수 있다.(수정할 수 있음)</strong> 
버퍼안을 비울 수 있다. 중간에 취소가 된다. 
그래서 flush()되기 전까지 그 안의 내용을 바꿀 수 있다. 
출력버퍼에 잠깐 저장했다가 보낸다. 그래서 수정할 수 있다. </p>
<p>3.버퍼가 다 차기 전까지 첫번째 헤더 변경 가능
헤더는 &#39;헤더&#39;와 &#39;라인&#39;을 같이 말하는 거
응답헤더(응답코드가 들어간다)는 &#39;첫번째 버퍼(a,b,c)&#39;가 전송이 될 때 200번코드(정상)가 들어간다. 
*<em>헤더값은 첫번째에만 전송이 되고 그다음에는 바디가 전송이된다. 헤더를 안읽고 바디만 읽어온다. *</em>
그러면 그 뒤(d,e)에 예외가 발생했을 때, 오류코드 500번이 날아가야 되는데 이미 헤더가 전송이 되었기 때문에 500번을 못 날린다.
응답코드의 문제가 생길 수 있다. 
첫번째 버퍼가 전송되지 않았으면 우리는 정보를 변경할 수 있다.</p>
<p>페이지디렉티브를 통해서 버퍼크기를 지정할 수 있다. 를 알기.
버퍼는 안쓰면 8kb를 사용한다. </p>
<p>버퍼크기를 수작업으로 정해줘야 할 일이 생길 수도 있으니 원리를 이해하고 있어라.</p>
<p>출력버퍼는 respose 바디에 들어가는 것을 얘기하는 것이다. </p>
<h2 id="웹-어플리케이션의-배포">웹 어플리케이션의 배포</h2>
<p>다 만들어 졌을 때 어떻게 배포하는가?
<strong>EXPORT!!</strong>
<img src="https://velog.velcdn.com/images/j_code/post/9a647074-98ba-4a3c-9aa6-95b339fd3f8d/image.png" alt=""></p>
<p>와르~ 웹어플리케이션 배포할 때는 &#39;.war&#39;로 배포한다.
<img src="https://velog.velcdn.com/images/j_code/post/b40e1e17-cab2-4f45-8468-42ae78918560/image.png" alt="">
export source files : 체크 안하면 자바 파일이 아니라 클래스파일만 뽑아줌.
인위적으로 변경하는 걸 막기위해서 실행만 되게 클래스파일만 준다. </p>
<p>만든 파일을 톰캣설치된 폴더에 그 안에 &#39;<strong>webapps폴더</strong>&#39;에 만든 파일을(war형태의 zip파일)그대로 옮긴다.
그럼 배포완료된 것이다!</p>
<p>톰캣 폴더 안에 binary : startup.baet를 누르면 안의 폴더가 풀려있어서 서버가 읽을 수 있다. 
결과값이 나온다.</p>
<h2 id="웹어플리케이션-폴더-구성과-url매핑">웹어플리케이션 폴더 구성과 url매핑</h2>
<p><strong><a href="http://localhost:8080">http://localhost:8080</a></strong>/04_20/hello.jsp
D:\am\web\apache-tomcat-6.0.53\webapps04_20\hello.jsp</p>
<p><a href="http://localhost:8080">http://localhost:8080</a>** : 얘만 치면 톰캣 홈페이지가 나온다.</p>
<h3 id="이클립스에서-서버를-등록">이클립스에서 서버를 등록.</h3>
<p>d:/tomcat 
eclipse안에 tomcat-설정이 있다.(설정1번:8080번 설정2번:8180번을 만들 수 있다.)</p>
<p>실제 톰캣의 설정을 바꾸지 않고, 이클립스를 통해서 톰캣을 설정할 수 있다. 내가 선택해서 실행시킬 수 있다. 
톰캣을 직접설정하면 1번했다가 다시 바꿔서 2번으로 하고 해야되는데,
이클립스는 논리적으로 설정을 여러개를 만들 수 있게 해준다.
이클립스에 등록된 서버중에 뭘로 선택할 지 하는 창이 나온다.</p>
<p>톰캣 폴더 안에 binary : shutup.bat 누르면 서버가 꺼진다...
이렇게 해도 안되면 작업관리자에서 꺼라.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/12058773-64fb-4743-972c-0ece3cfc521d/image.png" alt="">
여기서 4.20만 남겨두고 사용하기. 그게 좋다.</p>
<hr>
<h3 id="requestgetcontextpath">request.getContextPath()</h3>
<p>request.getContextPath()
하면url에서 .../04.20/...만 들고온다.(폴더이름만 들고 오는 느낌.)</p>
<hr>
<h3 id="기본객체">기본객체</h3>
<p>기본객체
request, response, pageContext
<img src="https://velog.velcdn.com/images/j_code/post/69820a70-8b34-48df-ac50-8fd9aba8cf17/image.jpg" alt=""></p>
<h3 id="out기본객체--스트림-write한다">out기본객체 : 스트림, write한다.</h3>
<p>뭐 사용되는 곳이 있긴 한데..사실상 쓸모가 없다.
&lt;useOutObject.jsp&gt;</p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=EUC-KR&quot;
    pageEncoding=&quot;EUC-KR&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;기본 객체 사용하기&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
    out.println(&quot;안녕하세요&quot;);    //사실상 쓸모가 없다. 
%&gt;
&lt;br&gt;
out 기본 객체를 사용하여
&lt;%
    out.println(&quot;출력한 결과입니다&quot;);
%&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/j_code/post/c5b68d5f-67a4-4150-977e-31cda324786b/image.png" alt=""></p>
<p>앞서 배운 출력버프는 &#39;out의 버퍼&#39;이다!!</p>
<h3 id="pagecontext기본객체⭐">pageContext기본객체⭐</h3>
<p><strong>pageContext 기본 객체는 JSP페이지와 일대일로 연결된 객체다 = jsp가 바뀌면 pageContext 객체도 바뀐다.</strong>중요!</p>
<p>기본객체구하기
기본객체메서드는 나중에 서블릿할 때는 기본객체가 없기 때문에 여기 있는 메서드를 사용한다는 것 같음.</p>
<p><img src="https://velog.velcdn.com/images/j_code/post/dbb9a27a-d4b0-4818-b200-ec6f08efa25d/image.jpg" alt=""></p>
<p>우리가 보고 있는 request는 HttpServletRequest이다. 
getRequest()가 들고오는 것은 ServletRequest이다.</p>
<p>ServletRequest &lt;-상속관계 HttpServletRequest.
그럼 pageContext.getRequest()를 사용하려면 형변화해줘야한다!!</p>
<p>getServletContext()는 application이다.</p>
]]></description>
        </item>
    </channel>
</rss>