<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>boram.log</title>
        <link>https://velog.io/</link>
        <description>안녕하세요, 한보람입니다.</description>
        <lastBuildDate>Sun, 11 Jun 2023 18:55:30 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>boram.log</title>
            <url>https://velog.velcdn.com/images/boram_han/profile/0b9ee981-5ec4-4215-91c0-6d11990ef5ec/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. boram.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/boram_han" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Cs Project] 동기 vs 비동기]]></title>
            <link>https://velog.io/@boram_han/Cs-Project-%EB%8F%99%EA%B8%B0-vs-%EB%B9%84%EB%8F%99%EA%B8%B0</link>
            <guid>https://velog.io/@boram_han/Cs-Project-%EB%8F%99%EA%B8%B0-vs-%EB%B9%84%EB%8F%99%EA%B8%B0</guid>
            <pubDate>Sun, 11 Jun 2023 18:55:30 GMT</pubDate>
            <description><![CDATA[<h1 id="동기-vs-비동기">동기 vs 비동기</h1>
<p><img src="https://velog.velcdn.com/images/boram_han/post/764e5f64-37c3-4bfb-ba92-852161e28386/image.JPG" alt=""></p>
<h2 id="동기-비동식-방식을-알아야-하는-이유">동기 비동식 방식을 알아야 하는 이유</h2>
<ul>
<li>자바스크립트는 기본적으로 동기처리를 하기 때문에 결과가 오래 걸리는 코드를 작성시 결과가 나오기 전까지 아무것도 출력하지 않음</li>
<li>결과를 출력하기 전까지 &quot;출력 중&quot;이라는 문구정도는 나올 수 있게 하기 위해서 비동기 처리</li>
<li>이 외에도 동기 비동기 방식은 여러가지 알아두면 최적화에 도움이 많이 되기 때문에 꼭 알아야 함<h2 id="동기식">동기식</h2>
<img src="https://velog.velcdn.com/images/boram_han/post/efae881a-a1bc-4124-bf2d-d9520d3df7e4/image.JPG" alt=""></li>
<li><strong>동기식</strong> : 클라이언트가 서버에게 요청을 보내면 다른 작업을 처리하지 않고 요청을 우선적으로 수행하는것을 의미</li>
<li>사진처럼 두 줄에 요청과 응답 존재, <ul>
<li>말 그대로 클라이언트가 서버에게 요청을 보내면 응답을 받기 전까진 아무런 처리 x </li>
<li>클라이언트가 응답을 받게 되면 그때가 되서야 다른 요청을 처리하는 것을 동기식 즉 동기라고 함</li>
</ul>
</li>
<li>이처럼 동기는 요청을 보내고 응답을 받는다 라는 전제</li>
<li><strong>장단점</strong><ul>
<li>장점 : 응답의 순서를 보장</li>
<li>단점<ul>
<li>응답이 지연된다면 무작정 기다리게 됨</li>
<li>응답이 지연되면 뒤에 들어오는 요청들은 연결가능한 쓰레드가 없어서 연결을 하지 못해 발생하는 여러 이슈가 생김</li>
</ul>
</li>
</ul>
</li>
<li><strong>사용 예</strong> <ul>
<li>채팅 프로그램 <ul>
<li>사용자가 입력한 메시지를 서버로 전송하고, 서버는 메시지를 다른 사용자에게 전달</li>
<li>메시지를 서버에 전송하고 서버는 다른 사용자에게 전송 이라는 방식 사용하므로 동기</li>
</ul>
</li>
<li>웹 서버<ul>
<li>클라이언트로부터 요청을 받아 처리하는데 각각의 요청은 순차적으로 처리</li>
</ul>
</li>
<li>계산기 프로그램<ul>
<li>입력값을 받고 연산하고 결과를 반환하는 구조</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="비동기식">비동기식</h2>
<ul>
<li>동기식은 응답이 지연되도 계속 기다린다는 단점 존재 -&gt; 이 부분을 보완한 방식이 비동기식</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boram_han/post/46e46951-b69b-4585-9cb4-9b865c9bf00c/image.JPG" alt=""></p>
<ul>
<li><strong>비동기식</strong> <ul>
<li>클라이언트가 요청을 보내고나서 서버의 응답을 언제 받아도 상관 없다는 뜻으로 응답을 기다리지 않는 상태 </li>
<li>응답을 기다리지 않으니 순서도 보장하지 않습니다.</li>
</ul>
</li>
<li>사진처럼 두 줄에 요청과 응답 존재, <ul>
<li>클라이언트가 요청을 보내고 나서 응답을 기다리지 않고 다음 요청을 보내거나 작업 진행</li>
</ul>
</li>
<li><strong>장단점</strong><ul>
<li>장점 : 클라이언트가 요청을 보내고 나서 응답을 기다리지 않으니 다른 일을 할 수가 있어 효율이 좋음</li>
<li>단점<ul>
<li>처리 결과를 보장받아야 하는 서비스에는 적합하지 않음</li>
<li>결과를 확인하기 위해서는 콜백함수가 필요하니 복잡</li>
</ul>
</li>
</ul>
</li>
<li><strong>사용 예</strong> <ul>
<li>웹 애플리케이션, 네트워크 애플리케이션, 게임서버<ul>
<li>많은 사용자들이 동시에 접속해 요청</li>
<li>이 요청의 처리 속도를 높이기 위해 비동기식 방식을 이용</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="블로킹-vs-논블로킹">블로킹 vs 논블로킹</h1>
<ul>
<li>동기/비동기를 잘 이해하기 위해서는 블로킹/논블로킹의 개념도 알아야 함</li>
<li>블로킹, 논블로킹은 제어권을 건내주느냐 안주느냐로 구분<blockquote>
<p>제어권 : 행동할 수 있는 권리</p>
</blockquote>
</li>
</ul>
<h2 id="블로킹">블로킹</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/d5aa3cb3-5b05-4d38-a9aa-d61c81b06fb4/image.JPG" alt=""></p>
<ul>
<li><strong>블로킹</strong> : 사진 속 두 개의 함수 참고<ul>
<li>A 함수가 B 함수를 호출하면서 제어권도 B 함수에 넘겨줌</li>
<li>제어권을 넘겨받은 B는 함수를 실행하고 A는 B에게 제어권을 넘겨주었기 때문에 함수 실행을 잠시 멈춤</li>
<li>B함수는 실행이 끝나면 자신을 호출한 A에게 제어권과 B함수 결과를 돌려주고 있음</li>
</ul>
</li>
</ul>
<h2 id="논블로킹">논블로킹</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/1f3f0ebc-dfcc-41fa-8dd7-d1c4f60598dd/image.JPG" alt=""></p>
<ul>
<li><strong>논블로킹</strong> : 사진 속 두 개의 함수 참고<ul>
<li>A함수가 B함수를 호출해도 제어권은 그대로 자신이 갖고 있음</li>
<li>A함수가 B함수를 호출하면, B 함수는 실행되지만, 제어권은 A 함수가 그대로</li>
<li>A함수는 계속 제어권을 가지고 있기 때문에 B함수를 호출한 이후에도 자신의 코드를 계속 실행</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>B 함수의 결과값은 어디로 갈까요?</strong> -&gt; 조합에서 확인 가능</p>
</blockquote>
<h1 id="조합">조합</h1>
<p><img src="https://velog.velcdn.com/images/boram_han/post/73204fb5-9386-481e-9a47-7c295242e509/image.JPG" alt=""></p>
<ul>
<li><strong>논블로킹</strong><ul>
<li><strong>동기</strong><ul>
<li>완료될 때 까지 진행상황을 확인하기 위해 호출</li>
<li>완료값을 확인 후 완료값 사용</li>
</ul>
</li>
<li><strong>비동기</strong><ul>
<li>완료되는 시점이 중요 x</li>
<li>하라고 냅두고 콜백으로 필요할 때 사용할 뿐</li>
</ul>
</li>
</ul>
</li>
<li><strong>블로킹</strong> : 두 개 다 파란함수는 결과가 반환될 때까지 대기<ul>
<li><strong>동기</strong><ul>
<li>반환값을 기반으로 다음 함수를 진행</li>
</ul>
</li>
<li><strong>비동기</strong><ul>
<li>호출 함수로 반환값을 받지만 그 값을 바로 이용할 지 안할지는 알 수 없음</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>즉,</strong> </p>
</blockquote>
<ul>
<li>블로킹 / 논블로킹:  <strong>한 작업이 처리되는 동안 다른 작업도 처리될 수 있는지</strong></li>
<li>동기 / 비동기 : 작업들이 <strong>결과값을 순서</strong>대로 실행하는지 아닌지에 대해 나타냄</li>
</ul>
<h1 id="조합-예시">조합 예시</h1>
<h2 id="동기-블로킹">동기-블로킹</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/a0f92026-4eb1-4bdc-b0e6-9b0eb3706c41/image.JPG" alt=""></p>
<ul>
<li><p><strong>파란 박스</strong></p>
<ul>
<li>파란함수는 초록함수의 리턴값을 필요로 하는 동기식이고 그에 따라 파란함수에서 초록함수를 호출하고 제어권을 넘겨줌</li>
<li>그렇게 제어권을 초록함수에 넘겨주고 초록함수가 실행을 완료해서 리턴값과 제어권을 돌려줄때까지 파란함수는 기다리게 되는 동기 블로킹 방식</li>
</ul>
</li>
<li><p><strong>예시</strong></p>
<ul>
<li>코드를 보면 teacher함수를 실행하게 되면 선생님이 입실하는 메시지가 뜨고 student함수가 실행</li>
<li>이때 teacher함수는 student함수가 리턴값과 제어권을 돌려줄때까지 대기</li>
<li>그리고 student에 for문으로 문제푸는 메시지를 반복해서 출력합니다.</li>
<li>student함수가 종료되서야 다시 teacher함수가 실행되고 선생님이 퇴실하는 메시지가 뜸</li>
</ul>
</li>
</ul>
<blockquote>
<p>teacher는 student함수가 리턴할때까지 기다리는 동기구조 및
블로킹 방식이 되기 때문에 동기 블로킹 방식 성립</p>
</blockquote>
<h2 id="동기-논블로킹">동기-논블로킹</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/958f3d19-61ce-4952-aae9-8cc161f4cfb0/image.JPG" alt="">
<img src="https://velog.velcdn.com/images/boram_han/post/895b25b3-77d1-4168-bd32-f0234e4d783a/image.gif" alt=""></p>
<ul>
<li><p><strong>노란박스</strong></p>
<ul>
<li>파란함수는 초록함수를 호출</li>
<li>이 때 파란함수는 초록함수에게 제어권을 주지 않고, 자신의 코드를 계속 실행함으로서 논블로킹 방식</li>
<li>그런데 파란함수는 초록함수의 리턴값이 필요하기 때문에, 중간중간 초록함수에게 함수 실행을 완료했는지 물어봄으로써 동기식 구조</li>
</ul>
</li>
<li><p><strong>영상 참고</strong> : 퇴근시간 조회 버튼을 누르게 되면 남은 퇴근시간을 초 단위로 보여줌</p>
<ul>
<li>버튼을 누르면 체크타임 함수를 불러오고</li>
<li>이 체크타임 함수는 셋팅된 퇴근시간과 현재시간을 빼 남은시간을 초단위로 불러옴</li>
</ul>
</li>
</ul>
<blockquote>
<p>파란함수를 가상의 A 직원이 하는 업무, 그리고 퇴근시간을 조회하는 이 코드를 초록함수로 가정</p>
</blockquote>
<ul>
<li>A 직원은 자신의 업무 하면서 퇴근시간이 얼마나 남았는지 계속 알고 싶으니 초록함수를 수시로 확인</li>
<li>초록함수가 완료가 되면 퇴근시간이므로 A직원의 업무도 끝</li>
</ul>
<blockquote>
<p>즉, </p>
</blockquote>
<ul>
<li>직원은 완료 전에 업무를 계속하고 있으니 퇴근 시간을 조회하는 초록함수에게 제어권을 주지 않기에 논블로킹 방식</li>
<li>중간중간 수시로 퇴근시간이 얼마나 남았는지 확인함으로서 동기식 구조를 완성</li>
</ul>
<h2 id="비동기-블로킹">비동기-블로킹</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/8a44349f-288d-49c2-98fa-12edd86ff9f7/image.JPG" alt=""></p>
<ul>
<li><strong>빨간박스</strong><ul>
<li>파란함수는 초록함수의 리턴값에 신경쓰지 않고, 콜백함수를 보내서 비동기 방식</li>
<li>그런데, 초록함수의 작업에 관심없음에도 불구하고, 파란함수는 초록함수에게 제어권을 넘겨서 블로킹 방식</li>
<li>그래서 파란함수는 자신과 관련 없는 초록함수의 작업이 끝날 때까지 기다려야 함</li>
</ul>
</li>
<li><strong>예시</strong><ul>
<li>직원이 출근 후 본인 일을 시작하고 작업진행 중 상사에게 서류를 제출</li>
<li>직원은 서류를 제출하고 그 서류가 상사에 의해 검토 완료 될 때까지 직원은 본인 일을 할 수 없음</li>
<li>제출한 서류의 검토가 끝나고 메일로 결과를 통보 받은 뒤에야 직원은 본인일을 마저 마칠 수 있음</li>
</ul>
</li>
<li><strong>예시 코드</strong><ul>
<li>employee함수로 진행중에 manager라는 함수를 불러옴</li>
<li>employee함수는 대기, manager함수가 진행</li>
<li>manager함수는 i값이 100이 될때까지 계속해서 진행하고 i값이 100보다 커지면 검토 완료라는 메시지와 함께 manager함수는 종료</li>
<li>manager함수가 종료되고 나서야 employee함수가 나머지 작업을 끝내고 서류검토를 완료했다는 코드</li>
</ul>
</li>
</ul>
<blockquote>
<p> 비동기 블로킹의 방식의 경우 동기식 블로킹과 차이가 거의 없어 사용 x</p>
</blockquote>
<h2 id="비동기---논블로킹">비동기 - 논블로킹</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/a3dfe820-67a1-4b4c-9948-b9dcf3f65ff1/image.JPG" alt=""></p>
<ul>
<li><p><strong>초록박스</strong></p>
<ul>
<li>파란함수가 초록함수를 호출할 때 제어권을 초록함수에 주지 않음으로서 논블로킹 방식</li>
<li>따라서 초록함수를 호출한 이후에도 멈추지 않고 자신의 코드를 계속 실행</li>
<li>초록함수를 호출할 때 콜백 함수를 함께 줌</li>
<li>초록함수는 자신의 작업이 끝나면 파란함수가 준 콜백함수를 실행</li>
</ul>
</li>
<li><p><strong>예시</strong> </p>
<ul>
<li>사원은 본인 작업을 하다가 상사에게 서류를 제출하고 다시 본인 일을 하러 감</li>
<li>상사는 작업을 검토하고 그동안 사원은 계속 자기 할 일</li>
<li>상사는 검토가 끝나면 직원에게 이메일을 보내기만 하면 됨</li>
</ul>
</li>
<li><p><strong>예시 코드</strong></p>
<ul>
<li>employee함수 실행</li>
<li>서류를 제출하면서 manager함수를 호출하지만 employee함수는 제어권을 넘기지 않기 때문에 바로 본인 일을 하러 감</li>
<li>실행된 manager함수를 보면 10마다 진행률을 보여줌</li>
<li>100이 되면 콜백함수를 호출하여 이메일 확인하라는 메세지를 출력하고 클리어인터벌함수로 갱신을 중단</li>
</ul>
</li>
</ul>
<h1 id="마지막-정리">마지막 정리</h1>
<ul>
<li>동기식과 비동기 블로킹과 논블로킹은 비슷하지만 바라보는 관점에서 차이<ul>
<li>동기식과 비동기식은 순서와 결과값의 영향 여부</li>
<li>블로킹과 논블로킹은 제어권의 위치가 이동하는 지 여부</li>
</ul>
</li>
</ul>
<blockquote>
<p>모두 추상적인 개념이라 어떤 메서드를 가지고 이렇다 저렇다 확실하게 나누기는 어렵다!!</p>
</blockquote>
<h3 id="출처">출처</h3>
<p><a href="https://gyoogle.dev/blog/computer-science/network/Blocking,Non-blocking%20&amp;%20Synchronous,Asynchronous.html">https://gyoogle.dev/</a>
<a href="https://homoefficio.github.io/2017/02/19/Blocking-NonBlocking-Synchronous-Asynchronous/">https://homoefficio.github.io/</a>
<a href="https://velog.io/@nittre/%EB%B8%94%EB%A1%9C%ED%82%B9-Vs.-%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EB%8F%99%EA%B8%B0-Vs.-%EB%B9%84%EB%8F%99%EA%B8%B0">https://velog.io/@nittre/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 프로젝트 실전 ② - validation check]]></title>
            <link>https://velog.io/@boram_han/Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%A0%84-validation-check</link>
            <guid>https://velog.io/@boram_han/Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%A0%84-validation-check</guid>
            <pubDate>Mon, 08 May 2023 17:10:16 GMT</pubDate>
            <description><![CDATA[<h1 id="1-name-입력-여부">1. Name 입력 여부</h1>
<h3 id="1-라이브러리-추가">(1) 라이브러리 추가</h3>
<ul>
<li>build.gradle에 다음 코드 추가 후 refresh gradle project<pre><code class="language-java">  implementation &#39;org.springframework.boot:spring-boot-starter-validation&#39;</code></pre>
<h3 id="2-notempty-추가">(2) @NotEmpty 추가</h3>
</li>
<li>객체로 사용할 DTO에 메시지 추가</li>
</ul>
<p><strong>Name입력 필수</strong></p>
<pre><code class="language-java">    @Getter @Setter
    public class MemberForm {

        @NotEmpty(message = &quot;회원 이름은 필수입니다.&quot;)
        private String name;

        private String city;
        private String street;
        private String zipcode;

    }</code></pre>
<h3 id="3-valid-추가">(3) @Valid 추가</h3>
<ul>
<li><p>validation 다음에 binding 있으면 error를 Binding에 담아준다.</p>
<pre><code class="language-java">  @PostMapping(&quot;/members/new&quot;)
  public String create(@Valid MemberForm form, BindingResult result) {
      // result에 에러값 여부
      if( result.hasErrors() ) {
          return &quot;members/createMemberForm&quot;;
      }

      Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
      Member member = new Member();
      member.setName(form.getName());
      member.setAddress(address);

      memberService.join(member);

      return &quot;redirect:/&quot;;
  }</code></pre>
</li>
<li><p>name 입력 ❌ </p>
<ul>
<li>result 객체에 <code>message = &quot;회원 이름은 필수입니다.&quot;</code> 받아서 처리 -&gt; 에러를 담아서 다시 회원가입 페이지 -&gt; input 박스에 에러메세지 담기</li>
</ul>
</li>
<li><p>name 입력 ⭕</p>
<ul>
<li>내용 저장<h3 id="4-input-box에-에러-메세지-담기">(4) input box에 에러 메세지 담기</h3>
<h4 id="①-에러여부에-따라-class명-바꾸기">① 에러여부에 따라 class명 바꾸기</h4>
<pre><code> - 에러 x : &#39;form-control&#39; 
- 에러 o : &#39;form-control fieldError&#39;</code></pre><pre><code class="language-html">&lt;input type=&quot;text&quot; class=&quot;form-control&quot; placeholder=&quot;이름을 입력하세요&quot; th:field=&quot;*{name}&quot; 
       th:class=&quot;${#fields.hasErrors(&#39;name&#39;)}?&#39;form-control fieldError&#39;:&#39;form-control&#39;&quot; /&gt;</code></pre>
<h4 id="②-notemptymessage--회원-이름은-필수입니다불러오기">② @NotEmpty(message = &quot;회원 이름은 필수입니다.&quot;)불러오기</h4>
<pre><code>&lt;p th:if=&quot;${#fields.hasErrors(&#39;name&#39;)}&quot; th:errors=&quot;*{name}&quot;&gt;&lt;/p&gt;</code></pre></li>
</ul>
</li>
<li><p><em>👍 결과*</em></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boram_han/post/d2c59128-5643-40c1-99d9-d1dc34d44d16/image.png" alt=""></p>
<h1 id="2-name-중복-여부">2. Name 중복 여부</h1>
<h3 id="1-service에서-repositorysave가-실행되기전에-메서드-추가">(1) @service에서 repository.save가 실행되기전에 메서드 추가</h3>
<pre><code class="language-java">    @Transactional
    public Long join(Member member){
        // name 중복 체크 메소드 호출
        validationMemberCheck(member);
        memberRepository.save(member);
        return member.getId();
    }</code></pre>
<h3 id="2-메서드-만들기">(2) 메서드 만들기</h3>
<p><strong>@Service</strong></p>
<pre><code class="language-java">    // 호출시켜주는 곳에 예외라고 던져주는 것
    private void validationMemberCheck(Member member) throws IllegalAccessException {
        List&lt;Member&gt; findMembers = memberRepository.findByName(member.getName());

        // findMembers가 비어있어야 정상, 비지 않았으면 exception
        if( !findMembers.isEmpty() ) {
            //exception 객체 생성 후 던지기
            throw new IllegalAccessException(&quot;이미 존재하는 회원입니다.&quot;);
        }
    }</code></pre>
<p><strong>@Repository</strong></p>
<pre><code class="language-java">    // service단에 만들었던 예외 처리
    public List&lt;Member&gt; findByName(String name) throws IllegalAccessException {
        return em.createQuery(&quot;select m from Member where m.name = :name&quot;, Member.class)
                 .setParameter(&quot;name&quot;, name)
                 .getResultList();
    }</code></pre>
<h3 id="3-예외-처리--메서드-메서드-호출하는-곳-서비스랑-연결된-곳">(3) 예외 처리 : 메서드, 메서드 호출하는 곳, 서비스랑 연결된 곳</h3>
<p><strong>@Controller</strong></p>
<pre><code class="language-java">    @PostMapping(&quot;/members/new&quot;)
    public String create(@Valid MemberForm form, BindingResult result) throws IllegalAccessException {
                        ....메소드....
    }</code></pre>
<p><strong>@Service</strong></p>
<pre><code class="language-java">    @Transactional
    public Long join(Member member) throws IllegalAccessException {
                        .... 메소드 ....
    }</code></pre>
<blockquote>
</blockquote>
<p><strong>👍 결과</strong></p>
<ul>
<li>같은 이름 입력하면 was단 -&gt; 500 error 발생</li>
<li>콘솔창 출력
<img src="https://velog.velcdn.com/images/boram_han/post/3f0c39b2-4e77-4452-80db-199e010e0e03/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 프로젝트 실전 ①]]></title>
            <link>https://velog.io/@boram_han/Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%A0%84</link>
            <guid>https://velog.io/@boram_han/Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%A4%EC%A0%84</guid>
            <pubDate>Mon, 08 May 2023 16:30:52 GMT</pubDate>
            <description><![CDATA[<h1 id="1-오류-확인하기---logger">1. 오류 확인하기 - Logger</h1>
<ul>
<li><code>Logger log = LoggerFactory.getLogger(getClass());</code>을 불러오거나</li>
<li>lombok쓰고 있으면 <code>@Slf4j</code>로 대체<pre><code>@Controller
@Slf4j
public class HomeController {
</code></pre></li>
</ul>
<p>//    Logger log = LoggerFactory.getLogger(getClass());</p>
<pre><code>@RequestMapping(&quot;/&quot;)
public String home() {
    log.info(&quot;home controller&quot;); 
    return &quot;home&quot;;    //home.html로 찾아간다.
}</code></pre><p>}</p>
<pre><code>- **콘솔창 출력**
![](https://velog.velcdn.com/images/boram_han/post/150165dc-a524-490b-856f-634fe3f97921/image.png)
&gt; 쉽게 현재 위치 파악 가능

# 2. DTO 객체 사용
- 엔티티로 사용할 DTO 따로 객체로 사용할 DTO 따로 사용

**MemberForm.java**
```java
    @Getter @Setter
    public class MemberForm {

        private String name;
        private String city;
        private String street;
        private String zipcode;

    }</code></pre><p><strong>controller에 객체 그대로 넘길 수 있음</strong></p>
<pre><code class="language-java">    @GetMapping(&quot;/members/new&quot;)
    public String createForm(Model model) {
        model.addAttribute(&quot;memberForm&quot;, new MemberForm());
        return &quot;members/createMemberForm&quot;;
    }</code></pre>
<p><strong>타임리프에서 th:object, th:field 사용</strong></p>
<pre><code class="language-html">        &lt;form role=&quot;form&quot; action=&quot;/members/new&quot; th:object=&quot;${memberForm}&quot; method=&quot;post&quot;&gt;
            &lt;div class=&quot;form-group&quot;&gt;
                &lt;label th:for=&quot;name&quot;&gt;이름&lt;/label&gt; 
                &lt;input type=&quot;text&quot; th:field=&quot;*{name}&quot; class=&quot;form-control&quot; placeholder=&quot;이름을 입력하세요&quot; /&gt;</code></pre>
<h1 id="3-프로젝트에서-jpa-사용">3. 프로젝트에서 JPA 사용</h1>
<ul>
<li><code>EntityManagerFactory</code> 주입<ul>
<li><code>@PersistenceUnit</code>를 사용하면 되긴하나 굳이 사용 ❌</li>
</ul>
</li>
<li><code>EntityManager</code> 불러오기<ul>
<li><code>@PersistenceContext</code>도 굳이 사용 ❌</li>
<li><code>@Autowired</code>가 대신 처리<ul>
<li>대신, <code>private final EntityManager em;</code></li>
<li><strong>final</strong>키워드 필요</li>
</ul>
</li>
<li><strong>final</strong> 키워드가 있는 생성자 : <code>@RequiredArgsConstructor</code>가 생성자 처리 가능</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>👍 결론</strong></p>
</blockquote>
<pre><code class="language-java">    @Repository
    @RequiredArgsConstructor
    public class MemberRepository {
        private final EntityManager em;
    }    </code></pre>
<h1 id="4-service-클래스---transactional">4. Service 클래스 - @transactional</h1>
<h2 id="1-transactional">(1) @transactional</h2>
<ul>
<li>DB와 관련된, 트랜잭션이 필요한 <strong>서비스 클래스</strong> 혹은 <strong>메서드</strong>에 <code>@Transactional</code> 추가<ul>
<li>repository가 잘 안되면 service영역까지 <strong>rollback</strong></li>
<li>즉, <strong>일련의 작업들을 묶어서 하나의 단위로 처리</strong>할 때 </li>
</ul>
</li>
</ul>
<p><strong>import 시 스프링에서 제공하는 것으로 사용</strong></p>
<pre><code>    org.springframework.transaction.annotation.Transactional</code></pre><h2 id="2-transactional-옵션">(2) @transactional 옵션</h2>
<ul>
<li><p><strong>readOnly</strong> = true or false</p>
<ul>
<li>사용 하는 이유<ul>
<li>읽기전용일 때 사용  </li>
<li>비용을 아끼게 되므로 많이사용</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>class차원</strong>에 어노테이션 처리</p>
<ul>
<li><code>@Transactional(readOnly=true)</code></li>
<li><strong>전체 메서드</strong>에 적용</li>
</ul>
</li>
<li><p>class의 메서드 중 <strong>join메서드</strong></p>
<ul>
<li>insert이므로 읽기 전용이면 안됨 </li>
<li>그러면 따로 <code>@Transactional</code>을 써두면 됨<blockquote>
<p>클래스 레벨에서 transactional주는 것과 메서드에서 transaction 하는 것의 차이?</p>
<ul>
<li>그냥 select에 걸어주는 습관 같은 것, insert에만 transactioanl 따로 해주는 것이 관례 </li>
</ul>
</blockquote>
</li>
</ul>
</li>
</ul>
<h1 id="5-embedded-객체-타임리프로-받아오기">5. embedded 객체 타임리프로 받아오기</h1>
<ul>
<li>addresss 객체 통째로 members에 담겨있을 때</li>
</ul>
<pre><code class="language-java">    &lt;tr th:each=&quot;member : ${members}&quot;&gt;
        &lt;td th:text=&quot;${member.id}&quot;&gt;&lt;/td&gt;
        &lt;td th:text=&quot;${member.name}&quot;&gt;&lt;/td&gt;
        &lt;td th:text=&quot;${member.address?.city}&quot;&gt;&lt;/td&gt;
        &lt;td th:text=&quot;${member.address?.street}&quot;&gt;&lt;/td&gt;
        &lt;td th:text=&quot;${member.address?.zipcode}&quot;&gt;&lt;/td&gt;
    &lt;/tr&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] ENUM]]></title>
            <link>https://velog.io/@boram_han/Spring-ENUM</link>
            <guid>https://velog.io/@boram_han/Spring-ENUM</guid>
            <pubDate>Mon, 08 May 2023 15:34:01 GMT</pubDate>
            <description><![CDATA[<h1 id="📋-enum">📋 ENUM</h1>
<h2 id="1-enum이란">(1) ENUM이란</h2>
<ul>
<li><strong>Enumeration Type(열거타입)</strong><ul>
<li>데이터 중에서 요일(월,화,수,목,금,토,일), 계절(봄,여름,가을,겨울) 등과 같이 몇 가지 한정된 값을 갖는 경우</li>
<li>요일, 계절등과 같이 한정된 데이터만을 가지는 타입</li>
</ul>
</li>
<li><strong>Enumeration constant(열거 상수)</strong><ul>
<li>월, 화, 수, 등 열거되는 값</li>
</ul>
</li>
<li>네이밍 방법<ul>
<li><strong>타입 이름</strong><ul>
<li>첫 글자를 대문자로 하여 생성</li>
</ul>
</li>
<li><strong>열거상수</strong><ul>
<li>대문자로 작성</li>
<li>2개의 단어로 연결 되어 있을 때는 <code>_</code>로 연결</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="2-enum의-장점">(2) ENUM의 장점</h2>
<ul>
<li>코드가 <strong>단순</strong>해지며 가독성이 좋아짐</li>
<li>인스턴스 생성과 상속을 방지하여 상수값의 안정성이 보장됨</li>
<li><strong>enum 예약어</strong>를 사용하므로 열거 의도를 분명히 함</li>
</ul>
<h2 id="3-enum과-메모리-구조">(3) ENUM과 메모리 구조</h2>
<ul>
<li><strong>열거 상수</strong>는 상수 각각을 내부적으로 <code>public static final</code> <strong>필드</strong>이면서 <strong>객체</strong>로 제공되도록 함</li>
<li><code>static</code>이 붙어있음<ul>
<li>각각의 상수는 <strong>클래식 변수</strong></li>
<li>클래스로더가 로드 시점에 JVM 메소드 영역에 해당 <strong>클래스 변수들을 항상 상주</strong>시킴</li>
<li>프로그램이 <strong>종료되기 전에는 언제든지 가져다</strong> 쓸 수 있는 주소공간을 확보</li>
</ul>
</li>
</ul>
<h2 id="4-enum-사용">(4) ENUM 사용</h2>
<h3 id="1-단순-열거">1) 단순 열거</h3>
<p><strong>◾ ENUM</strong></p>
<pre><code class="language-java">    public enum Week {

        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;

        public void dayInfo() {
            System.out.println(&quot;dayInfo enum&quot;);
        }
    }</code></pre>
<p><strong>◾ 구현</strong></p>
<pre><code class="language-java">    public static void main(String[] args) {
        Week today = Week.FRIDAY;
        System.out.println(today);

        // enum에서 지정한 메서드 불러오려면 열거상수 먼저 불러와야 함
        Week.MONDAY.dayInfo();
    }</code></pre>
<p><strong>◾ 결과</strong>
<img src="https://velog.velcdn.com/images/boram_han/post/be88719a-7cc4-4357-adfe-846a8eb07ad6/image.png" alt=""></p>
<h3 id="2-이름과-값-부여">2) 이름과 값 부여</h3>
<p><strong>◾ ENUM</strong></p>
<pre><code class="language-java">    public enum Type {

        // static final String WALKING = &quot;워킹화&quot;;
        // -&gt; 보기 편하게 만든 클래스가 ENUM 
        // -&gt; 1.5버전부터 쓰기 시작한 것
        WALKING(&quot;워킹화&quot;),
        RUNNING(&quot;등산화&quot;),
        TRACKING(&quot;트래킹화&quot;),
        HIKING(&quot;등산화&quot;);

        final private String name;

        private Type(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }</code></pre>
<p><strong>◾ 구현</strong></p>
<pre><code class="language-java">    public static void main(String[] args) {
        for( Type type : Type.values()) {
            System.out.println(type.getName());
        }
    }</code></pre>
<p><strong>◾ 결과</strong>
<img src="https://velog.velcdn.com/images/boram_han/post/66dc540a-06f0-4a3b-ab70-10b7b786201e/image.png" alt=""></p>
<h3 id="3-jpa에서-사용">3) JPA에서 사용</h3>
<ul>
<li><code>@Enumerated(EnumType.데이터 타입)</code> 사용</li>
</ul>
<p><strong>◾ ENUM</strong></p>
<pre><code class="language-java">    public enum OrderStatus {
        ORDER, CANCEL;
    }</code></pre>
<p><strong>◾ Order.java</strong> </p>
<pre><code class="language-java">    // 주문상태 (Order, Cancel)
    @Enumerated(EnumType.STRING)
    private OrderStatus status;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] JPQL]]></title>
            <link>https://velog.io/@boram_han/Spring-JPQL</link>
            <guid>https://velog.io/@boram_han/Spring-JPQL</guid>
            <pubDate>Mon, 08 May 2023 15:11:29 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-jpql">💻 JPQL</h1>
<h2 id="1-jpql">1. JPQL</h2>
<ul>
<li>Java Persistence Query Language</li>
<li>JPA가 제공하는 <strong>객체 지향 쿼리 언어</strong><ul>
<li>JPA를 사용하면 엔티티 객체를 중심으로 개발</li>
<li>JPA는 SQL을 추상화환 JPQL이라는 객체 지향 쿼리 언어 제공</li>
<li>JPA는 JPQL을 분석한 후 적절한 SQL을 만들어서 데이터베이스를 조회</li>
</ul>
</li>
<li>SQL vs JQPL<ul>
<li>SQL : 데이터베이스 테이블을 대상으로 쿼리</li>
<li>JQPL : 엔티티 객체를 대상으로 쿼리<ul>
<li>SQL과 문법 유사 -&gt; select, from, where, group by, having, join 지원</li>
<li>SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않음</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>💡 SQL</strong>    </p>
<pre><code class="language-sql">    select * from Member m where m.age&gt;18</code></pre>
<p><strong>💡 JPQL</strong>    </p>
<pre><code class="language-JAVA">    String jpql = &quot;select m from Member m where m.age &gt; 18&quot;; 
    List&lt;Member&gt; result = em.createQuery(jpql, Member.class)
                            .getResultList();</code></pre>
<h2 id="2-jpql-사용하기">2. JPQL 사용하기</h2>
<h3 id="1-사용-기본">(1) 사용 기본</h3>
<h4 id="①-대소문자-구분">① 대소문자 구분</h4>
<ul>
<li>엔티티와 속성은 대소문자를 구분 <ul>
<li>Member -&gt; member ❌, age-&gt; Age ❌</li>
</ul>
</li>
<li>JPQL 키워드는 대소문자 구분 ❌<ul>
<li>SELECT -&gt; select ⭕, WHERE -&gt; where ⭕</li>
</ul>
</li>
</ul>
<h4 id="②-엔티티-이름">② 엔티티 이름</h4>
<ul>
<li>테이블명 대신 엔티티명을 사용<ul>
<li><code>@Entity(name=&quot;&quot;)</code>으로 설정 가능</li>
</ul>
</li>
<li>지정하지 않을 시 클래스 명을 기본값으로 사용<strong>(추천)</strong><h4 id="③-별칭은-필수알리야스">③ 별칭은 필수(알리야스)</h4>
</li>
<li>JPQL은 별칭은 필수</li>
<li>as는 생략 가능<pre><code class="language-java">  em.createQuery( &quot;select m from Member as m&quot; );
                          ↓
  em.createQuery( &quot;select m from Member m&quot; );</code></pre>
<h3 id="2-jpql-문법">(2) JPQL 문법</h3>
</li>
<li><strong>select</strong> 문<pre><code>  select 절
  from 절
  where 절
  group by 절
  having 절
  orderby 절</code></pre></li>
<li><strong>update</strong> 문<pre><code>  update 절 where 절</code></pre></li>
<li><strong>delete</strong>문<pre><code>delete 절 where 절</code></pre></li>
<li><strong>집합</strong>과 <strong>정렬</strong><pre><code class="language-java">  select count(m),        // 회원수
          sum(m.age),     // 나이 합
          avg(m.age),        // 평균 나이
          max(m.age),        // 최대 나이
          min(m.age)        // 최소 나이
  from Member m</code></pre>
</li>
</ul>
<h3 id="3-반환-타입-지정">(3) 반환 타입 지정</h3>
<ul>
<li><p><strong>TypeQuery</strong>  : 반환 타입이 명확할 때 사용</p>
</li>
<li><p><em>💡 반환타입 정확히 MEMBER*</em></p>
<pre><code class="language-java">   TypeQuery&lt;Member&gt; query = 
       em.createQuery(&quot;select m from Member m&quot;, Member.class);</code></pre>
</li>
<li><p><strong>Query</strong> : 반환 타입이 명확하지 않을 때 사용</p>
</li>
<li><p><em>💡 여러개 SELECT -&gt; 타입 불명확*</em></p>
<pre><code class="language-java">   Query Query = 
       em.createQuery(&quot;select m.username, m.age from Member m&quot;);   </code></pre>
<h3 id="4-결과-조회">(4) 결과 조회</h3>
</li>
<li><p>반환된 객체 출력하기 위해 사용</p>
</li>
<li><p><code>query.getResultList()</code></p>
<ul>
<li>결과가 <strong>하나 이상</strong>일 때, 리스트 반환<ul>
<li>결과가 <strong>없으면 빈 리스트</strong> 반환</li>
<li>빈 collection이 반환되기 때문에 NullPointerException에 대한 걱정은 하지 않아도 됨</li>
</ul>
</li>
</ul>
</li>
<li><p><code>query.getSingleResult()</code></p>
<ul>
<li>결과가 <strong>정확히 하나</strong>(조심)</li>
<li>단일 객체 반환<ul>
<li>결과가 없으면 : javax.persistence.NoResultException</li>
<li>결과가 둘 이상이면 : javax.persistence.NonUniqueResultException </li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="5-파라미터-바인딩">(5) 파라미터 바인딩</h3>
<ul>
<li><p><strong>이름 기준</strong> : 변수앞에 <code>:</code></p>
<pre><code class="language-java">Member singleResult1 = em.createQuery(&quot;select m from Member as m where m.username = :username&quot;, Member.class)
                        .setParameter(&quot;username&quot;, &quot;member1&quot;)
                         .getSingleResult();</code></pre>
</li>
<li><p><strong>위치 기준</strong> : 실질 사용 ❌</p>
<pre><code class="language-java">em.createQuery(&quot;select m from Member as m where m.username = ?1&quot;, Member.class)
.setParameter(1, &quot;member1&quot;);</code></pre>
</li>
</ul>
<h3 id="6-프로젝션">(6) 프로젝션</h3>
<ul>
<li>select 절에 조회할 대상을 지정하는 것</li>
<li>프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자등 기본 타입)</li>
</ul>
<p><strong>💡 엔티티 프로젝션</strong></p>
<ul>
<li><p><strong>Member 엔티티 조회</strong></p>
<pre><code class="language-java">  List&lt;Member&gt; result = em.createQuery(&quot;select m from Member m&quot;, Member.class)
                          .getResultList();</code></pre>
</li>
<li><p><strong>Member와 관련된 team 조회</strong></p>
<pre><code class="language-java">  List&lt;Team&gt; result2 = em.createQuery(&quot;select m.team from Member m&quot;, Team.class)
                         .getResultList();</code></pre>
</li>
<li><p><strong>join문으로 Team 조회</strong></p>
<pre><code class="language-java">  List&lt;Team&gt; result2 = em.createQuery(&quot;select t from Member m join m.team t&quot;, Team.class)
                         .getResultList(); </code></pre>
</li>
<li><p><em>💡 임베디드 타입 프로젝션*</em></p>
<pre><code class="language-java">   // 직접 가져올 수 없고 o.address가 소속되어 있는 엔티티에서 가지고 와야 함
   List&lt;Address&gt; findAddress = em.createQuery(&quot;select o.address from Order o&quot;, Address.class)
                                   .getResultList();</code></pre>
</li>
<li><p><em>💡 스칼라 타입 프로젝션*</em></p>
</li>
<li><p><strong>object 받아오기</strong></p>
<pre><code class="language-java">  // 여러 값 조회
  List resultList = em.createQuery(&quot;select m.username, m.age from Member m&quot;)
                      .getResultList();

  // 타입을 지정하지 못하니까 object 받아오기
  Object o = resultList.get(0);

  // 배열로 담겨 있어야 꺼내쓰기가 쉬움
  Object[] result3 = (Object[])o;
  System.out.println(&quot;username = &quot; + result3[0]);
  System.out.println(&quot;age = &quot; + result3[1]);                        </code></pre>
</li>
<li><p><strong>Object배열의 리스트</strong></p>
<pre><code class="language-java">  List&lt;Object[]&gt; resultList2 = em.createQuery(&quot;select m.username, m.age from Member m&quot;)
                                 .getResultList();

  Object[] result4 = resultList2.get(0);
  System.out.println(&quot;username = &quot; + result4[0]);
  System.out.println(&quot;age = &quot; + result4[1]);</code></pre>
</li>
<li><p><strong>DTO</strong></p>
<pre><code class="language-java">  List&lt;MemberDTO&gt; result5 
      = em.createQuery(&quot;select new com.codingbox.jpql.MemberDTO(m.username, m.age) from Member m&quot;)
          .getResultList();

  MemberDTO memberDTO = result5.get(0);

  System.out.println(&quot;memberDTO = &quot; + memberDTO.getUsername());
  System.out.println(&quot;memberDTO = &quot; + memberDTO.getAge());</code></pre>
<ul>
<li><p>MemberDTO</p>
<pre><code class="language-java">@Getter @Setter
public class MemberDTO {

  private String username;
  private int age;

  public MemberDTO(String username, int age) {
      super();
      this.username = username;
      this.age = age;
  }
}</code></pre>
<h3 id="7-페이징-api">(7) 페이징 API</h3>
</li>
</ul>
</li>
<li><p>페이징 처리를 위해서는 <code>order by</code> 사용</p>
</li>
<li><p>JPA는 페이징을 다음 두 API로 추상화</p>
<ul>
<li><code>setFirstResult(int startPosition)</code> : 조회 시작 위치(0부터 시작)</li>
<li><code>setMaxResults(int maxResult)</code> : 조회할 데이터 수</li>
</ul>
</li>
<li><blockquote>
<p>몇 번째 부터 몇 개 가지고 올 것인가</p>
</blockquote>
<pre><code class="language-java">   // 나이 순으로 멤버 조회
   String jpql = &quot;select m from Member m order by m.age desc&quot;;
   // 10번째 부터 20개 데이터 조회
   List&lt;Member&gt; resultList = em.createQuery(jpql, Member.class)
                               .setFirstResult(10)
                               .setMaxResults(20)
                               .getResultList();

   // 멤버 리스트 조회
   System.out.println(&quot;result.size : &quot; + resultList.size());

   for(Member member1 : resultList) {
       System.out.println(&quot;member 1 = &quot; + member1);
   }</code></pre>
</li>
</ul>
<h3 id="8-조인">(8) 조인</h3>
<ul>
<li>문법이 객체 스타일</li>
<li><strong>내부 조인</strong> : inner 생략 가능 <pre><code class="language-java">  String jpql = &quot;select m from Member m inner join m.team t&quot;;
  List&lt;Member&gt; resultList = em.createQuery(jpql, Member.class)
                              .getResultList();</code></pre>
</li>
<li><strong>외부 조인</strong> : outer 생략 가능<pre><code class="language-java">  String jpql2 = &quot;select m from Member m left outer join m.team t&quot;;
  List&lt;Member&gt; resultList2 = em.createQuery(jpql2, Member.class)
                               .getResultList();</code></pre>
</li>
<li><strong>세타 조인</strong> (연관관계가 없는 조인)<pre><code class="language-java">  String jpql3 = &quot;select m from Member m, Team t where m.username = t.name&quot;;
  List&lt;Member&gt; resultList3 = em.createQuery(jpql3, Member.class)
                               .getResultList();</code></pre>
<h3 id="9-서브-쿼리">(9) 서브 쿼리</h3>
</li>
<li>SQL과 마찬가지로 서브쿼리도 JPQL에서 지원</li>
<li>하지만 FROM문에 대한 서브쿼리는 지원 ❌</li>
<li>조인으로 풀 것<pre><code class="language-java">  String jpql = &quot;select m from Member m where m.age &gt; (select avg(m2.age) from Member m2)&quot;;
  List&lt;Member&gt; resultList = em.createQuery(jpql, Member.class)
                              .getResultList();</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] 값 타입]]></title>
            <link>https://velog.io/@boram_han/Spring-%EA%B0%92-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@boram_han/Spring-%EA%B0%92-%ED%83%80%EC%9E%85</guid>
            <pubDate>Thu, 04 May 2023 09:15:45 GMT</pubDate>
            <description><![CDATA[<h1 id="💱-값-타입">💱 값 타입</h1>
<h2 id="1-엔티티-타입-vs-값-타입">(1) 엔티티 타입 vs 값 타입</h2>
<h3 id="◾-엔티티-타입">◾ 엔티티 타입</h3>
<ul>
<li>@Entity를 붙여서 관리하던 클래스들</li>
<li>PK값으로 관리가 되기때문에 데이터가 변해도 쉽게 추적이 가능하고 관리도 편리</li>
</ul>
<blockquote>
<p><strong>👀 PK값</strong></p>
<ul>
<li>&quot;Primary Key(기본 키)&quot;</li>
<li>각 레코드를 고유하게 식별하는 데 사용되는 필드(열) ex. 사용자 고유의 ID</li>
</ul>
</blockquote>
<h3 id="◾-값-타입">◾ 값 타입</h3>
<ul>
<li>int, String과 같은 단순히 값으로 사용하는 자바 <strong>기본타입</strong>이나 <strong>객체</strong></li>
<li>추적하기가 어려움</li>
</ul>
<h2 id="2-값-타입">(2) 값 타입</h2>
<h3 id="1-기본값-타입">1) 기본값 타입</h3>
<ul>
<li>자바 기본타입(int, double..), 래퍼클래스(Integer, Long..), String</li>
<li>생명주기를 엔티티한테 의존</li>
<li>예를 들어, <code>Order 엔티티</code>를 생성할 때 함께 <code>OrderDate 객체</code>도 <strong>생성</strong>되며, Order 엔티티가 <strong>삭제</strong>될 때 OrderDate 객체도 함께 <strong>삭제</strong><h3 id="2-임베디드-타입">2) 임베디드 타입</h3>
</li>
<li>복합 값타입으로 <strong>새로운 값 타입을 직접 정의</strong>할 수 있다.</li>
<li>예를 들어 회원 정보에서 <strong>비슷한 정보끼리 묶어 관리</strong>하고 싶다면 그런 묶음을 <code>임베디드 타입</code>으로 만들어 주고 사용</li>
</ul>
<h2 id="3-임베디드-타입embedded-type">(3) 임베디드 타입(EMBEDDED TYPE)</h2>
<blockquote>
<p><img src="https://velog.velcdn.com/images/boram_han/post/33c06a54-ca81-4604-b3b0-c1fe6d4b308e/image.png" alt=""></p>
</blockquote>
<ul>
<li>Period : 회원 정보 중 startDate, endDate를 묶은 것</li>
<li>Address : 회원 정보 중 city, street, zipcode를 묶은 것</li>
<li>이런 묶음을 <code>임베디드 타입</code>이라고 함</li>
</ul>
<h3 id="1-jpa에서-임베디드-타입-사용법">1) JPA에서 임베디드 타입 사용법</h3>
<ul>
<li><code>@Embeddable</code> <ul>
<li>값 타입을 정의하는 곳에 표시</li>
</ul>
</li>
<li><code>@Embedded</code><ul>
<li>값 타입을 사용하는 곳에 표시</li>
</ul>
</li>
<li>기본 생성자 필수</li>
<li><code>@Embeddable</code>과 <code>@Embedded</code> 둘 중에 하나만 넣어도 되지만 둘 다 넣는 것을 권장</li>
</ul>
<p><strong>💡 예시</strong></p>
<ul>
<li><p><code>@Embeddable</code></p>
<pre><code class="language-java">@Embeddable
@Getter @Setter
public class Address {

  private String city;
  private String street;
  private String zipcode;

  public Address(String city, String street, String zipcode) {
      super();
      this.city = city;
      this.street = street;
      this.zipcode = zipcode;
  }    
  // 기본생성자는 반드시 있어야 한다.
  public Address() { }
}</code></pre>
</li>
<li><p><code>@Embedded</code></p>
<pre><code class="language-java">@Entity
@Getter @Setter
public class Member {

  @Id @GeneratedValue
  private Long id;

  @Column(name = &quot;name&quot;, nullable = false)
  private String username;

  @Embedded
  private Address address;
</code></pre>
</li>
</ul>
<p>}</p>
<pre><code>- 실행파일에서 사용하기
```java
try {
     Member member = new Member();
     member.setUsername(&quot;user&quot;);
     member.setAddress(new Address(&quot;서울&quot;, &quot;역삼&quot;, &quot;123&quot;));
 }    </code></pre><h3 id="2-임베디드-타입의-장점">2) 임베디드 타입의 장점</h3>
<ul>
<li>재사용성</li>
<li>높은 응집도 </li>
<li>임베디드 타입에 사용할 특정 메서드를 따로 관리가능</li>
<li>임베디드 타입을 포함한 모든 값 타입은 값 타입을 소유한 엔티티 생명주기를 의존</li>
</ul>
<h3 id="3-임베디드-타입과-테이블-매핑">3) 임베디드 타입과 테이블 매핑</h3>
<ul>
<li>임베디드 타입은 엔티티의 값</li>
<li>임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 <strong>같음</strong></li>
<li>객체와 테이블은 아주 <strong>세밀하게 매핑</strong>하는 것이 가능</li>
<li>잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 <strong>많음</strong></li>
</ul>
<h3 id="4-attributeoverride--속성-재정의">4) @AttributeOverride : 속성 재정의</h3>
<ul>
<li>같은 임베디드 타입을 <strong>여러 개</strong> 쓰고 싶은 경우 <code>@AttributeOverrides</code>, <code>@AttributeOverride</code>어노테이션 사용</li>
</ul>
<p><strong>💡 예시</strong></p>
<pre><code class="language-java">    // 집주소
    @Embedded
    private Address address;

    // 회사 주소
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = &quot;city&quot;, column = @Column(name = &quot;WORK_CITY&quot;)),
        @AttributeOverride(name = &quot;street&quot;, column = @Column(name = &quot;WORK_STREET&quot;)),
        @AttributeOverride(name = &quot;zipcode&quot;, column = @Column(name = &quot;WORK_ZIPCODE&quot;))
    })
    private Address workAddress;
</code></pre>
<h3 id="5-객체-타입의-한계">5) 객체 타입의 한계</h3>
<p><strong>💡 하나의 객체 타입 값을 2객체에 할당</strong></p>
<pre><code class="language-java">    Address addr = new Address(&quot;서울&quot;, &quot;역삼&quot;, &quot;123&quot;);

    Member member = new Member();
    member.setUsername(&quot;user1&quot;);
    member.setAddress(addr);
    em.persist(member);

    Member member2 = new Member();
    member2.setUsername(&quot;user2&quot;);
    member2.setAddress(addr);
    em.persist(member2);

    tx.commit();</code></pre>
<p><strong>💡 그 중 한 객체의 값 변경</strong></p>
<pre><code class="language-java">    member.getAddress().setCity(&quot;newCity&quot;);</code></pre>
<p><strong>👍 결과 🚫 오류  user1, user2의 city값이 다 바뀜 🚫</strong></p>
<ul>
<li><p><strong>이유</strong> </p>
<ul>
<li><p>기본데이터 타입 변수일 때 -&gt; 재할당 가능</p>
<ul>
<li>a라는 공간에 10 대입</li>
<li>b라는 공간에 a값 복붙<pre><code class="language-java">int a = 10; 
int b = a</code></pre>
</li>
</ul>
</li>
<li><p>객체 공유 : 클래스 공유
<img src="https://velog.velcdn.com/images/boram_han/post/8a9d5797-d4df-43c3-afbc-c4200df956e0/image.png" alt=""></p>
<ul>
<li>공간 없음</li>
<li>각각의 클래는 주소값을 가지고 있는 new Member() 객체를 <strong>가리키기</strong>만 하고 있음</li>
<li>그러므로 <strong>member1 클래스에서 city</strong>값을 바꾸면** new Member() 객체 주소의 city** 변경</li>
<li>member2 클래스는 바뀐 new Member()객체를 <strong>그대로 가리키고</strong> 있음</li>
<li>밖에서 볼 때는 a, b클래스 city값 <strong>전체 수정</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><p><strong>임베디드 타입</strong>처럼 직접 정의한 값 타입은 자바의 기본타입이 아니라 <strong>객체 타입</strong></p>
<ul>
<li>자바 <strong>기본 타입</strong>에 값을 대입하면 값을 <strong>복사</strong>한다.</li>
<li><strong>객체 타입</strong>은 참조 값을 <strong>직접 대입</strong>하는 것을 막을 방법이 <strong>없음</strong></li>
</ul>
</li>
<li><p><strong>항상 값을 복사</strong>해서 사용하면 공유 참조로 인해 발생하는 부작용을 피할 수 있음</p>
</li>
<li><p><strong>해결법</strong></p>
<ul>
<li>새로운 객체를 무조건 만들어서 할당</li>
<li>setter를 없앤다</li>
</ul>
</li>
</ul>
<p><strong>💡 기본값타입들을 복사해서 넘겨주는 방식</strong></p>
<p><img src="https://velog.velcdn.com/images/boram_han/post/aabe48de-4cdf-43c6-a921-fcef784adc5a/image.png" alt=""></p>
<pre><code class="language-java">    Address addr = new Address(&quot;서울&quot;, &quot;역삼&quot;, &quot;123&quot;);
    // 새로운 객체 생성
    Address copyAddr = new Address(addr.getCity(), addr.getStreet(), addr.getZipcode());

    Member member = new Member();
    member.setUsername(&quot;user1&quot;);
    member.setAddress(addr);
    em.persist(member);

    Member member2 = new Member();
    member2.setUsername(&quot;user2&quot;);
    // 새로운 객체 할당
    member2.setAddress(copyAddr);
    em.persist(member2);

    // 1번째 member 주소만 바꾸고 싶어
    member.getAddress().setCity(&quot;newCity&quot;);

    tx.commit();</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] YamL]]></title>
            <link>https://velog.io/@boram_han/spring-YamL</link>
            <guid>https://velog.io/@boram_han/spring-YamL</guid>
            <pubDate>Thu, 04 May 2023 08:10:20 GMT</pubDate>
            <description><![CDATA[<h1 id="☘-yaml-설정파일-yml">☘ Yaml (설정파일 *.yml)</h1>
<ul>
<li><code>yaml</code>은 파일 작성에 자주 사용되는 데이터 직렬화 언어이자, 데이터 표현 양식의 한 종류</li>
<li>사용자가 보고 이해하기 쉬운 형태를 가지고 있기 때문에 최근들어 많이 활용되는 데이터 포멧</li>
</ul>
<h2 id="1-properties-vs-yml">(1) properties vs yml</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/ac26742e-bd6f-43ff-bc86-952a81acf070/image.png" alt=""></p>
<ul>
<li>한 눈에 보기에 가독성이 좋다. </li>
<li>불필요한 코드의 반복을 피할 수 있다.</li>
<li>계층 구조로 이해하기 쉽고 쓰기 편하다.</li>
</ul>
<h2 id="2-주의사항">(2) 주의사항</h2>
<ul>
<li>계층 간 : 띄어쓰기 <code>2칸</code></li>
<li>값 : 띄어쓰기(스페이스) <code>1칸</code></li>
</ul>
<p><strong>💡 application.yml</strong></p>
<pre><code class="language-yml">#port 수정
server:
  port: 9092

#DB Connection
spring:
  datasource:
    url: jdbc:oracle:thin:@localhost:1521:xe
    driver-class-name: oracle.jdbc.OracleDriver
    username: spring
    password: spring

# JPA (spring 영역이므로 두칸 띄어쓰기)
  jpa:
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: true
        show_sql: true

#thymeleaf 설정
  thymeleaf:
    cache: false</code></pre>
<ul>
<li><strong>DB</strong>, <strong>JPA</strong>, <strong>THYMELEAF</strong> 모두 <strong>spring</strong>에 속하므로 <code>2칸</code> 띄어쓰기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] 연관관계 매핑]]></title>
            <link>https://velog.io/@boram_han/Spring-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91</link>
            <guid>https://velog.io/@boram_han/Spring-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91</guid>
            <pubDate>Mon, 01 May 2023 15:03:22 GMT</pubDate>
            <description><![CDATA[<h1 id="연관관계-매핑">연관관계 매핑</h1>
<blockquote>
<p>객체 설계를 테이블 설계에 맞춘다면?</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/boram_han/post/2d17058c-bc33-4fc5-98f0-9de2a09ac869/image.png" alt=""></p>
<h3 id="1-member-team-객체-생성">1) Member, Team 객체 생성</h3>
<ul>
<li><p><strong>Member 객체생성</strong></p>
<pre><code class="language-java">  @Entity
  public class Member {

      @Id @GeneratedValue
      private Long id;

      @Column(name = &quot;USERNAME&quot;) 
      private String name;

      @Column(name = &quot;TEAM_ID&quot;)
      private Long teamId; 
  }</code></pre>
</li>
<li><p><strong>Team 객체생성</strong></p>
<pre><code class="language-java">  @Entity
  public class Team {

      @Id @GeneratedValue 
      private Long id;

      private String name; 
   }</code></pre>
<h3 id="2-멤버의-team을-알고-싶을-때">2) 멤버의 team을 알고 싶을 때</h3>
</li>
<li><p><strong>Team, Member 영속상태</strong></p>
<pre><code class="language-java">  //팀 저장
  Team team = new Team();
  team.setName(&quot;TeamA&quot;);
  em.persist(team);

  // 회원 저장
  Member member = new Member();
  member.setName(&quot;member1&quot;);
  member.setTeamId(team.getId());
  em.persist(member);</code></pre>
