<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jiu_lee.log</title>
        <link>https://velog.io/</link>
        <description>IT개발 입문합니다. </description>
        <lastBuildDate>Fri, 09 Dec 2022 03:51:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jiu_lee.log</title>
            <url>https://images.velog.io/images/jiu_lee/profile/ff161d20-62cc-41c2-bcba-ad8a9edcb921/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jiu_lee.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jiu_lee" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Oracle) Date 타입 날짜 검색]]></title>
            <link>https://velog.io/@jiu_lee/Oracle-Date-%ED%83%80%EC%9E%85-%EB%82%A0%EC%A7%9C-%EA%B2%80%EC%83%89</link>
            <guid>https://velog.io/@jiu_lee/Oracle-Date-%ED%83%80%EC%9E%85-%EB%82%A0%EC%A7%9C-%EA%B2%80%EC%83%89</guid>
            <pubDate>Fri, 09 Dec 2022 03:51:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>날짜 검색법 <a href="https://devkingdom.tistory.com/137">https://devkingdom.tistory.com/137</a></li>
</ul>
</blockquote>
<pre><code class="language-sql">SELECT openDt, TO_CHAR(openDt, &#39;yyyy&#39;) AS year
FROM tbl_movie;

SELECT iv.year , count(iv.year)
FROM (
    SELECT openDt, TO_CHAR(openDt, &#39;yyyy&#39;) AS year
    FROM tbl_movie
)iv
GROUP BY iv.year
ORDER BY iv.year DESC
</code></pre>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/04a5c847-0134-4409-b66b-a77f0faea91c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/4908ae0e-6b5c-4dcc-b920-b9b6380d200a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Oracle) Error) ORA-01461: can bind a LONG value only for insert into a LONG column - Varchar2 최대 크기]]></title>
            <link>https://velog.io/@jiu_lee/Oracle-Error-ORA-01461-can-bind-a-LONG-value-only-for-insert-into-a-LONG-column-Varchar2-%EC%B5%9C%EB%8C%80-%ED%81%AC%EA%B8%B0</link>
            <guid>https://velog.io/@jiu_lee/Oracle-Error-ORA-01461-can-bind-a-LONG-value-only-for-insert-into-a-LONG-column-Varchar2-%EC%B5%9C%EB%8C%80-%ED%81%AC%EA%B8%B0</guid>
            <pubDate>Thu, 08 Dec 2022 13:52:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>ORA-01461 <a href="https://offbyone.tistory.com/331">https://offbyone.tistory.com/331</a></li>
</ul>
</blockquote>
<ul>
<li><p>Oracle Varchar2 데이터 가공 주의점 <a href="https://developyo.tistory.com/364">https://developyo.tistory.com/364</a></p>
</li>
<li><p>Java 문자열 자르기 <a href="https://jul-liet.tistory.com/203">https://jul-liet.tistory.com/203</a></p>
</li>
<li><p>varchar2 최대 크기를 초과한 문자열 데이터를 저장하려할 때 발생</p>
<ul>
<li>Oracle Varchar2의 최대 크기는 4000 BYTES, UTF-8 한글 3Bytes, 1Bytes</li>
</ul>
</li>
</ul>
<pre><code class="language-java">// actorDetetail 최대 크기 제한

int actorDetailLengh = actorDetail.length();

log.info(movieVO.getMovieCd()+&quot;/&quot;+movieVO.getMovieNm()+&quot;/&quot;+actorDetailLengh);

if(actorDetailLengh &lt; 1330) {
    movieVO.setActorDetail(actorDetail);
} else {
    log.info(&quot;!!!!!!actorDetail!!!!!!&quot;+movieVO.getMovieCd()+&quot;/&quot;+movieVO.getMovieNm()+&quot;/&quot;+actorDetailLengh+&quot;/&quot;+actorDetail);
      actorDetail = actorDetail.substring(0, 1330);
    int lastCommaIdx = actorDetail.lastIndexOf(&quot;,&quot;);
    if(lastCommaIdx != -1) {
        actorDetail = actorDetail.substring(0, lastCommaIdx);
        movieVO.setActorDetail(actorDetail);
    }
}
// -&gt; length, substring 등의 함수는 bytes 기준. 문자열 모두 한글로 구성되어있다는 가혹조건으로 1330 임의로 지정함</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS) replace() 이용한 유니코드 -> 특수문자 치환]]></title>
            <link>https://velog.io/@jiu_lee/JS-replace-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C-%ED%8A%B9%EC%88%98%EB%AC%B8%EC%9E%90-%EC%B9%98%ED%99%98</link>
            <guid>https://velog.io/@jiu_lee/JS-replace-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C-%ED%8A%B9%EC%88%98%EB%AC%B8%EC%9E%90-%EC%B9%98%ED%99%98</guid>
            <pubDate>Fri, 02 Dec 2022 07:45:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>replace() <a href="https://harrydony.tistory.com/191">https://harrydony.tistory.com/191</a></li>
</ul>
</blockquote>
<ul>
<li><p>replace() <a href="https://mingggu.tistory.com/64">https://mingggu.tistory.com/64</a></p>
</li>
<li><p><strong>Spring Controller에서 전달된 이메일 값을 JS 변수에 담는 경우, 특수문자가 유니코드(&amp;# 코드,숫자형 표현)으로 변환되는 문제 발생</strong></p>
</li>
<li><blockquote>
<p>replace()와 정규표현식 이용해 치환</p>
</blockquote>
<pre><code class="language-javascript">&lt;sec:authorize access=&quot;isAuthenticated()&quot;&gt;
 userid = &#39;&lt;sec:authentication property=&quot;principal.username&quot;/&gt;&#39;;
 console.log(&quot;userid&quot;,userid);
 // 특수 문자 치환
 // @ . 과 같은 특수문자 Java -&gt; JS 넘어오면서 유니코드로 변환되어 메서드 에러 발생시킴
 userid = userid.replace(&quot;&amp;#64;&quot;, &quot;@&quot;);
 userid = userid.replace(/\&amp;\#46\;/gi, &quot;.&quot;); // /gi: JS엔 replaceAll 없음 -&gt; 정규식 gi 이용해 구현
 console.log(&quot;userid&quot;,userid);
&lt;/sec:authorize&gt;
</code></pre>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/e14d3538-4f72-4427-bd5a-66d31ae094b0/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[trialNerror) Spring) @Controller Post방식 URL 전송]]></title>
            <link>https://velog.io/@jiu_lee/Spring-POSTGET-%EB%B0%A9%EC%8B%9D-Mapping</link>
            <guid>https://velog.io/@jiu_lee/Spring-POSTGET-%EB%B0%A9%EC%8B%9D-Mapping</guid>
            <pubDate>Thu, 01 Dec 2022 10:52:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>아이디어 참고 <a href="https://okky.kr/articles/501050">https://okky.kr/articles/501050</a></li>
