<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>angela_.log</title>
        <link>https://velog.io/</link>
        <description>기록하기</description>
        <lastBuildDate>Fri, 12 Dec 2025 16:15:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>angela_.log</title>
            <url>https://velog.velcdn.com/images/angela_/profile/d353696e-fe5c-443f-ae3b-8850c490ec0f/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. angela_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/angela_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[자바스크립트] 콜백함수 개념]]></title>
            <link>https://velog.io/@angela_/%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@angela_/%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98</guid>
            <pubDate>Fri, 12 Dec 2025 16:15:16 GMT</pubDate>
            <description><![CDATA[<p>콜백함수는
만들어놓은 함수를 나중에 부를 수 있는 함수를 &quot;콜백&quot;이라고 하는건데 좀 더 정확히는
&quot;다른함수에게넘겨주고, 그 함수가 나중에 실행하는 함수&quot;를 콜백함수라고 한다.</p>
<pre><code class="language-javascript">function later(fn){
    fn();
}

later(()=&gt; console.log(&quot;Hello&quot;);
</code></pre>
<p>함수를 인자로 넘기고 다른함수가 later실행</p>
<pre><code class="language-javascript">later(  () =&gt; console.log(&quot;Hello&quot;))</code></pre>
<p>이 전체 함수가 later의 첫 번째 파라미터 자리에 들어감</p>
<pre><code class="language-javascript">fn = () =&gt; console.log(&quot;Hello&quot;)</code></pre>
<p>fn은 전달된 화살표 함수를 가리키는 변수!</p>
<pre><code class="language-javascript"> function later(fn) {                       │
│     fn();   ← ★ later가 콜백을 “부르는 부분&quot; │
│ }                                          │
└────────────────────────────────────────────┘
             ▲
             │  콜백 함수 전달
             │
              \ 
               \
                \
           () =&gt; console.log(&quot;Hello&quot;)
           ↑    ↑
  이것은 later가 아님!
  단지 later에게 ‘값으로 전달된 함수’</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Render] 스프링부트 배포하기(jsp, war)]]></title>
            <link>https://velog.io/@angela_/Render-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@angela_/Render-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 10 Oct 2025 14:57:40 GMT</pubDate>
            <description><![CDATA[<p>포트폴리오를 준비하다가 리액트는 vel? 배포를 쉽게 할 수 있는데 스프링부트는 무료배포가 없다고 알고있다가 render를 알게되서 적용해보기로 했다!</p>
<p>그런데 내가 작업한 뷰가 jsp로 작업을 했는데</p>
<blockquote>
<p>&quot;Spring Boot에서 JSP를 쓸 경우, 내장 Tomcat의 리소스 처리 방식이 제한되어서 jar보다 war로 패키징하는 게 일반적&quot;</p>
</blockquote>
<p>Render는 “JAR 실행형” 서버처럼 작동하는데,
Tomcat 같은 서버 컨테이너를 직접 배포하지는 못해요.</p>
<p>그래서 war 파일을 빌드해서도 Render에서 직접 배포는 불가능
(단독 Tomcat 서버를 Render가 띄워주지 않음)</p>
<p>그래서 Docker로 감싸야 함 </p>
<pre><code>[Render 서버]
    ⤷ [Docker 컨테이너]
        ⤷ Tomcat 설치됨
        ⤷ 내 war 배포됨 (/usr/local/tomcat/webapps/ROOT.war)
</code></pre><h3 id="도커파일이-필요한-이유">도커파일이 필요한 이유</h3>
<p>Render의 “native build”는 jar 실행만 지원하기 때문에,
JSP 프로젝트는 반드시 Dockerfile로 수동 배포해야 해요.</p>
<p>이부분을 모르고 있다가 검색하고 지피티한테 물어보면서 알게됨
로컬에서는 가능해서 처음엔 너무 당황했지만..! 
배포하면서 많이배웠음 ㅎㅎ</p>
<hr>
<h2 id="공통">공통</h2>
<h3 id="render-계정-준비">Render 계정 준비</h3>
<p><a href="https://render.com">https://render.com</a>
 접속 → GitHub 계정으로 로그인</p>
<p><a href="https://render.com%EC%A0%91%EC%86%8D">https://render.com접속</a> → GitHub 계정으로 회원가입후
Get started free 클릭</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/30d535e3-bdb5-45d5-be9f-f0dd91a8e4c4/image.png" alt=""></p>
<p>&quot;New +&quot; → &quot;Web Service&quot; 선택</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/78f63b2f-be2b-48ee-8b99-a8298e52a100/image.png" alt=""></p>
<p>Connect GitHub</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/ce256637-9644-40aa-a18f-0c3d7bab5af1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/2d814518-6df6-4866-ad30-376499074f84/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/23b4cad8-983f-4ce3-94c5-443b484763f2/image.png" alt=""></p>
<p>👉 연결 후 Blog_JPA 레포지토리 선택</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/317cc104-bf69-4583-85fa-93730f5a68ce/image.png" alt=""></p>
<h3 id="github-연결">GitHub 연결</h3>
<p>GitHub 연동 허용 → 배포할 리포지토리 선택</p>
<p>Render가 자동으로 build.gradle 또는 pom.xml을 감지함 (Maven/Gradle 프로젝트 둘 다 가능)</p>
<hr>
<h3 id="배포-설정">배포 설정</h3>
<p><img src="https://velog.velcdn.com/images/angela_/post/4a295b3e-c7a8-4b7d-bd83-0eecbeb38172/image.png" alt=""></p>
<p>Name    my-spring-app    서비스 이름
Region    Singapore or Oregon    가까운 리전 선택
Branch    main    배포할 브랜치
Build Command    ./mvnw clean package -DskipTests    </p>
<p>⚠️ Maven일 때
Start Command    java -jar target/*.jar    실행 명령어
Environment    Docker or Native    대부분은 Native로 가능</p>
<p>⚠️ Gradle일 경우
Build Command → ./gradlew build -x test
Start Command → java -jar build/libs/*.jar</p>
<h3 id="포트-설정">포트 설정</h3>
<p>Spring Boot 기본 포트는 8080인데, Render에서는 환경 변수 PORT 를 사용해야 해서</p>
<p>application.properties에 아래 한 줄 추가</p>
<pre><code>server.port=${PORT:8080}</code></pre><hr>
<h3 id="환경-변수-설정-db-등">환경 변수 설정 (DB 등)</h3>
<p>Render의 &quot;Environment&quot; 탭에서 환경변수를 추가할 수 있어.
예를 들어:</p>
<p>Key    Value
SPRING_PROFILES_ACTIVE    prod
DB_URL    jdbc:mysql://...
DB_USERNAME    root
DB_PASSWORD    mypassword</p>
<p>application.properties</p>
<pre><code>spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=${DB_URL}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect</code></pre><p><img src="blob:https://velog.io/96daa662-f450-491a-90f6-62f59e2c4415" alt="업로드중.."></p>
<h3 id="자동-배포">자동 배포</h3>
<p>GitHub에 코드 푸시하면 Render가 자동으로 다시 배포해 줘.
Logs 탭에서 배포 진행 상황과 오류 확인 가능.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/0f9b1473-2fe8-4457-8c5a-326d83c2c85d/image.png" alt=""></p>
<p>만약에 실패했다면 아래 이미지처럼 로그에 내용이 나와서 왜 안되는지 체크하면된다.
<img src="https://velog.velcdn.com/images/angela_/post/3520ce93-2f32-4d0c-b866-d14ef3f92374/image.png" alt=""></p>
<h3 id="성공-후-확인">성공 후 확인</h3>
<p><img src="blob:https://velog.io/c1bdb624-370a-4b68-addb-b74c8d9356b0" alt="업로드중..">
Render가 배포되면 https://<app-name>.onrender.com 주소가 생겨.
이 URL로 접속해서 앱이 잘 뜨는지 확인!
도메인이 <a href="https://blog-jpa.onrender.com%EB%B6%80%EB%B6%84%EC%9C%BC%EB%A1%9C">https://blog-jpa.onrender.com부분으로</a> 배포가 된게 확인되었다.!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[next.js] 레이아웃 페이지 만들기]]></title>
            <link>https://velog.io/@angela_/next.js-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@angela_/next.js-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Tue, 16 Sep 2025 13:39:13 GMT</pubDate>
            <description><![CDATA[<p>Next.js app/layout.tsx는 모든 페이지에서 공통으로 쓰이는 레이아웃</p>
<h3 id="layouttsx">layout.tsx</h3>
<pre><code class="language-tsx">import { Geist, Geist_Mono } from &quot;next/font/google&quot;;
import &quot;./globals.css&quot;;

const geistSans = Geist({
  variable: &quot;--font-geist-sans&quot;,
  subsets: [&quot;latin&quot;],
});

const geistMono = Geist_Mono({
  variable: &quot;--font-geist-mono&quot;,
  subsets: [&quot;latin&quot;],
});

// function RootLayout() 함수이름이 컴포넌트이름
// {children} props로 들어오는 자식 컴포넌트
// return jsx를 반환
// TypeScript에서는 props 타입 정의 ({ children: React.ReactNode }) 가능
export default function RootLayout({
  children,
}: Readonly&lt;{
  children: React.ReactNode;
}&gt;) {
  return (
    &lt;html lang=&#39;ko&#39;&gt;
      &lt;body
        className={`${geistSans.variable} ${geistMono.variable} antialiased bg-black text-white`}
      &gt;
        {/* 헤더 */}
        &lt;header className=&#39;fixed top-0 left-0 w-full bg-black shadow z-50&#39;&gt;
          &lt;nav className=&#39;max-w-4xl mx-auto px-4 py-3 flex justify-between&#39;&gt;
            &lt;span className=&#39;font-bold&#39;&gt;SUJINJEONG PORTPOLIO&lt;/span&gt;
            &lt;ul className=&#39;flex gap-4&#39;&gt;
              &lt;li&gt;
                &lt;a href=&#39;/&#39;&gt;홈&lt;/a&gt;
              &lt;/li&gt;
              &lt;li&gt;
                &lt;a href=&#39;/about&#39;&gt;About&lt;/a&gt;
              &lt;/li&gt;
              &lt;li&gt;
                &lt;a href=&#39;/projects&#39;&gt;projects&lt;/a&gt;
              &lt;/li&gt;
              &lt;li&gt;
                &lt;a href=&#39;/contact&#39;&gt;contact&lt;/a&gt;
              &lt;/li&gt;
            &lt;/ul&gt;
          &lt;/nav&gt;
        &lt;/header&gt;
        {/* 본문 (헤더 높이만큼 여백 줘야 함) */}
        &lt;main className=&#39;pt-16&#39;&gt;{children}&lt;/main&gt;

        {/* 푸터 */}
        &lt;footer className=&#39;fixed bottom-0 left-0 w-full bg-black-200 text-center py-3 shadow&#39;&gt;
          © 2025 내 사이트
        &lt;/footer&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  );
}
</code></pre>
<h3 id="pagetsx">page.tsx</h3>
<pre><code class="language-jsx">import Link from &quot;next/link&quot;;