</li>
<li><p><strong>멤버의 team 찾기</strong></p>
<pre><code class="language-java">  // 멤버 id로 멤버 정보 찾기
  Member findMember = em.find(Member.class, member.getId());
  // 그 결과로 teamId 찾기
  Long findTeamId = findMember.getTeamId();
  // 찾은 teamId로 team정보 가져오기
  Team findTeam = em.find(Team.class, findTeamId);
  // 가져온 team정보로 team 이름 알아내기
  System.out.println(&quot;findTeam : &quot; + findTeam.getName());</code></pre>
<blockquote>
<ul>
<li>결론<ul>
<li><strong>번거로움</strong></li>
<li>저장된 teamId를 이용해서 팀 조회 -&gt; 객체지향적인 방법이라고 보기 <strong>어려움</strong></li>
<li>member와 team의 연관관계가 <strong>없음</strong></li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ul>
<h2 id="1-연관관계-매핑-기초">(1) 연관관계 매핑 기초</h2>
<blockquote>
<p><strong>객체지향</strong>적인 모델링을 위해 연관관계 매핑</p>
</blockquote>
<ul>
<li>객체와 테이블의 연관관계를 이해하고 객체의 <strong>참조</strong>와 테이블의 <strong>외래키</strong>를 <strong>매핑</strong></li>
<li>용어<ul>
<li><strong>방향</strong>(Direction) : 단방향, 양방향</li>
<li><strong>다중성</strong>(Multiplicity) : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:N)</li>
<li><strong>연관관계 주인</strong>(Owner) : 객체 양방향 연관관계는 관리 주인이 필요</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>🙆‍♀️ 객체와 테이블 차이</strong></p>
</blockquote>
<ul>
<li><strong>테이블</strong> : 외래 키로 <strong>조인</strong>을 사용해서 연관된 테이블을 찾음</li>
<li><strong>객체</strong> : <strong>참조</strong>를 사용해서 연관된 객체를 찾음</li>
</ul>
<h2 id="2-단방향-연관관계">(2) 단방향 연관관계</h2>
<blockquote>
</blockquote>
<ul>
<li>객체지향적으로 모델링하는 법</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boram_han/post/28885281-7285-4791-9381-75068d175e2a/image.png" alt=""></p>
<ul>
<li><strong>Member</strong>안에 <strong>team이라는 객체</strong> 넣기<ul>
<li>객체가 참조를 바로할 수 있음</li>
</ul>
</li>
</ul>
<h3 id="1-연관관계-가진-member-team-객체-생성">1) 연관관계 가진 Member, Team 객체 생성</h3>
<h4 id="①-member객체-생성">① Member객체 생성</h4>
<ul>
<li>team객체를 통으로 가지고 옴</li>
<li>FK를 걸어줘야 하는 곳에 Join 걸어줌</li>
<li>@ManyToOne : 많은 것들 중 하나<ul>
<li>많은 것 : 멤버 수, 하나 : 팀 이름</li>
</ul>
</li>
<li>@JoinColumn<ul>
<li>관계 컬럼을 적어줌  </li>
</ul>
</li>
</ul>
<pre><code class="language-java">@Entity
@Getter @Setter
public class Member {