</ul>
</blockquote>
<ul>
<li><p>Spring Security logout 속성 <a href="https://baejangho.com/entry/Spring-Security-Logout">https://baejangho.com/entry/Spring-Security-Logout</a></p>
</li>
<li><p>forward/redirect 차이 <a href="https://sorjfkrh5078.tistory.com/271">https://sorjfkrh5078.tistory.com/271</a></p>
</li>
<li><p>Controller return 타입 <a href="https://ooeunz.tistory.com/101">https://ooeunz.tistory.com/101</a></p>
</li>
<li><p>Controller Model/ModelAndView 객체 <a href="https://hongku.tistory.com/116">https://hongku.tistory.com/116</a></p>
</li>
<li><p>시행착오) 비밀번호 변경/ 회원 탈퇴 수행 후 로그아웃 처리를 바로 해야할 필요가 있었는데, Spring Security의 logout은 지정한 URL(기본 /logout)에 POST 방식으로 전송해야만 정상 수행되기 때문에 @Controller의 Mapping 메서드에서 POST방식으로 URL 전송할 방법을 찾아야했다.</p>
</li>
<li><blockquote>
<p>결록적으론 찾지 못함. return값으로 redirect: 이용해 PRG(Post/Redirect/Get) 방식으로 전송하는 방법 사용.
=&gt; 기존엔 RedirectAttributes.addFlashAttribute 이용해 로그아웃 처리 필요함을 나타내는 데이터와 함께 redirect 방식으로 메인페이지로 보낸 뒤, JS 이용해 해당 데이터가 조건문 충족 시 메인 페이지에 include된 header 페이지의 로그아웃 form을 submit 시키는 방법 사용
=&gt; 상기 방법 사용 시 결국 메인 페이지 출력을 위한 다른 기능들도 수행되므로 비효율적.
그냥 빈 JSP 페이지에 logout form만 작성 후 jQuery로 즉시 submit 하도록 하였다.</p>
</blockquote>
<pre><code class="language-java">/* POST_비밀번호 변경 */
   @PostMapping(&quot;user/changePassword&quot;)
   @PreAuthorize(&quot;hasRole(&#39;ROLE_MEMBER&#39;)&quot;)
   @Transactional
   public String changePasswordPost(Principal principal, RedirectAttributes rttr,
           String userOldPw, String userNewPw) {

       log.info(&quot;userOldPw: &quot;+userOldPw);
       log.info(&quot;userNewPw: &quot; + userNewPw);
//         rttr : addFlashAttr. 이용해 결과값 view로 전달하기 위함

       log.info(&quot;changePasswordPost.....&quot; + userNewPw);

       System.out.println(&quot;타입정보 : &quot; + principal.getClass());
       System.out.println(&quot;ID정보 : &quot; + principal.getName());

       String userid = principal.getName();

       // 기존 비밀번호와 유저 DB 저장 비밀번호 동일 여부 체크
       if(! service.checkUserpwSameness(userid, userOldPw)) {
//             유저 DB 저장 비밀번호와 동일
           rttr.addFlashAttribute(&quot;result&quot;, &quot;입력하신 기존 비밀번호가 틀렸습니다. 정확히 입력바랍니다.&quot;);

           return &quot;redirect:/user/changePassword&quot;;
       }

       // 변경할 비밀번호와 기존 비밀번호와 동일 여부 체크
       if(service.checkUserpwSameness(userid, userNewPw)) {
//             기존 비밀번호와 동일
           rttr.addFlashAttribute(&quot;result&quot;, &quot;기존 비밀번호와 동일합니다. 다른 비밀번호로 변경바랍니다.&quot;);

           return &quot;redirect:/user/changePassword&quot;;
       }

       // 비밀 번호 변경
       service.resetPassword(userid, userNewPw);

       // 기존
//        rttr.addFlashAttribute(&quot;logoutResult&quot;, &quot;logout&quot;);        
//        return &quot;redirect:/&quot;;
           // -&gt; Main 페이지 이동 후 Main JSP 페이지에서 logout form post 전송하는걸로 구현..

       // 개선
       return &quot;redirect:/logout&quot;;
       // 다른 페이지로 재전송하는 이유 : register 페이지에서 입력 항목들 전송 후 list와 같이 다른 URL로 재전송하지 않으면 동일한 내용을 새로고침 통해 
       // 계속 반복(도배)하는 문제 발생 -&gt; 등록,수정,삭제 작업은 처리 완료된 후 다시 동일한 내용 전송할 수 없도록 아예 브라우저의 URL 이동하는 방식 이용

   }</code></pre>
<pre><code class="language-javascript">&lt;script type=&quot;text/javascript&quot;&gt;
$(document).ready(function(){

   var result = &#39;&lt;c:out value=&quot;${result }&quot; /&gt;&#39;; // rttr.addFlashAttribute 통해 넘긴 파라미터
   console.log(&quot;result&quot;,result);
   if(result != &#39;&#39;){
       alert(result);
   }

     // 기존 main.jsp
   // 비밀번호 변경 Post 처리 결과 리턴 -&gt; 로그아웃
   var logoutResult = &#39;&lt;c:out value=&quot;${logoutResult }&quot; /&gt;&#39;;
   if(logoutResult == &#39;logout&#39;){
       alert(&quot;비밀번호가 변경되었습니다. 다시 로그인해주세요.&quot;);
       $(&quot;#logoutForm&quot;).submit();

   }</code></pre>
<pre><code class="language-java">/* 개선) 31.6 로그아웃 처리 
    * 비밀번호 변경/회원탈퇴 등 기능 수행 후 로그아웃 필요한 경우 사용
    * */
   @GetMapping(&quot;/logout&quot;)
   public String logoutGET() {

       log.info(&quot;logout&quot;);

       return &quot;member/logout&quot;;
   }</code></pre>
<pre><code class="language-html">&lt;!-- 개선) 비밀번호변경/회원탈퇴 등의 과정 후 로그아웃 위한 GET 페이지 --&gt;
&lt;form id=&quot;logoutForm&quot; action=&quot;/logout&quot; method=&quot;POST&quot;&gt;
  &lt;input name=&quot;${_csrf.parameterName}&quot; type=&quot;hidden&quot; value=&quot;${_csrf.token}&quot;/&gt;&lt;!-- logout도 csrf 필수 --&gt;
&lt;/form&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
$(document).ready(function(){
   $(&quot;#logoutForm&quot;).submit(); // 즉시 전송
});
&lt;/script&gt;</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Oracle) SELECT 쿼리 실행 순서 / ROWNUM과 ORDER BY절 ]]></title>
            <link>https://velog.io/@jiu_lee/Oracle-SELECT-%EC%BF%BC%EB%A6%AC-%EC%8B%A4%ED%96%89-%EC%88%9C%EC%84%9C</link>
            <guid>https://velog.io/@jiu_lee/Oracle-SELECT-%EC%BF%BC%EB%A6%AC-%EC%8B%A4%ED%96%89-%EC%88%9C%EC%84%9C</guid>
            <pubDate>Tue, 29 Nov 2022 10:18:12 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>SELECT 쿼리 실행 순서 <a href="https://loghada.tistory.com/14">https://loghada.tistory.com/14</a></li>
</ul>
</blockquote>
<ul>
<li><p><strong>문법 순서</strong>
1_1. SELECT
1_2. FROM
2_1. JOIN
2_2. ON
3_1. WHERE
4_1. GROUP BY
4_2. HAVING 
5_1. ORDER BY</p>
</li>
<li><p><strong>실행 순서</strong>
1_1. FROM
1_2. ON
1_3. JOIN
2_1. WHERE
3_1. GROUP BY
3_2. HAVING
4_1. SELECT
4_2. DISTINCT
5_1. ORDER BY</p>
</li>
</ul>
<br>

<ul>
<li><strong>ALIAS 설정 규칙</strong><ul>
<li>FROM -&gt; SELECT, ORDER BY 사용 가능</li>
<li>SELECT -&gt; ORDER BY 사용 가능</li>
</ul>
</li>
<li><blockquote>
<p>실행 순서에 따라 이후 실행 절에서 사용 가능 한 것</p>
</blockquote>
</li>
</ul>
<br>

<ul>
<li>WHERE, SELECT절 실행 후 ORDER BY절 실행된다. 즉, ROWNUM이 각 row에 할당된 후 ORDER BY절 실행되므로 ROWNUM이 정렬되지 않게 된다.
=&gt; ORDER BY 정렬된 대로 ROWNUM 할당하고 싶으면, ORDER BY절이 포함된 쿼리를 서브쿼리로 하고 메인쿼리에서 ROWNUM 할당할 것
<img src="https://velog.velcdn.com/images/jiu_lee/post/44c2155b-ee61-42f1-ab10-a1ad9cf9e60d/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Oracle) Project) REGEXP_REPLACE() 및 정규표현식 이용해 값 리스트 변환한 문자열의 값 가공하기]]></title>
            <link>https://velog.io/@jiu_lee/Oracle-Project-REGEXPREPLACE-%EB%B0%8F-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EC%9D%B4%EC%9A%A9%ED%95%B4-%EA%B0%92-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%B3%80%ED%99%98%ED%95%9C-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%98-%EA%B0%92-%EA%B0%80%EA%B3%B5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jiu_lee/Oracle-Project-REGEXPREPLACE-%EB%B0%8F-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EC%9D%B4%EC%9A%A9%ED%95%B4-%EA%B0%92-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%B3%80%ED%99%98%ED%95%9C-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%98-%EA%B0%92-%EA%B0%80%EA%B3%B5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 28 Nov 2022 12:17:10 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p><strong>REPLACE() VS REGEXP_REPLACE()</strong>
: 두 함수는 다른 함수다. REPLACE() 함수에 정규표현식이 사용가능하고 추가로 옵션 인수들을 부여할 수 있는게 REGEXP_REPLACE() 함수이다.</p>
<blockquote>
<ul>
<li>REPLACE() VS REGEXP_REPLACE() <a href="https://jdm.kr/blog/8">https://jdm.kr/blog/8</a></li>
</ul>
</blockquote>
<ul>
<li><p>REPLACE() <a href="https://gent.tistory.com/228">https://gent.tistory.com/228</a></p>
</li>
<li><p>REGEXP_REPLACE() <a href="https://lee-mandu.tistory.com/40">https://lee-mandu.tistory.com/40</a></p>
</li>
<li><p>REPLACE(컬럼, &#39;찾을문자&#39;, &#39;변환할문자&#39;)</p>
</li>
<li><p>REGEXP_REPLACE(컬럼, &#39;찾을문자 또는 정규표현식&#39;, &#39;변환할문자&#39;, 검색시작위치, 패턴 일치 발생 횟수(default 0: 모든 값 대체, 그 외 n 번째 발생하는 위치의 문자열만 변환)<br>)</p>
</li>
</ul>
</li>
</ul>
<hr>
<ul>
<li><p>ex) 영화 검색 시 영화 출연 배우 정보를 담은 <code>배우명|배우영문명|극중역할,배우명|배우영문명|극중역할,...</code> 형식의 리스트를 변환한 문자열에서 극중역할 부분에서 검색어가 검색되어 row 리턴하지 않도록 가공할 필요가 있었다.
이전엔 service단으로 일단 가져온 뒤, 배열을 반복문 돌려 split 등으로 값 분리한 후 극중역할에 걸려 반환된 객체는 제외하는 식으로 코드를 짰다.. </p>
</li>
<li><blockquote>
<p>REPLACE()와 다른 REGEXP_REPLACE() 함수가 정규표현식을 사용할 수 있다는 사실을 알게되어 쿼리 개선하였다.</p>
</blockquote>
<p>```sql
SELECT  m_a.movieCd, m_a.movieNm, RTRIM(REGEXP_REPLACE(m_a.actorDetail||&#39;,&#39; ,&#39;|.<em>?,&#39;, &#39;,&#39;), &#39; , &#39;) AS acotrDetail
FROM tbl_movie m_a
WHERE REGEXP_REPLACE(m_a.actorDetail||&#39;,&#39; ,&#39;|.</em>?,&#39;, &#39;,&#39;) LIKE REPLACE(&#39;%병헌%&#39;,&#39; &#39;,&#39;&#39;) 
/* REPACE 함수와 REGEXP_REPACE 함수 다르다. / 와일드카드 % 사용 불가 <em>/
/</em> 정규표현식: 특수문자쓰고싶으면 앞에 \ 붙여야함, \ : OR, .* : 모든문자, ?:0또는 1회 포함</p>
</li>
<li><blockquote>
<p>배우명|영문명|역할, 배우명||역할, 배우명|영문명|, 배우명|, 모두 커버 */</p>
</blockquote>
<p>```
<img src="https://velog.velcdn.com/images/jiu_lee/post/a7881558-7efd-4bff-a217-4403958baaae/image.png" alt="">
<img src="https://velog.velcdn.com/images/jiu_lee/post/33e4f7f5-b1f7-43ab-ad40-57a246e85d17/image.png" alt=""></p>
</li>
<li><blockquote>
<p>배우명만 출력한 뒤 LIKE 이용해 포함여부 검색(사실 영문명까지 남기려 했는데 정규표현식에 서툴러 해내지 못했다..)</p>
</blockquote>
<ul>
<li><code>REGEXP_REPLACE(m_a.actorDetail||&#39;,&#39; ,&#39;\|.*?\,&#39;, &#39;,&#39;)</code>
콤마,을 기준으로 값들을 구분하므로 REGEXP_REPLACE 함수 실행 전에 <code>||&#39;,&#39;</code>를 통해 문자열 마지막에 콤마, 추가해 준 뒤에 원하는 형식의 정규표현식 + 콤마,를 찾을문자로 지정하고, 콤마,를 변환할 문자로 지정해 실행</li>
</ul>
</li>
</ul>
<hr>
<blockquote>
<ul>
<li>정규표현식 개념
<a href="https://ko.wikipedia.org/wiki/%EC%A0%95%EA%B7%9C_%ED%91%9C%ED%98%84%EC%8B%9D">https://ko.wikipedia.org/wiki/%EC%A0%95%EA%B7%9C_%ED%91%9C%ED%98%84%EC%8B%9D</a></li>
</ul>
</blockquote>
<ul>
<li>정규표현식 활용 <a href="https://gent.tistory.com/267">https://gent.tistory.com/267</a></li>
<li>TRIM() <a href="https://gent.tistory.com/521">https://gent.tistory.com/521</a>
tip) R/LTRIM() 이용하면 공백과 문자 한번에 제거 가능</li>
</ul>
<ul>
<li>참고) 와일드카드 %는 WHERE절 LIKE하고만 사용할 수 있는 것 같다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Oracle) Project) UNION 사용 시 ORDER BY 정렬 방법]]></title>
            <link>https://velog.io/@jiu_lee/Oracle-Project-UNION-%EC%82%AC%EC%9A%A9-%EC%8B%9C-ORDER-BY-%EC%A0%95%EB%A0%AC-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@jiu_lee/Oracle-Project-UNION-%EC%82%AC%EC%9A%A9-%EC%8B%9C-ORDER-BY-%EC%A0%95%EB%A0%AC-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 28 Nov 2022 11:07:47 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>UNION 할 SELECT 쿼리에 ORDER BY절이 포함되어 있으면 에러 발생한다.</p>