export default function Home() {
  return (
    &lt;main className=&#39;flex flex-col items-center justify-center min-h-screen p-6&#39;&gt;
      &lt;h1 className=&#39;text-4xl font-bold&#39;&gt;안녕하세요, 제 포트폴리오입니다!&lt;/h1&gt;
      &lt;p className=&#39;mt-4 text-lg text-gray-600&#39;&gt;
        Next.js + TailwindCSS로 만든 개인 사이트 🚀
      &lt;/p&gt;
      &lt;Link
        href=&#39;/about&#39;
        className=&#39;mt-6 px-4 bg-blue-500 text-white rounded-lg hover:bg-blue-600&#39;
      &gt;
        About
      &lt;/Link&gt;
    &lt;/main&gt;
  );
}
</code></pre>
<h3 id="appaboutpagetsx">app/about/page.tsx</h3>
<pre><code class="language-tsx">export default function About() {
  return (
    &lt;main className=&#39;p-6&#39;&gt;
      &lt;h1 className=&#39;text-3xl font-semibold&#39;&gt;About Me&lt;/h1&gt;
      &lt;p className=&#39;mt-2 text-gray-700&#39;&gt;
        저는 프론트엔드 개발자 입문자입니다~~~✨
      &lt;/p&gt;
    &lt;/main&gt;
  );
}
</code></pre>
<p>이렇게 하고 디렉토리에서 <a href="http://localhost:3000/about">http://localhost:3000/about</a> 들어가면 </p>
<p><img src="https://velog.velcdn.com/images/angela_/post/872f1473-323e-4311-b8fd-b3a4b2f1822b/image.png" alt=""></p>
<p>화면이 이렇게 나온당! 
그럼 헤더부분은 분리랑, 링크 Link넣어서 이동되는지 확인!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[next.js] 간단한 사이트 셋팅하기]]></title>
            <link>https://velog.io/@angela_/next.js-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%85%8B%ED%8C%85%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@angela_/next.js-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%85%8B%ED%8C%85%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 16 Sep 2025 00:58:09 GMT</pubDate>
            <description><![CDATA[<pre><code>npx create-next-app@latest my-portfolio 입력하면

Need to install the following packages:
create-next-app@15.5.3
Ok to proceed? (y) y  입력해주면 
아래에 </code></pre><p><img src="https://velog.velcdn.com/images/angela_/post/c02d7fbb-e559-4bd6-907f-65f062b311d6/image.png" alt=""></p>
<p>✅ Would you like to use TypeScript?
Yes 선택하면 TypeScript 기반의 Next.js 프로젝트
✅ Which linter would you like to use? › - Use arrow-keys. Return to submit.
❯   ESLint &lt;&lt; 선택
    More comprehensive lint rules
    Biome
    None
그럼 
Next.js + TypeScript + ESLint 환경이 세팅</p>
<p>✅ Would you like to use Tailwind CSS? … No / <strong>Yes</strong>
✅ Would you like your code inside a src/ directory? › <strong>No</strong> / Yes
✅ Would you like to use App Router? (recommended) › No / <strong>Yes</strong>
App Router → Next.js 13 이후 기본 라우팅 방식
✅ Would you like to use Turbopack? (recommended) › No / <strong>Yes</strong>
Turbopack → Next.js 13에서 새롭게 도입된 빠른 번들러
✅ Would you like to customize the import alias (@/* by default)? › <strong>No</strong> / Yes</p>
<table>
<thead>
<tr>
<th>옵션</th>
<th>추천 선택</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>TypeScript</td>
<td>Yes</td>
<td>나중에 확장성, 타입 안정성</td>
</tr>
<tr>
<td>Linter</td>
<td>ESLint</td>
<td>코드 정리, 오류 예방</td>
</tr>
<tr>
<td>Tailwind CSS</td>
<td>Yes</td>
<td>빠른 디자인 구현</td>
</tr>
<tr>
<td><code>src/</code> 폴더</td>
<td>No</td>
<td>작은 프로젝트라 루트로 충분</td>
</tr>
<tr>
<td>App Router</td>
<td>Yes</td>
<td>최신 Next.js 기능 활용</td>
</tr>
<tr>
<td>Turbopack</td>
<td>Yes</td>
<td>최신 번들러 경험, 속도 빠름</td>
</tr>
<tr>
<td>Import Alias</td>
<td>No</td>
<td>단순 구조라 필요 없음</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/angela_/post/df35ae37-d561-4c8f-a367-98b319aabedc/image.png" alt=""></p>
<p><a href="http://localhost:3000">http://localhost:3000</a> 여기로 접속하면,
<img src="https://velog.velcdn.com/images/angela_/post/560e6039-d6bf-4bef-84bf-2a9c20e98731/image.png" alt=""></p>
<p>이렇게 나오면 Next.js 개발서버가 정상적으로 켜졌고, 셋팅은 잘된거같다!!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[라운드로빈(RR)선점 스케줄러 계산]]></title>
            <link>https://velog.io/@angela_/%EB%9D%BC%EC%9A%B4%EB%93%9C%EB%A1%9C%EB%B9%88RR-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC-%EA%B3%84%EC%82%B0</link>
            <guid>https://velog.io/@angela_/%EB%9D%BC%EC%9A%B4%EB%93%9C%EB%A1%9C%EB%B9%88RR-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC-%EA%B3%84%EC%82%B0</guid>
            <pubDate>Sun, 20 Jul 2025 12:34:13 GMT</pubDate>
            <description><![CDATA[<p>2025년 2회차에 나온 라운드로빈 계산을 다시 했는데 
헷갈리는 부분이 생겼다.</p>
<table>
<thead>
<tr>
<th align="center">프로세스</th>
<th align="center">도착시간</th>
<th align="center">실행시간</th>
</tr>
</thead>
<tbody><tr>
<td align="center">P1</td>
<td align="center">0</td>
<td align="center">5</td>
</tr>
<tr>
<td align="center">P2</td>
<td align="center">1</td>
<td align="center">7</td>
</tr>
<tr>
<td align="center">P3</td>
<td align="center">3</td>
<td align="center">4</td>
</tr>
<tr>
<td align="center">타임슬라이스 2라고 한다면 계산식을 하는데</td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<p>p1에 바로 도착해서 실행하고 2초간격이라면 1이 남는데 그런상태에 일단 p2가 실행되는거면 p3가 아니라 p1이 오는게 이해가 안됐거든.</p>
<p>찾아보니
라운드로빈만의 특징이고,
&quot;도착한 순서대로 순환 큐(Round Queue) 에 따라 돌아가기 때문&quot;</p>
<h3 id="스케줄링-방식----동작-방식-요약">스케줄링 방식    동작 방식 요약</h3>
<ul>
<li>FCFS (First Come First Served)    도착 순서대로, 한 프로세스 끝날 때까지 절대 안 바꿈</li>
<li>SJF (Shortest Job First)    도착한 것 중 실행시간 제일 짧은 것 먼저</li>
<li>라운드로빈 (RR)    도착한 순서대로 큐에 넣고, 타임퀀텀만큼 실행 → 남은 건 큐 뒤로</li>
</ul>
<h3 id="💡-핵심-포인트">💡 핵심 포인트</h3>
<p>p3가 도착했어도, 그 시점에서 큐에 먼저 들어와 있던 p1, p2가 앞선 순서이기 때문에
p1이 완전히 끝나지 않았더라도 다시 돌아올 차례가 되면 먼저 실행됨
라운드로빈은 도착 후 큐에 들어온 순서를 철저히 유지함
그래서 p3보다 먼저 들어온 p1이 먼저 실행되는 건 정상이고,
이건 라운드로빈이니까 그런 것이라는걸 알게됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스프링부트] 자동 주입[@RequiredArgsConstructor]과 명시적 생성자의 차이]]></title>
            <link>https://velog.io/@angela_/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8</link>
            <guid>https://velog.io/@angela_/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8</guid>
            <pubDate>Sat, 01 Feb 2025 16:34:43 GMT</pubDate>
            <description><![CDATA[<pre><code>&lt;h1&gt;마이페이지&lt;/h1&gt;
&lt;p id=&quot;useruid&quot;&gt;사용자 UID: &lt;/p&gt;
&lt;p id=&quot;useremail&quot;&gt;이메일: &lt;/p&gt;


&lt;script&gt;
 // 사용자 정보
document.getElementById(&quot;useruid&quot;).innerText = `사용자 ID: ${data.uid}`;
document.getElementById(&quot;useremail&quot;).innerText = `이메일: ${data.email}`;
&lt;/script&gt;</code></pre><p>부분을 할때 </p>
<pre><code class="language-java">PrincipalDetails userDetails = (PrincipalDetails) authentication.getPrincipal();
System.out.println(&quot;📌 인증된 사용자 UID: &quot; + userDetails.getUsername()); // 확인용


response.put(&quot;uid&quot;, userDetails.getUsername());
response.put(&quot;email&quot;, userDetails.getUserDto().getEmail());
</code></pre>
<p>getUserDto().getEmail()) 부분에서 사용자를 찾을 수 없다는 메시지가 나와서 확인해보니,PrincipalDetails 부분에서 
‼️ userDto을 초기화를 제대로 하지않아서 발생한 오류였음.</p>
<p>@RequiredArgsConstructor는 모든 final 필드를 매개변수로 받는 생성자를 자동 생성해 줘.
문제는 userDto는 UserDto.fromEntity(user); 를 통해 변환해야 하는데, 자동 생성된 생성자는 이를 처리하지 못함! ❌</p>
<pre><code class="language-java">@RequiredArgsConstructor
public class PrincipalDetails implements UserDetails {

private final User user;  // User 엔티티를 직접 사용
    private final UserDto userDto; // ❌ @RequiredArgsConstructor로 해결되지 않음
}
    public PrincipalDetails(User user) { //직접 생성자 작성
        this.user=user;
        this.userDto=UserDto.fromEntity(user); // UserDto를 변환해서 초기화
    }

    }
</code></pre>
<p>이렇게 생성하고 다시 확인하면 제대로 
userDetails.getUserDto().getEmail()); 제대로 출력됨!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] EKS 스냅샷 생성 및 장애 복구 방법]]></title>
            <link>https://velog.io/@angela_/AWS-EKS-%EC%8A%A4%EB%83%85%EC%83%B7%EC%83%9D%EC%84%B1-%EC%9E%A5%EC%95%A0-%EB%B3%B5%EA%B5%AC%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@angela_/AWS-EKS-%EC%8A%A4%EB%83%85%EC%83%B7%EC%83%9D%EC%84%B1-%EC%9E%A5%EC%95%A0-%EB%B3%B5%EA%B5%AC%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 22 Jan 2025 12:21:08 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>스냅샷(Snapshot) 은 현재 상태를 저장하여 나중에 해당 상태로 복원할 수 있는 일종의 백업 파일이다</p>
</blockquote>
<h3 id="환경설정">환경설정</h3>
<h4 id="external-snapshotter-crd-다운로드-및-설치">external-snapshotter CRD 다운로드 및 설치</h4>
<pre><code>curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml

kubectl apply -f snapshot.storage.k8s.io_volumesnapshots.yaml,snapshot.storage.k8s.io_volumesnapshotclasses.yaml,snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl get crd | grep snapshot</code></pre><p>VolumeSnapshot 리소스를 사용하려면 먼저 Kubernetes 클러스터에 필요한 Custom Resource Definitions(CRDs)을 등록</p>
<ul>
<li>volumesnapshots는 스냅샷을 생성하고 관리할 수 있게 해줍니다.</li>
<li>volumesnapshotclasses는 스냅샷 클래스(어떤 스토리지 시스템에서 스냅샷을 생성할지 설정)를 정의합니다.</li>
<li>volumesnapshotcontents는 스냅샷의 내용을 설명합니다.</li>
</ul>
<h4 id="snapshot-controller-설치-및-배포">Snapshot Controller 설치 및 배포</h4>
<pre><code class="language-yaml"># rbac-snapshot-controller.yaml 파일을 다운로드
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
# setup-snapshot-controller.yaml 파일 다운로드
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml

# 두 개의 YAML 파일을 Kubernetes 클러스터에 적용하는 명령어
kubectl apply -f rbac-snapshot-controller.yaml,setup-snapshot-controller.yaml

# 배포 상태 확인
kubectl get deploy -n kube-system snapshot-controller
</code></pre>
<ul>
<li>snapshot-controller가 실행되는 데 필요한 RBAC (Role-Based Access Control) 권한을 설정하는 데 사용</li>
<li>snapshot-controller를 배포하는 Kubernetes 매니페스트 파일</li>
</ul>
<pre><code class="language-yaml"># VolumeSnapshotClass 생성
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/examples/kubernetes/snapshot/manifests/classes/snapshotclass.yaml
kubectl apply -f snapshotclass.yaml
kubectl get vsclass </code></pre>
<ul>
<li>AWS EBS에서 스냅샷을 생성하기 위한 VolumeSnapshotClass 리소스의 YAML 파일을 다운로드</li>
<li>다운로드되는 파일은 snapshotclass.yaml로, 이 파일은 EBS에서 스냅샷을 관리하는 설정</li>
</ul>
<h4 id="ebs-볼륨의-kubernetes-스냅샷-생성">EBS 볼륨의 Kubernetes 스냅샷 생성</h4>
<pre><code class="language-yaml">apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot # EBS 볼륨의 스냅샷을 생성
metadata:
  name: ebs-volume-snapshot # VolumeSnapshot의 이름을 ebs-volume-snapshot
spec:
  volumeSnapshotClassName: csi-aws-vsc
  source:
    persistentVolumeClaimName: my-pvc-claim</code></pre>
<p><img src="https://velog.velcdn.com/images/angela_/post/b6ddc41f-b23b-4d8a-9e39-0d78e9532f66/image.png" alt=""></p>
<p>스냅샷이 생성된걸 확인할 수 있다.</p>
<h4 id="장애발생-복구가능한지-테스트">장애발생, 복구가능한지 테스트</h4>
<p>만들었던 ebs-my-app[pod] 와 my-pvc-claim[pvc] 를 삭제
이름은 변경해서 지금 코드와  캡쳐본이 다름.! 
<img src="https://velog.velcdn.com/images/angela_/post/150f0830-c5d8-4fc2-9a61-92341bf54fd2/image.png" alt=""></p>
<p>🔖 볼륨이 삭제되는지는 ReclaimPolicy 설정에 따라 다른데. 
Retain: PVC를 삭제해도 EBS 볼륨은 삭제되지 않고 남아있다.
Delete: PVC를 삭제하면 자동으로 EBS 볼륨도 삭제됨</p>
<blockquote>
<p>❗️❗️ 동적 프로비저닝을 사용하면 Kubernetes가 PersistentVolume (PV) 리소스를 자동으로 생성되기 때문에, ReclaimPolicy가 기본값인 Delete로 설정된다. ❗️ ❗️</p>
</blockquote>
<p>다시 pvc,pod를 생성해주면 볼륨이 다시 생긴다.!
pvc 생성 </p>
<pre><code>apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-snapshot-restored-claim
spec:
  storageClassName: my-storage-class 
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
  dataSource:
    name: ebs-volume-snapshot # 복원할 스냅샷의 이름을 적어줘야함.
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io</code></pre><p>pod생성</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: ebs-my-app
spec:
  containers:
  - name: app
    image: centos
    command: [&quot;/bin/sh&quot;]
    args: [&quot;-c&quot;, &quot;while true; do echo $(date -u) &gt;&gt; /data/out.txt; sleep 10; done&quot;]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: ebs-snapshot-restored-claim</code></pre><p><img src="https://velog.velcdn.com/images/angela_/post/2978dfd0-e2c8-402e-ab29-1d5774c4eee3/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] 동적 EBS 볼륨 프로비저닝을 구현(feat스냅샷)]]></title>
            <link>https://velog.io/@angela_/eks-%EB%B3%BC%EB%A5%A8-%EC%8A%A4%EB%83%85%EC%83%B7-%EC%9E%A5%EC%95%A0-%EB%B3%B5%EA%B5%AC%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@angela_/eks-%EB%B3%BC%EB%A5%A8-%EC%8A%A4%EB%83%85%EC%83%B7-%EC%9E%A5%EC%95%A0-%EB%B3%B5%EA%B5%AC%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 22 Jan 2025 01:35:18 GMT</pubDate>
            <description><![CDATA[<h3 id="amazon-ebs-csi-driver를-이용하여-동적-ebs-볼륨-프로비저닝을-구현">Amazon EBS CSI Driver를 이용하여 동적 EBS 볼륨 프로비저닝을 구현</h3>
<p>Volume Snapshot 기능을 설정 및 활용</p>
<h4 id="amazon-ebs-csi-driver-설치-및-설정">Amazon EBS CSI Driver 설치 및 설정</h4>
<ul>
<li>aws-ebs-csi-driver 애드온을 설치하기 전 IRSA (IAM Roles for Service Accounts) 생성.</li>
<li>EBS 볼륨 드라이버를 클러스터에 설치한 후 kubectl 명령어로 구성 요소 및 배포 상태를 확인.</li>
<li>IRSA에 올바른 권한 (AmazonEBSCSIDriverPolicy)이 연결되었는지 재확인.</li>
</ul>
<pre><code class="language-bash"># IRSA 생성
eksctl create iamserviceaccount \
  --name ebs-csi-controller-sa \
  --namespace kube-system \
  --cluster ${CLUSTER_NAME} \
  --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
  --approve \
  --role-only \
  --role-name AmazonEKS_EBS_CSI_DriverRole

# IRSA 확인
eksctl get iamserviceaccount --cluster ${CLUSTER_NAME}


#  EBS CSI Driver 설치
eksctl create addon --name aws-ebs-csi-driver\ #설치할 애드온의 이름
 --cluster ${CLUSTER_NAME}\ #설치할 클러스터의 이름을 지정
 --service-account-role-arn arn:aws:iam::${ACCOUNT_ID}:role/AmazonEKS_EBS_CSI_DriverRole\ #EBS CSI Driver가 사용하는 IAM 역할 ARN을 지정
 --force #EBS CSI Driver가 설치된 경우에도 강제로 재설치를 수행</code></pre>
<h4 id="동적-프로비저닝">동적 프로비저닝</h4>
<ul>
<li>StorageClass를 정의하고, PVC를 통해 EBS 볼륨을 자동으로 생성 및 관리.</li>
</ul>
<p>1.스토리지가 동적으로 프로비저닝
StorageClass</p>
<pre><code class="language-yaml">apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: my-storage-class
allowVolumeExpansion: true #pvc가 동적으로 생성한 ebs볼륨의 크기를 이후에 확장할수있다.
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer # Pod 배치 후 프로비저닝
parameters:
  type: gp3 #EBS 볼륨 유형을 지정
  allowAutoIOPSPerGBIncrease: &#39;true&#39; #IOPS 최적화 설정
  encrypted: &#39;true&#39; # 생성되는 EBS 볼륨은 기본적으로 암호화</code></pre>
<p>2.PVC(PersistentVolumeClaim) 생성</p>
<pre><code class="language-yaml">apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc-claim # PVC의 이름
spec:
  accessModes:
    - ReadWriteOnce # 단일 노드에서 읽기와 쓰기가 가능,AWS EBS 볼륨은 일반적으로 ReadWriteOnce 모드만 지원
  resources:
    requests:
      storage: 4Gi # 4GiB 크기의 스토리지를 요청
  storageClassName: my-storage-class # PVC가 참조할 StorageClass의 이름</code></pre>
<p>3.Pod 생성
PVC를 사용하는 Pod를 정의.
Pod가 실행될 때 PVC와 연결된 볼륨이 노드에 자동으로 마운트된다.</p>
<pre><code class="language-yaml">apiVersion: v1 # v1 버전의 API
kind: Pod #파일이 정의하는 리소스 종류
metadata:
  name: ebs-my-app # pod의 이름
spec:
  terminationGracePeriodSeconds: 3 #Pod가 종료될 때, 종료 신호를 받은 후 3초 동안 종료를 기다리도록 설정
  containers: # Pod 내에서 실행될 컨테이너에 대한 설정을 정의
  - name: app # 컨테이너의 이름
    image: centos # CentOS 이미지를 사용하여 컨테이너를 실행
    command: [&quot;/bin/sh&quot;] # 컨테이너가 실행될 때 실행할 명령어를 지정, 여기서는 shell로 지정
    args: [&quot;-c&quot;, &quot;while true; do echo $(date -u) &gt;&gt; /data/out.txt; sleep 10; done&quot;] # 매 10초마다 UTC 시간(date -u)을 /data/out.txt 파일에 기록, Pod 내에서 마운트된 EBS 볼륨을 통해 저장
    volumeMounts: # 컨테이너에서 사용할 볼륨을 정의하는 부분
    - name: persistent-storage # 볼륨의 이름
      mountPath: /data #볼륨은 컨테이너 내에서 /data 경로에 마운트
  volumes: #스토리지(볼륨)를 정의하는 부분
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: my-pvc-claim # PVC를 사용하여 Pod에 스토리지를 연결</code></pre>
<p>이렇게하면 볼륨이 자동으로 생성 동적프로비저닝이 된다.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/2978dfd0-e2c8-402e-ab29-1d5774c4eee3/image.png" alt=""></p>
<h4 id="파일내용확인">파일내용확인</h4>
<pre><code>kubectl exec ebs-my-app -- tail -f /data/out.txt</code></pre><pre><code>2025-01-22T10:15:25Z
2025-01-22T10:15:35Z
2025-01-22T10:15:45Z
...</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Kubenetes] 모니터링, 프로메테우스, 그라파나 설정하기]]></title>
            <link>https://velog.io/@angela_/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-%ED%94%84%EB%A1%9C%EB%A9%94%ED%85%8C%EC%9A%B0%EC%8A%A4</link>
            <guid>https://velog.io/@angela_/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-%ED%94%84%EB%A1%9C%EB%A9%94%ED%85%8C%EC%9A%B0%EC%8A%A4</guid>
            <pubDate>Tue, 14 Jan 2025 12:58:26 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angela_/post/bc5bcf2e-9586-442a-8e0a-a73d62eb75e9/image.png" alt=""></p>
<p>Kubernetes 환경에서 Prometheus 및 관련 모니터링을 하기위해 설정해야 할 것들</p>
<pre><code>mkdir $HOME/advanced/monitoring/prometheus
cd $HOME/advanced/monitoring/prometheus
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm pull prometheus-community/kube-prometheus-stack
</code></pre><p>Helm 차트를 커스터마이징하고, Kubernetes 클러스터에서 Prometheus 기반 모니터링 스택을 설정하기 위한 전형적인 워크플로우</p>
<pre><code>ls
tar xvfz kube-prometheus-stack-68.0.0.tgz 
# 이름을 버전도 함께 변경해준다.
mv kube-prometheus-stack kube-prometheus-stack-
68.0.0
# 해당디렉토리 이동
cd kube-prometheus-stack-68.0.0/
# values를 my-values로 복사해서 만들어주기
cp values.yaml my-values.yaml</code></pre><p>vi my-values.yaml 에서 변경하고 </p>
<pre><code class="language-yaml">vi my-values.yaml 
...
serviceMonitorSelector...: false

...
retentionSize: &quot;1GiB&quot;</code></pre>
<p><img src="https://velog.velcdn.com/images/angela_/post/5a0ba1c3-e755-435f-a9c7-fe91ba8bd4bb/image.png" alt=""></p>
<pre><code class="language-bash">kubectl get svc --namespace monitoring</code></pre>
<p>네임스페이스로 검색하면 지금 TYPE이 
<code>kube-prometheus-stack-1736-alertmanager</code>
이 <code>NodePort</code>로 되어있는데 이것이 <code>ClusterIP 클러스터아이피</code>로 되어야 하고,
<code>kube-prometheus-stack-1736-prometheus 이 ClusterIP 아니라 NodePort</code>가 되어야한
다.</p>
<pre><code>kubectl edit svc kube-prometheus-stack-1736-prometheus --namespace monitoring  </code></pre><p><img src="https://velog.velcdn.com/images/angela_/post/318deeab-4e16-48bd-9700-c3b79064ac68/image.png" alt=""></p>
<pre><code>type: NodePort로 변경 후 
:wq로 저장해주고 나온다.
나머지 kube-prometheus-stack-1736-alertmanager도 동일하게 해준다.</code></pre><hr>
<pre><code class="language-bash">cd $HOME/advanced/monitoring/prometheus/kube-prometheus-stack-68.0.0
kubectl create namespace monitoring
kubectl get namespace
helm install --namespace monitoring --generate-name prometheus-community/kube-prometheus-stack -f my-values.yaml

# 조회하기
kubectl get all --namespace monitoring

# 1736836970 이건 각자 번호로 
kubectl port-forward --address 0.0.0.0 svc/kube-prometheus-stack-1736836970-prometheus-node-exporter 8080:9100 --namespace monitoring
</code></pre>
<p>마스터노드 포트포워딩 설정하기
<img src="https://velog.velcdn.com/images/angela_/post/efaf1d2d-c763-4723-adc5-bc86f4b193ec/image.png" alt=""></p>
<p>외부접속확인! 
127.0.0.1:8888
<img src="https://velog.velcdn.com/images/angela_/post/8e9152a8-dc5d-4254-ba02-0eff76db23c4/image.png" alt=""></p>
<pre><code>kubectl get svc --namespace monitoring
# 해당워커노드에서 확인해보기 
kubectl get pods -o wide -n monitoring</code></pre><p><img src="https://velog.velcdn.com/images/angela_/post/75366ca5-0204-4911-8a8a-64d21ca5fbce/image.png" alt=""></p>
<p>포트포워딩 :30539 접속확인하기, 워커노드1에 포트포워딩하기</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/70efaa58-04af-453b-84a6-71048b0ebc44/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/589aa1d7-277c-438c-b7cf-be81f48abf5f/image.png" alt=""></p>
<hr>
<h4 id="grafana-그라파나">Grafana 그라파나</h4>
<p>프로메테우스가 시각화가 약하므로 그라파나와 함께 사용~!</p>
<pre><code>mkdir $HOME/advanced/monitoring/prometheus
cd $HOME/advanced/monitoring/prometheus

kubectl get svc --namespace monitoring

kubectl get svc kube-prometheus-stack-1736836970-grafana --namespace monitoring -o yaml
kubectl patch svc kube-prometheus-stack-1736836970-grafana -n monitoring -o yaml -p &#39;{&quot;spec&quot;: {&quot;type&quot;: &quot;NodePort&quot;}}&#39;

kubectl get svc --namespace monitoring
kubectl get svc kube-prometheus-stack-1736836970-grafana --namespace monitoring -o yaml
</code></pre><p><img src="https://velog.velcdn.com/images/angela_/post/e52bc9dd-662e-4ea3-a2bb-4a96037f13ff/image.png" alt=""></p>
<p>포트포워딩 설정
127.0.0.1:31336 로 접속하면 로그인창이 나오는데 </p>
<p>secret은 디코딩으로 되어있어서 인코딩으로 
변경해주는 작업을 해야한다.</p>
<pre><code>kubectl get secret kube-prometheus-stack-1736836970-grafana --namespace monitoring -o yaml
kubectl get secret kube-prometheus-stack-1736836970-grafana --namespace monitoring -o jsonpath=&quot;{.data.admin-user}&quot; | base64 -d
kubectl get secret kube-prometheus-stack-
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] 우분투 IP 확인과 포트 포워딩을 통한 네트워크 설정]]></title>
            <link>https://velog.io/@angela_/Linux-%EC%9A%B0%EB%B6%84%ED%88%AC-IP-%ED%99%95%EC%9D%B8%EA%B3%BC-%ED%8F%AC%ED%8A%B8-%ED%8F%AC%EC%9B%8C%EB%94%A9%EC%9D%84-%ED%86%B5%ED%95%9C-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@angela_/Linux-%EC%9A%B0%EB%B6%84%ED%88%AC-IP-%ED%99%95%EC%9D%B8%EA%B3%BC-%ED%8F%AC%ED%8A%B8-%ED%8F%AC%EC%9B%8C%EB%94%A9%EC%9D%84-%ED%86%B5%ED%95%9C-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sat, 14 Dec 2024 07:35:53 GMT</pubDate>
            <description><![CDATA[<p>ifconfig 명령어를 사용하면 가상 머신에서 네트워크 통신에 필요한 내부 IP 주소를 확인할 수 있다.
호스트(맥북)나 다른 기기에서 가상 머신으로 접근하려면 이 IP 주소를 알아야 하기 때문에,
ifconfig를 통해 IP 주소를 확인하는 것이 중요하다.</p>
<blockquote>
<p>ifconfig 를 하면 설치하라는 문구가 나오면, 
sudo apt update
sudo apt install net-tools 설치하면 이렇게 나온다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/angela_/post/4da35e76-fedc-4d20-8138-788b4d7770ba/image.png" alt=""></p>
<h3 id="utm에서-네트워크-설정">UTM에서 네트워크 설정</h3>
<p>우분투를  정지를 한 상태에서만 실행이되서 멈추고 오른쪽 클릭을 하면 Edit로 들어가면 된다.
<img src="https://velog.velcdn.com/images/angela_/post/37c1f698-b15f-4da4-9f44-470e6fc37226/image.png" alt=""></p>
<p>네트워크 탭에서 
Network Mode를 <code>&#39;Emulated VLAN&#39;</code>을 설정하면 <code>포트포워딩</code>이라는게 나온다. </p>
<p><img src="https://velog.velcdn.com/images/angela_/post/61428170-77fd-444e-8225-c00cc62e0d66/image.png" alt=""></p>
<p>다시 네트워크로 들어가서 <code>추가</code>를 누르면 네트워크연결하는 주소랑 포트를 입력해주면 된다. </p>
<p><img src="https://velog.velcdn.com/images/angela_/post/26cdefd4-577b-4666-b33b-f29aa6c9aeda/image.png" alt=""></p>
<h3 id="포트-포워딩을-설정하는-이유">포트 포워딩을 설정하는 이유</h3>
<p>호스트(맥북)와 가상머신(UTM) 간의 네트워크 연결을 구성해야 호스트에서 가상머신 내부의 서비스를 사용할 수 있기 때문에</p>
<blockquote>
<p>위에서 확인한 ifconfig 주소</p>
</blockquote>
<ul>
<li>호스트(맥북) 루프백 IP 주소:  127.0.0.1</li>
<li>게스트(가상머신)에게 할당된 사설(유동)IP : 10.0.2.15</li>
</ul>
<p><img src="https://velog.velcdn.com/images/angela_/post/38fea0f8-e4b3-40f1-bd6b-b15b016c8fae/image.png" alt=""></p>
<h4 id="흐름">흐름</h4>
<ol>
<li>127.0.0.1:8081에 요청이 들어오면,</li>
<li>포트 포워딩 규칙에 따라, 요청이 10.0.2.15:8080으로 전달</li>
<li>10.0.2.15:8080에서 요청을 처리한 후, 응답을 다시 127.0.0.1:8081로 보내고,</li>
<li>호스트 시스템에서 로컬로 응답</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] 맥북(M1)에서 우분투를 설치하고 Terminus로 연결 및 환경 설정하기]]></title>
            <link>https://velog.io/@angela_/Linux-%EB%A7%A5%EB%B6%81M1%EC%97%90-UTM%EC%9C%BC%EB%A1%9C-%EC%9A%B0%EB%B6%84%ED%88%AC-%EC%84%A4%EC%B9%98%ED%95%98%EA%B3%A0-Terminus%EB%A1%9C-%EC%A0%91%EC%86%8D%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@angela_/Linux-%EB%A7%A5%EB%B6%81M1%EC%97%90-UTM%EC%9C%BC%EB%A1%9C-%EC%9A%B0%EB%B6%84%ED%88%AC-%EC%84%A4%EC%B9%98%ED%95%98%EA%B3%A0-Terminus%EB%A1%9C-%EC%A0%91%EC%86%8D%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 14 Dec 2024 07:34:08 GMT</pubDate>
            <description><![CDATA[<p>원격 연결을 위해서는 먼저 UTM을 통해 실행 중인 리눅스 가상머신을 켜고 해당 가상머신에 로그인을 해야한다 
Terminus와 같은 도구를 사용하여 원격 접속이 가능</p>
<h3 id="1-utm에서-리눅스-가상머신-실행">1. utm에서 리눅스 가상머신 실행</h3>
<p><img src="https://velog.velcdn.com/images/angela_/post/0605a1cb-eefb-47c8-a6e9-8caad13f80bd/image.png" alt=""></p>
<h3 id="2-termius-다운로드">2. termius 다운로드</h3>
<p><a href="https://termius.com/">https://termius.com/</a> 접속후 google 가입을 하면 다운로드 
<img src="https://velog.velcdn.com/images/angela_/post/8d4905d4-f1e6-43c3-881b-6733ff85582e/image.png" alt=""></p>
<p>설치해주면 된다.!
<img src="https://velog.velcdn.com/images/angela_/post/cb6abc8e-a92b-473d-ba53-9d519c5a8425/image.png" alt=""></p>
<p>Terminus앱을 실행하면,</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/3af43353-8bb7-43c7-91f2-22fb412dc8eb/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/6917d7b1-3743-487e-a2b5-2a5cc40bcb8f/image.png" alt=""></p>
<p>여기에서 9999포트는 utm에서 포트포워딩으로 설정한 포트를 적용해주면된다.
<img src="https://velog.velcdn.com/images/angela_/post/e5f53295-17c7-48cc-8430-b43cf40ce77f/image.png" alt=""></p>
<p>그럼 제대로 연결 성공 !
<img src="https://velog.velcdn.com/images/angela_/post/95302b42-3acf-4284-a064-ce8c4404324b/image.png" alt=""></p>
<h3 id="기본환경세팅">기본환경세팅</h3>
<p><img src="https://velog.velcdn.com/images/angela_/post/2b52ab94-3776-4ccf-b90e-b88122a27165/image.png" alt=""></p>
<h4 id="설치-예시-코드">설치 예시 코드</h4>
<p><img src="https://velog.velcdn.com/images/angela_/post/95bd8bd6-a370-43d6-9a46-1982d74634c4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/d0a15700-4d8b-401e-9768-d36925335de8/image.png" alt=""></p>
<h4 id="필요한-환경-설정과-세팅">필요한 환경 설정과 세팅</h4>
<pre><code># 현재시간 확인
timedatectl
# timezone 확인
timedatectl list-timezones
# timezone
sudo timedatectl set-timezone Asia/Seoul</code></pre><pre><code># tree 설치
sudo apt update
sudo apt install tree
# snap 패키지 관리자로 설치하기
sudo snap install tree

</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] 맥북(m1)에 가상화머신(UTM) 우분투 설치하기 ]]></title>
            <link>https://velog.io/@angela_/Linux-%EB%A7%A5%EB%B6%81m1%EC%97%90-%EC%9A%B0%EB%B6%84%ED%88%AC-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-UTM</link>
            <guid>https://velog.io/@angela_/Linux-%EB%A7%A5%EB%B6%81m1%EC%97%90-%EC%9A%B0%EB%B6%84%ED%88%AC-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-UTM</guid>
            <pubDate>Sat, 14 Dec 2024 04:35:31 GMT</pubDate>
            <description><![CDATA[<ol>
<li>UTM 설치
<a href="https://mac.getutm.app/">https://mac.getutm.app/</a></li>
</ol>
<p><img src="https://velog.velcdn.com/images/angela_/post/900850d9-ad1c-4646-ba67-a708290a42e5/image.png" alt="">
<img src="https://velog.velcdn.com/images/angela_/post/fb3977e6-749d-46de-adf5-aa0ed466762c/image.png" alt="">
<img src="https://velog.velcdn.com/images/angela_/post/317cf3b7-a9a7-4323-8eff-27ca97d84d4e/image.png" alt=""></p>
<p>그다음엔 일단 iso라는 파일이 필요하다.! </p>
<p>Ubuntu 22.04.5 LTS 를 눌러 우분투 이미지파일(.iso)를 다운받는다. </p>
<p><a href="https://cdimage.ubuntu.com/releases/22.04/release/">https://cdimage.ubuntu.com/releases/22.04/release/</a></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/67af7fde-636c-4325-aea6-12c7fbdbfaea/image.png" alt="">
여기 탐색에 들어갈 파일이 아래에 다운받은 이미지파일(.iso)을 넣어줘야한다.
<img src="https://velog.velcdn.com/images/angela_/post/4017475c-6b63-4434-a587-f157fb48b079/image.png" alt=""></p>
<p>Contnue 눌러주면 된다. 용량은 알아서 설정하면될듯!</p>
<p> <img src="https://velog.velcdn.com/images/angela_/post/08a85f32-9e59-4498-a05d-146a7a2e6d21/image.png" alt="">
 Contnue 눌러주면 된다. 
<img src="https://velog.velcdn.com/images/angela_/post/75d7e9bf-6c98-43cf-868e-4082803687b6/image.png" alt="">
Contnue 눌러주면 된다. 
<img src="https://velog.velcdn.com/images/angela_/post/5958b42b-f4ec-4963-9717-a1dbbf840c89/image.png" alt="">
이름은 Linux로 자동으로 되는데 여기서 설정을 직접해줘도 되고 나중에 변경해줘도 된다. 
난 일단 기본으로 설정
<img src="https://velog.velcdn.com/images/angela_/post/85de3fc7-514c-4d35-b678-b06241354c1e/image.png" alt=""></p>
<p>가상머신이 생성이 되었다.! </p>
<p><img src="https://velog.velcdn.com/images/angela_/post/9cb00e22-d7aa-48f6-841e-19d66ab68fd3/image.png" alt=""></p>
<p> 실행버튼을 눌러주면
 <code>Try or Install Ubuntu Server</code> 를 눌러주면 가상머신위에 우분투가 설치된다.</p>
<p> <img src="https://velog.velcdn.com/images/angela_/post/5219048e-a7cc-4a1c-8146-d31efdf1764f/image.png" alt=""></p>
<p> 언어는 영어로 실행 
 <img src="https://velog.velcdn.com/images/angela_/post/1acef07d-7830-4287-a37f-2ddb9fb35553/image.png" alt=""></p>
<p>Done 상태에서 엔터를 눌러주면 다음 넘어간다.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/0286aa22-134e-4dc4-b653-34fc34417a4a/image.png" alt=""></p>
<p>Done 상태에서 엔터를 눌러주면 다음 넘어간다.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/0c81af88-eda9-444b-9ca5-88bec9092250/image.png" alt="">
Done 상태에서 엔터를 눌러주면 다음 넘어간다.
<img src="https://velog.velcdn.com/images/angela_/post/5d8108f8-64df-4520-9e75-6c7d27c80f3a/image.png" alt="">
Done 상태에서 엔터를 눌러주면 다음 넘어간다.
<img src="https://velog.velcdn.com/images/angela_/post/a115f2ea-981a-48a2-81b4-6eea7d3f4e6f/image.png" alt="">
Done 상태에서 엔터를 눌러주면 다음 넘어간다.
<img src="https://velog.velcdn.com/images/angela_/post/c8b1a3e6-c4bd-42e2-83aa-fef2a4a7ff3d/image.png" alt="">
Done 상태에서 엔터를 눌러주면 다음 넘어간다.
<img src="https://velog.velcdn.com/images/angela_/post/5dfba777-15ee-43a0-81b8-02f2f43ba5a4/image.png" alt="">
Done 상태에서 엔터를 눌러주면 다음 넘어간다.
<img src="https://velog.velcdn.com/images/angela_/post/7bd23fc5-8705-4bea-9b42-41b2ce07c90e/image.png" alt="">
<img src="https://velog.velcdn.com/images/angela_/post/15c2ca82-ba47-4a1c-a237-6b74868ed8ba/image.png" alt="">
<img src="https://velog.velcdn.com/images/angela_/post/9a431ff8-645d-4725-8a9f-2f4eabd1d609/image.png" alt=""></p>
<p>Continue 상태에서 엔터를 눌러주면 다음 넘어간다.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/f3067507-cf82-41c0-bc61-761dbe362fca/image.png" alt=""></p>
<p>여기에는 이제 접속할때 계정을 만들어주면 된다.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/4e8c633a-42cc-4aef-977a-9af276f4c46d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/fc0ffc94-6faa-4c0b-98b5-ad8e91909534/image.png" alt=""></p>
<p>여기에서는 ssh를 설정할거라서  스페이스바를 눌러서 
체크하고 넘어가야한다! </p>
<p><img src="https://velog.velcdn.com/images/angela_/post/9d6a15b6-1d78-4991-97f3-09d03f8176e1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/ab41f50e-9e53-4c6f-86e9-0b258b763693/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/684ea334-887f-4718-b42e-5c99d06c1865/image.png" alt=""></p>
<p>설치가 다되면 아래에 Reboot Now 를 선택해준다.
<img src="https://velog.velcdn.com/images/angela_/post/48655eb5-d8ce-4e00-bce5-b89e0a296094/image.png" alt=""></p>
<p>그런데 커서만 나오고 아무것도 나오질 않는다.
<img src="https://velog.velcdn.com/images/angela_/post/fadb2bcc-b81b-4793-b135-7aaf0ad159d1/image.png" alt=""></p>
<p>그럴땐 기존창을 끄고 나와서</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/3f8bbcf3-f68f-45b3-a7f4-59680249feb4/image.png" alt=""></p>
<p>초기화를 눌러줘야한다. 
<img src="https://velog.velcdn.com/images/angela_/post/c9a28767-b830-49e7-8e53-65339f4b8c96/image.png" alt=""></p>
<p>그리고 다시 시작하면 우분투 버전정보와 로그인창이 나오고</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/01241fee-7d37-4847-a07b-92be24e53f0f/image.png" alt=""></p>
<p>아까 지정한 계정을 입력해주면 우분투가 정상적으로 실행된다.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/14906d66-a8a3-479f-9b81-746fd74d3db5/image.png" alt=""></p>
<p>가상화머신에 우분투 설치완료!! !</p>
<blockquote>
</blockquote>
<h4 id="🔖-참고용-iso버전이-맞지않는-경우-생기는-오류-이미지">🔖 참고용 iso버전이 맞지않는 경우 생기는 오류 이미지</h4>
<p>버전이 안맞으면 -&gt; 아래 그림처럼 오류가 생김!
<img src="https://velog.velcdn.com/images/angela_/post/e4cae29c-e542-4f0c-8ca9-50c431a6b754/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] 우분투에서 Docker로 MySQL 컨테이너 실행 후, MySQL 연결 성공
]]></title>
            <link>https://velog.io/@angela_/%EC%9A%B0%EB%B6%84%ED%88%AC%EC%97%90%EC%84%9C-%EB%8F%84%EC%BB%A4%EB%A1%9C-mysql-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@angela_/%EC%9A%B0%EB%B6%84%ED%88%AC%EC%97%90%EC%84%9C-%EB%8F%84%EC%BB%A4%EB%A1%9C-mysql-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Fri, 13 Dec 2024 14:08:17 GMT</pubDate>
            <description><![CDATA[<h3 id="우분투에서-docker로-mysql-실행-후-로컬-프로젝트-연결-성공-이미지-문제-해결">우분투에서 Docker로 MySQL 실행 후 로컬 프로젝트 연결 성공, 이미지 문제 해결</h3>
<p>우분투에 Docker를 설치하여 MySQL을 실행하고 이를 내 로컬 프로젝트에 연결하는 작업을 진행했으나, 처음에는 MySQL 연결에 실패했다ㅠㅠ 
그 이유는 Docker 실행 시 기본 mysql:latest 이미지를 사용했기 때문.. 
이 이미지는 내가 직접 빌드한 jeongsujin/mysql-server:v1 이미지와 호환되지 않아 연결되지 않았다. 😖</p>
<h4 id="로컬-프로젝트에서-이를-연결하니-정상적">로컬 프로젝트에서 이를 연결하니 정상적</h4>
<pre><code class="language-text">docker run --name mysql-server -p 3306:3306 \
--network app-network \
-e MYSQL_ROOT_PASSWORD=root \
-e MYSQL_DATABASE=app \
-e MYSQL_USER=app \
-e MYSQL_PASSWORD=app \
-d jeongsujin/mysql-server:v1</code></pre>
<p>도커위에 올린 mysql 부분이 연결이 안됐는데 그 이유가 
지금 <code>-d jeongsujin/mysql-server:v1</code> 
마지막 옵션 부분에서 <code>-d mysql:latest</code> 기본 이미지를 사용했던 것이 문제의 원인.
내가 빌드한 이미지가 아닌 기본 mysql이미지를 연결하고 있었던 것.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/b8182e26-2b2c-4e79-a6c2-90173499b47f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/e1fa8f8b-8ce0-4e59-bd73-b5298acb2bc3/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/57c601e4-fc0e-4811-88e1-da756f5c9265/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring JPA ] JPA동작방식 개념과  영속성 컨텍스트]]></title>
            <link>https://velog.io/@angela_/Spring-JPA</link>
            <guid>https://velog.io/@angela_/Spring-JPA</guid>
            <pubDate>Sat, 16 Nov 2024 12:56:21 GMT</pubDate>
            <description><![CDATA[<h3 id="구성-요소-설명">구성 요소 설명</h3>
<p>관계를 도식으로 표현</p>
<pre><code class="language-plaintext">JPA (Java Persistence API)
  └─ EntityManagerFactory
       └─ EntityManager
            └─ Persistence Context (영속성 컨텍스트)
</code></pre>
<ol>
<li><p><strong>JPA (Java Persistence API)</strong>  </p>
<ul>
<li>ORM(Object-Relational Mapping)을 위한 표준 스펙.</li>
<li>엔터티 관리와 데이터베이스 동기화를 위한 규칙 정의.</li>
</ul>
</li>
<li><p><strong>EntityManagerFactory</strong></p>
<ul>
<li>영속성 단위를 기반으로 <code>EntityManager</code>를 생성하는 <strong>팩토리 객체</strong>.</li>
<li>애플리케이션 전역에서 한 번만 생성.</li>
<li>데이터베이스 연결 설정 및 영속성 단위 정보 포함</li>
<li>영속성 컨텍스트를 생성할 수 있는 EntityManager를 생성하는 팩토리</li>
<li>애플리케이션 전체에서 한 번만 생성되며, 데이터베이스 연결 설정 및 영속성 단위의 정보를 포함.</li>
</ul>
</li>
</ol>
<ol start="3">
<li><strong>EntityManager</strong>  <ul>
<li><strong>영속성 컨텍스트(Persistence Context)</strong>를 직접 관리하는 객체.</li>
<li>엔터티 저장, 조회, 수정, 삭제와 같은 작업을 수행.</li>
<li>트랜잭션 범위에서 사용되며, 스레드에 안전하지 않음.</li>
<li>Transaction 수행 후에는 반드시 EntityManager 를 닫는다.</li>
</ul>
</li>
</ol>
<ol start="4">
<li><strong>Persistence Context (영속성 컨텍스트)</strong>  <ul>
<li>가장 중요한 용어</li>
<li>엔터티 객체를 관리하는 1차 캐시 역할.</li>
<li>엔터티의 상태(영속/준영속/비영속)를 추적.</li>
<li>데이터베이스와의 동기화를 책임지는 공간.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="관계-정리">관계 정리</h3>
<ul>
<li><code>EntityManagerFactory</code>는 <strong><code>EntityManager</code></strong>를 생성하는 팩토리.</li>
<li><code>EntityManager</code>는 <strong>영속성 컨텍스트를 생성 및 관리</strong>.</li>
<li>영속성 컨텍스트는 <strong>엔터티 객체를 관리</strong>하며, 데이터베이스와 동기화.</li>
</ul>
<pre><code class="language-java">Item item = new Item(); // 객체생성 비영속 상태, JPA영속성 컨텍스트와 연결되지 않음. 
item.setItemNm(&quot;테스트 상품&quot;); //Item 객체의 속성(itemNm)에 값을 설정, 여전히 비영속 상태


/*
EntityManagerFactory를 사용하여 EntityManager 객체를 생성
EntityManagerFactory는 데이터베이스와의 연결을 설정하고 EntityManager를 생성하는 팩토리
EntityManager는 영속성 컨텍스트(Persistence Context)를 관리하는 역할
새롭게 생성된 EntityManager는 데이터베이스와의 작업을 관리
*/
EntityManager em = entityManagerFactory.createEntityManager();


/*
EntityTransaction 객체를 가져옴.
EntityTransaction은 데이터베이스 작업(삽입, 수정, 삭제)을 묶어서 처리하는 트랜잭션을 
시작하거나 커밋/롤백하는 역할
*/
EntityTransaction transation = em.getTransaction();

// 트랜잭션 시작, DB작업 실행 하기전에 준비
transation.begin(); 


//Item 객체를 영속성 컨텍스트에 등록
em.persiste(item); 

transation.commit(); // 트랜잭션을 DB에 반영

em.close(); // 엔티티매니저close()메서드를 호출해 사용한 자원을 반환
emf.close(); // 엔티티매니저팩토리의 close()메서드를 호출해 사용한 자원을 반환</code></pre>
<h3 id="엔티티의-생명주기-entity-lifecycle">엔티티의 생명주기 (Entity LifeCycle)</h3>
<ol>
<li><strong>비영속(Transient)</strong>
JPA의 영속성 컨텍스트와 완전히 연관되지 않은 상태
단순히 메모리 상에서만 존재하며, 데이터베이스와는 전혀 관련이 없다.
new 키워드로 객체를 생성했을 때 비영속 상태</li>
</ol>
<pre><code class="language-java">Item item = new Item(); // 비영속 상태
item.setItemNm(&quot;테스트 상품&quot;);</code></pre>
<ol start="2">
<li><strong>영속(Persistent)</strong>
영속성 컨텍스트에 의해 관리되는 상태</li>
</ol>
<pre><code class="language-java">em.persist(item); // 영속 상태로 변경</code></pre>
<ol start="3">
<li><p><strong>준영속 상태 (Detached)</strong>
영속성 컨텍스트에서 관리가 더 이상 이루어지지 않는 상태
EntityManager.detach(), 
EntityManager.close(),
EntityManager.clear() 호출되면 준영속 상태로 변경</p>
</li>
<li><p><strong>삭제 상태 (Removed)</strong>
영속성 컨텍스트에 의해 관리되지만, 삭제가 예약된 상태
트랜잭션이 커밋되면 데이터베이스에서 삭제</p>
</li>
</ol>
<pre><code class="language-java">em.remove(item); // 삭제 상태로 변경</code></pre>
<hr>
<h3 id="생명주기-정리">생명주기 정리</h3>
<table>
<thead>
<tr>
<th>상태</th>
<th>메서드 호출</th>
<th>상태  결과</th>
</tr>
</thead>
<tbody><tr>
<td>비영속 (Transient)</td>
<td><code>EntityManager.persist()</code></td>
<td>영속 (Persistent)</td>
</tr>
<tr>
<td>영속 (Persistent)</td>
<td><code>EntityManager.detach()</code></td>
<td>준영속 (Detached)</td>
</tr>
<tr>
<td>영속 (Persistent)</td>
<td><code>EntityManager.remove()</code></td>
<td>삭제 (Removed)</td>
</tr>
<tr>
<td>준영속 (Detached)</td>
<td><code>EntityManager.merge()</code></td>
<td>영속 (Persistent)</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[개념공부] 자바스크립트 함수 ]]></title>
            <link>https://velog.io/@angela_/%EA%B0%9C%EB%85%90%EA%B3%B5%EB%B6%80-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@angela_/%EA%B0%9C%EB%85%90%EA%B3%B5%EB%B6%80-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%95%A8%EC%88%98</guid>
            <pubDate>Wed, 02 Oct 2024 13:32:46 GMT</pubDate>
            <description><![CDATA[<p>함수는 function 이라는 키워드를 사용해야하고, 함수이름을  적어준다.</p>
<h2 id="선언적-함수">선언적 함수</h2>
<h4 id="함수선언">함수선언</h4>
<pre><code class="language-javascript">function hello(){
// 함수를 호출했을때 실행하는 영역
console.log(&#39;hello1&#39;);
}
console.log(hello1, typeof hello1);
=&gt; 표준내장객체 , 함수도 객체의 한 종류
</code></pre>
<h4 id="매개변수-있는-함수">매개변수 있는 함수</h4>
<pre><code class="language-javascript">function hello2(name){
    console.log(&#39;hello2&#39;, name);
}

//매개변수는 hello2함수를 호출할때 값을 지정</code></pre>
<h4 id="함수의-리턴">함수의 리턴</h4>
<p>return 키워드 사용</p>
<pre><code class="language-javascript">function hello3(name){
      //탬플릿 스트링 사용
    return `hello3 ${name}`;
}

// hello3 함수 호출
console.log(hello3(&#39;Angela&#39;));</code></pre>
<hr>
<h2 id="익명함수">익명함수</h2>
<p>익명함수를 만들어 변수에 할당하는 함수</p>
<h4 id="함수선언-1">함수선언</h4>
<pre><code class="language-javascript">const hello1 = function(){
    console.log(&#39;hello1&#39;);
};

// 함수호출
console.log(hello1, typeof hello1);
</code></pre>
<h3 id="차이점">차이점</h3>
<p>호이스팅의차이점, 
변수가 끌어올리는 현상, </p>
<p>익명함수를 호출할때 함수를 선언한 위치보다 먼저 호출이 되면, 함수가 뭔지는 알지만, 함수가 아니라고 판단해서 undefined 라고 나옴. (var로 변수로지정했을시)
const로 변수로 지정시 not defined로 나오기 때문에, 선언하지 않은 변수로 생각해서 오류가 나기 때문에 </p>
<p>자바스크립트 특성상 함수를 먼저 메모리에 올리기 때문에 선언전함수는 <code>호이스팅문제는 없지만, 익명함수는 호이스팅문제가 있는 부분</code>을 생각하고 코드를 짜야 한다.</p>
<table>
<thead>
<tr>
<th>특징</th>
<th>익명함수</th>
<th>선언된 함수</th>
</tr>
</thead>
<tbody><tr>
<td>이름</td>
<td>이름이 없음</td>
<td>이름이 있음</td>
</tr>
<tr>
<td>호이스팅</td>
<td>호이스팅되지 않음 (선언 후에만 호출 가능)</td>
<td>호이스팅됨 (선언 전에 호출 가능)</td>
</tr>
<tr>
<td>용도</td>
<td>변수나 함수 인자로 사용, 콜백 함수로 자주 사용</td>
<td>재사용 가능한 일반 함수</td>
</tr>
<tr>
<td>가독성</td>
<td>이름이 없어 코드 가독성이 떨어질 수 있음</td>
<td>이름이 있어 코드 가독성이 높음</td>
</tr>
</tbody></table>
<hr>
<h2 id="화살표-함수">화살표 함수</h2>
<p>es6새로추가된 기능,</p>
<pre><code class="language-javascript">
// 화살표 함수 선언
const hello1 = () =&gt;{
    console.log(&#39;hello&#39;);
}

// 함수의 매개변수, 매개변수가 하나일땐 ()괄호 생략가능
const hello2 = name =&gt;{
    console.log(&#39;hello2&#39;, name);
};

// 매개변수가 1이상일땐 괄호 넣어야 함.
const hello3 = (name, age)=&gt;{
    console.log(&#39;hello3&#39;, name, age);
};

// 함수의 리턴
const hello4 = name =&gt; {
    return `hello4 ${name}&#39;;
};

리턴
const hello5 = name =&gt; `hello5 ${name}`;
</code></pre>
<p>화살표 함수에는 생성자함수로 사용할 수 없다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[개념공부] 비동기 객체 기본 개념]]></title>
            <link>https://velog.io/@angela_/%EA%B0%9C%EB%85%90%EA%B3%B5%EB%B6%80-Promise</link>
            <guid>https://velog.io/@angela_/%EA%B0%9C%EB%85%90%EA%B3%B5%EB%B6%80-Promise</guid>
            <pubDate>Sat, 28 Sep 2024 15:52:19 GMT</pubDate>
            <description><![CDATA[<h2 id="콜백함수-개념">콜백함수 개념</h2>
<ul>
<li>콜백 함수는 다른 함수에 매개변수로 전달되는 함수이며, 특정 조건이 만족될 때 호출</li>
<li>콜백 함수는 다른 함수의 매개변수로 전달되어, 해당 함수 내부에서 호출되는 함수</li>
<li>일반적으로 비동기 작업이나 특정 이벤트가 발생할 때 실행</li>
</ul>
<p>즉, 콜백함수는 함수1,함수2 있다고 가정 했을때 함수1 매개변수가 함수2 인자로 전달 될 때 실행되는 함수</p>
<pre><code class="language-javascript">function fetchData(callback) {
    setTimeout(() =&gt; {
        callback(&quot;Data loaded!&quot;);// 비동기 작업이 끝나면 콜백 함수 호출
    }, 1000);//1초 후에 작업수행
}

fetchData((data) =&gt; {
    console.log(data); // 1초 후 &quot;Data loaded!&quot; 출력
});</code></pre>
<h2 id="promise">Promise</h2>
<p>Promise는 es6부터 자바스크립트 내장객체로 추가 되었음.
생성자를 통해서 프로미스 객체를 만들 수 있고, 생성자의 인자로 executor 라는 함수를 이용 
new Promise((resolve, reject) =&gt; {
    // 비동기 작업을 여기서 수행합니다.
});</p>
<pre><code class="language-javascript">function fetchData() {
    return new Promise((resolve, reject) =&gt; {
        setTimeout(() =&gt; {
            resolve(&quot;Data loaded!&quot;);
        }, 1000);
    });
}

fetchData().then(data =&gt; {
    console.log(data); // 1초 후 &quot;Data loaded!&quot; 출력
}).catch(error =&gt; {
    console.error(error);
});</code></pre>
<h4 id="executor-함수">executor 함수</h4>
<p>Promise 객체를 생성할 때 전달하는 함수로, 비동기 작업의 실행 로직을 정의하는 부분. 
이 함수는 <code>Promise가 생성될 때 즉시 실행되며</code>, 
두 개의 매개변수를 받는다: resolve와 reject. 
resolve와 reject 함수는 Promise 생성자 내부에서 자동으로 생성되어 executor 함수의 매개변수로 전달</p>
<h4 id="executor-함수의-매개변수">executor 함수의 매개변수</h4>
<p>resolve: 비동기 작업이 성공했을 때 호출하는 함수. Promise가 &quot;fulfilled&quot; 상태로 변경
reject: 비동기 작업이 실패했을 때 호출하는 함수. Promise가 &quot;rejected&quot; 상태로 변경</p>
<pre><code class="language-javascript">new Promise((resolve, reject)=&gt;{}) // 이 상태는 pending(대기상태)</code></pre>
<pre><code class="language-javascript">const promiseExam = new Promise((resovle, reject)=&gt;{
    setTimeout(()=&gt;{
        resolve(); // fulfilled 상태 
    },1000);
});

//콜백함수 (Promise가 이행된 후 발생 시 자동으로 호출되기 때문)
promiseExam.then()=&gt;{
  console.log(&#39;1000ms 후에 fulfilled&#39;);
}
</code></pre>
<pre><code class="language-javascript">/*promiseExam 함수가 호출될 때, Promise객체가 만들어지면서 리턴되는
함수의 실행과, 동시에 프로미스 객체를 만들면서 pending이 시작하도록 하기 위해 
타이밍을 적절히 지정하기 위해서 사용

*/
function promiseExam(){
    return new Promise((resolve, reject) =&gt; {
    setTimeout(()=&gt;{
          resolve();  
    }, 1000);    
  });
}

promiseExam().then( () =&gt; {
    console.log(&#39;1000ms 후에 fuifilled 됨&#39;);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/angela_/post/cb6f22f7-6af4-4730-980d-df77cadca27c/image.png" alt=""></p>
<hr>
<p>값넣어서 값 출력하고싶을때,
new Error로 넣으면 에러메시지 콘솔에 출력, resolve처럼 문자열도 가능</p>
<pre><code class="language-javascript">
function promiseExam() {
    return new Promise((resolve, reject) =&gt; {
        setTimeout(() =&gt; {
              //resolve(&quot;성공완료 ! &quot;)
            reject(new Error(&#39;실패&#39;));// reject, 부분은 catch()로 이동 
        }, 1000);
    });
}

promiseExam().then((message) =&gt; {
    console.log(&#39;1000ms에 후에 fulfilled 됩니다.&#39;, message);
}).catch((error) =&gt; {
    console.log(&#39;1000ms에 후에 rejected 됩니다.&#39;, error);
}).finally(() =&gt; {
    console.log(&quot;end&quot;)
})
</code></pre>
<h2 id="async---await">Async - Await</h2>
<p>Promise는 객체이고, Async는 함수, 둘다 비동기 작업이랑 연관 되어있다.
Promise를 더 직관적으로 다루기 위한 문법</p>
<p>async 키워드가 함수 앞에 붙으면 해당 함수는 
항상 Promise를 반환하고, 
await 키워드를 통해 비동기 작업의 완료를 기다릴 수 있다.</p>
<pre><code class="language-javascript">async function fetchData() {
    return &quot;Data loaded!&quot;;
}

async function getData() {
    try {
        const data = await fetchData();
        console.log(data); // &quot;Data loaded!&quot; 출력
    } catch (error) {
        console.error(error);
    }
}

getData();</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] 환경설정,vite]]></title>
            <link>https://velog.io/@angela_/TheUltimateReactJSCourse2024BasicstoAdvancedReact</link>
            <guid>https://velog.io/@angela_/TheUltimateReactJSCourse2024BasicstoAdvancedReact</guid>
            <pubDate>Tue, 23 Jan 2024 14:23:06 GMT</pubDate>
            <description><![CDATA[<p>TheUltimateReactJSCourse2024BasicstoAdvancedReact 프로젝트 온라인강의 보면서 정리글</p>
<hr>
<p>node[npm install]를 설치 후 vscode에서 확장프로그램 설치</p>
<ul>
<li>ES7 React/Redux/GraphQL/React-Native</li>
<li>Prettier - Code formatter</li>
<li>Ayu</li>
</ul>
<hr>
<p>2 way to create React application</p>
<ol>
<li><p>create react app</p>
</li>
<li><p>vite [빌드도구]</p>
</li>
<li><p>create react app 은 First, we can use create, React app, or we can use White Create React app is the old and takes more time to create.
먼저 create, React app을 사용하거나 White Create React app을 사용할 수 있습니다. 오래되어 생성하는 데 시간이 더 걸립니다.</p>
</li>
<li><p>We will use white for creating the new React applications.
새 React 응용 프로그램을 만들기 위해 흰색을 사용합니다.</p>
</li>
</ol>
<h3 id="세팅">세팅</h3>
<p>터미널 창에 
npm create vite@latest
project name: 여기에 프로젝트 이름 적고, 탭키로 리액트 선택하면 그다음은 자바스크립트 선택
여기에 first-application을 입력함</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/2ac6f4ed-1614-40da-9a94-8883962731ba/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/84306c04-ffa8-4062-9898-ca5175d2ca47/image.png" alt=""></p>
<p>그리고 터미널창에 그 다음 실행할 명령어가 나온다.</p>
<pre><code>cd 아까적은프로젝트경로로 이동하고 pwd로 확인
cd first-application
npm install
npm run dev </code></pre><p><img src="https://velog.velcdn.com/images/angela_/post/e06ee203-b184-4c4c-95e0-fa2848698f09/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[깃허브 푸쉬 오류 fatal:could not read Username for 'https://github.com': terminal prompts disabled ]]></title>
            <link>https://velog.io/@angela_/3y30cgsp</link>
            <guid>https://velog.io/@angela_/3y30cgsp</guid>
            <pubDate>Tue, 23 Jan 2024 13:39:08 GMT</pubDate>
            <description><![CDATA[<p>갑자기 잘되던 깃크라켄에서 모든 프로젝트가 푸쉬(push)가 안되는 에러발생 ..</p>
<h3 id="토큰만료-한달전부터-푸쉬가-안되서-새로운토큰-발급받음">토큰만료 한달전부터 푸쉬가 안되서 새로운토큰 발급받음</h3>
<p>그래서 생각해보니 음..토큰?만료가 되는 시기인거 같은데 들어가보니 2024년2월20일까지던데.. 아직 남았는데 음....
혹시나 하는 마음에 토큰을 생성했더니 됐다!
<img src="https://velog.velcdn.com/images/angela_/post/88e2f60c-b9d7-48ac-8433-5a31a402a492/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/30eb9e61-b3dd-4790-8914-1537625737a8/image.png" alt=""></p>
<p><code>Generate new token</code> 클릭 후 <code>Generate new token(classic)</code> 선택 후, 
<code>note</code> 이랑 <code>Expiration</code> , <code>Select scopes에 repo</code>만 
설정하면 거의 됨.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/e5de78a7-4905-4013-b621-bb2dc3691190/image.png" alt=""></p>
<p>토큰발급!
<img src="https://velog.velcdn.com/images/angela_/post/20343903-8324-457c-abd3-4db0b79109e1/image.png" alt=""></p>
<p>그리고 나는 mac os 사용해서 키체인에 토큰발급받은 비밀번호를 새로 넣어줬다.</p>
<p><img src="https://velog.velcdn.com/images/angela_/post/f4bafc9f-65fb-412a-9d49-b5fdd490ae01/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/angela_/post/513ce397-458e-4620-9146-8112e6409877/image.png" alt=""></p>
<p>그러고 다시 깃크라켄에 푸쉬했더니, 제대로 된다. !!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpringBoot - 관리자페이지] 3. 회원가입 필요한 테이블 설정과 관리자계정 insert 시키기 ]]></title>
            <link>https://velog.io/@angela_/SpringBoot-%EA%B4%80%EB%A6%AC%EC%9E%90%ED%8E%98%EC%9D%B4%EC%A7%80-3.-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%ED%95%84%EC%9A%94%ED%95%9C-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@angela_/SpringBoot-%EA%B4%80%EB%A6%AC%EC%9E%90%ED%8E%98%EC%9D%B4%EC%A7%80-3.-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%ED%95%84%EC%9A%94%ED%95%9C-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Mon, 08 Jan 2024 13:36:04 GMT</pubDate>
            <description><![CDATA[<p>MySql을 이용해서 사용</p>
<h3 id="1tb_member-테이블생성">1.tb_member 테이블생성</h3>
<p>일단 내가 넣고싶은 칼럼을 설정했다. 여기서 다 쓸수 있을진 모르겠지만 대략적으로 생성했다..ㅎ</p>
<pre><code class="language-sql">create table tb_member(
member_id bigint(20) not null auto_increment comment &#39;회원번호 (PK)&#39;,
login_id varchar(200) not null comment &#39;로그인 ID&#39;,
member_pw varchar(200) not null comment &#39;비밀번호&#39;,
member_name varchar(100) not null comment &#39;이름&#39;,
member_mail varchar(100) not null comment &#39;이메일&#39;,
gender enum(&#39;M&#39;,&#39;F&#39;) not null comment &#39;성별&#39;,
birthday date not null comment &#39;생년월일&#39;,
member_addr1 varchar(100) not null comment &#39;주소1&#39;,
member_addr2 varchar(100) not null comment &#39;주소2&#39;, 
member_addr3 varchar(100) not null comment &#39;주소3&#39;, 
delete_yn tinyint(1) not null comment &#39;삭제여부&#39;,
created_date datetime not null default current_timestamp() comment &#39;생성일시&#39;,
modified_date datetime  default null comment &#39;최종 수정일시&#39;,
admin_ck int not null comment &#39;관리자 유무&#39;,
money int not null comment &#39;충전&#39;,
primary key (member_id ),
unique key uix_member_login_id (login_id)
)comment &#39;회원&#39;;</code></pre>
<p>application.properties에 설정에 아래 코드처럼 추가</p>
<pre><code class="language-sql">#SELECT 칼럼과 멤버 변수 매핑(바인딩)하기, 언더바를 camel case로 변환하는 설정.
mybatis.configuration.map-underscore-to-camel-case=true</code></pre>
<p>DB에서 만든 칼럼명 언더바를 camel case로 변환하는 설정.
DB는 대소문자 구별이 없으므로 언더바를 이용해 단어의 결합을 구분,
java는 대소문자를 구분하므로 camel case방식을 사용하기 때문에
서로 이름이 다르게 되는데 이를 같은 이름으로 인식하여 의외로 에러잡는데 시간이 많이 
걸리는걸 방지하기 위해 설정! </p>
<h3 id="2관리자계정-insert-시키기-임의로내가-넣은거-타입확인하고-칼럼명순서대로-작성">2.관리자계정 Insert 시키기 (임의로내가 넣은거, 타입확인하고 칼럼명순서대로 작성)</h3>
<p>여기서 하나 배운건 
자동증감은 null로 하면 자동적으로 생기는걸 알았고,
생년월일과 생성일시는 둘다 date 타입인데 default와 current_timestamp() 차이로 시간을 많이잡아먹었다..
자동으로 생기니 당연히 칼럼을 빼고 입력했다... ㅠㅠㅠ큐 
계속 안되길래 정신차리고 하나씩 검색으로 고치고 나머지도 또 고치고 결국 성공..! 😵‍💫</p>
<pre><code class="language-sql">-- 회원가입 쿼리(MySQL), 관리자 아이디 만들기
-- 자동 증감은 null로 하면 자동으로 생성해줌
insert into tb_member values(null, &#39;admin&#39;, &#39;admin&#39;, &#39;admin&#39;, &#39;admin&#39;, &#39;F&#39;, &quot;1992-04-24&quot;, &#39;admin&#39;, &#39;admin&#39;, &#39;admin&#39;, 0, now(), null, 1, 100);</code></pre>
<p><img src="https://velog.velcdn.com/images/angela_/post/23134c3b-d352-41fa-892c-384aa0b271c2/image.png" alt=""></p>
<p>데이터 값 들어가고 성공 !</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SpringBoot - 관리자페이지] 2.  관리자페이지 타임리프 뷰 설정 ]]></title>
            <link>https://velog.io/@angela_/SpringBoot-%EA%B4%80%EB%A6%AC%EC%9E%90%ED%8E%98%EC%9D%B4%EC%A7%80-2.-%EA%B4%80%EB%A6%AC%EC%9E%90%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%83%80%EC%9E%84%EB%A6%AC%ED%94%84-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@angela_/SpringBoot-%EA%B4%80%EB%A6%AC%EC%9E%90%ED%8E%98%EC%9D%B4%EC%A7%80-2.-%EA%B4%80%EB%A6%AC%EC%9E%90%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%83%80%EC%9E%84%EB%A6%AC%ED%94%84-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Mon, 08 Jan 2024 12:48:51 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/angela_/post/ffc306a0-44e0-4f33-96b1-8932c1b716d6/image.png" alt=""></p>
<h3 id="타임리프-레이아웃-설정">타임리프 레이아웃 설정</h3>
<p><img src="https://velog.velcdn.com/images/angela_/post/b3dc6224-6db5-4d9a-9c75-855d281b1ed5/image.png" alt=""></p>
<p><code>templates.fragments</code> 안에 <code>body.html, header.html</code> 생성</p>
<h3 id="headerhtml">header.html</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot; xmlns:th=&quot;http://www.thymeleaf.org&quot; xmlns:layout=&quot;http://www.ultraq.net.nz/thymeleaf/layout&quot;&gt;
&lt;head th:fragment=&quot;main-head&quot;&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
&lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=Edge&quot; /&gt;

&lt;th:block layout:fragment=&quot;title&quot;&gt;&lt;/th:block&gt;

&lt;link rel=&quot;stylesheet&quot; th:href=&quot;@{/css/default.css}&quot;/&gt;
&lt;link rel=&quot;stylesheet&quot; th:href=&quot;@{/css/common.css}&quot;/&gt;
&lt;link rel=&quot;stylesheet&quot; th:href=&quot;@{/css/content.css}&quot;/&gt;
&lt;link rel=&quot;stylesheet&quot; th:href=&quot;@{/css/button.css}&quot;/&gt;

&lt;th:block layout:fragment=&quot;add-css&quot;&gt;&lt;/th:block&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="bodyhtml">body.html</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot; xmlns:th=&quot;http://www.thymeleaf.org&quot;
    xmlns:layout=&quot;http://www.ultraq.net.nz/thymeleaf/layout&quot;&gt;
&lt;body th:fragment=&quot;main-body&quot;&gt;

    &lt;div id=&quot;adm_wrap&quot;&gt;

        &lt;header&gt;
            &lt;div class=&quot;head&quot;&gt;
                &lt;h1&gt;게시판 프로젝트&lt;/h1&gt;
                &lt;div class=&quot;top_menu&quot; th:if=&quot;${session.loginMember != null}&quot;&gt;
                    &lt;div class=&quot;login_user&quot;&gt;
                        &lt;strong&gt;&lt;i class=&quot;far fa-user-circle&quot;&gt;&lt;/i&gt;[[ ${session.loginMember.name}]]&lt;/strong&gt;님 반갑습니다.
                    &lt;/div&gt;
                    &lt;div class=&quot;logout&quot;&gt;
                        &lt;form action=&quot;/logout&quot; method=&quot;post&quot;&gt;
                        &lt;button type=&quot;submit&quot;&gt;
                            &lt;span class=&quot;skip_info&quot;&gt;로그아웃&lt;/span&gt;&lt;i class=&quot;fas fa-sign-out-alt&quot;&gt;&lt;/i&gt;
                        &lt;/button&gt;
                        &lt;/form&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/header&gt;




        &lt;div id=&quot;container&quot;&gt;

            &lt;div class=&quot;menu_toggle&quot;&gt;
                &lt;span&gt;&lt;/span&gt;
            &lt;/div&gt;

            &lt;div class=&quot;lcontent&quot;&gt;
                &lt;nav&gt;
                    &lt;ul&gt;
                        &lt;li class=&quot;has_sub&quot;&gt;&lt;a href=&quot;javascript: void(0);&quot;&gt;&lt;span&gt;게시판
                                    관리&lt;/span&gt;&lt;/a&gt;
                            &lt;ul&gt;
                                &lt;li&gt;&lt;a href=&quot;/post/list&quot; class=&quot;on&quot;&gt;공지사항 목록&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href=&quot;/post/write&quot;&gt;공지사항 글쓰기&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href=&quot;javascript: alert(&#39;준비중입니다.&#39;)&quot;&gt;자주하는 질문&lt;/a&gt;&lt;/li&gt;
                            &lt;/ul&gt;
                        &lt;/li&gt;

                        &lt;li class=&quot;has_sub&quot;&gt;&lt;a href=&quot;javascript:void(0);&quot;&gt;&lt;span&gt;회원관리&lt;/span&gt;&lt;/a&gt;
                            &lt;ul&gt;
                                &lt;li&gt;&lt;a th:href=&quot;@{/members/list}&quot; class=&quot;on&quot;&gt;회원목록&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a th:href=&quot;@{/members/mypage}&quot;&gt;회원수정&lt;/a&gt;&lt;/li&gt;
                            &lt;/ul&gt;
                        &lt;/li&gt;
                    &lt;/ul&gt;
                &lt;/nav&gt;
            &lt;/div&gt;
            &lt;!-- lcontent --&gt;

            &lt;div class=&quot;rcontent&quot;&gt;
                &lt;!-- 페이지별 콘텐츠 --&gt;
                &lt;th:block layout:fragment=&quot;content&quot;&gt;&lt;/th:block&gt;
            &lt;/div&gt;

        &lt;/div&gt;
        &lt;!-- container end --&gt;

    &lt;/div&gt;
    &lt;!-- adm_wrap --&gt;


&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js&quot;&gt;&lt;/script&gt;
    &lt;script th:src=&quot;@{/js/function.js}&quot;&gt;&lt;/script&gt;
    &lt;script th:src=&quot;@{/js/jquery-3.6.0.min.js}&quot;&gt;&lt;/script&gt;
    &lt;script th:src=&quot;@{/js/common.js}&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;https://kit.fontawesome.com/79613ae794.js&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js&quot;&gt;&lt;/script&gt;

    &lt;th:block layout:fragment=&quot;script&quot;&gt;&lt;/th:block&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><code>templates.layout</code> 안에 <code>basic.html</code></p>
<h3 id="basichtml">basic.html</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
    &lt;head th:replace=&quot;fragments/header :: main-head&quot;&gt; &lt;/head&gt;
    &lt;body th:replace=&quot;fragments/body :: main-body&quot;&gt;&lt;/body&gt;  
&lt;/html&gt;</code></pre>
<p>이렇게 공통으로 설정을 해놓으면 
나중에 html코드부분에 <code>xmlns:layout</code> 부분으로 설정하고 <code>layout:decorate</code> 넣어주면 분리되서 유지보수가 쉬워진다. </p>
<pre><code class="language-html">&lt;html lang=&quot;ko&quot; xmlns:th=&quot;http://www.thymeleaf.org&quot;
    xmlns:layout=&quot;http://www.ultraq.net.nz/thymeleaf/layout&quot;
    layout:decorate=&quot;layout/basic&quot;&gt;</code></pre>
]]></description>
        </item>
    </channel>
</rss>