    @Id @GeneratedValue
    @Column(name=&quot;MEMBER_ID&quot;)
    private Long id;

    @Column(name=&quot;USERNAME&quot;)
    private String name;

    @ManyToOne
    @JoinColumn(name = &quot;TEAM_ID&quot;)
    private Team team;
}    </code></pre>
<blockquote>
<p><strong>👀 외래키</strong></p>
</blockquote>
<ul>
<li>외래키가 포함된 테이블이 자식 테이블</li>
<li>기준이 되는 테이블의 내용을 참조해서 레코드가 입력</li>
</ul>
<h4 id="②-team-객체-생성">② Team 객체 생성</h4>
<ul>
<li><p>@Column의 name 속성과 @JoinColumn의 name 속성은 반드시 같아야 함</p>
<pre><code class="language-java">@Entity
@Getter @Setter
public class Team {

  @Id @GeneratedValue
  @Column(name=&quot;TEAM_ID&quot;)
  private Long id;
  private String name;
}    </code></pre>
<h3 id="2-멤버의-team을-알고-싶을-때-1">2) 멤버의 team을 알고 싶을 때</h3>
<h4 id="①-team-member-영속상태">① Team, Member 영속상태</h4>
<pre><code class="language-java">//팀 저장
Team team = new Team();
team.setName(&quot;TeamA&quot;); 
em.persist(team);
</code></pre>
</li>
</ul>
<p>//회원 저장
Member member = new Member(); 
member.setName(&quot;member1&quot;);
// member.setTeamId(team.getId());
// 단방향 연관관계 설정, 참조 저장 
member.setTeam(team); </p>
<p>em.persist(member);</p>
<pre><code>#### ② 멤버의 team 찾기
```java
    // 멤버 id로 멤버 정보 찾기
    Member findMember = em.find(Member.class, member.getId());
    // 멤버 정보에서 객체 team 찾아냄
    Team findTeam = findMember.getTeam();
    // team의 이름 찾아냄
    System.out.println(&quot;findTeamName : &quot; + findTeam.getName());</code></pre><blockquote>
<ul>
<li>결론<ul>
<li>team객체를 넘겨주면 알아서 teamId를 찾아 <strong>FK값</strong>으로 쓰고 <strong>연관관계를 설정</strong></li>
<li>team을 getTeam을 이용해서 참조로 가져옴</li>
</ul>
</li>
</ul>
</blockquote>
<h2 id="3-양방향-연관관계">(3) 양방향 연관관계</h2>
<blockquote>
<ul>
<li>단방향 매핑 후, Team의 Member들을 역참조 : 양방향 객체 연관관계</li>
</ul>
</blockquote>
<p><img src="https://velog.velcdn.com/images/boram_han/post/b3a0ceae-b80f-44b3-a388-a1df505b308f/image.png" alt=""></p>
<h3 id="1-member-team-객체-양방향-연관관계-맺기">1) Member, Team 객체 양방향 연관관계 맺기</h3>
<h4 id="①-member객체-생성-1">① Member객체 생성</h4>
<ul>
<li><p>단방향과 똑같이</p>
<pre><code class="language-java">@Entity
@Getter @Setter
public class Member {

  @Id @GeneratedValue
  @Column(name=&quot;MEMBER_ID&quot;)
  private Long id;

  @Column(name=&quot;USERNAME&quot;)
  private String name;

  @ManyToOne
  @JoinColumn(name = &quot;TEAM_ID&quot;)
  private Team team;
}  </code></pre>
<h4 id="②-team-객체-생성-1">② Team 객체 생성</h4>
</li>
<li><p>Team에 members 리스트를 추가</p>
</li>
<li><p>@OneToMany 어노테이션 붙이고 mappedBy로 연결할 객체 넣어줌 </p>
<ul>
<li><p>여기선 Member 클래스의 Team team 객체</p>
<pre><code class="language-java">@Entity
@Getter @Setter
public class Team {

@Id @GeneratedValue
@Column(name=&quot;TEAM_ID&quot;)
private Long id;

private String name;

@OneToMany(mappedBy = &quot;team&quot;)
List&lt;Member&gt; members = new ArrayList&lt;Member&gt;();
}</code></pre>
<h4 id="③-역참조-가능">③ 역참조 가능</h4>
<pre><code class="language-JAVA">team.getMembers();</code></pre>
<h3 id="2-연관관계의-주인owner">2) 연관관계의 주인(Owner)</h3>
<p><img src="https://velog.velcdn.com/images/boram_han/post/47094859-a123-45a7-b2c5-5bfa69319a17/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
<h4 id="①-테이블-객체에서의-연관관계">① 테이블, 객체에서의 연관관계</h4>
<ul>
<li>테이블(DB)에서의 연관관계<ul>
<li>FK값으로 JOIN을 통해서 양방향 접근 가능</li>
</ul>
</li>
<li>객체에서의 연관관계<ul>
<li><code>회원 -&gt; 팀</code>, <code>팀 -&gt; 회원</code> 연관관계</li>
<li>단방향 연관관계 2개<h4 id="②-딜레마--member에서-team값을-수정하고-싶을-때">② 딜레마 : Member에서 Team값을 수정하고 싶을 때</h4>
</li>
</ul>
</li>
<li>DB 입장 <ul>
<li>MEMBER에 있는 TEAM_ID만 UPDATE해도 수정 가능</li>
</ul>
</li>
<li>객체 입장 : <ul>
<li>TEAM에서 MEMBER 수정? OR MEMBER에서 TEAM 수정? <h4 id="③-두-관계중-하나를-연관관계의-주인owner으로-지정">③ 두 관계중 하나를 연관관계의 주인(Owner)으로 지정</h4>
</li>
</ul>
</li>
<li>주인이 아닌 쪽에 <code>mappedBy</code><ul>
<li><code>mappedBy</code> : 내가 누군가에 의해 <strong>MAPPING</strong> 되었다는 뜻</li>
</ul>
</li>
<li>항상 <strong>외래키</strong>가 있는 곳을 주인 -&gt; 비즈니스 로직을 기준 ❌<ul>
<li><code>@ManyToOne</code>을 포함하는 클래스가 주인</li>
<li>여기서는 <code>Member.team</code> 이 연관관계의 주인<h4 id="④-연관관계의-주인owner">④ 연관관계의 주인(Owner)</h4>
</li>
</ul>
</li>
<li>주인만이 외래키를 관리</li>
<li>주인쪽만 <strong>연관관계를 등록하고 수정</strong>가능</li>
<li>주인이 아닌 쪽은 읽기만 가능</li>
<li>주인쪽에서 연관관계를 생성하는 코드 추가<pre><code>  member.setTeam(team);   </code></pre></li>
</ul>
<h3 id="3-team에-담긴-member-리스트-보기">3) Team에 담긴 member 리스트 보기</h3>
<pre><code class="language-java">    try {
            Team team = new Team();
            team.setName(&quot;TeamA&quot;);
            em.persist(team); // 영속상태

            Member member = new Member();
            member.setName(&quot;member1&quot;);
            // 연관관계 생성하는 코드 추가
            member.setTeam(team);
            em.persist(member);

            // 양방향 매핑
            Member findSideMember = em.find(Member.class, member.getId());
            // 팀은 멤버를 가지고 있고 
            List&lt;Member&gt; members = findSideMember.getTeam().getMember();

            for(Member m : members) {
                System.out.println(&quot;result = &quot; + m.getName());
            }

            tx.commit(); // db에 적용되는 자리 
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
            emf.close();
        }</code></pre>
<h3 id="4-최적화">4) 최적화</h3>
<h4 id="①-위의-코드--실행-❌-null">① 위의 코드 : 실행 ❌ (null)</h4>
<ul>
<li><strong>why?</strong> 아직 관계 생성 ❌ = DB에 반영 ❌<ul>
<li>영속성 컨텍스트에 들어가 있는 데 (1차 캐시) -&gt; 영속성 컨텍스트는 변경된 것을 감지하지 못함</li>
</ul>
</li>
</ul>
<h4 id="②-flush-추가">② flush 추가</h4>
<ul>
<li><p><code>em.persist</code> 뒤에 <strong>flush</strong>로 DB에 쿼리를 날려주고 캐시를 참고하지 않도록 <strong>clear</strong>까지 해주면 DB에서 가져올 수 있음</p>
<ul>
<li><p>출력 가능한 상태</p>
<pre><code class="language-java">try {
        Team team = new Team();
        team.setName(&quot;TeamA&quot;);
        em.persist(team); // 영속상태

        Member member = new Member();
        member.setName(&quot;member1&quot;);
        member.setTeam(team);
        em.persist(member);

        // 강제로 DB쿼리를 보고 싶을 때 사용
        em.flush();
        em.clear();

        Member findSideMember = em.find(Member.class, member.getId());
        List&lt;Member&gt; members = findSideMember.getTeam().getMember();

        for(Member m : members) {
            System.out.println(&quot;result = &quot; + m.getName());
        }

        tx.commit(); // db에 적용되는 자리 
    } catch (Exception e) {
        tx.rollback();
    } finally {
        em.close();
        emf.close();
    }</code></pre>
<h4 id="③-순수-객체로-사용하기">③ 순수 객체로 사용하기</h4>
</li>
</ul>
</li>
<li><p><strong>flush</strong> 호출을 매번하는 것은 번거롭고 실수할 가능성 높임</p>
<ul>
<li><strong>flush</strong> 자리에 <code>team.getMembers().add(member);</code> 추가</li>
<li>Team은 주인이 아니므로 읽기만 가능</li>
<li>양쪽 객체에 값을 모두 입력시키는 것</li>
<li>DB에 다녀오지 않고 객체 상태로만 사용 가능</li>
</ul>
</li>
</ul>
<h4 id="④-주의사항">④ 주의사항</h4>
<ul>
<li>수동으로 값을 입력하는 것 : 실수 유발</li>
<li>무한루프 주의 <ul>
<li>toString 실행 시, member객체는 team객체를 부르고 team객체는 member객체를 부름</li>
</ul>
</li>
</ul>
<h4 id="⑤-최종-해결">⑤ 최종 해결</h4>
<ul>
<li><p><strong>Member.java</strong></p>
<ul>
<li><p>Member를 기준으로 team 넣을 경우</p>
</li>
<li><p>기존의 setter 변경한 다음 메서드 추가</p>
<pre><code class="language-java">      public void changeTeam(Team team) {
    this.team = team;
    // this : 나 자신의 인스턴스를 넣어준다.
    team.getMember().add(this);
}</code></pre>
<ul>
<li>기존의 setter 변경 : 이름 바꾸기</li>
<li>@Setter(value = AccessLevel.NONE)<ul>
<li>lombok에서 자동으로 setter생성을 막아줌<pre><code class="language-java">@Entity
@Getter @Setter
public class Member {
</code></pre>
</li>
</ul>
</li>
</ul>
<p>@Id @GeneratedValue
@Column(name = &quot;MEMBER_ID&quot;)
private Long id;
@Column(name = &quot;USERNAME&quot;)
private String name;</p>
<p>@ManyToOne
@JoinColumn(name = &quot;TEAM_ID&quot;)
@Setter(value = AccessLevel.NONE)    // setter생성을 막아줌
private Team team;</p>
<p>/*</p>
<ul>
<li>일반적인 setter의 형태(자바에서의 관례 형태를 벗어났다)가 아니면 이름을 바꿔준다. </li>
<li>그럼 추후 소스코드를 봤을때 단순 settet작업이 아닌 중요한 작업을 진행하는지를 파악 할 수 있다.</li>
<li>/
public void changeTeam(Team team) {
  this.team = team;
  // this : 나 자신의 인스턴스를 넣어준다.
  team.getMember().add(this);
}</li>
</ul>
<p>public void setTeam(Team team) {</p>
<pre><code>this.team = team;</code></pre><p>}    
}
```</p>
</li>
</ul>
</li>
<li><p><strong>Team.java</strong></p>
<ul>
<li><p>Team을 기준으로 member를 넣을 경우</p>
</li>
<li><p>team에 멤버 입력 시 자동으로 member측에도 team값 설정</p>
<pre><code class="language-java">@Entity
@Getter @Setter
public class Team {
@Id @GeneratedValue
@Column(name = &quot;TEAM_ID&quot;)
private Long id;
private String name;

@OneToMany(mappedBy = &quot;team&quot;)
private List&lt;Member&gt; member = new ArrayList&lt;&gt;();

public void addMember(Member member) {
    member.setTeam(this);
    this.member.add(member);
}
}</code></pre>
</li>
</ul>
</li>
<li><p><strong>최종 최적화 코드</strong></p>
<pre><code class="language-java">  try{
      Team team = new Team();
      team.setName(&quot;TeamB&quot;);
      em.persist(team);

      Member member = new Member();
      member.setName(&quot;member2&quot;);

      //member.changeTeam(team);    // 1안 : member를 기준으로 team을 넣는다.
      em.persist(member);

      team.addMember(member);        // 2안 : team을 기준으로 member를 넣는다.

      Team findTeam = em.find(Team.class, team.getId());
      List&lt;Member&gt; members = findTeam.getMember();

      System.out.println(&quot;members = &quot; + findTeam);

      for( Member m : members ) {
              System.out.println(&quot;result = &quot; + m.getName());
      }

      tx.commit();

  }catch (Exception e) {
          tx.rollback();
  } finally {
      em.close();
      emf.close();
  }</code></pre>
<h3 id="5-양방향-매핑-정리">5) 양방향 매핑 정리</h3>
</li>
<li><p><strong>단방향</strong> 매핑만으로도 이미 연관관계 매핑은 <strong>완료</strong> </p>
</li>
<li><p><strong>양방향</strong> 매핑은 <strong>반대 방향</strong>으로 조회 기능이 <strong>추가</strong></p>
<ul>
<li><strong>양방향 사용 이유</strong> : <code>JPQL</code>에서 양방향으로 탐색할 일이 많음</li>
</ul>
</li>
<li><p>단방향 매핑을 메인으로 사용하고 양방향 매핑은 필요할 때 추가하기</p>
<ul>
<li>양방향 : select를 좀 더 직관적으로 하고 싶을 때 사용, 테이블에 영향을 주지 않음</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>결론</strong> </p>
</blockquote>
<ul>
<li>객체입장에서 양방향 매핑은 이득이 별로 안되므로 필수가 아님.</li>
<li>필요시에 생성하기 (옵션)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] Entity mapping]]></title>
            <link>https://velog.io/@boram_han/Spring-Entity-mapping</link>
            <guid>https://velog.io/@boram_han/Spring-Entity-mapping</guid>
            <pubDate>Sun, 30 Apr 2023 16:00:42 GMT</pubDate>
            <description><![CDATA[<h1 id="🌍-entity-mapping">🌍 Entity mapping</h1>
<blockquote>
<p><strong>JPA</strong>를 이용해 <strong>데이터베이스</strong>의 테이블과 <strong>상호 작용</strong>(데이터 저장, 수정, 조회, 삭제 등) 하기 위해 먼저 해야 하는 작업은 <strong>데이터베이스</strong> 테이블과 <strong>엔티티</strong> 클래스 간의 <strong>매핑</strong> 작업</p>
</blockquote>
<h2 id="사용-어노테이션">사용 어노테이션</h2>
<ul>
<li>객체와 테이블 매핑 : <code>@Entity</code>, <code>@Table</code></li>
<li>필드와 컬럼 매핑 : <code>@Column</code></li>
<li>기본 키 매핑 : <code>@Id</code></li>
<li>연관관계 매핑 : <code>@ManyTOne</code>, <code>@JoinColumn</code></li>
</ul>
<h1 id="1-객체와-테이블-매핑--entity-table">1. 객체와 테이블 매핑 : @Entity, @Table</h1>
<h2 id="1-entity">(1) @Entity</h2>
<blockquote>
<ul>
<li><code>@Entity</code>가 붙은 클래스는 JPA 관리 엔티티</li>
<li>JPA를 사용해서 테이블과 매핑할 클래스는 <code>@Entity</code> 필수</li>
</ul>
</blockquote>
<ul>
<li><p><strong>주의사항</strong></p>
<ul>
<li>기본 생성자 필수 (파라미터가 없는 public 또는 protected 생성자)</li>
<li>저장 필드에 final 사용 ❌</li>
<li>final 클래스, enum, interface 사용 ❌ </li>
<li>즉, <strong>일반 클래스로 만들면 됨</strong></li>
</ul>
</li>
<li><p><strong>속성</strong> </p>
<ul>
<li>name 속성<pre><code>  @Entity(name = &quot;Member2&quot;)</code></pre></li>
<li>JPA에서 사용할 엔티티 이름을 지정가능</li>
<li>기본값 : 클래스 이름을 그대로</li>
<li>같은 클래스 이름이 없으면 가급적 기본값을 사용할 것</li>
</ul>
</li>
</ul>
<h2 id="2-table">(2) @Table</h2>
<blockquote>
<ul>
<li>@Table 엔티티와 매핑할 테이블 </li>
</ul>
</blockquote>
<ul>
<li><strong>속성</strong>
<img src="https://velog.velcdn.com/images/boram_han/post/1d865698-9249-4c0d-855b-9c008ccc40d1/image.png" alt=""><ul>
<li>name : 매핑할 테이블 이름 , 엔티티 이름을 사용</li>
<li>catalog : 데이터베이스 catalog 매핑</li>
<li>schema : 데이터베이스 schema 매핑</li>
<li>uniqueConstraints(DDL) : <strong>DDL 생성</strong> 시에 유니크 제약 조건 생성</li>
<li><pre><code class="language-java">@Entity
@Table(name = &quot;MBR&quot;)
public class Member {
    @Id
    private Long memberId;
}</code></pre>
</li>
</ul>
</li>
</ul>
<p><strong>🙆‍♀️ DDL 명령어</strong> </p>
<ul>
<li><p><strong>DDL 명령어에 대한 자동 생성 여부</strong></p>
<ul>
<li>persistence.xml에서 조정 가능</li>
<li><code>&lt;property name=&quot;hibernate.hbm2ddl.auto&quot; value=&quot;none&quot; /&gt;</code></li>
<li>속성 : <code>value=&quot;속성&quot;</code></li>
<li><img src="https://velog.velcdn.com/images/boram_han/post/3acd867a-4665-4e80-91d1-ade7cac27937/image.png" alt=""></li>
<li>CREATE은 실무단계에서 절대 사용하지 않음</li>
</ul>
</li>
<li><p><strong>DDL 생성기능</strong></p>
<ul>
<li>DDL을 자동 생성할 때(value=&quot;create&quot;)만 사용. JPA의 실행 로직에는 영향 ❌</li>
<li>제약조건 추가<pre><code class="language-java">// 회원 이름을 필수, 10자 초과X
   @Column(nullable = false, length = 10)
   private String name;</code></pre>
<ul>
<li>유니크 제약조건 추가<pre><code class="language-java"> @Entity
  @Table(name = &quot;MBR&quot;,
           uniqueContraints={
               @UniqueConstraint(name=&quot;NAME_AGE_UNIQUE&quot;,
               columnNames={&quot;NAME&quot;,&quot;AGE&quot;})})
  public class Member {
      @Id
      private Long memberId;
}</code></pre>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="2-필드와-컬럼-매핑">2. 필드와 컬럼 매핑</h1>
<blockquote>
<ul>
<li>필드 차원에서의 매핑 어노테이션</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>필드 매핑 어노테이션 종류
<img src="https://velog.velcdn.com/images/boram_han/post/0a38224f-0b54-4fc7-a0a3-ecb77b04b2dc/image.png" alt=""></li>
</ul>
</blockquote>
<pre><code class="language-JAVA">@Entity
@Getter @Setter
@Table(name = &quot;MBR&quot;)
public class Member {

    @Id
    private Long id;

    @Column(unique = true, length = 10)
    private String name;

    @Column(name = &quot;myage&quot;)
    private int age;

    // DB에 날짜관련 매핑 시
    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;

    // 마지막에 갱신된 날짜 매핑
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;

    // DB에 넣지 않는 데이터
    @Transient
    private int temp;
}</code></pre>
<h2 id="1-column">(1) @Column</h2>
<ul>
<li><strong>컬럼 속성</strong>
<img src="https://velog.velcdn.com/images/boram_han/post/4b2c1319-705e-41ee-8b01-6a4b5be8f294/image.png" alt=""></li>
</ul>
<h2 id="2-transient">(2) @Transient</h2>
<ul>
<li>필드 매핑 ❌</li>
<li>데이터베이스에 저장 ❌, 조회❌</li>
<li>주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용</li>
</ul>
<h1 id="3-기본-키-매핑-fk">3. 기본 키 매핑 (FK)</h1>
<blockquote>
<p>직접 할당 or 자동 할당</p>
</blockquote>
<h2 id="1-직접-할당">(1) 직접 할당</h2>
<ul>
<li><code>@Id</code> 사용</li>
<li>엔티티 클래스의 기본키를 지정할 필드에 <code>@Id</code> 어노테이션을 붙이는 것으로 기본키를 직접 할당<h2 id="2-자동-할당">(2) 자동 할당</h2>
</li>
<li><code>@GeneratedValue</code> 사용</li>
<li>기본키 생성 전략 <ul>
<li>IDENTITY<ul>
<li>데이터베이스에 위임</li>
<li><code>@GeneratedValue(strategy = GenerationType.SEQUENCE)</code></li>
</ul>
</li>
<li>SEQUENCE<ul>
<li>데이터베이스 시퀀스 오브젝트 사용</li>
<li>ORACLE, <code>@SequenceGenerator</code> 사용</li>
</ul>
</li>
<li>TABLE<ul>
<li>키 생성용 테이블 사용, 모든 DB에서 사용 가능</li>
<li><code>@TableGenerator</code> </li>
<li>권장 ❌</li>
</ul>
</li>
<li>AUTO<ul>
<li>DB 방언에 맞는 SQL 자동 지정</li>
<li>auto가 기본값 (오라클, h2, MySQL등에 맞게 자동으로 설정해서 사용)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>💡 예시</strong></p>
<p><strong>IDENTITY 예시 : 데이터베이스에서 바로 생성</strong></p>
<pre><code class="language-java">@Entity
@Getter @Setter
public class Member2 {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @Column(name=&quot;name&quot;, nullable=false)
    private String username;

}</code></pre>
<p><strong><code>@SequenceGenerator</code> 사용 예시</strong></p>
<pre><code>@Entity
@Getter @Setter
@SequenceGenerator(name= &quot;MEMBER2_SEQ_GENERATOR&quot;,
                    sequenceName = &quot;MEMBER_SEQ&quot;,
                    initialValue = 1, allocationSize = 1)
public class Member2 {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                    generator = &quot;MEMBER2_SEQ_GENERATOR&quot;)
    private Long id;

    @Column(name=&quot;name&quot;, nullable=false)
    private String username;

}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] JPQL, 영속성 컨텍스트]]></title>
            <link>https://velog.io/@boram_han/Spring-JPQL-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@boram_han/Spring-JPQL-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Sun, 30 Apr 2023 14:43:31 GMT</pubDate>
            <description><![CDATA[<h1 id="1-📇-jpql">1. 📇 JPQL</h1>
<h2 id="jpql이란">JPQL이란?</h2>
<blockquote>
<p>JPA는 SQL을 추상화한 JPQL이라는 <strong>객체 지향 쿼리 언어</strong> 제공</p>
</blockquote>
<ul>
<li><strong>JPA</strong>는 쿼리를 짤 때 table을 대상으로 쿼리를 짜지 않고 <strong>엔티티 객체를 중심</strong>으로 개발<ul>
<li><strong>문제점</strong> : 검색 쿼리, 테이블이 아닌 엔티티 객체를 대상으로 검색</li>
<li>모든 DB데이터를 객체로 변환해서 검색하는 것은 불가능 </li>
<li>애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색조건이 포함된 <strong>SQL문</strong>이 필요<blockquote>
<p><strong>해결</strong></p>
<ul>
<li>JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공</li>
</ul>
</blockquote>
<ul>
<li><strong>SQL</strong> : 데이터베이스 테이블을 대상으로 쿼리</li>
<li><strong>JPQL</strong> : 엔티티 객체를 대상으로 쿼리, 객체지향 SQL<ul>
<li>SQL과 <strong>문법</strong>이 유사 <ul>
<li>SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>💡 예시</strong></p>
<pre><code>    List&lt;Member&gt; result = em.createQuery(&quot;select m from Member as m&quot;, Member.class)
                            .setFirstResult(5)    // 5번 부터
                            .setMaxResults(10)  // 10개 가지고 오기
                            .getResultList();

    for(Member member : result) {
        System.out.println(&quot;member.name = &quot; + member.getName());
    }</code></pre><h1 id="👀-jpa에서-중요한-2가지">👀 JPA에서 중요한 2가지</h1>
<h3 id="1-객체와-관계형-데이터베이스-매핑object-relational-mapping---orm">1. 객체와 관계형 데이터베이스 매핑(Object Relational Mapping) - ORM</h3>
<h3 id="2-영속성-컨텍스트">2. 영속성 컨텍스트</h3>
<h1 id="2-영속성-컨텍스트-1">2. 영속성 컨텍스트</h1>
<h2 id="1-영속성-컨텍스트">(1) 영속성 컨텍스트</h2>
<blockquote>
<ul>
<li>JPA를 이해하는 데 가장 중요한 용어 </li>
<li><strong>&quot;엔티티(ENTITY)를 영구 저장하는 환경(공간)&quot;</strong>이라는 뜻의 논리적인 개념</li>
</ul>
</blockquote>
<h3 id="1-entity">1) ENTITY</h3>
<ul>
<li>JPA는 데이터와 연관된 객체들을 Entity로 분류하여 관리</li>
<li>따라서 데이터를 읽거나, 조회하거나 쓰는 내용들이 Entity를 통해서 이루어 짐</li>
</ul>
<blockquote>
<p>이러한 <strong>엔티티</strong>들을 JPA는 어떠한 곳에 <strong>저장</strong>하는데, 이 영역을 <strong>영속성 컨텍스트</strong></p>
</blockquote>
<h3 id="2-entity-manager">2) ENTITY MANAGER</h3>
<p><img src="https://velog.velcdn.com/images/boram_han/post/11c01b54-49e6-4b79-8728-4f5dd234f0ae/image.png" alt=""></p>
<blockquote>
<p>영속성 컨텍스트는 엔티티 매니저를 통해서 <strong>접근</strong></p>
</blockquote>
<ul>
<li>JPA는 스레드가 하나 생성될 때 마다(매 요청마다) EntityManagerFactory에서 EntityManager를 생성</li>
<li>EntityManager는 내부적으로 DB 커넥션 풀을 사용해서 DB에 붙음</li>
<li>커넥션 풀에서 커넥션을 꺼내 DB에 데이터를 넣거나 조회하는 작업 등을 수행</li>
<li>persist, findBy와 같은 메소드를 가지고 이 영속성 컨텍스트에서 엔티티들을 꺼내오거나 넣게 됨</li>
</ul>
<blockquote>
<p><code>EntityManager.persist(entity);</code> <strong><em>cf.</em></strong> <code>em.persist(member);</code> 
&quot;entity&quot;에 들어가는 객체를 DB에 저장하는 느낌
실제로는 DB에 저장하는 것 ❌ 영속성 컨텍스트에 저장
DB 저장은 <code>tx.commit();</code>에서</p>
</blockquote>
<h3 id="3-엔티티-매니저-vs-영속성-컨텍스트">3) 엔티티 매니저 vs 영속성 컨텍스트</h3>
<ul>
<li>엔티티 매니저를 통해서 영속성 컨텍스트라는 공간에 접근</li>
<li>DB에 가기전에 어떤 공간을 들러야 하는데 엔티티 매니저로 가능하다는 뜻</li>
<li>영속성 컨텍스트는 논리적인 개념이므로 눈에 보이지는 않는다. </li>
<li>엔티티 매니저와 영속성 컨텍스트는 1:1 대응</li>
</ul>
<h2 id="2-엔티티-생명주기">(2) 엔티티 생명주기</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/73e8e852-27ac-44d4-b566-64a2ae6121c8/image.png" alt=""></p>
<ul>
<li><p><strong>비영속 (new/transient)</strong></p>
<ul>
<li>영속성 컨텍스트와 전혀 관계가 없는 새로운 상태</li>
<li><img src="https://velog.velcdn.com/images/boram_han/post/32c7b547-49d0-41a0-971a-851ed8181efe/image.png" alt=""></li>
</ul>
<pre><code class="language-java">  // 객체를 생성만 한 상태(비영속)
  Member member = new Member();
  member.setId(&quot;member1&quot;);
  member.setUsername(&quot;회원1&quot;);</code></pre>