<blockquote>
<p><a href="https://h5bak.tistory.com/78">https://h5bak.tistory.com/78</a></p>
</blockquote>
<pre><code class="language-sql">SELECT * FROM

(

SELECT NO, NAME, TEL, ADDR FROM A_TABLE A

UNION

SELECT NO, NAME, TEL, ADDR FROM B_TABLE B

)

ORDER BY A.NO, B.NO;</code></pre>
<p>와 같이 UNION 쿼리를 인라인뷰로 하고, 각 SELECT절에 ALIAS 지정해 각 테이블의 컬럼들을 기준으로 정렬하면 된다....
고 하는데 나는 왜인지 안되서 다른 방법을 찾아보았다..</p>
</li>
<li><p><strong>정렬을 위한 컬럼 추가해 메인쿼리에서 정렬하는 방법</strong></p>
<blockquote>
<p><a href="https://stackoverflow.com/questions/6036793/sql-how-to-use-union-and-order-by-a-specific-select">https://stackoverflow.com/questions/6036793/sql-how-to-use-union-and-order-by-a-specific-select</a></p>
</blockquote>
<pre><code class="language-sql">select * from 
(
  SELECT id, 2 as ordered FROM a -- returns 1,4,2,3
  UNION
  SELECT id, 1 as ordered FROM b -- returns 2,1
)
order by ordered</code></pre>
<p>정렬용 컬럼을 하나 추가해 같은 이름으로 ALIAS 지정하고 메인쿼리 ORDER BY절에서 최우선 기준으로 정렬한다.</p>
<ul>
<li><strong>개인 프로젝트 예제)</strong>
검색 시 영화명, 감독명, 출연 배우명 순으로 검색 결과를 출력하고, UNION할 각 쿼리는 movie 테이블의 updateDate 컬럼을 기준으로 우선 정렬하고자 했다.</li>
</ul>
</li>
<li><blockquote>
<p>검색 페이지에서 더보기 버튼이나 무한 스크롤 등을 통해 추가적으로 검색 결과 리스트를 append해 나갈 것이기 때문에 ORDER BY절 작성 필수. 언제나 똑같은 순서로 출력되어야한다.</p>
</blockquote>
<p>```sql
SELECT iv.*
FROM ( 
   SELECT 1 AS ordered, m_mn.updateDate, m_mn.movieCd, m_mn.movieNm, m_mn.prdtYear, m_mn.repGenreNm, m_mn.repNationNm, m_mn.directorDetail,  m_mn.posterUrl
   FROM tbl_movie m_mn
   WHERE REPLACE(LOWER(m_mn.movieNm),&#39; &#39;,&#39;&#39;) LIKE REPLACE(&#39;%병헌%&#39;,&#39; &#39;,&#39;&#39;) </p>
<pre><code>   OR REPLACE(LOWER(m_mn.movieNmEn),&#39; &#39;,&#39;&#39;) LIKE REPLACE(&#39;%병헌%&#39;,&#39; &#39;,&#39;&#39;)</code></pre></li>
<li><ul>
<li>ORDER BY m_mn.updateDate DESC<br>UNION
SELECT 2 AS ordered, m_d.updateDate, m_d.movieCd, m_d.movieNm, m_d.prdtYear, m_d.repGenreNm, m_d.repNationNm, m_d.directorDetail, m_d.posterUrl
FROM tbl_movie m_d
WHERE REPLACE(LOWER(m_d.directorDetail),&#39; &#39;,&#39;&#39;) LIKE REPLACE(&#39;%병헌%&#39;,&#39; &#39;,&#39;&#39;) </li>
</ul>
</li>
<li><ul>
<li>ORDER BY m_d.updateDate DESC
UNION
SELECT 3 AS ordered, m_a.updateDate, m_a.movieCd, m_a.movieNm, m_a.prdtYear, m_a.repGenreNm, m_a.repNationNm, m_a.directorDetail,  m_a.posterUrl
FROM tbl_movie m_a
WHERE REGEXP_REPLACE(m_a.actorDetail||&#39;,&#39; ,&#39;|.<em>?,&#39;, &#39;,&#39;) LIKE REPLACE(&#39;%병헌%&#39;,&#39; &#39;,&#39;&#39;) 
/</em> REPACE 함수와 REGEXP_REPACE 함수 다르다. / 와일드카드 % 사용 불가 <em>/
/</em> 정규표현식: 특수문자쓰고싶으면 앞에 \ 붙여야함, \ : OR, .* : 모든문자, ?:0또는 1회 포함
-&gt; 배우명|영문명|역할, 배우명||역할, 배우명|영문명|, 배우명|, 모두 커버 */</li>
</ul>
</li>
<li><ul>
<li>ORDER BY m_a.updateDate DESC
) iv
ORDER BY iv.ordered ASC, iv.updateDate DESC<pre><code>![](https://velog.velcdn.com/images/jiu_lee/post/4e328ad0-6bcd-4506-93f0-4fd93a22861d/image.png)
</code></pre></li>
</ul>
<p>=&gt; 인라인뷰의 UNION한 각 쿼리는 별도로 ORDER BY 정렬하지 않고 정렬용 컬럼만 추가 조회한 뒤, 메인 쿼리에서 정렬용 컬럼을 기준으로 먼저 정렬하고 그 뒤에 원하는 기준 컬럼을 작성</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Oracle) NVL(), NVL2() 를 이용한 MyBatis Null 체크 조건문 대체]]></title>
            <link>https://velog.io/@jiu_lee/Oracle-NVL-NVL2-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-MyBatis-Null-%EC%B2%B4%ED%81%AC-%EC%A1%B0%EA%B1%B4%EB%AC%B8-%EB%8C%80%EC%B2%B4</link>
            <guid>https://velog.io/@jiu_lee/Oracle-NVL-NVL2-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-MyBatis-Null-%EC%B2%B4%ED%81%AC-%EC%A1%B0%EA%B1%B4%EB%AC%B8-%EB%8C%80%EC%B2%B4</guid>
            <pubDate>Sun, 27 Nov 2022 08:39:56 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>그동안 Mybatis) IF 태그나 CHOOSE - WHEN 태그 또는 Oracle) CASE WHEN THEN ELSE END를 이용해 변수의 Null 여부 체크를 하였는데
이럴 필요 없이 NVL, NVL2 함수를 이용하면 직관적으로 이를 대체할 수 있음을 깨달았다.
selectKey의 keyProperty 변수를 이용해 Null 체크를 해야한다거나 하는, MyBatis 조건문 태그를 사용할 수 없는 경우에 유용할 것 같다.</p>
<blockquote>
<p><a href="http://www.gurubee.net/article/66445">http://www.gurubee.net/article/66445</a></p>
</blockquote>
<pre><code class="language-sql">UPDATE tbl_people 
      SET 
          repMovieList = #{repMovieList}, 
          repMovieListDate = NVL2(#{repMovieList}, SYSDATE, NULL)
&lt;!--             &lt;if test=&quot;repMovieList != null&quot;&gt;문법에러 발생 --&gt;
&lt;!--             &lt;if test=&quot;#{repMovieList} != null&quot;&gt;에러발생하지 않으나 원하는결과x.문법오류같음 --&gt;
&lt;!--             &lt;if test=&quot;#{repMovieList != null} &quot;&gt;에러발생하지 않으나 원하는결과x.문법오류같음 --&gt;
&lt;!--                 repMovieListDate = SYSDATE --&gt;
&lt;!--             &lt;/if&gt; --&gt;

  &lt;!--         repMovieListDate = #{repMovieListDate} --&gt;
      WHERE peopleCd = #{peopleCd}    </code></pre>
<ul>
<li>유의) 대부분의 경우엔 Null 체크와 &#39;&#39;빈값 여부를 함께 체크하므로 이땐 조건문을 이용할 수 밖에 없겠다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Oracle) Project) 값 리스트를 특수문자로 구분해 변환한 문자열에서 특정 값의 순서를 구하는 방법 : REGEXP_INSTR(), REGEXP_COUNT()]]></title>
            <link>https://velog.io/@jiu_lee/Oracle-Project-%EA%B0%92-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A5%BC-%ED%8A%B9%EC%88%98%EB%AC%B8%EC%9E%90%EB%A1%9C-%EA%B5%AC%EB%B6%84%ED%95%B4-%EB%B3%80%ED%99%98%ED%95%9C-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%97%90%EC%84%9C-%ED%8A%B9%EC%A0%95-%EA%B0%92%EC%9D%98-%EC%88%9C%EC%84%9C%EB%A5%BC-%EA%B5%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-INSTR-REGEXPCOUNT</link>
            <guid>https://velog.io/@jiu_lee/Oracle-Project-%EA%B0%92-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A5%BC-%ED%8A%B9%EC%88%98%EB%AC%B8%EC%9E%90%EB%A1%9C-%EA%B5%AC%EB%B6%84%ED%95%B4-%EB%B3%80%ED%99%98%ED%95%9C-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%97%90%EC%84%9C-%ED%8A%B9%EC%A0%95-%EA%B0%92%EC%9D%98-%EC%88%9C%EC%84%9C%EB%A5%BC-%EA%B5%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-INSTR-REGEXPCOUNT</guid>
            <pubDate>Sun, 27 Nov 2022 04:19:39 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>특정 값들이 포함된 배열을 ,와 같은 특수문자로 구분하여 하나의 문자열로 변환해 사용하는 경우 빈번하다. 이때 특정 값이 이 문자열 내에서 몇번째 순서(즉, 배열 내 index값)인지 구해야 하는 경우가 있다.
이때 문자열을 다시 배열로 바꿔 특정 값의 index를 구하는 것이 아니라 DB 내에서 쿼리 작성을 통해 해결해야할 때가 있다.</p>
</li>
<li><blockquote>
<p>Oracle의 문자열에서 특정 문자열의 index 반환해주는 REGEXP_INSTR() 함수와 문자열에서 특정 문자열의 개수를 반환해주는 REGEXP_COUNT() 함수 이용</p>
</blockquote>
<blockquote>
</blockquote>
<ul>
<li>REGEXP_INSTR() <a href="https://lnsideout.tistory.com/entry/ORACLE-%EC%98%A4%EB%9D%BC%ED%81%B4-INSTR-%EB%AC%B8%EC%9E%90-%EC%9C%84%EC%B9%98-%EC%B0%BE%EA%B8%B0-%EB%B0%8F-%EC%98%88%EC%A0%9C">https://lnsideout.tistory.com/entry/ORACLE-%EC%98%A4%EB%9D%BC%ED%81%B4-INSTR-%EB%AC%B8%EC%9E%90-%EC%9C%84%EC%B9%98-%EC%B0%BE%EA%B8%B0-%EB%B0%8F-%EC%98%88%EC%A0%9C</a></li>
<li>REGEXP_COUNT <a href="https://gent.tistory.com/427">https://gent.tistory.com/427</a></li>
</ul>
</li>
</ul>
<ul>
<li><p>ex) 특정 영화에 출연한 배우의 정보를  배우의 비중에 따라 순서대로 <code>배우명|배우영문명|극중역할,배우명|배우영문명|극중역할,...</code> 과 같은 형식으로 저장한 문자열에서 특정 배우의 순서(중요도)를 숫자값으로 출력하고자 한다.</p>
<ul>
<li><p>REGEXP_INSTR(&#39;문자열&#39;,&#39;찾을문자열&#39;,&#39;시작위치&#39;,&#39;발생횟수&#39;)
: &#39;문자열&#39;에서 &#39;찾을문자열&#39;의 위치를 &#39;시작위치&#39;에서부터 찾기 시작해 &#39;발생횟수&#39;번째 &#39;찾을문자열&#39;의 위치값을 반환</p>
</li>
<li><p>REGEXP_COUNT(&#39;문자열&#39;,&#39;찾을문자열&#39;)
: &#39;문자열&#39;에서 &#39;찾을문자열&#39;의 총 개수를 반환</p>
</li>
</ul>
<pre><code class="language-sql">SELECT ft.peopleNm, ft.movieCd, ft.movieNm
   ,ft.commaCnt, ft.peopleIdx
   /* ---------------------------------------------------------- */
   ,ft.commaCnt - REGEXP_COUNT(ft.actorDetail, &#39;,&#39;, ft.peopleIdx) +1 AS actorOrder
   /* ---------------------------------------------------------- */
   ,ft.actorDetail
FROM (
   SELECT iv2.peopleNm, m.movieCd, m.movieNm
       /* ---------------------------------------------------------- */
       , REGEXP_INSTR(m.actorDetail, iv2.peopleNm) AS peopleIdx
       , REGEXP_COUNT(m.actorDetail, &#39;,&#39;) AS commaCnt
       /* ---------------------------------------------------------- */
       , m.actorDetail
   from (
       SELECT iv1.peopleCd, iv1.peopleNm, regexp_substr(iv1.actorFilmos, &#39;[^,]+&#39;, 1, level) actorFilmo
       from (
           SELECT p.peopleCd, p.peopleNm, p.actorFilmos from tbl_people p where p.peoplecd= &#39;10019067&#39;
       ) iv1
       CONNECT BY regexp_substr(iv1.actorFilmos, &#39;[^,]+&#39;, 1, level) IS NOT NULL
       /* CONNECT BY는 WHERE 이전에 실행되므로 FROM절이 단일행이 아니면 전체 FULL SCAN하여 연삭속도 극히느려짐 */
   ) iv2
   INNER JOIN tbl_movie m
   ON (iv2.actorFilmo = m.movieCd)
) ft
WHERE ft.peopleIdx != 0 /* INSTR()은 찾고자 하는 문자열 없으면 0 반환 */
;</code></pre>
<ul>
<li>구하는 순서
1) REGEXP_COUNT 함수를 이용해 특수문자(,)의 총 개수 구하기
2) REGEXP_INSTR 함수 이용해 찾고자 하는 문자열의 위치 구하기
3) REGEXP_COUNT 함수 이용해 2)에서 구한 찾고자하는 문자열의 위치부터 특수문자(,)의 개수 구하기
4) 찾고자하는 문자열의 순서(1부터) : 특수문자 총 개수 - 찾고자하는 문자열의 위치부터의 특수문자 개수 + 1</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/ab99c8e7-bad0-4d63-9a96-46fba38340fc/image.png" alt=""></p>
<hr>
<ul>
<li><p>*<em>시행착오) *</em>
동일한 값을 얻기 위해 이전엔 한 컬럼의 문자열을 특정 문자로 나누어 행으로 정렬해주는 REGEXP_SUBSTR() 함수를 두번 이용해 같은 과정 수행했었다...
똑같이 한 배우의 필모를 REGEXP_SUBSTR 함수로 정렬해 리스트 값 반환받고,
이를 다시 파라미터값으로 전달해 필모의 출연배우 컬럼을 REGEXP_SUBSTR 함수로 나누어 그때의 ROWNUM 값을 리턴받음..
REGEXP_SUBSTR 함수는 단일행 인라인뷰를 사용하지않으면 연산속도 극히 느려지므로 Service 계층에서 반복문 돌려서 한 영화마다 쿼리를 실행해야했다..)</p>
<pre><code class="language-sql">&lt;select id=&quot;getActorFilmoList&quot; resultType=&quot;com.moviepedia.domain.FavoritePeopleDTO&quot;&gt;
        SELECT ft.peopleNm, m.movieCd, m.actorDetail  /*ft.peopleCd , ft.actorFilmo, m.movieCd, */ 
        FROM (
            SELECT iv.peopleCd, iv.peopleNm, regexp_substr(iv.actorFilmos, &#39;[^,]+&#39;, 1, level) actorFilmo
            FROM (
                SELECT p.peopleCd, p.peopleNm, p.actorFilmos FROM tbl_people p WHERE p.peoplecd= #{peopleCd}
            ) iv
            CONNECT BY REGEXP_SUBSTR(iv.actorFilmos, &#39;[^,]+&#39;, 1, LEVEL) IS NOT NULL
            /* CONNECT BY는 WHERE 이전에 실행되므로 FROM절이 단일행이 아니면 전체 FULL SCAN하여 연삭속도 극히느려짐 */
        ) ft
        INNER JOIN tbl_movie m
        ON (ft.actorFilmo = m.movieCd)
    &lt;/select&gt;

    &lt;select id=&quot;getActorRoleImportanceValue&quot; resultType=&quot;Integer&quot;&gt;

        SELECT ft.rank AS actorRoleImportanceValue, ft.actorRole
        FROM (
            SELECT ROWNUM AS rank, REGEXP_SUBSTR(iv.actorDetail, &#39;[^,]+&#39;, 1, LEVEL) AS actorRole
            FROM (
                SELECT m.actorDetail
                FROM tbl_movie m
                WHERE movieCd =#{movieCd}
            ) iv    
            CONNECT BY    REGEXP_SUBSTR(iv.actorDetail, &#39;[^,]+&#39;, 1, LEVEL) IS NOT NULL
            ORDER BY rank ASC
        ) ft
        WHERE REGEXP_INSTR(ft.actorRole, #{peopleNm}) &gt; 0 AND ROWNUM = 1

    &lt;/select&gt;</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Oracle) Column 합치기 : ||, CONCAT() /  Row 합치기 : LISTAGG()]]></title>
            <link>https://velog.io/@jiu_lee/Oracle-Column-%ED%95%A9%EC%B9%98%EA%B8%B0-CONCAT-Row-%ED%95%A9%EC%B9%98%EA%B8%B0-LISTAGG</link>
            <guid>https://velog.io/@jiu_lee/Oracle-Column-%ED%95%A9%EC%B9%98%EA%B8%B0-CONCAT-Row-%ED%95%A9%EC%B9%98%EA%B8%B0-LISTAGG</guid>
            <pubDate>Sat, 26 Nov 2022 03:21:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>Column 합치기 <a href="https://gent.tistory.com/256">https://gent.tistory.com/256</a></li>
</ul>
</blockquote>
<ul>
<li>Row 합치기 <a href="https://subbak2.com/26">https://subbak2.com/26</a></li>
</ul>
<pre><code class="language-sql">--SELECT ft.movieCd ||&#39;|&#39;|| ft.movieNm  AS repMovie
/* -------------------------------------------------------------------------------------------- */
SELECT LISTAGG(ft.movieCd ||&#39;|&#39;|| ft.movieNm, &#39;,&#39;) WITHIN GROUP (ORDER BY ROWNUM) AS repMovieList
    /* LISTAGG : ROWNUM으로 정렬한 것 인지. ft에서 최종 정렬한대로 출력하기 위함 */
/* -------------------------------------------------------------------------------------------- */
FROM (
    SELECT ROWNUM, iv4.* 
    FROM (
        SELECT iv3.peopleNm, iv3.updateDate, iv3.movieCd, iv3.movieNm
            ,iv3.commaCnt - REGEXP_COUNT(iv3.actorDetail, &#39;,&#39;, iv3.peopleIdx) +1 AS actorOrder
            ,iv3.cntStarRating, iv3.avgStarRating
            ,iv3.actorDetail
        FROM (
            SELECT iv2.peopleNm, iv2.updateDate
                , m.movieCd, m.movieNm, m.actorDetail
                , REGEXP_INSTR(m.actorDetail, iv2.peopleNm) AS peopleIdx
                , REGEXP_COUNT(m.actorDetail, &#39;,&#39;) AS commaCnt  
                , COUNT(s.starRating)  AS cntStarRating
                , ROUND(AVG(s.starRating) , 1) AS avgStarRating
            FROM (
                SELECT iv1.peopleCd, iv1.peopleNm, iv1.updateDate, REGEXP_SUBSTR(iv1.actorFilmos, &#39;[^,]+&#39;, 1, level) actorFilmo
                FROM (
                    SELECT p.peopleCd, p.peopleNm, p.actorFilmos, p.updateDate 
                    FROM tbl_people p 
                    WHERE p.peopleCd= #{peopleCd}
                ) iv1
                CONNECT BY REGEXP_SUBSTR(iv1.actorFilmos, &#39;[^,]+&#39;, 1, level) IS NOT NULL
                /* CONNECT BY는 WHERE 이전에 실행되므로 FROM절이 단일행이 아니면 전체 FULL SCAN하여 연삭속도 극히느려짐 */
            ) iv2
            INNER JOIN tbl_movie m
            ON (iv2.actorFilmo = m.movieCd)
            LEFT OUTER JOIN tbl_starRating s
            ON (m.movieCd = s.movieCd)
            GROUP BY iv2.peopleNm, iv2.updateDate, m.movieCd, m.movieNm, m.actorDetail
        ) iv3
        WHERE iv3.peopleIdx != 0 /* INSTR()은 찾고자 하는 문자열 없으면 0 반환 */
        ORDER BY actorOrder ASC /* 비중 기준 정렬 */
    ) iv4
    WHERE ROWNUM BETWEEN 1 AND 10 /* 비중 높은 출연작 10개 먼저 선정 */
    ORDER BY iv4.cntStarRating DESC, iv4.avgStarRating DESC, iv4.updateDate ASC /* 이후, 정렬 */
) ft
WHERE ROWNUM BETWEEN 1 AND 2</code></pre>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/bec0b4cf-38a0-4e98-9051-4694a356bb5f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/8c2d98e4-c937-4c39-b421-ac5308c8821c/image.png" alt=""></p>
<ul>
<li>참고) LISTAGG()에서 ROWNUM을 기준으로 ORDER BY 했다는 사실 유념할 것.</li>
<li><blockquote>
<p>서브쿼리에서 ORDER BY했던 정렬 순서대로 row 합치기 위함</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[jQuery) event.target -> jQuery 객체로서 사용]]></title>
            <link>https://velog.io/@jiu_lee/JSJquery-event.target-%EC%9D%B4%EC%9A%A9</link>
            <guid>https://velog.io/@jiu_lee/JSJquery-event.target-%EC%9D%B4%EC%9A%A9</guid>
            <pubDate>Fri, 25 Nov 2022 05:20:09 GMT</pubDate>
            <description><![CDATA[<ul>
<li>event.target 요소는 말그대로 HTML 태그 요소이므로 jQuery 객체로 사용하려면 $(event.target)과 같이 $()로 감싸야 jQuery 객체로서 메서드 사용할 수 있게됨</li>
</ul>
<pre><code class="language-javascript">// 바깥 화면 클릭 시 drop 요소 숨기기
    $(&quot;html&quot;).on(&quot;click&quot;, function(e){
        // ?) stopPropagation() 이벤트 전파 방지가 제대로 작동하지 않아 document 대신 html 태그 지정

        console.log(this);
        console.log($(this));
        console.log(e.target);
        console.log(&quot;e.target: &quot; + e.target);
        console.log(&quot;e.target: &quot;, e.target);
        console.log($(e.target));
        console.log(&quot;$(e.target): &quot; + $(e.target));
        console.log(&quot;$(e.target): &quot;, $(e.target));
        console.log(e.currentTarget);
        console.log($(e.currentTarget));

        if($(e.target).attr(&quot;id&quot;) != &quot;search&quot; 
                   &amp;&amp; $($(e.target).closest(&quot;#searchDrop&quot;)[0]).attr(&quot;id&quot;) != &quot;searchDrop&quot;) {
             searchDropUL.hide();
        };

    });</code></pre>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/070b5775-347c-48dd-a22e-e8463d34beb3/image.png" alt=""></p>
<blockquote>
<p><a href="https://jmjmjm.tistory.com/134">https://jmjmjm.tistory.com/134</a></p>
</blockquote>
<ul>
<li><p>this / event.target / event.currentTarget
this = event.currentTarget : 이벤트 생성 위치 --&gt; event.target : 이벤트 실제 발생 위치
위 코드에선 html 태그에 이벤트 걸었기 때문에 this와 event.currentTarget이 html 태그인 것. 실제 클릭은 input 태그이므로 event.target은 input 태그 요소</p>
</li>
<li><p>tip) console.log(&#39;문자열&#39; + 변수)과 같이 작성하면 변수가 [object Object]처럼 자세히 출력되지 않음</p>
</li>
<li><blockquote>
<p>console.log(&#39;문자열&#39;, 변수)와 같이 작성하면 원하는대로 출력됨</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS) 요소 내부 텍스트 넘칠 경우 생략(...)]]></title>
            <link>https://velog.io/@jiu_lee/JS-%EC%9A%94%EC%86%8C-%EB%82%B4%EB%B6%80-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%84%98%EC%B9%A0-%EA%B2%BD%EC%9A%B0-%EC%83%9D%EB%9E%B5</link>
            <guid>https://velog.io/@jiu_lee/JS-%EC%9A%94%EC%86%8C-%EB%82%B4%EB%B6%80-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%84%98%EC%B9%A0-%EA%B2%BD%EC%9A%B0-%EC%83%9D%EB%9E%B5</guid>
            <pubDate>Thu, 24 Nov 2022 06:36:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://webruden.tistory.com/655">https://webruden.tistory.com/655</a>
<a href="https://devbirdfeet.tistory.com/140">https://devbirdfeet.tistory.com/140</a></p>
</blockquote>
<ul>
<li>요소 내부의 텍스트가 길어질 경우 이를 생략할 필요가 있다.
그 방법으론 크게 한 줄/ 두 줄 이상의 경우로 나뉨</li>
<li><blockquote>
<p>white-space 속성 값과 width/height 중 무엇을 고정할지가 핵심</p>
</blockquote>
</li>
</ul>
<pre><code class="language-css">/* 한줄 */
.movieList .movie-Name {
    font: bold 17px ;
    width: 100%;
    overflow: hidden;          
    white-space: nowrap;
    text-overflow: ellipsis; 


} 

/* 두 줄 이상 */
.commentsList .slide-inner .commentContents{
    height : 192px;
    white-space: normal; /* 줄바꿈 */
    overflow: hidden; /* 요소 크기 넘어가는 경우 표시 허용 여부 */
    text-overflow :ellipsis; /* 넘어간 텍스트 ...로 대체 */
    word-wrap : brek-word; 
    display: -webkit-box;
    -webkit-line-clamp:12; 
    -webkit-box-orient:vertical;
}</code></pre>
<ul>
<li>참고) 두 줄 이상에서 line-clamp 속성값의 줄에서 ... 표시한다는 것이지 그 이후의 텍스트가 생략되지 않음(height 값 적절히 조정해서 이하의 텍스트 보이지 않도록 조절)</li>
<li><blockquote>
<p>더 세련된 방법이 있지않을까..</p>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/6a329e7c-9762-4c13-9be6-99fa43c41f92/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/2ed4f5c0-8f25-41f2-a867-669d9066c94c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS) alert(경고창), confirm(선택창), prompt(입력창)]]></title>
            <link>https://velog.io/@jiu_lee/JS-alert%EA%B2%BD%EA%B3%A0%EC%B0%BD-confirm%EC%84%A0%ED%83%9D%EC%B0%BD-prompt%EC%9E%85%EB%A0%A5%EC%B0%BD</link>
            <guid>https://velog.io/@jiu_lee/JS-alert%EA%B2%BD%EA%B3%A0%EC%B0%BD-confirm%EC%84%A0%ED%83%9D%EC%B0%BD-prompt%EC%9E%85%EB%A0%A5%EC%B0%BD</guid>
            <pubDate>Thu, 24 Nov 2022 02:51:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://coding-factory.tistory.com/198">https://coding-factory.tistory.com/198</a></p>
</blockquote>
<ul>
<li>alert는 단순 알림창으로 확인 누를 경우에 실행할 이벤트 지정할 수 없음</li>
<li><blockquote>
<p>confirm은 확인/취소 버튼 클릭에 따른 true/false 반환하므로 페이지 이동 등의 이벤트 지정 가능</p>
</blockquote>
</li>
</ul>
<pre><code class="language-javascript">// 미로그인 사용자
var confirmResult = confirm(&quot;로그인 후 좋아요 기능을 이용할 수 있습니다. \n 로그인 페이지로 이동하시겠습니까?&quot;);
if(confirmResult){
  location.href= &#39;/login&#39;;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS) 페이지 이동: location.replace()/ location.href/ history.back()/.forward()]]></title>
            <link>https://velog.io/@jiu_lee/JS-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99.-replace-href-history-%EA%B0%9D%EC%B2%B4.back-.forward</link>
            <guid>https://velog.io/@jiu_lee/JS-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99.-replace-href-history-%EA%B0%9D%EC%B2%B4.back-.forward</guid>
            <pubDate>Wed, 23 Nov 2022 11:30:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://blogpack.tistory.com/592">https://blogpack.tistory.com/592</a>
<a href="https://coding-restaurant.tistory.com/192">https://coding-restaurant.tistory.com/192</a></p>
</blockquote>
<p>href는 일반적인 페이지 이동을 의미. 기본적으로 href 사용하는 것이 원칙(웹브라우저에 히스토리 남기므로).
replace는 현재 페이지를 새로운 페이지로 덮어씌우기 때문에 replace를 사용한 다음에는 이전 페이지로 돌아갈 수 없음. -&gt; 보안 상 뒤로가기를 불허할 때 사용</p>
<pre><code class="language-javascript">/* 페이지 접근 시 코멘트 개수 0개인 경우 유저 정보 페이지로 이동 */
    if($(&quot;.commentLi&quot;).length == 0){
        alert(&quot;작성한 코멘트가 없습니다. 유저 페이지로 이동합니다.&quot;);

//         history.back(-1);
//        location.href = &quot;/users/&quot;+ userRandomString;
        location.replace(&quot;/users/&quot;+ userRandomString);

    }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[jQuery) Ajax 비동기 함수 async 설정. 이벤트 결과 return받을 때까지 브라우저 정지]]></title>
            <link>https://velog.io/@jiu_lee/jQuery-Ajax-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-async-%EC%84%A4%EC%A0%95.-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B2%B0%EA%B3%BC-return%EB%B0%9B%EC%9D%84-%EB%95%8C%EA%B9%8C%EC%A7%80-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%A0%95%EC%A7%80</link>
            <guid>https://velog.io/@jiu_lee/jQuery-Ajax-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-async-%EC%84%A4%EC%A0%95.-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B2%B0%EA%B3%BC-return%EB%B0%9B%EC%9D%84-%EB%95%8C%EA%B9%8C%EC%A7%80-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%A0%95%EC%A7%80</guid>
            <pubDate>Wed, 23 Nov 2022 05:29:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://qnfmfmd.tistory.com/408">https://qnfmfmd.tistory.com/408</a></p>
</blockquote>
<ul>
<li>Ajax(비동기 통신) 호출 반복 사용 시 기본적으로 return(호출 응답) 기다리지 않고 다음 호출 실행함
ex) 반복문 실행 시 반복문 내의 Ajax의 결과를 기다리지 않고 다음 index에 대해 실행하므로 원하는 결과 얻지 못할 수 있음.</li>
<li><blockquote>
<p>순차적 실행을 위해 즉시 실행함수로 감싸거나(추가 학습 필요) async 설정 false로 변경(return받을때까지 브라우저 정지)</p>
</blockquote>
</li>
<li><blockquote>
<p>아예 JSTL의 foreach 태그 이용</p>
</blockquote>
</li>
</ul>
<pre><code class="language-javascript">$.ajaxSetup({ async: false });

또는

각 ajax 메서드에서
async: false 
지정</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JSTL) 현재 날짜 / 날짜, 시간 계산]]></title>
            <link>https://velog.io/@jiu_lee/JSTL-%ED%98%84%EC%9E%AC-%EB%82%A0%EC%A7%9C-%EB%82%A0%EC%A7%9C-%EC%8B%9C%EA%B0%84-%EA%B3%84%EC%82%B0</link>
            <guid>https://velog.io/@jiu_lee/JSTL-%ED%98%84%EC%9E%AC-%EB%82%A0%EC%A7%9C-%EB%82%A0%EC%A7%9C-%EC%8B%9C%EA%B0%84-%EA%B3%84%EC%82%B0</guid>
            <pubDate>Wed, 23 Nov 2022 04:52:03 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>fmt:parseNumber 태그 이용. value의 값에 .time 지정하면 
Wed Nov 23 13:50:29 KST 2022 과 같은 날짜(문자열 타입)이
1669179029871 과 같은 시작일 이후 ms 단위 숫자값으로 변환됨을 이용</p>
<blockquote>
<p><a href="https://sowon-dev.github.io/2022/08/18/220818JSTL-date/">https://sowon-dev.github.io/2022/08/18/220818JSTL-date/</a></p>
</blockquote>
<pre><code class="language-html">&lt;jsp:useBean id=&quot;now&quot; class=&quot;java.util.Date&quot;/&gt;
&lt;fmt:parseNumber value=&quot;${now.time / (1000*60)}&quot; var=&quot;nowfmtTime&quot;/&gt;&lt;!-- .time 필수 --&gt;
&lt;fmt:parseNumber value=&quot;${commentVO.commentDate.time / (1000*60)}&quot; var=&quot;commentDatefmtTime&quot;/&gt;&lt;!-- .time 필수 --&gt;
&lt;fmt:parseNumber value=&quot;${nowfmtTime - commentDatefmtTime}&quot; var=&quot;timeDefference&quot;/&gt;
&lt;c:choose&gt;
&lt;c:when test=&quot;${timeDefference &lt;= 10}&quot;&gt;&lt;!-- 10분 이하 --&gt;
  방금 전
&lt;/c:when&gt;
&lt;c:when test=&quot;${timeDefference &gt; 10 &amp;&amp; timeDefference &lt;= 60}&quot;&gt;&lt;!-- 1시간 이하 --&gt;
  &lt;fmt:parseNumber value=&quot;${timeDefference}&quot; integerOnly=&quot;true&quot; var=&quot;timeDefference&quot;/&gt;    
  ${timeDefference }분 전
&lt;/c:when&gt;
&lt;c:when test=&quot;${timeDefference &gt; 60 &amp;&amp; timeDefference &lt;= 60*24}&quot;&gt;&lt;!-- 24시간 이하 --&gt;
  &lt;fmt:parseNumber value=&quot;${timeDefference / 60}&quot; integerOnly=&quot;true&quot; var=&quot;timeDefference&quot;/&gt;
  ${timeDefference }시간 전
&lt;/c:when&gt;
&lt;c:when test=&quot;${timeDefference &gt; 60*24 &amp;&amp; timeDefference &lt;= 60*24*30}&quot;&gt;&lt;!-- 30일 이하 --&gt;
  &lt;fmt:parseNumber value=&quot;${timeDefference / (60*24)}&quot; integerOnly=&quot;true&quot; var=&quot;timeDefference&quot;/&gt;
  ${timeDefference }일 전
&lt;/c:when&gt;
&lt;c:when test=&quot;${timeDefference &gt; 60*24*30 &amp;&amp; timeDefference &lt;= 60*24*365}&quot;&gt;&lt;!-- 1년 이하 --&gt;
  &lt;fmt:parseNumber value=&quot;${timeDefference / (60*24*30)}&quot; integerOnly=&quot;true&quot; var=&quot;timeDefference&quot;/&gt;
  ${timeDefference }월 전
&lt;/c:when&gt;
&lt;c:when test=&quot;${timeDefference &gt; 60*24*365}&quot;&gt;
  &lt;fmt:parseNumber value=&quot;${timeDefference / (60*24*365)}&quot; integerOnly=&quot;true&quot; var=&quot;timeDefference&quot;/&gt;
  ${timeDefference }년 전
&lt;/c:when&gt;
&lt;/c:choose&gt;    </code></pre>
<p>참고. 한달 전의 기준을 임의로 30일로 정함. 28,30,31과 같이 구체적으로 정하려면 해당일의 달을 기준으로하는 추가적인 코드 작성 필요</p>
</li>
</ul>
<hr> 

<ul>
<li><p><strong>시간을 0으로 변경 후 날짜만으로 계산</strong></p>
<blockquote>
<p><a href="https://sowon-dev.github.io/2022/11/02/221103JSP-utilDatefmt/">https://sowon-dev.github.io/2022/11/02/221103JSP-utilDatefmt/</a></p>
</blockquote>
<ul>
<li>project) 박스오피스 개봉일 출력 시 시간 제외하고 날짜만으로 계산 필요</li>
</ul>
<pre><code class="language-html">
&lt;!-- 개봉일 
           https://sowon-dev.github.io/2022/11/02/221103JSP-utilDatefmt/ --&gt;
&lt;jsp:useBean id=&quot;now&quot; class=&quot;java.util.Date&quot;/&gt;
&lt;fmt:formatDate var=&quot;today&quot; value=&quot;${now }&quot; pattern=&quot;yyyyMMdd000000&quot;/&gt;    
&lt;fmt:parseDate var=&quot;nowfmt&quot; value=&quot;${today }&quot; pattern=&quot;yyyyMMddHHmmss&quot;/&gt;

&lt;fmt:parseNumber value=&quot;${nowfmt.time}&quot; var=&quot;nowfmtTime&quot;/&gt;&lt;!-- .time 필수 --&gt;
&lt;fmt:parseNumber value=&quot;${boxOfficeWithStar.openDt.time}&quot; var=&quot;openDtfmtTime&quot;/&gt;&lt;!-- .time 필수 --&gt;
&lt;fmt:parseNumber value=&quot;${(nowfmtTime - openDtfmtTime) / (1000*60*60*24)}&quot; var=&quot;timeDefference&quot;/&gt;

&lt;c:choose&gt;
  &lt;c:when test=&quot;${timeDefference &gt;= 0}&quot;&gt;
    &lt;div&gt;개봉 ${timeDefference +1 }일째&lt;/div&gt;    
  &lt;/c:when&gt;
  &lt;c:otherwise&gt;
    &lt;div&gt;개봉 ${timeDefference +1 }일전&lt;/div&gt;
  &lt;/c:otherwise&gt;            
&lt;/c:choose&gt;        </code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Error) Spring 406 에러]]></title>
            <link>https://velog.io/@jiu_lee/Error-Spring-406-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@jiu_lee/Error-Spring-406-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Tue, 22 Nov 2022 12:11:31 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@0_sujeong/error-spring-ajax-return-406%EC%97%90%EB%9F%AC">https://velog.io/@0_sujeong/error-spring-ajax-return-406%EC%97%90%EB%9F%AC</a></p>
<blockquote>
<p>스프링에서 ajax를 통해 통신을 하려면 컨트롤러에서는 json 응답으로 클라이언트에게 넘겨줘야하는데, 1. 라이브러리가 없어서거나 2. 웹페이지에서 개발자도구를 봤을때 응답이 json타입이 아닌 text/html타입이기 때문이다.</p>
</blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/3a210cbe-dcd2-416e-b198-fd485a702fc6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/64e70904-569b-4670-b581-85a66e1a8178/image.png" alt=""></p>
<pre><code class="language-java">// 좋아요 삭제
    @DeleteMapping(value = &quot;/rest/likes/delete&quot;,
            consumes = &quot;application/json&quot;,
//            produces = {MediaType.TEXT_PLAIN_VALUE})
            produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
    public ResponseEntity&lt;Integer&gt; remove(@RequestBody LikeVO likeVO){
//        public int remove(@RequestBody LikeVO likeVO){

        log.info(&quot;remove likeVO: &quot; + likeVO);
        Integer likeCnt = service.remove(likeVO);
//        return  == 1
//                ? new ResponseEntity&lt;String&gt;(&quot;success&quot;, HttpStatus.OK)
//                : new ResponseEntity&lt;String&gt;(HttpStatus.INTERNAL_SERVER_ERROR);
        return new ResponseEntity&lt;Integer&gt;(likeCnt, HttpStatus.OK);</code></pre>
<p>Ajax 사용 시@Controller 메서드의 produces 타입을 JSON으로 해줘야 하는데 text 등으로 지정해서 에러 발생</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[trialNerror) Error) java.lang.NoClassDefFoundError: org/springframework/core/log/LogMessage at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:126) ]]></title>
            <link>https://velog.io/@jiu_lee/trialNError-Error-java.lang.NoClassDefFoundError-orgspringframeworkcorelogLogMessage-at-org.springframework.security.web.csrf.CsrfFilter.doFilterInternalCsrfFilter.java126</link>
            <guid>https://velog.io/@jiu_lee/trialNError-Error-java.lang.NoClassDefFoundError-orgspringframeworkcorelogLogMessage-at-org.springframework.security.web.csrf.CsrfFilter.doFilterInternalCsrfFilter.java126</guid>
            <pubDate>Fri, 18 Nov 2022 07:00:36 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>Ajax Post 방식 전송 시 아래와 같이 Ajax 전송 시 CSRF 토큰을 함께 전달하기 위한 코드를 작성한다.</p>
<pre><code class="language-javascript">var csrfHeaderName = &quot;${_csrf.headerName}&quot;;
  var csrfTokenValue = &quot;${_csrf.token}&quot;

  /* 38.5.3 Ajax로 CSRF 토큰 전송 방식
      : ajaxSend() 이용 시 모든 Ajax 전송 시 CSRF 토큰 같이 전송하도록 되어있음 */
  $(document).ajaxSend(function(e, xhr, options){
      xhr.setRequestHeader(csrfHeaderName, csrfTokenValue);
  });</code></pre>
</li>
<li><p>그런데 똑같은 Ajax 전송 메서드를 각기 다른 JSP에서 실행했는데 한 페이지에서만 정상적으로 동작하지 않는 문제가 발생하였다. (500에러 발생. 상기 AjaxSend 코드를 타지 못함.. )
  -&gt; 궁여지책으로 Ajax 전송 시 beforeSend 속성 작성을 통해 해당 Ajax 코드에 직접 csrf 토큰 정보를 전달하여 해결..
  =&gt; 노트북 재부팅하니까 잘 됨.. 또 이러면 프로젝트 clean, 서버 clean 필수적으로 먼저 해보자..</p>
<pre><code class="language-text">java.lang.NoClassDefFoundError: org/springframework/core/log/LogMessage at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:126) </code></pre>
<pre><code class="language-javascript">var like = {
                  commentCd : commentCdValue,
                  movieCd: movieCdValue,
                  userid : userid,
                  csrfHeaderName: csrfHeaderName,
                  csrfTokenValue: csrfTokenValue
              }

              likeService.add(like, function(result){
                  alert(result);

              });</code></pre>
<pre><code class="language-javascript">/* 좋아요 등록 메서드 */
   function add(like, callback){
       console.log(&quot;like.............&quot;);

       // add 메서드 내에 ajax 이용해 post방식으로 호출하는 코드
       $.ajax({
           type: &#39;post&#39;,
           url: &#39;/rest/likes/new&#39;,
           data: JSON.stringify(like),
           contentType: &quot;application/json; charset=utf-8&quot;,
           beforeSend : function(xhr){
              xhr.setRequestHeader(like.csrfHeaderName, like.csrfTokenValue);
          },
           success: function(result, status, xhr) {

               if(callback) {

                   callback(result);
               }    
           },
           error : function(xhr, status, err) {
               if(err) {
                   error(err);
                   console.log(&quot;e&quot;);
              }
           }         
       })
   } // add(reply, callback)</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[trialNerror) MyBatis) 조회 데이터 -> List에 정상적으로 담기지 않는 경우 - NVL() / 그룹함수 조회 & resultMap 사용 시]]></title>
            <link>https://velog.io/@jiu_lee/trialNerror-MyBatis-NVL-List%EC%97%90-%EC%A0%95%EC%83%81%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%8B%B4%EA%B8%B0%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B2%BD%EC%9A%B0</link>
            <guid>https://velog.io/@jiu_lee/trialNerror-MyBatis-NVL-List%EC%97%90-%EC%A0%95%EC%83%81%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%8B%B4%EA%B8%B0%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B2%BD%EC%9A%B0</guid>
            <pubDate>Tue, 15 Nov 2022 06:18:59 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>사례1) SELECT 쿼리 출력할 때 null 값 변환을 위해 NVL()를 사용하였는데 SQL 실행 결과 시엔 정상적으로 출력되었으나 List에 모든 행이 제대로 담기지 않았다.</p>
</li>
<li><blockquote>
<p>데이터 타입의 문제라 생각해 TO_NUMBER(), CAST() 등의 Oracle 메서드를 사용해보았으나 해결안됨.
=&gt; 어차피 null값이 java 변수에 담기면 각각의 기본값으로 변환되므로 서브쿼리에서 null값을 계산해야하는 경우가 아닌 메인 SELECT절에선 NVL()(그리고 다른 문자함수) 사용 지양하는 걸로.</p>
</blockquote>
<ul>
<li>cause) 박스오피스에 해당하는 영화가 API 호출했지만 얻지 못해 출력되지않은 것임을 알게되었다.. tbl_movie 테이블에 없었음.. 예상했던 상기 부분들이 원인 아닐수도..</li>
</ul>
</li>
<li><blockquote>
<p>일단 LEFT OUTER JOIN tbl_movie m 으로 쿼리 수정하고 호출 API 개선하기로함</p>
</blockquote>
<pre><code class="language-java">@Data
public class BoxOfficeWithStarDTO {

   private BoxOfficeVO boxOfficeVO;

   private String posterUrl;
   private int prdtYear;
   private String repNationNm;

   private double avgStarRating;

   private double userStarRating;
</code></pre>
</li>
</ul>
<p>}</p>
<pre><code>  ```java
  public List&lt;BoxOfficeWithStarDTO&gt; getBoxOfficeWithStarList(@Param(&quot;showDate&quot;) String showDate, @Param(&quot;userid&quot;) String userid);</code></pre><pre><code class="language-sql">  &lt;resultMap type=&quot;com.moviepedia.domain.BoxOfficeWithStarDTO&quot; id=&quot;boxOfficeWithStarDTO&quot;&gt;

        &lt;result property=&quot;avgStarRating&quot; column=&quot;avgstarrating&quot;/&gt;
        &lt;result property=&quot;userStarRating&quot; column=&quot;userstarrating&quot;/&gt;

        &lt;result property=&quot;posterUrl&quot; column=&quot;posterurl&quot;/&gt;
        &lt;result property=&quot;prdtYear&quot; column=&quot;prdtyear&quot;/&gt;
        &lt;result property=&quot;repNationNm&quot; column=&quot;repnationnm&quot;/&gt;


        &lt;association property=&quot;boxOfficeVO&quot; javaType=&quot;com.moviepedia.domain.BoxOfficeVO&quot;&gt;
            &lt;id property=&quot;showDate&quot; column=&quot;showdate&quot;/&gt;
            &lt;id property=&quot;movieCd&quot; column=&quot;moviecd&quot;/&gt;

            &lt;result property=&quot;rank&quot; column=&quot;rank&quot;/&gt;
            &lt;result property=&quot;movieNm&quot; column=&quot;movienm&quot;/&gt;
            &lt;result property=&quot;openDt&quot; column=&quot;opendt&quot;/&gt;
            &lt;result property=&quot;audiCnt&quot; column=&quot;audicnt&quot;/&gt;
            &lt;result property=&quot;audiAcc&quot; column=&quot;audiacc&quot;/&gt;
            &lt;result property=&quot;updateDate&quot; column=&quot;updatedate&quot;/&gt;

        &lt;/association&gt;
    &lt;/resultMap&gt;


      &lt;select id=&quot;getBoxOfficeWithStarList&quot; resultMap=&quot;boxOfficeWithStarDTO&quot;&gt;

        SELECT DISTINCT b.rank, b.movieCd, b.movieNm, b.openDt, b.audiAcc, b.showDate
            , m.posterUrl, m.prdtYear, m.repNationNm
