<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>나만의 개발 기록</title>
    <link>https://aamoos.tistory.com/</link>
    <description>웹개발을 하면서 기억하고 싶은 내용을 정리하는 블로그입니다</description>
    <language>ko</language>
    <pubDate>Sun, 12 Apr 2026 11:09:49 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>JaeSung Kim</managingEditor>
    <image>
      <title>나만의 개발 기록</title>
      <url>https://tistory1.daumcdn.net/tistory/3049955/attach/0cb037e12f8e48e9932620fd1aee0cff</url>
      <link>https://aamoos.tistory.com</link>
    </image>
    <item>
      <title>Error occurred during initialization of VMjava.lang.Error: Properties init: Could not determine current working directory.at jdk.internal.util.SystemProps$Raw.platformProperties(java.base@17.0.18/Native Method)at jdk.internal.util.SystemProps$Raw.&amp;lt;init&amp;gt;(j</title>
      <link>https://aamoos.tistory.com/770</link>
      <description>&lt;pre id=&quot;code_1771835112468&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Error occurred during initialization of VM
java.lang.Error: Properties init: Could not determine current working directory.
at jdk.internal.util.SystemProps$Raw.platformProperties(java.base@17.0.18/Native Method)
at jdk.internal.util.SystemProps$Raw.&amp;lt;init&amp;gt;(java.base@17.0.18/SystemProps.java:234)
at jdk.internal.util.SystemProps.initProperties(java.base@17.0.18/SystemProps.java:54)
at java.lang.System.initPhase1(java.base@17.0.18/System.java:2106)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssh에서 java -version을 입력했을때 위와 같은 에러가 발생하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이경우 java 자체의 결함이기보단 현재 터미널에 위치한 디렉터리가 갑자기 삭제되었거나 그럴때 터미널이 위치한 폴더를 시스템이 찾지 못했을때 생기는 에러입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771835205045&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd /
java -version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 폴더나 루트 디렉터리로 이동후 다시 입력을 해보면 정상적으로 java version이 표기가 됩니다.&lt;/p&gt;</description>
      <category>DevOps</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/770</guid>
      <comments>https://aamoos.tistory.com/770#entry770comment</comments>
      <pubDate>Mon, 23 Feb 2026 17:27:07 +0900</pubDate>
    </item>
    <item>
      <title>Vue3 기본문법</title>
      <link>https://aamoos.tistory.com/768</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. App.vue&lt;/h3&gt;
&lt;pre id=&quot;code_1768374292842&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;h1&amp;gt;Hello Vue 3&amp;lt;/h1&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;style scoped&amp;gt;
&amp;lt;/style&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- script setup -&amp;gt; Vue3 권장 문법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- template -&amp;gt; 화면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- style scoped -&amp;gt; 이 컴포넌트에만 스타일 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 반응형 데이터 (ref)&lt;/h3&gt;
&lt;pre id=&quot;code_1768374430892&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { ref } from &quot;vue&quot;;

const count = ref(0);
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;h1&amp;gt;Count: {{ count }}&amp;lt;/h1&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ref(값) -&amp;gt; 반응형&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- template에서는 .value 안씀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 이벤트 바인딩 (click)&lt;/h3&gt;
&lt;pre id=&quot;code_1768374513020&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { ref } from &quot;vue&quot;;

const count = ref(0);

function increment(){
  count.value++;
}

&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;h1&amp;gt;Count: {{ count }}&amp;lt;/h1&amp;gt;
  &amp;lt;button @click=&quot;increment&quot; &amp;gt;+&amp;lt;/button&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- @click = v-on:click&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- script에서는 .value로 값을 뽑음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 버튼 클릭시 count를 1씩 증가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;332&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wkffz/dJMcaacXqBP/HpvMU17tkLk1GRkxk27Fp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wkffz/dJMcaacXqBP/HpvMU17tkLk1GRkxk27Fp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wkffz/dJMcaacXqBP/HpvMU17tkLk1GRkxk27Fp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWkffz%2FdJMcaacXqBP%2FHpvMU17tkLk1GRkxk27Fp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;332&quot; height=&quot;216&quot; data-origin-width=&quot;332&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. v-model (양방향 바인딩)&lt;/h3&gt;
&lt;pre id=&quot;code_1768374663424&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { ref } from &quot;vue&quot;;

const name = ref(&quot;&quot;);
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;input v-model=&quot;name&quot; placeholder=&quot;이름 입력&quot; /&amp;gt;
  &amp;lt;p&amp;gt;안녕하세요, {{ name }}&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- v-model = input &amp;lt;-&amp;gt; state 자동 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- form에서 필수 문법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c82i0M/dJMcaiPxTOU/8lwbC3Q7vdzYKKDfHsUsS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c82i0M/dJMcaiPxTOU/8lwbC3Q7vdzYKKDfHsUsS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c82i0M/dJMcaiPxTOU/8lwbC3Q7vdzYKKDfHsUsS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc82i0M%2FdJMcaiPxTOU%2F8lwbC3Q7vdzYKKDfHsUsS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;270&quot; height=&quot;90&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 조건 렌더링 (v-if)&lt;/h3&gt;
&lt;pre id=&quot;code_1768374803883&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { ref } from &quot;vue&quot;;

const isLogin = ref(false);
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;button @click=&quot;isLogin = !isLogin&quot;&amp;gt;
    로그인 토글
  &amp;lt;/button&amp;gt;

  &amp;lt;p v-if=&quot;isLogin&quot;&amp;gt;로그인 상태&amp;lt;/p&amp;gt;
  &amp;lt;p v-else&amp;gt;로그아웃 상태&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기본값 false이므로 처음 화면은 로그아웃 상태, 버튼 클릭시 로그인 상태로 변경됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 조간이 자주 바뀌면 v-show 고려,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. reactive vs ref&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ref : 원시값 (number, string, boolean)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- reactive : 객체, 폼데이터&lt;/p&gt;
&lt;pre id=&quot;code_1768375150201&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { reactive, ref } from &quot;vue&quot;;

const count = ref(0);

const user = reactive({
  name: &quot;&quot;,
  age: 0,
})
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;p&amp;gt;count: {{ count }}&amp;lt;/p&amp;gt;

  &amp;lt;input v-model=&quot;user.name&quot; placeholder=&quot;이름&quot; /&amp;gt;
  &amp;lt;input v-model=&quot;user.age&quot; type=&quot;number&quot; placeholder=&quot;이름&quot; /&amp;gt;

  &amp;lt;p&amp;gt;{{ user.name }} // {{ user.age }}&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- reactive는 .value 못씀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 객체는 왠만하면 reactive&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. computed&lt;/h3&gt;
&lt;pre id=&quot;code_1768375296099&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { computed, ref } from &quot;vue&quot;;

const count = ref(0);

const doubleCount = computed(() =&amp;gt; {
  return count.value * 2;
})

&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;button @click=&quot;count++&quot;&amp;gt;&amp;lt;/button&amp;gt;
  &amp;lt;p&amp;gt;count: {{ count }}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;double: {{ doubleCount }}&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 값 가공은 computed&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- computed는 캐싱효과가 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7-1. computed 캐싱&lt;/h3&gt;
&lt;pre id=&quot;code_1768375476911&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { computed, ref } from &quot;vue&quot;;

const count = ref(0);

const doubleCount = computed(() =&amp;gt; {
  console.log(&quot;computed&quot;)
  return count.value * 2;
})

const test = () =&amp;gt; {
  console.log(&quot;method&quot;);
  return count.value * 2;
}

&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;button @click=&quot;count++&quot;&amp;gt;&amp;lt;/button&amp;gt;
  &amp;lt;p&amp;gt;count: {{ count }}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;double: {{ doubleCount }}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;double: {{ doubleCount }}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;double: {{ test() }}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;double: {{ test() }}&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 위에 코드를 실행후 network console로 확인을하면 computed로 선언한 함수는 2번 호출했는데 1번이 console로 찍히고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 2번 호출시는 2번 console이 찍힘&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;65&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oiI3X/dJMcacPnsap/iM4iJaYKa4BkKDbzS4FQmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oiI3X/dJMcacPnsap/iM4iJaYKa4BkKDbzS4FQmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oiI3X/dJMcacPnsap/iM4iJaYKa4BkKDbzS4FQmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoiI3X%2FdJMcacPnsap%2FiM4iJaYKa4BkKDbzS4FQmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;206&quot; height=&quot;65&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;65&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. Watch&lt;/h3&gt;
&lt;pre id=&quot;code_1768375711243&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { ref, watch } from &quot;vue&quot;;

const count = ref(0);

watch(count, (newVal, oldVal) =&amp;gt; {
  console.log(&quot;변경 : &quot;, oldVal, &quot;-&amp;gt;&quot;, newVal);
})

&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;button @click=&quot;count++&quot;&amp;gt;&amp;lt;/button&amp;gt;
  &amp;lt;p&amp;gt;{{ count }}&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- count 값이 변경될때 watch가 동작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. v-for&lt;/h3&gt;
&lt;pre id=&quot;code_1768375877869&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { ref } from &quot;vue&quot;;

const todos = ref([
  { id: 1, text: &quot;Vue 공부&quot; },
  { id: 2, text: &quot;TypeScript 공부&quot; },
]);
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&quot;todo in todos&quot; :key=&quot;todo.id&quot;&amp;gt;
      {{ todo.text }}
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- key 필수, id 없으면 에러&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9-1. computed + v-for&lt;/h3&gt;
&lt;pre id=&quot;code_1768375915327&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { ref, computed } from &quot;vue&quot;;

const todos = ref([
  { id: 1, text: &quot;Vue&quot;, done: true },
  { id: 2, text: &quot;TS&quot;, done: false },
]);

const doneTodos = computed(() =&amp;gt;
  todos.value.filter(t =&amp;gt; t.done)
);
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;h3&amp;gt;완료&amp;lt;/h3&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&quot;t in doneTodos&quot; :key=&quot;t.id&quot;&amp;gt;
      {{ t.text }}
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- todo 리스트에서 done이 true인 항목만 필터링해서 화면에 뿌림&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. props &amp;amp; emit&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Parent.vue&lt;/h4&gt;
&lt;pre id=&quot;code_1768453312470&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup&amp;gt;
import { ref } from 'vue'
import Child from './Child.vue' // 자식 컴포넌트 불러오기

const myTitle = ref(&quot;부모가 주는 선물&quot;)
const myCount = ref(0)

// 자식이 보낸 신호를 받았을 때 실행할 함수
const handleIncrease = () =&amp;gt; {
  myCount.value++
  console.log(&quot;자식의 신호를 받았습니다!&quot;)
}
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;div style=&quot;border: 2px solid #35495e; padding: 20px;&quot;&amp;gt;
    &amp;lt;h1&amp;gt; &amp;zwj; &amp;zwj;  여기는 부모 컴포넌트&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;현재 부모의 숫자: {{ myCount }}&amp;lt;/p&amp;gt;

    &amp;lt;Child 
      :childTitle=&quot;myTitle&quot;
      :count=&quot;myCount&quot;
      @increase-count=&quot;handleIncrease&quot; 
    /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Child.vue&lt;/h4&gt;
&lt;pre id=&quot;code_1768453343809&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script setup&amp;gt;
// 1. Props 정의: 부모에게 받을 데이터 이름표를 만듭니다.
const props = defineProps({
  childTitle: String, // 타입 지정 (String, Number 등)
  count: Number
})

// 2. Emits 정의: 부모에게 보낼 신호 이름을 정합니다.
const emit = defineEmits(['increase-count'])

// 버튼 클릭 시 실행할 함수
const sendSignal = () =&amp;gt; {
  // 'increase-count'라는 신호를 부모에게 발사! (필요하면 두 번째 인자로 데이터 전달 가능)
  emit('increase-count')
}
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;div style=&quot;border: 2px solid #42b883; padding: 10px; margin: 10px;&quot;&amp;gt;
    &amp;lt;h3&amp;gt;  여기는 자식 컴포넌트&amp;lt;/h3&amp;gt;
    &amp;lt;p&amp;gt;받은 제목: {{ childTitle }}&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;받은 숫자: {{ count }}&amp;lt;/p&amp;gt;
    
    &amp;lt;button @click=&quot;sendSignal&quot;&amp;gt;숫자 올려달라고 조르기 (Emit)&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>frontend/Vue</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/768</guid>
      <comments>https://aamoos.tistory.com/768#entry768comment</comments>
      <pubDate>Thu, 15 Jan 2026 13:56:13 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript 기본문법 정리</title>
      <link>https://aamoos.tistory.com/767</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript = 자바스크립트에 타입을 붙인 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰는 이유?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컴파일 전에 오류를 잡아줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자동완성 + 리팩토링이 미친 듯이 좋아짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 협업 / 대규모 프로젝트 필수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- TS는 &quot;문법&quot;이 아니라 &quot; 안전장치&quot;라고 생각하면됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 타입 붙이기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Javascript&lt;/h3&gt;
&lt;pre id=&quot;code_1768371959747&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let count = 0;
count = &quot;hello&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자바스크립트는 런타임 실행 전까지 에러인지 모름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Typescript&lt;/h3&gt;
&lt;pre id=&quot;code_1768372010563&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let count: number = 0;
count = &quot;hello&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 타입스크립트는 컴파일시 에러가 발생하여 바로 확인이 가능함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자주 쓰는 기본 타입&lt;/h3&gt;
&lt;pre id=&quot;code_1768372125237&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let userName: string = &quot;kim&quot;
let age: number = 30;
let isLogin: boolean = true;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Typescript 타입 추론&lt;/h3&gt;
&lt;pre id=&quot;code_1768372160975&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let price = 100;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- pice 뒤에 : number를 선언하지 않아도 값을 보고 자동으로 타입을 추론함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 배열 &amp;amp; 객체&lt;/h2&gt;
&lt;pre id=&quot;code_1768372213023&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let numbers: number[] = [1, 2, 3];
let users: string[] = [&quot;kim&quot;, &quot;lee&quot;];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. interface / type&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;interface&lt;/h3&gt;
&lt;pre id=&quot;code_1768372251862&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface User {
  id: number;
  email: string;
  isAdmin: boolean;
}

const user: User = {
  id: 1,
  email: &quot;test@test.com&quot;,
  isAdmin: false,
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1 interface를 써야하는 상황&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DTO / API 응답 구조&lt;/h4&gt;
&lt;pre id=&quot;code_1768373197190&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface UserDto {
  id: number;
  email: string;
  name: string;
}

interface ApiResponse&amp;lt;T&amp;gt; {
  success: boolean;
  data: T;
  message?: string;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Vue props 정의&lt;/h4&gt;
&lt;pre id=&quot;code_1768373256694&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface User {
  id: number;
  email: string;
}

const props = defineProps&amp;lt;{
  user: User;
  isAdmin: boolean;
}&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;implements&lt;/h3&gt;
&lt;pre id=&quot;code_1768373297015&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface LoginService {
  login(email: string, password: string): Promise&amp;lt;void&amp;gt;;
}

class AuthService implements LoginService {
  async login(email: string, password: string) {
    // ...
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;확장 (extends)&lt;/h4&gt;
&lt;pre id=&quot;code_1768373332121&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface BaseEntity {
  id: number;
  createdAt: string;
}

interface User extends BaseEntity {
  email: string;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;type&lt;/h3&gt;
&lt;pre id=&quot;code_1768372306091&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type User = {
  id: number;
  email: string;
  isAdmin: boolean;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 구조 정의 -&amp;gt; interface&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;union, 함수 타입 -&amp;gt; type&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-2. type를 써야하는 상황&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Union 타입&lt;/h4&gt;
&lt;pre id=&quot;code_1768373401288&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Status = &quot;idle&quot; | &quot;loading&quot; | &quot;error&quot;;
let status: Status = &quot;loading&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;함수 타입 정의&lt;/h4&gt;
&lt;pre id=&quot;code_1768373442379&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type LoginFn = (email: string, password: string) =&amp;gt; Promise&amp;lt;void&amp;gt;;
const login: LoginFn = async (email, password) =&amp;gt; {
  // ...
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;조합 타입 (intersection)&lt;/h4&gt;
&lt;pre id=&quot;code_1768373472115&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type WithToken = {
  accessToken: string;
};

type User = {
  id: number;
  email: string;
};

type AuthedUser = User &amp;amp; WithToken;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;제네릭 + 조건 타입&lt;/h4&gt;
&lt;pre id=&quot;code_1768373494794&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type ApiResult&amp;lt;T&amp;gt; = 
  T extends string ? { value: string } : { value: number };&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 함수 타입&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-1. 기본 함수&lt;/h3&gt;
&lt;pre id=&quot;code_1768372413935&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function add(a: number, b: number): number {
  return a + b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4-2. 화살표 함수&lt;/h3&gt;
&lt;pre id=&quot;code_1768372443722&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const add = (a: number, b: number): number =&amp;gt; {
  return a + b;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-3. return 없는 함수&lt;/h3&gt;
&lt;pre id=&quot;code_1768372461088&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function log(msg: string): void {
  console.log(msg);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Optional / Nullable&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1. Optional&lt;/h3&gt;
&lt;pre id=&quot;code_1768372496988&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface User {
  id: number;
  name?: string; // 있어도 되고 없어도 됨
}

const u1: User = { id: 1 };
const u2: User = { id: 2, name: &quot;kim&quot; };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- name뒤에 ?를 붙이면 값이 있을수도 있고 없을수도 있다라는 의미임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-2. null / undefined&lt;/h3&gt;
&lt;pre id=&quot;code_1768372543113&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let token: string | null = null;

token = &quot;abc&quot;;
token = null; // OK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- token 값의 타입이 string이거나 null일수 있다 라는 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Union 타입&lt;/h2&gt;
&lt;pre id=&quot;code_1768372593148&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let status: &quot;idle&quot; | &quot;loading&quot; | &quot;success&quot; | &quot;error&quot;;
status = &quot;loading&quot;;
status = &quot;done&quot;;   // 컴파일 에러&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 상태값에 done을 선언하지 않아서 컴파일 에러&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. any&lt;/h2&gt;
&lt;pre id=&quot;code_1768372720272&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let data: any = 123;
data.toUpperCase(); // 런타임 에러&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- any는 어떤타입이든 들어올수 있다라고 설정하는거여서, 사용하면 typescript 쓰는 의미가 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 123을 대문자로 바꾸려니 런타임 에러 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. Unknown&lt;/h2&gt;
&lt;pre id=&quot;code_1768372815291&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let data: unknown;

if (typeof data === &quot;string&quot;) {
  data.toUpperCase();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- unknown은 타입을 아직 모른다는걸 typescript에게 솔직하게 말하는 타입&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. any vs unknown&lt;/h2&gt;
&lt;pre id=&quot;code_1768372935045&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let data: any;

data = 123;
data = &quot;hello&quot;;

data.toUpperCase();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- any로 선언을하면 toUpperCase 컴파일은 통과하지만, 런타임에서 에러가 발생함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768372970313&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let data: unknown;

data = 123;
data = &quot;hello&quot;;

data.toUpperCase();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- unknown으로 선언하면 toUpperCase 컴파일에서 에러가 발생함, 즉 unknown은 확인하고 쓰라는걸 강제함&lt;/p&gt;</description>
      <category>frontend/Typescript</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/767</guid>
      <comments>https://aamoos.tistory.com/767#entry767comment</comments>
      <pubDate>Wed, 14 Jan 2026 15:43:37 +0900</pubDate>
    </item>
    <item>
      <title>ubuntu 서버 최초 세팅</title>
      <link>https://aamoos.tistory.com/766</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 서버 접속&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(기본 계정 : ubuntu)&lt;/p&gt;
&lt;pre id=&quot;code_1768356567387&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh ubuntu@server_ip&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 시스템업데이트&lt;/h3&gt;
&lt;pre id=&quot;code_1768356613192&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt update
sudo apt upgrade -y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 타임존 설정(선택이지만 추천)&lt;/h3&gt;
&lt;pre id=&quot;code_1768356638032&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo timedatectl set-timezone Asia/Seoul
timedatectl&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. sudo 권한 일반 사용자 계정 생성&lt;/h3&gt;
&lt;pre id=&quot;code_1768356809768&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo adduser username
sudo usermod -aG sudo username
groups username&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4-1. sudo 계정 생성후 정상확인&lt;/h4&gt;
&lt;pre id=&quot;code_1768356854351&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;su - username
sudo ls&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 비밀번호 묻고 정상실행돠면 성공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. ROOT 계정 SSH 로그인 비활성화&lt;/h3&gt;
&lt;pre id=&quot;code_1768356904335&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vim /etc/ssh/sshd_config

PermitRootLogin no # 해당 부분 no로 설정 yes면 root로 바로 로그인 가능한 상태임&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(선택) 로그인 유지시간 설정&lt;/h3&gt;
&lt;pre id=&quot;code_1768357315607&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vim /etc/ssh/sshd_config

ClientAliveInterval 300
ClientAliveCountMax 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ClientAliveInterval 300 : **300초(5분)**마다 클라이언트 응답 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ClientAliveCountMax 0 : 응답 없으면 &lt;b&gt;즉시 세션 종료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과: &lt;b&gt;5분 동안 아무 작업 없으면 자동 로그아웃&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(선택) 패스워드 오류 허용 횟수 (로그인 실패 제한)&lt;/h3&gt;
&lt;pre id=&quot;code_1768357383307&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MaxAuthTries 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패스워드 3번 틀리면 즉시 접속 종료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(선택) 로그인 시도 제한&lt;/h3&gt;
&lt;pre id=&quot;code_1768357558926&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LoginGraceTime 30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속 후 &lt;b&gt;30초 안에 인증 못 하면 강제 종료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;운영서버에서 가장 많이 쓰는 조합&lt;/h3&gt;
&lt;pre id=&quot;code_1768357622052&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 로그인 유지시간
ClientAliveInterval 300
ClientAliveCountMax 0

# 인증 보안
MaxAuthTries 3
LoginGraceTime 30

# root 로그인 차단 (이미 설정했을 가능성 높음)
PermitRootLogin no&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 만약 추가적으로 ssh 파일로만 로그인하고싶으면 아래 옵션을 설정하면 된다. id password 로그인 x&lt;/p&gt;
&lt;pre id=&quot;code_1768357726662&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 패스워드 로그인 허용 여부 (권장: 키 로그인만)
PasswordAuthentication no&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1 ssh 설정파일 정상적으로 변경되었는지 확인후 restart&lt;/h3&gt;
&lt;pre id=&quot;code_1768357105780&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo sshd -t
sudo systemctl restart ssh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo sshd -t : sshd_config에 문법에 오류가 없으면 아무것도 안뜸&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo systemctl restart ssh : ssh 재시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 방화벽(UFW) 설정&lt;/h3&gt;
&lt;pre id=&quot;code_1768363497259&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install ufw -y
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp   # 또는 변경 포트
sudo ufw enable
sudo ufw status verbose&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6-1. 웹서버 방화벽 설정&lt;/h3&gt;
&lt;pre id=&quot;code_1768363646947&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo ufw allow 80/tcp
sudo ufw allow 443/tcp&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DevOps</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/766</guid>
      <comments>https://aamoos.tistory.com/766#entry766comment</comments>
      <pubDate>Wed, 14 Jan 2026 13:07:52 +0900</pubDate>
    </item>
    <item>
      <title>npm : 'npm' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이름으로 인식되지 않습니다. 이름이 정확한지 확인하고 경로가 포함된 경우 경로가 올바른지 검증한 다음 다시 시도하십시오.</title>
      <link>https://aamoos.tistory.com/765</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTKvJC/dJMcahXrtfZ/ckrDAVIsbktAzwO75s0ko0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTKvJC/dJMcahXrtfZ/ckrDAVIsbktAzwO75s0ko0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTKvJC/dJMcahXrtfZ/ckrDAVIsbktAzwO75s0ko0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTKvJC%2FdJMcahXrtfZ%2FckrDAVIsbktAzwO75s0ko0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1244&quot; height=&quot;125&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;125&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터를 포맷한후 npm명령어가 먹지않아서 해결방법 포스팅합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm 명령어를 쓰다가 해당 에러가 발생할경우, cmd창에 아래명령어를 쳐서 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767948979703&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;node -v&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;59&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D45rt/dJMcacV6Fjo/sQKGghE2WgodMZ8unotRz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D45rt/dJMcacV6Fjo/sQKGghE2WgodMZ8unotRz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D45rt/dJMcacV6Fjo/sQKGghE2WgodMZ8unotRz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD45rt%2FdJMcacV6Fjo%2FsQKGghE2WgodMZ8unotRz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;576&quot; height=&quot;59&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;59&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; node js가 설치가 안되어있으면 node js를 다운로드합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nodejs.org/ko/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nodejs.org/ko/download&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768356213787&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Node.js &amp;mdash; Node.js&amp;reg; 다운로드&quot; data-og-description=&quot;Node.js&amp;reg; is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&quot; data-og-host=&quot;nodejs.org&quot; data-og-source-url=&quot;https://nodejs.org/ko/download&quot; data-og-url=&quot;https://nodejs.org/ko/download&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hgJfM/dJMb8SXq8iM/9UeODXgBtGGfwabNkAkng0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bjrH6c/dJMb9hCUDkO/b9Dj5lVNzHybMN6RRfFQqK/img.png?width=224&amp;amp;height=256&amp;amp;face=0_0_224_256&quot;&gt;&lt;a href=&quot;https://nodejs.org/ko/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nodejs.org/ko/download&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hgJfM/dJMb8SXq8iM/9UeODXgBtGGfwabNkAkng0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bjrH6c/dJMb9hCUDkO/b9Dj5lVNzHybMN6RRfFQqK/img.png?width=224&amp;amp;height=256&amp;amp;face=0_0_224_256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Node.js &amp;mdash; Node.js&amp;reg; 다운로드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Node.js&amp;reg; is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nodejs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;powershell에서 관리자 권한으로 실행&lt;/p&gt;
&lt;pre id=&quot;code_1767949232089&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Get-ExecutionPolicy
Set-ExecutionPolicy RemoteSigned
npm -v&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;39&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvHT7f/dJMcaajG35k/pR3SKIGPkXwimzsTDWbOm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvHT7f/dJMcaajG35k/pR3SKIGPkXwimzsTDWbOm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvHT7f/dJMcaajG35k/pR3SKIGPkXwimzsTDWbOm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvHT7f%2FdJMcaajG35k%2FpR3SKIGPkXwimzsTDWbOm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;39&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;39&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OfoZv/dJMcaiWipWt/QhYz0qcUG2IPXk2Gz1WQhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OfoZv/dJMcaiWipWt/QhYz0qcUG2IPXk2Gz1WQhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OfoZv/dJMcaiWipWt/QhYz0qcUG2IPXk2Gz1WQhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOfoZv%2FdJMcaiWipWt%2FQhYz0qcUG2IPXk2Gz1WQhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;932&quot; height=&quot;131&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 npm명령어가 정상적으로 동작하는것을 볼수있습니다.&lt;/p&gt;</description>
      <category>frontend</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/765</guid>
      <comments>https://aamoos.tistory.com/765#entry765comment</comments>
      <pubDate>Wed, 14 Jan 2026 11:04:16 +0900</pubDate>
    </item>
    <item>
      <title>reactJs 확장프로그램 devtool 설치</title>
      <link>https://aamoos.tistory.com/764</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1765796958149&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;React Developer Tools - Chrome 웹 스토어&quot; data-og-description=&quot;Adds React debugging tools to the Chrome Developer Tools. Created from revision 3cde211b0c on 10/20/2025.&quot; data-og-host=&quot;chromewebstore.google.com&quot; data-og-source-url=&quot;https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi&quot; data-og-url=&quot;https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KDXvJ/hyZPriI5bx/TbGRW5yg8caez4o0H6x4h0/img.jpg?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128&quot;&gt;&lt;a href=&quot;https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KDXvJ/hyZPriI5bx/TbGRW5yg8caez4o0H6x4h0/img.jpg?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;React Developer Tools - Chrome 웹 스토어&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Adds React debugging tools to the Chrome Developer Tools. Created from revision 3cde211b0c on 10/20/2025.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;chromewebstore.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 사이트에서 devtools 확장프로그램 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.jsx&lt;/p&gt;
&lt;pre id=&quot;code_1765797165663&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Counter from &quot;@/components/Counter&quot;;
import UserCard from &quot;@/components/UserCard&quot;;

function App() {
  const user = {
    name: &quot;Jaesung&quot;,
    age: 30,
    role: &quot;Frontend Dev&quot;,
  };

  return (
    &amp;lt;div style={{ padding: 20 }}&amp;gt;
      &amp;lt;h1&amp;gt;React DevTools Test&amp;lt;/h1&amp;gt;

      &amp;lt;Counter initial={5} /&amp;gt;
      &amp;lt;Counter /&amp;gt;

      &amp;lt;UserCard user={user} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;components/Counter.jsx&lt;/p&gt;
&lt;pre id=&quot;code_1765797180846&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

function Counter({ initial = 0 }) {
    const [count, setCount] = useState(initial);

    return (
        &amp;lt;div style={{ border: &quot;1px solid #ccc&quot;, padding: 12, marginBottom: 12 }}&amp;gt;
            &amp;lt;h3&amp;gt;Counter&amp;lt;/h3&amp;gt;
            &amp;lt;p&amp;gt;count: {count}&amp;lt;/p&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; setCount((c) =&amp;gt; c + 1)}&amp;gt;+&amp;lt;/button&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; setCount((c) =&amp;gt; c - 1)}&amp;gt;-&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default Counter;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;components/UserCard.jsx&lt;/p&gt;
&lt;pre id=&quot;code_1765797196315&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function UserCard({ user }) {
    return (
        &amp;lt;div style={{ border: &quot;1px solid #aaa&quot;, padding: 12 }}&amp;gt;
            &amp;lt;h3&amp;gt;UserCard&amp;lt;/h3&amp;gt;
            &amp;lt;p&amp;gt;name: {user.name}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;age: {user.age}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;role: {user.role}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default UserCard;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;655&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GB8La/dJMcab3OKJ9/YtZzDT50koDvER9bIZKrEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GB8La/dJMcab3OKJ9/YtZzDT50koDvER9bIZKrEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GB8La/dJMcab3OKJ9/YtZzDT50koDvER9bIZKrEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGB8La%2FdJMcab3OKJ9%2FYtZzDT50koDvER9bIZKrEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;741&quot; height=&quot;655&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;655&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WedgJ/dJMcaajxey5/KWFhSOjckIpTMqhRFCxARk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WedgJ/dJMcaajxey5/KWFhSOjckIpTMqhRFCxARk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WedgJ/dJMcaajxey5/KWFhSOjckIpTMqhRFCxARk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWedgJ%2FdJMcaajxey5%2FKWFhSOjckIpTMqhRFCxARk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;94&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;94&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tXJib/dJMcadAvN3Z/lXr7fPryh3jRuwghFg5Wzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tXJib/dJMcadAvN3Z/lXr7fPryh3jRuwghFg5Wzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tXJib/dJMcadAvN3Z/lXr7fPryh3jRuwghFg5Wzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtXJib%2FdJMcadAvN3Z%2FlXr7fPryh3jRuwghFg5Wzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;307&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dADjbH/dJMcaf6dDg3/3BfT3cyryrV40VzekKI7pK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dADjbH/dJMcaf6dDg3/3BfT3cyryrV40VzekKI7pK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dADjbH/dJMcaf6dDg3/3BfT3cyryrV40VzekKI7pK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdADjbH%2FdJMcaf6dDg3%2F3BfT3cyryrV40VzekKI7pK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;531&quot; height=&quot;275&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b720pC/dJMcafLUQvJ/CItbhQWnS2IYEVzPuK1g80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b720pC/dJMcafLUQvJ/CItbhQWnS2IYEVzPuK1g80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b720pC/dJMcafLUQvJ/CItbhQWnS2IYEVzPuK1g80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb720pC%2FdJMcafLUQvJ%2FCItbhQWnS2IYEVzPuK1g80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;255&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자도구 - Components를 클릭해보면 각 컴포넌트의 props hooks등을 확인할수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>frontend/React</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/764</guid>
      <comments>https://aamoos.tistory.com/764#entry764comment</comments>
      <pubDate>Mon, 15 Dec 2025 20:16:10 +0900</pubDate>
    </item>
    <item>
      <title>reactJs vsCode eslint 플러그인 설정</title>
      <link>https://aamoos.tistory.com/763</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인 선택후 eslint 설치&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lVIoo/dJMcah31Y50/TH3QxCp8wVEiTvhIdktnE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lVIoo/dJMcah31Y50/TH3QxCp8wVEiTvhIdktnE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lVIoo/dJMcah31Y50/TH3QxCp8wVEiTvhIdktnE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlVIoo%2FdJMcah31Y50%2FTH3QxCp8wVEiTvhIdktnE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;346&quot; height=&quot;229&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eslint.config.js&lt;/p&gt;
&lt;pre id=&quot;code_1765796771669&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{js,jsx}'],
    extends: [
      js.configs.recommended,
      reactHooks.configs.flat.recommended,
      reactRefresh.configs.vite,
    ],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parserOptions: {
        ecmaVersion: 'latest',
        ecmaFeatures: { jsx: true },
        sourceType: 'module',
      },
    },
    rules: {
      'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
      'react/prop-types': 'off',
    },
  },
])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- rule안에 react/prop-types off로 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>frontend/React</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/763</guid>
      <comments>https://aamoos.tistory.com/763#entry763comment</comments>
      <pubDate>Mon, 15 Dec 2025 20:07:42 +0900</pubDate>
    </item>
    <item>
      <title>reactJs 프로젝트 경로 src '@' 설정</title>
      <link>https://aamoos.tistory.com/762</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;vite.config.js&lt;/p&gt;
&lt;pre id=&quot;code_1765796102247&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { defineConfig } from &quot;vite&quot;;
import react from &quot;@vitejs/plugin-react&quot;;
import { resolve } from &quot;path&quot;;

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      &quot;@&quot;: resolve(&quot;src&quot;),
    },
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트디렉터리에 jsconfig.json 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- vite.config.js만 생성하도 컴파일 에러가 나지않지만, Eslint 설정이 빡세게 되어있거나, idel에서 에러가 날경우가 있어서 아래 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;jsconfig.json&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765796130880&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;compilerOptions&quot;: {
        &quot;baseUrl&quot;: &quot;.&quot;,
        &quot;paths&quot;: {
            &quot;@/*&quot;: [
                &quot;src/*&quot;
            ]
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSTbyE/dJMcachlNC9/XbgI0pL58aM5UIJQl7lsyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSTbyE/dJMcachlNC9/XbgI0pL58aM5UIJQl7lsyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSTbyE/dJMcachlNC9/XbgI0pL58aM5UIJQl7lsyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSTbyE%2FdJMcachlNC9%2FXbgI0pL58aM5UIJQl7lsyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;304&quot; height=&quot;273&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;273&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;components/Header.jsx&lt;/p&gt;
&lt;pre id=&quot;code_1765796247611&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Header() {
    return (
        &amp;lt;header&amp;gt;
            &amp;lt;h1&amp;gt;My App Header&amp;lt;/h1&amp;gt;
        &amp;lt;/header&amp;gt;
    );
}

export default Header;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.jsx&lt;/p&gt;
&lt;pre id=&quot;code_1765796261734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Header from &quot;@/components/Header&quot;;

function App() {
  return &amp;lt;Header /&amp;gt;;
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index.css, app.css는 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 시작&lt;/p&gt;
&lt;pre id=&quot;code_1765796305686&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm run dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH3rfw/dJMcahXhxEu/PkEakbdZj60PICYZqHanvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH3rfw/dJMcahXhxEu/PkEakbdZj60PICYZqHanvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH3rfw/dJMcahXhxEu/PkEakbdZj60PICYZqHanvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH3rfw%2FdJMcahXhxEu%2FPkEakbdZj60PICYZqHanvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;328&quot; height=&quot;165&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>frontend/React</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/762</guid>
      <comments>https://aamoos.tistory.com/762#entry762comment</comments>
      <pubDate>Mon, 15 Dec 2025 19:58:53 +0900</pubDate>
    </item>
    <item>
      <title>react 프로젝트 세팅</title>
      <link>https://aamoos.tistory.com/761</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;노드 설치 및 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vsCode를 실행한후 아래 명령어를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1765792597252&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;node -v
npm -v&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nEgn1/dJMcagcZTiq/0X94KiDr6RmUbJ5sBBTphK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nEgn1/dJMcagcZTiq/0X94KiDr6RmUbJ5sBBTphK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nEgn1/dJMcagcZTiq/0X94KiDr6RmUbJ5sBBTphK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnEgn1%2FdJMcagcZTiq%2F0X94KiDr6RmUbJ5sBBTphK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;328&quot; height=&quot;73&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 설치가 되어있지않으면 node js 설치를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nodejs.org/en/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nodejs.org/en/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1765792691503&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Node.js &amp;mdash; Run JavaScript Everywhere&quot; data-og-description=&quot;Node.js&amp;reg; is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&quot; data-og-host=&quot;nodejs.org&quot; data-og-source-url=&quot;https://nodejs.org/en/&quot; data-og-url=&quot;https://nodejs.org/en/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/7u0Pi/hyZPs9JzTm/7vMDQ52kk5etgd7CnDqGXk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bm7Pak/hyZPusXdhH/dWrN0Z4pSWEtAjT7Atr3bK/img.png?width=224&amp;amp;height=256&amp;amp;face=0_0_224_256&quot;&gt;&lt;a href=&quot;https://nodejs.org/en/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nodejs.org/en/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/7u0Pi/hyZPs9JzTm/7vMDQ52kk5etgd7CnDqGXk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bm7Pak/hyZPusXdhH/dWrN0Z4pSWEtAjT7Atr3bK/img.png?width=224&amp;amp;height=256&amp;amp;face=0_0_224_256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Node.js &amp;mdash; Run JavaScript Everywhere&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Node.js&amp;reg; is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nodejs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vsCode 터미널에서 프로젝트를 생성할 폴더로 이동후 아래 명령어를 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1765792776477&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm create vite@latest 프로젝트명&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK25hb/dJMcajgqyuj/d4AWODHtqbcAcNQNcEgrRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK25hb/dJMcajgqyuj/d4AWODHtqbcAcNQNcEgrRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK25hb/dJMcajgqyuj/d4AWODHtqbcAcNQNcEgrRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK25hb%2FdJMcajgqyuj%2Fd4AWODHtqbcAcNQNcEgrRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;182&quot; height=&quot;225&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- React 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bO2zcw/dJMcacPbHxm/ku6gWBd2ee8vEkYMsGorZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bO2zcw/dJMcacPbHxm/ku6gWBd2ee8vEkYMsGorZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bO2zcw/dJMcacPbHxm/ku6gWBd2ee8vEkYMsGorZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbO2zcw%2FdJMcacPbHxm%2Fku6gWBd2ee8vEkYMsGorZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;257&quot; height=&quot;232&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Typescript나 JavaScript 사용하고자 하는 언어 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;285&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd0xIn/dJMcadAvMRQ/6j2hy4bdothvf4CBQIkfI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd0xIn/dJMcadAvMRQ/6j2hy4bdothvf4CBQIkfI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd0xIn/dJMcadAvMRQ/6j2hy4bdothvf4CBQIkfI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd0xIn%2FdJMcadAvMRQ%2F6j2hy4bdothvf4CBQIkfI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;285&quot; height=&quot;75&quot; data-origin-width=&quot;285&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- no 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; rolldown-vite는 Rust로 만든 차세대 번들러임 , rollup을 대체하려는 목적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징은 훨씬 빠른 빌드속도를 가지고 있지만, 아직 실험단계임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;47&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRcUQ4/dJMcad1z4fV/PY2eYb7o0QXesRVN1OWADK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRcUQ4/dJMcad1z4fV/PY2eYb7o0QXesRVN1OWADK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRcUQ4/dJMcad1z4fV/PY2eYb7o0QXesRVN1OWADK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRcUQ4%2FdJMcad1z4fV%2FPY2eYb7o0QXesRVN1OWADK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;247&quot; height=&quot;47&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;47&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- yes 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs3y5g/dJMcaaqi1Wa/uRlOjgvyuMRIllqFZkX7ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs3y5g/dJMcaaqi1Wa/uRlOjgvyuMRIllqFZkX7ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs3y5g/dJMcaaqi1Wa/uRlOjgvyuMRIllqFZkX7ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs3y5g%2FdJMcaaqi1Wa%2FuRlOjgvyuMRIllqFZkX7ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;280&quot; height=&quot;219&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프로젝트안에 접속후 코드를 보고 싶으면 해당 경로로 이동후 아래 명령어를 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1765793130116&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd 프로젝트경로
code .&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;209&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPmrF6/dJMcabW26JG/rh78rJpGN5U4u6qWwkUfpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPmrF6/dJMcabW26JG/rh78rJpGN5U4u6qWwkUfpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPmrF6/dJMcabW26JG/rh78rJpGN5U4u6qWwkUfpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPmrF6%2FdJMcabW26JG%2Frh78rJpGN5U4u6qWwkUfpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;209&quot; height=&quot;251&quot; data-origin-width=&quot;209&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>frontend/React</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/761</guid>
      <comments>https://aamoos.tistory.com/761#entry761comment</comments>
      <pubDate>Mon, 15 Dec 2025 19:06:48 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 13. dotenv 적용해서 spring 프로퍼티 중요정보 .env 파일로 분리하기</title>
      <link>https://aamoos.tistory.com/757</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;spring 프로퍼티를 설정할때 중요정보를 보여주지 않고 따로 환경설정 파일로 분리하고 싶을때가 있을것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그때 dotenv를 적용해서 env파일로 분리하는 방법을 아래 내용에 작성하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle.kts&lt;/p&gt;
&lt;pre id=&quot;code_1739853946425&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//dotenv
implementation (&quot;io.github.cdimascio:java-dotenv:5.2.2&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;403&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lBdkh/btsMljNtTk2/Pe3p2Fa67Oc6FvQpxWQiyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lBdkh/btsMljNtTk2/Pe3p2Fa67Oc6FvQpxWQiyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lBdkh/btsMljNtTk2/Pe3p2Fa67Oc6FvQpxWQiyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlBdkh%2FbtsMljNtTk2%2FPe3p2Fa67Oc6FvQpxWQiyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;403&quot; height=&quot;427&quot; data-origin-width=&quot;403&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 디렉터리 경로에 .env 파일 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.env&lt;/p&gt;
&lt;pre id=&quot;code_1739854033740&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SPRING_PROFILES_ACTIVE=local
SECRET=jwt시크릿키&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml&lt;/p&gt;
&lt;pre id=&quot;code_1739854054083&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  config:
    import: optional:file:.env[.properties]
  profiles:
    active: ${SPRING_PROFILES_ACTIVE}  # 기본적으로 local 프로파일 사용 (운영에서는 prod로 변경)

jwt:
  expiration_time: 86400000 # 1일
  secret: ${secret}

logging:
  level:
    org.hibernate.SQL: DEBUG  # Hibernate 쿼리 로그 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config 아래 import 부분을 추가, .env에 설정한 key를 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739854110080&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management

import io.github.cdimascio.dotenv.Dotenv
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.data.jpa.repository.config.EnableJpaAuditing


@SpringBootApplication
@EnableJpaAuditing
class ManagementApplication

fun main(args: Array&amp;lt;String&amp;gt;) {
	runApplication&amp;lt;ManagementApplication&amp;gt;(*args)
	val dotenv = Dotenv.load()
	System.out.println(&quot;SPRING_PROFILES_ACTIVE: &quot; + dotenv.get(&quot;SPRING_PROFILES_ACTIVE&quot;));
	System.out.println(&quot;SECRET: &quot; + dotenv.get(&quot;SECRET&quot;));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test 코드를 만들어도 되지만 간단하게 테스트 하기위해서 applicaiton.kt에 해당 코드 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;43&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDsFt1/btsMnolYx0K/oN04MkcdCl7ruzsSshTYs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDsFt1/btsMnolYx0K/oN04MkcdCl7ruzsSshTYs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDsFt1/btsMnolYx0K/oN04MkcdCl7ruzsSshTYs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDsFt1%2FbtsMnolYx0K%2FoN04MkcdCl7ruzsSshTYs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;43&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;43&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.gitignore&lt;/p&gt;
&lt;pre id=&quot;code_1739854203334&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.env*&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 깃에 커밋시 해당 환경설정 파일이 commit되면 안되므로 맨 아래 라인에 .env*을 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git ignore에 .env*을 포함해도 .env파일이 git staging에 보일경우 안보이게 하는법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. .env 파일을 백업하고 프로젝트에 있는 .env 파일을 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 아래 명령어 intelij 터미널에 입력&lt;/p&gt;
&lt;pre id=&quot;code_1739854818234&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git rm .env --cached
git add .
git commit -m &quot;remove .env file from git&quot;
git push&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 이후 다시 루트 디렉터리에 백업한 .env파일을 생성한후 git - commit 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Nf4j/btsMlFJuqVi/Y14CFhMiJTxJ5y0sEKUFkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Nf4j/btsMlFJuqVi/Y14CFhMiJTxJ5y0sEKUFkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Nf4j/btsMlFJuqVi/Y14CFhMiJTxJ5y0sEKUFkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Nf4j%2FbtsMlFJuqVi%2FY14CFhMiJTxJ5y0sEKUFkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;401&quot; height=&quot;183&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;env 파일을 변경해도 커밋 changes에 안보이는것을 확인할수 있음&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/757</guid>
      <comments>https://aamoos.tistory.com/757#entry757comment</comments>
      <pubDate>Tue, 18 Feb 2025 14:01:42 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 12. Render 무료 호스팅서버에 Spring boot + Kotlin 배포하기</title>
      <link>https://aamoos.tistory.com/756</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;백엔드 무료서버가 몇가지있지만 그중 Render라는 무료 서버 호스팅 사이트에 배포하는 방법 관련 내용입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이전 포스팅한 간단한 프로젝트를 Render에 배포해보겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 docker hub 회원가입 및 도커가 설치 되어있다는 기준으로 진행하겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;intelij 프로젝트에서 gradle build 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVo9pI/btsMjCL1HOK/kiKiYuMjtAKIP6MWnKLErk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVo9pI/btsMjCL1HOK/kiKiYuMjtAKIP6MWnKLErk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVo9pI/btsMjCL1HOK/kiKiYuMjtAKIP6MWnKLErk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVo9pI%2FbtsMjCL1HOK%2FkiKiYuMjtAKIP6MWnKLErk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;843&quot; height=&quot;170&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DockerFile&lt;/p&gt;
&lt;pre id=&quot;code_1739515448666&quot; class=&quot;dockerfile&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;FROM azul/zulu-openjdk:17-latest
VOLUME /tmp
COPY build/libs/*.jar app.jar
ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;/app.jar&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIlCIz/btsMjBfg9vq/K0riv7ykEXEe3aCPjU7Ll0/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIlCIz/btsMjBfg9vq/K0riv7ykEXEe3aCPjU7Ll0/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIlCIz/btsMjBfg9vq/K0riv7ykEXEe3aCPjU7Ll0/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIlCIz%2FbtsMjBfg9vq%2FK0riv7ykEXEe3aCPjU7Ll0%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;404&quot; height=&quot;264&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;intelij 터미널에 아래 명령어 순서대로 입력&lt;/p&gt;
&lt;pre id=&quot;code_1739515467447&quot; class=&quot;properties&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;docker build -t 프로젝트명 .
docker images
docker tag 프로젝트명:latest         도커아이디명/프로젝트명
docker push 도커아이디명/프로젝트명&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker hub 사이트 접속&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hub.docker.com/&quot;&gt;https://hub.docker.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739515481701&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Docker Hub Container Image Library | App Containerization&quot; data-og-description=&quot;Increase your reach and adoption on Docker Hub With a Docker Verified Publisher subscription, you'll increase trust, boost discoverability, get exclusive data insights, and much more.&quot; data-og-host=&quot;hub.docker.com&quot; data-og-source-url=&quot;https://hub.docker.com/&quot; data-og-url=&quot;https://hub.docker.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kqSh0/hyYfI0mPnA/59Knn0B7Vkao5jkXHr0T8k/img.png?width=2560&amp;amp;height=1030&amp;amp;face=0_0_2560_1030,https://scrap.kakaocdn.net/dn/bBLTUz/hyYf5VzF5X/bYkuobQ9uCcqEOmTutvVv0/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://hub.docker.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hub.docker.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kqSh0/hyYfI0mPnA/59Knn0B7Vkao5jkXHr0T8k/img.png?width=2560&amp;amp;height=1030&amp;amp;face=0_0_2560_1030,https://scrap.kakaocdn.net/dn/bBLTUz/hyYf5VzF5X/bYkuobQ9uCcqEOmTutvVv0/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Docker Hub Container Image Library | App Containerization&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Increase your reach and adoption on Docker Hub With a Docker Verified Publisher subscription, you'll increase trust, boost discoverability, get exclusive data insights, and much more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hub.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1441&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UtFoc/btsMiIZ4Iqo/itsJwNGcHcSg1UoohBvrh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UtFoc/btsMiIZ4Iqo/itsJwNGcHcSg1UoohBvrh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UtFoc/btsMiIZ4Iqo/itsJwNGcHcSg1UoohBvrh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUtFoc%2FbtsMiIZ4Iqo%2FitsJwNGcHcSg1UoohBvrh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1441&quot; height=&quot;263&quot; data-origin-width=&quot;1441&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- docker hub사이트에 접속해보면 이미지가 push가 잘된것을 볼수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;render 사이트에 접속을 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dashboard.render.com/&quot;&gt;https://dashboard.render.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739515547662&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Cloud Application Hosting for Developers | Render&quot; data-og-description=&quot;Render is a unified cloud to build and run all your apps and websites with free SSL, global CDN, private networks and automatic deploys from Git.&quot; data-og-host=&quot;dashboard.render.com&quot; data-og-source-url=&quot;https://dashboard.render.com/&quot; data-og-url=&quot;https://dashboard.render.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/byOOnY/hyYfLiutUl/Xi3H7qyaC0ZQMRJdqZ1MD1/img.png?width=3840&amp;amp;height=2146&amp;amp;face=0_0_3840_2146,https://scrap.kakaocdn.net/dn/b36ZzX/hyYfDdGV9d/ktokRUf2A2NcH5FeKKKqyk/img.png?width=3840&amp;amp;height=2146&amp;amp;face=0_0_3840_2146&quot;&gt;&lt;a href=&quot;https://dashboard.render.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dashboard.render.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/byOOnY/hyYfLiutUl/Xi3H7qyaC0ZQMRJdqZ1MD1/img.png?width=3840&amp;amp;height=2146&amp;amp;face=0_0_3840_2146,https://scrap.kakaocdn.net/dn/b36ZzX/hyYfDdGV9d/ktokRUf2A2NcH5FeKKKqyk/img.png?width=3840&amp;amp;height=2146&amp;amp;face=0_0_3840_2146');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Cloud Application Hosting for Developers | Render&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Render is a unified cloud to build and run all your apps and websites with free SSL, global CDN, private networks and automatic deploys from Git.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dashboard.render.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZCf1U/btsMjhVw3Xs/SeijrMaSOHDLlFDc3Q9ISk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZCf1U/btsMjhVw3Xs/SeijrMaSOHDLlFDc3Q9ISk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZCf1U/btsMjhVw3Xs/SeijrMaSOHDLlFDc3Q9ISk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZCf1U%2FbtsMjhVw3Xs%2FSeijrMaSOHDLlFDc3Q9ISk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1486&quot; height=&quot;478&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Overview - Create New Service&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FsBXp/btsMhSJgowD/az8ujn2PPyQUrerCFuutRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FsBXp/btsMhSJgowD/az8ujn2PPyQUrerCFuutRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FsBXp/btsMhSJgowD/az8ujn2PPyQUrerCFuutRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFsBXp%2FbtsMhSJgowD%2Faz8ujn2PPyQUrerCFuutRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1426&quot; height=&quot;543&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;543&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web Service 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPm9xL/btsMjJqs5uf/PdDH585xmmdBCIcmIwV0B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPm9xL/btsMjJqs5uf/PdDH585xmmdBCIcmIwV0B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPm9xL/btsMjJqs5uf/PdDH585xmmdBCIcmIwV0B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPm9xL%2FbtsMjJqs5uf%2FPdDH585xmmdBCIcmIwV0B0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1444&quot; height=&quot;433&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker hub에 push한 image url을 입력후 Connect&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;727&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br8OF7/btsMh3DWvRT/KudEVDIgPcQY1KUU3brgbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br8OF7/btsMh3DWvRT/KudEVDIgPcQY1KUU3brgbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br8OF7/btsMh3DWvRT/KudEVDIgPcQY1KUU3brgbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr8OF7%2FbtsMh3DWvRT%2FKudEVDIgPcQY1KUU3brgbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1410&quot; height=&quot;727&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;727&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Region은 가장 가까운 Singapore로 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instance Type는 Free로 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1385&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNMXXz/btsMka8VPfB/uUI0riKrfHtd9vjIMemDX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNMXXz/btsMka8VPfB/uUI0riKrfHtd9vjIMemDX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNMXXz/btsMka8VPfB/uUI0riKrfHtd9vjIMemDX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNMXXz%2FbtsMka8VPfB%2FuUI0riKrfHtd9vjIMemDX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1385&quot; height=&quot;277&quot; data-origin-width=&quot;1385&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPRING_PROFILES_ACTIVE -&amp;gt; prod로 설정이후 Deploy Web Service&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 완료화면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;653&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NmFjs/btsMjDjO4dQ/QPC9zG9c8fFiZVKRILiyf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NmFjs/btsMjDjO4dQ/QPC9zG9c8fFiZVKRILiyf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NmFjs/btsMjDjO4dQ/QPC9zG9c8fFiZVKRILiyf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNmFjs%2FbtsMjDjO4dQ%2FQPC9zG9c8fFiZVKRILiyf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1350&quot; height=&quot;653&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;653&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 로그를 보면 application.yml prod를 바라보게 설정이 된것을 볼수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TW1pC/btsMiJSofyD/ooD7lpSAX29iKIKjcVc3x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TW1pC/btsMiJSofyD/ooD7lpSAX29iKIKjcVc3x1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TW1pC/btsMiJSofyD/ooD7lpSAX29iKIKjcVc3x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTW1pC%2FbtsMiJSofyD%2FooD7lpSAX29iKIKjcVc3x1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1065&quot; height=&quot;822&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포한 서버의 사용자 조회 api 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;525&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lmdr5/btsMjJqzh1k/PgZQO3prtY29tJb2Ju2WH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lmdr5/btsMjJqzh1k/PgZQO3prtY29tJb2Ju2WH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lmdr5/btsMjJqzh1k/PgZQO3prtY29tJb2Ju2WH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flmdr5%2FbtsMjJqzh1k%2FPgZQO3prtY29tJb2Ju2WH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;728&quot; height=&quot;525&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;525&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포한 서버의 신규 사용자 등록 api 호출&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/756</guid>
      <comments>https://aamoos.tistory.com/756#entry756comment</comments>
      <pubDate>Fri, 14 Feb 2025 16:26:12 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 11. Render 무료 호스팅서버 Postgres 데이터베이스 생성</title>
      <link>https://aamoos.tistory.com/755</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;render 사이트에 접속을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dashboard.render.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dashboard.render.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739513139360&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Cloud Application Hosting for Developers | Render&quot; data-og-description=&quot;Render is a unified cloud to build and run all your apps and websites with free SSL, global CDN, private networks and automatic deploys from Git.&quot; data-og-host=&quot;dashboard.render.com&quot; data-og-source-url=&quot;https://dashboard.render.com/&quot; data-og-url=&quot;https://dashboard.render.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/byOOnY/hyYfLiutUl/Xi3H7qyaC0ZQMRJdqZ1MD1/img.png?width=3840&amp;amp;height=2146&amp;amp;face=0_0_3840_2146,https://scrap.kakaocdn.net/dn/b36ZzX/hyYfDdGV9d/ktokRUf2A2NcH5FeKKKqyk/img.png?width=3840&amp;amp;height=2146&amp;amp;face=0_0_3840_2146&quot;&gt;&lt;a href=&quot;https://dashboard.render.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dashboard.render.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/byOOnY/hyYfLiutUl/Xi3H7qyaC0ZQMRJdqZ1MD1/img.png?width=3840&amp;amp;height=2146&amp;amp;face=0_0_3840_2146,https://scrap.kakaocdn.net/dn/b36ZzX/hyYfDdGV9d/ktokRUf2A2NcH5FeKKKqyk/img.png?width=3840&amp;amp;height=2146&amp;amp;face=0_0_3840_2146');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Cloud Application Hosting for Developers | Render&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Render is a unified cloud to build and run all your apps and websites with free SSL, global CDN, private networks and automatic deploys from Git.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dashboard.render.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 메인 - project에 들어가서 Create new proejct를 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;149&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kMEbu/btsMjf4rf0w/fYp5JZWuCGd9qsTEyq9hmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kMEbu/btsMjf4rf0w/fYp5JZWuCGd9qsTEyq9hmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kMEbu/btsMjf4rf0w/fYp5JZWuCGd9qsTEyq9hmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkMEbu%2FbtsMjf4rf0w%2FfYp5JZWuCGd9qsTEyq9hmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;149&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;149&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 프로젝트 이름 작성후&amp;nbsp; create&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PKfu0/btsMhTuAmtd/wR4JG0wpkoZs0KQh7GeXzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PKfu0/btsMhTuAmtd/wR4JG0wpkoZs0KQh7GeXzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PKfu0/btsMhTuAmtd/wR4JG0wpkoZs0KQh7GeXzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPKfu0%2FbtsMhTuAmtd%2FwR4JG0wpkoZs0KQh7GeXzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;574&quot; height=&quot;463&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;201&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDYCkp/btsMkdEu4zN/D7b25A1J9LT0rSbS1uKPM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDYCkp/btsMkdEu4zN/D7b25A1J9LT0rSbS1uKPM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDYCkp/btsMkdEu4zN/D7b25A1J9LT0rSbS1uKPM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDYCkp%2FbtsMkdEu4zN%2FD7b25A1J9LT0rSbS1uKPM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1426&quot; height=&quot;201&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;201&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Overview 클릭 - 오른쪽에 플러스 버튼 클릭 - Create New Service 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1453&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDaMWy/btsMisDd6bM/9Aq56C7cOEfZixjVMlDlR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDaMWy/btsMisDd6bM/9Aq56C7cOEfZixjVMlDlR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDaMWy/btsMisDd6bM/9Aq56C7cOEfZixjVMlDlR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDaMWy%2FbtsMisDd6bM%2F9Aq56C7cOEfZixjVMlDlR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1453&quot; height=&quot;544&quot; data-origin-width=&quot;1453&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. render는 database를 Postgres를 지원함, 하단에 Postgres 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Postgres를 설정하는 화면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OPkKY/btsMjOyjxHa/1MTIhBWt7YtoiH1jH2lvP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OPkKY/btsMjOyjxHa/1MTIhBWt7YtoiH1jH2lvP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OPkKY/btsMjOyjxHa/1MTIhBWt7YtoiH1jH2lvP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOPkKY%2FbtsMjOyjxHa%2F1MTIhBWt7YtoiH1jH2lvP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1454&quot; height=&quot;1130&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Name : postgres 프로젝트 이름 (render에서 사용하는거라 아무거나 적어도 무방)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Databse : 데이터베이스명&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;User: 사용자명&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Region: 지역 선택 (가장 가까운 지역이 싱가폴이라서 고름)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Plan Options: Free로 선택해야함, 그래야 돈 안나감&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. info에 들어가면 생성된 database 정보를 볼수 있음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1359&quot; data-origin-height=&quot;746&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UUJgp/btsMjFBCmMh/FwC1Qy9gMUQroEm40JJb41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UUJgp/btsMjFBCmMh/FwC1Qy9gMUQroEm40JJb41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UUJgp/btsMjFBCmMh/FwC1Qy9gMUQroEm40JJb41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUUJgp%2FbtsMjFBCmMh%2FFwC1Qy9gMUQroEm40JJb41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1359&quot; height=&quot;746&quot; data-origin-width=&quot;1359&quot; data-origin-height=&quot;746&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- External Database Url 옆에 눈모양 클릭후 jdbc url 복사&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.prod.yml&lt;/p&gt;
&lt;pre id=&quot;code_1739514772612&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://호스트명:5432/데이터베이스
    username: 사용자명
    password: 패스워드

  jpa:
    hibernate:
      ddl-auto: create
    show-sql: false
    properties:
      hibernate:
        format_sql: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 위에 복사한 url에서 필요한 정보들 yml에 값 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml&lt;/p&gt;
&lt;pre id=&quot;code_1739515090524&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  profiles:
    active: prod  # 기본적으로 local 프로파일 사용 (운영에서는 prod로 변경)

jwt:
  expiration_time: 86400000 # 1일
  secret: 시크릿키

logging:
  level:
    org.hibernate.SQL: DEBUG  # Hibernate 쿼리 로그 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prod로 설정해보고 application 시작&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;135&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YqRo1/btsMiHtsgcn/1Fk5ysvJQxpQlGRaeAhI80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YqRo1/btsMiHtsgcn/1Fk5ysvJQxpQlGRaeAhI80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YqRo1/btsMiHtsgcn/1Fk5ysvJQxpQlGRaeAhI80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYqRo1%2FbtsMiHtsgcn%2F1Fk5ysvJQxpQlGRaeAhI80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1350&quot; height=&quot;135&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;135&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 시작시 로그를 보면 h2가 아니라 postgres가 뜨는것을 볼수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1134&quot; data-origin-height=&quot;862&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wza5M/btsMjPYhhss/8uaK2IasiGfKCLVJ0gbK2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wza5M/btsMjPYhhss/8uaK2IasiGfKCLVJ0gbK2k/img.png&quot; data-alt=&quot;-&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wza5M/btsMjPYhhss/8uaK2IasiGfKCLVJ0gbK2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwza5M%2FbtsMjPYhhss%2F8uaK2IasiGfKCLVJ0gbK2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1134&quot; height=&quot;862&quot; data-origin-width=&quot;1134&quot; data-origin-height=&quot;862&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;-&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이전에 만들었던 사용자 등록 api 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckhEoX/btsMjL2T5DB/73r1B9o2PX2BrRC5iw6EC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckhEoX/btsMjL2T5DB/73r1B9o2PX2BrRC5iw6EC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckhEoX/btsMjL2T5DB/73r1B9o2PX2BrRC5iw6EC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckhEoX%2FbtsMjL2T5DB%2F73r1B9o2PX2BrRC5iw6EC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;728&quot; height=&quot;549&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이전에 만들었던 사용자 조회 api 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;render 무료호스팅으로 생성한 postgres가 정상적으로 연결된것을 확인할수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/755</guid>
      <comments>https://aamoos.tistory.com/755#entry755comment</comments>
      <pubDate>Fri, 14 Feb 2025 15:35:47 +0900</pubDate>
    </item>
    <item>
      <title>[코프링]  10. application.yml 운영, 로컬 분리하기, postgres 설정</title>
      <link>https://aamoos.tistory.com/754</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 application.yml이 하나였는데 로컬, 운영으로 분리해보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZEPQM/btsMjgCgnpu/AVaszm57cWRGiNXoGkUjBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZEPQM/btsMjgCgnpu/AVaszm57cWRGiNXoGkUjBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZEPQM/btsMjgCgnpu/AVaszm57cWRGiNXoGkUjBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZEPQM%2FbtsMjgCgnpu%2FAVaszm57cWRGiNXoGkUjBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;387&quot; height=&quot;236&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml&lt;/p&gt;
&lt;pre id=&quot;code_1739514080915&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  profiles:
    active: local  # 기본적으로 local 프로파일 사용 (운영에서는 prod로 변경)

jwt:
  expiration_time: 86400000 # 1일
  secret: jwt 시크릿키&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application-local.yml&lt;/p&gt;
&lt;pre id=&quot;code_1739514091552&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  h2:
    console:
      enabled: true
      path: /h2-console

  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:management
    username: sa
    password:

  jpa:
    hibernate:
      ddl-auto: create
    show-sql: true
    database-platform: org.hibernate.dialect.H2Dialect
    properties:
      hibernate:
        format_sql: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에서는 h2 데이터베이스를 바라보게 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application-prod.yml&lt;/p&gt;
&lt;pre id=&quot;code_1739514150082&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://호스트명:5432/데이터베이스명
    username: 사용자명
    password: 패스워드

  jpa:
    hibernate:
      ddl-auto: create
    show-sql: false
    database-platform: org.hibernate.dialect.PostgreSQLDialect
    properties:
      hibernate:
        format_sql: false

logging:
  level:
    org.hibernate.SQL: DEBUG  # Hibernate 쿼리 로그 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영에서는 postgres 데이터베이스를 바라보게 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle&lt;/p&gt;
&lt;pre id=&quot;code_1739514176609&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation(&quot;org.postgresql:postgresql&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;posgresql 의존성 추가&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/754</guid>
      <comments>https://aamoos.tistory.com/754#entry754comment</comments>
      <pubDate>Fri, 14 Feb 2025 15:23:10 +0900</pubDate>
    </item>
    <item>
      <title>jsch를 활용한 spring java에서 ssh command 명령어 실행하기</title>
      <link>https://aamoos.tistory.com/753</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 하다가 웹에서 버튼클릭시 서버에 설치된 파이썬 파일을 실행해야하는 일이 생겨서, 아래 관련 내용을 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jsch란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSCH는&amp;nbsp;Java에서&amp;nbsp;SSH&amp;nbsp;프로토콜을&amp;nbsp;지원하는&amp;nbsp;라이브러리로,&amp;nbsp;SFTP,&amp;nbsp;SCP,&amp;nbsp;포트&amp;nbsp;포워딩,&amp;nbsp;터널링&amp;nbsp;등의&amp;nbsp;기능을&amp;nbsp;제공합니다. &lt;br /&gt;Spring에서는&amp;nbsp;JSCH를&amp;nbsp;활용하여&amp;nbsp;원격&amp;nbsp;서버에&amp;nbsp;접속하고&amp;nbsp;파일을&amp;nbsp;업로드/다운로드하는&amp;nbsp;등의&amp;nbsp;작업을&amp;nbsp;수행할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pom.xml&lt;/p&gt;
&lt;pre id=&quot;code_1739506083147&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
	&amp;lt;groupId&amp;gt;com.jcraft&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;jsch&amp;lt;/artifactId&amp;gt;
	&amp;lt;version&amp;gt;0.1.55&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle&lt;/p&gt;
&lt;pre id=&quot;code_1739506134919&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation group: 'com.jcraft', name: 'jsch', version: '0.1.55'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller&lt;/p&gt;
&lt;pre id=&quot;code_1739506351480&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

...

@ResponseBody
@PostMapping(&quot;/sshCommand.json&quot;)

    public String sshCommand(@RequestBody Map&amp;lt;String, Object&amp;gt; params) {

        String host = &quot;ssh ip를 입력&quot;;  // SSH 서버의 호스트
        int port = port를 입력;
        String user = &quot;로그인 아이디 입력&quot;;     // SSH 로그인 사용자
        String password = &quot;로그인 패스워드 입력&quot;;  // SSH 로그인 비밀번호
        String command = &quot;df -h&quot;;  // ssh command에 입력할 명령어 입력

        Session session = null;
        ChannelExec channel = null;

        try {
            // JSch 객체 생성
            JSch jsch = new JSch();
            session = jsch.getSession(user, host, port);
            session.setPassword(password);

            // SSH 연결 시 호스트 키 확인을 건너뛰기
            session.setConfig(&quot;StrictHostKeyChecking&quot;, &quot;no&quot;);

            // 연결 타임아웃 설정
        	session.connect(10000);

       		//session.connect();



            // 명령어 실행을 위한 채널 열기
            channel = (ChannelExec) session.openChannel(&quot;exec&quot;);
            channel.setCommand(command);  // 쉘 스크립트 실행 명령 설정
            channel.setErrStream(System.err);

            // 명령어 실행
            InputStream inputStream = channel.getInputStream();
            channel.connect();

            // 실행 결과 읽기
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);  // 명령어 실행 결과 출력
            }
            channel.disconnect();
            session.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return &quot;ok&quot;;  // 명령어 실행 후 홈으로 리다이렉트

    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- command에는 ssh 명령어 입력창에 입력되는 명령어를 입력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739507294467&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//예제 home으로 이동한후 test.sh파일을 실행해라
cd /home &amp;amp;&amp;amp; ./test.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;session time out 현상이 있어서 아래 코드를 추가하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래코드는 session time out을 10초까지 기다린다는 의미입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739506464721&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;session.connect(10000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jsch 0.1.55 socket is not established error가 발생할경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- host, post, user, password를 다시 한번 확인해서 입력을 해야 합니다. 해당 정보가 맞지않거나 방화벽이 허용되지 않아, 웹 소켓 연결이 안될경우 발생하는 에러입니다.&lt;/p&gt;</description>
      <category>backend/스프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/753</guid>
      <comments>https://aamoos.tistory.com/753#entry753comment</comments>
      <pubDate>Fri, 14 Feb 2025 13:23:38 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 9. Spring Kotlin Jpa Querydsl Security Jwt 적용</title>
      <link>https://aamoos.tistory.com/752</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;jwt secret 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jwtsecret.com/generate&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jwtsecret.com/generate&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739171774099&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;JwtSecret.com - Generate JWT Secrets Online&quot; data-og-description=&quot;Full secret is hidden for security.&quot; data-og-host=&quot;jwtsecret.com&quot; data-og-source-url=&quot;https://jwtsecret.com/generate&quot; data-og-url=&quot;https://jwtsecret.com/generate&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://jwtsecret.com/generate&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jwtsecret.com/generate&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;JwtSecret.com - Generate JWT Secrets Online&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Full secret is hidden for security.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jwtsecret.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wimh3/btsMd4OQDcr/HRGoLc788C7aKpA2P7PTLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wimh3/btsMd4OQDcr/HRGoLc788C7aKpA2P7PTLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wimh3/btsMd4OQDcr/HRGoLc788C7aKpA2P7PTLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwimh3%2FbtsMd4OQDcr%2FHRGoLc788C7aKpA2P7PTLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;803&quot; height=&quot;440&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 위사이트 접속후 32자 클릭, Generate 선택후 복사&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml&lt;/p&gt;
&lt;pre id=&quot;code_1739171846435&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  # H2 Console 설정
  h2:
    console:
      enabled: true  # H2 Console을 사용할지 여부
      path: /h2-console  # H2 Console의 접근 경로

  # 데이터베이스 설정
  datasource:
    driver-class-name: org.h2.Driver  # H2 드라이버 사용
    url: jdbc:h2:mem:management  # 메모리 내 데이터베이스 (테스트용)
    username: sa  # 접속할 사용자명
    password:  # 비밀번호 (없으면 공백으로 설정)

  # JPA 설정
  jpa:
    hibernate:
      ddl-auto: create  # 테이블 자동 생성 및 업데이트 (설정에 따라 'none', 'update', 'create', 'create-drop' 등이 가능)
    show-sql: true  # SQL 쿼리를 로그에 출력
    database-platform: org.hibernate.dialect.H2Dialect  # H2 데이터베이스용 Hibernate Dialect 설정
    properties:
      hibernate:
        format_sql: true  # SQL을 보기 쉽게 포맷

jwt:
  expiration_time: 86400000 #1일
  secret: 위에 복사한 key를 여기에 넣으세요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jwt, expiration_time, secret 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에 추가, 변경된 파일들&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;129&quot; data-origin-height=&quot;32&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btU6yl/btsMcsXBVq1/XL3GA0GW6wm2Zt0XrBgp91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btU6yl/btsMcsXBVq1/XL3GA0GW6wm2Zt0XrBgp91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btU6yl/btsMcsXBVq1/XL3GA0GW6wm2Zt0XrBgp91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtU6yl%2FbtsMcsXBVq1%2FXL3GA0GW6wm2Zt0XrBgp91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;129&quot; height=&quot;32&quot; data-origin-width=&quot;129&quot; data-origin-height=&quot;32&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;136&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9fRnY/btsMdKQCdL6/9X9aBhV9rUHoFRxOlER6N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9fRnY/btsMdKQCdL6/9X9aBhV9rUHoFRxOlER6N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9fRnY/btsMdKQCdL6/9X9aBhV9rUHoFRxOlER6N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9fRnY%2FbtsMdKQCdL6%2F9X9aBhV9rUHoFRxOlER6N0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;136&quot; height=&quot;67&quot; data-origin-width=&quot;136&quot; data-origin-height=&quot;67&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;238&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crKWMT/btsMbXX3dHd/qllu6Kj3RTBK8q8JkyZiZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crKWMT/btsMbXX3dHd/qllu6Kj3RTBK8q8JkyZiZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crKWMT/btsMbXX3dHd/qllu6Kj3RTBK8q8JkyZiZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrKWMT%2FbtsMbXX3dHd%2Fqllu6Kj3RTBK8q8JkyZiZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;238&quot; height=&quot;119&quot; data-origin-width=&quot;238&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;219&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWz8W3/btsMcfK04RJ/6MaPm510hsMCHvvtHufMg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWz8W3/btsMcfK04RJ/6MaPm510hsMCHvvtHufMg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWz8W3/btsMcfK04RJ/6MaPm510hsMCHvvtHufMg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWz8W3%2FbtsMcfK04RJ%2F6MaPm510hsMCHvvtHufMg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;219&quot; height=&quot;121&quot; data-origin-width=&quot;219&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle.kts&lt;/p&gt;
&lt;pre id=&quot;code_1739171972603&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
	id(&quot;org.springframework.boot&quot;) version &quot;3.4.2&quot;
	id(&quot;io.spring.dependency-management&quot;) version &quot;1.1.7&quot;
	kotlin(&quot;jvm&quot;) version &quot;1.9.25&quot;
	kotlin(&quot;plugin.spring&quot;) version &quot;1.9.25&quot;
	kotlin(&quot;plugin.jpa&quot;) version &quot;1.9.25&quot;
	kotlin(&quot;kapt&quot;) version &quot;1.9.25&quot;
}

group = &quot;com.contact&quot;
version = &quot;0.0.1-SNAPSHOT&quot;

java {
	toolchain {
		languageVersion.set(JavaLanguageVersion.of(17))
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation(&quot;org.springframework.boot:spring-boot-starter-data-jpa&quot;)
	implementation(&quot;org.springframework.boot:spring-boot-starter-web&quot;)
	implementation(&quot;org.jetbrains.kotlin:kotlin-stdlib-jdk8&quot;)
	implementation(&quot;org.jetbrains.kotlin:kotlin-reflect&quot;)
	implementation(&quot;com.fasterxml.jackson.module:jackson-module-kotlin&quot;)
	implementation(&quot;org.springframework.boot:spring-boot-starter-validation&quot;)

	//jwt
	implementation(&quot;io.jsonwebtoken:jjwt-api:0.11.5&quot;)
	implementation(&quot;io.jsonwebtoken:jjwt-impl:0.11.5&quot;)
	implementation(&quot;io.jsonwebtoken:jjwt-jackson:0.11.5&quot;)

	// QueryDSL 의존성 추가
	implementation(&quot;com.querydsl:querydsl-jpa:5.1.0:jakarta&quot;)
	kapt(&quot;com.querydsl:querydsl-apt:5.1.0:jakarta&quot;)
	kapt(&quot;jakarta.annotation:jakarta.annotation-api&quot;)
	kapt(&quot;jakarta.persistence:jakarta.persistence-api&quot;)

	implementation (&quot;org.springframework.boot:spring-boot-starter-security&quot;)

	runtimeOnly(&quot;com.h2database:h2&quot;)
	testImplementation(&quot;org.springframework.boot:spring-boot-starter-test&quot;)
}

// Querydsl 설정부 추가 - start
val generated = file(&quot;src/main/generated&quot;)

// querydsl QClass 파일 생성 위치를 지정
tasks.withType&amp;lt;JavaCompile&amp;gt; {
	options.generatedSourceOutputDirectory.set(generated)
}

// kotlin source set 에 querydsl QClass 위치 추가
sourceSets {
	main {
		kotlin.srcDirs += generated
	}
}

// gradle clean 시에 QClass 디렉토리 삭제
tasks.named(&quot;clean&quot;) {
	doLast {
		generated.deleteRecursively()
	}
}
kapt {
	generateStubs = true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- jwt 부분 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 의존성의 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- jjwt-api (io.jsonwebtoken:jjwt-api:0.11.5)&lt;br /&gt;JWT를 생성, 서명, 검증할 수 있는 API를 제공하는 라이브러리&lt;br /&gt;예를&amp;nbsp;들어,&amp;nbsp;JWT를&amp;nbsp;생성할&amp;nbsp;때&amp;nbsp;Jwts.builder()&amp;nbsp;같은&amp;nbsp;기능을&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;해줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- jjwt-impl (io.jsonwebtoken:jjwt-impl:0.11.5) &lt;br /&gt;jjwt-api에서 제공하는 기능의 실제 구현체&lt;br /&gt;API만 추가하면 동작하지 않으므로 반드시 함께 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- jjwt-jackson (io.jsonwebtoken:jjwt-jackson:0.11.5)&lt;br /&gt;JSON 파싱을 위해 Jackson을 활용하는 구현체&lt;br /&gt;JWT의 Payload(내용) 을 JSON 형식으로 변환하거나, JSON을 객체로 변환할 때 필요&lt;br /&gt;Jackson을&amp;nbsp;사용하여&amp;nbsp;claims(클레임,&amp;nbsp;JWT의&amp;nbsp;데이터)&amp;nbsp;정보를&amp;nbsp;다룰&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;해줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172234703&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.entity

import com.contact.management.audit.BaseEntity
import com.contact.management.dto.SignUpRequest
import com.contact.management.dto.UserUpdateRequest
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table
import org.springframework.security.crypto.password.PasswordEncoder

@Entity
@Table(name = &quot;users&quot;)
class User (
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long?= null,
    var password: String,
    var name: String,
    var age: Int,
    var email: String,

    @Enumerated(EnumType.STRING)
    @Column(name = &quot;ROLE&quot;, nullable = false)
    var role: RoleType // Adding the role field

) : BaseEntity(){
    companion object {
        fun from(request: SignUpRequest, encoder: PasswordEncoder) = User(
            password = encoder.encode(request.password),
            name = request.name,
            age = request.age,
            email = request.email,
            role = RoleType.ROLE_USER
        )
    }

    fun update(request: UserUpdateRequest, encoder: PasswordEncoder){
        this.password = encoder.encode(request.password)
        this.name = request.name
        this.age = request.age
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용자 권한 role 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RoleType.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172575506&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.entity

enum class RoleType {
    ROLE_USER,  // 일반 사용자
    ROLE_ADMIN  // 관리자
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 권한은 간단하게 일반사용자, 관리자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JwtAccessDeniedHandler.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172309940&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.jwt

import jakarta.servlet.ServletException
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.web.access.AccessDeniedHandler
import org.springframework.stereotype.Component
import java.io.IOException

/**
 *  필요한 권한이 존재하지 않는 경우에 403 Forbidden 에러 return
 */
@Component
class JwtAccessDeniedHandler : AccessDeniedHandler {
    @Throws(IOException::class, ServletException::class)
    override fun handle(request: HttpServletRequest, response: HttpServletResponse, accessDeniedException: AccessDeniedException) {
        response.sendError(HttpServletResponse.SC_FORBIDDEN)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 코드는 Spring Security에서 JWT 기반 인증 및 권한 관리를 할 때, 사용자가 필요한 권한이 없을 경우 403 Forbidden 응답을 반환하는 역할을 하는 클래스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 사용자가 보호된 API 엔드포인트에 요청 &lt;br /&gt;2. Spring Security가 사용자의 JWT 토큰을 검증 &lt;br /&gt;3. 사용자가 필요한 권한이 없으면 JwtAccessDeniedHandler의 handle() 메서드가 호출됨 &lt;br /&gt;4. 클라이언트에게 403 Forbidden 응답 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JwtAuthenticationEntryPoint.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172327034&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.jwt

import jakarta.servlet.ServletException
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.core.AuthenticationException
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.stereotype.Component
import java.io.IOException

/**
 *  유효한 자격증명을 제공하지 않고 접근하려 할때 401 Unauthorized 에러 리턴
 */
@Component
class JwtAuthenticationEntryPoint : AuthenticationEntryPoint {
    @Throws(IOException::class, ServletException::class)
    override fun commence(request: HttpServletRequest, response: HttpServletResponse, authException: AuthenticationException) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JwtAuthenticationEntryPoint는 로그인하지 않은 사용자가 보호된 API에 접근하면 401 응답을 반환&lt;br /&gt;@Component를 사용하여 Spring Bean으로 등록되며, Security 설정에서 사용됩&lt;br /&gt;AuthenticationEntryPoint 인터페이스를 구현하여 Spring Security에서 인증되지 않은 요청을 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JwtAuthFilter.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172351508&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.jwt

import com.contact.management.security.CustomUserDetailsService
import jakarta.servlet.FilterChain
import jakarta.servlet.ServletException
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.web.filter.OncePerRequestFilter
import java.io.IOException

class JwtAuthFilter(
    private val customUserDetailsService: CustomUserDetailsService,
    private val jwtUtil: JwtUtil
) : OncePerRequestFilter() { // OncePerRequestFilter -&amp;gt; 한 번 실행 보장

    @Throws(ServletException::class, IOException::class)
    override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
        val authorizationHeader = request.getHeader(&quot;Authorization&quot;)

        // JWT가 헤더에 있는 경우
        if (authorizationHeader != null &amp;amp;&amp;amp; authorizationHeader.startsWith(&quot;Bearer &quot;)) {
            val token = authorizationHeader.substring(7)
            // JWT 유효성 검증
            if (jwtUtil.validateToken(token)) {
                val userId = jwtUtil.getUserId(token)

                // 유저와 토큰 일치 시 userDetails 생성
                val userDetails: UserDetails = customUserDetailsService.loadUserByUsername(userId.toString())

                val usernamePasswordAuthenticationToken = UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.authorities
                )
                SecurityContextHolder.getContext().authentication = usernamePasswordAuthenticationToken
            }
        }

        filterChain.doFilter(request, response) // 다음 필터로 넘기기
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. HTTP 요청의 &quot;Authorization&quot; 헤더에서 JWT를 추출. &lt;br /&gt;2. JWT가 &quot;Bearer &quot;로 시작하는지 확인. &lt;br /&gt;3. JWT의 유효성을 검사. &lt;br /&gt;4. 유효하면 userId를 추출하여 UserDetails 객체를 생성. &lt;br /&gt;5. UsernamePasswordAuthenticationToken을 생성하고 SecurityContextHolder에 저장. &lt;br /&gt;6. 인증이 완료된 요청으로 설정한 후, 다음 필터로 넘김.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT&amp;nbsp;인증을&amp;nbsp;처리하는&amp;nbsp;필터로,&amp;nbsp;요청을&amp;nbsp;가로채어&amp;nbsp;JWT를&amp;nbsp;검증하고&amp;nbsp;SecurityContext에&amp;nbsp;인증&amp;nbsp;정보를&amp;nbsp;저장하는&amp;nbsp;역할. &lt;br /&gt;JWT를&amp;nbsp;헤더에서&amp;nbsp;추출하고&amp;nbsp;유효성을&amp;nbsp;검사한&amp;nbsp;후,&amp;nbsp;해당&amp;nbsp;사용자&amp;nbsp;정보를&amp;nbsp;SecurityContextHolder에&amp;nbsp;저장하여&amp;nbsp;Spring&amp;nbsp;Security가&amp;nbsp;인증된&amp;nbsp;사용자로&amp;nbsp;인식하도록&amp;nbsp;설정. &lt;br /&gt;Spring&amp;nbsp;Security&amp;nbsp;설정에서&amp;nbsp;필터로&amp;nbsp;등록해야&amp;nbsp;동작.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JwtUtil.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172369160&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.jwt

import com.contact.management.exception.CommonException
import com.contact.management.exception.CommonExceptionCode
import com.contact.management.security.CustomUserDto
import io.jsonwebtoken.*
import io.jsonwebtoken.io.Decoders
import io.jsonwebtoken.security.Keys
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import java.security.Key
import java.time.ZonedDateTime
import java.util.*

/**
 * [JWT 관련 메서드를 제공하는 클래스]
 */
@Component
class JwtUtil(
    @Value(&quot;\${jwt.secret}&quot;) secretKey: String,
    @Value(&quot;\${jwt.expiration_time}&quot;) private val accessTokenExpTime: Long
) {
    private val log: Logger = LoggerFactory.getLogger(JwtUtil::class.java)
    private val key: Key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey))

    /**
     * Access Token 생성
     * @param user
     * @return Access Token String
     */
    fun createAccessToken(user: CustomUserDto): String {
        return createToken(user, accessTokenExpTime)
    }

    /**
     * JWT 생성
     * @param user
     * @param expireTime
     * @return JWT String
     */
    private fun createToken(user: CustomUserDto, expireTime: Long): String {
        val claims: Claims = Jwts.claims().apply {
            put(&quot;id&quot;, user.id)
            put(&quot;email&quot;, user.email)
            put(&quot;authorities&quot;, user.role)
        }

        val now = ZonedDateTime.now()
        val tokenValidity = now.plusSeconds(expireTime)

        return Jwts.builder()
            .setClaims(claims)
            .setIssuedAt(Date.from(now.toInstant()))
            .setExpiration(Date.from(tokenValidity.toInstant()))
            .signWith(key, SignatureAlgorithm.HS256)
            .compact()
    }

    /**
     * Token에서 User ID 추출
     * @param token
     * @return User ID
     */
    fun getUserId(token: String): Long {
        val id = parseClaims(token)[&quot;id&quot;, Integer::class.java]  // Read the claim as an Integer
        return id?.toLong() ?: throw CommonException(CommonExceptionCode.INVALID_AUTH_TOKEN) // Convert Integer to Long
    }

    /**
     * JWT 검증
     * @param token
     * @return IsValidate
     */
    fun validateToken(token: String): Boolean {
        return try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token)
            true
        } catch (e: io.jsonwebtoken.security.SecurityException) {
            log.info(&quot;Invalid JWT Token&quot;, e)
            false
        } catch (e: MalformedJwtException) {
            log.info(&quot;Invalid JWT Token&quot;, e)
            false
        } catch (e: ExpiredJwtException) {
            log.info(&quot;Expired JWT Token&quot;, e)
            false
        } catch (e: UnsupportedJwtException) {
            log.info(&quot;Unsupported JWT Token&quot;, e)
            false
        } catch (e: IllegalArgumentException) {
            log.info(&quot;JWT claims string is empty.&quot;, e)
            false
        }
    }

    /**
     * JWT Claims 추출
     * @param accessToken
     * @return JWT Claims
     */
    fun parseClaims(accessToken: String): Claims {
        return try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(accessToken).body
        } catch (e: ExpiredJwtException) {
            e.claims
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 JWT(JSON Web Token) 관련 유틸리티 클래스로,&lt;br /&gt;JWT&amp;nbsp;생성 &lt;br /&gt;JWT&amp;nbsp;검증 &lt;br /&gt;JWT에서&amp;nbsp;사용자&amp;nbsp;정보&amp;nbsp;추출 &lt;br /&gt;하는 기능을 제공&lt;br /&gt;Spring&amp;nbsp;Security&amp;nbsp;기반으로&amp;nbsp;JWT&amp;nbsp;인증을&amp;nbsp;처리하는&amp;nbsp;핵심&amp;nbsp;로직&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT 인증 흐름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가&amp;nbsp;로그인&amp;nbsp;&amp;rarr;&amp;nbsp;JwtUtil.createAccessToken(user)&amp;nbsp;실행&amp;nbsp;&amp;rarr;&amp;nbsp;JWT&amp;nbsp;생성&amp;nbsp;후&amp;nbsp;반환. &lt;br /&gt;클라이언트가&amp;nbsp;JWT를&amp;nbsp;Authorization&amp;nbsp;헤더에&amp;nbsp;담아&amp;nbsp;요청. &lt;br /&gt;JwtAuthFilter가&amp;nbsp;요청을&amp;nbsp;가로채&amp;nbsp;JwtUtil.validateToken(token)&amp;nbsp;실행&amp;nbsp;&amp;rarr;&amp;nbsp;JWT가&amp;nbsp;유효하면&amp;nbsp;SecurityContext에&amp;nbsp;인증&amp;nbsp;정보&amp;nbsp;저장. &lt;br /&gt;Spring&amp;nbsp;Security는&amp;nbsp;SecurityContext에서&amp;nbsp;인증된&amp;nbsp;사용자&amp;nbsp;정보를&amp;nbsp;가져와&amp;nbsp;요청을&amp;nbsp;처리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CustomUserDetails.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172405233&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.security

import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails

class CustomUserDetails(
    private val member: CustomUserDto // Change from CustomUserInfoDto to CustomUserDto
) : UserDetails {

    override fun getAuthorities(): Collection&amp;lt;GrantedAuthority&amp;gt; {
        // RoleType에 따라 권한 리스트를 생성
        val roles = listOf(member.role).map { role -&amp;gt; SimpleGrantedAuthority(role.name) }
        return roles
    }

    override fun getPassword(): String = member.password // Ensure the DTO has password if needed

    override fun getUsername(): String = member.id.toString() // or member.name if that's preferred

    override fun isAccountNonExpired(): Boolean = true

    override fun isAccountNonLocked(): Boolean = true

    override fun isCredentialsNonExpired(): Boolean = true

    override fun isEnabled(): Boolean = true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CustomUserDetails 클래스는 사용자 정보를 Spring Security에 맞는 형태로 변환하여 제공&lt;br /&gt;이를 통해 Spring Security의 인증(Authentication) 및 인가(Authorization) 절차가 정상적으로 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CustomUserDetailsService.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172426264&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.security

import com.contact.management.entity.User
import com.contact.management.repository.UserRepository
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional(readOnly = true)
class CustomUserDetailsService(
    private val userRepository: UserRepository
) : UserDetailsService {

    override fun loadUserByUsername(id: String): UserDetails {
        val user: User = userRepository.findById(id.toLong())
            .orElseThrow { UsernameNotFoundException(&quot;해당하는 유저가 없습니다.&quot;) }

        // entity -&amp;gt; dto 변환
        val dto = CustomUserDto.from(user)

        return CustomUserDetails(dto)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에서&amp;nbsp;사용자&amp;nbsp;조회&amp;nbsp;(UserRepository&amp;nbsp;이용) &lt;br /&gt;찾은&amp;nbsp;User&amp;nbsp;엔티티를&amp;nbsp;CustomUserDto로&amp;nbsp;변환 &lt;br /&gt;Spring&amp;nbsp;Security의&amp;nbsp;UserDetails&amp;nbsp;객체(CustomUserDetails)로&amp;nbsp;반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CustomUserDto.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172445880&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.security

//내부에서 사용하는 dto
import com.contact.management.entity.RoleType
import com.contact.management.entity.User

data class CustomUserDto(
    val id: Long?= null,
    var password: String,
    val name: String,
    val age: Int,
    val email: String,
    val role: RoleType
) {
    companion object {
        // Factory method for creating CustomUserDto from User entity
        fun from(user: User): CustomUserDto {
            return CustomUserDto(
                id = user.id,
                password = user.password,
                name = user.name,
                age = user.age,
                email = user.email,
                role = user.role
            )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SecurityConfig.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739172472301&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.security

import com.contact.management.jwt.JwtAccessDeniedHandler
import com.contact.management.jwt.JwtAuthFilter
import com.contact.management.jwt.JwtAuthenticationEntryPoint
import com.contact.management.jwt.JwtUtil
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.autoconfigure.security.servlet.PathRequest
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

@Configuration
@EnableWebSecurity
//@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)         //PreAuthorize 사용하기위한
class SecurityConfig(
    private val customUserDetailsService: CustomUserDetailsService,
    private val jwtUtil: JwtUtil,
    private val accessDeniedHandler: JwtAccessDeniedHandler,
    private val authenticationEntryPoint: JwtAuthenticationEntryPoint
) {

    companion object {
        private val AUTH_WHITELIST = arrayOf(
            &quot;/api/users/**&quot;, // Allows unrestricted access to this endpoint
        )
    }

    @Bean
    @ConditionalOnProperty(name = [&quot;spring.h2.console.enabled&quot;], havingValue = &quot;true&quot;)
    fun configureH2ConsoleEnable(): WebSecurityCustomizer {
        return WebSecurityCustomizer { web -&amp;gt; web.ignoring().requestMatchers(PathRequest.toH2Console()) }
    }

    @Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .csrf { it.disable() }
            .cors { }
            .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
            .authorizeHttpRequests {
                it
                    .requestMatchers(*AUTH_WHITELIST).permitAll()
                    .requestMatchers(&quot;/api/test/users/**&quot;).hasAuthority(&quot;ROLE_USER&quot;)   //USER 역할만 접근 허용
                    .requestMatchers(&quot;/api/test/admin/**&quot;).hasAuthority(&quot;ROLE_ADMIN&quot;)  // ADMIN 역할만 접근 허용
                    .anyRequest().authenticated()
            }
            .exceptionHandling {
                it.authenticationEntryPoint(authenticationEntryPoint)
                    .accessDeniedHandler(accessDeniedHandler)
            }
            .addFilterBefore(JwtAuthFilter(customUserDetailsService, jwtUtil), UsernamePasswordAuthenticationFilter::class.java)
            .formLogin { it.disable() }
            .httpBasic { it.disable() }
        return http.build()
    }

    @Bean
    fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT&amp;nbsp;기반&amp;nbsp;인증&amp;nbsp;적용&amp;nbsp;(JwtAuthFilter&amp;nbsp;추가) &lt;br /&gt;세션&amp;nbsp;사용&amp;nbsp;안&amp;nbsp;함&amp;nbsp;(STATELESS&amp;nbsp;설정) &lt;br /&gt;CSRF&amp;nbsp;및&amp;nbsp;기본&amp;nbsp;로그인&amp;nbsp;기능&amp;nbsp;비활성화 &lt;br /&gt;예외&amp;nbsp;처리&amp;nbsp;설정&amp;nbsp;(JwtAccessDeniedHandler,&amp;nbsp;JwtAuthenticationEntryPoint) &lt;br /&gt;H2&amp;nbsp;콘솔&amp;nbsp;보안&amp;nbsp;설정&amp;nbsp;(ConditionalOnProperty) &lt;br /&gt;비밀번호&amp;nbsp;암호화&amp;nbsp;(BCryptPasswordEncoder)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSRF 보호 비활성화 (csrf { it.disable() })&lt;br /&gt;JWT&amp;nbsp;기반&amp;nbsp;인증에서는&amp;nbsp;CSRF&amp;nbsp;토큰이&amp;nbsp;필요하지&amp;nbsp;않음. &lt;br /&gt;CORS 설정 (cors { })&lt;br /&gt;기본&amp;nbsp;CORS&amp;nbsp;설정&amp;nbsp;(필요시&amp;nbsp;CorsFilter&amp;nbsp;추가&amp;nbsp;가능). &lt;br /&gt;세션 미사용 (SessionCreationPolicy.STATELESS)&lt;br /&gt;JWT&amp;nbsp;기반&amp;nbsp;인증&amp;nbsp;방식에서는&amp;nbsp;세션을&amp;nbsp;사용하지&amp;nbsp;않음. &lt;br /&gt;기본 로그인 방식 비활성화&lt;br /&gt;formLogin&amp;nbsp;{&amp;nbsp;it.disable()&amp;nbsp;}&amp;nbsp;&amp;rarr;&amp;nbsp;기본&amp;nbsp;폼&amp;nbsp;로그인&amp;nbsp;X. &lt;br /&gt;httpBasic&amp;nbsp;{&amp;nbsp;it.disable()&amp;nbsp;}&amp;nbsp;&amp;rarr;&amp;nbsp;HTTP&amp;nbsp;Basic&amp;nbsp;인증&amp;nbsp;X.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security 인증흐름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 사용자 로그인 요청 &lt;br /&gt;/api/users/login&amp;nbsp;엔드포인트로&amp;nbsp;로그인&amp;nbsp;요청&amp;nbsp;(AUTH_WHITELIST에&amp;nbsp;포함됨). &lt;br /&gt;2. 로그인 성공 시 JWT 토큰 발급. &lt;br /&gt;요청 헤더에 JWT 포함 &lt;br /&gt;클라이언트는&amp;nbsp;이후&amp;nbsp;요청마다&amp;nbsp;Authorization:&amp;nbsp;Bearer&amp;nbsp;&amp;lt;JWT&amp;gt;&amp;nbsp;헤더를&amp;nbsp;포함하여&amp;nbsp;보냄. &lt;br /&gt;3. JwtAuthFilter에서 JWT 검증 &lt;br /&gt;JWT가&amp;nbsp;유효하면&amp;nbsp;SecurityContext에&amp;nbsp;사용자&amp;nbsp;정보&amp;nbsp;저장. &lt;br /&gt;4. 요청 인증 처리 &lt;br /&gt;SecurityFilterChain에서&amp;nbsp;설정한&amp;nbsp;대로&amp;nbsp;인증이&amp;nbsp;필요한&amp;nbsp;요청인지&amp;nbsp;확인&amp;nbsp;후&amp;nbsp;처리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserController.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739173400693&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.controller

import com.contact.management.dto.LoginRequest
import com.contact.management.dto.SignUpRequest
import com.contact.management.dto.UserResponse
import com.contact.management.dto.UserUpdateRequest
import com.contact.management.service.QuerydslUserService
import com.contact.management.service.UserService
import jakarta.validation.Valid
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.web.bind.annotation.*
import kotlin.math.log

@RestController
@RequestMapping(&quot;/api/users&quot;)
class UserController(
     private val userService: UserService
    ,private val querydslUserService: QuerydslUserService
) {

    @GetMapping
    fun getUsers(): ResponseEntity&amp;lt;List&amp;lt;UserResponse&amp;gt;&amp;gt; {
        val users = userService.getAllUsers()
        return ResponseEntity.ok(users)
    }

    @GetMapping(&quot;/{id}&quot;)
    fun getUser(@PathVariable id: Long): ResponseEntity&amp;lt;UserResponse&amp;gt; {
        return ResponseEntity.ok(userService.getUserById(id))
    }

    @PostMapping
    fun createUser(@Valid @RequestBody request: SignUpRequest): ResponseEntity&amp;lt;UserResponse&amp;gt; {
        val createdUser = userService.createdUser(request)
        return ResponseEntity
            .status(HttpStatus.CREATED) // 201 Created
            .header(HttpHeaders.LOCATION, &quot;/api/users/${createdUser.id}&quot;) // 생성된 리소스의 URI 반환
            .body(createdUser)
    }

    @PutMapping(&quot;/{id}&quot;)
    fun updateUser(@PathVariable id: Long,@Valid @RequestBody request: UserUpdateRequest): ResponseEntity&amp;lt;UserResponse&amp;gt; {
        val updatedUser = userService.updateUser(id, request)
        return ResponseEntity.ok(updatedUser)
    }

    @DeleteMapping(&quot;/{id}&quot;)
    fun deleteUser(@PathVariable id: Long): ResponseEntity&amp;lt;Void&amp;gt; {
        userService.deleteUser(id)
        return ResponseEntity.noContent().build() // 204 No Content
    }

    @GetMapping(&quot;/paged&quot;)
    fun getUsersWithPaging(pageable: Pageable, @RequestParam searchVal: String?): Page&amp;lt;UserResponse&amp;gt; {
        // 페이징된 사용자 목록 반환
        return querydslUserService.getUsersWithPaging(pageable, searchVal)
    }

    @PostMapping(&quot;/login&quot;)
    fun login(@Valid @RequestBody loginRequest: LoginRequest): ResponseEntity&amp;lt;String&amp;gt;{
        val token = userService.login(loginRequest)
        return ResponseEntity.ok(token)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/login 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserService.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739173421410&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.service

import com.contact.management.dto.LoginRequest
import com.contact.management.dto.SignUpRequest
import com.contact.management.dto.UserResponse
import com.contact.management.dto.UserUpdateRequest
import com.contact.management.entity.QUser
import com.contact.management.entity.User
import com.contact.management.exception.CommonException
import com.contact.management.exception.CommonExceptionCode.EMAIL_ALREADY_EXISTS
import com.contact.management.exception.CommonExceptionCode.USER_NOT_FOUND
import com.contact.management.jwt.JwtUtil
import com.contact.management.repository.UserRepository
import com.contact.management.security.CustomUserDto
import com.querydsl.jpa.impl.JPAQueryFactory
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class UserService(
     private val userRepository: UserRepository
    ,private val queryFactory: JPAQueryFactory
    ,private val encoder: PasswordEncoder
    ,private val jwtUtil: JwtUtil
) {

    @Transactional(readOnly = true)
    fun getAllUsers() : List&amp;lt;UserResponse&amp;gt; {
        return userRepository.findAll().map { UserResponse.from(it) }
    }

    @Transactional(readOnly = true)
    fun getUserById(id: Long): UserResponse {
        val user = userRepository.findById(id).orElseThrow{ CommonException(USER_NOT_FOUND) }
        return UserResponse.from(user)
    }

    fun createdUser(request: SignUpRequest): UserResponse {
        userRepository.findByEmail(request.email)?.let {
            throw CommonException(EMAIL_ALREADY_EXISTS)
        }

        val user = userRepository.save(User.from(request, encoder))
        return UserResponse.from(user)
    }

    // 사용자 업데이트
    fun updateUser(id: Long, request: UserUpdateRequest): UserResponse{
        val user = userRepository.findById(id).orElseThrow{ CommonException(USER_NOT_FOUND) }

        user.update(request, encoder)

        return UserResponse.from(user)
    }

    fun deleteUser(id: Long){
        if(!userRepository.existsById(id)){
            throw CommonException(USER_NOT_FOUND)
        }
        userRepository.deleteById(id)
    }

    @Transactional(readOnly = true)
    fun getUsersWithPaging(pageable: Pageable): Page&amp;lt;UserResponse&amp;gt; {
        val qUser = QUser.user

        // JPAQueryFactory를 사용하여 쿼리 작성
        val query = queryFactory
            .selectFrom(qUser)
            .orderBy(qUser.id.asc()) // ID 기준으로 오름차순 정렬 (필요에 따라 변경 가능)

        // 실제 데이터 조회
        val users = query
            .offset(pageable.offset) // 페이지의 시작 인덱스
            .limit(pageable.pageSize.toLong()) // 페이지 크기
            .fetch() // 결과 가져오기

        // 총 개수를 구하는 쿼리 작성 (countQuery)
        val countQuery = queryFactory
            .select(qUser.count()) // 총 개수 계산
            .from(qUser)
        val total = countQuery.fetchOne() ?: 0L // fetchOne()으로 단일 값 가져오기

        // Page 객체로 반환
        val userDtos = users.map { UserResponse.from(it) }
        return PageImpl(userDtos, pageable, total)
    }

    @Transactional(readOnly = true)
    fun login(request: LoginRequest): String {
        val email = request.email
        val password = request.password
        val user = userRepository.findByEmail(email) ?: throw CommonException(USER_NOT_FOUND)

        // 암호화된 password를 디코딩한 값과 입력한 패스워드 값이 다르면 예외를 던짐
        if (!encoder.matches(password, user.password)) {
            throw CommonException(USER_NOT_FOUND)
        }

        val info = CustomUserDto.from(user)
        return jwtUtil.createAccessToken(info)
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;login 메소드 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인시 해당 계정이 존재하지않는경우 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;USER_NOT_FOUND&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화된 패스워드를 디코딩한 값과 입력한 패스워드값이 다를경우 USER_NOT_FOUND ERROR&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통과시 jwt access token 생성 후 return&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 등록&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mGjom/btsMbFXyhSA/xOx75b9iZz54gCkWLcmAS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mGjom/btsMbFXyhSA/xOx75b9iZz54gCkWLcmAS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mGjom/btsMbFXyhSA/xOx75b9iZz54gCkWLcmAS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmGjom%2FbtsMbFXyhSA%2FxOx75b9iZz54gCkWLcmAS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;714&quot; height=&quot;506&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 로그인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsP9NH/btsMdlwPIdO/JHjZ6bKkQnXTwjTZFBeNPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsP9NH/btsMdlwPIdO/JHjZ6bKkQnXTwjTZFBeNPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsP9NH/btsMdlwPIdO/JHjZ6bKkQnXTwjTZFBeNPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsP9NH%2FbtsMdlwPIdO%2FJHjZ6bKkQnXTwjTZFBeNPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;708&quot; height=&quot;504&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;token값 return&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TestController.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739173735643&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.controller

import com.contact.management.dto.LoginRequest
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@RestController
class TestController {

    @GetMapping(&quot;/test&quot;)
    fun test(): String{
        return &quot;ok&quot;;
    }

    @PostMapping(&quot;/api/test/user&quot;)
//    @PreAuthorize(&quot;hasRole('ROLE_USER')&quot;)
    fun userTest(@RequestBody loginRequest: LoginRequest): LoginRequest {
        return loginRequest
    }

    @PostMapping(&quot;/api/test/admin&quot;)
//    @PreAuthorize(&quot;hasRole('ROLE_ADMIN')&quot;)
    fun adminTest(@RequestBody loginRequest: LoginRequest): LoginRequest {
        return loginRequest
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;users 계정으로 /api/test/user 호출&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tHc2W/btsMcYvPqu0/2sx7v1ZWMerMvq4GKwmow0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tHc2W/btsMcYvPqu0/2sx7v1ZWMerMvq4GKwmow0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tHc2W/btsMcYvPqu0/2sx7v1ZWMerMvq4GKwmow0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtHc2W%2FbtsMcYvPqu0%2F2sx7v1ZWMerMvq4GKwmow0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;727&quot; height=&quot;473&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user계정으로 /api/test/admin 호출&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kPFQW/btsMet2zBlc/y4fwJ8I4BLSDn6lvOyuuk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kPFQW/btsMet2zBlc/y4fwJ8I4BLSDn6lvOyuuk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kPFQW/btsMet2zBlc/y4fwJ8I4BLSDn6lvOyuuk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkPFQW%2FbtsMet2zBlc%2Fy4fwJ8I4BLSDn6lvOyuuk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;739&quot; height=&quot;445&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;445&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user 계정으로 admin api를 호출하였을때, 403이 나와야할것 같은데, 401이 나옴 원인 파악중..&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/752</guid>
      <comments>https://aamoos.tistory.com/752#entry752comment</comments>
      <pubDate>Tue, 11 Feb 2025 14:27:42 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 8. Companion Object - 팩토리 메서드 패턴 설정</title>
      <link>https://aamoos.tistory.com/751</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;팩토리&amp;nbsp;메서드&amp;nbsp;패턴이란? &lt;br /&gt;팩토리 메서드 패턴은 객체 생성을 캡슐화하여, 객체 생성 로직을 별도의 메서드로 분리하는 디자인 패턴 이를 통해 객체 생성 방식을 통제하고, 생성 과정이 변경되더라도 클라이언트 코드의 수정을 최소화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존방식 (팩토리 메서드 미적용)&lt;/p&gt;
&lt;pre id=&quot;code_1739153088363&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val user = User(
    password = encoder.encode(signUpRequest.password),
    name = signUpRequest.name,
    age = signUpRequest.age,
    email = signUpRequest.email
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존 방식의코드는 api 메소드 마다 user 값을 setting 해주려면 하나하나 전부 setting 해줘야하는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 방식 (팩토리 메서드 적용)&lt;/p&gt;
&lt;pre id=&quot;code_1739153128218&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val user = User.from(signUpRequest, encoder)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 팩토리 메서드를 적용하면 api 메서드 마다 .from을 호출하는식으로 캡슐화를 해서, 코드도 더 간결해 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 팩토리 메서드 역할&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;from 메서드는 SignUpRequest DTO와 PasswordEncoder를 받아, User 객체를 생성하는 역할&lt;br /&gt;비밀번호는 평문으로 저장하면 보안상 문제가 있기 때문에, encoder.encode(request.password)를 통해 암호화된 비밀번호를 저장하는 로직을 캡슐화&lt;br /&gt;이를 통해 User 객체를 직접 생성하지 않고, from 메서드를 통해 일관된 방식으로 안전하게 객체를 생성&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 팩토리&amp;nbsp;메서드&amp;nbsp;패턴의&amp;nbsp;장점&amp;nbsp;적용&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;객체&amp;nbsp;생성을&amp;nbsp;캡슐화&lt;br /&gt;User 객체의 생성 로직이 한 곳(from)에 모여 있어, 객체 생성을 단순화하고 코드 중복을 방지&lt;br /&gt;유지보수성&amp;nbsp;증가&lt;br /&gt;만약&amp;nbsp;User&amp;nbsp;생성&amp;nbsp;방식이&amp;nbsp;바뀌어도&amp;nbsp;from&amp;nbsp;메서드만&amp;nbsp;수정하면&amp;nbsp;되므로,&amp;nbsp;다른&amp;nbsp;코드에&amp;nbsp;영향을&amp;nbsp;주지&amp;nbsp;않습니다.&lt;br /&gt;의미&amp;nbsp;있는&amp;nbsp;생성&amp;nbsp;메서드&amp;nbsp;제공&lt;br /&gt;from(request, encoder)는 User 객체를 생성하는 의미를 직관적으로 전달&lt;br /&gt;생성자를 직접 호출하는 것보다 가독성이 좋아지고, 명확한 객체 생성 방법을 제공&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러면 이전에 작성한 코드에서 팩토리 메서드 패턴을 적용해 보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739151863801&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.entity

import com.contact.management.audit.BaseEntity
import com.contact.management.dto.SignUpRequest
import com.contact.management.dto.UserUpdateRequest
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table
import org.springframework.security.crypto.password.PasswordEncoder

@Entity
@Table(name = &quot;users&quot;)
class User (
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long?= null,
    var password: String,
    var name: String,
    var age: Int,
    var email: String
) : BaseEntity(){
    companion object {
        fun from(request: SignUpRequest, encoder: PasswordEncoder) = User(
            password = encoder.encode(request.password),
            name = request.name,
            age = request.age,
            email = request.email
        )
    }

    fun update(request: UserUpdateRequest, encoder: PasswordEncoder){
        this.password = encoder.encode(request.password)
        this.name = request.name
        this.age = request.age
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserController.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739152053369&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.controller

import com.contact.management.dto.SignUpRequest
import com.contact.management.dto.UserResponse
import com.contact.management.dto.UserUpdateRequest
import com.contact.management.service.QuerydslUserService
import com.contact.management.service.UserService
import jakarta.validation.Valid
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping(&quot;/api/users&quot;)
class UserController(
     private val userService: UserService
    ,private val querydslUserService: QuerydslUserService
) {

    @GetMapping
    fun getUsers(): ResponseEntity&amp;lt;List&amp;lt;UserResponse&amp;gt;&amp;gt; {
        val users = userService.getAllUsers()
        return ResponseEntity.ok(users)
    }

    @GetMapping(&quot;/{id}&quot;)
    fun getUser(@PathVariable id: Long): ResponseEntity&amp;lt;UserResponse&amp;gt; {
        return ResponseEntity.ok(userService.getUserById(id))
    }

    @PostMapping
    fun createUser(@Valid @RequestBody request: SignUpRequest): ResponseEntity&amp;lt;UserResponse&amp;gt; {
        val createdUser = userService.createdUser(request)
        return ResponseEntity
            .status(HttpStatus.CREATED) // 201 Created
            .header(HttpHeaders.LOCATION, &quot;/api/users/${createdUser.id}&quot;) // 생성된 리소스의 URI 반환
            .body(createdUser)
    }

    @PutMapping(&quot;/{id}&quot;)
    fun updateUser(@PathVariable id: Long,@Valid @RequestBody request: UserUpdateRequest): ResponseEntity&amp;lt;UserResponse&amp;gt; {
        val updatedUser = userService.updateUser(id, request)
        return ResponseEntity.ok(updatedUser)
    }

    @DeleteMapping(&quot;/{id}&quot;)
    fun deleteUser(@PathVariable id: Long): ResponseEntity&amp;lt;Void&amp;gt; {
        userService.deleteUser(id)
        return ResponseEntity.noContent().build() // 204 No Content
    }

    @GetMapping(&quot;/paged&quot;)
    fun getUsersWithPaging(pageable: Pageable, @RequestParam searchVal: String?): Page&amp;lt;UserResponse&amp;gt; {
        // 페이징된 사용자 목록 반환
        return querydslUserService.getUsersWithPaging(pageable, searchVal)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존에 dto를 UserDto 하나만 사용해서 실제 api를 만들때는 그렇게 하지 않기때문에 나눴습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserDto -&amp;gt; SignUpRequest, UserUpdateRequest&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SignUpRequest -&amp;gt; 사용자 등록할때 사용하는 요청 파라미터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserUpdateRequest -&amp;gt; 사용자 수정할때 사용하는 요청 파라미터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserResponse 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SignUpRequest.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739152190287&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.dto

import jakarta.validation.constraints.Email
import jakarta.validation.constraints.Max
import jakarta.validation.constraints.Min
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Pattern
import jakarta.validation.constraints.Size

data class SignUpRequest(
    val id: Long?= null,

    @field:Size(min = 8, message = &quot;비밀번호는 최소 8자 이상이어야 합니다.&quot;)
    @field:Pattern(
        regexp = &quot;^(?=.*[0-9])(?=.*[!@#\$%^&amp;amp;*()_+\\-=\\[\\]{};':\&quot;\\\\|,.&amp;lt;&amp;gt;/?]).{8,}\$&quot;,
        message = &quot;비밀번호는 최소 1개 이상의 숫자와 특수문자를 포함해야 합니다.&quot;
    )
    val password:String,

    @field:NotBlank(message = &quot;이름은 필수 입력 항목입니다.&quot;)
    @field:Size(min = 2, max = 50, message = &quot;이름은 2~50자 사이여야 합니다.&quot;)
    val name: String,

    @field:Min(value = 0, message = &quot;나이는 0 이상이어야 합니다.&quot;)
    @field:Max(value = 150, message = &quot;나이는 150 이하여야 합니다.&quot;)
    val age: Int,

    @field:NotBlank(message = &quot;이메일은 필수 입력 항목입니다.&quot;)
    @field:Email(message = &quot;올바른 이메일 형식을 입력하세요.&quot;)
    val email: String
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- validation을 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserUpdate.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739152202193&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.dto

import jakarta.validation.constraints.*

data class UserUpdateRequest(

    @field:Size(min = 8, message = &quot;비밀번호는 최소 8자 이상이어야 합니다.&quot;)
    @field:Pattern(
        regexp = &quot;^(?=.*[0-9])(?=.*[!@#\$%^&amp;amp;*()_+\\-=\\[\\]{};':\&quot;\\\\|,.&amp;lt;&amp;gt;/?]).{8,}\$&quot;,
        message = &quot;비밀번호는 최소 1개 이상의 숫자와 특수문자를 포함해야 합니다.&quot;
    )
    val password:String,

    @field:NotBlank(message = &quot;이름은 필수 입력 항목입니다.&quot;)
    @field:Size(min = 2, max = 50, message = &quot;이름은 2~50자 사이여야 합니다.&quot;)
    val name: String,

    @field:Min(value = 0, message = &quot;나이는 0 이상이어야 합니다.&quot;)
    @field:Max(value = 150, message = &quot;나이는 150 이하여야 합니다.&quot;)
    val age: Int,

    @field:NotBlank(message = &quot;이메일은 필수 입력 항목입니다.&quot;)
    @field:Email(message = &quot;올바른 이메일 형식을 입력하세요.&quot;)
    val email: String
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- validation을 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserResponse.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739152541846&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.dto

import com.contact.management.entity.User
import jakarta.validation.constraints.Email
import jakarta.validation.constraints.Size
import org.springframework.security.crypto.password.PasswordEncoder

data class UserResponse(
    val id: Long?= null,
    val name: String,
    val age: Int,
    val email: String
){
    companion object {
        fun from(user: User) = UserResponse(
            id = user.id,
            name = user.name,
            age = user.age,
            email = user.email
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- UserResponse 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserService.kt&lt;/p&gt;
&lt;pre id=&quot;code_1739152368336&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.service

import com.contact.management.dto.SignUpRequest
import com.contact.management.dto.UserResponse
import com.contact.management.dto.UserUpdateRequest
import com.contact.management.entity.QUser
import com.contact.management.entity.User
import com.contact.management.exception.CommonException
import com.contact.management.exception.CommonExceptionCode.EMAIL_ALREADY_EXISTS
import com.contact.management.exception.CommonExceptionCode.USER_NOT_FOUND
import com.contact.management.repository.UserRepository
import com.querydsl.jpa.impl.JPAQueryFactory
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class UserService(
     private val userRepository: UserRepository
    ,private val queryFactory: JPAQueryFactory
    ,private val encoder: PasswordEncoder
) {

    @Transactional(readOnly = true)
    fun getAllUsers() : List&amp;lt;UserResponse&amp;gt; {
        return userRepository.findAll().map { UserResponse.from(it) }
    }

    @Transactional(readOnly = true)
    fun getUserById(id: Long): UserResponse {
        val user = userRepository.findById(id).orElseThrow{ CommonException(USER_NOT_FOUND) }
        return UserResponse.from(user)
    }

    fun createdUser(request: SignUpRequest): UserResponse {
        userRepository.findByEmail(request.email)?.let {
            throw CommonException(EMAIL_ALREADY_EXISTS)
        }

        val user = userRepository.save(User.from(request, encoder))
        return UserResponse.from(user)
    }

    // 사용자 업데이트
    fun updateUser(id: Long, request: UserUpdateRequest): UserResponse{
        val user = userRepository.findById(id).orElseThrow{ CommonException(USER_NOT_FOUND) }

        user.update(request, encoder)

        return UserResponse.from(user)
    }

    fun deleteUser(id: Long){
        if(!userRepository.existsById(id)){
            throw CommonException(USER_NOT_FOUND)
        }
        userRepository.deleteById(id)
    }

    @Transactional(readOnly = true)
    fun getUsersWithPaging(pageable: Pageable): Page&amp;lt;UserResponse&amp;gt; {
        val qUser = QUser.user

        // JPAQueryFactory를 사용하여 쿼리 작성
        val query = queryFactory
            .selectFrom(qUser)
            .orderBy(qUser.id.asc()) // ID 기준으로 오름차순 정렬 (필요에 따라 변경 가능)

        // 실제 데이터 조회
        val users = query
            .offset(pageable.offset) // 페이지의 시작 인덱스
            .limit(pageable.pageSize.toLong()) // 페이지 크기
            .fetch() // 결과 가져오기

        // 총 개수를 구하는 쿼리 작성 (countQuery)
        val countQuery = queryFactory
            .select(qUser.count()) // 총 개수 계산
            .from(qUser)
        val total = countQuery.fetchOne() ?: 0L // fetchOne()으로 단일 값 가져오기

        // Page 객체로 반환
        val userDtos = users.map { UserResponse.from(it) }
        return PageImpl(userDtos, pageable, total)
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User.kt에서 작성한 from, update를 호출하게 service단에서 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;passwordEncoder는 아직 Bean으로 등록을 하지않아서, 다음 포스팅에 security 적용하면서 작성할 예정입니다.&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/751</guid>
      <comments>https://aamoos.tistory.com/751#entry751comment</comments>
      <pubDate>Mon, 10 Feb 2025 11:09:03 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] Kotlin Spring Boot Api에서 파라미터 not null, null 체크</title>
      <link>https://aamoos.tistory.com/750</link>
      <description>&lt;pre id=&quot;code_1739150989797&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.basic_kotlin_2025.controller.response

import com.example.basic_kotlin_2025.model.http.UserRequest
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping(&quot;/api/response&quot;)
class ResponseApiController {

    // 1. get 4xx
    // http://localhost:8080/api/response
    @GetMapping(&quot;/get-mapping&quot;)
    fun getMapping(@RequestParam age: Int?): ResponseEntity&amp;lt;String&amp;gt; {   //age 값이 없을수도 있다.

        // 1. age &amp;gt; 20 -&amp;gt; 400 error
        if(age == null){
            return ResponseEntity.status(400).body(&quot;age 값이 누락되었습니다.&quot;)
        }

        // 2. age &amp;lt; 20 -&amp;gt; 400 error
        if(age &amp;lt; 20){
            return ResponseEntity.status(400).body(&quot;age 값은 20보다 커야 합니다.&quot;)
        }
     
        //return ResponseEntity.ok(&quot;OK&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 만약 이러한 스타일의 api를 kotlin스럽게 코딩을 한다면 아래 코드 처럼 변경할수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739151074649&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.basic_kotlin_2025.controller.response

import com.example.basic_kotlin_2025.model.http.UserRequest
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping(&quot;/api/response&quot;)
class ResponseApiController {

    // 1. get 4xx
    // http://localhost:8080/api/response
    @GetMapping(&quot;/get-mapping&quot;)
    fun getMapping(@RequestParam age: Int?): ResponseEntity&amp;lt;String&amp;gt; {   //age 값이 없을수도 있다.

       return age?.let{

            if(age &amp;lt; 20){
                return ResponseEntity.status(400).body(&quot;age 값은 20보다 커야 합니다.&quot;)
            }

           ResponseEntity.ok(&quot;OK&quot;)
            // age not null
        }?: kotlin.run {
            //age is null
            return ResponseEntity.status(400).body(&quot;age 값이 누락되었습니다.&quot;)
        }
        
        //return ResponseEntity.ok(&quot;OK&quot;)
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739151133085&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return age?.let{

            if(age &amp;lt; 20){
                return ResponseEntity.status(400).body(&quot;age 값은 20보다 커야 합니다.&quot;)
            }

           ResponseEntity.ok(&quot;OK&quot;)
            // age not null
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 코드는 age값이 null이 아닐경우 타는 부분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739151163214&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;?: kotlin.run {
            //age is null
            return ResponseEntity.status(400).body(&quot;age 값이 누락되었습니다.&quot;)
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 코드는 age값이 null일 경우 타는 부분&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/750</guid>
      <comments>https://aamoos.tistory.com/750#entry750comment</comments>
      <pubDate>Mon, 10 Feb 2025 10:33:24 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 7. Spring Kotlin Jpa Querydsl BooleanExpression 동적쿼리 적용</title>
      <link>https://aamoos.tistory.com/749</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;UserController.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738916453570&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/paged&quot;)
fun getUsersWithPaging(pageable: Pageable, @RequestParam searchVal: String?): Page&amp;lt;UserDto&amp;gt; {
    // 페이징된 사용자 목록 반환
    return querydslUserService.getUsersWithPaging(pageable, searchVal)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 지난번에 추가한 controller 해당 api에 @RequestParam으로 검색어를 받는부분을 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QuerydslUserService.kt&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1738916517986&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.service

import com.contact.management.dto.UserDto
import com.contact.management.entity.QUser
import com.contact.management.repository.UserRepository
import com.querydsl.core.types.dsl.BooleanExpression
import com.querydsl.jpa.impl.JPAQueryFactory
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class QuerydslUserService(
    private val queryFactory: JPAQueryFactory
) {

    @Transactional(readOnly = true)
    fun getUsersWithPaging(pageable: Pageable, searchVal: String?): Page&amp;lt;UserDto&amp;gt; {
        val qUser = QUser.user
        var predicate: BooleanExpression = qUser.isNotNull

        searchVal?.let {
            predicate = predicate.and(qUser.name.containsIgnoreCase(searchVal))
        }

        // JPAQueryFactory를 사용하여 쿼리 작성
        val query = queryFactory
            .selectFrom(qUser)
            .where(predicate)
            .orderBy(qUser.id.asc()) // ID 기준으로 오름차순 정렬 (필요에 따라 변경 가능)

        // 실제 데이터 조회
        val users = query
            .offset(pageable.offset) // 페이지의 시작 인덱스
            .limit(pageable.pageSize.toLong()) // 페이지 크기
            .fetch() // 결과 가져오기

        // 총 개수를 구하는 쿼리 작성 (countQuery)
        val countQuery = queryFactory
            .select(qUser.count()) // 총 개수 계산
            .from(qUser)
            .where(predicate)
        val total = countQuery.fetchOne() ?: 0L // fetchOne()으로 단일 값 가져오기

        // Page 객체로 반환
        val userDtos = users.map { UserDto(it.id, it.name, it.email) }
        return PageImpl(userDtos, pageable, total)
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;searchVal 값을 null 허용을 하고 searchVal?.let을 선언해서 검색어가 &quot;&quot; 이거나 null일때는 통과, 그 외는 containsIgnoreCase를 사용해서 이름 like 검색을 하게 추가 하였음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;searchVal null인경우&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;1103&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AbgPS/btsMbDDTYfe/bHSks0e0WnKRL5wgO7k8L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AbgPS/btsMbDDTYfe/bHSks0e0WnKRL5wgO7k8L0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AbgPS/btsMbDDTYfe/bHSks0e0WnKRL5wgO7k8L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAbgPS%2FbtsMbDDTYfe%2FbHSks0e0WnKRL5wgO7k8L0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;741&quot; height=&quot;1103&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;1103&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 모든 항목이 나옴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;searchVal 빈값인 경우&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCMXwu/btsMcsoaxMT/QARVjMWpFU6EKcnNZa5nWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCMXwu/btsMcsoaxMT/QARVjMWpFU6EKcnNZa5nWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCMXwu/btsMcsoaxMT/QARVjMWpFU6EKcnNZa5nWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCMXwu%2FbtsMcsoaxMT%2FQARVjMWpFU6EKcnNZa5nWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;724&quot; height=&quot;1112&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 모든 항목이 나옴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;searchVal -&amp;gt; aa로 보낸경우&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;1113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKhz1K/btsMbaoDSNO/xnNLWIrIhubIAMIYuqO58K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKhz1K/btsMbaoDSNO/xnNLWIrIhubIAMIYuqO58K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKhz1K/btsMbaoDSNO/xnNLWIrIhubIAMIYuqO58K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKhz1K%2FbtsMbaoDSNO%2FxnNLWIrIhubIAMIYuqO58K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;746&quot; height=&quot;1113&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;1113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aa@naver.com 데이터가 조회됨&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/749</guid>
      <comments>https://aamoos.tistory.com/749#entry749comment</comments>
      <pubDate>Fri, 7 Feb 2025 17:28:14 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 6. Spring Kotlin Jpa Querydsl 적용, 페이징 예제</title>
      <link>https://aamoos.tistory.com/748</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738913842246&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
	id(&quot;org.springframework.boot&quot;) version &quot;3.4.2&quot;
	id(&quot;io.spring.dependency-management&quot;) version &quot;1.1.7&quot;
	kotlin(&quot;jvm&quot;) version &quot;1.9.25&quot;
	kotlin(&quot;plugin.spring&quot;) version &quot;1.9.25&quot;
	kotlin(&quot;plugin.jpa&quot;) version &quot;1.9.25&quot;
	kotlin(&quot;kapt&quot;) version &quot;1.9.25&quot;
}

group = &quot;com.contact&quot;
version = &quot;0.0.1-SNAPSHOT&quot;

java {
	toolchain {
		languageVersion.set(JavaLanguageVersion.of(17))
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation(&quot;org.springframework.boot:spring-boot-starter-data-jpa&quot;)
	implementation(&quot;org.springframework.boot:spring-boot-starter-web&quot;)
	implementation(&quot;org.jetbrains.kotlin:kotlin-stdlib-jdk8&quot;)
	implementation(&quot;org.jetbrains.kotlin:kotlin-reflect&quot;)
	implementation(&quot;com.fasterxml.jackson.module:jackson-module-kotlin&quot;)
	implementation(&quot;org.springframework.boot:spring-boot-starter-validation&quot;)

	// QueryDSL 의존성 추가
	implementation(&quot;com.querydsl:querydsl-jpa:5.1.0:jakarta&quot;)
	kapt(&quot;com.querydsl:querydsl-apt:5.1.0:jakarta&quot;)
	kapt(&quot;jakarta.annotation:jakarta.annotation-api&quot;)
	kapt(&quot;jakarta.persistence:jakarta.persistence-api&quot;)

	runtimeOnly(&quot;com.h2database:h2&quot;)
	testImplementation(&quot;org.springframework.boot:spring-boot-starter-test&quot;)
}

// Querydsl 설정부 추가 - start
val generated = file(&quot;src/main/generated&quot;)

// querydsl QClass 파일 생성 위치를 지정
tasks.withType&amp;lt;JavaCompile&amp;gt; {
	options.generatedSourceOutputDirectory.set(generated)
}

// kotlin source set 에 querydsl QClass 위치 추가
sourceSets {
	main {
		kotlin.srcDirs += generated
	}
}

// gradle clean 시에 QClass 디렉토리 삭제
tasks.named(&quot;clean&quot;) {
	doLast {
		generated.deleteRecursively()
	}
}
kapt {
	generateStubs = true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Query-dsl 종속성 추가&lt;/p&gt;
&lt;pre id=&quot;code_1738913985953&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation(&quot;com.querydsl:querydsl-jpa:5.1.0:jakarta&quot;)
kapt(&quot;com.querydsl:querydsl-apt:5.1.0:jakarta&quot;)
kapt(&quot;jakarta.annotation:jakarta.annotation-api&quot;)
kapt(&quot;jakarta.persistence:jakarta.persistence-api&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;com.querydsl:querydsl-jpa: 이는 QueryDSL JPA 모듈로, QueryDSL을 사용하여 유형이 안전한 쿼리를 작성하기 위한 JPA 관련 기능을 제공&lt;br /&gt;com.querydsl:querydsl-apt: 이 주석 프로세서는 빌드 프로세스 중에 QueryDSL Q-클래스(즉, JPA 엔터티를 나타내는 클래스)를 생성&lt;br /&gt;jakarta.annotation-api&amp;amp;jakarta.persistence-api : JPA 엔터티를 처리하는 데 사용되는 Jakarta 지속성과 관련된 필수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Q-class 생성&lt;/p&gt;
&lt;pre id=&quot;code_1738914083919&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val generated = file(&quot;src/main/generated&quot;)

tasks.withType&amp;lt;JavaCompile&amp;gt; {
	options.generatedSourceOutputDirectory.set(generated)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 Q-클래스에 대한 디렉토리 설정 : 생성된 QueryDSL Q-클래스가 저장될 위치를 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 생성된 소스를 Kotlin 소스 세트에 추가&lt;/p&gt;
&lt;pre id=&quot;code_1738914165869&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sourceSets {
	main {
		kotlin.srcDirs += generated
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 생성된 QueryDSL Q-클래스가 Kotlin 소스 세트에 추가되어 프로젝트의 나머지 부분에서 사용 generatedKotlin이 코드를 컴파일할 때 고려하는 디렉토리 목록에 디렉토리를 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Gradle에서 생성된 파일 정리 Clean:&lt;/p&gt;
&lt;pre id=&quot;code_1738914191122&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tasks.named(&quot;clean&quot;) {
	doLast {
		generated.deleteRecursively()
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradle clean시 Qclass 디렉토리를 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;querydsl 패키지 생성후 QuerydslConfog.kt 생성&lt;/p&gt;
&lt;pre id=&quot;code_1738914364135&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.querydsl

import com.querydsl.jpa.impl.JPAQueryFactory
import jakarta.persistence.EntityManager
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class QueryDslConfig(
    val em: EntityManager
) {

    @Bean
    fun queryFactory(): JPAQueryFactory {
        return JPAQueryFactory(em)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;service 패키지에 QuerydslUserSerivce.kt 생성&lt;/p&gt;
&lt;pre id=&quot;code_1738914461097&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.service

import com.contact.management.dto.UserDto
import com.contact.management.entity.QUser
import com.contact.management.repository.UserRepository
import com.querydsl.jpa.impl.JPAQueryFactory
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class QuerydslUserService(
    private val queryFactory: JPAQueryFactory
) {

    @Transactional(readOnly = true)
    fun getUsersWithPaging(pageable: Pageable): Page&amp;lt;UserDto&amp;gt; {
        val qUser = QUser.user

        // JPAQueryFactory를 사용하여 쿼리 작성
        val query = queryFactory
            .selectFrom(qUser)
            .orderBy(qUser.id.asc()) // ID 기준으로 오름차순 정렬 (필요에 따라 변경 가능)

        // 실제 데이터 조회
        val users = query
            .offset(pageable.offset) // 페이지의 시작 인덱스
            .limit(pageable.pageSize.toLong()) // 페이지 크기
            .fetch() // 결과 가져오기

        // 총 개수를 구하는 쿼리 작성 (countQuery)
        val countQuery = queryFactory
            .select(qUser.count()) // 총 개수 계산
            .from(qUser)
        val total = countQuery.fetchOne() ?: 0L // fetchOne()으로 단일 값 가져오기

        // Page 객체로 반환
        val userDtos = users.map { UserDto(it.id, it.name, it.email) }
        return PageImpl(userDtos, pageable, total)
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserController.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738914484946&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.controller

import com.contact.management.dto.UserDto
import com.contact.management.service.QuerydslUserService
import com.contact.management.service.UserService
import jakarta.validation.Valid
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping(&quot;/api/users&quot;)
class UserController(
     private val userService: UserService
    ,private val querydslUserService: QuerydslUserService
) {

    @GetMapping
    fun getUsers(): ResponseEntity&amp;lt;List&amp;lt;UserDto&amp;gt;&amp;gt; {
        val users = userService.getAllUsers()
        return ResponseEntity.ok(users)
    }

    @GetMapping(&quot;/{id}&quot;)
    fun getUser(@PathVariable id: Long): ResponseEntity&amp;lt;UserDto&amp;gt; {
        return ResponseEntity.ok(userService.getUserById(id))
    }

    @PostMapping
    fun createUser(@Valid @RequestBody userDto: UserDto): ResponseEntity&amp;lt;UserDto&amp;gt; {
        val createdUser = userService.createdUser(userDto)
        return ResponseEntity
            .status(HttpStatus.CREATED) // 201 Created
            .header(HttpHeaders.LOCATION, &quot;/api/users/${createdUser.id}&quot;) // 생성된 리소스의 URI 반환
            .body(createdUser)
    }

    @PutMapping(&quot;/{id}&quot;)
    fun updateUser(@PathVariable id: Long,@Valid @RequestBody userDto: UserDto): ResponseEntity&amp;lt;UserDto&amp;gt; {
        val updatedUser = userService.updateUser(id, userDto)
        return ResponseEntity.ok(updatedUser)
    }

    @DeleteMapping(&quot;/{id}&quot;)
    fun deleteUser(@PathVariable id: Long): ResponseEntity&amp;lt;Void&amp;gt; {
        userService.deleteUser(id)
        return ResponseEntity.noContent().build() // 204 No Content
    }

    @GetMapping(&quot;/paged&quot;)
    fun getUsersWithPaging(pageable: Pageable): Page&amp;lt;UserDto&amp;gt; {
        // 페이징된 사용자 목록 반환
        return querydslUserService.getUsersWithPaging(pageable)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getUsersWithPaging 메소드 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 미리 넣어보고 /api/users/paged 호출하면 아래형식으로 return됨&lt;/p&gt;
&lt;pre id=&quot;code_1738914599856&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;content&quot;: [
        {
            &quot;id&quot;: 1,
            &quot;name&quot;: &quot;aa&quot;,
            &quot;email&quot;: &quot;aa@domain.com&quot;
        },
        {
            &quot;id&quot;: 2,
            &quot;name&quot;: &quot;bb&quot;,
            &quot;email&quot;: &quot;bb@domain.com&quot;
        },
        {
            &quot;id&quot;: 3,
            &quot;name&quot;: &quot;cc&quot;,
            &quot;email&quot;: &quot;cc@domain.com&quot;
        },
        {
            &quot;id&quot;: 4,
            &quot;name&quot;: &quot;dd&quot;,
            &quot;email&quot;: &quot;dd@domain.com&quot;
        },
        {
            &quot;id&quot;: 5,
            &quot;name&quot;: &quot;ee&quot;,
            &quot;email&quot;: &quot;ee@domain.com&quot;
        },
        {
            &quot;id&quot;: 6,
            &quot;name&quot;: &quot;ff&quot;,
            &quot;email&quot;: &quot;ff@domain.com&quot;
        },
        {
            &quot;id&quot;: 7,
            &quot;name&quot;: &quot;gg&quot;,
            &quot;email&quot;: &quot;gg@domain.com&quot;
        },
        {
            &quot;id&quot;: 8,
            &quot;name&quot;: &quot;hh&quot;,
            &quot;email&quot;: &quot;hh@domain.com&quot;
        },
        {
            &quot;id&quot;: 9,
            &quot;name&quot;: &quot;ii&quot;,
            &quot;email&quot;: &quot;ii@domain.com&quot;
        },
        {
            &quot;id&quot;: 10,
            &quot;name&quot;: &quot;jj&quot;,
            &quot;email&quot;: &quot;jj@domain.com&quot;
        },
        {
            &quot;id&quot;: 11,
            &quot;name&quot;: &quot;kk&quot;,
            &quot;email&quot;: &quot;kk@domain.com&quot;
        },
        {
            &quot;id&quot;: 12,
            &quot;name&quot;: &quot;ll&quot;,
            &quot;email&quot;: &quot;ll@domain.com&quot;
        },
        {
            &quot;id&quot;: 13,
            &quot;name&quot;: &quot;mm&quot;,
            &quot;email&quot;: &quot;mm@domain.com&quot;
        },
        {
            &quot;id&quot;: 14,
            &quot;name&quot;: &quot;nn&quot;,
            &quot;email&quot;: &quot;nn@domain.com&quot;
        },
        {
            &quot;id&quot;: 15,
            &quot;name&quot;: &quot;oo&quot;,
            &quot;email&quot;: &quot;oo@domain.com&quot;
        },
        {
            &quot;id&quot;: 16,
            &quot;name&quot;: &quot;pp&quot;,
            &quot;email&quot;: &quot;pp@domain.com&quot;
        },
        {
            &quot;id&quot;: 17,
            &quot;name&quot;: &quot;qq&quot;,
            &quot;email&quot;: &quot;qq@domain.com&quot;
        },
        {
            &quot;id&quot;: 18,
            &quot;name&quot;: &quot;rr&quot;,
            &quot;email&quot;: &quot;rr@domain.com&quot;
        },
        {
            &quot;id&quot;: 19,
            &quot;name&quot;: &quot;ss&quot;,
            &quot;email&quot;: &quot;ss@domain.com&quot;
        },
        {
            &quot;id&quot;: 20,
            &quot;name&quot;: &quot;tt&quot;,
            &quot;email&quot;: &quot;tt@domain.com&quot;
        }
    ],
    &quot;pageable&quot;: {
        &quot;pageNumber&quot;: 0,
        &quot;pageSize&quot;: 20,
        &quot;sort&quot;: {
            &quot;empty&quot;: true,
            &quot;sorted&quot;: false,
            &quot;unsorted&quot;: true
        },
        &quot;offset&quot;: 0,
        &quot;paged&quot;: true,
        &quot;unpaged&quot;: false
    },
    &quot;last&quot;: true,
    &quot;totalElements&quot;: 20,
    &quot;totalPages&quot;: 1,
    &quot;first&quot;: true,
    &quot;size&quot;: 20,
    &quot;number&quot;: 0,
    &quot;sort&quot;: {
        &quot;empty&quot;: true,
        &quot;sorted&quot;: false,
        &quot;unsorted&quot;: true
    },
    &quot;numberOfElements&quot;: 20,
    &quot;empty&quot;: false
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/748</guid>
      <comments>https://aamoos.tistory.com/748#entry748comment</comments>
      <pubDate>Fri, 7 Feb 2025 16:51:10 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 5. Spring Jpa Auditing 적용</title>
      <link>https://aamoos.tistory.com/747</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;audit란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔터티(Entity)의 생성 및 변경 이력을 자동으로 감지. 이를 통해 데이터 변경 내역을 자동으로 기록할 수 있고, 수동으로 createdAt, updatedAt 등을 설정할 필요가 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제&lt;/p&gt;
&lt;pre id=&quot;code_1738909499360&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.entity

import jakarta.persistence.*
import org.springframework.data.annotation.CreatedBy
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedBy
import org.springframework.data.annotation.LastModifiedDate
import java.time.LocalDateTime

@Entity
@Table(name = &quot;users&quot;)
class User2 (
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long?= null,
    var name: String,
    var email: String,
    @CreatedDate
    @Column(updatable = false)
    var createdAt: LocalDateTime,

    @LastModifiedDate
    var updatedAt: LocalDateTime,
    
    @CreatedBy
    @Column(updatable = false)
    var createdBy: String,
    
    @LastModifiedBy
    var updatedBy: String
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; entity를 생성할때 등록자, 수정자, 등록일, 수정일을 추가하고싶을때 수많은 entity에 동일한 코드가 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;audit를 적용하면 중복된 코드들을 하나의 entity로 관리를 할수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;audit 적용 예제&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;172&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btInbE/btsL9Oz7N9E/oP95bWzo40qUfmIkHqwmd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btInbE/btsL9Oz7N9E/oP95bWzo40qUfmIkHqwmd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btInbE/btsL9Oz7N9E/oP95bWzo40qUfmIkHqwmd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtInbE%2FbtsL9Oz7N9E%2FoP95bWzo40qUfmIkHqwmd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;172&quot; height=&quot;72&quot; data-origin-width=&quot;172&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;audit 패키지를 생성후 아래 .kt 파일들을 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AuditorAwareConfig.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738909665642&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.audit

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.domain.AuditorAware
import java.util.Optional

@Configuration
class AuditorAwareConfig {
    @Bean
    fun auditorProvider(): AuditorAware&amp;lt;String&amp;gt; {
        return AuditorAware { Optional.of(&quot;system&quot;) } // 실제 환경에서는 SecurityContext에서 가져오기
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 아직 spring security를 적용하기 전이므로 system이라는 값을 하드코딩 하였음, security를 적용한 이후에는 로그인한 사용자의 아이디를 가져오게 변경해야함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BaseEntity.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738909723913&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.audit

import jakarta.persistence.Column
import jakarta.persistence.EntityListeners
import jakarta.persistence.MappedSuperclass
import org.springframework.data.annotation.CreatedBy
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedBy
import org.springframework.data.annotation.LastModifiedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.time.LocalDateTime

@EntityListeners(AuditingEntityListener::class)
@MappedSuperclass
abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    lateinit var createdAt: LocalDateTime

    @LastModifiedDate
    lateinit var updatedAt: LocalDateTime

    @CreatedBy
    @Column(updatable = false)
    lateinit var createdBy: String

    @LastModifiedBy
    lateinit var updatedBy: String
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738909750180&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.data.jpa.repository.config.EnableJpaAuditing

@SpringBootApplication
@EnableJpaAuditing
class ManagementApplication

fun main(args: Array&amp;lt;String&amp;gt;) {
	runApplication&amp;lt;ManagementApplication&amp;gt;(*args)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 프로젝트를 생성할때 만들어지는 Application.kt 파일에 @EnableJpaAuditing을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738909791933&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.entity

import com.contact.management.audit.BaseEntity
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table

@Entity
@Table(name = &quot;users&quot;)
class User (
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long?= null,
    var name: String,
    var email: String
) : BaseEntity()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;audit를 적용하고자 하는 entity 뒤에 : BaseEntity()를 추가해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YgETp/btsMbZfngNt/rndG6lbiPnU8yKPo97xO2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YgETp/btsMbZfngNt/rndG6lbiPnU8yKPo97xO2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YgETp/btsMbZfngNt/rndG6lbiPnU8yKPo97xO2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYgETp%2FbtsMbZfngNt%2FrndG6lbiPnU8yKPo97xO2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;436&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 이전에 만든 사용자 등록 api를 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;295&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IcXCb/btsMakyxCCe/NcYjSbz1lbgvhdGrPsJox1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IcXCb/btsMakyxCCe/NcYjSbz1lbgvhdGrPsJox1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IcXCb/btsMakyxCCe/NcYjSbz1lbgvhdGrPsJox1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIcXCb%2FbtsMakyxCCe%2FNcYjSbz1lbgvhdGrPsJox1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;722&quot; height=&quot;295&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;295&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;USERS 테이블을 조회하면 날짜와 하드 코딩한 system이 등록자, 수정자에 insert가 된것을 확인할수 있음&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/747</guid>
      <comments>https://aamoos.tistory.com/747#entry747comment</comments>
      <pubDate>Fri, 7 Feb 2025 15:34:14 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 4. @Valid를 사용한 REST API validation 처리</title>
      <link>https://aamoos.tistory.com/746</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;의존성 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle dependencies 안에 아래 코드 추가&lt;/p&gt;
&lt;pre id=&quot;code_1738833586686&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation(&quot;org.springframework.boot:spring-boot-starter-validation&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738833601715&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
	id(&quot;org.springframework.boot&quot;) version &quot;3.4.2&quot;
	id(&quot;io.spring.dependency-management&quot;) version &quot;1.1.7&quot;
	id(&quot;java&quot;)
	id(&quot;org.jetbrains.kotlin.jvm&quot;) version &quot;1.9.25&quot;
	id(&quot;org.jetbrains.kotlin.plugin.jpa&quot;) version &quot;1.9.25&quot;
	id(&quot;org.jetbrains.kotlin.plugin.spring&quot;) version &quot;1.9.25&quot;
	id(&quot;org.jetbrains.kotlin.kapt&quot;) version &quot;1.9.25&quot;
}

group = &quot;com.contact&quot;
version = &quot;0.0.1-SNAPSHOT&quot;

java {
	toolchain {
		languageVersion.set(JavaLanguageVersion.of(17))
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation(&quot;org.springframework.boot:spring-boot-starter-data-jpa&quot;)
	implementation(&quot;org.springframework.boot:spring-boot-starter-web&quot;)
	implementation(&quot;org.jetbrains.kotlin:kotlin-stdlib-jdk8&quot;)
	implementation(&quot;org.jetbrains.kotlin:kotlin-reflect&quot;)
	implementation(&quot;com.fasterxml.jackson.module:jackson-module-kotlin&quot;)
	implementation(&quot;org.springframework.boot:spring-boot-starter-validation&quot;)
	runtimeOnly(&quot;com.h2database:h2&quot;)
	testImplementation(&quot;org.springframework.boot:spring-boot-starter-test&quot;)
}

tasks.withType&amp;lt;Test&amp;gt; {
	useJUnitPlatform()
}

tasks.withType&amp;lt;org.jetbrains.kotlin.gradle.tasks.KotlinCompile&amp;gt; {
	kotlinOptions {
		jvmTarget = &quot;17&quot;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserDto.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738833657998&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.dto

import jakarta.validation.constraints.Email
import jakarta.validation.constraints.Size

data class UserDto(
    val id: Long?= null,

    @field:Size(min = 2, max = 50, message = &quot;이름은 2~50자 사이여야 합니다.&quot;)
    val name: String,

    @field:Email(message = &quot;올바른 이메일 형식을 입력하세요.&quot;)
    val email: String
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GlobalExceptionHandler.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738833905229&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.exception

import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice

@RestControllerAdvice
class GlobalExceptionHandler {

    @ExceptionHandler(CommonException::class)
    fun handleCommonException(e: CommonException): ResponseEntity&amp;lt;ErrorResponse&amp;gt; {
        val errorResponse = ErrorResponse(
            status = e.exceptionCode.status.value(),
            error = e.exceptionCode.message,
            message = e.message ?: &quot;예외 발생&quot;
        )
        return ResponseEntity(errorResponse, e.exceptionCode.status)
    }

    @ExceptionHandler(MethodArgumentNotValidException::class)
    fun handleValidationExceptions(ex: MethodArgumentNotValidException): ResponseEntity&amp;lt;ErrorResponse&amp;gt; {
        val errors = ex.bindingResult.fieldErrors.associate { it.field to (it.defaultMessage ?: &quot;잘못된 값&quot;) }

        val errorResponse = ErrorResponse(
            status = HttpStatus.BAD_REQUEST.value(),
            error = &quot;Validation Failed&quot;,
            message = &quot;입력값이 올바르지 않습니다.&quot;,
            errors = errors
        )

        return ResponseEntity(errorResponse, HttpStatus.BAD_REQUEST)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; handlerValdationExceptions를 추가함, vadliation에 걸렸을때 errorResponse에 400 BAD_REQUEST, 에러 내용을 return 하게 설정함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserController.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738833986008&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.controller

import com.contact.management.dto.UserDto
import com.contact.management.service.UserService
import jakarta.validation.Valid
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping(&quot;/api/users&quot;)
class UserController(
    private val userService: UserService
) {

    @GetMapping
    fun getUsers(): ResponseEntity&amp;lt;List&amp;lt;UserDto&amp;gt;&amp;gt; {
        val users = userService.getAllUsers()
        return ResponseEntity.ok(users)
    }

    @GetMapping(&quot;/{id}&quot;)
    fun getUser(@PathVariable id: Long): ResponseEntity&amp;lt;UserDto&amp;gt; {
        return ResponseEntity.ok(userService.getUserById(id))
    }

    @PostMapping
    fun createUser(@Valid @RequestBody userDto: UserDto): ResponseEntity&amp;lt;UserDto&amp;gt; {
        val createdUser = userService.createdUser(userDto)
        return ResponseEntity
            .status(HttpStatus.CREATED) // 201 Created
            .header(HttpHeaders.LOCATION, &quot;/api/users/${createdUser.id}&quot;) // 생성된 리소스의 URI 반환
            .body(createdUser)
    }

    @PutMapping(&quot;/{id}&quot;)
    fun updateUser(@PathVariable id: Long,@Valid @RequestBody userDto: UserDto): ResponseEntity&amp;lt;UserDto&amp;gt; {
        val updatedUser = userService.updateUser(id, userDto)
        return ResponseEntity.ok(updatedUser)
    }

    @DeleteMapping(&quot;/{id}&quot;)
    fun deleteUser(@PathVariable id: Long): ResponseEntity&amp;lt;Void&amp;gt; {
        userService.deleteUser(id)
        return ResponseEntity.noContent().build() // 204 No Content
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; createUser, updateUser api에 @Valid를 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름 길이가 2~50자가 아닌경우&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsTssp/btsL8mjsaC7/VuF6iOgvr1qDMZAbDNUfNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsTssp/btsL8mjsaC7/VuF6iOgvr1qDMZAbDNUfNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsTssp/btsL8mjsaC7/VuF6iOgvr1qDMZAbDNUfNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsTssp%2FbtsL8mjsaC7%2FVuF6iOgvr1qDMZAbDNUfNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;714&quot; height=&quot;541&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;541&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이메일 형식을 벗어난 경우&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vPne2/btsMaghuILf/lJXKFSYQ7p5Kfwa5oOWhvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vPne2/btsMaghuILf/lJXKFSYQ7p5Kfwa5oOWhvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vPne2/btsMaghuILf/lJXKFSYQ7p5Kfwa5oOWhvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvPne2%2FbtsMaghuILf%2FlJXKFSYQ7p5Kfwa5oOWhvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;533&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 GlobalException에 handleValidationExceptions를 적용하지않았다면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baQpFO/btsMav6y6ge/gYxhw675q6hfp5PaCkchkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baQpFO/btsMav6y6ge/gYxhw675q6hfp5PaCkchkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baQpFO/btsMav6y6ge/gYxhw675q6hfp5PaCkchkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaQpFO%2FbtsMav6y6ge%2FgYxhw675q6hfp5PaCkchkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;494&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; GlobalExcpetionHandler에 등록했을때와 비교했을때 에러 메시지가 부족&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/746</guid>
      <comments>https://aamoos.tistory.com/746#entry746comment</comments>
      <pubDate>Thu, 6 Feb 2025 18:37:56 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 3. GlobalException handler를 이용한 restApi 에러 처리</title>
      <link>https://aamoos.tistory.com/745</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 구조&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;221&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tysds/btsL7Clwtxy/QCGaqU22jNfckroPFQ2kHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tysds/btsL7Clwtxy/QCGaqU22jNfckroPFQ2kHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tysds/btsL7Clwtxy/QCGaqU22jNfckroPFQ2kHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftysds%2FbtsL7Clwtxy%2FQCGaqU22jNfckroPFQ2kHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;221&quot; height=&quot;127&quot; data-origin-width=&quot;221&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;147&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpj0WP/btsL9qqoz5D/R7KC2AUfipq7a3ZMbMKUJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpj0WP/btsL9qqoz5D/R7KC2AUfipq7a3ZMbMKUJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpj0WP/btsL9qqoz5D/R7KC2AUfipq7a3ZMbMKUJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpj0WP%2FbtsL9qqoz5D%2FR7KC2AUfipq7a3ZMbMKUJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;147&quot; height=&quot;121&quot; data-origin-width=&quot;147&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;165&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DFtpA/btsL7qr2wZY/p4Iyw2epC4hHrIA0fc7aQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DFtpA/btsL7qr2wZY/p4Iyw2epC4hHrIA0fc7aQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DFtpA/btsL7qr2wZY/p4Iyw2epC4hHrIA0fc7aQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDFtpA%2FbtsL7qr2wZY%2Fp4Iyw2epC4hHrIA0fc7aQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;165&quot; height=&quot;101&quot; data-origin-width=&quot;165&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CommonException.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738744689358&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.exception

class CommonException(val exceptionCode: CommonExceptionCode) : RuntimeException()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CommonExceptionCode.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738744722223&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.exception

import org.springframework.http.HttpStatus

enum class CommonExceptionCode(
    val status: HttpStatus,
    val message: String,
) {
    USER_NOT_FOUND(HttpStatus.NOT_FOUND, &quot;사용자를 찾을 수 없습니다.&quot;),
    EMAIL_ALREADY_EXISTS(HttpStatus.BAD_REQUEST, &quot;이미 존재하는 이메일 입니다.&quot;),
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ErrorResponse.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738744739407&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.exception

import com.fasterxml.jackson.annotation.JsonInclude
import java.time.LocalDateTime

@JsonInclude(JsonInclude.Include.NON_NULL) // null 필드는 JSON 응답에서 제외
data class ErrorResponse(
    val timestamp: LocalDateTime = LocalDateTime.now(),
    val status: Int,
    val error: String,
    val message: String,
    val errors: Map&amp;lt;String, String&amp;gt;? = null // validation 에러 처리를 위한 필드 추가
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GlobalExceptionHandler.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738744770141&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.exception

import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice

@RestControllerAdvice
class GlobalExceptionHandler {

    @ExceptionHandler(CommonException::class)
    fun handleCommonException(e: CommonException): ResponseEntity&amp;lt;ErrorResponse&amp;gt; {
        val errorResponse = ErrorResponse(
            status = e.exceptionCode.status.value(),
            error = e.exceptionCode.message,
            message = e.message ?: &quot;예외 발생&quot;
        )
        return ResponseEntity(errorResponse, e.exceptionCode.status)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; api에서 CommonException을 throw 하였을때 선언한 httpStatus, message를 return 하게 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserController.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738744881163&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.controller

import com.contact.management.dto.UserDto
import com.contact.management.service.UserService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping(&quot;/api/users&quot;)
class UserController(
    private val userService: UserService
) {

    @GetMapping
    fun getUsers(): ResponseEntity&amp;lt;List&amp;lt;UserDto&amp;gt;&amp;gt; {
        val users = userService.getAllUsers()
        return ResponseEntity.ok(users)
    }

    @GetMapping(&quot;/{id}&quot;)
    fun getUser(@PathVariable id: Long): ResponseEntity&amp;lt;UserDto&amp;gt; {
        return ResponseEntity.ok(userService.getUserById(id))
    }

     @PostMapping
    fun createUser(@RequestBody userDto: UserDto): ResponseEntity&amp;lt;UserDto&amp;gt; {
        val createdUser = userService.createdUser(userDto)
        return ResponseEntity
            .status(HttpStatus.CREATED) // 201 Created
            .header(HttpHeaders.LOCATION, &quot;/api/users/${createdUser.id}&quot;) // 생성된 리소스의 URI 반환
            .body(createdUser)
    }

    @PutMapping(&quot;/{id}&quot;)
    fun updateUser(@PathVariable id: Long, @RequestBody userDto: UserDto): ResponseEntity&amp;lt;UserDto&amp;gt; {
        val updatedUser = userService.updateUser(id, userDto)
        return ResponseEntity.ok(updatedUser)
    }

    @DeleteMapping(&quot;/{id}&quot;)
    fun deleteUser(@PathVariable id: Long): ResponseEntity&amp;lt;Void&amp;gt; {
        userService.deleteUser(id)
        return ResponseEntity.noContent().build() // 204 No Content
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rest api 설계 원칙&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. request, response 파라미터는 dto를 사용한다. (entity 사용 x)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;why : entity는 테이블 생성에 영향을 주기때문에 분리함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 간혹 api를 전부 @PostMapping으로 모두 선언해서 설계하는 경우가 있는데, 이는 올바른 설계가 아님&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. api가 정상적인경우는 데이터를 return, 정상적이지 않는경우 status, message, timeStamp 등 에러메시지를 return&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. api는 버전을 통한 관리가 이뤄줘야함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;why : 버전관리가 안되고 기존 api를 변경이되면 api를 사용하고 있던 client들에게 장애가 발생할수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 기존 api -&amp;gt; /api/v1/users&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 api -&amp;gt; /api/v2/users&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. api를 설계할때 명칭은 계층적으로 만들어야함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) /api/v1/car/suv/genesis&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserDto.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738744905646&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.dto

data class UserDto(
    val id: Long?= null,
    val name: String,
    val email: String
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserRepository.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738744921447&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.repository

import com.contact.management.entity.User
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import java.util.Optional

@Repository
interface UserRepository : JpaRepository&amp;lt;User, Long&amp;gt;{
    fun findByEmail(username: String): User?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 사용자 등록시 이메일 중복 체크를 위해 findByEmail을 생성함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; Kotlin에서는 Optional보다는 User?(nullable)로 반환, Optional 사용 시 let 블록 오작동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserService.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738744934102&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.service

import com.contact.management.dto.UserDto
import com.contact.management.entity.User
import com.contact.management.exception.CommonException
import com.contact.management.exception.CommonExceptionCode.EMAIL_ALREADY_EXISTS
import com.contact.management.exception.CommonExceptionCode.USER_NOT_FOUND
import com.contact.management.repository.UserRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class UserService(
    private val userRepository: UserRepository
) {

    @Transactional(readOnly = true)
    fun getAllUsers() : List&amp;lt;UserDto&amp;gt; {
        return userRepository.findAll().map { UserDto(it.id, it.name, it.email) }
    }

    @Transactional(readOnly = true)
    fun getUserById(id: Long): UserDto {
        val user = userRepository.findById(id).orElseThrow{ CommonException(USER_NOT_FOUND) }
        return UserDto(user.id, user.name, user.email)
    }

    fun createdUser(userDto: UserDto): UserDto {
        userRepository.findByEmail(userDto.email)?.let {
            throw CommonException(EMAIL_ALREADY_EXISTS)
        }

        val user = userRepository.save(User(name = userDto.name, email = userDto.email))
        return UserDto(user.id, user.name, user.email)
    }

    // 사용자 업데이트
    fun updateUser(id: Long, userDto: UserDto): UserDto{
        val updatedUser = userRepository.findById(id).orElseThrow{ CommonException(USER_NOT_FOUND) }

        // 엔티티의 속성 수정
        updatedUser.name = userDto.name
        updatedUser.email = userDto.email
        // DB에 업데이트 로직

        return UserDto(
            id = updatedUser.id,
            name = updatedUser.name,
            email = updatedUser.email
        )
    }

    fun deleteUser(id: Long){
        if(!userRepository.existsById(id)){
            throw CommonException(USER_NOT_FOUND)
        }
        userRepository.deleteById(id)
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 넘어온 id에 해당하는 사용자가 없을경우 CommonException(USER_NOT_FOUND)로 예외처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 사용자 등록시 이메일 계정이 이미 등록되어있는경우 CommonException(EMAIL_ALREADY_EXISTS)로 예외처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; getAllUsers, getUserById는 데이터를 조회만 하기 떄문에 @Transactional(readOnly = true)를 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;why : 이렇게 하면 변경감지를 하지않으므로 성능향상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 추가/수정/삭제하는 메서드에&amp;nbsp; @Transactional(readOnly = true)를 적용을 하면 데이터 변경이 반영되지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;postman&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 등록&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yMNnZ/btsL8J5nowJ/4zngg5t3dJ6dDNlmPuTrj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yMNnZ/btsL8J5nowJ/4zngg5t3dJ6dDNlmPuTrj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yMNnZ/btsL8J5nowJ/4zngg5t3dJ6dDNlmPuTrj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyMNnZ%2FbtsL8J5nowJ%2F4zngg5t3dJ6dDNlmPuTrj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;469&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;469&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;201 새로운 리소스(사용자)가 성공적으로 생성되었음을 명확하게 표시 또한&amp;nbsp;Location&amp;nbsp;헤더에&amp;nbsp;생성된&amp;nbsp;리소스의&amp;nbsp;URL을&amp;nbsp;포함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 등록 (이메일 중복)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d3e5i6/btsL80fW8E0/aY2WWwrJL2PoBrDZlF9ODk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d3e5i6/btsL80fW8E0/aY2WWwrJL2PoBrDZlF9ODk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d3e5i6/btsL80fW8E0/aY2WWwrJL2PoBrDZlF9ODk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd3e5i6%2FbtsL80fW8E0%2FaY2WWwrJL2PoBrDZlF9ODk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;709&quot; height=&quot;486&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;400 Bad Request&lt;/td&gt;
&lt;td&gt;잘못된 요청 (유효성 검사 실패)&lt;/td&gt;
&lt;td&gt;클라이언트가 유효하지 않은 데이터를 보냈을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;409 Conflict&lt;/td&gt;
&lt;td&gt;리소스 충돌 (중복 데이터)&lt;/td&gt;
&lt;td&gt;데이터베이스의 고유 값 제약 조건을 위반할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 전체조회&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/01GhX/btsL73iSHbB/0On7Sj4x16HRq6kpqWrZSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/01GhX/btsL73iSHbB/0On7Sj4x16HRq6kpqWrZSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/01GhX/btsL73iSHbB/0On7Sj4x16HRq6kpqWrZSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F01GhX%2FbtsL73iSHbB%2F0On7Sj4x16HRq6kpqWrZSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;509&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LIw9k/btsL7fYG9Eb/cSpOZJM7eZqK1hJoM8kKN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LIw9k/btsL7fYG9Eb/cSpOZJM7eZqK1hJoM8kKN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LIw9k/btsL7fYG9Eb/cSpOZJM7eZqK1hJoM8kKN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLIw9k%2FbtsL7fYG9Eb%2FcSpOZJM7eZqK1hJoM8kKN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;291&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;291&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 사용자 조회&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsPGUk/btsL8V5vC8U/Qok8802LnUQkG5oawtWUB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsPGUk/btsL8V5vC8U/Qok8802LnUQkG5oawtWUB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsPGUk/btsL8V5vC8U/Qok8802LnUQkG5oawtWUB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsPGUk%2FbtsL8V5vC8U%2FQok8802LnUQkG5oawtWUB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;474&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 사용자 조회 (없는 id)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;497&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcR1Hy/btsL8INaSN0/AMA09pmRnNRt2UkZ2FyWv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcR1Hy/btsL8INaSN0/AMA09pmRnNRt2UkZ2FyWv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcR1Hy/btsL8INaSN0/AMA09pmRnNRt2UkZ2FyWv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcR1Hy%2FbtsL8INaSN0%2FAMA09pmRnNRt2UkZ2FyWv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;497&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;497&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;404 Not Found는 요청한 리소스(사용자)가 존재하지 않을 때 사용하는 표준 상태 코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 변경&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceQ9d7/btsL7rdubSY/rSI1vWhxoKCkibVSqHU5W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceQ9d7/btsL7rdubSY/rSI1vWhxoKCkibVSqHU5W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceQ9d7/btsL7rdubSY/rSI1vWhxoKCkibVSqHU5W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceQ9d7%2FbtsL7rdubSY%2FrSI1vWhxoKCkibVSqHU5W0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;472&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 삭제&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ7ACU/btsL9anN1b0/tv9fOuvUkjceK916sPpc71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ7ACU/btsL9anN1b0/tv9fOuvUkjceK916sPpc71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ7ACU/btsL9anN1b0/tv9fOuvUkjceK916sPpc71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ7ACU%2FbtsL9anN1b0%2Ftv9fOuvUkjceK916sPpc71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;712&quot; height=&quot;400&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스를 삭제할 때 204 No Content 응답 코드를 사용하는 것이 일반적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;204&amp;nbsp;No&amp;nbsp;Content를&amp;nbsp;사용하는&amp;nbsp;이유 &lt;br /&gt;성공적으로&amp;nbsp;삭제되었음을&amp;nbsp;나타냄:&amp;nbsp;요청이&amp;nbsp;정상적으로&amp;nbsp;처리되었음을&amp;nbsp;의미함. &lt;br /&gt;응답&amp;nbsp;본문이&amp;nbsp;필요&amp;nbsp;없음:&amp;nbsp;삭제된&amp;nbsp;리소스에&amp;nbsp;대한&amp;nbsp;추가적인&amp;nbsp;정보가&amp;nbsp;필요하지&amp;nbsp;않음. &lt;br /&gt;클라이언트가&amp;nbsp;불필요한&amp;nbsp;데이터&amp;nbsp;처리를&amp;nbsp;하지&amp;nbsp;않음:&amp;nbsp;응답&amp;nbsp;본문이&amp;nbsp;없으므로&amp;nbsp;클라이언트가&amp;nbsp;불필요한&amp;nbsp;데이터를&amp;nbsp;처리할&amp;nbsp;필요가&amp;nbsp;없음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 삭제 (없는 사용자)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C5jlm/btsL87lqSLh/Yk3AMTKMIMRJviYouTGVxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C5jlm/btsL87lqSLh/Yk3AMTKMIMRJviYouTGVxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C5jlm/btsL87lqSLh/Yk3AMTKMIMRJviYouTGVxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC5jlm%2FbtsL87lqSLh%2FYk3AMTKMIMRJviYouTGVxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;455&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/745</guid>
      <comments>https://aamoos.tistory.com/745#entry745comment</comments>
      <pubDate>Wed, 5 Feb 2025 18:10:03 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 2. H2 데이터베이스 메모리 내 데이터베이스 설정</title>
      <link>https://aamoos.tistory.com/744</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. application.properties 파일 삭제후 application.yml 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFxkKv/btsL6i7u1UJ/GyA1aCBk6N3zOcCacf3Buk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFxkKv/btsL6i7u1UJ/GyA1aCBk6N3zOcCacf3Buk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFxkKv/btsL6i7u1UJ/GyA1aCBk6N3zOcCacf3Buk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFxkKv%2FbtsL6i7u1UJ%2FGyA1aCBk6N3zOcCacf3Buk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;434&quot; height=&quot;107&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVwhuO/btsL4IzHIHU/sVLci0DgPlmpiMhyxooW60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVwhuO/btsL4IzHIHU/sVLci0DgPlmpiMhyxooW60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVwhuO/btsL4IzHIHU/sVLci0DgPlmpiMhyxooW60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVwhuO%2FbtsL4IzHIHU%2FsVLci0DgPlmpiMhyxooW60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;225&quot; height=&quot;104&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. h2 메모리 내 데이터베이스용으로 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml&lt;/p&gt;
&lt;pre id=&quot;code_1738570919858&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  # H2 Console 설정
  h2:
    console:
      enabled: true  # H2 Console을 사용할지 여부
      path: /h2-console  # H2 Console의 접근 경로

  # 데이터베이스 설정
  datasource:
    driver-class-name: org.h2.Driver  # H2 드라이버 사용
    url: jdbc:h2:mem:management  # 메모리 내 데이터베이스 (테스트용)
    username: sa  # 접속할 사용자명
    password:  # 비밀번호 (없으면 공백으로 설정)

  # JPA 설정
  jpa:
    hibernate:
      ddl-auto: create  # 테이블 자동 생성 및 업데이트 (설정에 따라 'none', 'update', 'create', 'create-drop' 등이 가능)
    show-sql: true  # SQL 쿼리를 로그에 출력
    database-platform: org.hibernate.dialect.H2Dialect  # H2 데이터베이스용 Hibernate Dialect 설정
    properties:
      hibernate:
        format_sql: true  # SQL을 보기 쉽게 포맷&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. entity 패키지 생성후 Item.kt 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/berdCT/btsL6JKoZAe/PyFRzpSHJ8uvloElG57rq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/berdCT/btsL6JKoZAe/PyFRzpSHJ8uvloElG57rq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/berdCT/btsL6JKoZAe/PyFRzpSHJ8uvloElG57rq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FberdCT%2FbtsL6JKoZAe%2FPyFRzpSHJ8uvloElG57rq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;211&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;211&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Item.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738570985717&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.entity

import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id

@Entity
class Item(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,

    val name: String,
    val description: String,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. application 재시작&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGqztO/btsL4N8Ew82/pmlzuKMG0mHShChcwtuimK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGqztO/btsL4N8Ew82/pmlzuKMG0mHShChcwtuimK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGqztO/btsL4N8Ew82/pmlzuKMG0mHShChcwtuimK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGqztO%2FbtsL4N8Ew82%2FpmlzuKMG0mHShChcwtuimK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;193&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;193&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. http://localhost:8080/h2-console/ 입력후 접속&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sSomm/btsL4JFlMK8/olORWDP3dXtOzXQOsV2tBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sSomm/btsL4JFlMK8/olORWDP3dXtOzXQOsV2tBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sSomm/btsL4JFlMK8/olORWDP3dXtOzXQOsV2tBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsSomm%2FbtsL4JFlMK8%2FolORWDP3dXtOzXQOsV2tBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;304&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mdjG9/btsL4ERDpnK/dcT3B6MJTQMdmB6oZI6UcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mdjG9/btsL4ERDpnK/dcT3B6MJTQMdmB6oZI6UcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mdjG9/btsL4ERDpnK/dcT3B6MJTQMdmB6oZI6UcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmdjG9%2FbtsL4ERDpnK%2FdcT3B6MJTQMdmB6oZI6UcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;309&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/744</guid>
      <comments>https://aamoos.tistory.com/744#entry744comment</comments>
      <pubDate>Mon, 3 Feb 2025 17:25:17 +0900</pubDate>
    </item>
    <item>
      <title>[코프링] 1. 프로젝트 생성</title>
      <link>https://aamoos.tistory.com/743</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. 해당사이트 접속후 Spring Web, H2 Database, Spring Boot Dev Tools, Spring Data Jpa를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Generate를 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://start.spring.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://start.spring.io/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;721&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G1cmD/btsL6CxDqrV/oWzRng5CKuOklknBtG3km0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G1cmD/btsL6CxDqrV/oWzRng5CKuOklknBtG3km0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G1cmD/btsL6CxDqrV/oWzRng5CKuOklknBtG3km0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG1cmD%2FbtsL6CxDqrV%2FoWzRng5CKuOklknBtG3km0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1516&quot; height=&quot;721&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;721&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. File - Project structure 클릭후 SDK가 선택한 JAVA로 선택되어있는지 확인후 변경합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;863&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U8KBs/btsL4xycKfk/iwgzTQdZMaS3F4KPJApwfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U8KBs/btsL4xycKfk/iwgzTQdZMaS3F4KPJApwfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U8KBs/btsL4xycKfk/iwgzTQdZMaS3F4KPJApwfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU8KBs%2FbtsL4xycKfk%2FiwgzTQdZMaS3F4KPJApwfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;863&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. File - Settings - Gradle&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Build and run using, Run tests Using이 inteliJ IDEA로 선택을 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nDVjz/btsL485AoHp/lVebpFXYFbB9grakYwI4n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nDVjz/btsL485AoHp/lVebpFXYFbB9grakYwI4n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nDVjz/btsL485AoHp/lVebpFXYFbB9grakYwI4n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnDVjz%2FbtsL485AoHp%2FlVebpFXYFbB9grakYwI4n1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;981&quot; height=&quot;734&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle&lt;/p&gt;
&lt;pre id=&quot;code_1738566708418&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
	id(&quot;org.springframework.boot&quot;) version &quot;3.4.2&quot;
	id(&quot;io.spring.dependency-management&quot;) version &quot;1.1.7&quot;
	id(&quot;java&quot;)
	id(&quot;org.jetbrains.kotlin.jvm&quot;) version &quot;1.9.25&quot;
	id(&quot;org.jetbrains.kotlin.plugin.jpa&quot;) version &quot;1.9.25&quot;
	id(&quot;org.jetbrains.kotlin.plugin.spring&quot;) version &quot;1.9.25&quot;
	id(&quot;org.jetbrains.kotlin.kapt&quot;) version &quot;1.9.25&quot;
}

group = &quot;com.contact&quot;
version = &quot;0.0.1-SNAPSHOT&quot;

java {
	toolchain {
		languageVersion.set(JavaLanguageVersion.of(17))
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation(&quot;org.springframework.boot:spring-boot-starter-data-jpa&quot;)
	implementation(&quot;org.springframework.boot:spring-boot-starter-web&quot;)
	implementation(&quot;org.jetbrains.kotlin:kotlin-stdlib-jdk8&quot;)
	implementation(&quot;org.jetbrains.kotlin:kotlin-reflect&quot;)
	implementation(&quot;com.fasterxml.jackson.module:jackson-module-kotlin&quot;)

	runtimeOnly(&quot;com.h2database:h2&quot;)
	testImplementation(&quot;org.springframework.boot:spring-boot-starter-test&quot;)
}

tasks.withType&amp;lt;Test&amp;gt; {
	useJUnitPlatform()
}

tasks.withType&amp;lt;org.jetbrains.kotlin.gradle.tasks.KotlinCompile&amp;gt; {
	kotlinOptions {
		jvmTarget = &quot;17&quot;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. New - Kotlin Class/File 클릭후 TestController 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clgLcb/btsL4pf01Py/QTI4IOkqyOkdURNgq5zR81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clgLcb/btsL4pf01Py/QTI4IOkqyOkdURNgq5zR81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clgLcb/btsL4pf01Py/QTI4IOkqyOkdURNgq5zR81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclgLcb%2FbtsL4pf01Py%2FQTI4IOkqyOkdURNgq5zR81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;758&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. TestController.kt&lt;/p&gt;
&lt;pre id=&quot;code_1738566922261&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.contact.management.controller

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class TestController {

    @GetMapping(&quot;/test&quot;)
    fun test(): String{
        return &quot;ok&quot;;
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;283&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o0Pxw/btsL4LiJghh/tEm09s0peTrY4q3nywkWK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o0Pxw/btsL4LiJghh/tEm09s0peTrY4q3nywkWK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o0Pxw/btsL4LiJghh/tEm09s0peTrY4q3nywkWK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo0Pxw%2FbtsL4LiJghh%2FtEm09s0peTrY4q3nywkWK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;283&quot; height=&quot;161&quot; data-origin-width=&quot;283&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>backend/코프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/743</guid>
      <comments>https://aamoos.tistory.com/743#entry743comment</comments>
      <pubDate>Mon, 3 Feb 2025 16:16:08 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot 3.0 이상, 3.0이하 querydsl 설정 방법</title>
      <link>https://aamoos.tistory.com/741</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring boot 3.0 이상&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle&lt;/p&gt;
&lt;pre id=&quot;code_1696043751653&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
	id 'java'
	id 'org.springframework.boot' version '3.1.4'
	id 'io.spring.dependency-management' version '1.1.3'
}

group = 'study'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

	// QueryDSL Implementation
	implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
	annotationProcessor &quot;com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta&quot;
	annotationProcessor &quot;jakarta.annotation:jakarta.annotation-api&quot;
	annotationProcessor &quot;jakarta.persistence:jakarta.persistence-api&quot;
}

test {
	useJUnitPlatform()
}

/**
 * QueryDSL Build Options
 */
def querydslDir = &quot;src/main/generated&quot;

sourceSets {
	main.java.srcDirs += [ querydslDir ]
}

tasks.withType(JavaCompile) {
	options.getGeneratedSourceOutputDirectory().set(file(querydslDir))
}

clean.doLast {
	file(querydslDir).deleteDir()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Spring boot 3.0 이하&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle&lt;/p&gt;
&lt;pre id=&quot;code_1696043896234&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. queryDsl version 정보 추가
buildscript {
	ext {
		queryDslVersion = &quot;5.0.0&quot;
	}
}

plugins {
	id 'org.springframework.boot' version '2.7.1'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	// 2. querydsl plugins 추가
	id &quot;com.ewerk.gradle.plugins.querydsl&quot; version &quot;1.0.10&quot;
	id 'java'
}

group = 'study'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'

	// 3. querydsl dependencies 추가
	implementation &quot;com.querydsl:querydsl-jpa:${queryDslVersion}&quot;
	implementation &quot;com.querydsl:querydsl-apt:${queryDslVersion}&quot;

	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

/*
 * queryDSL 설정 추가
 */
// querydsl에서 사용할 경로 설정
def querydslDir = &quot;$buildDir/generated/querydsl&quot;
// JPA 사용 여부와 사용할 경로를 설정
querydsl {
	jpa = true
	querydslSourcesDir = querydslDir
}
// build 시 사용할 sourceSet 추가
sourceSets {
	main.java.srcDir querydslDir
}
// querydsl 컴파일시 사용할 옵션 설정
compileQuerydsl{
	options.annotationProcessorPath = configurations.querydsl
}
// querydsl 이 compileClassPath 를 상속하도록 설정
configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
	querydsl.extendsFrom compileClasspath
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QuerydslConfig.java&lt;/p&gt;
&lt;pre id=&quot;code_1714623616705&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package study.querydsl.config;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QuerydslConfig {
    private final EntityManager entityManager;

    public QuerydslConfig(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test.java&lt;/p&gt;
&lt;pre id=&quot;code_1696043941043&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package study.querydsl;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import study.querydsl.entity.Hello;
import study.querydsl.entity.QHello;

import static org.assertj.core.api.Assertions.*;

@SpringBootTest
@Transactional
class QuerydslApplicationTests {

	@Autowired
	EntityManager em;

	@Test
	void contextLoads() {
		Hello hello = new Hello();
		em.persist(hello);

		JPAQueryFactory query = new JPAQueryFactory(em);
		QHello qHello = QHello.hello;

		Hello result = query
				.selectFrom(qHello)
				.fetchOne();

		assertThat(result).isEqualTo(hello);
		assertThat(result.getId()).isEqualTo(hello.getId());

	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;build 하는법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradle&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clean - build&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;265&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boTWsL/btswbc2TUzI/JTiVNn1mk2kjcGTO1KsJU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boTWsL/btswbc2TUzI/JTiVNn1mk2kjcGTO1KsJU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boTWsL/btswbc2TUzI/JTiVNn1mk2kjcGTO1KsJU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboTWsL%2Fbtswbc2TUzI%2FJTiVNn1mk2kjcGTO1KsJU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;265&quot; height=&quot;552&quot; data-origin-width=&quot;265&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>backend/스프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/741</guid>
      <comments>https://aamoos.tistory.com/741#entry741comment</comments>
      <pubDate>Sat, 30 Sep 2023 12:19:13 +0900</pubDate>
    </item>
    <item>
      <title>H2 데이터베이스 생성 (Database not found, either pre-create it or allow remote database creation (not recommended in secure environments)</title>
      <link>https://aamoos.tistory.com/740</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1004&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lJIbQ/btswbc2TRg2/fSZMC1hGWDEZPIyK8QkhOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lJIbQ/btswbc2TRg2/fSZMC1hGWDEZPIyK8QkhOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lJIbQ/btswbc2TRg2/fSZMC1hGWDEZPIyK8QkhOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlJIbQ%2Fbtswbc2TRg2%2FfSZMC1hGWDEZPIyK8QkhOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1004&quot; height=&quot;412&quot; data-origin-width=&quot;1004&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 프로젝트 생성후 Generice H2로 (Server)로 접근하려고 하면 해당 에러가 나오면서 접속이 되지 않음,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이경우 데이터베이스가 존재하지 않아서 나타나는 에러임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jdbc:h2:~/데이터베이스명&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nd5tD/btsv8y6ABx9/wmqQw5K0UzI3uWPM2tKNq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nd5tD/btsv8y6ABx9/wmqQw5K0UzI3uWPM2tKNq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nd5tD/btsv8y6ABx9/wmqQw5K0UzI3uWPM2tKNq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnd5tD%2Fbtsv8y6ABx9%2FwmqQw5K0UzI3uWPM2tKNq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;442&quot; height=&quot;305&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 이미지 처럼 Generice H2 (Embedded)를 선택후 연결을 누르면 맨 처음에 데이터베이스가 없을때 생성하는 것으로, 위 문제를 해결할수 있음, 다음에 접속할때는 Generice H2 (Server)를 선택후 접속하면됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;위처럼 해도 데이터베이스가 안만들어지는경우&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjpRI5/btswknQhdFx/w5ahRGmyFg52puyKCRA9YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjpRI5/btswknQhdFx/w5ahRGmyFg52puyKCRA9YK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjpRI5/btswknQhdFx/w5ahRGmyFg52puyKCRA9YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjpRI5%2FbtswknQhdFx%2Fw5ahRGmyFg52puyKCRA9YK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;270&quot; height=&quot;91&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;H2 Exit (종료)후 다시 실행 위작업을 진행&lt;/p&gt;</description>
      <category>backend/스프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/740</guid>
      <comments>https://aamoos.tistory.com/740#entry740comment</comments>
      <pubDate>Sat, 30 Sep 2023 12:13:43 +0900</pubDate>
    </item>
    <item>
      <title>InteliJ UTF-8 한글깨짐 설정</title>
      <link>https://aamoos.tistory.com/739</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;File - Settings - File Encodings에 해당 부분 변경&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M5cUq/btsuG73AvKl/FGmgGlPKiTCZVyZHjLfhTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M5cUq/btsuG73AvKl/FGmgGlPKiTCZVyZHjLfhTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M5cUq/btsuG73AvKl/FGmgGlPKiTCZVyZHjLfhTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM5cUq%2FbtsuG73AvKl%2FFGmgGlPKiTCZVyZHjLfhTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;987&quot; height=&quot;730&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Help - Edit Custom VM Options 클릭후 해당 코드 넣기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;663&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1tmBM/btsuOfGTcHv/mUP5qHyY8bKLJpNhATKpMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1tmBM/btsuOfGTcHv/mUP5qHyY8bKLJpNhATKpMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1tmBM/btsuOfGTcHv/mUP5qHyY8bKLJpNhATKpMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1tmBM%2FbtsuOfGTcHv%2FmUP5qHyY8bKLJpNhATKpMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;272&quot; height=&quot;663&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;663&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1695143403256&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-Dfile.encoding=UTF-8
-Dconsole.encoding=UTF-8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>backend/스프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/739</guid>
      <comments>https://aamoos.tistory.com/739#entry739comment</comments>
      <pubDate>Wed, 20 Sep 2023 02:10:13 +0900</pubDate>
    </item>
    <item>
      <title>Docker Desktop - WSL kernel verision too low</title>
      <link>https://aamoos.tistory.com/738</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;- docker Desktop을 실행하였는데 해당에러가 발생할경우 wsl을 업데이트 해줘야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AomYA/btstlNR4euG/QcSfPZbPUp0ap52st1SnGk/img.png&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;188&quot; data-is-animation=&quot;false&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- windows powerShell을 실행한 후 해당 명령어 실행&lt;/p&gt;
&lt;pre id=&quot;code_1694009140486&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;wsl --update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이후 docker desktop 실행하였을때 정상적으로 실행됨&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1265&quot; data-origin-height=&quot;715&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1It90/btstfSGTjQw/KPWuqVn4A0u2l00SlbpOk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1It90/btstfSGTjQw/KPWuqVn4A0u2l00SlbpOk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1It90/btstfSGTjQw/KPWuqVn4A0u2l00SlbpOk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1It90%2FbtstfSGTjQw%2FKPWuqVn4A0u2l00SlbpOk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1265&quot; height=&quot;715&quot; data-origin-width=&quot;1265&quot; data-origin-height=&quot;715&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>DevOps/Docker</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/738</guid>
      <comments>https://aamoos.tistory.com/738#entry738comment</comments>
      <pubDate>Wed, 6 Sep 2023 23:07:12 +0900</pubDate>
    </item>
    <item>
      <title>windows에서 'docker-compose'은 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.</title>
      <link>https://aamoos.tistory.com/737</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MYJLz/btstcPD9s6J/HkckmEkQLDLgUlswKMqHj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MYJLz/btstcPD9s6J/HkckmEkQLDLgUlswKMqHj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MYJLz/btstcPD9s6J/HkckmEkQLDLgUlswKMqHj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMYJLz%2FbtstcPD9s6J%2FHkckmEkQLDLgUlswKMqHj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;593&quot; height=&quot;50&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- widnows에서 docker 관련된 명령어를 사용할때는 docker desktop을 설치 해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Docker DeskTop 설치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.docker.com/get-started/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.docker.com/get-started/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694008669451&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Get Started | Docker&quot; data-og-description=&quot;Get started with Docker Desktop and join millions of developers in faster, more secure app development using containers and beyond.&quot; data-og-host=&quot;www.docker.com&quot; data-og-source-url=&quot;https://www.docker.com/get-started/&quot; data-og-url=&quot;https://www.docker.com/get-started/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/g5wvh/hyTSrBwonF/I4r9z5U3kPX3p15LRpIig1/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/BFujx/hyTSyUXexb/zWE1nvBHj4MYNy3ly4tOd0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/LQ5JU/hyTSzsNHtO/IpRL8m6tOzZ7TS8yLEdMW0/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627&quot;&gt;&lt;a href=&quot;https://www.docker.com/get-started/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.docker.com/get-started/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/g5wvh/hyTSrBwonF/I4r9z5U3kPX3p15LRpIig1/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/BFujx/hyTSyUXexb/zWE1nvBHj4MYNy3ly4tOd0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/LQ5JU/hyTSzsNHtO/IpRL8m6tOzZ7TS8yLEdMW0/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Get Started | Docker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Get started with Docker Desktop and join millions of developers in faster, more secure app development using containers and beyond.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/znd7B/btstksOokDn/PJz8G77ztDOaHNbZ6ib231/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/znd7B/btstksOokDn/PJz8G77ztDOaHNbZ6ib231/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/znd7B/btstksOokDn/PJz8G77ztDOaHNbZ6ib231/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fznd7B%2FbtstksOokDn%2FPJz8G77ztDOaHNbZ6ib231%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1651&quot; height=&quot;593&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fafbfd; color: #333333; text-align: left;&quot;&gt;Docker Desktop - WSL kernel verision too low 에러가 발생한경우&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aamoos.tistory.com/738&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://aamoos.tistory.com/738&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694009291124&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Docker Desktop - WSL kernel verision too low&quot; data-og-description=&quot;- windows powerShell을 실행한 후 해당 명령어 실행 wsl --update - 이후 docker desktop 실행하였을때 정상적으로 실행됨&quot; data-og-host=&quot;aamoos.tistory.com&quot; data-og-source-url=&quot;https://aamoos.tistory.com/738&quot; data-og-url=&quot;https://aamoos.tistory.com/738&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/PTNiE/hyTSxhrEPr/pinIBDhbss7cVqXhyVKY3k/img.png?width=556&amp;amp;height=188&amp;amp;face=0_0_556_188,https://scrap.kakaocdn.net/dn/Wo704/hyTPyoJpYm/4Sk0rAnWycAV9ntP9f0RZ1/img.png?width=556&amp;amp;height=188&amp;amp;face=0_0_556_188,https://scrap.kakaocdn.net/dn/cyTHTj/hyTPxi4D3E/3DwCQzIBVatWk3s8C4Gb80/img.png?width=1265&amp;amp;height=715&amp;amp;face=0_0_1265_715&quot;&gt;&lt;a href=&quot;https://aamoos.tistory.com/738&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aamoos.tistory.com/738&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/PTNiE/hyTSxhrEPr/pinIBDhbss7cVqXhyVKY3k/img.png?width=556&amp;amp;height=188&amp;amp;face=0_0_556_188,https://scrap.kakaocdn.net/dn/Wo704/hyTPyoJpYm/4Sk0rAnWycAV9ntP9f0RZ1/img.png?width=556&amp;amp;height=188&amp;amp;face=0_0_556_188,https://scrap.kakaocdn.net/dn/cyTHTj/hyTPxi4D3E/3DwCQzIBVatWk3s8C4Gb80/img.png?width=1265&amp;amp;height=715&amp;amp;face=0_0_1265_715');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Docker Desktop - WSL kernel verision too low&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;- windows powerShell을 실행한 후 해당 명령어 실행 wsl --update - 이후 docker desktop 실행하였을때 정상적으로 실행됨&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;aamoos.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/Docker</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/737</guid>
      <comments>https://aamoos.tistory.com/737#entry737comment</comments>
      <pubDate>Wed, 6 Sep 2023 22:58:33 +0900</pubDate>
    </item>
    <item>
      <title>[LeafletJs] Leaflet Draw Circle 총반경 구하기</title>
      <link>https://aamoos.tistory.com/735</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이번 포스팅에서는 leaflet으로 circle을 그리고 총반경을 구하는것을 해보겠습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLcDdQ/btrVt5kPmZ9/4nlMgGkVisaOekzovDsxEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLcDdQ/btrVt5kPmZ9/4nlMgGkVisaOekzovDsxEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLcDdQ/btrVt5kPmZ9/4nlMgGkVisaOekzovDsxEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLcDdQ%2FbtrVt5kPmZ9%2F4nlMgGkVisaOekzovDsxEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;446&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 네이버 지도에 보면 원 총반경을 구하는 기능이 있는데 해당 기능을 구현해보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체코드&lt;/h2&gt;
&lt;pre id=&quot;code_1672905587619&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%@ page contentType=&quot;text/html; charset=utf-8&quot; pageEncoding=&quot;utf-8&quot; %&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;main role=&quot;main&quot; class=&quot;inner cover&quot;&amp;gt;
        &amp;lt;h1 class=&quot;cover-heading&quot;&amp;gt;LeafletJs Tutorial&amp;lt;/h1&amp;gt;
&amp;lt;!-- 		&amp;lt;p class=&quot;lead&quot;&amp;gt;- openstreetmap&amp;lt;/p&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;br&amp;gt; --&amp;gt;
		&amp;lt;p class=&quot;lead&quot;&amp;gt;polygon 면적 측정&amp;lt;/p&amp;gt;
		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/main&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;script&amp;gt;
	let lon = &quot;&quot;;
	let lat = &quot;&quot;;
	let leafletMap = &quot;&quot;;
	let startMarker = &quot;&quot;;
	let finishMarker = &quot;&quot;;
	let contextMenuFlag = true;
	let lineArray = [];
	let drawControl = &quot;&quot;;
	let layerType = &quot;&quot;;
	let polygon = new L.featureGroup();
	let drawlon = &quot;&quot;;
	let drawlat = &quot;&quot;;
	let drawnItems = &quot;&quot;;
	let layer = &quot;&quot;;
	
	//apikey
	const apiKey = $(&quot;#apiKey&quot;).val();
	
	$(function(){
		init();
	});
	
	function init(){
		navigator.geolocation.getCurrentPosition(onGeoOk,onGeoError);
	}
	
	//현재 좌표값 가져오기
	function onGeoOk(position){
	    lon = position.coords.latitude;
	    lat = position.coords.longitude;
	    console.log(&quot;You live in&quot;, lon, lat);
	    
	    //wmts 가져오기
	    vworldWmts();
	}
	
	function onGeoError(){
	    alert(&quot;Can't find you. No weather for you.&quot;);
	}

	function vworldWmts(){
		//leaflet 지도 띄우기 (EPSG : 4326)
		leafletMap = L.map('map').setView([lon, lat], 15)
		
		L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;).addTo(leafletMap);
		
		//해당 플래그가 true일때만 동작
		//맵에 오른쪽마우스 클릭이벤트
		leafletMap.on('click', leftMouseClick);
		leafletMap.on('contextmenu', rightMouseClick);
		
		//layer 변경
		changeLayer(leafletMap)
		
		//leaflet 그리기
		initLeafletDraw(leafletMap)
		
	}
	
	//layer 변경
	function changeLayer(leafletMap){
		drawnItems = L.featureGroup().addTo(leafletMap);
		const vworld = L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;, { maxZoom: 18});
		//레이어 변경
		L.control.layers({
			'vworld' : vworld.addTo(leafletMap),
			'osm': L.tileLayer(&quot;http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&quot;, {
				attribution: 'osm'
			}),
			'google': L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&amp;amp;gl=cn&amp;amp;x={x}&amp;amp;y={y}&amp;amp;z={z}', {
				attribution: 'google'
			})
		}, { 'drawlayer': drawnItems }, { position: 'topleft', collapsed: false }).addTo(leafletMap);		
	}
	
	//leaflet 그리기
	function initLeafletDraw(leafletMap){
		
		drawControl = new L.Control.Draw({
			edit : false,
// 			edit: {
// 				featureGroup: drawnItems,
// 				poly: {
// 					allowIntersection: false,
// 					remove: false
// 				}
// 			},
			
			draw: {
				polygon: {
					allowIntersection: false,
					showArea: true
				}
			}
		});
		
        leafletMap.addControl(drawControl);

		//created
		leafletMap.on(L.Draw.Event.CREATED, function (event) {
			layer = event.layer;
			layerType = event.layerType;
			drawnItems.addLayer(layer);
			//polyline 일경우
			if(layerType == 'polyline') {
				createAreaTooltip(layer);
		    }else if(layerType == 'polygon'){
		    	createAreaTooltip(layer);
		    }else if(layerType == 'circle'){
		    	createAreaTooltip(layer);
		    }
			  
		});
		
		//그리기 도구 열때
		leafletMap.on('draw:toolbaropened', function (e) {
			contextMenuFlag = false;
		});
		
		//그리기 도구 닫힐때
		leafletMap.on('draw:toolbarclosed', function (e) {
			contextMenuFlag = true;
		});
		
		//도구모음 클릭시 layerType
		leafletMap.on('draw:drawstart', function (e) {
			layerType = e.layerType;
		});
		
	}
	
	//폴리곤 tooltip 생성
	function createAreaTooltip(layer) {
		console.log(layer.areaTooltip);
        if(layer.areaTooltip) {
            return;
        }

        layer.areaTooltip = L.tooltip({
            permanent: true,
            direction: 'center',
            className: 'area-tooltip'
        });

        layer.on('remove', function(event) {
            layer.areaTooltip.remove();
        });

        layer.on('add', function(event) {
            updateAreaTooltip(layer);
            layer.areaTooltip.addTo(leafletMap);
        });

        if(leafletMap.hasLayer(layer)) {
            updateAreaTooltip(layer);
            console.log(layer.areaTooltip);
            //얘가문제
            layer.areaTooltip.addTo(leafletMap);
        }
    }
	
	//폴리곤 영역 update
	function updateAreaTooltip(layer) {

        let latlng = &quot;&quot;;
        let content = &quot;&quot;;
        
        if(layerType == &quot;polyline&quot;){
        	latlng = layer.getCenter();
        	let coords = layer.getLatLngs();
			let length = 0;
			for (let i = 0; i &amp;lt; coords.length - 1; i++) {
				length += coords[i].distanceTo(coords[i + 1]);
			}
			console.log(coords[coords.length - 1]);
		    
			//반올림
			let totalMeter = meterCalcurate(length);
			content = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' class='list-group-item list-group-item-action'&amp;gt;총거리 : &quot;+totalMeter+&quot;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;
			latlng = [coords[coords.length - 1].lat, coords[coords.length - 1].lng];
			
        }else if(layerType == &quot;polygon&quot;){
        	 let area = L.GeometryUtil.geodesicArea(layer.getLatLngs()[0]);
        	 area = area.toFixed(1).replace(/\B(?&amp;lt;!\.\d*)(?=(\d{3})+(?!\d))/g, &quot;,&quot;)+&quot;㎡&quot;;
        	 latlng = layer.getCenter();
        	 content = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' class='list-group-item list-group-item-action'&amp;gt;총면적 : &quot;+area+&quot;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;
        }
        
        else if(layerType == &quot;circle&quot;){
        	let radius = layer.getRadius().toFixed(1);
        	latlng = layer.getLatLng();
        	content = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' class='list-group-item list-group-item-action'&amp;gt;총반경 : &quot;+radius+&quot;m&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;
        }
   
        //ha -&amp;gt; 제곱미터로 변환후 소수 첫째자리까지 반올림후 3째자리까지 나누기
        layer.areaTooltip
            .setContent(content)
            .setLatLng(latlng);
        
        //polyline인경우 offset 처리
        if(layerType == &quot;polyline&quot;){
        	layer.areaTooltip.options.offset = [0,80];	
        }
    }
	
	//왼쪽마우스 클릭
	function leftMouseClick(e){
		
		if(!contextMenuFlag){
			//선
			if(layerType == &quot;polyline&quot;){
				lineArray.push(e.latlng);
				console.log(lineArray);
				
				let length = lineArray.length;
				
				const polyLineIcon = L.icon({
				    iconUrl: '/images/start-marker.png',

				    iconSize:     [30, 30], // size of the icon
				});

				drawlon = lineArray[length-1].lat;
				drawlat = lineArray[length-1].lng;
				
				polyLineMarker = new L.marker([drawlon, drawlat], {
					draggable: false,
					autoPan: false,
					icon: polyLineIcon
				});
				
				polyLineMarker.addTo(leafletMap);
				
				if(length &amp;gt; 1){
					let distance = lineArray[length-2].distanceTo(lineArray[length-1]);
					
					//미터 -&amp;gt; 키로미터로 변환
					distance = meterCalcurate(distance);
					
					polyLineMarker.bindTooltip(distance, {direction: 'top',noWrap: true, opacity: 0.9, permanent: true}).openTooltip();
				}	
			}
		}
	}
	
	// m -&amp;gt; km로 계산
	function meterCalcurate(distance){
		distance = Math.round(distance);
		
		if(distance &amp;gt;= 1000){
			distance = (distance / 1000).toFixed(2) + &quot;km&quot;;
		}else{
			distance = distance + &quot;m&quot;;
		}
		
		return distance;
	}
	
	//오른쪽 마우스 클릭
	function rightMouseClick(e) {
		
	    const lon = e.latlng.lat;
	    const lat = e.latlng.lng;
	    
	    const popDiv = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='startBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;출발&amp;lt;/a&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='finishBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;도착&amp;lt;/a&amp;gt;&amp;lt;a href='javascript:void(0);' onclick='distanceMeasure()' class='list-group-item list-group-item-action'&amp;gt;거리측정&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;;
  
	    console.log(&quot;팝업 lon : &quot; + lon, &quot;lat : &quot; + lat)
	    
	    //contextMenuFlag가 true일때만 팝업 show
	    if(contextMenuFlag){
	    	//오른쪽마우스 클릭이벤트
			const popup = L.popup()
		    .setLatLng([lon, lat])
		    .setContent(popDiv)
		    .openOn(leafletMap);
	    }
	}
	
	//출발버튼 클릭
	function startBtnClicked(lon, lat){
		if(startMarker != &quot;&quot;){
			startMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const startIcon = L.icon({
		    iconUrl: '/images/start-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});

		startMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: startIcon
		});
		
		startMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(startMarker);
	}
	
	//도착버튼 클릭
	function finishBtnClicked(lon, lat){
		
		if(finishMarker != &quot;&quot;){
			finishMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const finishIcon = L.icon({
		    iconUrl: '/images/finish-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});
		
		finishMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: finishIcon
		});
		finishMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(finishMarker);
	}
	
	//마커 마우스 이벤트
	function markerMouseEvent(marker){
		marker.on('mouseover', function (e) {
			marker.bindTooltip(&quot;끌어서 이동&quot;, {direction: 'top',noWrap: true, opacity: 0.9}).openTooltip();
			//class명 추가
// 			marker._icon.classList.add(&quot;btn__close&quot;);
        });
        marker.on('mouseout', function (e) {
//         	marker._icon.classList.remove(&quot;btn__close&quot;);
        });
        
        marker.on('click', function (e) {
        	marker.remove();
        });
	}
	
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- updateTooltip에서 circle일경우 반경을 구해서 1의자리까지 반올림후 표현을 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cc5uhi/btrVtUcOf3e/rW7na61ybGQkmrhSH9WqeK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cc5uhi/btrVtUcOf3e/rW7na61ybGQkmrhSH9WqeK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cc5uhi/btrVtUcOf3e/rW7na61ybGQkmrhSH9WqeK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cc5uhi/btrVtUcOf3e/rW7na61ybGQkmrhSH9WqeK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;939&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>gis/LeafletJs</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/735</guid>
      <comments>https://aamoos.tistory.com/735#entry735comment</comments>
      <pubDate>Thu, 5 Jan 2023 17:02:25 +0900</pubDate>
    </item>
    <item>
      <title>[LeafletJs] Leaflet Draw Polygon 면적 재기</title>
      <link>https://aamoos.tistory.com/734</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이번장에서는 Leaflet Draw 기능중에 polygon으로 그렸을때 해당 총 면적을 tooltip으로 표현하는것을 해보겠습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1123&quot; data-origin-height=&quot;455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lvic3/btrVsoLCYBY/13K2HfEgTSmuIcczU2Tnh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lvic3/btrVsoLCYBY/13K2HfEgTSmuIcczU2Tnh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lvic3/btrVsoLCYBY/13K2HfEgTSmuIcczU2Tnh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flvic3%2FbtrVsoLCYBY%2F13K2HfEgTSmuIcczU2Tnh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1123&quot; height=&quot;455&quot; data-origin-width=&quot;1123&quot; data-origin-height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 네이버 지도에서 면적기능을 클릭하면 polygon을 그리면 총면적을 볼수 있습니다. 해당 기능을 구현해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672895642070&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%@ page contentType=&quot;text/html; charset=utf-8&quot; pageEncoding=&quot;utf-8&quot; %&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;main role=&quot;main&quot; class=&quot;inner cover&quot;&amp;gt;
        &amp;lt;h1 class=&quot;cover-heading&quot;&amp;gt;LeafletJs Tutorial&amp;lt;/h1&amp;gt;
&amp;lt;!-- 		&amp;lt;p class=&quot;lead&quot;&amp;gt;- openstreetmap&amp;lt;/p&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;br&amp;gt; --&amp;gt;
		&amp;lt;p class=&quot;lead&quot;&amp;gt;polygon 면적 측정&amp;lt;/p&amp;gt;
		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/main&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;script&amp;gt;
	let lon = &quot;&quot;;
	let lat = &quot;&quot;;
	let leafletMap = &quot;&quot;;
	let startMarker = &quot;&quot;;
	let finishMarker = &quot;&quot;;
	let contextMenuFlag = true;
	let lineArray = [];
	let drawControl = &quot;&quot;;
	let layerType = &quot;&quot;;
	let polygon = new L.featureGroup();
	let drawlon = &quot;&quot;;
	let drawlat = &quot;&quot;;
	let drawnItems = &quot;&quot;;
	let layer = &quot;&quot;;
	
	//apikey
	const apiKey = $(&quot;#apiKey&quot;).val();
	
	$(function(){
		init();
	});
	
	function init(){
		navigator.geolocation.getCurrentPosition(onGeoOk,onGeoError);
	}
	
	//현재 좌표값 가져오기
	function onGeoOk(position){
	    lon = position.coords.latitude;
	    lat = position.coords.longitude;
	    console.log(&quot;You live in&quot;, lon, lat);
	    
	    //wmts 가져오기
	    vworldWmts();
	}
	
	function onGeoError(){
	    alert(&quot;Can't find you. No weather for you.&quot;);
	}

	function vworldWmts(){
		//leaflet 지도 띄우기 (EPSG : 4326)
		leafletMap = L.map('map').setView([lon, lat], 15)
		
		L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;).addTo(leafletMap);
		
		//해당 플래그가 true일때만 동작
		//맵에 오른쪽마우스 클릭이벤트
		leafletMap.on('click', leftMouseClick);
		leafletMap.on('contextmenu', rightMouseClick);
		
		//layer 변경
		changeLayer(leafletMap)
		
		//leaflet 그리기
		initLeafletDraw(leafletMap)
		
	}
	
	//layer 변경
	function changeLayer(leafletMap){
		drawnItems = L.featureGroup().addTo(leafletMap);
		const vworld = L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;, { maxZoom: 18});
		//레이어 변경
		L.control.layers({
			'vworld' : vworld.addTo(leafletMap),
			'osm': L.tileLayer(&quot;http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&quot;, {
				attribution: 'osm'
			}),
			'google': L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&amp;amp;gl=cn&amp;amp;x={x}&amp;amp;y={y}&amp;amp;z={z}', {
				attribution: 'google'
			})
		}, { 'drawlayer': drawnItems }, { position: 'topleft', collapsed: false }).addTo(leafletMap);		
	}
	
	//leaflet 그리기
	function initLeafletDraw(leafletMap){
		
		drawControl = new L.Control.Draw({
			edit : false,
// 			edit: {
// 				featureGroup: drawnItems,
// 				poly: {
// 					allowIntersection: false,
// 					remove: false
// 				}
// 			},
			
			draw: {
				polygon: {
					allowIntersection: false,
					showArea: true
				}
			}
		});
		
        leafletMap.addControl(drawControl);

		//created
		leafletMap.on(L.Draw.Event.CREATED, function (event) {
			layer = event.layer;
			layerType = event.layerType;
			drawnItems.addLayer(layer);
			//polyline 일경우
			if(layerType == 'polyline') {
				createAreaTooltip(layer);
		    }else if(layerType == 'polygon'){
		    	createAreaTooltip(layer);
		    }  
		});
		
		//그리기 도구 열때
		leafletMap.on('draw:toolbaropened', function (e) {
			
			contextMenuFlag = false;
		});
		
		//그리기 도구 닫힐때
		leafletMap.on('draw:toolbarclosed', function (e) {
			contextMenuFlag = true;
		});
		
		//도구모음 클릭시 layerType
		leafletMap.on('draw:drawstart', function (e) {
			layerType = e.layerType;
		});
	}
	
	//폴리곤 tooltip 생성
	function createAreaTooltip(layer) {
		console.log(layer.areaTooltip);
        if(layer.areaTooltip) {
            return;
        }

        layer.areaTooltip = L.tooltip({
            permanent: true,
            direction: 'center',
            className: 'area-tooltip'
        });

        layer.on('remove', function(event) {
            layer.areaTooltip.remove();
        });

        layer.on('add', function(event) {
            updateAreaTooltip(layer);
            layer.areaTooltip.addTo(leafletMap);
        });

        if(leafletMap.hasLayer(layer)) {
            updateAreaTooltip(layer);
            console.log(layer.areaTooltip);
            //얘가문제
            layer.areaTooltip.addTo(leafletMap);
        }
    }
	
	//폴리곤 영역 update
	function updateAreaTooltip(layer) {
        let latlng = &quot;&quot;;
        let content = &quot;&quot;;
        
        if(layerType == &quot;polyline&quot;){
        	latlng = layer.getCenter();
        	let coords = layer.getLatLngs();
			let length = 0;
			for (let i = 0; i &amp;lt; coords.length - 1; i++) {
				length += coords[i].distanceTo(coords[i + 1]);
			}
			console.log(coords[coords.length - 1]);
		    
			//반올림
			let totalMeter = meterCalcurate(length);
			content = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' class='list-group-item list-group-item-action'&amp;gt;총거리 : &quot;+totalMeter+&quot;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;
			latlng = [coords[coords.length - 1].lat, coords[coords.length - 1].lng];
			
        }else if(layerType == &quot;polygon&quot;){
        	 let area = L.GeometryUtil.geodesicArea(layer.getLatLngs()[0]);
        	 
        	 area = area.toFixed(1).replace(/\B(?&amp;lt;!\.\d*)(?=(\d{3})+(?!\d))/g, &quot;,&quot;)+&quot;㎡&quot;;

        	 latlng = layer.getCenter();
        	 content = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' class='list-group-item list-group-item-action'&amp;gt;총면적 : &quot;+area+&quot;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;
        }
     
        //ha -&amp;gt; 제곱미터로 변환후 소수 첫째자리까지 반올림후 3째자리까지 나누기
        layer.areaTooltip
            .setContent(content)
            .setLatLng(latlng);
        
        //polyline인경우 offset 처리
        if(layerType == &quot;polyline&quot;){
        	layer.areaTooltip.options.offset = [0,80];	
        }
        
    }
	
	//왼쪽마우스 클릭
	function leftMouseClick(e){
		
		if(!contextMenuFlag){
			//선
			if(layerType == &quot;polyline&quot;){
				lineArray.push(e.latlng);
				console.log(lineArray);
				
				let length = lineArray.length;
				
				const polyLineIcon = L.icon({
				    iconUrl: '/images/start-marker.png',

				    iconSize:     [30, 30], // size of the icon
				});

				drawlon = lineArray[length-1].lat;
				drawlat = lineArray[length-1].lng;
				
				polyLineMarker = new L.marker([drawlon, drawlat], {
					draggable: false,
					autoPan: false,
					icon: polyLineIcon
				});
				
				polyLineMarker.addTo(leafletMap);
				
				if(length &amp;gt; 1){
					let distance = lineArray[length-2].distanceTo(lineArray[length-1]);
					
					//미터 -&amp;gt; 키로미터로 변환
					distance = meterCalcurate(distance);
					
					polyLineMarker.bindTooltip(distance, {direction: 'top',noWrap: true, opacity: 0.9, permanent: true}).openTooltip();
				}	
			}
		}
	}
	
	// m -&amp;gt; km로 계산
	function meterCalcurate(distance){
		distance = Math.round(distance);
		
		if(distance &amp;gt;= 1000){
			distance = (distance / 1000).toFixed(2) + &quot;km&quot;;
		}else{
			distance = distance + &quot;m&quot;;
		}
		
		return distance;
	}
	
	//오른쪽 마우스 클릭
	function rightMouseClick(e) {
		
	    const lon = e.latlng.lat;
	    const lat = e.latlng.lng;
	    
	    const popDiv = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='startBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;출발&amp;lt;/a&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='finishBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;도착&amp;lt;/a&amp;gt;&amp;lt;a href='javascript:void(0);' onclick='distanceMeasure()' class='list-group-item list-group-item-action'&amp;gt;거리측정&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;;
  
	    console.log(&quot;팝업 lon : &quot; + lon, &quot;lat : &quot; + lat)
	    
	    //contextMenuFlag가 true일때만 팝업 show
	    if(contextMenuFlag){
	    	//오른쪽마우스 클릭이벤트
			const popup = L.popup()
		    .setLatLng([lon, lat])
		    .setContent(popDiv)
		    .openOn(leafletMap);
	    }
	}
	
	//출발버튼 클릭
	function startBtnClicked(lon, lat){
		if(startMarker != &quot;&quot;){
			startMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const startIcon = L.icon({
		    iconUrl: '/images/start-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});

		startMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: startIcon
		});
		
		startMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(startMarker);
	}
	
	//도착버튼 클릭
	function finishBtnClicked(lon, lat){
		
		if(finishMarker != &quot;&quot;){
			finishMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const finishIcon = L.icon({
		    iconUrl: '/images/finish-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});
		
		finishMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: finishIcon
		});
		finishMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(finishMarker);
	}
	
	//마커 마우스 이벤트
	function markerMouseEvent(marker){
		marker.on('mouseover', function (e) {
			marker.bindTooltip(&quot;끌어서 이동&quot;, {direction: 'top',noWrap: true, opacity: 0.9}).openTooltip();
			//class명 추가
// 			marker._icon.classList.add(&quot;btn__close&quot;);
        });
        marker.on('mouseout', function (e) {
//         	marker._icon.classList.remove(&quot;btn__close&quot;);
        });
        
        marker.on('click', function (e) {
        	marker.remove();
        });
	}
	
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- polygon으로 그릴때 해당 area를 소수점 첫째자리로 반올림하고 제곱미터를 붙였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GqRnS/btrVvk95DCV/bCZO6ILmOkKARNBt2TdO6K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GqRnS/btrVvk95DCV/bCZO6ILmOkKARNBt2TdO6K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GqRnS/btrVvk95DCV/bCZO6ILmOkKARNBt2TdO6K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/GqRnS/btrVvk95DCV/bCZO6ILmOkKARNBt2TdO6K/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;939&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>gis/LeafletJs</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/734</guid>
      <comments>https://aamoos.tistory.com/734#entry734comment</comments>
      <pubDate>Thu, 5 Jan 2023 14:20:36 +0900</pubDate>
    </item>
    <item>
      <title>[LeafletJs] Leaflet Draw Poly line 거리 측정</title>
      <link>https://aamoos.tistory.com/733</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이번장에서는 Leaflet Draw 기능중에 Poly Line으로 지도에 표시할때마다 거리와 총거리를 표현해보겠습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QGxK8/btrVt5dBp5k/xSXAaRcEELPjzcEWw6pvM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QGxK8/btrVt5dBp5k/xSXAaRcEELPjzcEWw6pvM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QGxK8/btrVt5dBp5k/xSXAaRcEELPjzcEWw6pvM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQGxK8%2FbtrVt5dBp5k%2FxSXAaRcEELPjzcEWw6pvM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;742&quot; height=&quot;486&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 네이버 지도에 보면 거리측정을 하면 마우스로 찍은 점마다 미터를 표시를 하는데 해당 기능을 구현해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전체소스&lt;/h3&gt;
&lt;pre id=&quot;code_1672894811523&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%@ page contentType=&quot;text/html; charset=utf-8&quot; pageEncoding=&quot;utf-8&quot; %&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;main role=&quot;main&quot; class=&quot;inner cover&quot;&amp;gt;
        &amp;lt;h1 class=&quot;cover-heading&quot;&amp;gt;LeafletJs Tutorial&amp;lt;/h1&amp;gt;
&amp;lt;!-- 		&amp;lt;p class=&quot;lead&quot;&amp;gt;- openstreetmap&amp;lt;/p&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;br&amp;gt; --&amp;gt;
		&amp;lt;p class=&quot;lead&quot;&amp;gt;leaflet draw 예제&amp;lt;/p&amp;gt;
		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/main&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;script&amp;gt;
	let lon = &quot;&quot;;
	let lat = &quot;&quot;;
	let leafletMap = &quot;&quot;;
	let startMarker = &quot;&quot;;
	let finishMarker = &quot;&quot;;
	let contextMenuFlag = true;
	let lineArray = [];
	let drawControl = &quot;&quot;;
	let layerType = &quot;&quot;;
	let polygon = new L.featureGroup();
	let drawlon = &quot;&quot;;
	let drawlat = &quot;&quot;;
	let drawnItems = &quot;&quot;;
	let layer = &quot;&quot;;
	
	//apikey
	const apiKey = $(&quot;#apiKey&quot;).val();
	
	$(function(){
		init();
	});
	
	function init(){
		navigator.geolocation.getCurrentPosition(onGeoOk,onGeoError);
	}
	
	//현재 좌표값 가져오기
	function onGeoOk(position){
	    lon = position.coords.latitude;
	    lat = position.coords.longitude;
	    console.log(&quot;You live in&quot;, lon, lat);
	    
	    //wmts 가져오기
	    vworldWmts();
	}
	
	function onGeoError(){
	    alert(&quot;Can't find you. No weather for you.&quot;);
	}

	function vworldWmts(){
		//leaflet 지도 띄우기 (EPSG : 4326)
		leafletMap = L.map('map').setView([lon, lat], 15)
		
		L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;).addTo(leafletMap);
		
		//해당 플래그가 true일때만 동작
		//맵에 오른쪽마우스 클릭이벤트
		leafletMap.on('click', leftMouseClick);
		leafletMap.on('contextmenu', rightMouseClick);
		
		//layer 변경
		changeLayer(leafletMap)
		
		//leaflet 그리기
		initLeafletDraw(leafletMap)
		
	}
	
	//layer 변경
	function changeLayer(leafletMap){
		drawnItems = L.featureGroup().addTo(leafletMap);
		const vworld = L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;, { maxZoom: 18});
		//레이어 변경
		L.control.layers({
			'vworld' : vworld.addTo(leafletMap),
			'osm': L.tileLayer(&quot;http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&quot;, {
				attribution: 'osm'
			}),
			'google': L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&amp;amp;gl=cn&amp;amp;x={x}&amp;amp;y={y}&amp;amp;z={z}', {
				attribution: 'google'
			})
		}, { 'drawlayer': drawnItems }, { position: 'topleft', collapsed: false }).addTo(leafletMap);		
	}
	
	//leaflet 그리기
	function initLeafletDraw(leafletMap){
		
		drawControl = new L.Control.Draw({
			edit : false,
// 			edit: {
// 				featureGroup: drawnItems,
// 				poly: {
// 					allowIntersection: false,
// 					remove: false
// 				}
// 			},
			
			draw: {
				polygon: {
					allowIntersection: false,
					showArea: true
				}
			}
		});
		
        leafletMap.addControl(drawControl);

		//created
		leafletMap.on(L.Draw.Event.CREATED, function (event) {
			layer = event.layer;
			layerType = event.layerType;
			drawnItems.addLayer(layer);
			//polyline 일경우
			if(layerType == 'polyline') {
				createAreaTooltip(layer);
		    }
		});
		
		//그리기 도구 열때
		leafletMap.on('draw:toolbaropened', function (e) {
			
			contextMenuFlag = false;
		});
		
		//그리기 도구 닫힐때
		leafletMap.on('draw:toolbarclosed', function (e) {
			contextMenuFlag = true;
		});
		
		//도구모음 클릭시 layerType
		leafletMap.on('draw:drawstart', function (e) {
			layerType = e.layerType;
		});
	}
	
	//tooltip 생성
	function createAreaTooltip(layer) {
		console.log(layer.areaTooltip);
        if(layer.areaTooltip) {
            return;
        }

        layer.areaTooltip = L.tooltip({
            permanent: true,
            direction: 'center',
            className: 'area-tooltip'
        });

        layer.on('remove', function(event) {
            layer.areaTooltip.remove();
        });

        layer.on('add', function(event) {
            updateAreaTooltip(layer);
            layer.areaTooltip.addTo(leafletMap);
        });

        if(leafletMap.hasLayer(layer)) {
            updateAreaTooltip(layer);
            console.log(layer.areaTooltip);
            //얘가문제
            layer.areaTooltip.addTo(leafletMap);
        }
    }
	
	//툴팁 content 업데이트
	function updateAreaTooltip(layer) {
        let latlng = &quot;&quot;;
        let content = &quot;&quot;;
        
        if(layerType == &quot;polyline&quot;){
        	latlng = layer.getCenter();
        	let coords = layer.getLatLngs();
			let length = 0;
			for (let i = 0; i &amp;lt; coords.length - 1; i++) {
				length += coords[i].distanceTo(coords[i + 1]);
			}
			console.log(coords[coords.length - 1]);
		    
			//반올림
			let totalMeter = meterCalcurate(length);
			content = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' class='list-group-item list-group-item-action'&amp;gt;총거리 : &quot;+totalMeter+&quot;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;
			latlng = [coords[coords.length - 1].lat, coords[coords.length - 1].lng];
			
        }
        
        //ha -&amp;gt; 제곱미터로 변환후 소수 첫째자리까지 반올림후 3째자리까지 나누기
        layer.areaTooltip
            .setContent(content)
            .setLatLng(latlng);
        
        //polyline인경우 offset 처리
        if(layerType == &quot;polyline&quot;){
        	layer.areaTooltip.options.offset = [0,80];	
        }
    }
	
	//왼쪽마우스 클릭
	function leftMouseClick(e){
		
		if(!contextMenuFlag){
			//선
			if(layerType == &quot;polyline&quot;){
				lineArray.push(e.latlng);
				console.log(lineArray);
				
				let length = lineArray.length;
				
				const polyLineIcon = L.icon({
				    iconUrl: '/images/start-marker.png',

				    iconSize:     [30, 30], // size of the icon
				});

				drawlon = lineArray[length-1].lat;
				drawlat = lineArray[length-1].lng;
				
				polyLineMarker = new L.marker([drawlon, drawlat], {
					draggable: false,
					autoPan: false,
					icon: polyLineIcon
				});
				
				polyLineMarker.addTo(leafletMap);
				
				if(length &amp;gt; 1){
					let distance = lineArray[length-2].distanceTo(lineArray[length-1]);
					
					//미터 -&amp;gt; 키로미터로 변환
					distance = meterCalcurate(distance);
					
					polyLineMarker.bindTooltip(distance, {direction: 'top',noWrap: true, opacity: 0.9, permanent: true}).openTooltip();
				}	
			}
		}
	}
	
	// m -&amp;gt; km로 계산
	function meterCalcurate(distance){
		distance = Math.round(distance);
		
		if(distance &amp;gt;= 1000){
			distance = (distance / 1000).toFixed(2) + &quot;km&quot;;
		}else{
			distance = distance + &quot;m&quot;;
		}
		
		return distance;
	}
	
	//오른쪽 마우스 클릭
	function rightMouseClick(e) {
		
	    const lon = e.latlng.lat;
	    const lat = e.latlng.lng;
	    
	    const popDiv = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='startBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;출발&amp;lt;/a&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='finishBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;도착&amp;lt;/a&amp;gt;&amp;lt;a href='javascript:void(0);' onclick='distanceMeasure()' class='list-group-item list-group-item-action'&amp;gt;거리측정&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;;
  
	    console.log(&quot;팝업 lon : &quot; + lon, &quot;lat : &quot; + lat)
	    
	    //contextMenuFlag가 true일때만 팝업 show
	    if(contextMenuFlag){
	    	//오른쪽마우스 클릭이벤트
			const popup = L.popup()
		    .setLatLng([lon, lat])
		    .setContent(popDiv)
		    .openOn(leafletMap);
	    }
	}
	
	//출발버튼 클릭
	function startBtnClicked(lon, lat){
		if(startMarker != &quot;&quot;){
			startMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const startIcon = L.icon({
		    iconUrl: '/images/start-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});

		startMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: startIcon
		});
		
		startMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(startMarker);
	}
	
	//도착버튼 클릭
	function finishBtnClicked(lon, lat){
		
		if(finishMarker != &quot;&quot;){
			finishMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const finishIcon = L.icon({
		    iconUrl: '/images/finish-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});
		
		finishMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: finishIcon
		});
		finishMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(finishMarker);
	}
	
	//마커 마우스 이벤트
	function markerMouseEvent(marker){
		marker.on('mouseover', function (e) {
			marker.bindTooltip(&quot;끌어서 이동&quot;, {direction: 'top',noWrap: true, opacity: 0.9}).openTooltip();
			//class명 추가
// 			marker._icon.classList.add(&quot;btn__close&quot;);
        });
        marker.on('mouseout', function (e) {
//         	marker._icon.classList.remove(&quot;btn__close&quot;);
        });
        
        marker.on('click', function (e) {
        	marker.remove();
        });
	}
	
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저는 왼쪽마우스를 클릭할때마다 마커를 찍고 해당 마커 좌표를 배열에 넣고, 마커가 2개이상일경우에 가장 마지막으로 찍은 마커와 그 직전에 찍은 마커의 좌표사이의 거리를 계산해서 popup으로 표시를 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 draw:created를 보면 그림그리기 완료시 레이어에 찍힌 마커들 사이의 거리를 합쳐서 총거리를 구해서 팝업으로 표현하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OTDta/btrVtGliNY6/gtnMkj87dYrpvmNOkuy8E0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OTDta/btrVtGliNY6/gtnMkj87dYrpvmNOkuy8E0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OTDta/btrVtGliNY6/gtnMkj87dYrpvmNOkuy8E0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/OTDta/btrVtGliNY6/gtnMkj87dYrpvmNOkuy8E0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;939&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>gis/LeafletJs</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/733</guid>
      <comments>https://aamoos.tistory.com/733#entry733comment</comments>
      <pubDate>Thu, 5 Jan 2023 14:06:17 +0900</pubDate>
    </item>
    <item>
      <title>[LeafletJs] Leaflet draw 사용</title>
      <link>https://aamoos.tistory.com/732</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이번 포스팅에서는 Leaflet Draw 라이브러리를 사용해보겠습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leaflet.github.io/Leaflet.draw/docs/examples/full.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leaflet.github.io/Leaflet.draw/docs/examples/full.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1672894154899&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Leaflet.draw vector editing handlers&quot; data-og-description=&quot;&quot; data-og-host=&quot;leaflet.github.io&quot; data-og-source-url=&quot;https://leaflet.github.io/Leaflet.draw/docs/examples/full.html&quot; data-og-url=&quot;https://leaflet.github.io/Leaflet.draw/docs/examples/full.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://leaflet.github.io/Leaflet.draw/docs/examples/full.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leaflet.github.io/Leaflet.draw/docs/examples/full.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Leaflet.draw vector editing handlers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leaflet.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 사이트에 있는 리소스 파일이랑 소스를 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7YF46/btrVpvdwwJ0/XIUDpUUf3AGSDBo8cTVKJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7YF46/btrVpvdwwJ0/XIUDpUUf3AGSDBo8cTVKJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7YF46/btrVpvdwwJ0/XIUDpUUf3AGSDBo8cTVKJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7YF46%2FbtrVpvdwwJ0%2FXIUDpUUf3AGSDBo8cTVKJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;276&quot; height=&quot;559&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스코드&lt;/h2&gt;
&lt;pre id=&quot;code_1672894232091&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%@ page contentType=&quot;text/html; charset=utf-8&quot; pageEncoding=&quot;utf-8&quot; %&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;main role=&quot;main&quot; class=&quot;inner cover&quot;&amp;gt;
        &amp;lt;h1 class=&quot;cover-heading&quot;&amp;gt;LeafletJs Tutorial&amp;lt;/h1&amp;gt;
&amp;lt;!-- 		&amp;lt;p class=&quot;lead&quot;&amp;gt;- openstreetmap&amp;lt;/p&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;br&amp;gt; --&amp;gt;
		&amp;lt;p class=&quot;lead&quot;&amp;gt;leaflet draw 예제&amp;lt;/p&amp;gt;
		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/main&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;script&amp;gt;
	let lon = &quot;&quot;;
	let lat = &quot;&quot;;
	let leafletMap = &quot;&quot;;
	let startMarker = &quot;&quot;;
	let finishMarker = &quot;&quot;;
	let contextMenuFlag = true;
	let drawControl = &quot;&quot;;
	
	//apikey
	const apiKey = $(&quot;#apiKey&quot;).val();
	
	$(function(){
		init();
	});
	
	function init(){
		navigator.geolocation.getCurrentPosition(onGeoOk,onGeoError);
	}
	
	//현재 좌표값 가져오기
	function onGeoOk(position){
	    lon = position.coords.latitude;
	    lat = position.coords.longitude;
	    console.log(&quot;You live in&quot;, lon, lat);
	    
	    //wmts 가져오기
	    vworldWmts();
	}
	
	function onGeoError(){
	    alert(&quot;Can't find you. No weather for you.&quot;);
	}

	function vworldWmts(){
		//leaflet 지도 띄우기 (EPSG : 4326)
		leafletMap = L.map('map').setView([lon, lat], 15)
		
		L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;).addTo(leafletMap);
		
		//해당 플래그가 true일때만 동작
		//맵에 오른쪽마우스 클릭이벤트
		leafletMap.on('contextmenu', rightMouseClick);
		
		//layer 변경
		changeLayer(leafletMap)
		
		//leaflet 그리기
		initLeafletDraw(leafletMap)
		
	}
	
	//layer 변경
	function changeLayer(leafletMap){
		
		vworld = L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;, { maxZoom: 18}),
		drawnItems = L.featureGroup().addTo(leafletMap);
		
		//레이어 변경
		L.control.layers({
			'vworld' : vworld.addTo(leafletMap),
			'osm': L.tileLayer(&quot;http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&quot;, {
				attribution: 'osm'
			}),
			'google': L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&amp;amp;gl=cn&amp;amp;x={x}&amp;amp;y={y}&amp;amp;z={z}', {
				attribution: 'google'
			})
		}, { 'drawlayer': drawnItems }, { position: 'topleft', collapsed: false }).addTo(leafletMap);		
	}
	
	//leaflet 그리기
	function initLeafletDraw(leafletMap){
		
		drawControl = new L.Control.Draw({
			edit : false,
// 			edit: {
// 				featureGroup: drawnItems,
// 				poly: {
// 					allowIntersection: false,
// 					remove: false
// 				}
// 			},
			
			draw: {
				polygon: {
					allowIntersection: false,
					showArea: true
				}
			}
		});
		
        leafletMap.addControl(drawControl);
        
        leafletMap.on(L.Draw.Event.CREATED, function (event) {
            var layer = event.layer;

            drawnItems.addLayer(layer);
        });
        
	}
	
	//오른쪽 마우스 클릭
	function rightMouseClick(e) {
		
	    const lon = e.latlng.lat;
	    const lat = e.latlng.lng;
	    
	    const popDiv = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='startBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;출발&amp;lt;/a&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='finishBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;도착&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;;
  
	    console.log(&quot;팝업 lon : &quot; + lon, &quot;lat : &quot; + lat)
	    
	    //contextMenuFlag가 true일때만 팝업 show
	    if(contextMenuFlag){
	    	//오른쪽마우스 클릭이벤트
			const popup = L.popup()
		    .setLatLng([lon, lat])
		    .setContent(popDiv)
		    .openOn(leafletMap);
	    }
	}
	
	//출발버튼 클릭
	function startBtnClicked(lon, lat){
		if(startMarker != &quot;&quot;){
			startMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const startIcon = L.icon({
		    iconUrl: '/images/start-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});

		startMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: startIcon
		});
		
		startMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(startMarker);
	}
	
	//도착버튼 클릭
	function finishBtnClicked(lon, lat){
		
		if(finishMarker != &quot;&quot;){
			finishMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const finishIcon = L.icon({
		    iconUrl: '/images/finish-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});
		
		finishMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: finishIcon
		});
		finishMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(finishMarker);
	}
	
	//마커 마우스 이벤트
	function markerMouseEvent(marker){
		marker.on('mouseover', function (e) {
			marker.bindTooltip(&quot;끌어서 이동&quot;, {direction: 'top',noWrap: true, opacity: 0.9}).openTooltip();
			//class명 추가
// 			marker._icon.classList.add(&quot;btn__close&quot;);
        });
        marker.on('mouseout', function (e) {
//         	marker._icon.classList.remove(&quot;btn__close&quot;);
        });
        
        marker.on('click', function (e) {
        	marker.remove();
        });
	}
	
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kP7bR/btrVoHleJJ5/6yJmikEa8h0kzKobt08l31/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kP7bR/btrVoHleJJ5/6yJmikEa8h0kzKobt08l31/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kP7bR/btrVoHleJJ5/6yJmikEa8h0kzKobt08l31/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/kP7bR/btrVoHleJJ5/6yJmikEa8h0kzKobt08l31/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;939&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>gis/LeafletJs</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/732</guid>
      <comments>https://aamoos.tistory.com/732#entry732comment</comments>
      <pubDate>Thu, 5 Jan 2023 13:54:11 +0900</pubDate>
    </item>
    <item>
      <title>[leafletjs] 배경지도 레이어 변경</title>
      <link>https://aamoos.tistory.com/731</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이번 포스팅에서는 왼쪽 상단에 팝업하나에 레이어를 선택함에 따라 해당 레이어로 변경이 되는것을 해보겠습니다. (vworld를 클릭하면 vworld wmts, osm을 클릭하면 openstree map, google을 클릭하면 google map)&lt;/blockquote&gt;
&lt;pre id=&quot;code_1672885784202&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%@ page contentType=&quot;text/html; charset=utf-8&quot; pageEncoding=&quot;utf-8&quot; %&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;main role=&quot;main&quot; class=&quot;inner cover&quot;&amp;gt;
        &amp;lt;h1 class=&quot;cover-heading&quot;&amp;gt;LeafletJs Tutorial&amp;lt;/h1&amp;gt;
&amp;lt;!-- 		&amp;lt;p class=&quot;lead&quot;&amp;gt;- openstreetmap&amp;lt;/p&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;br&amp;gt; --&amp;gt;
		&amp;lt;p class=&quot;lead&quot;&amp;gt;- 레이어변경&amp;lt;/p&amp;gt;
		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/main&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;script&amp;gt;
	let lon = &quot;&quot;;
	let lat = &quot;&quot;;
	let leafletMap = &quot;&quot;;
	let startMarker = &quot;&quot;;
	let finishMarker = &quot;&quot;;
	let contextMenuFlag = true;
	
	//apikey
	const apiKey = $(&quot;#apiKey&quot;).val();
	
	$(function(){
		init();
	});
	
	function init(){
		navigator.geolocation.getCurrentPosition(onGeoOk,onGeoError);
	}
	
	//현재 좌표값 가져오기
	function onGeoOk(position){
	    lon = position.coords.latitude;
	    lat = position.coords.longitude;
	    console.log(&quot;You live in&quot;, lon, lat);
	    
	    //wmts 가져오기
	    vworldWmts();
	}
	
	//내좌표값 가져올때 error
	function onGeoError(){
	    alert(&quot;Can't find you. No weather for you.&quot;);
	}

	//vowld wmts 배경지도
	function vworldWmts(){
		//leaflet 지도 띄우기 (EPSG : 4326)
		leafletMap = L.map('map').setView([lon, lat], 15)
		
		L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;).addTo(leafletMap);
		
		//해당 플래그가 true일때만 동작
		//맵에 오른쪽마우스 클릭이벤트
		leafletMap.on('contextmenu', rightMouseClick);
		
		
		changeLayer(leafletMap)
	}
	
	//layer 변경
	function changeLayer(leafletMap){
		
		vworld = L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;, { maxZoom: 18}),
		drawnItems = L.featureGroup().addTo(leafletMap);
		
		//레이어 변경
		L.control.layers({
			'vworld' : vworld.addTo(leafletMap),
			'osm': L.tileLayer(&quot;http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&quot;, {
				attribution: 'osm'
			}),
			'google': L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&amp;amp;gl=cn&amp;amp;x={x}&amp;amp;y={y}&amp;amp;z={z}', {
				attribution: 'google'
			})
		}, { 'drawlayer': drawnItems }, { position: 'topleft', collapsed: false }).addTo(leafletMap);		
	}
	
	//오른쪽 마우스 클릭
	function rightMouseClick(e) {
		
	    const lon = e.latlng.lat;
	    const lat = e.latlng.lng;
	    
	    const popDiv = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='startBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;출발&amp;lt;/a&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='finishBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;도착&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;;
  
	    console.log(&quot;팝업 lon : &quot; + lon, &quot;lat : &quot; + lat)
	    
	    //contextMenuFlag가 true일때만 팝업 show
	    if(contextMenuFlag){
	    	//오른쪽마우스 클릭이벤트
			const popup = L.popup()
		    .setLatLng([lon, lat])
		    .setContent(popDiv)
		    .openOn(leafletMap);
	    }
	}
	
	//출발버튼 클릭
	function startBtnClicked(lon, lat){
		if(startMarker != &quot;&quot;){
			startMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const startIcon = L.icon({
		    iconUrl: '/images/start-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});

		startMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: startIcon
		});
		
		startMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(startMarker);
	}
	
	//도착버튼 클릭
	function finishBtnClicked(lon, lat){
		
		if(finishMarker != &quot;&quot;){
			finishMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const finishIcon = L.icon({
		    iconUrl: '/images/finish-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});
		
		finishMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: finishIcon
		});
		finishMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(finishMarker);
	}
	
	//마커 마우스 이벤트
	function markerMouseEvent(marker){
		//마우스 오버
		marker.on('mouseover', function (e) {
			marker.bindTooltip(&quot;끌어서 이동&quot;, {direction: 'top',noWrap: true, opacity: 0.9}).openTooltip();
			//class명 추가
// 			marker._icon.classList.add(&quot;btn__close&quot;);
        });
		
		//마우스 아웃
        marker.on('mouseout', function (e) {
//         	marker._icon.classList.remove(&quot;btn__close&quot;);
        });
        
      //마우스 클릭
        marker.on('click', function (e) {
        	marker.remove();
        });
	}
	
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6R4t8/btrVqgmGJG4/GczIEY3pOmVfKIPLUEsiU1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6R4t8/btrVqgmGJG4/GczIEY3pOmVfKIPLUEsiU1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6R4t8/btrVqgmGJG4/GczIEY3pOmVfKIPLUEsiU1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/d6R4t8/btrVqgmGJG4/GczIEY3pOmVfKIPLUEsiU1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;939&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>gis/LeafletJs</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/731</guid>
      <comments>https://aamoos.tistory.com/731#entry731comment</comments>
      <pubDate>Thu, 5 Jan 2023 13:17:37 +0900</pubDate>
    </item>
    <item>
      <title>[leafletjs] wmts 배경지도에 오른쪽 마우스 클릭시 출발 도착 marker 찍기</title>
      <link>https://aamoos.tistory.com/730</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;저번에 wmts로 배경지도를 개발하였다면, 이번장에서는 해당 기능을 개발하려고 합니다.&lt;br /&gt;1. 지도에 오른쪽 마우스 클릭시 출발, 도착 팝업 표출&lt;br /&gt;2. 출발, 도착 버튼 누를경우 마커가 지도에 찍힘&lt;br /&gt;3. 찍힌 마커에 마우스 오버시 텍스트 문구 표현&lt;br /&gt;4. 찍힌 마커 드래그시 이동이 가능하게함&lt;br /&gt;5. 마커 클릭시 클릭한 마커 삭제&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스코드&lt;/h2&gt;
&lt;pre id=&quot;code_1672732852828&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%@ page contentType=&quot;text/html; charset=utf-8&quot; pageEncoding=&quot;utf-8&quot; %&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;main role=&quot;main&quot; class=&quot;inner cover&quot;&amp;gt;
        &amp;lt;h1 class=&quot;cover-heading&quot;&amp;gt;LeafletJs Tutorial&amp;lt;/h1&amp;gt;
&amp;lt;!-- 		&amp;lt;p class=&quot;lead&quot;&amp;gt;- openstreetmap&amp;lt;/p&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;br&amp;gt; --&amp;gt;
		&amp;lt;p class=&quot;lead&quot;&amp;gt;- 출발 도착 버튼 클릭시 마커찍기&amp;lt;/p&amp;gt;
		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/main&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;script&amp;gt;
	let lon = &quot;&quot;;
	let lat = &quot;&quot;;
	let leafletMap = &quot;&quot;;
	let startMarker = &quot;&quot;;
	let finishMarker = &quot;&quot;;
	const apiKey = $(&quot;#apiKey&quot;).val();
	
	$(function(){
		init();
	});
	
	function init(){
		navigator.geolocation.getCurrentPosition(onGeoOk,onGeoError);
	}
	
	//현재 좌표값 가져오기
	function onGeoOk(position){
	    lon = position.coords.latitude;
	    lat = position.coords.longitude;
	    console.log(&quot;You live in&quot;, lon, lat);
	    
	    //wmts 가져오기
	    vworldWmts();
	}
	
	//내좌표값 가져올때 error
	function onGeoError(){
	    alert(&quot;Can't find you. No weather for you.&quot;);
	}

	//vowld wmts 배경지도
	function vworldWmts(){
		
		//leaflet 지도 띄우기 (EPSG : 4326)
		leafletMap = L.map('map').setView([lon, lat], 15)
		
		L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;).addTo(leafletMap);
		
		//맵에 오른쪽마우스 클릭이벤트
		leafletMap.on('contextmenu', onMapClick);
	}
	
	//마우스 오른쪽 클릭시
	function onMapClick(e) {
	    lon = e.latlng.lat;
	    lat = e.latlng.lng;
	    
	    const popDiv = &quot;&amp;lt;div class='list-group'&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='startBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;출발&amp;lt;/a&amp;gt;&amp;lt;a style='text-align: center;' href='javascript:void(0);' onclick='finishBtnClicked(&quot;+lon+&quot;, &quot;+lat+&quot;)' class='list-group-item list-group-item-action'&amp;gt;도착&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&quot;;
	    
	    console.log(&quot;팝업 lon : &quot; + lon, &quot;lat : &quot; + lat)
	    
	  	//오른쪽마우스 클릭이벤트
		const popup = L.popup()
	    .setLatLng([lon, lat])
	    .setContent(popDiv)
	    .openOn(leafletMap);
	}
	
	//출발버튼 클릭
	function startBtnClicked(lon, lat){
		if(startMarker != &quot;&quot;){
			startMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const startIcon = L.icon({
		    iconUrl: '/images/start-marker.png',
		    iconSize:     [30, 30], // size of the icon
		});

		startMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: startIcon
		});
		
		startMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(startMarker);
	}
	
	//도착버튼 클릭
	function finishBtnClicked(lon, lat){
		
		if(finishMarker != &quot;&quot;){
			finishMarker.remove();	
		}
		
		leafletMap.closePopup();
		
		const finishIcon = L.icon({
		    iconUrl: '/images/finish-marker.png',

		    iconSize:     [30, 30], // size of the icon
		});
		
		finishMarker = new L.marker([lon, lat], {
			draggable: true,
			autoPan: false,
			icon: finishIcon
		});
		finishMarker.addTo(leafletMap);
		
		//마커 마우스 이벤트
		markerMouseEvent(finishMarker);
	}
	
	//마커 마우스 이벤트
	function markerMouseEvent(marker){
		//마우스 오버
		marker.on('mouseover', function (e) {
			marker.bindTooltip(&quot;끌어서 이동&quot;, {direction: 'top',noWrap: true, opacity: 0.9}).openTooltip();
			//class명 추가
// 			marker._icon.classList.add(&quot;btn__close&quot;);
        });
		
		//마우스 아웃
        marker.on('mouseout', function (e) {
//         	marker._icon.classList.remove(&quot;btn__close&quot;);
        });
        
		//마우스 클릭
        marker.on('click', function (e) {
        	marker.remove();
        });
	}
	
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- maker 이미지는 해당 사이트에서 찾아서 넣었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;icon 사이트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.iconfinder.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.iconfinder.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1672892903010&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;7,125,000+ free and premium vector icons, illustrations and 3D illustrations&quot; data-og-description=&quot;Iconfinder is the world's largest marketplace for icons, illustrations and 3D illustrations in SVG, AI, and PNG format.&quot; data-og-host=&quot;www.iconfinder.com&quot; data-og-source-url=&quot;https://www.iconfinder.com/&quot; data-og-url=&quot;https://www.iconfinder.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biFfDH/hyRaxx8O4k/V5Ns7VddnkjlLHta5jzX81/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/bb0Rz7/hyRaBm0Az5/6tVI4khap0BpWCNk736Ej0/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/E8mwF/hyRaxSqH58/JIKYZlj2NJALJW9CE324K0/img.png?width=1920&amp;amp;height=1125&amp;amp;face=0_0_1920_1125&quot;&gt;&lt;a href=&quot;https://www.iconfinder.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.iconfinder.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biFfDH/hyRaxx8O4k/V5Ns7VddnkjlLHta5jzX81/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/bb0Rz7/hyRaBm0Az5/6tVI4khap0BpWCNk736Ej0/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/E8mwF/hyRaxSqH58/JIKYZlj2NJALJW9CE324K0/img.png?width=1920&amp;amp;height=1125&amp;amp;face=0_0_1920_1125');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;7,125,000+ free and premium vector icons, illustrations and 3D illustrations&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Iconfinder is the world's largest marketplace for icons, illustrations and 3D illustrations in SVG, AI, and PNG format.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.iconfinder.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;화면&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWNdTB/btrVpCKc0WX/6s5PlEdiwlkIPk0cYbozg1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWNdTB/btrVpCKc0WX/6s5PlEdiwlkIPk0cYbozg1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWNdTB/btrVpCKc0WX/6s5PlEdiwlkIPk0cYbozg1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bWNdTB/btrVpCKc0WX/6s5PlEdiwlkIPk0cYbozg1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;939&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>gis/LeafletJs</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/730</guid>
      <comments>https://aamoos.tistory.com/730#entry730comment</comments>
      <pubDate>Tue, 3 Jan 2023 17:04:13 +0900</pubDate>
    </item>
    <item>
      <title>[leafletjs] vworld wmts 배경지도 내위치 띄우기</title>
      <link>https://aamoos.tistory.com/729</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이번 포스팅에서는 leaflet js를 설정하고 vworld에서 지원하는 wmts 배경지도 api를 사용해서 지도를 표현하는것을 개발해보겠습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;css, js&lt;/p&gt;
&lt;pre id=&quot;code_1672720372252&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- leaflet --&amp;gt;
&amp;lt;script src=&quot;https://unpkg.com/leaflet@1.9.3/dist/leaflet.js&quot; integrity=&quot;sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=&quot; crossorigin=&quot;&quot;&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;!-- leaflet --&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;https://unpkg.com/leaflet@1.9.3/dist/leaflet.css&quot; integrity=&quot;sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=&quot; crossorigin=&quot;&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672719297182&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%@ page contentType=&quot;text/html; charset=utf-8&quot; pageEncoding=&quot;utf-8&quot; %&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;main role=&quot;main&quot; class=&quot;inner cover&quot;&amp;gt;
        &amp;lt;h1 class=&quot;cover-heading&quot;&amp;gt;LeafletJs Tutorial&amp;lt;/h1&amp;gt;
&amp;lt;!-- 		&amp;lt;p class=&quot;lead&quot;&amp;gt;- openstreetmap&amp;lt;/p&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt; --&amp;gt;
&amp;lt;!-- 		&amp;lt;br&amp;gt; --&amp;gt;
		&amp;lt;p class=&quot;lead&quot;&amp;gt;- wmts 배경지도 (내위치)&amp;lt;/p&amp;gt;
		&amp;lt;div id=&quot;map&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/main&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;script&amp;gt;

	$(function(){
		init();
	});
	
	function init(){
		navigator.geolocation.getCurrentPosition(onGeoOk,onGeoError);
	}
	
	//현재 좌표값 가져오기
	function onGeoOk(position){
	    const lon = position.coords.latitude;
	    const lat = position.coords.longitude;
	    vworldWmts(lon, lat);
	}
	
	//내좌표값 가져올때 error
	function onGeoError(){
	    alert(&quot;Can't find you. No weather for you.&quot;);
	}

	//vowld wmts 배경지도
	function vworldWmts(lon, lat){
		//apikey
		const apiKey = $(&quot;#apiKey&quot;).val();
		
		//leaflet 지도 띄우기 (EPSG : 4326)
		const leafletMap = L.map('map').setView([lon, lat], 15)
		
		L.tileLayer(&quot;http://api.vworld.kr/req/wmts/1.0.0/&quot;+apiKey+&quot;/Base/{z}/{y}/{x}.png&quot;).addTo(leafletMap);
	}
	
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 화면이 로딩될때 navigator.geolocation.getCurrentPosition 함수가 호출되어 현재 내 좌표값을 얻어 냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이후 해당 좌표값 위치로 wmts를 사용해서 표현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1149&quot; data-origin-height=&quot;909&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mmUBo/btrVclvFdNE/Ue3pK2HkJFJ7HXiQaUpteK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mmUBo/btrVclvFdNE/Ue3pK2HkJFJ7HXiQaUpteK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mmUBo/btrVclvFdNE/Ue3pK2HkJFJ7HXiQaUpteK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmmUBo%2FbtrVclvFdNE%2FUe3pK2HkJFJ7HXiQaUpteK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1149&quot; height=&quot;909&quot; data-origin-width=&quot;1149&quot; data-origin-height=&quot;909&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- apikey는 아래 포스팅에서 한것처럼 vworld api key를 받아서 넣으면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;vworld wmts api 사용방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aamoos.tistory.com/515&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://aamoos.tistory.com/515&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1672720304755&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[LeafletJs] vworld wmts 배경지도 띄우기, map, circle, polygon, pin 객체 올리기&quot; data-og-description=&quot;vworld wmts api를 사용하여서 배경지도를 띄우는 부분입니다. 인터넷에 대부분의 예제는 아래 코드처럼 띄우는 형식으로 되어있는데 url에 보면 202002 버전으로 업데이트된 지도이므로 최신의 지도&quot; data-og-host=&quot;aamoos.tistory.com&quot; data-og-source-url=&quot;https://aamoos.tistory.com/515&quot; data-og-url=&quot;https://aamoos.tistory.com/515&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/chdNXj/hyQ8TgWkNM/S3hqtfBVnKQNShHTQrZnhK/img.png?width=800&amp;amp;height=440&amp;amp;face=0_0_800_440,https://scrap.kakaocdn.net/dn/bl0OfG/hyQ8Xjkj59/8pkmtF87Rorg6eZBTdJEdK/img.png?width=800&amp;amp;height=440&amp;amp;face=0_0_800_440,https://scrap.kakaocdn.net/dn/dKXlDI/hyQ80f39zq/ttegt3djY700qAuMFZjQAk/img.png?width=1227&amp;amp;height=675&amp;amp;face=0_0_1227_675&quot;&gt;&lt;a href=&quot;https://aamoos.tistory.com/515&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aamoos.tistory.com/515&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/chdNXj/hyQ8TgWkNM/S3hqtfBVnKQNShHTQrZnhK/img.png?width=800&amp;amp;height=440&amp;amp;face=0_0_800_440,https://scrap.kakaocdn.net/dn/bl0OfG/hyQ8Xjkj59/8pkmtF87Rorg6eZBTdJEdK/img.png?width=800&amp;amp;height=440&amp;amp;face=0_0_800_440,https://scrap.kakaocdn.net/dn/dKXlDI/hyQ80f39zq/ttegt3djY700qAuMFZjQAk/img.png?width=1227&amp;amp;height=675&amp;amp;face=0_0_1227_675');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[LeafletJs] vworld wmts 배경지도 띄우기, map, circle, polygon, pin 객체 올리기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;vworld wmts api를 사용하여서 배경지도를 띄우는 부분입니다. 인터넷에 대부분의 예제는 아래 코드처럼 띄우는 형식으로 되어있는데 url에 보면 202002 버전으로 업데이트된 지도이므로 최신의 지도&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;aamoos.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>gis/LeafletJs</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/729</guid>
      <comments>https://aamoos.tistory.com/729#entry729comment</comments>
      <pubDate>Tue, 3 Jan 2023 13:31:03 +0900</pubDate>
    </item>
    <item>
      <title>Vue 예제 - BootStrap Vue 적용</title>
      <link>https://aamoos.tistory.com/727</link>
      <description>&lt;pre id=&quot;code_1672122377461&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install vue bootstrap bootstrap-vue-3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- VsCode에 해당 명령어를 입력해서 bootstrap을 install 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main.js&lt;/p&gt;
&lt;pre id=&quot;code_1672122662945&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import BootstrapVue3 from 'bootstrap-vue-3';
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue-3/dist/bootstrap-vue-3.css'

const app = createApp(App)
app.use(BootstrapVue3)
app.use(router)
app.mount('#app')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bootstrap-vue-3로 되어있는부분을 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MainView.vue&lt;/p&gt;
&lt;pre id=&quot;code_1672122687902&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;b-button&amp;gt;Button&amp;lt;/b-button&amp;gt;
&amp;lt;b-button variant=&quot;danger&quot;&amp;gt;Button&amp;lt;/b-button&amp;gt;
&amp;lt;b-button variant=&quot;success&quot;&amp;gt;Button&amp;lt;/b-button&amp;gt;
&amp;lt;b-button variant=&quot;outline-primary&quot;&amp;gt;Button&amp;lt;/b-button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트로 main에 부트스트랩이 적용된 코드를 넣어서 확인해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k6BMs/btrULN6cZfY/DkNaJBuJa7Rfp94KtUDj9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k6BMs/btrULN6cZfY/DkNaJBuJa7Rfp94KtUDj9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k6BMs/btrULN6cZfY/DkNaJBuJa7Rfp94KtUDj9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk6BMs%2FbtrULN6cZfY%2FDkNaJBuJa7Rfp94KtUDj9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;340&quot; height=&quot;179&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>frontend/Vue</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/727</guid>
      <comments>https://aamoos.tistory.com/727#entry727comment</comments>
      <pubDate>Tue, 27 Dec 2022 15:31:59 +0900</pubDate>
    </item>
    <item>
      <title>VsCode 것허브 연동 커밋</title>
      <link>https://aamoos.tistory.com/726</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이번 게시글에는 VsCode에서 작성한 소스를 깃허브와 연동하고 커밋하는것을 해보겠습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;299&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/znLdk/btrUL7coChT/KyChp1ZpQboR0iSKAD7Q01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/znLdk/btrUL7coChT/KyChp1ZpQboR0iSKAD7Q01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/znLdk/btrUL7coChT/KyChp1ZpQboR0iSKAD7Q01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FznLdk%2FbtrUL7coChT%2FKyChp1ZpQboR0iSKAD7Q01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;387&quot; height=&quot;299&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;299&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- VsCode 좌측하단에 해당 버튼을 눌러서 깃허브를 로그인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;542&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byEJ5Q/btrULOYeKeC/2ntcjpsRMT8cVKWokJRX50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byEJ5Q/btrULOYeKeC/2ntcjpsRMT8cVKWokJRX50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byEJ5Q/btrULOYeKeC/2ntcjpsRMT8cVKWokJRX50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyEJ5Q%2FbtrULOYeKeC%2F2ntcjpsRMT8cVKWokJRX50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;542&quot; height=&quot;734&quot; data-origin-width=&quot;542&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5QLyS/btrUIg2hOiu/C0uGi5wTkJFU6tjJXqLKuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5QLyS/btrUIg2hOiu/C0uGi5wTkJFU6tjJXqLKuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5QLyS/btrUIg2hOiu/C0uGi5wTkJFU6tjJXqLKuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5QLyS%2FbtrUIg2hOiu%2FC0uGi5wTkJFU6tjJXqLKuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;978&quot; height=&quot;186&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- private로 할지 public으로 할지 선택후 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czaub4/btrUL8PUYOi/Qa4421wD1GEVoyWg2eyKLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czaub4/btrUL8PUYOi/Qa4421wD1GEVoyWg2eyKLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czaub4/btrUL8PUYOi/Qa4421wD1GEVoyWg2eyKLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fczaub4%2FbtrUL8PUYOi%2FQa4421wD1GEVoyWg2eyKLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;454&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- access token을 입력하라는 창이뜹니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;access token 확인하는방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 깃허브 로그인후 프로필 - settings&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9xdvF/btrUJki1e2d/tpmghcokGVI5dr8YnbumB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9xdvF/btrUJki1e2d/tpmghcokGVI5dr8YnbumB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9xdvF/btrUJki1e2d/tpmghcokGVI5dr8YnbumB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9xdvF%2FbtrUJki1e2d%2FtpmghcokGVI5dr8YnbumB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;220&quot; height=&quot;532&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M1Yn7/btrUE58fHI5/Cbisw4r6sFX6ESzCP98k7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M1Yn7/btrUE58fHI5/Cbisw4r6sFX6ESzCP98k7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M1Yn7/btrUE58fHI5/Cbisw4r6sFX6ESzCP98k7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM1Yn7%2FbtrUE58fHI5%2FCbisw4r6sFX6ESzCP98k7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;251&quot; height=&quot;606&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;606&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 왼쪽 메뉴에 Developer Settings 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;163&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJcQPb/btrUCiz1uTJ/J4B4jzfBWYb8rw6Y2WZp91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJcQPb/btrUCiz1uTJ/J4B4jzfBWYb8rw6Y2WZp91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJcQPb/btrUCiz1uTJ/J4B4jzfBWYb8rw6Y2WZp91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJcQPb%2FbtrUCiz1uTJ%2FJ4B4jzfBWYb8rw6Y2WZp91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;292&quot; height=&quot;163&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;163&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Tokens (classic)후 Generate new Token (classic) 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be48IB/btrUNpRaDsR/wtXR0KpbTc8F4zYht89NY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be48IB/btrUNpRaDsR/wtXR0KpbTc8F4zYht89NY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be48IB/btrUNpRaDsR/wtXR0KpbTc8F4zYht89NY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe48IB%2FbtrUNpRaDsR%2FwtXR0KpbTc8F4zYht89NY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;801&quot; height=&quot;750&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;750&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 토큰명과 repo 체크후 Generate Token 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qLsgi/btrUE6lRLjE/vbBiyrimYFrJJCMXv87FWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qLsgi/btrUE6lRLjE/vbBiyrimYFrJJCMXv87FWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qLsgi/btrUE6lRLjE/vbBiyrimYFrJJCMXv87FWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqLsgi%2FbtrUE6lRLjE%2FvbBiyrimYFrJJCMXv87FWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;224&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당키 복사후 위에 토큰 입력창에 넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqPgRl/btrUMJbjpyD/kcukIZWNpuzHEjKy2AwEa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqPgRl/btrUMJbjpyD/kcukIZWNpuzHEjKy2AwEa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqPgRl/btrUMJbjpyD/kcukIZWNpuzHEjKy2AwEa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqPgRl%2FbtrUMJbjpyD%2FkcukIZWNpuzHEjKy2AwEa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;908&quot; height=&quot;113&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 깃허브에 확인해보면 제가 지정한 vue froject 이름으로 repository가 생성이 되었습니다. 이제 github에 커밋을 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;깃허브 커밋&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUfl21/btrUEvFSnGX/T7zZa90Htmk60wHgIYlyH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUfl21/btrUEvFSnGX/T7zZa90Htmk60wHgIYlyH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUfl21/btrUEvFSnGX/T7zZa90Htmk60wHgIYlyH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUfl21%2FbtrUEvFSnGX%2FT7zZa90Htmk60wHgIYlyH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;380&quot; height=&quot;451&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;451&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 버튼의 오른쪽에 화살표 버튼을 클릭한후 push&amp;amp;commit을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhqSWF/btrUJiyLh5T/xjbplY4Xnmwhoib58dd1bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhqSWF/btrUJiyLh5T/xjbplY4Xnmwhoib58dd1bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhqSWF/btrUJiyLh5T/xjbplY4Xnmwhoib58dd1bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhqSWF%2FbtrUJiyLh5T%2FxjbplY4Xnmwhoib58dd1bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;260&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 커밋이 정상적으로 된것을 확인할수 있습니다.&lt;/p&gt;</description>
      <category>frontend/Vue</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/726</guid>
      <comments>https://aamoos.tistory.com/726#entry726comment</comments>
      <pubDate>Tue, 27 Dec 2022 14:51:04 +0900</pubDate>
    </item>
    <item>
      <title>3. Vue 예제 - 서버 실행, vue-router 적용, footer, header 나누기</title>
      <link>https://aamoos.tistory.com/725</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이번 게시글에서는vue-router를 적용해보고 프로젝트를 실행해보겠습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Vue-Router란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 특정 url에 접근하였을때 url마다 선언한 component vue로 화면을 보여주며, 하나의 페이지로 여러 화면을 보여주는 SPA를 개발할수 있게 해주는 라이브러리 입니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm add vue-router@next&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 터미널에 해당 명령어를 입력해서 vue-rotuer를 설치합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9AzEU/btrUHNsg0CF/EhJUXVni6iX6HUKltsvgpk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9AzEU/btrUHNsg0CF/EhJUXVni6iX6HUKltsvgpk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9AzEU/btrUHNsg0CF/EhJUXVni6iX6HUKltsvgpk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/9AzEU/btrUHNsg0CF/EhJUXVni6iX6HUKltsvgpk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1393&quot; height=&quot;292&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwjR7w/btrUKX16IDo/OXg3trvf3rmT6ywpiEnhSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwjR7w/btrUKX16IDo/OXg3trvf3rmT6ywpiEnhSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwjR7w/btrUKX16IDo/OXg3trvf3rmT6ywpiEnhSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwjR7w%2FbtrUKX16IDo%2FOXg3trvf3rmT6ywpiEnhSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;221&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- src 폴더 밑에 router 폴더를 만들고 index.js 파일을 만듭니다. 그리고 src 폴더 밑에 views 폴더를 만들고 LoginView.vue, MainView.vue 파일을 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index.js&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { createWebHistory, createRouter } from &quot;vue-router&quot;;
import Main from &quot;@/views/MainView.vue&quot;;
import Login from &quot;@/views/LoginView.vue&quot;;

const routes = [
  {
    path: &quot;/&quot;,
    name: &quot;Main&quot;,
    component: Main,
  },
  {
    path: &quot;/login&quot;,
    name: &quot;Login&quot;,
    component: Login,
  },
];

const router = createRouter({
    history: createWebHistory(),
    routes,
});

export default router;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;LoginView.vue&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;h1&amp;gt;Login Page&amp;lt;/h1&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MainView.vue&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;h1&amp;gt;Main Page&amp;lt;/h1&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.vue&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;router-view /&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main.js&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(router).mount('#app')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672115134179&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm run build
npm run serve&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 명령어를 VsCode 터미널에 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1625&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zb9Cd/btrUMJvyYW7/apr6ewgqi5NCLgxHpK7shK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zb9Cd/btrUMJvyYW7/apr6ewgqi5NCLgxHpK7shK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zb9Cd/btrUMJvyYW7/apr6ewgqi5NCLgxHpK7shK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/zb9Cd/btrUMJvyYW7/apr6ewgqi5NCLgxHpK7shK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1625&quot; height=&quot;486&quot; data-origin-width=&quot;1625&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 vue-router를 이용해서 header, footer를 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;283&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uXkUu/btrUIZlGuvC/RrI0aNUXLmp3F6fCjUQSzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uXkUu/btrUIZlGuvC/RrI0aNUXLmp3F6fCjUQSzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uXkUu/btrUIZlGuvC/RrI0aNUXLmp3F6fCjUQSzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuXkUu%2FbtrUIZlGuvC%2FRrI0aNUXLmp3F6fCjUQSzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;283&quot; height=&quot;102&quot; data-origin-width=&quot;283&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src/views/layout 폴더를 생성후 그안에 FooterView.vue, HeaderView.vue를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FooterView.vue&lt;/p&gt;
&lt;pre id=&quot;code_1672119235471&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
    &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
    &amp;lt;div class=&quot;footer&quot;&amp;gt;made by jaeSung&amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HeaderView.vue&lt;/p&gt;
&lt;pre id=&quot;code_1672119246368&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div id=&quot;nav&quot;&amp;gt;
    &amp;lt;router-link class=&quot;menu&quot; to=&quot;/&quot;&amp;gt;Main&amp;lt;/router-link&amp;gt; | 
    &amp;lt;router-link class=&quot;menu&quot; to=&quot;/login&quot;&amp;gt;Login&amp;lt;/router-link&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.vue&lt;/p&gt;
&lt;pre id=&quot;code_1672119266320&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;Header /&amp;gt;
  &amp;lt;router-view /&amp;gt;
  &amp;lt;Footer /&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import Header from './views/layout/HeaderView.vue';
import Footer from './views/layout/FooterView.vue';

export default {
  name: 'App',
  components: {
    Header,
    Footer
  }
}
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과화면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1625&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgdGw5/btrUCitfpXU/WJzx9sx5NAaoKxVKbz7OR1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgdGw5/btrUCitfpXU/WJzx9sx5NAaoKxVKbz7OR1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgdGw5/btrUCitfpXU/WJzx9sx5NAaoKxVKbz7OR1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bgdGw5/btrUCitfpXU/WJzx9sx5NAaoKxVKbz7OR1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1625&quot; height=&quot;486&quot; data-origin-width=&quot;1625&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>frontend/Vue</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/725</guid>
      <comments>https://aamoos.tistory.com/725#entry725comment</comments>
      <pubDate>Tue, 27 Dec 2022 13:33:56 +0900</pubDate>
    </item>
    <item>
      <title>2. Vue 예제 - VsCode 플러그인 사용해보기</title>
      <link>https://aamoos.tistory.com/724</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이번글에서는 vue 개발에 유용한 VsCode 플러그인을 다운로드 해보고 사용해보는것을 해보겠습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMGfCL/btrUyCedudz/jAeqj0qvab7kKwrZqfb4MK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMGfCL/btrUyCedudz/jAeqj0qvab7kKwrZqfb4MK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMGfCL/btrUyCedudz/jAeqj0qvab7kKwrZqfb4MK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMGfCL%2FbtrUyCedudz%2FjAeqj0qvab7kKwrZqfb4MK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1278&quot; height=&quot;804&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 먼저 VsCode를 열면 해당 버튼을 누릅니다. (Extensions)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;vetur-설치&quot; data-ke-size=&quot;size26&quot;&gt;vetur 설치&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;761&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L5ClU/btrUAcsKbSE/ca2i5Rh4iEeqluHxTS47Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L5ClU/btrUAcsKbSE/ca2i5Rh4iEeqluHxTS47Q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L5ClU/btrUAcsKbSE/ca2i5Rh4iEeqluHxTS47Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL5ClU%2FbtrUAcsKbSE%2Fca2i5Rh4iEeqluHxTS47Q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1019&quot; height=&quot;761&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;761&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Vetur를 install 합니다. vetur를 받으면 vue 파일 하이라이팅 기능을 사용할수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;vetur-설치&quot; data-ke-size=&quot;size26&quot;&gt;vetur 설치전&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQfKqV/btrULOQYdk0/NqZA0FETN42WidAAnkSDN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQfKqV/btrULOQYdk0/NqZA0FETN42WidAAnkSDN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQfKqV/btrULOQYdk0/NqZA0FETN42WidAAnkSDN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQfKqV%2FbtrULOQYdk0%2FNqZA0FETN42WidAAnkSDN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1392&quot; height=&quot;802&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;vetur-설치&quot; data-ke-size=&quot;size23&quot;&gt;vetur 설치후&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQUUO5/btrUKY7i9tI/KKvStlZncMEFGpTcFuSx11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQUUO5/btrUKY7i9tI/KKvStlZncMEFGpTcFuSx11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQUUO5/btrUKY7i9tI/KKvStlZncMEFGpTcFuSx11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQUUO5%2FbtrUKY7i9tI%2FKKvStlZncMEFGpTcFuSx11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1467&quot; height=&quot;811&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;811&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;material-icon-설치&quot; data-ke-size=&quot;size26&quot;&gt;material icon 설치&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y9CgX/btrUHRgPfYL/MU8ng3D6eGRX4tpoVcuGJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y9CgX/btrUHRgPfYL/MU8ng3D6eGRX4tpoVcuGJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y9CgX/btrUHRgPfYL/MU8ng3D6eGRX4tpoVcuGJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY9CgX%2FbtrUHRgPfYL%2FMU8ng3D6eGRX4tpoVcuGJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;312&quot; height=&quot;171&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Material Icon 플러그인을 install 합니다. 아이콘 테마를 바꿀수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;material-icon-설치&quot; data-ke-size=&quot;size23&quot;&gt;material icon 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File - Preferences - File Icon Theme - Material Icon Theme 입력후 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpUepy/btrUIns18uQ/b3LCtzevuzdDuz6QYKMap0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpUepy/btrUIns18uQ/b3LCtzevuzdDuz6QYKMap0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpUepy/btrUIns18uQ/b3LCtzevuzdDuz6QYKMap0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpUepy%2FbtrUIns18uQ%2Fb3LCtzevuzdDuz6QYKMap0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1014&quot; height=&quot;183&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;material icon 설치전&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vlavD/btrUKnTCfqV/bPycBQrcOGzY7QFIyfSmA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vlavD/btrUKnTCfqV/bPycBQrcOGzY7QFIyfSmA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vlavD/btrUKnTCfqV/bPycBQrcOGzY7QFIyfSmA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvlavD%2FbtrUKnTCfqV%2FbPycBQrcOGzY7QFIyfSmA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;327&quot; height=&quot;442&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;material icon 설치후&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;329&quot; data-origin-height=&quot;455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EQXtp/btrUyA8zu81/WOgtdI12p2kVludSgKY7c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EQXtp/btrUyA8zu81/WOgtdI12p2kVludSgKY7c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EQXtp/btrUyA8zu81/WOgtdI12p2kVludSgKY7c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEQXtp%2FbtrUyA8zu81%2FWOgtdI12p2kVludSgKY7c0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;329&quot; height=&quot;455&quot; data-origin-width=&quot;329&quot; data-origin-height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 아이콘 색깔이 알록달록 해졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Night Owl 설치&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;310&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DfYyB/btrUImVcDKl/lshglD0lXX7WqjQZC7HVQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DfYyB/btrUImVcDKl/lshglD0lXX7WqjQZC7HVQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DfYyB/btrUImVcDKl/lshglD0lXX7WqjQZC7HVQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDfYyB%2FbtrUImVcDKl%2FlshglD0lXX7WqjQZC7HVQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;310&quot; height=&quot;175&quot; data-origin-width=&quot;310&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Night Owl 플러그인을 install 합니다. 색테마를 변경할수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Night Owl 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File - Preferences - Color Theme - Night owl 입력후 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIRwCI/btrUIeXbE6e/iNsyjnY5nZgjb7Uwmkah1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIRwCI/btrUIeXbE6e/iNsyjnY5nZgjb7Uwmkah1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIRwCI/btrUIeXbE6e/iNsyjnY5nZgjb7Uwmkah1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIRwCI%2FbtrUIeXbE6e%2FiNsyjnY5nZgjb7Uwmkah1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;973&quot; height=&quot;260&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Night Owl 적용전&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1277&quot; data-origin-height=&quot;933&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nENty/btrUGHS4Fpo/HuXagRnkcKiL9iRNtSkOq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nENty/btrUGHS4Fpo/HuXagRnkcKiL9iRNtSkOq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nENty/btrUGHS4Fpo/HuXagRnkcKiL9iRNtSkOq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnENty%2FbtrUGHS4Fpo%2FHuXagRnkcKiL9iRNtSkOq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1277&quot; height=&quot;933&quot; data-origin-width=&quot;1277&quot; data-origin-height=&quot;933&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Night Owl 적용후&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;963&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwp0WL/btrUIY0S3nJ/2v6reiSXJtc3t28ZHT9420/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwp0WL/btrUIY0S3nJ/2v6reiSXJtc3t28ZHT9420/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwp0WL/btrUIY0S3nJ/2v6reiSXJtc3t28ZHT9420/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcwp0WL%2FbtrUIY0S3nJ%2F2v6reiSXJtc3t28ZHT9420%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1274&quot; height=&quot;963&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;963&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 색이 파래지는? 것을 볼수 있습니다. 아이콘, 색 테마는 본인의 취향에 맞게 적용을 하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ESLint 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- vue를 개발할때 문법체크를 해주는 플러그인입니다. 해당 플러그인을 install합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kwI3V/btrUHM7H7cg/lzeZFO9FlHcogWqcE4NLZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kwI3V/btrUHM7H7cg/lzeZFO9FlHcogWqcE4NLZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kwI3V/btrUHM7H7cg/lzeZFO9FlHcogWqcE4NLZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkwI3V%2FbtrUHM7H7cg%2FlzeZFO9FlHcogWqcE4NLZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1022&quot; height=&quot;770&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Prettier 설치&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;295&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baHCVR/btrUBbHqQui/Plif6NcbFqMmPhlkiJt9U1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baHCVR/btrUBbHqQui/Plif6NcbFqMmPhlkiJt9U1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baHCVR/btrUBbHqQui/Plif6NcbFqMmPhlkiJt9U1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaHCVR%2FbtrUBbHqQui%2FPlif6NcbFqMmPhlkiJt9U1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;295&quot; height=&quot;124&quot; data-origin-width=&quot;295&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- vue를 개발할때 코드 포맷팅을 도와주는 플러그인입니다. 해당 플러그인을 install 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Auto Close Tag 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 태그를 열면 닫히는 태그가 자동으로 입력되게 수행하는 플러그인입니다. 해당플러그인을 install 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9EP8H/btrULO4xqYq/YYF5PmiWIIvzRc2WahQwe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9EP8H/btrULO4xqYq/YYF5PmiWIIvzRc2WahQwe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9EP8H/btrULO4xqYq/YYF5PmiWIIvzRc2WahQwe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9EP8H%2FbtrULO4xqYq%2FYYF5PmiWIIvzRc2WahQwe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;311&quot; height=&quot;130&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7llIJ/btrUHSGQPes/w6T00KHWB25xRX9rubrVn1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7llIJ/btrUHSGQPes/w6T00KHWB25xRX9rubrVn1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7llIJ/btrUHSGQPes/w6T00KHWB25xRX9rubrVn1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/7llIJ/btrUHSGQPes/w6T00KHWB25xRX9rubrVn1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;302&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Atom Keymap 설치&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKUM1h/btrUGG0069t/BMo7p8mdS89kCkfEAN4MN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKUM1h/btrUGG0069t/BMo7p8mdS89kCkfEAN4MN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKUM1h/btrUGG0069t/BMo7p8mdS89kCkfEAN4MN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKUM1h%2FbtrUGG0069t%2FBMo7p8mdS89kCkfEAN4MN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;314&quot; height=&quot;138&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 단축키로 코드를 자동으로 입력되게 도와주는 플러그인입니다. 해당 플러그인을 install 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단축키&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;! + Tab&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DcWRZ/btrUIlPGBkF/cbpDBPsm3CKwapSTiaT1mK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DcWRZ/btrUIlPGBkF/cbpDBPsm3CKwapSTiaT1mK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DcWRZ/btrUIlPGBkF/cbpDBPsm3CKwapSTiaT1mK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/DcWRZ/btrUIlPGBkF/cbpDBPsm3CKwapSTiaT1mK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;302&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기본적인 html 구조가 만들어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;div#id&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UtXiN/btrUKY0LGnL/Eb1x9rQFnWJ19NGEBquSMk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UtXiN/btrUKY0LGnL/Eb1x9rQFnWJ19NGEBquSMk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UtXiN/btrUKY0LGnL/Eb1x9rQFnWJ19NGEBquSMk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/UtXiN/btrUKY0LGnL/Eb1x9rQFnWJ19NGEBquSMk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;302&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 입력한 id를 가진 div가 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;script + tab&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqud5S/btrUGqYi8L0/hbf0hGnuKEt8ipvErAaiI1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqud5S/btrUGqYi8L0/hbf0hGnuKEt8ipvErAaiI1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqud5S/btrUGqYi8L0/hbf0hGnuKEt8ipvErAaiI1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cqud5S/btrUGqYi8L0/hbf0hGnuKEt8ipvErAaiI1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;302&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- script 코드가 생성이됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;log + tab&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmcNhV/btrUJjRFxl6/UzaKxBO06Y8PXfmN7PzQw1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmcNhV/btrUJjRFxl6/UzaKxBO06Y8PXfmN7PzQw1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmcNhV/btrUJjRFxl6/UzaKxBO06Y8PXfmN7PzQw1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dmcNhV/btrUJjRFxl6/UzaKxBO06Y8PXfmN7PzQw1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;388&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- console이 생성이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도 다양한 단축키가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>frontend/Vue</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/724</guid>
      <comments>https://aamoos.tistory.com/724#entry724comment</comments>
      <pubDate>Tue, 27 Dec 2022 10:24:35 +0900</pubDate>
    </item>
    <item>
      <title>1. vue 예제 - Vue 프로젝트 생성</title>
      <link>https://aamoos.tistory.com/723</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 본 게시글은 VsCode에서 Vue3 프로젝트를 생성하는 방법을 정리하였습니다.&lt;/blockquote&gt;
&lt;h1 id=&quot;vscode-설치&quot;&gt;VsCode 설치&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/download&quot;&gt;https://code.visualstudio.com/download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;자신의 os에 맞는 vscode를 설치합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1063&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mAZfg/btrUAbUWiu4/peW1YxQjRM3jZVyNnfyNd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mAZfg/btrUAbUWiu4/peW1YxQjRM3jZVyNnfyNd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mAZfg/btrUAbUWiu4/peW1YxQjRM3jZVyNnfyNd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmAZfg%2FbtrUAbUWiu4%2FpeW1YxQjRM3jZVyNnfyNd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1063&quot; height=&quot;585&quot; data-origin-width=&quot;1063&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 id=&quot;nodejs-설치&quot;&gt;NodeJs 설치&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nodejs.org/ko/&quot;&gt;https://nodejs.org/ko/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;LTS 버전을 설치합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/15f35/btrUKYzs7tb/JiRHroE595d2EbkKiPazRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/15f35/btrUKYzs7tb/JiRHroE595d2EbkKiPazRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/15f35/btrUKYzs7tb/JiRHroE595d2EbkKiPazRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F15f35%2FbtrUKYzs7tb%2FJiRHroE595d2EbkKiPazRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;755&quot; height=&quot;485&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;485&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;vuejs-devtools-설치&quot;&gt;VueJs Devtools 설치&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;chrome에 확장프로그램 추가하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwiokf/btrUImVbjOU/ebONAxITo5WhBdt2zCxdv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwiokf/btrUImVbjOU/ebONAxITo5WhBdt2zCxdv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwiokf/btrUImVbjOU/ebONAxITo5WhBdt2zCxdv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwiokf%2FbtrUImVbjOU%2FebONAxITo5WhBdt2zCxdv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1207&quot; height=&quot;906&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;906&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 id=&quot;vuejs-devtools-설치&quot;&gt;Vue 설치&lt;/h1&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm install -g @vue/cli&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;vuejs-devtools-설치&quot;&gt;Vue 설치 확인&lt;/h1&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;vue --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 위에 설치가 다되었으면 해당 명령어를 날려서 vue를 설치하고 확인을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;vuejs-devtools-설치&quot;&gt;Vue 프로젝트 생성&lt;/h1&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;vue cretate frontend&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFLRWc/btrULNLBoh8/cRUgBSjwrEBhOCWUEHbeYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFLRWc/btrULNLBoh8/cRUgBSjwrEBhOCWUEHbeYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFLRWc/btrULNLBoh8/cRUgBSjwrEBhOCWUEHbeYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFLRWc%2FbtrULNLBoh8%2FcRUgBSjwrEBhOCWUEHbeYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;382&quot; height=&quot;104&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- VsCode 터미널에 해당 명령어를 실행후 Vue3 Default를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpOfxY/btrUHNZ5Jbq/kiTboRWhDF8cmjBLvBhw61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpOfxY/btrUHNZ5Jbq/kiTboRWhDF8cmjBLvBhw61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpOfxY/btrUHNZ5Jbq/kiTboRWhDF8cmjBLvBhw61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpOfxY%2FbtrUHNZ5Jbq%2FkiTboRWhDF8cmjBLvBhw61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1022&quot; height=&quot;358&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- view project를 생성한 경로에 들어가보면 vue 관련 파일이 생긴것을 알수 있습니다. 해당 폴더를 vscode에 드래그합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1277&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eiek7u/btrUJj4WrFj/0nsYCjsBU86CBQ6Phbcb3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eiek7u/btrUJj4WrFj/0nsYCjsBU86CBQ6Phbcb3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eiek7u/btrUJj4WrFj/0nsYCjsBU86CBQ6Phbcb3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feiek7u%2FbtrUJj4WrFj%2F0nsYCjsBU86CBQ6Phbcb3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1277&quot; height=&quot;958&quot; data-origin-width=&quot;1277&quot; data-origin-height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다음 글에서는 VsCode vue 관련 유용한 플러그인을 다운받아보고 사용해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aamoos.tistory.com/724&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://aamoos.tistory.com/724&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1672114512200&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;2. Vue 기능 튜토리얼 - VsCode 플러그인 사용해보기&quot; data-og-description=&quot;이번글에서는 vue 개발에 유용한 VsCode 플러그인을 다운로드 해보고 사용해보는것을 해보겠습니다. - 먼저 VsCode를 열면 해당 버튼을 누릅니다. (Extensions) vetur 설치 - Vetur를 install 합니다. vetur를 받&quot; data-og-host=&quot;aamoos.tistory.com&quot; data-og-source-url=&quot;https://aamoos.tistory.com/724&quot; data-og-url=&quot;https://aamoos.tistory.com/724&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/m6Uxt/hyQ32Y4OD2/sTGVYHnJfgmnatjufOTGwk/img.png?width=800&amp;amp;height=503&amp;amp;face=0_0_800_503,https://scrap.kakaocdn.net/dn/LIC1L/hyQ3WYP6cz/kgFIpYhJ3gwlvDkc8mffs0/img.png?width=800&amp;amp;height=503&amp;amp;face=0_0_800_503,https://scrap.kakaocdn.net/dn/BFCYD/hyQ3Utb0sN/nnSJ7aTObGRm2xMhqoRZl0/img.png?width=1467&amp;amp;height=811&amp;amp;face=0_0_1467_811&quot;&gt;&lt;a href=&quot;https://aamoos.tistory.com/724&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aamoos.tistory.com/724&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/m6Uxt/hyQ32Y4OD2/sTGVYHnJfgmnatjufOTGwk/img.png?width=800&amp;amp;height=503&amp;amp;face=0_0_800_503,https://scrap.kakaocdn.net/dn/LIC1L/hyQ3WYP6cz/kgFIpYhJ3gwlvDkc8mffs0/img.png?width=800&amp;amp;height=503&amp;amp;face=0_0_800_503,https://scrap.kakaocdn.net/dn/BFCYD/hyQ3Utb0sN/nnSJ7aTObGRm2xMhqoRZl0/img.png?width=1467&amp;amp;height=811&amp;amp;face=0_0_1467_811');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;2. Vue 기능 튜토리얼 - VsCode 플러그인 사용해보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번글에서는 vue 개발에 유용한 VsCode 플러그인을 다운로드 해보고 사용해보는것을 해보겠습니다. - 먼저 VsCode를 열면 해당 버튼을 누릅니다. (Extensions) vetur 설치 - Vetur를 install 합니다. vetur를 받&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;aamoos.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>frontend/Vue</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/723</guid>
      <comments>https://aamoos.tistory.com/723#entry723comment</comments>
      <pubDate>Tue, 27 Dec 2022 10:06:33 +0900</pubDate>
    </item>
    <item>
      <title>A problem occurred configuring root project</title>
      <link>https://aamoos.tistory.com/722</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;A&amp;nbsp;problem&amp;nbsp;occurred&amp;nbsp;configuring&amp;nbsp;root&amp;nbsp;project&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Could&amp;nbsp;not&amp;nbsp;resolve&amp;nbsp;all&amp;nbsp;files&amp;nbsp;for&amp;nbsp;configuration&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Could&amp;nbsp;not&amp;nbsp;resolve&amp;nbsp;org.springframework.boot:spring-boot-gradle-plugin:3.0.1.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1531&quot; data-origin-height=&quot;904&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tN7vK/btrUIl20tEj/PaOHseorO7YK8RKncFNtiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tN7vK/btrUIl20tEj/PaOHseorO7YK8RKncFNtiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tN7vK/btrUIl20tEj/PaOHseorO7YK8RKncFNtiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtN7vK%2FbtrUIl20tEj%2FPaOHseorO7YK8RKncFNtiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1531&quot; height=&quot;904&quot; data-origin-width=&quot;1531&quot; data-origin-height=&quot;904&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- spring start io에서 java 11로 선택하고 프로젝트를 받은후 intelij에서 gradle build를 했을경우 아래 에러를 마주하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc7aQX/btrUJiSt5YB/xO2Kj0rRCd31XgYqUppeg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc7aQX/btrUJiSt5YB/xO2Kj0rRCd31XgYqUppeg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc7aQX/btrUJiSt5YB/xO2Kj0rRCd31XgYqUppeg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc7aQX%2FbtrUJiSt5YB%2FxO2Kj0rRCd31XgYqUppeg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1208&quot; height=&quot;335&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yfkCF/btrUKneZYEt/sYl3Z4BB2Y6ZOc4E2an791/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yfkCF/btrUKneZYEt/sYl3Z4BB2Y6ZOc4E2an791/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yfkCF/btrUKneZYEt/sYl3Z4BB2Y6ZOc4E2an791/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyfkCF%2FbtrUKneZYEt%2FsYl3Z4BB2Y6ZOc4E2an791%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;606&quot; height=&quot;483&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- build.gradle 파일을 열어보니 sourceCompatibility 자바 버전이 17로 되어있습니다. 저는 11버전을 선택했는데도..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보니 spring boot 3.0 부터는 자바가 17이 필요해서, 프로젝트 생성시 무조건 17로 된다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File - Project Structure&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mB53T/btrUKXHj29S/EhJCeidcScfhxOLGN45W4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mB53T/btrUKXHj29S/EhJCeidcScfhxOLGN45W4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mB53T/btrUKXHj29S/EhJCeidcScfhxOLGN45W4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmB53T%2FbtrUKXHj29S%2FEhJCeidcScfhxOLGN45W4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;844&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저 같은경우에는 sdk가 No Sdk로 되어있었습니다. jdk 17이 없는경우 Add Sdk - Download Sdk에 들어가서 자바 17을 받은후 설정해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;File - Settings 들어가서 gradle 검색&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;739&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGda7V/btrUHSz6C0r/5pmn1vmZaNskBsYTIifGI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGda7V/btrUHSz6C0r/5pmn1vmZaNskBsYTIifGI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGda7V/btrUHSz6C0r/5pmn1vmZaNskBsYTIifGI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGda7V%2FbtrUHSz6C0r%2F5pmn1vmZaNskBsYTIifGI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;982&quot; height=&quot;739&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;739&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 맨아래 Gradle JVM에서 자바 17을 선택해줍니다. 마찬가지로 동일하게 자바 17이 없는 경우 Download Jdk에서 자바 17을 다운로드후 선택해주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;879&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/co52CW/btrUIlBVVHO/m62GZutLjX3cgmtTVSwds0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/co52CW/btrUIlBVVHO/m62GZutLjX3cgmtTVSwds0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/co52CW/btrUIlBVVHO/m62GZutLjX3cgmtTVSwds0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fco52CW%2FbtrUIlBVVHO%2Fm62GZutLjX3cgmtTVSwds0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1224&quot; height=&quot;879&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;879&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- build가 정상적으로 되었습니다.&lt;/p&gt;</description>
      <category>backend/오류모음</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/722</guid>
      <comments>https://aamoos.tistory.com/722#entry722comment</comments>
      <pubDate>Tue, 27 Dec 2022 09:47:15 +0900</pubDate>
    </item>
    <item>
      <title>Spring + jquery ajax post 파일 다운로드</title>
      <link>https://aamoos.tistory.com/721</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 회사에서 일하던중 기존에 개발되어있던 소스가 해당코드처럼 링크를 클릭하면 get방식으로 다운로드 하는 방식이었습니다. 그런데 이게 웹 취약성에 걸려 post로 바꿔달라는 요구 사항이 있어서 그 내용에 관한것을 정리하고자 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- get 방식 (이전방식)&lt;/p&gt;
&lt;pre id=&quot;code_1670463384725&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;/dashboard/fileDownload.do?type=5&amp;amp;sn=1&quot; target =&quot;_blank&quot; class=&quot;gray_btn2_line pdfDown&quot;&amp;gt;PDF다운로드&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- post 방식 (변경한방식)&lt;/p&gt;
&lt;pre id=&quot;code_1670463472292&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;javascript:void(0);&quot; onclick=&quot;fileDownload(this)&quot; type='5' sn='1' target =&quot;_blank&quot; class=&quot;gray_btn2_line pdfDown&quot;&amp;gt;PDF다운로드&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;- 더 나은 방식이 있을수 있겠지만, 저는 기존에 get방식에서 뒤에 붙는 파라미터를 attribute로 선언을하여서 넣어줬습니다. (수정하고자한 프로젝트에 개발되어있던 방식이 type 마다 해당 파일을 찾는식으로 개발이 되어있음)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;common.js&lt;/p&gt;
&lt;pre id=&quot;code_1670463683910&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//파일다운로드 post 방식
function fileDownload(target){
	const type = $(target).attr(&quot;type&quot;);
	const sn = $(target).attr(&quot;sn&quot;);
	
	const formObj = $(&quot;#downloadForm&quot;);
	formObj.find('input[name=type]').val(type);
	formObj.find('input[name=sn]').val(sn);
    
	formObj.attr(&quot;action&quot;, &quot;/fileDownload.do&quot;);
	formObj.attr(&quot;method&quot;, &quot;POST&quot;);
	formObj.submit();
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;- ajax로 처음에 post 방식으로 다운로드 api에 전송해봤는데 ajax로는 다운로드가 되지 않는 현상이 있어서 (구글링 해봤을때도 안된다고함) form 전송으로 처리를 하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;download form&lt;/p&gt;
&lt;pre id=&quot;code_1670463854958&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- download form --&amp;gt;
&amp;lt;form id=&quot;downloadForm&quot; method=&quot;post&quot;&amp;gt;
    &amp;lt;input type=&quot;hidden&quot; name=&quot;type&quot;&amp;gt;
    &amp;lt;input type=&quot;hidden&quot; name=&quot;sn&quot;&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;- header나 공통 페이지에 넣어서 다운로드 클릭시 해당 hidden값에 값을 setting후 post로 다운로드 api에 전송하는 방식입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드 api는 인터넷에 예제가 많기 때문에 생략하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>backend/스프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/721</guid>
      <comments>https://aamoos.tistory.com/721#entry721comment</comments>
      <pubDate>Thu, 8 Dec 2022 10:52:35 +0900</pubDate>
    </item>
    <item>
      <title>spring boot restart시 query 실행하기</title>
      <link>https://aamoos.tistory.com/720</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. application.yml&lt;/p&gt;
&lt;pre id=&quot;code_1669712761703&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  h2:
    console:
      enabled: true

  datasource:
    url: &quot;dburl명&quot;
    username: sa
    password:
    driver-class-name: org.h2.Driver

  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        show_sql: true
        format_sql: true
    defer-datasource-initialization: true

sql:
  init:
    mode: always

logging:
  level:
    org.hibernate.SQL: debug&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669712842366&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;defer-datasource-initialization: true

sql:
  init:
    mode: always&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 코드를 application.yml에 붙여줌 (글쓴이는 spring boot 2.7.5 버전 기준 h2 db에 쿼리를 실행하려고 함)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.resources 디렉터리 밑에 import.sql 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;147&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bedP2p/btrSm1NH3mz/i0DgmPk2PmPNj0EeGjZqaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bedP2p/btrSm1NH3mz/i0DgmPk2PmPNj0EeGjZqaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bedP2p/btrSm1NH3mz/i0DgmPk2PmPNj0EeGjZqaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbedP2p%2FbtrSm1NH3mz%2Fi0DgmPk2PmPNj0EeGjZqaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;385&quot; height=&quot;147&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;147&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- sql명을 처음에 data.sql로 지정하였는데, insert가 안되길래 import.sql로 이름을 바꾸면 insert가 된다는 글이 있길래 바꿔보니 insert가 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import.sql&lt;/p&gt;
&lt;pre id=&quot;code_1669713005579&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;INSERT INTO USERS(USER_ID, USERNAME, PASSWORD, NICKNAME, ACTIVATED) VALUES(1, 'admin', '$2a$10$E2pUd//AHzJ9GftoOUGMnudE967qaiZ1elVzkpv3lO0NNYhl6Dmmy', 'admin', 1);

INSERT INTO AUTHORITY (AUTHORITY_NAME) VALUES ('ROLE_USER');
INSERT INTO AUTHORITY (AUTHORITY_NAME) VALUES ('ROLE_ADMIN');

INSERT INTO USER_AUTHORITY (USER_ID, AUTHORITY_NAME) VALUES (1, 'ROLE_USER');
INSERT INTO USER_AUTHORITY (USER_ID, AUTHORITY_NAME) VALUES (1, 'ROLE_ADMIN');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- import.sql은 서버 restart시 실행할 쿼리를 입력하면됨, 글쓴이는 default 사용자 정보 권한을 입력하려고 넣었음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행결과&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rLBhT/btrSqGIGMkX/Zpalkrnxt5RViAAZPogqA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rLBhT/btrSqGIGMkX/Zpalkrnxt5RViAAZPogqA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rLBhT/btrSqGIGMkX/Zpalkrnxt5RViAAZPogqA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrLBhT%2FbtrSqGIGMkX%2FZpalkrnxt5RViAAZPogqA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;912&quot; height=&quot;608&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>backend/스프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/720</guid>
      <comments>https://aamoos.tistory.com/720#entry720comment</comments>
      <pubDate>Tue, 29 Nov 2022 18:13:41 +0900</pubDate>
    </item>
    <item>
      <title>Cannot find method 'value'</title>
      <link>https://aamoos.tistory.com/719</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b25kVH/btrR6HfYpAZ/IhG3zi9uygvLgJNnuk3VM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b25kVH/btrR6HfYpAZ/IhG3zi9uygvLgJNnuk3VM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b25kVH/btrR6HfYpAZ/IhG3zi9uygvLgJNnuk3VM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb25kVH%2FbtrR6HfYpAZ%2FIhG3zi9uygvLgJNnuk3VM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;998&quot; height=&quot;477&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jwt TokenProvider 구현중 Cannot find method 'value' 에러를 마주쳤습니다. 이 에러를 처리하는 방법에 대해 정리하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. File-Settings에서 Annotation Processors에서 Enable annotation processing을 체크합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cm7yab/btrR3ee334f/tvYw3ZbOwWVyGUNKFDdTD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cm7yab/btrR3ee334f/tvYw3ZbOwWVyGUNKFDdTD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cm7yab/btrR3ee334f/tvYw3ZbOwWVyGUNKFDdTD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcm7yab%2FbtrR3ee334f%2FtvYw3ZbOwWVyGUNKFDdTD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;984&quot; height=&quot;714&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 상단 import가 된 lombok 부분을 수정을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정전&lt;/p&gt;
&lt;pre id=&quot;code_1669342830505&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import lombok.Value;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정후&lt;/p&gt;
&lt;pre id=&quot;code_1669342850715&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.beans.factory.annotation.Value;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D04HD/btrR3bWZZ8V/zdx2jJsmVvBeWp5SWYkSEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D04HD/btrR3bWZZ8V/zdx2jJsmVvBeWp5SWYkSEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D04HD/btrR3bWZZ8V/zdx2jJsmVvBeWp5SWYkSEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD04HD%2FbtrR3bWZZ8V%2Fzdx2jJsmVvBeWp5SWYkSEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;519&quot; height=&quot;488&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.yml을 열어서 jwt가 spring 하단으로 갔는지 체크합니다. (하단으로 가면 안됨)&lt;/p&gt;
&lt;pre id=&quot;code_1669343140581&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  h2:
    console:
      enabled: true

  datasource:
    url: jdbc:h2:tcp://localhost/~/jwtdb
    driver-class-name: org.h2.Driver
    username: sa
    password:

  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        format_sql: true
        show_sql: true

jwt:
  header: Authorization
  #HS512 알고리즘을 사용할 것이기 때문에 512bit, 즉 64byte 이상의 secret key를 사용해야 한다.
  #echo 'silvernine-tech-spring-boot-jwt-tutorial-secret-silvernine-tech-spring-boot-jwt-tutorial-secret'|base64
  secret: c2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQtc2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQK
  token-validity-in-seconds: 86400&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wIJpp/btrR3Zu0Bh7/3EJFwyPEfz20EmkMTiqilK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wIJpp/btrR3Zu0Bh7/3EJFwyPEfz20EmkMTiqilK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wIJpp/btrR3Zu0Bh7/3EJFwyPEfz20EmkMTiqilK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwIJpp%2FbtrR3Zu0Bh7%2F3EJFwyPEfz20EmkMTiqilK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1418&quot; height=&quot;308&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류가 사라진것을 확인할수 있습니다. yml에서 value를 가져올때 &lt;span&gt;Cannot find method 'value' 에러를 마주치면, 해당 내용을 참고하면 될것 같습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>backend/오류모음</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/719</guid>
      <comments>https://aamoos.tistory.com/719#entry719comment</comments>
      <pubDate>Fri, 25 Nov 2022 11:16:16 +0900</pubDate>
    </item>
    <item>
      <title>Cannot resolve symbol 'NotBlank', Cannot resolve symbol 'Pattern'</title>
      <link>https://aamoos.tistory.com/718</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oQOE9/btrRTsR37if/NJbd9pdtCk1WLMpPmMEfdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oQOE9/btrRTsR37if/NJbd9pdtCk1WLMpPmMEfdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oQOE9/btrRTsR37if/NJbd9pdtCk1WLMpPmMEfdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoQOE9%2FbtrRTsR37if%2FNJbd9pdtCk1WLMpPmMEfdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;666&quot; height=&quot;204&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 회원가입 api를 개발할때 dto에서 유효성검사 관련 어노테이션을 사용하려고 하는데 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;해당에러를 마주칠경우 Gradle에서 start-validation을 추가하면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669180017263&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'org.springframework.boot:spring-boot-starter-validation'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 코드 build.gradle에 추가후 빌드하면 아래 이미지처럼 import를 할수 있는 창이 나옵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G0jAR/btrRXbPjlxh/AXka93FkXAxkqLrU3BQaa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G0jAR/btrRXbPjlxh/AXka93FkXAxkqLrU3BQaa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G0jAR/btrRXbPjlxh/AXka93FkXAxkqLrU3BQaa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG0jAR%2FbtrRXbPjlxh%2FAXka93FkXAxkqLrU3BQaa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1008&quot; height=&quot;321&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>backend/스프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/718</guid>
      <comments>https://aamoos.tistory.com/718#entry718comment</comments>
      <pubDate>Wed, 23 Nov 2022 14:08:43 +0900</pubDate>
    </item>
    <item>
      <title>intelij project 이름변경</title>
      <link>https://aamoos.tistory.com/717</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;533&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IaqQV/btrRN5CKgJg/atS1XIeHFPiw2eXVgCxQ1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IaqQV/btrRN5CKgJg/atS1XIeHFPiw2eXVgCxQ1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IaqQV/btrRN5CKgJg/atS1XIeHFPiw2eXVgCxQ1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIaqQV%2FbtrRN5CKgJg%2FatS1XIeHFPiw2eXVgCxQ1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;533&quot; height=&quot;226&quot; data-origin-width=&quot;533&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setting.gradle&lt;/p&gt;
&lt;pre id=&quot;code_1669095829686&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rootProject.name = '프로젝트 이름'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- setting.gradle에서 rootProject 이름 변경후 rebuild&lt;/p&gt;</description>
      <category>backend/스프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/717</guid>
      <comments>https://aamoos.tistory.com/717#entry717comment</comments>
      <pubDate>Tue, 22 Nov 2022 14:44:13 +0900</pubDate>
    </item>
    <item>
      <title>github에 project 올리기 (Share Project on Github)</title>
      <link>https://aamoos.tistory.com/716</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;VCS - Share Project on GitHub&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bxbcv/btrRN6hhnJ5/a6Dbaut97MFhFP5bCvZWJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bxbcv/btrRN6hhnJ5/a6Dbaut97MFhFP5bCvZWJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bxbcv/btrRN6hhnJ5/a6Dbaut97MFhFP5bCvZWJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBxbcv%2FbtrRN6hhnJ5%2Fa6Dbaut97MFhFP5bCvZWJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;300&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 OK 버튼 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>backend/스프링</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/716</guid>
      <comments>https://aamoos.tistory.com/716#entry716comment</comments>
      <pubDate>Tue, 22 Nov 2022 14:42:18 +0900</pubDate>
    </item>
    <item>
      <title>Arachni (아라크니) 웹취약성 무료 검사 툴 설치 및 사용방법</title>
      <link>https://aamoos.tistory.com/714</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 해당 사이트 접속&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.arachni-scanner.com/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.arachni-scanner.com/download/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1664511518742&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Download&quot; data-og-description=&quot;Arachni is licensed under the Arachni Public Source License v1.0, requiring a non-free license for cases of commercialization. Please review the license before downloading. Current version: v1.6.1.&amp;hellip;&quot; data-og-host=&quot;www.arachni-scanner.com&quot; data-og-source-url=&quot;https://www.arachni-scanner.com/download/&quot; data-og-url=&quot;https://www.arachni-scanner.com/download/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oPDXs/hyPXRrHOPR/YwlxaViKT61kdgXhAQVK2k/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/dipTEv/hyPXEy6Tf6/kRU1YohJLxoNOQLa1Olal0/img.png?width=270&amp;amp;height=270&amp;amp;face=0_0_270_270&quot;&gt;&lt;a href=&quot;https://www.arachni-scanner.com/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.arachni-scanner.com/download/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oPDXs/hyPXRrHOPR/YwlxaViKT61kdgXhAQVK2k/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/dipTEv/hyPXEy6Tf6/kRU1YohJLxoNOQLa1Olal0/img.png?width=270&amp;amp;height=270&amp;amp;face=0_0_270_270');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Download&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Arachni is licensed under the Arachni Public Source License v1.0, requiring a non-free license for cases of commercialization. Please review the license before downloading. Current version: v1.6.1.&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.arachni-scanner.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 사용하는 컴퓨터 환경에 맞는 파일을 다운로드&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;833&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/urOrq/btrNueW0WNs/aVMbGhK0VYx1xdZGRQOPK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/urOrq/btrNueW0WNs/aVMbGhK0VYx1xdZGRQOPK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/urOrq/btrNueW0WNs/aVMbGhK0VYx1xdZGRQOPK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FurOrq%2FbtrNueW0WNs%2FaVMbGhK0VYx1xdZGRQOPK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;833&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;833&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfSgO1/btrNuev2vpm/0XiNPPbNvXGk2LqlQPdf7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfSgO1/btrNuev2vpm/0XiNPPbNvXGk2LqlQPdf7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfSgO1/btrNuev2vpm/0XiNPPbNvXGk2LqlQPdf7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfSgO1%2FbtrNuev2vpm%2F0XiNPPbNvXGk2LqlQPdf7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1217&quot; height=&quot;628&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 경로는 전부 영문으로 이뤄줘야 되므로, c:\arachni 폴더를 만들고 해당 경로에 설치하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 아라크니 실행&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eaIBNG/btrNuj48svt/oASXjZdVa8HiCMA1WkUHl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eaIBNG/btrNuj48svt/oASXjZdVa8HiCMA1WkUHl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eaIBNG/btrNuj48svt/oASXjZdVa8HiCMA1WkUHl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeaIBNG%2FbtrNuj48svt%2FoASXjZdVa8HiCMA1WkUHl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;821&quot; height=&quot;394&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;아라크니 압축해제 한 폴더 bin 폴더안에 arachni_web.bat를 실행합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIs3He/btrNukbSpa6/02akTrdG2i5MiKPNzVb5sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIs3He/btrNukbSpa6/02akTrdG2i5MiKPNzVb5sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIs3He/btrNukbSpa6/02akTrdG2i5MiKPNzVb5sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIs3He%2FbtrNukbSpa6%2F02akTrdG2i5MiKPNzVb5sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1546&quot; height=&quot;626&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 아라크니 사이트 접속&lt;/h3&gt;
&lt;pre id=&quot;code_1664512833531&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http://localhost:9292/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbRtpg/btrNrghtcQC/HZJhsXDoG5cUDtKHTvmXo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbRtpg/btrNrghtcQC/HZJhsXDoG5cUDtKHTvmXo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbRtpg/btrNrghtcQC/HZJhsXDoG5cUDtKHTvmXo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbRtpg%2FbtrNrghtcQC%2FHZJhsXDoG5cUDtKHTvmXo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;221&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;아라크니 폴더안에 README폴더를 열어보면 해당 사이트의 admin 계정이 default로 등록되어있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwV6GU/btrNqWXEOrq/Kozx7Jye2bcNJECukx4wM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwV6GU/btrNqWXEOrq/Kozx7Jye2bcNJECukx4wM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwV6GU/btrNqWXEOrq/Kozx7Jye2bcNJECukx4wM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwV6GU%2FbtrNqWXEOrq%2FKozx7Jye2bcNJECukx4wM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;654&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id : admin@admin.admin&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pwd : administrator&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;해당 계정은 default로 등록된 계정입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 로그인된 화면&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHioPR/btrNufojxqt/uSaFnlCLWoMPlmLmeyxZk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHioPR/btrNufojxqt/uSaFnlCLWoMPlmLmeyxZk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHioPR/btrNufojxqt/uSaFnlCLWoMPlmLmeyxZk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHioPR%2FbtrNufojxqt%2FuSaFnlCLWoMPlmLmeyxZk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1298&quot; height=&quot;586&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 스캔하기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HvhMI/btrNrVRHzh4/AzJlHMji2Ka9sU7F7sNHy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HvhMI/btrNrVRHzh4/AzJlHMji2Ka9sU7F7sNHy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HvhMI/btrNrVRHzh4/AzJlHMji2Ka9sU7F7sNHy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHvhMI%2FbtrNrVRHzh4%2FAzJlHMji2Ka9sU7F7sNHy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;463&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xGYYl/btrNt32AmOR/4VgEf8ZHVYMjW4togCfEw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xGYYl/btrNt32AmOR/4VgEf8ZHVYMjW4togCfEw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xGYYl/btrNt32AmOR/4VgEf8ZHVYMjW4togCfEw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxGYYl%2FbtrNt32AmOR%2F4VgEf8ZHVYMjW4togCfEw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1696&quot; height=&quot;381&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;targetUrl에는 검사할 URL을 입력합니다. 인가되지않은 서버를 scan하면 불법이기 때문에 인가된서버에 하시기를 권장드립니다. 만약 취약성 검사를 했을때 google driver 98 error~가 뜨면 chrome 98version에서 아라크니를 사용할수 있는것 같아서, chrome을 98로 다운그레이드후 진행을 하면 정상적으로 진행이 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;chrom 버전다운그레이드 다운 홈페이지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://google-chrome.en.uptodown.com/windows/download/4407134&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://google-chrome.en.uptodown.com/windows/download/4407134&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1664515653091&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Download Google Chrome 98.0.4758.82 for Windows | Uptodown.com&quot; data-og-description=&quot;It arrives Mozilla successor... from the same creators A great browser to keep you organized while you work A browser that's geared towards gamers&quot; data-og-host=&quot;google-chrome.en.uptodown.com&quot; data-og-source-url=&quot;https://google-chrome.en.uptodown.com/windows/download/4407134&quot; data-og-url=&quot;https://google-chrome.en.uptodown.com/windows/download/4407134&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://google-chrome.en.uptodown.com/windows/download/4407134&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://google-chrome.en.uptodown.com/windows/download/4407134&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Download Google Chrome 98.0.4758.82 for Windows | Uptodown.com&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;It arrives Mozilla successor... from the same creators A great browser to keep you organized while you work A browser that's geared towards gamers&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;google-chrome.en.uptodown.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>기타/소프트웨어 설치</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/714</guid>
      <comments>https://aamoos.tistory.com/714#entry714comment</comments>
      <pubDate>Fri, 30 Sep 2022 13:19:51 +0900</pubDate>
    </item>
    <item>
      <title>Window 10 CentOS 설치, 파이썬 3.8, pip-3 설치, 파이썬 파일 실행해보기</title>
      <link>https://aamoos.tistory.com/712</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;안녕하세요. 회사에서 CentOS를 윈도우에 설치해보고, 파이썬을 실행해볼일이 있어서 방법을 포스팅해보겠습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1.&amp;nbsp; 해당 github에 들어가서 CentOS.zip을 다운로드 받습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/wsldl-pg/CentWSL/releases/tag/7.0.1905.1&quot;&gt;&lt;span&gt;htt&lt;/span&gt;&lt;span&gt;ps://github.com/wsldl-pg/CentWSL/releases/tag/7.0.1905.1&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663648564227&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Release 7.0.1905.1 &amp;middot; wsldl-pg/CentWSL&quot; data-og-description=&quot;CentOS 7.6 1905 wsldl 19070200&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/wsldl-pg/CentWSL/releases/tag/7.0.1905.1&quot; data-og-url=&quot;https://github.com/wsldl-pg/CentWSL/releases/tag/7.0.1905.1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/6CA6W/hyPQnx0vHg/8hWhGqwiFGhGWCR5Gv7N90/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/wsldl-pg/CentWSL/releases/tag/7.0.1905.1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/wsldl-pg/CentWSL/releases/tag/7.0.1905.1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/6CA6W/hyPQnx0vHg/8hWhGqwiFGhGWCR5Gv7N90/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Release 7.0.1905.1 &amp;middot; wsldl-pg/CentWSL&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;CentOS 7.6 1905 wsldl 19070200&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chx2xY/btrMy0sOWKq/46m86VaIJ7V25sVbdn9Vdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chx2xY/btrMy0sOWKq/46m86VaIJ7V25sVbdn9Vdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chx2xY/btrMy0sOWKq/46m86VaIJ7V25sVbdn9Vdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fchx2xY%2FbtrMy0sOWKq%2F46m86VaIJ7V25sVbdn9Vdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;918&quot; height=&quot;428&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2. 제어판 - 프로그램 - Windows 기능 켜기/끄기에서 Linux용 Windows 하위 시스템을 체크합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;897&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9gQLz/btrMxTnohxr/KkiY0ntIoiixBgtORPIqb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9gQLz/btrMxTnohxr/KkiY0ntIoiixBgtORPIqb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9gQLz/btrMxTnohxr/KkiY0ntIoiixBgtORPIqb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9gQLz%2FbtrMxTnohxr%2FKkiY0ntIoiixBgtORPIqb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;527&quot; data-origin-width=&quot;897&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;3. 1번에서 받은 압축파일을 적당한곳에 압축해제 합니다. (CentOs를 실행하면 서버 환경을 해당 디렉터리에 해제해서 옮기기 어려우므로 잘지정해야합니다.)&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LsM6u/btrMyZ1KPv4/ybJ42ANaVfA2LCMElv1rPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LsM6u/btrMyZ1KPv4/ybJ42ANaVfA2LCMElv1rPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LsM6u/btrMyZ1KPv4/ybJ42ANaVfA2LCMElv1rPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLsM6u%2FbtrMyZ1KPv4%2FybJ42ANaVfA2LCMElv1rPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;868&quot; height=&quot;211&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;211&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저는 C:\centos 폴더에 해제하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;4. CentOS를 관리자 계정으로 실행을 하면 install이라고 써져있는 창이 뜨는데, 설치가 될때까지 기다립니다. 설치가 완료되면 해당 이미지처럼 디렉터리가 구성됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rDB7Q/btrMy00I41m/ijmErQpBHZb5qI6krbOkh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rDB7Q/btrMy00I41m/ijmErQpBHZb5qI6krbOkh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rDB7Q/btrMy00I41m/ijmErQpBHZb5qI6krbOkh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrDB7Q%2FbtrMy00I41m%2FijmErQpBHZb5qI6krbOkh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;837&quot; height=&quot;494&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;5. CentOs를 실행후 해당 명령어들을 입력합니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1663648840982&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yum update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- centOS를 업데이트 해서 라이브러리들을 최신 라이브러리를 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663648927144&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 명령어를 치면 기본으로 2.7.5 버전이 설치된것을 볼수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663648990888&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd /home
https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tgz&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 명령어를 쳐서 home디렉터리 안에 python 3.8.1을 다운로드 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663649054962&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tar xzf Python-3.8.1.tgz&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 받은 파이썬 압축파일을 해제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663649132643&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd Python-3.8.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 설치된 파이썬 3.8.1 디렉터리로 이동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663649151268&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./configure --enable-optimizations&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- configure을 실행해서 컴파일 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663649184940&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;make altinstall&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컴파일이 끝났으면 설치를 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663649254403&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;which python3.8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JVS1Y/btrMDrQeFFQ/mZ1JvpgJcXKCMRMBrIE2sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JVS1Y/btrMDrQeFFQ/mZ1JvpgJcXKCMRMBrIE2sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JVS1Y/btrMDrQeFFQ/mZ1JvpgJcXKCMRMBrIE2sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJVS1Y%2FbtrMDrQeFFQ%2FmZ1JvpgJcXKCMRMBrIE2sK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;33&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- python3.8이 설치된 디렉터리를 찾습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663649287603&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vi /root/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1663649315224&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;alias python='/usr/local/bin/python3.8'
alias pip='/usr/local/bin/pip3.8'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- vi를 연후 alias로 python, pip를 등록해줍니다. pip 설치는 아래에서 진행해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663649472739&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yum install epel-release
yum install python3-pip
rpm -qa | grep -i python3-pip
which pip3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tESNe/btrMxWdv4pV/vdoJvBQLGwBk8YfkW9WdXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tESNe/btrMxWdv4pV/vdoJvBQLGwBk8YfkW9WdXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tESNe/btrMxWdv4pV/vdoJvBQLGwBk8YfkW9WdXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtESNe%2FbtrMxWdv4pV%2FvdoJvBQLGwBk8YfkW9WdXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;462&quot; height=&quot;33&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;32&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTlt64/btrMwYXaJEa/tDGnb5RpS8s15eiKwVGA91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTlt64/btrMwYXaJEa/tDGnb5RpS8s15eiKwVGA91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTlt64/btrMwYXaJEa/tDGnb5RpS8s15eiKwVGA91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTlt64%2FbtrMwYXaJEa%2FtDGnb5RpS8s15eiKwVGA91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;305&quot; height=&quot;32&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;32&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- pip3란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬3 패키지 매니저로 예를 들면 소스에 import 되어있는 부분들을 사용하려면 먼저 pip로 해당 패키지를 설치를 해야합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAwMTd/btrMDq4VqNy/SG5RI07IBRUT4yv6Oo9jk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAwMTd/btrMDq4VqNy/SG5RI07IBRUT4yv6Oo9jk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAwMTd/btrMDq4VqNy/SG5RI07IBRUT4yv6Oo9jk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAwMTd%2FbtrMDq4VqNy%2FSG5RI07IBRUT4yv6Oo9jk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;326&quot; height=&quot;161&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- requests를 사용하려면 구글 검색창에 pip3 requests 검색하면 해당 홈페이지가 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pypi.org/project/requests/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://pypi.org/project/requests/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663649901606&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;requests&quot; data-og-description=&quot;Python HTTP for Humans.&quot; data-og-host=&quot;pypi.org&quot; data-og-source-url=&quot;https://pypi.org/project/requests/&quot; data-og-url=&quot;https://pypi.org/project/requests/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qBZj2/hyPQ6uGaib/iLFS501m7y6Ek0aSBguj8K/img.jpg?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/bwV3vt/hyPQl1gmix/MRaQj2xEmnPiVn3fFvMZU1/img.png?width=1310&amp;amp;height=1191&amp;amp;face=0_0_1310_1191&quot;&gt;&lt;a href=&quot;https://pypi.org/project/requests/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pypi.org/project/requests/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qBZj2/hyPQ6uGaib/iLFS501m7y6Ek0aSBguj8K/img.jpg?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/bwV3vt/hyPQl1gmix/MRaQj2xEmnPiVn3fFvMZU1/img.png?width=1310&amp;amp;height=1191&amp;amp;face=0_0_1310_1191');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;requests&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Python HTTP for Humans.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pypi.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1149&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S0NmQ/btrMyfjy3Hd/JTrKQa8DxQkNglVgHbbKx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S0NmQ/btrMyfjy3Hd/JTrKQa8DxQkNglVgHbbKx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S0NmQ/btrMyfjy3Hd/JTrKQa8DxQkNglVgHbbKx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS0NmQ%2FbtrMyfjy3Hd%2FJTrKQa8DxQkNglVgHbbKx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1149&quot; height=&quot;337&quot; data-origin-width=&quot;1149&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 pip install requests를 복사후 centos에 입력을 하면 해당 패키지가 설치가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 파이썬 test 파일을 만들어보고 실행해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 메모장을 연후 해당 명령어를 작성후 저장합니다. (저는 이름을 test.txt로 저장했습니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1663650169298&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print('Hello, Python')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 해당 파일을 centos 안에 home 디렉터리에 넣은후 해당 명령어를 입력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1663650219590&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python test.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pHJ5q/btrMxXcrF8E/4usm1EWzob2eb76FMZrZI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pHJ5q/btrMxXcrF8E/4usm1EWzob2eb76FMZrZI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pHJ5q/btrMxXcrF8E/4usm1EWzob2eb76FMZrZI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpHJ5q%2FbtrMxXcrF8E%2F4usm1EWzob2eb76FMZrZI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;365&quot; height=&quot;34&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 파이썬파일이 잘 실행되었습니다.&lt;/p&gt;</description>
      <category>DevOps</category>
      <author>JaeSung Kim</author>
      <guid isPermaLink="true">https://aamoos.tistory.com/712</guid>
      <comments>https://aamoos.tistory.com/712#entry712comment</comments>
      <pubDate>Tue, 20 Sep 2022 14:04:15 +0900</pubDate>
    </item>
  </channel>
</rss>