</li>
<li><p><strong>영속 (managed)</strong></p>
<ul>
<li><p>영속성 컨텍스트에 저장된 상태</p>
</li>
<li><p>영속성 컨텍스트에 의해 관리되는 상태</p>
</li>
<li><p>DB에 저장 ❌
```java
// 객체를 생성한 상태(비영속)
Member member = new Member();
member.setId(&quot;member1&quot;);
member.setUsername(&quot;회원1&quot;);</p>
<p>EntityManager em = emf.createEntityManager();
em.getTransaction().begin();</p>
<p>// 객체를 저장한 상태(영속)
em.persist(member);
``</p>
</li>
</ul>
</li>
<li><p><strong>준영속 (detached)</strong></p>
<ul>
<li>영속성 컨텍스트에 저장되었다가 분리된 상태<pre><code class="language-java">// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
em.clear(member);
em.close(member);</code></pre>
</li>
<li><code>.merge()</code> : 분리되었다가 다시 영속성 컨텍스트로   </li>
</ul>
</li>
<li><p><strong>삭제 (removed)</strong></p>
<ul>
<li>삭제된 상태 = 영속성 컨텍스트가 비워진 상태<pre><code class="language-java">// 객체를 삭제한 상태
em.remove(member);</code></pre>
</li>
<li><code>.persist()</code> : 다시 영속성 컨텍스트로</li>
</ul>
</li>
<li><p><code>flush()</code> : 영속성 컨텍스트에서 db에 들어가기 위해사용</p>
</li>
</ul>
<h1 id="3-영속성-컨텍스트의-장점">3. 영속성 컨텍스트의 장점</h1>
<blockquote>
</blockquote>
<ul>
<li>애플리케이션과 DB사이에 왜 영속성 컨텍스트가 필요할까?<ul>
<li>1차 캐시</li>
<li>동일성(identity)보장</li>
<li>트랜잭션을 지원하는 쓰기 지연(Transactional write-behind)</li>
<li>변경감지(Dirty Checking)</li>
<li>지연로딩(Lazy Loading)</li>
</ul>
</li>
</ul>
<h2 id="1-1차-캐시">(1) 1차 캐시</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/8143fc85-ffdb-4339-b3a6-2a89326edc61/image.png" alt=""></p>
<ul>
<li><p>영속성 컨텍스트 내부에 1차 캐시 존재</p>
</li>
<li><p>영속성 컨텍스트 내에 저장되어 있는 값은 별도의 새로운 트랜잭션 없이 1차 캐시 영역 내에서 가져와 트랜잭션을 절약할 수 있음</p>
</li>
<li><p>엔티티를 영속성 컨텍스트에 저장</p>
<pre><code>  Member member = new Member();
  member.setId(&quot;member1&quot;);
  member.setUsername(&quot;회원1&quot;);

  // 객체를 영속성 컨텍스트에 저장(영속)
  em.persist(member);</code></pre></li>
<li><p>key : @Id로 선언한 필드 값, value : 해당 엔티티 자체로 <strong>캐시</strong>에 저장</p>
</li>
</ul>
<blockquote>
<ul>
<li><strong>영속성 컨텍스트와 식별자 값</strong><ul>
<li>엔티티를 식별자값(<code>@id</code>로 테이블의 기본 키와 매핑한 값)으로 구분</li>
<li>영속 상태는 식별자값이 반드시 있어야 한다.</li>
<li>식별자 값이 없으면 예외 발생</li>
</ul>
</li>
</ul>
</blockquote>
<ul>
<li><strong>영속성 컨텍스트와 데이터베이스 저장</strong><ul>
<li>JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 DB에 반영</li>
<li>플러쉬(flush)</li>
</ul>
</li>
</ul>
<blockquote>
<p>이때, <strong>find()</strong>가 일어난다면?</p>
</blockquote>
<pre><code>  Member findMember = em.find(Member.class, &quot;Member1&quot;); </code></pre><p><strong>⭕ 1차 캐시에 데이터가 있을 때</strong></p>
<p><img src="https://velog.velcdn.com/images/boram_han/post/c27d1155-8c00-4b05-96f8-b9de6d4f43fa/image.png" alt=""></p>
<ul>
<li>find()가 일어나는 순간, 엔티티 매니저 내부의 1차 캐시를 먼저 찾음</li>
<li>1차 캐시에 엔티티가 존재하면 바로 반환 -&gt; DB 안 들림</li>
</ul>
<p><strong>❌ 1차 캐시에 데이터가 없을 때</strong>
<img src="https://velog.velcdn.com/images/boram_han/post/12c01324-7915-4317-a41d-77e0313969b8/image.png" alt=""></p>
<ul>
<li>1차 캐시에 없음 -&gt; DB 들림</li>
<li>DB에서 꺼냄 -&gt; 1차 캐시에 저장 -&gt; MEMBER2 반환</li>
<li>이후에 member2를 조회 -&gt; 1차 캐시에 있는 member2가 반환</li>
</ul>
<h2 id="2-동일성identity보장">(2) 동일성(identity)보장</h2>
<p><strong>같은 데이터를 2번 조회할 경우</strong></p>
<pre><code>Member a = em.find(Member.class, &quot;member1&quot;);
Member b = em.find(Member.class, &quot;member1&quot;);

System.out.println(a == b) // true </code></pre><ul>
<li>같은 호출을 반복해도 1차 캐시에 있는 같은 엔티티 반환하여 동일성을 보장</li>
<li>Mybatis는 동일성 보장이 안됨</li>
</ul>
<h2 id="3-트랜잭션을-지원하는-쓰기-지연transactional-write-behind---엔티티-등록">(3) 트랜잭션을 지원하는 쓰기 지연(transactional write-behind) - 엔티티 등록</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/d0527185-858d-4f5e-b34a-18d75f96c41e/image.png" alt=""></p>
<pre><code class="language-java">EntityManager em = EntityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction(); // 트랜잭션

// 트랜잭션 시작
tx.begin();

// 비영속
Member memberA = new Member();
memberA.setId(&quot;member1&quot;);
memberA.setUsername(&quot;홍길동&quot;);

Member memberB = new Member();
memberB.setId(&quot;member1&quot;);
memberB.setUsername(&quot;홍길동&quot;);

// 영속 : 여기까지 Insert SQL을 데이터베이스에 보내지 않는다
em.persist(memberA);
em.persist(memberB);

// 엔티티 등록 : commit하는 순간 데이터베이스에 Insert SQL을 보냄
tx.commit();</code></pre>
<ul>
<li><code>persist()</code>가 일어나면 </li>
<li>엔티티들을 <strong>1차 캐시</strong>에 저장 + <strong>논리적으로 쓰기 지연 SQL 저장소</strong> 라는 곳에 INSERT 쿼리들을 저장</li>
<li>DB에 바로 넣지 않고 기다림</li>
<li><code>commit()</code>할 때, DB에 쿼리들을 보냄</li>
<li>쿼리들을 DB에 보내는 동작이 <code>flush()</code></li>
</ul>
<blockquote>
<p>여러 개의 엔티티를 생성하고 <code>persist</code>를 하더라도 <code>commit()</code>을 하기 전에는 데이터베이스에 저장❌ --&gt; <strong>쓰기 지연</strong></p>
</blockquote>
<h2 id="4-변경감지dirty-checking">(4) 변경감지(Dirty Checking)</h2>
<p><strong>📌 엔티티 수정 코드</strong></p>
<pre><code class="language-java">EntityManager em = EntityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction(); // 트랜잭션

// 트랜잭션 시작
tx.begin();

// 조회 
Member member A = em.find(Member.class, &quot;memberA&quot;);
// 영속 엔티티 데이터 수정
memberA.setUsername(&quot;hi&quot;);
memberA.setAge(10);

// em.update(member); 이런 코드 없이도 update 가능
tx.commit();</code></pre>
<ul>
<li><p><strong>엔티티 수정</strong> -&gt; <code>update()</code>, <code>persist()</code> 필요없음</p>
</li>
<li><p>데이터만 <strong>set</strong>하고 트랜잭션을 <strong>커밋</strong>하면 <strong>자동으로 업데이트</strong> 쿼리</p>
</li>
<li><p>HOW? 변경 감지(Dirty Checking)를 통해
<img src="https://velog.velcdn.com/images/boram_han/post/af9f6f05-736b-4e29-a007-268c4dae62ad/image.png" alt=""></p>
</li>
<li><p><code>persist()</code>로 1차 캐시에 저장할 때 동시에 스냅샷 필드도 저장</p>
</li>
<li><p><code>commit()</code>또는 <code>flush()</code> 실행 시, 엔티티와 스냅샷을 비교해서 변경사항이있으면 UPDATE SQL을 알아서 만들어서 DB에 저장</p>
</li>
</ul>
<h1 id="4-플러시">4. 플러시</h1>
<blockquote>
<p>영속성 컨텍스트의 <strong>변경내용</strong>을 데이터베이스에 <strong>반영</strong>하는 것</p>
</blockquote>
<h2 id="⚡-flush">⚡ flush</h2>
<h3 id="플러시가-발생하면">플러시가 발생하면</h3>
<ul>
<li>변경을 감지(Dirty Checking)</li>
<li>수정된 엔티티를 쓰기 지연 SQL 저장소(transactional write-behind)에 등록</li>
<li>쓰기 지연 SQL 저장소(transactional write-behind)의 <strong>쿼리</strong>를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)</li>
<li>플러시가 발생한다고 커밋❌ 플러시 다음에 커밋</li>
</ul>
<h3 id="영속성-컨텍스트를-플러시-하는-법">영속성 컨텍스트를 플러시 하는 법</h3>
<ul>
<li>직접 호출 : <code>em.flush()</code></li>
<li>자동 호출 : <code>tx.commit()</code>(트랜잭션 커밋), JPQL 쿼리 실행</li>
</ul>
<h3 id="플러시의-역할">플러시의 역할</h3>
<ul>
<li>영속성 컨텍스트를 비우지 않음</li>
<li>영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화</li>
<li>플러시가 동작할 수 있는 이유는 데이터베이스 트랜잭션이라는 작업 단위(개념)가 있기 때문 → 커밋 직전에만 동기화하면 됨 </li>
<li>JPA는 기본적으로 데이터를 맞추거나 동시성에 관련된 것들은 데이터베이스 트랜잭션에 위임</li>
</ul>
<h1 id="5-준영속상태">5. 준영속상태</h1>
<blockquote>
</blockquote>
<ul>
<li>영속상태<ul>
<li>1차 캐시에 올라간 상태</li>
<li>엔티티 매니저가 관리하는 상태</li>
<li><code>em.persist()</code>로 영속성 컨텍스트에 저장한 상태</li>
<li><code>em.find()</code>로 조회를 할 때, 영속성 컨텍스트 1차 캐시에 없어서 DB에서 조회해와서 1차 캐시에 저장한 상태<ul>
<li>준영속상태</li>
</ul>
</li>
<li>영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)된 상태</li>
<li>영속성 컨텍스트가 제공하는 기능을 사용하지 못함. 쿼리 안나감.</li>
</ul>
</li>
</ul>
<h3 id="준영속-상태로-만드는-방법">준영속 상태로 만드는 방법</h3>
<ul>
<li><code>em.detach(entity)</code><ul>
<li>특정 엔티티만 준영속 상태로 전환</li>
</ul>
</li>
<li><code>em.clear()</code><ul>
<li>영속성 컨텍스트를 완전히 초기화</li>
<li>clear는 테스트 케이스 작성시에 도움</li>
</ul>
</li>
<li><code>em.close()</code>  <ul>
<li>영속성 컨텍스트를 종료</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JPA] JPA 소개]]></title>
            <link>https://velog.io/@boram_han/Spring-JPA-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@boram_han/Spring-JPA-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Sat, 29 Apr 2023 18:54:25 GMT</pubDate>
            <description><![CDATA[<h1 id="📋-jpa">📋 JPA</h1>
<h2 id="1-jpa">(1) JPA</h2>
<ul>
<li>Java Persistence API</li>
<li>Java 진영에서 <strong>ORM(Object-Relational Mapping) 기술 표준</strong>으로 사용하는 인터페이스 모음 </li>
<li>자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 <strong>인터페이스</strong></li>
<li>JPA는 자바 표준 interface만을 제공 -&gt; 여러 업체에서 구현체 기술을 만들어놓음<ul>
<li>인터페이스 = 설계문서</li>
<li>설계문서를 구현한 라이브러리(구현체) : eclipseLink, <strong><em>hibernate</em></strong>, dataNucleus 등등...
<img src="https://velog.velcdn.com/images/boram_han/post/6197cb5c-8ce9-490a-9192-924a7849ab2f/image.png" alt=""></li>
</ul>
</li>
</ul>
<h2 id="2-orm-object-relational-mapping">(2) ORM (Object-Relational Mapping)</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/5a454950-acc4-4c9d-87ab-5f1f709253ab/image.png" alt=""></p>
<ul>
<li><strong>객체</strong>와 관계형 데이터베이스의 <strong>데이터(= RDB(Relational DataBase)의 테이블)</strong>를 자동으로 <strong>매핑(연결)</strong> 해줌<ul>
<li>기술적으로는 어플리케이션의 객체를 RDB테이블에 자동으로 <strong>영속화</strong> 해주는 것 </li>
<li><strong>객체</strong>지향 프로그래밍은 클래스를 사용하고, <strong>관계</strong>형 데이터베이스는 테이블을 사용한다.</li>
<li><strong>객체</strong> 모델과 <strong>관계</strong>형 모델간에 <strong>불일치</strong>가 존재한다.</li>
<li><strong>ORM</strong>을 통해 객체간의 관계를 바탕으로 SQL을 자동으로 생성하여 <strong>불일치를 해결</strong>한다.</li>
</ul>
</li>
<li>데이터베이스 데이터 <strong>_&lt;-----mapping-----&gt;_</strong> object 필드</li>
<li><strong>객체</strong>를 통해 간접적으로 데이터베이스 <strong>데이터</strong>를 다룸<blockquote>
<ul>
<li>객체는 객체대로, RDB는 RDB대로 설계 후</li>
<li>그 사이의 불일치는 ORM이 SQL을 자동 생성하여 해결해 준다는 뜻</li>
<li>JPA는 ORM을 구현한 기술 중 하나 : 객체를 통해 데이터베이스를 조작할 수 있는 API를 제공하는 것</li>
</ul>
</blockquote>
</li>
</ul>
<h2 id="3-jpa-장단점">(3) JPA 장단점</h2>
<h3 id="1-장점">1) 장점</h3>
<h4 id="①-개발자는-비즈니스-로직을-구성하는데만-집중할-수-있음">① 개발자는 비즈니스 로직을 구성하는데만 집중할 수 있음</h4>
<ul>
<li>SQL문이 아닌 method를 통해 DB를 조작하므로 객체 모델을 이용하여 로직을 구성하면 됨. (내부적으로는 쿼리를 생성하여 DB를 조작함. 하지만 개발자는 이를 신경쓰지 않아도 됨)<h4 id="②-코드의-가독성을-높임">② 코드의 가독성을 높임</h4>
</li>
<li>Query와 같이 필요한 선언문, 할당 등의 부수적인 코드가 줄어들어 각종 객체에 대한 코드를 별도로 작성하여 가독성 높임<h4 id="③-객체지향적인-코드-작성이-가능하여-생산성이-증가">③ 객체지향적인 코드 작성이 가능하여 생산성이 증가</h4>
</li>
<li>매핑하는 정보가 Class로 명시하므로 ERD를 보는 의존도를 낮출 수 잇음<h4 id="④-유지보수-및-리팩토링에-유리">④ 유지보수 및 리팩토링에 유리</h4>
</li>
<li>기존방식에서 MySql 데이터베이스를 사용하다가 PostgreSQL (대용량 데이터) 로 변환한다고 가정해보면, 새로운 쿼리를 짜야하는 경우가 발생. 이런 경우에 ORM기술을 사용하면 쿼리를 수정할 필요가 없음</li>
</ul>
<h3 id="2-단점">2) 단점</h3>
<h4 id="①-크고-복잡한-프로젝트의-설계가-잘못되면-속도-저하-및-일관성을-무너뜨리는-문제점-발생">① 크고 복잡한 프로젝트의 설계가 잘못되면 속도 저하 및 일관성을 무너뜨리는 문제점 발생</h4>
<h4 id="②-결국엔-sql문을-써야할-수도-있음">② 결국엔 SQL문을 써야할 수도 있음</h4>
<ul>
<li>복잡하고 무거운 Query는 속도를 위해 별도의 튜닝이 필요하기 때문<h4 id="③-학습비용이-비쌈">③ 학습비용이 비쌈</h4>
</li>
</ul>
<h2 id="4-데이터베이스-방언">(4) 데이터베이스 방언</h2>
<ul>
<li>JPA는 특정 데이터베이스에 종속 ❌</li>
<li>각각의 데이터베이스가 제공하는 SQL문법과 함수는 조금씩 다름<ul>
<li>가변문자 : Oracle varchar2, MySQL varchar</li>
<li>문자열을 자르는 함수 : Oracle substr(), SQL표준 substring()</li>
</ul>
</li>
<li>데이터베이스 방언은 그 중 특정 데이터베이스 방언으로 쿼리를 작성하라는 뜻<pre><code>// 오라클 10g 방언으로 사용
  &lt;property name=&quot;hibernate.dialect&quot; value=&quot;org.hibernate.dialect.Oracle10gDialect&quot; /&gt;</code></pre><h2 id="5-jpa-사용하기">(5) JPA 사용하기</h2>
<h3 id="1-jpa-dependencies-설정">1) JPA dependencies 설정</h3>
</li>
<li>build.gradle &gt; dependencies에 추가<pre><code class="language-java">  implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;
  runtimeOnly &#39;com.oracle.database.jdbc:ojdbc8&#39;</code></pre>
</li>
<li>프로젝트 생성 시 추가
<img src="https://velog.velcdn.com/images/boram_han/post/b22b3832-3800-431e-b0ec-bae39949e0ff/image.png" alt=""></li>
</ul>
<h3 id="2-xml-파일-생성">2) .xml 파일 생성</h3>
<ul>
<li><p>src/main/resources에 폴더 <code>META-INF</code>생성</p>
</li>
<li><p>META-INF에 <code>persistence.xml</code> 파일 생성
<img src="https://velog.velcdn.com/images/boram_han/post/11ec93e1-8cf0-4ec1-9e68-62d21676cd00/image.png" alt=""></p>
</li>
<li><p><code>persistence.xml</code>에 추가</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;persistence version=&quot;2.2&quot;
  xmlns=&quot;http://xmlns.jcp.org/xml/ns/persistence&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd&quot;&gt;
  &lt;persistence-unit name=&quot;hello&quot;&gt;
      &lt;properties&gt;
          &lt;!-- 필수 속성 --&gt;
          &lt;property name=&quot;javax.persistence.jdbc.driver&quot; value=&quot;oracle.jdbc.driver.OracleDriver&quot; /&gt;
          &lt;!-- oracle id, password --&gt;
          &lt;property name=&quot;javax.persistence.jdbc.user&quot; value=&quot;spring&quot; /&gt;
          &lt;property name=&quot;javax.persistence.jdbc.password&quot; value=&quot;spring&quot; /&gt;
          &lt;property name=&quot;javax.persistence.jdbc.url&quot; value=&quot;jdbc:oracle:thin:@localhost:1521:xe&quot; /&gt;
&lt;!--                 오라클 10g 방언으로 사용 --&gt;
          &lt;property name=&quot;hibernate.dialect&quot; value=&quot;org.hibernate.dialect.Oracle10gDialect&quot; /&gt;
&lt;!--              Postgre : 웬만한 쿼리 다 가능 --&gt;
&lt;!--              &lt;property name=&quot;hibernate.dialect&quot; value=&quot;org.hibernate.dialect.PostgreSQL10Dialect&quot; /&gt;  --&gt;
          &lt;property name=&quot;hibernate.hbm2ddl.auto&quot; value=&quot;create&quot; /&gt;
          &lt;!-- value = none or value = create --&gt;

          &lt;!-- 옵션 --&gt;
          &lt;!-- 콘솔에 하이버네이트가 실행하는 SQL문 출력 --&gt;
          &lt;property name=&quot;hibernate.show_sql&quot; value=&quot;true&quot; /&gt;
          &lt;!-- SQL 출력 시 보기 쉽게 정렬 --&gt;
          &lt;property name=&quot;hibernate.format_sql&quot; value=&quot;true&quot; /&gt;
          &lt;!-- 쿼리 출력 시 주석(comments)도 함께 출력 --&gt;
          &lt;property name=&quot;hibernate.use_sql_comments&quot; value=&quot;true&quot; /&gt;
      &lt;/properties&gt;
  &lt;/persistence-unit&gt;
&lt;/persistence&gt;</code></pre><h3 id="3-jpa-사용하기">3) JPA 사용하기</h3>
<h4 id="①-entity-적용하기">① @Entity 적용하기</h4>
</li>
<li><p>@Entity 어노테이션이 붙은 클래스는 데이터베이스에서 하나의 테이블과 매핑</p>
</li>
<li><p>객체를 관계형 데이터베이스 테이블과 매핑하기 위해 사용</p>
</li>
</ul>
<pre><code class="language-java">  @Entity
  @Getter @Setter
  public class Member {

      @Id
      private Long id;
      private String name;
  }</code></pre>
<h4 id="②-jpa-사용하는-클래스-만들기">② jpa 사용하는 클래스 만들기</h4>
<ul>
<li><p>엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 사용</p>
</li>
<li><p>transaction : 데이터베이스의 상태를 변화시키기 위해 수행하는 작업 단위</p>
<pre><code class="language-java">public class JpaMain {

  public static void main(String[] args) {
      EntityManagerFactory emf 
          = Persistence.createEntityManagerFactory(&quot;hello&quot;);
      EntityManager em = emf.createEntityManager();

      // transaction 선언
      EntityTransaction tx = em.getTransaction();
      // transaction 시작 
      tx.begin();

      try {
          // 추가 
          Member member = new Member();
          member.setId(2L);
          member.setName(&quot;UserB&quot;);
          em.persist(member);

          // 회원조회
          Member findMember = em.find(Member.class, 1L);
          System.out.println(&quot;findMember.id : &quot; + findMember.getId());
          System.out.println(&quot;findMember.name : &quot; + findMember.getName());

          // 회원 수정
          findMember.setName(&quot;HelloJPA&quot;);

          // 회원 삭제
          em.remove(findMember);

          // transaction 커밋
          tx.commit();
          // 커밋과 커밋 사이를 하나의 트랜잭션 단위 = 비즈니스 단위
          // 오류 나면 트랜잭션 자체가 롤백
      }catch (Exception e) {
          tx.rollback();
      } finally {
          em.close();
          emf.close();
      }
  }
}</code></pre>
</li>
</ul>
<h4 id="③-ctrl--f11로-실행-후-디비버에서-테이블-확인">③ ctrl + f11로 실행 후 디비버에서 테이블 확인</h4>
<p><img src="https://velog.velcdn.com/images/boram_han/post/f22864d9-6d00-45a5-be7f-910553db4e18/image.png" alt=""></p>
<blockquote>
<p><strong>jpa 세팅을 위한 기본 프레임</strong></p>
</blockquote>
<pre><code class="language-java">package com.codingbox.jpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class Jpabasic {
    public static void main(String[] args) {
        EntityManagerFactory emf 
            = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();        
        EntityTransaction tx = em.getTransaction();
        tx.begin();        
        try {
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}</code></pre>
<blockquote>
<ul>
<li>@Entity 있으면 기본 프레임만 실행해도 테이블 생성</li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 필터와 인터셉터]]></title>
            <link>https://velog.io/@boram_han/Spring-%ED%95%84%ED%84%B0%EC%99%80-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0</link>
            <guid>https://velog.io/@boram_han/Spring-%ED%95%84%ED%84%B0%EC%99%80-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0</guid>
            <pubDate>Sat, 29 Apr 2023 17:36:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="🔒-필터-인터셉터-사용-예시">🔒 필터, 인터셉터 사용 예시</h3>
<ul>
<li>로그인된 사람들만 특가를 확인하도록 할 때</li>
<li>모니터링만 할 수 있고 데이터 조작은 안되는 관리자모드 설정 할 때</li>
</ul>
</blockquote>
<h1 id="1-🧪-필터">1. 🧪 필터</h1>
<h2 id="1-필터-흐름">(1) 필터 흐름</h2>
<ul>
<li>Http Request -&gt; WAS -&gt; <strong>필터</strong> -&gt; 서블릿 -&gt; 컨트롤러 -&gt; ...<h2 id="2-필터-생성">(2) 필터 생성</h2>
<h3 id="1-필터-생성-예시">1) 필터 생성 예시</h3>
</li>
<li><code>Filter 인터페이스</code> 상속</li>
<li>로그인이 필요한 url 접속을 위해 로그인 여부를 확인하는 필터 생성</li>
</ul>
<pre><code class="language-java">// 필터 인터페이스 구현
public class LoginCheckFilter implements Filter{

    // 화이트리스트 -&gt; 로그인 검증이 필요없음
    private static final String[] whitelist = 
        {&quot;/&quot;, &quot;/members/add&quot;, &quot;/login&quot;, &quot;/logout&quot;, &quot;/css/*&quot;};

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        //request, response 생성
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestURI = httpRequest.getRequestURI();
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        try {
            System.out.println(&quot;인증 필터 체크 시작 : &quot; + requestURI);

            /* whitelist가 아닌 URI만을 검증 */
            if( isLoginCheckPath(requestURI) ) {
                /* session에 값 x -&gt; 단순 url로 들어온 것 
                   미인증 된 사람 = 로그인 안 되어 있는 사람 */
                HttpSession session = httpRequest.getSession(false);
                if( session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null  ) {                    
                    // 로그인창으로 redirect
                    // 예를들어, 내가 로그인 하지 않은 상태로 items url을 접근 
                    // -&gt; 로그인 페이지가 보여져야 한다.
                    httpResponse.sendRedirect(&quot;/login?redirectURL=&quot;+requestURI);
                    return; // **중요 : 미 인증 사용자는 다음으로 진행하지않고 종료

                }                
            }

            // if문 다음으로 진행
            chain.doFilter(request, response);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(&quot;인증 체크 필터 종료 : &quot; + requestURI);
        }


    }

    // whitelist인지 아닌지 체크 : whitelist인 경우 인증 체크 하지 않음
    private boolean isLoginCheckPath(String requestURI) {
        // !를 넣음으로서 whitelist에 없을 때 return하도록
        return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
    }
}</code></pre>
<blockquote>
<ul>
<li><strong>PatternMatchUtils</strong> <ul>
<li>간단한 패턴 매치 판별해주는 유틸 </li>
</ul>
</li>
</ul>
</blockquote>
<ul>
<li><code>return !PatternMatchUtils.simpleMatch(whitelist, requestURI);</code><ul>
<li>들어온 uri가 whitelist에 있는 지 확인</li>
</ul>
</li>
</ul>
<h3 id="2-필터-생성-로직">2) 필터 생성 로직</h3>
<p>① <code>private static final String[] whitelist = {&quot;/&quot;, &quot;/members/add&quot;, &quot;/login&quot;, &quot;/logout&quot;, &quot;/css/*&quot;};</code></p>
<ul>
<li>인증 필터를 적용해도 홈, 회원가입, 로그인화면, css같은 리소스에는 접근가능 해야함</li>
<li>화이트 리스트 경로는 인증과 무관하게 항상 허용</li>
<li>화이트 리스트를 제외한 나머지 보든 경로에는 인증체크 로직을 적용</li>
</ul>
<p>②<code>isLoginCheckPath(String requestURI)</code></p>
<ul>
<li>화이트 리스트를 제외한 모든 경우에 인증 체크 로직을 적용</li>
</ul>
<p>③ <code>return;</code></p>
<ul>
<li>필터를 더이상 진행하지 않는다라는 뜻의 코드</li>
<li>이후 필터는 물론 서블릿, 컨트롤러가 더는 호출되지 않음</li>
<li>앞서 redirect를 사용했기 때문에 redirect가 응답으로 적용되고 요청이 끝</li>
</ul>
<p>④ <code>httpResponse.sendRedirect(&quot;/login?redirectURL=&quot;+requestURI);</code></p>
<ul>
<li>미 인증된 사용자는 로그인 화면으로 리다이렉트</li>
<li>로그인 이후에 다시 홈으로 이동해버리면, 원하는 경로를 다시 찾아가야 해서 불편함</li>
<li>이를 해소해 주는 코드</li>
</ul>
<blockquote>
<p><code>/login?redirectURL=&quot;+requestURI</code></p>
</blockquote>
<ul>
<li>사용자가 들어가고 싶어하는 페이지를 파라미터로 로그인 페이지를 열어줌</li>
<li>로그인이 완료된 후 해당 페이지로 넘어가려면 login controller에서 한번 더 처리 필요</li>
<li><pre><code class="language-java">@PostMapping(&quot;/login&quot;)
public String loginv3(@ModelAttribute LoginForm form, 
      Model model, RedirectAttributes redirectAttributes, HttpServletRequest request,
      // @RequestParam(defaultValue = &quot;/&quot;) 추가 : 로그인 완료 된 후 사용자가 원하는 페이지로 넘어갈 수 있도록
      @RequestParam(defaultValue = &quot;/&quot;) String redirectURL    
      // 파라미터가 없으면 /, 있으면 redirectURL에 넣어줌
      ) {
          ~ 로그인 로직 ~
          return &quot;redirect:&quot; + redirectURL;
      }</code></pre>
</li>
<li>jsessionID 숨겨주기 &gt; <strong>application.properties</strong>에 추가<pre><code class="language-java">#jsessionID 노출되지 않도록
server.servlet.session.tracking-modes=cookie</code></pre>
</li>
</ul>
<h2 id="3-필터-적용">(3) 필터 적용</h2>
<h3 id="1-필터-적용-예시">1) 필터 적용 예시</h3>
<ul>
<li><code>@Configuration</code> <ul>
<li>class에 적용 시킴으로서 필터 사용 </li>
</ul>
</li>
<li><code>@Bean</code> <ul>
<li>스프링 컨테이너가 해당 메서드를 빈으로 인식</li>
</ul>
</li>
<li><code>FilterRegistrationBean</code> : 필터 등록 시 사용<pre><code>//@Configuration
public class WebConfig {
</code></pre></li>
</ul>
<p>//    @Bean
    public FilterRegistrationBean logFilter() {
        FilterRegistrationBean<Filter> filterRegistrationBean
            = new FilterRegistrationBean&lt;&gt;();</p>
<pre><code>    filterRegistrationBean.setFilter(new LoginCheckFilter());
    filterRegistrationBean.setOrder(1);
    filterRegistrationBean.addUrlPatterns(&quot;/*&quot;);

    return filterRegistrationBean;
}</code></pre><p>}</p>
<pre><code>### 2) 필터 적용 로직
①```filterRegistrationBean.setFilter(new 필터생성className())```
- 필터 생성한 클래스 이름 불러와서 set
②```filterRegistrationBean.setOrder(1)```
- 필터 적용할 순서
- 1 : 첫번째
③```filterRegistrationBean.addUrlPatterns(&quot;/*&quot;)```
- 필터 적용할 경로조건
- ```/*``` : 전체 경로 

# 2. 🧪 인터셉터
## (1) 인터셉터
- 서블릿 필터와 같이 웹과 관련된 공통 관심 사항을 효과적으로 해결할 수 있는 기술
- 서블릿 필터가 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 스프링 mvc가 제공하는 기술
- 둘 다 웹과 관련된 공통 관심사항을 처리 but, 적용되는 순서와 범위, 그리고 사용방법이 다름
- 필터보다 인터셉터가 더 디테일한 컨트롤이 가능
## (2) 인터셉터 흐름
![](https://velog.velcdn.com/images/boram_han/post/f6607354-972e-489b-961b-358b9113bfab/image.png)
### 1) 기본 흐름
- HTTP요청 -&gt; WAS -&gt; **필터** -&gt; 서블릿 -&gt; **스프링 인터셉터** -&gt; 컨트롤러
    - 컨트롤러 호출 직전에 호출
    - 디스패처 서블릿 이후에 등장
        - 스프링 MVC가 제공하는 기능이기 때문에
         - 스프링 mvc의 시작점이 디스패처 서블릿
    - 스프링 인터셉터에도 URL패턴을 적용 가능

### 2) 스프링 인터셉터 체인
- HTTP요청 -&gt; WAS -&gt; 필터 -&gt; 서블릿 -&gt; **인터셉터1** -&gt;
**인터셉터2** -&gt; 컨트롤러
  - 스프링 인터셉터는 체인으로 구성
    - 중간에 인터셉터를 자유롭게 추가 가능

## (3) 인터셉터 생성
### 1) 인터셉터 생성 기본 예시
- ```HandlerIntercepter 인터페이스```를 구현
- HandlerIntercepter 인터페이스 **메서드**
  - 서블릿 필터는 ```doFilter()``` 1개, 인터셉터는 3개로 세분화
    - ```preHandle``` : 컨트롤러 호출 전에 호출
      - preHandle의 응답값이 **true**이면 다음으로 진행, **false**면 진행 ❌
      -  **false**인 경우 나머지 인터셉터는 물론이고, 핸들러 어댑터도 호출 ❌
    - ```postHandle``` : 컨트롤러 호출 후에 호출
    - ```afterHandle``` : 뷰가 렌더링 된 이후에 호출</code></pre><p>public class LogIntercepter implements HandlerInterceptor{</p>
<pre><code>public static final String LOG_ID = &quot;logId&quot;;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    String requestURI = request.getRequestURI();
    String uuid = UUID.randomUUID().toString();

    request.setAttribute(LOG_ID, uuid);

    System.out.println(&quot;[intercepter] uuid : &quot; + uuid);
    System.out.println(&quot;[intercepter] requestURI : &quot; + requestURI);

    return true; // false로 전환시 진행하지 않음

}</code></pre><p>// 컨트롤러 호출 후 modelAndView<br>    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println(&quot;[intercepter] postHandle : &quot; + modelAndView);
    }
// 뷰가 렌더리 된 후 logId, requestURI 출력<br>    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {</p>
<pre><code>    String requestURI = request.getRequestURI();
    String logId = (String)request.getAttribute(LOG_ID);
    // pre에서 해놨던 랜덤 값을 after에서 한번 더 꺼내옴

    System.out.println(&quot;[intercepter] logId : &quot; + logId);
    System.out.println(&quot;[intercepter] requestURI : &quot; + requestURI);
}</code></pre><p>}</p>
<pre><code>### 2) 인터셉터 생성 응용 예시
- 기본적으로 ```preHandle```만 사용</code></pre><p>public class LoginCheckInterceptor implements HandlerInterceptor{</p>
<pre><code>@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    String requestURI = request.getRequestURI();
    System.out.println(&quot;[인증 체크 인터셉터 실행] : &quot; + requestURI);
    // 아이디에 대한 정보 session으로 받기
    HttpSession session = request.getSession(false);

    if( session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null  ) {
        System.out.println(&quot;[미인증 사용자 요청]&quot;);
        // 로그인으로 redirect
        response.sendRedirect(&quot;/login?redirectURL=&quot;+requestURI);
        return false;
    }
    return true;    
}</code></pre><p>}</p>
<pre><code>## (4) 인터셉터 적용
### 1) 인터셉터 적용 예시
- ```@Configuration``` : filter와 똑같이 어노테이션 적용
- ```WebMvcConfigurer``` 인터페이스 상속</code></pre><p>@Configuration
public class WebConfig2 implements WebMvcConfigurer{</p>
<pre><code>    registry.addInterceptor(new LoginCheckInterceptor())
            .order(1)
            .addPathPatterns(&quot;/**&quot;)     // 모든 경로 전체 가능
            .excludePathPatterns(&quot;/&quot;, &quot;/members/add&quot;, &quot;/login&quot;, &quot;/logout&quot;, &quot;/css/**&quot;); // 이 경로들을 제외

}</code></pre><p>}</p>
<pre><code>### 2) 인터셉터 적용 로직
- ```.addInterceptor(new 인터페이스생성className())```
    - 인터페이스 생성한 클래스 이름 불러와서 add
- ```.order(1)```
    - 인터페이스 적용할 순서
- ```.addPathPatterns(&quot;/**&quot;)```
    - 구현할 경로
- ```.excludePathPatterns(&quot;/css/**&quot;, &quot;/*.ico&quot;, &quot;/error&quot;);```
    - 제외할 경로
&gt; **인터셉터 경로**
&gt; - ```/**```
    - 모든 경로에 대해서 적용할때
    - 예 : ```/css/**```
&gt; - ```/*```
    - 1개의 어떠한 경로에 상관없이 사용, 2번째 경로를 조율하고 싶을 때
    - 예 : ```/*.ico```</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 쿠키, 세션]]></title>
            <link>https://velog.io/@boram_han/Spring-%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98</link>
            <guid>https://velog.io/@boram_han/Spring-%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98</guid>
            <pubDate>Sat, 29 Apr 2023 15:49:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="🔒-로그인-처리하기---쿠키-세션-사용">🔒 로그인 처리하기 - 쿠키, 세션 사용</h3>
</blockquote>
<h1 id="1-🍪-쿠키">1. 🍪 쿠키</h1>
<h2 id="1-쿠키">(1) 쿠키</h2>
<ul>
<li><strong>쿠키</strong> : 서버에서 전달받은 내용을 사용자 측에서 보관</li>
<li><strong>쿠키의 종류</strong><ul>
<li><code>영속 쿠키</code> : 만료 날짜를 입력하면 해당 날짜까지 유지</li>
<li><code>세션 쿠키</code> : 만료 날짜를 생략하면 브라우저 종료시 까지만 유지</li>
</ul>
</li>
</ul>
<h2 id="2-로그인-페이지-쿠키-사용">(2) 로그인 페이지 쿠키 사용</h2>
<h3 id="1-쿠키-생성-후-추가">1) 쿠키 생성 후 추가</h3>
<pre><code class="language-java">    // 쿠키 생성
    Cookie idCookie = new Cookie(&quot;memberId&quot;, String.valueOf(loginMember.getId()));
    // 쿠키 보내기
    response.addCookie(idCookie);
    // 쿠키 생성 후 &quot;/&quot;(home)으로
    return &quot;redirect:/&quot;;</code></pre>
<h3 id="2-home화면-로그인-전후-controll">2) home화면 로그인 전/후 controll</h3>
<ul>
<li><code>@CookieValue</code> : 쿠키에 담긴 값 사용</li>
<li><code>required = false</code> : 쿠키 정보가 없어도 접근 가능 = 로그인 안 한 사용자도 사용 가능</li>
</ul>
<pre><code class="language-java">public String homeLogin(
            @CookieValue(name=&quot;memberId&quot;, required = false) Long memberId, Model model) {

        // 로그인 한 사용자가 아니라면 home으로
        if(memberId == null) {
            return &quot;home&quot;;
        }
        // db 조회를 한 후, 사용자가 없으면 다시 home으로 보낸다.
        Member loginMember = memberRepository.findById(memberId);
        if( loginMember == null ) {
            return &quot;home&quot;;
        }
        // 로그인에 성공한 사람은 loginHome화면으로 이동        model.addAttribute(&quot;member&quot;,loginMember);
        return &quot;loginHome&quot;;
    }</code></pre>
<h3 id="3-추가한-쿠키-받아서-로그아웃">3) 추가한 쿠키 받아서 로그아웃</h3>
<ul>
<li>expire 메서드 만들기<pre><code class="language-java">  private void expireCookie(HttpServletResponse response, String CookieName) {
      Cookie cookie = new Cookie(CookieName, null);
      cookie.setMaxAge(0);
      response.addCookie(cookie);
  }
</code></pre>
</li>
</ul>
<pre><code>- expire 메서드 사용
```java
    @PostMapping(&quot;/logout&quot;)
    public String logout(HttpServletResponse response) {
        expireCookie(response, &quot;memberId&quot;);
        return &quot;redirect:/&quot;;
    }</code></pre><h1 id="2-📲-session">2. 📲 session</h1>
<h2 id="1-session">(1) session</h2>
<ul>
<li><p>서버에 session이라고 하는 공간에 저장</p>
<blockquote>
<ul>
<li><code>cookie</code> : HttpServletResponse </li>
<li><code>session</code> : HttpServletRequest </li>
</ul>
</blockquote>
</li>
<li><p>웹 페이지 사용하다 시간이 지나면 자동으로 로그아웃</p>
<blockquote>
<p>project &gt; <strong>application.properties</strong>에 추가</p>
<pre><code>   # session time out
   server.servlet.session.timeout=1800</code></pre><ul>
<li>시간단위 : 초 단위</li>
<li>1800 -&gt; 30분</li>
</ul>
</blockquote>
<h2 id="2-session-옵션">(2) session 옵션</h2>
</li>
<li><p><code>request.getSession(true);</code></p>
<ul>
<li>세션이 있으면 기존 세션을 반환</li>
<li>세션이 없으면 새로운 세션을 생성해서 반환</li>
</ul>
</li>
<li><p><code>request.getSession(false);</code></p>
<ul>
<li>세션이 있으면 기존 세션을 반환</li>
<li>세션이 없으면 새로운 세션을 생성하지 않고 <strong>null</strong>로 반환</li>
</ul>
</li>
<li><p><code>request.getSession();</code></p>
<ul>
<li>신규 세션을 생성하는 <code>request.getSession(true);</code>와 동일</li>
</ul>
</li>
</ul>
<h2 id="3-로그인-페이지-session-사용">(3) 로그인 페이지 session 사용</h2>
<h3 id="1-세션에서-사용될-상수를-정의해놓은-클래스-만들기">1) 세션에서 사용될 상수를 정의해놓은 클래스 만들기</h3>
<ul>
<li>세션에서 로그인한 회원 정보를 저장할 때 사용</li>
<li>오타 발생 가능성을 줄임<pre><code class="language-java">  public class SessionConst {
      public static final String LOGIN_MEMBER = &quot;loginMember&quot;;
  }</code></pre>
<h3 id="2-세션-생성">2) 세션 생성</h3>
<pre><code class="language-java">  //세션 생성    
  HttpSession session = request.getSession();
  // 속성 설정
  session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
  // 세션 생성 후 &quot;/&quot;(home)으로
  return &quot;redirect:/&quot;;</code></pre>
<h3 id="3-세션-받아서-사용">3) 세션 받아서 사용</h3>
<pre><code class="language-java">  // 세션 받기
  HttpSession session = request.getSession(false);
  // 받은 세션 사용
  Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER);   </code></pre>
<h3 id="4-세션-받아서-사용-②---sessionattribute">4) 세션 받아서 사용 ② - @SessionAttribute</h3>
<pre><code class="language-java">  @GetMapping(&quot;/&quot;)
  public String homeLoginv3(
      @SessionAttribute(name=SessionConst.LOGIN_MEMBER, required = false)Member loginMember ,
      Model model) </code></pre>
</li>
<li>SessionConst.LOGIN_MEMBER를 key값으로 Member에 할당</li>
</ul>
<h3 id="5-세션-삭제">5) 세션 삭제</h3>
<ul>
<li><code>session.invalidate();</code> 사용해서 세션 삭제<pre><code>  @PostMapping(&quot;/logout&quot;)
  public String logoutv2(HttpServletRequest request) {
      // 세션 받아서
      HttpSession session = request.getSession(false);
      if( session != null ) {
          // 세션 삭제
          session.invalidate();
      }
      return &quot;redirect:/&quot;;
  }</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 경로, Redirect]]></title>
            <link>https://velog.io/@boram_han/Spring-%EA%B2%BD%EB%A1%9C-Redirect</link>
            <guid>https://velog.io/@boram_han/Spring-%EA%B2%BD%EB%A1%9C-Redirect</guid>
            <pubDate>Sat, 29 Apr 2023 10:20:26 GMT</pubDate>
            <description><![CDATA[<h1 id="1-🔀-경로">1. 🔀 경로</h1>
<h2 id="1-일반-경로">(1) 일반 경로</h2>
<ul>
<li><code>/</code> : 최상위 루트 경로 </li>
<li><code>./</code> : 현재 위치의 폴더를 의미</li>
<li><code>../</code> :  현재 위치의 상단 폴더</li>
<li><em>💡 예시*</em><pre><code>   &lt;link href=&quot;../css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot;&gt;</code></pre><h2 id="2-타임리프로-경로-설정">(2) 타임리프로 경로 설정</h2>
</li>
<li>링크 URL 표현식 <code>@{...}</code> 사용</li>
<li><em>💡 예시*</em></li>
<li><code>th:href</code><pre><code>  &lt;th:href=&quot;@{/basic/items/{itemId}(itemId=${item.id})}&quot;&gt;</code></pre></li>
<li>괄호 생략<pre><code>  &lt;th:href=&quot;@{|/basic/items/${item.id}|}&quot;&gt;</code></pre></li>
<li><code>th:onclick</code><ul>
<li><code>|location.href=&#39;@{...}&#39;|</code><pre><code>&lt;button th:onclick=&quot;|location.href=&#39;@{/basic/items/{itemId}/edit(itemId=${item.id})}&#39;|&quot; type=&quot;button&quot;&gt;</code></pre></li>
</ul>
</li>
</ul>
<h1 id="2-🔙-redirect">2. 🔙 Redirect</h1>
<h2 id="1-redirect">(1) Redirect</h2>
<ul>
<li><p>리퀘스트에 내용을 담지 않고 새로운 요청을 보낼 때 하는 방식</p>
</li>
<li><p>스프링은 <code>redirect:/...</code> 으로 편리하게 리다이렉트를 지원</p>
<pre><code>  @PostMapping(&quot;/add&quot;)
  public String save5(Item item) {

      itemRepository.save(item);
      return &quot;redirect:/basic/items/&quot; + item.getId();
  }</code></pre></li>
<li><p><code>@PathVariable</code> 값은 redirect에도 사용가능</p>
<pre><code>  @PostMapping(&quot;/{itemId}/edit&quot;)
  public String editItem(@PathVariable Long itemId, Item item) {
      itemRepository.update(itemId, item);
      return &quot;redirect:/basic/items/{itemId}&quot;;
  }</code></pre></li>
<li><p><code>redirect:/basic/items/{itemId}</code></p>
<ul>
<li><code>@PathVariable Long itemId</code>의 값 그대로 사용</li>
</ul>
</li>
</ul>
<h2 id="2-redirectattributes">(2) RedirectAttributes</h2>
<ul>
<li><p><code>RedirectAttributes</code>를 사용하면 <code>@PathVarialbe</code> 쿼리 파라미터까지도 처리 가능</p>
<pre><code>  @PostMapping(&quot;/add&quot;)
  public String save6(Item item, RedirectAttributes redirectAttributes) {
      // 객체 Item 저장
      Item savedItem = itemRepository.save(item);

      // addAttribute() 메서드를 사용하여 전달할 파라미터의 이름과 값을 지정
      // 1. 저장된 &#39;Item&#39;객체의 &#39;id&#39;값 사용
      redirectAttributes.addAttribute(&quot;itemId&quot;, savedItem.getId());
      // 2. status 파라미터의 값은 true로 지정
      redirectAttributes.addAttribute(&quot;status&quot;, true);
      return &quot;redirect:/basic/items/{itemId}&quot;;
  }</code></pre><blockquote>
</blockquote>
<p>주소값 : <code>http://localhost:9091/basic/items/4?status=true</code></p>
<blockquote>
<ul>
<li>4 ➡ <code>{itemId}</code> : pathVariable로 바인딩</li>
<li>?status=true ➡ <code>redirectAttributes.addAttribute(&quot;status&quot;, true)</code> : 쿼리 파라미터로 처리</li>
</ul>
</blockquote>
</li>
<li><p><strong>html에서 사용 가능</strong></p>
<pre><code>  &lt;script th:inline=&quot;javascript&quot;&gt;
      /*&lt;![CDATA[*/
          if([[${param.status}]]){
              alert(&quot;저장이 완료 되었습니다.&quot;);
          }
      /*]]&gt;*/
  &lt;/script&gt;</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] @PostConstruct, @PreDestroy, @ModelAttribute]]></title>
            <link>https://velog.io/@boram_han/Spring-PostConstruct-PreDestroy-ModelAttribute</link>
            <guid>https://velog.io/@boram_han/Spring-PostConstruct-PreDestroy-ModelAttribute</guid>
            <pubDate>Sat, 29 Apr 2023 09:21:37 GMT</pubDate>
            <description><![CDATA[<h1 id="1-객체의-초기화-소멸">1. 객체의 초기화, 소멸</h1>
<h2 id="1-객체의-초기화">(1) 객체의 초기화</h2>
<ul>
<li><code>@PostConstruct</code></li>
<li>객체가 생성된 후 별도의 초기화 작업을 위해 실행하는 메소드에 선언</li>
<li>WAS가 띄워질 때 실행됨</li>
</ul>
<p><strong>테스트용 데이터 생성 예시</strong>  </p>
<pre><code>    @PostConstruct
    public void init() {
        itemRepository.save(new Item(&quot;testA&quot;, 10000, 10));
        itemRepository.save(new Item(&quot;testB&quot;, 20000, 10));
    }</code></pre><h2 id="2-객체-소멸">(2) 객체 소멸</h2>
<ul>
<li><code>@PreDestroy</code></li>
<li>마지막 소멸 단계에 사용</li>
<li>스프링 컨테이너에서 객체(빈)를 제거하기 전에 해야할 메소드 위에 사용하는 어노테이션</li>
</ul>
<p><strong>종료 메서드 예시</strong></p>
<pre><code>    @PreDestroy
    public void destory() {
        System.out.println(&quot;종료 메서드 호출&quot;);
    }

    // 서버 재가동 시 호출됨</code></pre><h1 id="2-modelattribute">2. @ModelAttribute</h1>
<h2 id="1-modelattribute란">(1) @ModelAttribute란?</h2>
<ul>
<li><p>스프링 MVC에서 요청 매핑 메서드</p>
</li>
<li><p>HTTP 요청 파라미터를 컨트롤러의 메서드 파라미터로 전달할 때 사용</p>
</li>
<li><p>요청 파라미터가 객체일 때 편리하게 사용할 수 있다.</p>
<h2 id="2-사용-예시">(2) 사용 예시</h2>
<h3 id="1-modelattribute-없이-사용">1) @ModelAttribute 없이 사용</h3>
</li>
<li><p>파라미터를 직접 가져와서 DTO에 set</p>
<pre><code>//    @PostMapping(&quot;/add&quot;)
  public String save(
          @RequestParam String    itemName,
          @RequestParam int        price,
          @RequestParam Integer    quantity,
          Model model
          ) {

      Item item = new Item();
      item.setItemName(itemName);
      item.setPrice(price);
      item.setQuantity(quantity);

      itemRepository.save(item);
      model.addAttribute(&quot;item&quot;, item);

      return &quot;basic/item&quot;;
  }</code></pre><h3 id="2-modelattribute-사용">2) @ModelAttribute 사용</h3>