&lt;!--             , NVL(ROUND(AVG(s.starRating) OVER(PARTITION BY s.movieCd),1), 0) AS avgStarRating --&gt;
            , ROUND(AVG(s.starRating) OVER(PARTITION BY s.movieCd),1) AS avgStarRating
            &lt;!-- ?) NVL(, 0(or 0.0)) 이용 시 List에 제대로 안담김. 몇몇 행만 담김... to_number, cast도 안됨 
                =&gt; 반환되는 데이터 타입의 문제 아닐까? 
                : 어차피 DTO엔 0.0으로 담기므로 할 필요없음.. --&gt;
             &lt;if test=&quot;userid neq null&quot;&gt; 
                , (SELECT ss.starRating FROM tbl_starRating ss WHERE ss.userid = #{userid} AND ss.movieCd = b.movieCd) AS userStarRating
             &lt;/if&gt;
        FROM tbl_boxOffice b
        JOIN tbl_movie m
        ON (b.movieCd = m.movieCd)
        LEFT OUTER JOIN tbl_starRating s
        ON (b.movieCd = s.movieCd)
        WHERE b.showDate = #{showDate} 
        ORDER BY rank ASC

    &lt;/select&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/jiu_lee/post/e66f84be-7900-4974-9a36-3e63aca9d72d/image.png" alt=""></p>
<ul>
<li>사례2) 사례1)과 같이 SELECT절에 AVG(), COUNT() 등의 그룹함수 포함해 조회 &amp; resultmap(collection, association) 사용 시 List에 제대로 담기지 않음.. 도대체 왜? 사례1)의 코드는 NVL() 미사용시 정상적으로 수행됨... </li>
<li><blockquote>
<p>그룹 함숨를 조회하지 않았더니 정상적으로 List에 담김</p>
</blockquote>
</li>
<li><blockquote>
<p>resultMap이 아닌 resultType 사용 시 정상적으로 List에 담김
=&gt; DTO에 VO나 다른 DTO를 변수로 하여 resultMap을 이용하지 말고 VO나 다른 DTO의 필요한 변수만 뽑아 새로운 DTO 생성해 resultType 이용하자..</p>
</blockquote>
</li>
</ul>
<pre><code class="language-java">  @Data
  public class DisplayMovieDTO {

      private MovieVO movieVO;

      private double avgStarRating;
      private double userStarRating;

  }</code></pre>