</li>
<li><p>파라미터로 넘어온 값을 Item 객체에 set 해주고</p>
</li>
<li><p>그 결과값을 model에 add 해줄 때 키값이 &quot;item&quot; 된다.</p>
<pre><code>  @PostMapping(&quot;/add&quot;)
  public String save2(@ModelAttribute(&quot;item&quot;)Item item, Model model) {

      itemRepository.save(item);
      model.addAttribute(&quot;item&quot;, item);

      return &quot;basic/item&quot;;
  }</code></pre></li>
</ul>
<h3 id="3-modeladdattributeitem-item-삭제-가능">3) <code>model.addAttribute(&quot;item&quot;, item);</code> 삭제 가능</h3>
<ul>
<li>모델에 @ModelAttribute로 지정한 객체를 넣어주므로 삭제해도 된다.<pre><code>  @PostMapping(&quot;/add&quot;)
  public String save2(@ModelAttribute(&quot;item&quot;)Item item, Model model){    
      itemRepository.save(item);        
      return &quot;basic/item&quot;;
  }</code></pre></li>
</ul>
<h3 id="4-name-생략-가능">4) name 생략 가능</h3>
<ul>
<li>생략시 model에 저장되는 name은 클래스명 첫 글자만</li>
<li>소문자로 등록 경우만 생략 가능<pre><code>  @PostMapping(&quot;/add&quot;)
  public String save3(@ModelAttribute Item item) {
      itemRepository.save(item);
      return &quot;basic/item&quot;;
  }</code></pre><h3 id="5-modelattribute-자체-생략-가능">5) <code>@ModelAttribute</code> 자체 생략 가능</h3>