<pre><code class="language-java">  @Data
  public class DisplayMovieDTO2 {

      private String movieCd;
      private String movieNm;
      private int prdtYear;
      private String repNationNm;
      private String posterUrl;

      private double avgStarRating;
      private double userStarRating;

  }</code></pre>
<pre><code class="language-sql">      &lt;resultMap type=&quot;com.moviepedia.domain.DisplayMovieDTO&quot; id=&quot;displayMovieDTO&quot;&gt;

          &lt;result property=&quot;avgStarRating&quot; column=&quot;avgstarrating&quot; /&gt;
          &lt;result property=&quot;userStarRating&quot; column=&quot;userstarrating&quot; /&gt;

          &lt;association property=&quot;movieVO&quot; javaType=&quot;com.moviepedia.domain.MovieVO&quot;&gt;

              &lt;id property=&quot;movieCd&quot; column=&quot;moviecd&quot;/&gt;

              &lt;result property=&quot;movieNm&quot; column=&quot;movienm&quot;/&gt;
              &lt;result property=&quot;movieNmEn&quot; column=&quot;movieNmEn&quot;/&gt;
              &lt;result property=&quot;prdtYear&quot; column=&quot;prdtyear&quot;/&gt;
              &lt;result property=&quot;openDt&quot; column=&quot;openDt&quot;/&gt;
              &lt;result property=&quot;typeNm&quot; column=&quot;typeNm&quot;/&gt;
              &lt;result property=&quot;prdtStatNm&quot; column=&quot;prdtStatNm&quot;/&gt;
              &lt;result property=&quot;nationAlt&quot; column=&quot;nationAlt&quot;/&gt;
              &lt;result property=&quot;genreAlt&quot; column=&quot;genreAlt&quot;/&gt;
              &lt;result property=&quot;repNationNm&quot; column=&quot;repNationNm&quot;/&gt;
              &lt;result property=&quot;repGenreNm&quot; column=&quot;repGenreNm&quot;/&gt;
              &lt;result property=&quot;companyCd&quot; column=&quot;companyCd&quot;/&gt;
              &lt;result property=&quot;companyNm&quot; column=&quot;companyNm&quot;/&gt;
              &lt;result property=&quot;directorDetail&quot; column=&quot;directorDetail&quot;/&gt;
              &lt;result property=&quot;showTm&quot; column=&quot;showTm&quot;/&gt;
              &lt;result property=&quot;actorDetail&quot; column=&quot;actorDetail&quot;/&gt;
              &lt;result property=&quot;watchGradeNm&quot; column=&quot;watchGradeNm&quot;/&gt;
              &lt;result property=&quot;posterUrl&quot; column=&quot;posterUrl&quot;/&gt;
              &lt;result property=&quot;storyText&quot; column=&quot;storyText&quot;/&gt;
              &lt;result property=&quot;updateDate&quot; column=&quot;updateDate&quot;/&gt;
              &lt;result property=&quot;commentCnt&quot; column=&quot;commentCnt&quot;/&gt;


          &lt;/association&gt;
      &lt;/resultMap&gt;


      &lt;select id=&quot;getHighStarRatingMovieList&quot; resultType=&quot;com.moviepedia.domain.DisplayMovieDTO2&quot; &gt;
          SELECT  ft.movieNm, ft.prdtYear, ft.repNationNm, ft.posterUrl, ft.movieCd
              , ft.avgStarRating
          FROM (
              SELECT  iv.movieCd, iv.movieNm, iv.prdtYear, iv.repNationNm, iv.posterUrl
                  , ROUND(AVG(s.starRating), 1) AS avgStarRating
                  , COUNT(s.starRating) AS cntStarRating 
              FROM (
                  SELECT m.movieCd, m.movieNm, m.prdtYear, m.repNationNm, m.posterUrl
                  FROM tbl_movie m
                  WHERE m.movieCd NOT IN (
                      SELECT movieCd
                      FROM tbl_starRating s
                      WHERE userid = #{userid})
              ) iv 
              INNER JOIN tbl_starRating s
              /* INNER JOIN : 다른 유저들이 평가한 영화 중에서 출력 */ 
              ON (iv.movieCd = s.movieCd)
              GROUP BY iv.movieCd, iv.movieNm, iv.prdtYear, iv.repNationNm, iv.posterUrl
              ORDER BY avgStarRating DESC, cntStarRating DESC
          ) ft
          WHERE ROWNUM BETWEEN 1 AND 20    


      &lt;/select&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Error) AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext]]></title>
            <link>https://velog.io/@jiu_lee/Error-AuthenticationCredentialsNotFoundException-An-Authentication-object-was-not-found-in-the-SecurityContext</link>
            <guid>https://velog.io/@jiu_lee/Error-AuthenticationCredentialsNotFoundException-An-Authentication-object-was-not-found-in-the-SecurityContext</guid>
            <pubDate>Fri, 04 Nov 2022 01:41:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://stackoverflow.com/questions/3087548/can-spring-security-use-preauthorize-on-spring-controllers-methods">https://stackoverflow.com/questions/3087548/can-spring-security-use-preauthorize-on-spring-controllers-methods</a></p>