</li>
<li>대상 객체는 모델에 자동 등록 됨<pre><code>  @PostMapping(&quot;/add&quot;)
  public String save4(Item item) {
      itemRepository.save(item);
      return &quot;basic/item&quot;;
  }</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Lombok 사용]]></title>
            <link>https://velog.io/@boram_han/Spring-Lombok-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@boram_han/Spring-Lombok-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Sat, 29 Apr 2023 06:44:40 GMT</pubDate>
            <description><![CDATA[<h1 id="🌶-lombok">🌶 Lombok</h1>
<h2 id="1-롬복이란">(1) 롬복이란?</h2>
<ul>
<li>롬복은 자바 언어를 사용하는 개발자들을 위한 라이브러리</li>
<li>코드 작성의 반복을 줄여주는 기능들을 제공</li>
</ul>
<h2 id="2-롬복-주요-기능">(2) 롬복 주요 기능</h2>
<ul>
<li><code>@Getter</code> 및 <code>@Setter</code>: 객체 필드 생성, getter와 setter를 만들어 줌</li>
<li><code>@ToString</code> : 객체의 필드를 출력하는 toString() 메서드를 자동으로 생성</li>
<li><code>@RequiredArgsConstructor</code> :<ul>
<li>final이 붙은 멤버변수만 사용해서 생성자를 자동으로 만들어 줌</li>
<li>생성자를 롬복이 자동으로 생성해주기 때문에 코드를 간결하게 작성</li>
</ul>
</li>
<li><code>@Data</code> : <ul>
<li><code>@Getter</code>, <code>@Setter</code>, <code>@ToString</code>, <code>@RequiredArgsConstructor</code> 등의 어노테이션을 모두 포함 </li>
<li>클래스의 모든 필드를 생성하고, 모든 메서드를 만들어 줌</li>
</ul>
</li>
</ul>
<h2 id="3-롬복-사용방법">(3) 롬복 사용방법</h2>
<h3 id="1-롬복-의존성-추가">1) 롬복 의존성 추가</h3>
<ul>
<li>project 생성 시 의존성 추가</li>
<li>build.gradle &gt; dependencies에 의존성 추가<pre><code>  compileOnly &#39;org.projectlombok:lombok&#39;
  annotationProcessor &#39;org.projectlombok:lombok&#39;</code></pre><h3 id="2-cmd창에서-롬복-jar-파일-위치-진입">2) cmd창에서 롬복 jar 파일 위치 진입</h3>
</li>
<li>프로젝트의 Project and External Dependencies에서 <code>lombok-1.18.26.jar</code> 위치 확인</li>
<li>cmd 창에서 jar파일 실행<ul>
<li>cd lombok경로</li>
<li>java -jar lombok-1.18.26.jar
<img src="https://velog.velcdn.com/images/boram_han/post/742859ef-6196-4e1f-a5fb-d712f1b6fbf3/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="3-project-lombok-실행">3) project lombok 실행</h3>
<ul>
<li>cmd에서 jar파일 실행시 다음 화면 켜짐
<img src="https://velog.velcdn.com/images/boram_han/post/aa2367a8-f651-46e3-a690-c15f7c1ec132/image.png" alt=""></li>
<li>sts 파일 위치 select
<img src="https://velog.velcdn.com/images/boram_han/post/e9540271-78d1-45cf-a0d0-a5259c74061a/image.png" alt="">
<img src="https://velog.velcdn.com/images/boram_han/post/a3cad092-1347-429a-bcd1-addfd35e399d/image.png" alt=""></li>
<li>install 성공
<img src="https://velog.velcdn.com/images/boram_han/post/9a10bad9-fb5b-480a-81da-c3265d1d5c3c/image.png" alt=""></li>
</ul>
<h3 id="4-class-에서-어노테이션-실행">4) class 에서 어노테이션 실행</h3>
<p><img src="https://velog.velcdn.com/images/boram_han/post/6c06d9fa-30b7-4d0b-b4fc-faa69699f74b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 타임리프(Thymeleaf)]]></title>
            <link>https://velog.io/@boram_han/Spring-%ED%83%80%EC%9E%84%EB%A6%AC%ED%94%84Thymeleaf</link>
            <guid>https://velog.io/@boram_han/Spring-%ED%83%80%EC%9E%84%EB%A6%AC%ED%94%84Thymeleaf</guid>
            <pubDate>Fri, 28 Apr 2023 10:50:39 GMT</pubDate>
            <description><![CDATA[<h1 id="🌱-타임리프">🌱 타임리프</h1>
<h2 id="1-타임리프의-특징">(1) 타임리프의 특징</h2>
<h4 id="①-서버사이드-html-렌더링">① 서버사이드 HTML 렌더링</h4>
<ul>
<li>타임리프는 백엔드 서버에서 (jsp처럼) HTML을 동적으로 렌더링하는 용도로 사용<h4 id="②-네츄럴-템플릿">② 네츄럴 템플릿</h4>
</li>
<li>타임리프는 순수 HTML을 최대한 유지</li>
<li>웹 브라우저에서 파일을 직접 열어도 내용을 확인 가능</li>
<li>서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인 가능<h4 id="③-스프링-통합-지원">③ 스프링 통합 지원</h4>
</li>
<li>타임리프는 스프링과 자연스럽게 통합</li>
<li>스프링의 다양한 기능을 편리하게 사용할 수 있게 지원</li>
</ul>
<h2 id="2-기본표현식">(2) 기본표현식</h2>
<h3 id="1-간단한-표현">1) 간단한 표현</h3>
<ul>
<li><code>${...}</code> : 변수 표현식 </li>
<li><code>*{...}</code> : 선택 변수표현식 </li>
<li><code>#{...}</code> : 메시지 표현식 </li>
<li><code>@{...}</code> : 링크 URL 표현식 </li>
<li><code>~{...}</code> : 조각 표현식</li>
</ul>
<h3 id="2--리터럴">2)  리터럴</h3>
<ul>
<li>텍스트 : <code>&#39;one text&#39;</code>, <code>&#39;two text&#39;</code>, ···</li>
<li>숫자 : <code>0</code>, <code>34</code>, <code>3.0</code></li>
<li>불린 : <code>true</code>, <code>false</code></li>
<li>널 : <code>null</code></li>
</ul>
<h3 id="3--문자-연산">3)  문자 연산</h3>
<ul>
<li>문자 합치기 : <code>+</code></li>
<li>리터럴 대체 : <code>|The Name is ${name}|</code></li>
</ul>
<h3 id="4--산술과-불린-연산">4)  산술과 불린 연산</h3>
<ul>
<li>산술 : <code>-</code>, <code>+</code> , <code>-</code> , <code>*</code>, <code>/</code> , <code>%</code></li>
<li>불린 : <code>and</code>, <code>or</code> , <code>!</code>, <code>not</code></li>
</ul>
<h3 id="5--비교와-동등-연산">5)  비교와 동등 연산</h3>
<ul>
<li>비교 : <code>&gt;</code>(gt), <code>&lt;</code>(lt), <code>&gt;=</code>(ge), <code>&lt;=</code>(le)</li>
<li>동등 : <code>==</code>(eq), <code>!=</code>(ne)</li>
</ul>
<h3 id="6-조건-연산">6) 조건 연산</h3>
<ul>
<li><code>if-then</code>, <code>if-then-else</code></li>
</ul>
<h2 id="3-텍스트-출력">(3) 텍스트 출력</h2>
<h3 id="1-텍스트-기본-출력">1) 텍스트 기본 출력</h3>
<ul>
<li><p>th:text 사용 : <code>&lt;span th:text=&quot;${data}&quot;&gt;&lt;/span&gt;</code></p>
</li>
<li><p>컨텐츠 안에서 직접 출력하기 : <code>[[${data}]]</code></p>
</li>
<li><p><em>💡 예시*</em></p>
<pre><code>&lt;java&gt;

   @GetMapping(&quot;text-basic&quot;)
   public String textBasic(Model model) {
       model.addAttribute(&quot;data&quot;,&quot;&lt;b&gt;Hello spring!!&lt;/b&gt;&quot;);
       return &quot;basic/text-basic&quot;;
   }
</code></pre></li>
</ul>
<html>

<pre><code>&lt;body&gt;
    &lt;h1&gt;컨텐츠에 데이터 출력하기&lt;/h1&gt;
    &lt;ul&gt;
        &lt;li&gt;th:text 사용 : &lt;span th:text=&quot;${data}&quot;&gt;&lt;/span&gt;&lt;/li&gt;
        &lt;li&gt;컨텐츠 안에서 직접 출력하기 = [[${data}]]&lt;/li&gt;
    &lt;/ul&gt;
&lt;/body&gt;</code></pre><pre><code>**👍 결과**
![](https://velog.velcdn.com/images/boram_han/post/f82d7caf-2e83-4115-a127-ffc1b782e21c/image.png)
### 2) unescaped 태그로 출력
- th:text 사용 : ```&lt;span th:utext=&quot;${data}&quot;&gt;&lt;/span&gt;```
- inline 방식 : ```&lt;span th:inline=&quot;none&quot;&gt;[(...)]&lt;/span&gt; [(${data})]```
**💡 예시**</code></pre><java>

<pre><code>@GetMapping(&quot;text-unescaped&quot;)
public String textUnescaped(Model model) {
    model.addAttribute(&quot;data&quot;, &quot;&lt;b&gt;Hello Spring!!&lt;/b&gt;&quot;);
    return &quot;basic/text-unescaped&quot;;
}</code></pre><html>

<pre><code>&lt;body&gt;
    &lt;h1&gt;text vs utext&lt;/h1&gt;
    &lt;ul&gt;
        &lt;li&gt;th:text = &lt;span th:text=&quot;${data}&quot;&gt;&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;th:utext = &lt;span th:utext=&quot;${data}&quot;&gt;&lt;/span&gt; &lt;/li&gt;
    &lt;/ul&gt;

    &lt;h1&gt;
        &lt;span th:inline=&quot;none&quot;&gt;[[...]] vs [(...)]&lt;/span&gt;  
    &lt;/h1&gt;
    &lt;ul&gt;
        &lt;li&gt;&lt;span th:inline=&quot;none&quot;&gt;[[...]]&lt;/span&gt; [[${data}]] &lt;/li&gt;
        &lt;li&gt;&lt;span th:inline=&quot;none&quot;&gt;[(...)]&lt;/span&gt; [(${data})] &lt;/li&gt;
    &lt;/ul&gt;
&lt;/body&gt;</code></pre><pre><code>**👍 결과**
![](https://velog.velcdn.com/images/boram_han/post/7829beb0-f4bc-4629-8786-d006862f4880/image.png)

## (4) 표준 표현식 구문
### 1) Spring EL
#### ① Object
- ```${객체.변수이름}``` : ```&quot;${user.username}&quot;&gt;```
- ```${객체[&#39;변수이름&#39;]}``` : ```${user[&#39;username&#39;]}```
- ```${객체.get변수이름()}``` : ```${user.getUsername()}```

#### ② List
- ```${리스트이름[i].변수이름}``` : ```${users[0].username}```
- ```${리스트이름[i][&#39;변수이름&#39;]}``` : ```${users[0][&#39;username&#39;]}```
- ```${리스트이름[i].get변수이름()}``` : ```${users[0].getUsername()}```

#### ③ Map
- ```${맵이름[&#39;키값&#39;].변수이름}``` : ```${userMap[&#39;userA&#39;].username}```
- ```${맵이름[&#39;키값&#39;][&#39;변수이름&#39;]}``` : ```${userMap[&#39;userA&#39;][&#39;username&#39;]}```
- ```${맵이름[&#39;키값&#39;].get변수이름()}```: ```&lt;${userMap[&#39;userA&#39;].getUsername()}```

#### ④ 지역변수 사용
- 변수로 리스트 할당
    - th:with 사용 : ```&lt;div th:with=&quot;first=${users[0]}&quot;&gt;&lt;/div&gt;```
- 지역변수 사용
    - 예시 : ```&lt;span th:text=${first.username}&gt;&lt;/span&gt;```

**💡 예시**

- **JAVA**

```java
    // 객체 만들기
    public class User {

    private String username;
    private int age;

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public User(String username, int age) {
        super();
        this.username = username;
        this.age = age;
    }

    // controller

    @GetMapping(&quot;variable&quot;)
    public String textVariable (Model model) {

      User userA = new User(&quot;userA&quot;, 10);
      User userB = new User(&quot;userB&quot;, 20);

      List&lt;User&gt; list = new ArrayList&lt;&gt;();
      list.add(userA);
      list.add(userB);

      Map&lt;String, User&gt; map = new HashMap&lt;&gt;();
      map.put(&quot;userA&quot;, userA);
      map.put(&quot;userB&quot;, userB);

      model.addAttribute(&quot;user&quot;,userA);
      model.addAttribute(&quot;users&quot;,list);
      model.addAttribute(&quot;userMap&quot;,map);

      return &quot;basic/variable    
    }</code></pre><ul>
<li><strong>Html</strong></li>
</ul>
<pre><code>&lt;body&gt;
    &lt;h1&gt;Spring EL표현식&lt;/h1&gt;
    &lt;ul&gt;Object
        &lt;li&gt;
            ${user.username} = &lt;span th:text=&quot;${user.username}&quot;&gt;&lt;/span&gt;
        &lt;/li&gt;
        &lt;li&gt;
            ${user[&#39;username&#39;]} = &lt;span th:text=&quot;${user[&#39;username&#39;]}&quot;&gt;&lt;/span&gt;
        &lt;/li&gt;
        &lt;li&gt;
            ${user.getUsername()} = &lt;span th:text=&quot;${user.getUsername()}&quot;&gt;&lt;/span&gt;
        &lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul&gt;
        List
        &lt;li&gt;
            ${users[0].username} = &lt;span th:text=&quot;${users[0].username}&quot;&gt;&lt;/span&gt;
        &lt;/li&gt;
        &lt;li&gt;
            ${users[0][&#39;username&#39;]} = &lt;span th:text=&quot;${users[0][&#39;username&#39;]}&quot;&gt;&lt;/span&gt;
        &lt;/li&gt;
        &lt;li&gt;
            ${users[0].getUsername()} = &lt;span th:text=&quot;${users[0].getUsername()}&quot;&gt;&lt;/span&gt;
        &lt;/li&gt;
    &lt;/ul&gt;
        &lt;ul&gt;
        Map
        &lt;li&gt;
            ${userMap[&#39;userA&#39;].username} = &lt;span th:text=&quot;${userMap[&#39;userA&#39;].username}&quot;&gt;&lt;/span&gt;
        &lt;/li&gt;
        &lt;li&gt;
            ${userMap[&#39;userA&#39;][&#39;username&#39;]} = &lt;span th:text=&quot;${userMap[&#39;userA&#39;][&#39;username&#39;]}&quot;&gt;&lt;/span&gt;
        &lt;/li&gt;
        &lt;li&gt;
            ${userMap[&#39;userA&#39;].getUsername()} = &lt;span th:text=&quot;${userMap[&#39;userA&#39;].getUsername()}&quot;&gt;&lt;/span&gt;
        &lt;/li&gt;
    &lt;/ul&gt;

    &lt;h1&gt;지역변수 - (th:with)&lt;/h1&gt;
    &lt;!-- 변수로 리스트 할당 --&gt;
    &lt;div th:with=&quot;first=${users[0]}&quot;&gt;
        &lt;p&gt;
            처음 사람의 이름은 
            &lt;span th:text=${first.username}&gt;&lt;/span&gt;
        &lt;/p&gt;
    &lt;/div&gt;
&lt;/body&gt;</code></pre><p><strong>👍 결과</strong>
<img src="https://velog.velcdn.com/images/boram_han/post/9c008d16-1eca-4158-b8b0-03fbb7b1b8da/image.png" alt=""></p>
<h3 id="2-기본-및-편의-객체">2) 기본 및 편의 객체</h3>
<ul>
<li><p>기본 객체 : <code>${#request}</code>, <code>${#response}</code>, <code>${#session}</code>, <code>${#servletContext}</code>, <code>${#locale}</code></p>
</li>
<li><p>편의 객체 : </p>
<ul>
<li>리퀘스트 파라미터 : <code>${#request.getParameter(&#39;파라미터 이름&#39;)}</code>, <code>${param.파라미터 이름}</code></li>
<li>세션 : <code>${session.key값}</code></li>
<li>@Component(&quot;객체이름&quot;) = 스프링 Bean 사용<ul>
<li><code>${@객체이름.메서드이름(&#39;파라미터&#39;)}</code> :  <code>${@helloBean.hello(&#39;user&#39;)}</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>💡 편의 객체 예시</strong></p>
<ul>
<li><p><strong>Java</strong></p>
<pre><code class="language-java">  // 파라미터용 
  &lt;a href=&quot;basic/basic-objects?paramData=HelloParam&quot;&gt;&lt;/a&gt;

  // 세션용
  @GetMapping(&quot;/basic-objects&quot;)
  public String basicObjects(HttpSession session) {
      session.setAttribute(&quot;sessionData&quot;, &quot;hello&quot;);
      return &quot;basic/basic-objects&quot;;
  }

  // HelloBean.java
  @Component(&quot;helloBean&quot;)
  public class HelloBean {
      public String hello(String data) {
          return &quot;spring data : &quot; + data;
      }
  }</code></pre>
</li>
<li><p><strong>Html</strong></p>
<pre><code>&lt;body&gt;
  &lt;h1&gt;편의 객체&lt;/h1&gt;
  &lt;ul&gt;
      &lt;li&gt;
          Request Parameter = &lt;span th:text=&quot;${#request.getParameter(&#39;paramData&#39;)}&quot;&gt;&lt;/span&gt;        
      &lt;/li&gt;

      &lt;li&gt;
          Request Parameter2 = &lt;span th:text=&quot;${param.paramData}&quot;&gt;&lt;/span&gt;        
      &lt;/li&gt;

      &lt;li&gt;
          session = &lt;span th:text=&quot;${session.sessionData}&quot;&gt;&lt;/span&gt;        
      &lt;/li&gt;        

      &lt;li&gt;
          &lt;!-- hellobean의 메소드 hello에 변수user을 대입 --&gt;
          spring bean = &lt;span th:text=&quot;${@helloBean.hello(&#39;user&#39;)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
  &lt;/ul&gt;
&lt;/body&gt;</code></pre></li>
<li><p><em>👍 결과*</em>
<img src="https://velog.velcdn.com/images/boram_han/post/893f8b94-af5c-422f-bfd5-726c532370e2/image.png" alt=""></p>
</li>
</ul>
<h3 id="3-날짜">3) 날짜</h3>
<ul>
<li><p>기본 표현 : <code>${localDateTime}</code></p>
</li>
<li><p>형식에 맞춘 표현 : </p>
<ul>
<li><code>${#temporals.format(localDateTime, &#39;yyyy-MM-dd HH:mm:ss&#39;)}</code></li>
<li><code>${#temporals.format(localDateTime, &#39;yyyy-MM-dd&#39;)}</code></li>
<li>이 외 변형해서 사용 가능</li>
</ul>
</li>
<li><p>디테일한 표현 :</p>
<ul>
<li>년 : <code>${#temporals.year(localDateTime)}</code></li>
<li>월 : <code>${#temporals.month(localDateTime)}</code></li>
<li>일 : <code>${#temporals.day(localDateTime)}</code></li>
<li>요일 : <code>${#temporals.dayOfWeek(localDateTime)}</code></li>
<li>이 외 변형해서 사용 가능</li>
</ul>
</li>
<li><p><em>💡 예시*</em></p>
</li>
<li><p><strong>Java</strong></p>
<pre><code>  @GetMapping(&quot;/date&quot;)    
  public String basicDate(Model model) {
      model.addAttribute(&quot;localDateTime&quot;, LocalDateTime.now());
      return &quot;basic/date&quot;;
  }</code></pre></li>
<li><p><strong>Html</strong></p>
<pre><code>&lt;body&gt;
  &lt;h1&gt;LocalDateTime&lt;/h1&gt;
  &lt;ul&gt;
      &lt;li&gt;default = &lt;span th:text=&quot;${localDateTime}&quot;&gt;&lt;/span&gt;&lt;/li&gt;
      &lt;li&gt;yyyy-MM-dd HH:mm:ss = 
      &lt;span th:text=&quot;${#temporals.format(localDateTime, &#39;yyyy-MM-dd HH:mm:ss&#39;)}&quot;&gt;&lt;/span&gt;&lt;/li&gt;
      &lt;li&gt;yyyy-MM-dd = 
      &lt;span th:text=&quot;${#temporals.format(localDateTime, &#39;yyyy-MM-dd&#39;)}&quot;&gt;&lt;/span&gt;&lt;/li&gt;
      &lt;li&gt;MM/dd = 
      &lt;span th:text=&quot;${#temporals.format(localDateTime, &#39;MM/dd&#39;)}&quot;&gt;&lt;/span&gt;&lt;/li&gt;

  &lt;/ul&gt;

  &lt;h1&gt;LocalDateTime - Utils&lt;/h1&gt;
  &lt;ul&gt;
      &lt;li&gt;${#temporals.day(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.day(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.month(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.month(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.monthName(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.monthName(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.monthNameShort(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.monthNameShort(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.year(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.year(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;

      &lt;li&gt;${#temporals.dayOfWeek(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.dayOfWeek(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.dayOfWeekName(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.dayOfWeekName(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.dayOfWeekNameShort(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.dayOfWeekNameShort(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.hour(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.hour(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.minute(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.minute(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.second(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.second(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;${#temporals.nanosecond(localDateTime)} =
          &lt;span th:text=&quot;${#temporals.nanosecond(localDateTime)}&quot;&gt;&lt;/span&gt;
      &lt;/li&gt;                            
  &lt;/ul&gt;
&lt;/body&gt;</code></pre></li>
<li><p><em>👍 결과*</em>
<img src="https://velog.velcdn.com/images/boram_han/post/0133ca4f-438b-4f69-9b78-da1e29d5b2a6/image.png" alt=""></p>
</li>
</ul>
<h3 id="4-링크-url">4) 링크 URL</h3>
<ul>
<li>url 기본 <ul>
<li><code>th:href=&quot;@{/hello}&quot;</code></li>
<li>결과 : <code>http://localhost:9090/hello</code></li>
</ul>
</li>
<li>쿼리 parameter<ul>
<li><code>th:href=@{/hello(param1=${param1}, param2=${param2})}</code></li>
<li><pre><code>    @GetMapping(&quot;/link&quot;)    
      public String basicLink(Model model) {
  model.addAttribute(&quot;param1&quot;,&quot;data1&quot;);
  model.addAttribute(&quot;param2&quot;,&quot;data2&quot;);
  return &quot;basic/link&quot;;
  }</code></pre></li>
<li>결과 : <code>/hello?param1=data1&amp;param2=data2</code></li>
<li>파라미터는 컨트롤러에서 처리</li>
</ul>
</li>
<li>경로 variable<ul>
<li><code>th:href=&quot;@{/hello/{param1}/{param2}(param1=${param1},param2=${param2})}</code></li>
<li>결과 : <code>http://localhost:9090/hello/data1/data2</code></li>
</ul>
</li>
<li>쿼리 parameter + 경로 variable<ul>
<li><code>th:href=&quot;@{/hello/{param1}(param1=${param1},param2=${param2})}</code></li>
<li>결과 : <code>http://localhost:9090/hello/data1?param2=data2</code> </li>
</ul>
</li>
</ul>
<h3 id="5-리터럴">5) 리터럴</h3>
<ul>
<li>리터럴 : 소스 코드 상에 고정된 값을 말하는 용어</li>
<li>타임리프의 리터럴 : 문자열(String), 숫자(Number), 불리언(Boolean), null</li>
<li>문자 리터럴 1 : 원칙상 항상 <code>&quot;&quot;</code>(큰 따옴표) 안에 <code>&#39;&#39;</code>(작은 따옴표)로 표현<ul>
<li><code>th:text=&quot;&#39;hello&#39; + &#39; world&#39;&quot;</code></li>
<li><code>th:text=&quot;&#39;hello world&#39;&quot;</code></li>
</ul>
</li>
<li>문자 리터럴 2 : 공백없이 쭉 이어진다면 작은 따옴표를 생략 가능<ul>
<li><code>th:text=&quot;hello&quot;</code></li>
</ul>
</li>
<li>문자 + data<ul>
<li><code>th:text=&quot;&#39;hello &#39; + ${data}&quot;</code></li>
</ul>
</li>
<li>리터럴 대체 : <code>||</code>를 사용하면 작은 따옴표와 <code>+</code>를 대체할 수 있다.<ul>
<li><code>&quot;|hello ${data}|&quot;</code></li>
</ul>
</li>
</ul>
<h3 id="6-연산">6) 연산</h3>
<ul>
<li><p>산술연산 </p>
<ul>
<li><code>&lt;span th:text=&quot;10 + 2&quot;&gt;&lt;/span&gt;</code> <ul>
<li>출력 : 12 </li>
</ul>
</li>
</ul>
</li>
<li><p>비교연산</p>
<ul>
<li><code>&lt;span th:text=&quot;1 &gt;= 10&quot;&gt;&lt;/span&gt;</code><ul>
<li>출력 : false</li>
</ul>
</li>
</ul>
</li>
<li><p>조건식</p>
<ul>
<li><code>&lt;span th:text=&quot;(10 % 2 == 0)?&#39;짝수&#39;:&#39;홀수&#39;&quot;&gt;&lt;/span&gt;&quot;</code><ul>
<li>출력 : 짝수</li>
</ul>
</li>
</ul>
</li>
<li><p>Elvis 연산자 : 조건식의 축약버전</p>
<ul>
<li><pre><code>     @GetMapping(&quot;/operation&quot;)    
  public String basicOperation(Model model) {
  model.addAttribute(&quot;data&quot;,&quot;Spring&quot;);
  model.addAttribute(&quot;nullData&quot;, null);
  return &quot;basic/operation&quot;;
  }</code></pre></li>
<li><code>&lt;span th:text=&quot;${data}?:&#39;데이터가 없습니다.&#39;&quot;&gt;&lt;/span&gt;</code><ul>
<li>출력 : Spring</li>
</ul>
</li>
<li><code>th:text=&quot;${nullData}?:&#39;데이터가 없습니다.&#39;&quot;</code><ul>
<li>출력 : 데이터가 없습니다.</li>
</ul>
</li>
</ul>
</li>
<li><p>No-Operation</p>
<ul>
<li><code>&lt;span th:text=&quot;${data}?:_&quot;&gt;데이터가 없습니다.&lt;/span&gt;</code><ul>
<li>출력 : Spring</li>
</ul>
</li>
<li><code>&lt;span th:text=&quot;${nullData}?:_&quot;&gt;데이터가 없습니다.&lt;/span&gt;</code><ul>
<li>출력 : 데이터가 없습니다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="5-속성-값-설정">(5) 속성 값 설정</h2>
<h3 id="1-속성-설정">1) 속성 설정</h3>
<ul>
<li><p><code>th:*</code> </p>
<ul>
<li>속성을 지정하면 타임리프는 기존 속성을 th:*로 지정한 속성으로 대체</li>
<li>기존 속성이 없다면 새로 생성<ul>
<li><code>&lt;input type=&quot;text&quot; name=&quot;mock&quot; th:name=&quot;userA&quot;&gt;</code></li>
<li>결과 : 속성값 name = userA로 설정<h3 id="2-속성-추가">2) 속성 추가</h3>
</li>
</ul>
</li>
</ul>
</li>
<li><p><code>th:attrappend</code></p>
<ul>
<li>속성 값 뒤에 값을 추가<ul>
<li><code>&lt;input type=&quot;text&quot; class=&quot;text&quot; th:attrappend=&quot;class=&#39;large&#39;&quot;&gt;</code></li>
<li>속성 값 : class=&quot;text&quot; -&gt; <code>class=&quot;textlarge&quot;</code></li>
</ul>
</li>
</ul>
</li>
<li><p><code>th:attrprepend</code></p>
<ul>
<li>속성 값 앞에 값을 추가<ul>
<li><code>&lt;input type=&quot;text&quot; class=&quot;text&quot; th:attrprepend=&quot;class=&#39;large&#39;&quot;&gt;</code></li>
<li>속성 값 : class=&quot;text&quot; -&gt; <code>class=&quot;largetext&quot;</code> </li>
</ul>
</li>
</ul>
</li>
<li><p><code>th:classappend</code></p>
<ul>
<li>자연스럽게 추가<ul>
<li><code>&lt;input type=&quot;text&quot; class=&quot;text&quot; th:classappend=&quot;large&quot;&gt;</code></li>
<li>속성 값 : class=&quot;text&quot; -&gt; <code>class=&quot;text large&quot;</code> <h3 id="3-check-처리">3) check 처리</h3>
</li>
</ul>
</li>
</ul>
</li>
<li><p>checked O</p>
<ul>
<li><code>&lt;input type=&quot;text&quot; class=&quot;text&quot; th:attrappend=&quot;class=&#39;large&#39;&quot;&gt;</code></li>
</ul>
</li>
<li><p>checked X</p>
<ul>
<li><code>&lt;input type=&quot;checkbox&quot; name=&quot;active&quot; th:checked=&quot;false&quot;&gt;</code></li>
</ul>
</li>
<li><p>checked = false</p>
<ul>
<li><code>&lt;input type=&quot;checkbox&quot; name=&quot;active&quot; checked=&quot;false&quot;&gt;</code></li>
</ul>
</li>
<li><p><em>👍 결과*</em>
<img src="https://velog.velcdn.com/images/boram_han/post/7d5d6e42-ebf1-4cb9-9515-b48d63a7d0a9/image.png" alt=""></p>
</li>
</ul>
<h2 id="6-반복-theach">(6) 반복 <a href="th:each">th:each</a></h2>
<h3 id="1-기본-반복">1) 기본 반복</h3>
<p><strong>💡 예시</strong></p>
<ul>
<li><p><strong>Java</strong></p>
<pre><code>  @GetMapping(&quot;/each&quot;)    
  public String basicEach(Model model) {
      addUsers(model);
      return &quot;basic/each&quot;;
  }</code></pre><pre><code>  private void addUsers(Model model) {

      List&lt;User&gt; list = new ArrayList&lt;&gt;();

      list.add(new User(&quot;userA&quot;, 10));
      list.add(new User(&quot;userB&quot;, 20));
      list.add(new User(&quot;userC&quot;, 30));

      model.addAttribute(&quot;users&quot;, list);
  }</code></pre></li>
<li><p><strong>Html</strong></p>
<pre><code>  &lt;table border=&quot;1&quot;&gt;
      &lt;tr&gt;
          &lt;th&gt;username&lt;/th&gt;
          &lt;th&gt;age&lt;/th&gt;
      &lt;/tr&gt;
      &lt;!-- arraylist 이름 users를 user이름 아래 하나씩 담아줌 --&gt;
      &lt;tr th:each=&quot;user : ${users}&quot;&gt;
          &lt;td th:text=&quot;${user.username}&quot;&gt;username&lt;/td&gt;            
          &lt;td th:text=&quot;${user.age}&quot;&gt;0&lt;/td&gt;            
      &lt;/tr&gt;
  &lt;/table&gt;</code></pre></li>
<li><p><em>👍 결과*</em>
<img src="https://velog.velcdn.com/images/boram_han/post/c9e1b173-ad45-4372-a9c0-d9060066985f/image.png" alt=""></p>
</li>
</ul>
<h3 id="2-반복-상태-유지">2) 반복 상태 유지</h3>
<ul>
<li>반복문에서 두번째 파라미터를 설정해 <code>반복의 상태</code>확인 가능<ul>
<li>예) count, size, even, true  </li>
</ul>
</li>
<li>두번째 파라미터는 생략 가능, 생략하면 지정한 변수명(user) + Stat<ul>
<li>예) userStat</li>
</ul>
</li>
</ul>
<p><strong>💡 예시</strong></p>
<ul>
<li><strong>Html</strong><pre><code>  &lt;table border=&quot;1&quot;&gt;
      &lt;tr&gt;
          &lt;th&gt;count&lt;/th&gt;
          &lt;th&gt;username&lt;/th&gt;
          &lt;th&gt;age&lt;/th&gt;
          &lt;th&gt;etc&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr th:each=&quot;user, userStat : ${users}&quot;&gt;
          &lt;td th:text=&quot;${userStat.count}&quot;&gt;&lt;/td&gt;
          &lt;td th:text=&quot;${user.username}&quot;&gt;&lt;/td&gt;            
          &lt;td th:text=&quot;${user.age}&quot;&gt;&lt;/td&gt;    
          &lt;td&gt;
              index = &lt;span th:text=&quot;${userStat.index}&quot;&gt;&lt;/span&gt;
              count = &lt;span th:text=&quot;${userStat.count}&quot;&gt;&lt;/span&gt;
              size = &lt;span th:text=&quot;${userStat.size}&quot;&gt;&lt;/span&gt;
              even = &lt;span th:text=&quot;${userStat.even}&quot;&gt;&lt;/span&gt;
              odd = &lt;span th:text=&quot;${userStat.odd}&quot;&gt;&lt;/span&gt;
              first = &lt;span th:text=&quot;${userStat.first}&quot;&gt;&lt;/span&gt;
              last = &lt;span th:text=&quot;${userStat.last}&quot;&gt;&lt;/span&gt;
          &lt;/td&gt;                                
      &lt;/tr&gt;
  &lt;/table&gt;</code></pre></li>
<li><em>👍 결과*</em>
<img src="https://velog.velcdn.com/images/boram_han/post/7b6bc454-2a1c-41b4-b05a-f81afb7b9feb/image.png" alt=""></li>
</ul>
<h2 id="7-조건부-평가">(7) 조건부 평가</h2>
<h3 id="1--if-unless">1)  if, unless</h3>
<ul>
<li><code>if</code> : 만약 ~ 한다면</li>
<li><code>unless</code> : 만약 ~ 하지 않는다면</li>
<li>해당 조건이 맞지 않으면 태그 자체를 렌더링 안 함</li>
</ul>
<p><strong>💡 예시</strong></p>
<ul>
<li><strong>Html</strong><pre><code>  &lt;table border = &quot;1&quot;&gt;
      &lt;tr&gt;
          &lt;th&gt;count&lt;/th&gt;
          &lt;th&gt;username&lt;/th&gt;
          &lt;th&gt;age&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr th:each=&quot;user, userStat : ${users}&quot;&gt;
          &lt;td th:text=&quot;${userStat.count}&quot;&gt;&lt;/td&gt;
          &lt;td th:text=&quot;${user.username}&quot;&gt;&lt;/td&gt;
          &lt;td&gt;
              &lt;span th:text=&quot;${user.age}&quot;&gt;&lt;/span&gt;
              &lt;span th:text=&quot;&#39;미성년자&#39;&quot; th:if=&quot;${user.age lt 20}&quot;&gt;&lt;/span&gt;
              &lt;span th:text=&quot;&#39;미성년자&#39;&quot; th:unless=&quot;${user.age ge 20}&quot;&gt;&lt;/span&gt;
          &lt;/td&gt;
      &lt;/tr&gt;
  &lt;/table&gt;</code></pre></li>
<li><em>👍 결과*</em>
<img src="https://velog.velcdn.com/images/boram_han/post/363aacdc-0a78-430a-811c-36aeea5812b5/image.png" alt=""></li>
</ul>
<p><strong>💡 렌더링 예시</strong></p>
<ul>
<li><code>&lt;span th:text=&quot;&#39;미성년자&#39;&quot; th:unless=&quot;${user.age ge 20}&quot;&gt;&lt;/span&gt;</code> 
=&gt; <code>&lt;span th:text=&quot;&#39;성인&#39;&quot; th:unless=&quot;${user.age lt 20}&quot;&gt;&lt;/span&gt;</code> </li>
</ul>
<p><strong>👍 결과</strong>
<img src="https://velog.velcdn.com/images/boram_han/post/454dbf4b-ff0d-4e93-b4b1-3b3ac9768451/image.png" alt=""></p>
<h3 id="2-switch">2) switch</h3>
<ul>
<li>조건을 만족하지 않으면 해당 tag가 조회되지 않는다.</li>
</ul>
<p><strong>💡 예시</strong></p>
<ul>
<li><strong>Html</strong><pre><code>  &lt;table border=&quot;1&quot;&gt;
      &lt;tr&gt;
          &lt;th&gt;count&lt;/th&gt;
          &lt;th&gt;username&lt;/th&gt;
          &lt;th&gt;age&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr th:each=&quot;user,userStat : ${users}&quot;&gt;
          &lt;td th:text=&quot;${userStat.count}&quot;&gt;&lt;/td&gt;
          &lt;td th:text=&quot;${user.username}&quot;&gt;&lt;/td&gt;
          &lt;td th:switch=${user.age}&gt;
              &lt;span th:case=&quot;10&quot;&gt;10살&lt;/span&gt;
              &lt;span th:case=&quot;20&quot;&gt;20살&lt;/span&gt;
              &lt;span th:case=&quot;*&quot;&gt;기타&lt;/span&gt;
          &lt;/td&gt;
      &lt;/tr&gt;
  &lt;/table&gt;</code></pre></li>
<li><em>👍 결과*</em>
<img src="https://velog.velcdn.com/images/boram_han/post/c8285abc-1731-4c12-b9ab-bcd12b0315a2/image.png" alt=""></li>
</ul>
<h2 id="8-기타">(8) 기타</h2>
<h3 id="1-주석">1) 주석</h3>
<h4 id="①-주석-노출--기본-html-주석">① 주석 노출 : 기본 html 주석</h4>
<ul>
<li><code>&lt;!-- --&gt;</code></li>
</ul>
<h4 id="②-주석-노출-x--타임리프-주석">② 주석 노출 x : 타임리프 주석</h4>
<ul>
<li>한 줄 주석 : <code>&lt;!--/* 내용 */--&gt;</code></li>
<li>여러 줄 주석<ul>
<li><code>&lt;!--/* --&gt;</code> 내용 <code>&lt;!-- */--&gt;</code></li>
<li><code>&lt;!--/*</code> 내용 <code>*/--&gt;</code><h3 id="2-블록">2) 블록</h3>
</li>
</ul>
</li>
<li>반복할 내용을 포함하는 블록(block) 역할</li>
<li>표에 담지 않고도 반복문을 돌릴 수 있다</li>
</ul>
<p><strong>💡 예시</strong></p>
<ul>
<li><strong>Html</strong><pre><code>  &lt;th:block th:each=&quot;user : ${users}&quot;&gt;
      &lt;div&gt;
          사용자 이름1&lt;span th:text=&quot;${user.username}&quot;&gt;&lt;/span&gt;
          사용자 나이1&lt;span th:text=&quot;${user.age}&quot;&gt;&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;
          요약&lt;span th:text=&quot;${user.username} +&#39;/&#39; + ${user.age}&quot;&gt;&lt;/span&gt;
      &lt;/div&gt;
  &lt;/th:block&gt;</code></pre></li>
<li><em>👍 결과*</em>
<img src="https://velog.velcdn.com/images/boram_han/post/171cdc9b-a4f2-4c93-be94-502533583b31/image.png" alt=""></li>
</ul>
<h3 id="3-자바스크립트-인라인">3) 자바스크립트 인라인</h3>
<ul>
<li>자바스크립트 영역에 자바스크립트 인라인을 사용하면 타임리프가 관여해 도와줌<h3 id="①-기본-inline">① 기본 inline</h3>
</li>
<li><em>💡 예시*</em></li>
<li><strong>Java</strong><pre><code>  @GetMapping(&quot;/javascript&quot;)    
  public String basicJavascript(Model model) {
      model.addAttribute(&quot;user&quot;, new User(&quot;userD&quot;, 40));
      addUsers(model);
      return &quot;basic/javascript&quot;;
  }</code></pre></li>
<li><strong>Html</strong><pre><code>  &lt;script th:inline=&quot;javascript&quot;&gt;
      let username = [[${user.username}]];
      let age = [[${user.age}]];
  &lt;/script&gt;    </code></pre></li>
<li><em>👍 결과(페이지 소스보기)*</em><pre><code class="language-html">   &lt;script&gt;
       let username = &quot;userD&quot;;
       let age = 40;
   &lt;/script&gt;  </code></pre>
<h3 id="②-내추럴-템플릿-표기법">② 내추럴 템플릿 표기법</h3>
</li>
<li><code>/*[[${객체.변수이름}]]*/ &quot;test&quot;</code></li>
</ul>
<p><strong>💡 예시</strong></p>
<ul>
<li><strong>Html</strong><pre><code>  &lt;script th:inline=&quot;javascript&quot;&gt;
      let username2 = /*[[${user.username}]]*/ &quot;test username&quot;;
  &lt;/script&gt;    </code></pre></li>
<li><em>👍 결과(페이지 소스보기)*</em><pre><code class="language-html">   &lt;script&gt;
       // 자바스크립트 내추럴 템플릿
       let username2 = &quot;userD&quot;;
   &lt;/script&gt;  </code></pre>
<h3 id="③-객체-담기">③ 객체 담기</h3>
</li>
<li>JSON 형태로 받아옴</li>
</ul>
<p><strong>💡 예시</strong></p>
<ul>
<li><p><strong>Html</strong></p>
<pre><code>  &lt;script th:inline=&quot;javascript&quot;&gt;
      let user = [[${user}]];
  &lt;/script&gt;    </code></pre></li>
<li><p><em>👍 결과(페이지 소스보기)*</em></p>
<pre><code class="language-html">   &lt;script&gt;
       // 객체
       let user = {&quot;username&quot;:&quot;userD&quot;,&quot;age&quot;:40};
   &lt;/script&gt;  </code></pre>
<h3 id="④-자바스크립트-인라인-each">④ 자바스크립트 인라인 each</h3>
</li>
<li><p><em>💡 예시*</em></p>
</li>
<li><p><strong>Html</strong></p>
<pre><code>  &lt;script th:inline=&quot;javascript&quot;&gt;
      /*&lt;![CDATA[*/
          [# th:each=&quot;user, stat : ${users}&quot;]
              let user[[${stat.count}]] = [[${user}]];
          [/]
      /*]]&gt;*/
  &lt;/script&gt;    </code></pre></li>
<li><p><em>👍 결과(페이지 소스보기)*</em></p>
<pre><code class="language-html">   &lt;script&gt;
       /*&lt;![CDATA[*/

               let user1 = {&quot;username&quot;:&quot;userA&quot;,&quot;age&quot;:10};
               let user2 = {&quot;username&quot;:&quot;userB&quot;,&quot;age&quot;:20};
               let user3 = {&quot;username&quot;:&quot;userC&quot;,&quot;age&quot;:30};

       /*]]&gt;*/
   &lt;/script&gt;  </code></pre>
<h3 id="4-템플릿-레이아웃">4) 템플릿 레이아웃</h3>
</li>
<li><p><code>th:fragment</code></p>
<ul>
<li>다른 html에서 해당 html을 사용하고 싶을 때 사용</li>
</ul>
</li>
</ul>
<p><strong>💡 예시</strong></p>
<ul>
<li><p>다른 html에서 copy, copyParam이란 이름으로 해당 html 사용 가능</p>
<pre><code>  &lt;footer th:fragment=&quot;copy&quot;&gt;
      footer 자리 입니다.
  &lt;/footer&gt;

  &lt;footer th:fragment=&quot;copyParam(param1,param2)&quot;&gt;
      &lt;p&gt;파라미터 자리입니다.&lt;/p&gt;
      &lt;p th:text=&quot;${param1}&quot;&gt;&lt;/p&gt;
      &lt;p th:text=&quot;${param2}&quot;&gt;&lt;/p&gt;
  &lt;/footer&gt;</code></pre><h3 id="①-thinsert">① th:insert</h3>
</li>
<li><p><em>💡 예시*</em></p>
</li>
<li><p><strong>Html</strong></p>
<pre><code>  &lt;h2&gt;부분 포함 insert&lt;/h2&gt;
  &lt;div th:insert=&quot;~{template/fragment/footer :: copy}&quot;&gt;&lt;/div&gt;    </code></pre></li>
<li><p><em>👍 결과(페이지 소스보기)*</em></p>
<pre><code class="language-html">   &lt;h2&gt;부분 포함 insert&lt;/h2&gt;
   &lt;div&gt;&lt;footer&gt;
       footer 자리 입니다.
   &lt;/footer&gt;&lt;/div&gt;</code></pre>
<h3 id="②-threplace">② th:replace</h3>
</li>
<li><p><em>💡 예시*</em></p>
</li>
<li><p><strong>Html</strong></p>
<pre><code>  &lt;h2&gt;부분 포함 : replace&lt;/h2&gt;
  &lt;div th:replace=&quot;~{template/fragment/footer :: copy}&quot;&gt;&lt;/div&gt;
  &lt;/script&gt;    </code></pre></li>
<li><p><em>👍 결과(페이지 소스보기)*</em></p>
<pre><code class="language-html">   &lt;h2&gt;부분 포함 : replace&lt;/h2&gt;
   &lt;footer&gt;
       footer 자리 입니다.
   &lt;/footer&gt;</code></pre>
<h3 id="③-threplace-단순-표현식">③ th:replace 단순 표현식</h3>
</li>
<li><p><em>💡 예시*</em></p>
</li>
<li><p><strong>Html</strong></p>
<pre><code>  &lt;h2&gt;부분 포함 단순 표현식&lt;/h2&gt;
  &lt;div th:replace=&quot;template/fragment/footer :: copy&quot;&gt;&lt;/div&gt;  </code></pre></li>
<li><p><em>👍 결과(페이지 소스보기)*</em></p>
<pre><code class="language-html">   &lt;h2&gt;부분 포함 단순 표현식&lt;/h2&gt;
   &lt;footer&gt;
       footer 자리 입니다.
   &lt;/footer&gt;</code></pre>
<h3 id="④-파라미터-사용--threplace">④ 파라미터 사용 : th:replace</h3>
</li>
<li><p><em>💡 예시*</em></p>
<pre><code>   &lt;h1&gt;파라미터 사용&lt;/h1&gt;
       &lt;div th:replace=&quot;~{template/fragment/footer :: copyParam(&#39;데이터1&#39;,&#39;데이터2&#39;)}&quot;&gt;
       &lt;/div&gt;  </code></pre></li>
<li><p><em>👍 결과(페이지 소스보기)*</em></p>
<pre><code class="language-html">   &lt;h1&gt;파라미터 사용&lt;/h1&gt;
   &lt;footer&gt;
       &lt;p&gt;파라미터 자리입니다.&lt;/p&gt;
       &lt;p&gt;데이터1&lt;/p&gt;
       &lt;p&gt;데이터2&lt;/p&gt;
   &lt;/footer&gt;</code></pre>
</li>
<li><p><em>👍 결과*</em>
<img src="https://velog.velcdn.com/images/boram_han/post/2df53289-27cc-424e-823c-e2dfb332785b/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] HTTP 요청 파라미터 & Response view]]></title>
            <link>https://velog.io/@boram_han/Spring-HTTP-%EC%9A%94%EC%B2%AD-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0</link>
            <guid>https://velog.io/@boram_han/Spring-HTTP-%EC%9A%94%EC%B2%AD-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0</guid>
            <pubDate>Wed, 26 Apr 2023 09:10:27 GMT</pubDate>
            <description><![CDATA[<h1 id="1-http-요청-파라미터">1. HTTP 요청 파라미터</h1>
<ul>
<li>스프링이 제공하는 <code>@RequestParam</code>, <code>@ModelAttribute</code>을 사용하면 요청 파라미터를 매우 편리하게 이용할 수 있다.<h2 id="1-requestparam">(1) @RequestParam</h2>
</li>
<li>String, int같은 단순 타입의 파라미터를 받아올 때 사용</li>
<li>파라미터 이름으로 바인딩하는 방법</li>
</ul>
<p><strong>💡 기본 예시</strong> </p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/request-param-v2&quot;)
    public String requestParamV2(@RequestParam(&quot;username&quot;) String memberName, @RequestParam(&quot;age&quot;) int memberAge) {
        // 콘솔창에 띄우기
        System.out.println(&quot;username : &quot; + memberName);
        System.out.println(&quot;age : &quot; + memberAge);
        // 브라우저에 띄우기
        return &quot;ok v2&quot;;
    }</code></pre>
<blockquote>
<p><strong>📌 <code>@ResponseBody</code></strong> </p>
</blockquote>
<ul>
<li>view조회를 무시하고, HTTP message body에 직접 해당 내용 입력</li>
</ul>
<blockquote>
<ul>
<li><code>http://localhost:9090/request-param-v2?username=홍길동&amp;age=17</code></li>
</ul>
</blockquote>
<ul>
<li>@RequestParam(&quot;username&quot;) String memberName<ul>
<li>username으로 파싱해서 String memberName으로 받아옴 </li>
<li><strong>memberName = 홍길동</strong></li>
</ul>
</li>
<li>@RequestParam(&quot;age&quot;) int memberAge  <ul>
<li>age로 파싱해서 int memberAge으로 받아옴</li>
<li><strong>memberAge = 17</strong></li>
</ul>
</li>
</ul>
<p><strong>💡 HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(&quot;XXX&quot;) 생략 가능</strong></p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/request-param-v3&quot;)
    public String requestParamV3(@RequestParam String username, @RequestParam int age) {

        System.out.println(&quot;username : &quot; + username);
        System.out.println(&quot;age : &quot; + age);

        return &quot;ok v3&quot;;
    }</code></pre>
<blockquote>
</blockquote>
<p><code>@RequestParam(&quot;username&quot;) String memberName</code> → <code>@RequestParam String username</code></p>
<p>*<em>💡 String, int 등의 단순 타입이면 @RequestParam도 생략가능 *</em></p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/request-param-v4&quot;)
    public String requestParamV4(String username, int age) {

        System.out.println(&quot;username : &quot; + username);
        System.out.println(&quot;age : &quot; + age);

        return &quot;ok v4&quot;;
    }</code></pre>
<blockquote>
</blockquote>
<p><code>@RequestParam String username, @RequestParam int age</code> → <code>String username, int age</code></p>
<p><strong>💡 파라미터값에 대한 필수 여부</strong></p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/request-param-required&quot;)
    public String requestParamRequired(@RequestParam(required = true) String username, @RequestParam(required = false) Integer age) {

        System.out.println(&quot;username : &quot; + username);
        System.out.println(&quot;age : &quot; + age);

        return &quot;ok required&quot;;
    }</code></pre>
<blockquote>
</blockquote>
<p><code>required = false</code> : 파라미터 누락 가능
<code>required = true</code>  : 파라미터 반드시 필요</p>
<blockquote>
<p>int등 단순 변수 타입에 null값 ❌ -&gt; wrapper 타입으로 변경 Integer age</p>
</blockquote>
<p><strong>💡 defaultvalue : 기본값 세팅</strong></p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/request-param-default&quot;)
    public String requestParamDefault(@RequestParam(required = true, defaultValue = &quot;guest&quot;) String username, @RequestParam(required = false, defaultValue = &quot;-1&quot;) Integer age) {

        System.out.println(&quot;username : &quot; + username);
        System.out.println(&quot;age : &quot; + age);

        return &quot;ok defaultValue&quot;;
    }</code></pre>
<blockquote>
</blockquote>
<p><code>required = true</code>  : 파라미터 반드시 필요
<code>defaultValue = &quot;guest&quot;</code>  : username 기본값 &quot;guest&quot;
<code>defaultValue = &quot;-1&quot;</code>  : age 기본값 &quot;-1&quot;</p>
<blockquote>
<p><code>localhost:9090/request-param-default?username=</code>처럼 빈 문자열도 사용가능</p>
</blockquote>
<h2 id="2-modelattribute">(2) @ModelAttribute</h2>
<ul>
<li>String, int같은 단순 타입 외에 <code>사용자 정의 객체</code> 사용 시 </li>
</ul>
<p><strong>💡 기본 예시</strong></p>
<ul>
<li><p>객체 생성</p>
<pre><code class="language-java">public class HelloData {

  private String username;
  private int age;

</code></pre>
</li>
</ul>
<pre><code>public String getUsername() {
    return username;
}
public void setUsername(String username) {
    this.username = username;
}
public int getAge() {
    return age;
}
public void setAge(int age) {
    this.age = age;
}

@Override
public String toString() {
    return &quot;HelloData [username=&quot; + username + &quot;, age=&quot; + age + &quot;]&quot;;
}</code></pre><p>}</p>
<pre><code>```java
    @ResponseBody
    @RequestMapping(&quot;/model-attribute-v2&quot;)
    public String modelAttributeV2(@ModelAttribute HelloData helloData) {

        System.out.println(&quot;username : &quot; + helloData.getUsername());
        System.out.println(&quot;age : &quot; + helloData.getAge());
        System.out.println(&quot;helloData : &quot; + helloData.toString());

        return &quot;ok hellodata&quot;;
    }</code></pre><blockquote>
</blockquote>
<p><code>@ModelAttribute</code>  : 다음의 코드 자동화</p>
<pre><code>        HelloData helloData = new HelloData();
        helloData.setUsername(username);
        helloData.setAge(age);</code></pre><p><strong>💡 <code>@ModelAttribute</code> 생략가능</strong></p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/model-attribute-v3&quot;)
    public String modelAttributeV3(HelloData helloData) {

        System.out.println(&quot;username : &quot; + helloData.getUsername());
        System.out.println(&quot;age : &quot; + helloData.getAge());
        System.out.println(&quot;helloData : &quot; + helloData.toString());

        return &quot;ok hellodata3&quot;;
    }</code></pre>
<blockquote>
</blockquote>
<p><code>@ModelAttribute HelloData helloData</code> → <code>HelloData helloData</code></p>
<h1 id="2-response-view">2. Response View</h1>
<p><strong>💡 뷰 생성</strong></p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;p th:text=&quot;${data}&quot;&gt;empty&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="1-modelandview">(1) ModelAndView</h2>
<p><strong>💡 사용 예시</strong></p>
<pre><code class="language-java">    @RequestMapping(&quot;/response-view-v1&quot;)
    public ModelAndView responseViewV1() {
        ModelAndView mav = new ModelAndView(&quot;response/hello&quot;)
                                    .addObject(&quot;data&quot;,&quot;hello&quot;);
        return mav;
    }</code></pre>
<blockquote>
</blockquote>
<p><code>new ModelAndView(&quot;response/hello&quot;)</code> : 뷰 경로
<code>.addObject(&quot;data&quot;,&quot;hello&quot;)</code> : 데이터 담는 곳 
<code>&quot;data&quot;</code> : key 값 
<code>&quot;hello&quot;</code> : value 값</p>
<h2 id="2-model">(2) Model</h2>
<p><strong>💡 사용 예시</strong></p>
<pre><code class="language-java">    @RequestMapping(&quot;/response-view-v2&quot;)
    public String responseViewV2(Model model) {
        model.addAttribute(&quot;data&quot;,&quot;hello2&quot;);
        return &quot;response/hello&quot;;
    }</code></pre>
<blockquote>
</blockquote>
<p><code>model.addAttribute(&quot;data&quot;,&quot;hello2&quot;)</code> : 데이터 담는 곳<br><code>&quot;data&quot;</code> : key 값 
<code>&quot;hello2&quot;</code> : value 값 </p>
<p><strong>💡 @ResponseBody를 사용한다면</strong></p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/response-view-v3&quot;)
    public String responseViewV3(Model model) {
        model.addAttribute(&quot;data&quot;,&quot;hello2&quot;);
        return &quot;response/hello&quot;;
    }</code></pre>