<blockquote>
<p>Yes, it works fine.
You need in . It also requires CGLIB proxies, so either your controllers shouldn&#39;t have interfaces, or you should use 
.&lt;security:global-method-security pre-post-annotations=&quot;enabled&quot; /&gt;...-servlet.xmlproxy-target-class = true
//
Generally we would recommend applying method security at the service layer rather than on individual web controllers.</p>
</blockquote>
</blockquote>
<ul>
<li><p>@PreAuthorize/@PostAuthorize를 @Controller에서 사용하는 것에 대해.
: Controller단에서 사용하는 것은 권장하지 않으나 servlet-context에서 설정함으로서 가능하긴함.</p>
</li>
<li><p>Error) AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
: login URL에 접근해 로그인할 때 존재하지 않는계정, 비밀번호 오류, 계정 잠김 등의 에러 발생 시 CustomLoginFailureHandler를 타고 정상 동작하던 것이 어느날 갑자기 위와 같은 에러를 발생시켰다.</p>
</li>
<li><blockquote>
<p>login URL가 Mapping된 메서드에 지정한 @PreAuthorize를 지우고
security-context에서 intercept-url 태그를 이용하니 에러 해결됨.</p>
</blockquote>
<pre><code class="language-java">@RequestMapping(value = &quot;/login&quot;, method = {RequestMethod.GET ,RequestMethod.POST})
//    @PreAuthorize(&quot;isAnonymous()&quot;)
   public String Login(HttpServletRequest request, HttpServletResponse response,
           @RequestParam(value = &quot;error&quot;, required = false) boolean error) {</code></pre>
<pre><code class="language-xml">&lt;security:http auto-config=&quot;true&quot; use-expressions=&quot;true&quot; &gt;        
 ...
   &lt;security:intercept-url pattern=&quot;/login&quot; access=&quot;isAnonymous()&quot;/&gt;
 ...
&lt;/security:http&gt;</code></pre>
</li>
</ul>
<p>=&gt; 결론. @Controller에서 어노테이션을 이용해 권한 부여를 하는 것은 간편하고 직관적이지만 적절하진 않은 것 같다. login과 같이 Spring Security와 충돌이 염려되는 URL에 대해서는 security-context에서 권한 부여하는 것으로 하자.</p>
]]></description>
        </item>
    </channel>
</rss>