<blockquote>
</blockquote>
<p><code>@ResponseBody</code> ❌ : response/body로 뷰 리졸버가 실행되어서 뷰를 찾고 렌더링
<code>@ResponseBody</code> ⭕ : 뷰 리졸버를 실행하지 않고 , http 메시지 바디에 직접 response/body라는 문자열을 반환</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 스프링 구조와 의존성]]></title>
            <link>https://velog.io/@boram_han/spring-%EC%8A%A4%ED%94%84%EB%A7%81-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9D%98%EC%A1%B4%EC%84%B1</link>
            <guid>https://velog.io/@boram_han/spring-%EC%8A%A4%ED%94%84%EB%A7%81-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9D%98%EC%A1%B4%EC%84%B1</guid>
            <pubDate>Mon, 24 Apr 2023 18:08:56 GMT</pubDate>
            <description><![CDATA[<h1 id="1-스프링-mvc-구조">1. 스프링 MVC 구조</h1>
<p><img src="https://velog.velcdn.com/images/boram_han/post/fd683fa4-3a50-4668-90dd-95d85f30b8cb/image.PNG" alt=""></p>
<p><strong>🙆‍♀️ 알아두기</strong></p>
<blockquote>
</blockquote>
<p><strong>파란색</strong> : spring framewor가 관장하는 방식
<strong>보라색</strong> : 개발자가 관장하는 부분(MVC 패턴)
<strong>초록색</strong> : view = html or jsp 순수 html 사용 비중 증가</p>
<p>① 클라이언트가 request -&gt; 프론트 컨트롤러 역할의 dispatcher servlet은 그 요청을 처리하기 위한 컨트롤러 객체를 검색 
② dispatcher servlet이 직접 컨트롤러를 검색하지 않고 HandlerMapping이라는 빈 객체에게 컨트롤러 검색을 요청</p>
<p>HandlerMapping은 클라이언트의 요청 경로를 이요해서이를 처리할 컨트롤러 빈 객체를 dispatcherservlet에 전달</p>
<p>ex. 웹 요청 경로가 /hello -&gt; 컨트롤러 빈 중 /hello요청 경로를 처리할 controller 리턴</p>
<p>③ dispatcher servlet은 handler mapping이 찾아준 컨트롤러 객체를 처리할 수 있도록 handlerAdapter 빈에게 요청 처리를 위임</p>
<p>④ handlerAdapter는 controller의 알맞은 메서드를 호출해서 요청을 처리</p>
<p>⑤ 컨트롤러 처리 결과를 리턴</p>
<p>⑥ HandlerAdapter로 부터 컨트롤러의 요청 처리 결과를 받으면 dispatcher Adapter는 결과를 보여줄 뷰를 찾기 위해 view resolver 빈 객체 사용 -&gt; view resolver는 이 뷰 이름에 해당하는 view 객체를 찾거나 생성해서 리턴</p>
<p>⑦ dispatcherservlet은 view resolver가 리턴한 view 객체에게 응답 결과 생성을 요청</p>
<p>⑧ view 객체는 웹 브라워에 응답결과를 생성</p>
<p><img src="https://velog.velcdn.com/images/boram_han/post/2950807d-383f-4439-b58d-e56a740c1301/image.PNG" alt=""></p>
<p><strong>🙆‍♀️ 알아두기</strong></p>
<blockquote>
</blockquote>
<p><strong>Model</strong> : 사용자가 원하는 데이터나 정보를 제공(db)</p>
<pre><code>dto : 양쪽으로 전송되어 오고가는 데이터들을 담은 객체
dao : 데이터에 접근, 데이터를 관리하기 위한 객체
서비스 : 핵심 비즈니스 로직 구현</code></pre><p><strong>View</strong> : 보여지는 화면
<strong>Controller</strong> : 사용자의 요청을 처리하고, 그 요청에 따른 전체적은 흐름을 제어</p>
<h1 id="2-웹-애플리케이션의-계층-구조">2. 웹 애플리케이션의 계층 구조</h1>
<p><img src="https://velog.velcdn.com/images/boram_han/post/6701865b-4e56-4ad4-871f-54bcd0b0e23b/image.PNG" alt=""></p>
<blockquote>
<p>컨트롤러는 서비스의 기능을 사용하며, 서비스에 의존
서비스는 리포지토리의 기능을 사용하며, 리포지토리에 의존</p>
</blockquote>
<ul>
<li><strong>컨트롤러</strong> : http 요청 처리, 웹 MVC의 컨트롤러 역할</li>
<li><strong>서비스</strong> : 핵심 비즈니스 로직 구현</li>
<li><strong>레파지토리</strong> : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리한다.</li>
<li><strong>도메인</strong> : 비즈니스 도메인 객체 </li>
</ul>
<h1 id="3-의존성">3. 의존성</h1>
<h2 id="1-제어역전">(1) 제어역전</h2>
<ul>
<li><p><strong>IoC</strong>, Inversion of Control</p>
</li>
<li><p>제어의 흐름이 기존에는 개발자가 작성한 코드에서 객체를 생성하고 관리하는 방식에서 프레임워크나 컨테이너로 넘어가게 되는 것</p>
</li>
<li><p>개발자가 프레임워크 기능을 호출하던 것에서 -&gt; 프레임워크가 개발자 코드를 호출</p>
<ul>
<li>제어의 흐름 : 개발자가 작성한 코드 -&gt; 프레임워크</li>
<li>개발자 -&gt; 전체를 직접 구현 x, 자신의 코드를 부분적으로 &quot;끼워넣기&quot; 하는 형태로 구현 </li>
<li>개발자는 비즈니스 로직에 집중 가능</li>
</ul>
</li>
<li><p>프레임워크 기능</p>
<ul>
<li>객체의 생성, 소멸과 같은 라이프 사이클을 관리</li>
<li>스프링으로부터 필요한 객체를 얻어올 수도 있음</li>
<li>즉, 프레임워크가 개발자가 작성한 코드를 호출하면서, 필요한 객체를 생성하고, 관리하고, 필요한 때에 호출</li>
</ul>
<p><strong>💡 예시</strong></p>
<ul>
<li>서비스 객체가 리포지토리 객체를 사용</li>
<li>기존 : 서비스 객체가 직접 리포지토리 객체를 생성하고 사용</li>
<li>제어 역전 후 : <ul>
<li>리포지토리 객체의 생성과 관리를 프레임워크에 맡김</li>
<li>서비스 객체는 직접 리포지토리 객체를 생성하거나 호출하지 않고, 프레임워크에서 제공하는 기능을 이용하여 사용</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>결론</strong> : 객체의 의존성을 역전 함으로서 객체간의 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여 가독성 강화 및 코드 중복 최소화, 편리한 유지보수 할 수 있게 함</p>
</li>
</ul>
<blockquote>
<p>📌 스프링 프레임워크에서 객체를 생성하고 관리하는 곳 :  <strong>컨테이너</strong></p>
</blockquote>
<h2 id="2-spring-container">(2) Spring Container</h2>
<p><img src="https://velog.velcdn.com/images/boram_han/post/1ac7846c-2c3a-4588-b2ca-50ac9329b092/image.PNG" alt=""></p>
<ul>
<li>스프링은 실행 시 객체들을 담고 있는 Container가 존재<ul>
<li>자바객체 = Bean</li>
<li>자바 객체의 생명 주기 관리<ul>
<li>개발자는 객체를 생성 소멸할 수 있는 데 스프링 컨테이너가 이 역할을 대신 해줌</li>
<li>즉, 제어의 흐름을 개발자 x -&gt; 프레임워크가 관리</li>
</ul>
</li>
<li>생성된 자바 객체들에게 추가적인 기능을 제공<ul>
<li>객체들 간의 <code>의존관계</code>를 스프링 컨테이너가 런타임 과정에서 만듦</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boram_han/post/320cc352-7c95-458b-9da5-f7944e7b07cc/image.PNG" alt=""></p>
<h2 id="3-membercontroller">(3) MemberController</h2>
<ul>
<li>생성자에 @Autowired -&gt; 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어줌</li>
<li>객체 의존관계를 외부에서 넣어주는 것을 <code>DI(Dependecy Injection) =  의존성 주입</code></li>
<li>이전에는 개발자가 직접 주입 -&gt; 현재, @Autowired에 의해 스프링이 주입</li>
</ul>
<blockquote>
<p><strong>📌 @Autowired</strong></p>
</blockquote>
<ul>
<li>필요한 의존 객체의 &quot;타입&quot;에 해당하는 빈을 찾아 주입</li>
<li>기본값이 true이기 때문에 의존성 주입을 할 대상을 찾지 못한다면 애플리케이션 구동에 실패</li>
</ul>
<blockquote>
<p><strong>📌 POJO</strong></p>
</blockquote>
<ul>
<li>Plain Old Java Object, 단순한 자바 오브젝트</li>
<li>객체 지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트</li>
</ul>
<h2 id="4-컴포넌트-스캔의-대상">(4) 컴포넌트 스캔의 대상</h2>
<ul>
<li><p><strong>Component Scan</strong></p>
<ul>
<li>어노테이션을 사용하여 스프링에서 관리하는 Bean으로 등록</li>
<li>자동으로 이루어지며 스프링 컨테이너가 지정된 패키지에서 모든 클래스를 검색하고, 스프링에서 관리하는 Bean으로 등록</li>
</ul>
</li>
<li><p><strong>컴포넌트 스캔의 대상</strong></p>
<ul>
<li><code>main메서드</code>가 있는 class의 동일 패키지 또는 하위 패키지만 spring이 scan<ul>
<li>예를 들어, main 메서드가 있는 클래스가 &quot;com.example&quot; 패키지에 있다면, &quot;component scan&quot;은 &quot;com.example&quot; 패키지와 그 하위 패키지에서만 검색을 수행</li>
</ul>
</li>
<li>@Component, @Controller, @Service, @Repository 등이 지정된 클래스를 검색하여 Bean으로 등록</li>
</ul>
</li>
</ul>
<h2 id="⭐-5-의존성-주입-방법">⭐ (5) 의존성 주입 방법</h2>
<ul>
<li>의존성 주입(Dependency Injection, DI) : 제어역전을 구현하는 방법</li>
<li>객체간의 의존성을 낮추기 위해 외부에서 필요한 객체를 주입받는 방식</li>
</ul>
<h4 id="①-field-injection-필드-주입">① Field Injection (필드 주입)</h4>
<pre><code>- 순환참조 때문에 권장 x</code></pre><pre><code class="language-JAVA">    @Autowired 
    private MemberRepository memberRepository;</code></pre>
<h4 id="②-setter-injection-수정자-주입">② Setter Injection (수정자 주입)</h4>
<pre><code>- 바뀔(수정될) 가능성이 있으므로 사용 x</code></pre><pre><code class="language-JAVA">    private MemberRepository memberRepository;

     @Autowired
     public void setMemberRepository(MemberRepository memberRepository) {
             this.memberRepository = memberRepository;
      } </code></pre>
<h4 id="③-constructor-injection-생성자-주입">③ Constructor Injection (생성자 주입)</h4>
<pre><code>- 가장 많이 사용되는 방법</code></pre><pre><code>    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }


    // 현재 클래스에서만 접근 가능하도록 막아두기
    // 의존 관계가 한번 만들어지면 아예 수정이 불가능한 상태로 만들어주기</code></pre><h1 id="⭐-4-solid">⭐ 4. SOLID</h1>
<ul>
<li><strong>SOLID</strong> : 클린코드로 유명한 로버트 마틴이 정리한 좋은 객체 지향 설계의 5가지 원칙을 정리</li>
<li>스프링 프레임워크는 SOLID 원칙을 지키도록 하여, 유지보수, 확장성, 재사용성, 테스트 용이성 등을 보장하며, 객체지향적으로 프로그래밍하는 데 도움을 줌<blockquote>
<ul>
<li><strong>SRP</strong> : 단일 책임 원칙 (Single responsibility principle)</li>
<li><strong>OCP</strong> : 개방-폐쇄 원칙 (Open-closed principle)</li>
<li><strong>LSP</strong> : 리스코프 치환 원칙 (Liskov substitution principle)</li>
<li><strong>ISP</strong> : 인터페이스 분리 원칙 (Interface segregation principle)</li>
<li><strong>DIP</strong> : 의존관계 역전 원칙 (Dependenct inversion principle)</li>
</ul>
</blockquote>
</li>
</ul>
<h3 id="1-srp--단일-책임-원칙">(1) SRP : 단일 책임 원칙</h3>
<ul>
<li>한 클래스는 하나의 책임만 </li>
<li>하나의 책임이라는 것은 모호<ul>
<li>클 수도 있고, 작을 수도</li>
<li>문맥과 상황에 따라 다름</li>
</ul>
</li>
<li>어떻게 하는 게 설계가 잘 된건지 중요한 판단 기준은 변경 </li>
<li>변경이 있을 때 파급효과가 적으면 단일 책임 원칙을 잘 따른 것</li>
<li>EX) ui 변경할 때 하나 바꾸면 다른 거 다 바꿔야 하는 것 ❌</li>
</ul>
<h3 id="2-ocp--개방-폐쇄-원칙">(2) OCP : 개방-폐쇄 원칙</h3>
<ul>
<li>클래스가 확장에는 열려있고, 수정과 변경에는 닫힘</li>
<li>수정과 변경은 오류를 야기할 수 있으므로<blockquote>
</blockquote>
기존 코드 변경 없이 코드 확장하는 법  <blockquote>
<p><strong>1.  다형성 활용</strong>
인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현 -&gt; 기존 코드 변경 x
<strong>2. 스프링 활용</strong>
객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자 = 스프링</p>
</blockquote>
</li>
</ul>
<h3 id="3-lsp--리스코프-치환-원칙">(3) LSP : 리스코프 치환 원칙</h3>
<ul>
<li>프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위타입의 인스턴스로 바꿀 수 있어야 함</li>
<li>다형성에서 하위클래스는 인터페이스 규약을 다 지켜야 함<ul>
<li>인터페이스를 구현한 구현체를 믿고 사용하려면, 이 원칙이 필요 </li>
<li>단순히 컴파일에 성공하는 것을 넘어서 이야기<blockquote>
<p>EX. 악셀 구현 -&gt; 악셀은 앞으로 가야함 BUT, 뒤로 가게 구현 한다면? 
컴파일 오류는 없음 -&gt; BUT, 인터페이스 규약이 깨짐 -&gt; 인터페이스 규약을 지켜줘야됨</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<h3 id="4-isp--인터페이스-분리-원칙">(4) ISP : 인터페이스 분리 원칙</h3>
<ul>
<li>특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 나음</li>
<li>인터페이스가 명확해지고, 대체 가능성이 높아짐<blockquote>
<p>EX. 자동차 인터페이스 -&gt; 운전 인터페이스, 정비 인터페이스로 분리
인터페이스를 분리하면 정비 인터페이스 자체가 변해도 운전 인터페이스는 영향을 주지 않는다.</p>
</blockquote>
</li>
</ul>
<h3 id="5-dip--의존관계-역전-원칙">(5) DIP : 의존관계 역전 원칙</h3>
<ul>
<li>프로그래머는 추상화에 의존해야지 구체화에 의존하면 ❌</li>
<li>의존성 주입은 이 원칙을 따르는 방법 중 하나</li>
<li>즉, 구현 클래스에 의존하지 말고 인터페이스에 의존할 것<blockquote>
<p>EX. 운전자가 자동차역할에 대해 잘 알아야지 k3, 테스라 각각에 집중하면 운전이 힘들어 짐</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 스프링 개요]]></title>
            <link>https://velog.io/@boram_han/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@boram_han/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Wed, 19 Apr 2023 16:39:39 GMT</pubDate>
            <description><![CDATA[<h1 id="1-framework-vs-library">1. Framework vs Library</h1>
<h2 id="1-framework">(1) Framework</h2>
<ul>
<li><code>&#39;뼈대나 근간&#39;</code>을 이루는 코드들의 묶음</li>
<li>프로그램의 기본 흐름이나 구조를 정하고,  이 구조에 자신의 코드를 추가하는 방식으로 개발할 수 있도록 하는 <code>프로그래밍의 기본 틀</code>을 의미</li>
<li>개발에 필요한 구조가 제공되고, 여기에 필요한 부분을 <code>조립</code>하는 형태로 개발이 진행된다. </li>
</ul>
<h2 id="2-library">(2) Library</h2>
<ul>
<li>자주 사용되는 로직을 재사용하기 편리하도록 잘 정리한 <code>일련의 코드들의 집합</code></li>
</ul>
<h2 id="3-framework-vs-library">(3) Framework vs Library</h2>
<ul>
<li>프레임워크는 자동차의 프레임, 즉 기본적으로 구성하고 있는 뼈대 -&gt; 바꿀 수 없음</li>
<li>라이브러리는 자동차의 기능을 하는 부품을 의미</li>
<li><blockquote>
<p>쉽게 바꿀 수 있음</p>
</blockquote>
</li>
<li><code>스프링 프레임워크</code> : 이미 여러 라이브러리들이 장착돼 있음 </li>
</ul>
<h1 id="2-spring">2. Spring</h1>
<h2 id="1-스프링-프레임워크">(1) 스프링 프레임워크</h2>
<ul>
<li><p>스프링 프레임워크는 자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크</p>
</li>
<li><p>동적인 웹 사이트를 개발하기 위한 여러가지 서비스를 제공</p>
</li>
<li><p><a href="https://docs.spring.io/spring-boot/docs/2.7.10/reference/html/web.html#web">스프링 사용방법</a></p>
<h2 id="2-스프링-사용하기">(2) 스프링 사용하기</h2>
<h3 id="1-sts-다운받기">1) sts 다운받기</h3>
</li>
<li><p>sts : spring-tool-suite</p>
</li>
<li><p><a href="https://spring.io/tools">sts 다운</a></p>
</li>
<li><p>압축파일 풀어서 툴 사용하기
<img src="https://velog.velcdn.com/images/boram_han/post/a866a249-d27b-4c91-b2a0-c00c862d21b6/image.png" alt=""></p>
</li>
</ul>
<h3 id="2-spring-프로젝트-만들기">2) spring 프로젝트 만들기</h3>
<ul>
<li><a href="https://start.spring.io">스프링 프로젝트 만들기</a></li>
</ul>
<p><img src="https://velog.velcdn.com/images/boram_han/post/f78c079c-9e88-41de-9766-ffe6d2f95249/image.png" alt=""></p>
<ul>
<li><p>프로젝트 옵션</p>
<ul>
<li><p><strong>Project</strong> : 빌드 툴 (프로젝트에 필요한 의존성을 관리하는 툴) 선택</p>
<ul>
<li>과거에는 Maven<ul>
<li>xml 방식으로 작성</li>
<li>depency 태그로 라이브러리 복붙</li>
<li>매번 정확한 버전을 입력해줘야 함</li>
</ul>
</li>
<li>최근에는 Gradle<ul>
<li>주소 복붙으로 가능</li>
<li>매번 정확한 버전을 입력해줄 필요 없음</li>
<li>더 직관적</li>
<li><img src="https://velog.velcdn.com/images/boram_han/post/b2ddaeb9-849a-4110-8571-df79fe9d99b5/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Spring Boot</strong> : 사용할 버전 선택</p>
<ul>
<li>SNAPshot : 개발중인 버전</li>
<li>M2 : 베타버전 ( 버그테스트 중)</li>
</ul>
</li>
<li><p><strong>Group</strong> : 회사의 identity</p>
</li>
<li><p><strong>artifact</strong> : 프로젝트 이름</p>
</li>
<li><p><strong>Packaging</strong> : 스프링부트는 웬만하면 jar로 배포</p>
</li>
<li><p><strong>Dependencies</strong> : 프로젝트 생성과 동시에 사용할 라이브러리</p>
<ul>
<li>spring web : 별도 설치 없이 톰캣, 서블릿 사용 가능</li>
<li>thymeleaf : jsp 대신 사용할 라이브러리</li>
</ul>
</li>
</ul>
</li>
<li><p>generate로 프로젝트 생성</p>
</li>
<li><p>사용할 workplace에서 프로젝트 zip 풀기</p>
</li>
</ul>
<h3 id="3-eclipse에서-프로젝트-사용">3) eclipse에서 프로젝트 사용</h3>
<h4 id="①-import-하기">① import 하기</h4>
<p><img src="https://velog.velcdn.com/images/boram_han/post/6078248e-1aa0-4e38-91d5-0c517acd2e81/image.png" alt=""></p>
<h4 id="②-html-파일-불러올-수-있는-환경-만들기">② html 파일 불러올 수 있는 환경 만들기</h4>
<ul>
<li>eclipse marketplace 들어가기
<img src="https://velog.velcdn.com/images/boram_han/post/c021a1ca-be7e-45cc-98b0-4559f8032e7c/image.png" alt=""></li>
</ul>
<ul>
<li>순서대로 눌러주기
<img src="https://velog.velcdn.com/images/boram_han/post/8243f5fb-2e99-4af7-9611-c1ea6047f570/image.png" alt=""></li>
</ul>
<h4 id="③-applicationproperties-사용해서-환경설정하기">③ application.properties 사용해서 환경설정하기</h4>
<ul>
<li><p>src/main/resources의 application.properties에서 환경설정 가능함</p>
</li>
<li><p>port 수정
<img src="https://velog.velcdn.com/images/boram_han/post/d100bff4-767f-4f1d-97b7-70edd45d9420/image.png" alt=""></p>
</li>
</ul>
<pre><code>server.port=9090</code></pre><ul>
<li>한글에 대한 기본 세팅<pre><code>spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true</code></pre></li>
<li>타임리프 설정 <pre><code>spring.thymeleaf.cache=false
// 새로고침으로 타임리프 변경사항 웹에서 확인 가능 </code></pre><h4 id="④-buildgradle-이용하면-원하는-라이브러리-추가-생략-가능">④ build.gradle 이용하면 원하는 라이브러리 추가 생략 가능</h4>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boram_han/post/3204c1d9-94d9-4b03-a0ff-8a1e10300cba/image.png" alt=""></p>
<h4 id="⑤-타임리프-템플릿-추가">⑤ 타임리프 템플릿 추가</h4>
<ul>
<li>앞으로 사용할 html에 타임리프를 계속 사용하기 때문에 매번 선언해주는 번거로움 방지
<img src="https://velog.velcdn.com/images/boram_han/post/90d3f3cb-5011-4131-8996-210b68fd454a/image.png" alt=""></li>
</ul>
<h2 id="3-주요-어노테이션과-기능">(3) 주요 어노테이션과 기능</h2>
<h3 id="1-주요-어노테이션">1) 주요 어노테이션</h3>
<h4 id="①-스프링-시작점">① 스프링 시작점</h4>
<ul>
<li><code>@SpringBootApplication</code><ul>
<li>스프링 부트의 자동 설정 읽기와 생성이 모두 자동으로 설정</li>
<li>src/main/java &gt; CoreApplication.java 파일 내에 자동 위치<h4 id="②-spring-mvc">② Spring MVC</h4>
</li>
</ul>
</li>
<li><code>@Controller</code> : 해당 url mapping을 찾는 역할</li>
<li><code>@GetMapping/@PostMapping</code> : url get방식/post방식 요청 매핑</li>
<li><code>@RequestMapping</code> : 방식에 상관없이 요청 매핑</li>
</ul>
<h4 id="③-파라미터-값-받는-어노테이션">③ 파라미터 값 받는 어노테이션</h4>
<ul>
<li><code>@RequestParam</code> <ul>
<li><code>@RequestParam(&quot;name&quot;)</code> : 기본 name이라는 key값을 파싱, 옵션 추가 가능<ul>
<li><code>required</code> : 파라미터 값 필수 여부<ul>
<li>true -&gt; 필수(default) , false -&gt; 필수 x</li>
</ul>
</li>
<li><code>defaultValue</code> : 파라미터 값이 없을 경우, 기본으로 들어갈 값</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>💡 예시</strong></p>
<pre><code class="language-java">&lt;&lt;Controller.java&gt;&gt;

@GetMapping(&quot;/hello-mvc&quot;)
public String helloMvc(@RequestParam(value = &quot;name&quot;, required = false, defaultValue = &quot;required value&quot;) String name, Model model) {
    model.addAttribute(&quot;name&quot;, name);
    return &quot;hello-template&quot;;
}</code></pre>
<pre><code class="language-java">&lt;&lt;hello-template.html&gt;&gt;

&lt;!DOCTYPE html&gt;
&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;

&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;p th:text=&quot;&#39;hello &#39; + ${name}&quot;&gt;Hello empty&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><strong>👍 결과</strong>
<img src="blob:https://velog.io/eeeb60ce-bab7-44d4-80f2-09d91636673c" alt="업로드중.."></p>
<p><strong>🙆‍♀️ 알아두기</strong></p>
<ul>
<li>컨트롤러에서 리턴값으로 문자를 반환하면 
resources/templates/ + {viewName} + .html
  -&gt; <code>뷰 리졸버(viewResolver)</code></li>
</ul>
<p><strong>💡 예시</strong></p>
<pre><code class="language-java">&lt;HomeController.java&gt;

@Controller
public class HomeContoller {

    // localhost:9090으로 호출하면 home() 호출
    @GetMapping(&quot;/&quot;)
    public String home() {
        return &quot;home&quot;;
    }
}</code></pre>
<pre><code class="language-java">&lt;src/main/resources/templates/home.html&gt;

&lt;!DOCTYPE html&gt;
&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    home 출력 테스트
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><strong>👍 결과</strong>
localhost:9090으로 호출하면 home.html 파일 출력</p>
<h3 id="2-주요-기능">2) 주요 기능</h3>
<h4 id="①-viewresolver-뷰-리졸버">① ViewResolver (뷰 리졸버)</h4>
<ul>
<li>실행할 뷰를 찾음</li>
<li>페이지 컨트롤러가 리턴한 뷰 이름에 해당하는 뷰 객체를 매핑하는 역할<h4 id="②-dispatcherservlet">② DispatcherServlet</h4>
</li>
<li>스프링 MVC도 프론트 컨트롤러 패턴으로 구현되어 있다.</li>
<li>스프링 MVC의 프론트 컨트롤러가 바로 디스패처 서블릿이다.</li>
<li>DispatcherServlet -&gt; FrameworkServlet -&gt; HttpServletBean -&gt; HttpServlet 상속받고 있음</li>
</ul>
<h2 id="4-model">(4) Model</h2>
<h3 id="1-model">1) Model</h3>
<ul>
<li>Controller에서의 데이터를 Model에 담음</li>
<li>View는 Model에 담겨 있는 데이터만 쏙쏙 골라서 화면에 바인딩</li>
<li>HashMap의 형태를 갖고 있고, Key와 value값을 저장</li>
<li>servlet에서 request.setAttribute()와 비슷한 역할</li>
</ul>
<p><strong>💡 예시</strong></p>
<pre><code class="language-java">@Controller
public class MemberController {

    // member url mapping
    @RequestMapping(&quot;member&quot;)
    public String getMember(Model model) {

        MemberDTO member = new MemberDTO( 1, &quot;자바학생&quot;, &quot;01012345678&quot;);
        model.addAttribute(&quot;member&quot;,member);
        return &quot;thymeleaf/member&quot;;
        // templates 파일 밑에 thymeleaf 밑에 member.html
    }    
}</code></pre>
<h3 id="2-modelandview">2) ModelAndView</h3>
<ul>
<li>model에서 view의 영역이 좀 더 확장</li>
<li>Model과 View를 동시에 설정이 가능하려면 컨트롤러는 ModelAndView객체만 리턴하지만 Model과 View가 모두 리턴 가능<ul>
<li>addObject(key, value) : 데이터 설정</li>
<li>setViewName(&quot;경로&quot;) : 뷰 설정</li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>