<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jhin.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 18 Jul 2025 01:38:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jhin.log</title>
            <url>https://velog.velcdn.com/images/com_jinnn99/profile/a97a902f-2f82-4401-ab4f-1b7b8e9b02b4/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jhin.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/com_jinnn99" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Spring boot로 Todo 앱 만들기]]></title>
            <link>https://velog.io/@com_jinnn99/Spring-boot%EB%A1%9C-Todo-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@com_jinnn99/Spring-boot%EB%A1%9C-Todo-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Fri, 18 Jul 2025 01:38:55 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="개발환경-설정">개발환경 설정</h1>
<p><a href="https://start.spring.io/">https://start.spring.io/</a></p>
<p>dependencies에 spring web, spring data jpa, h2 database, lombok을 넣어주고 generate 한다.</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/dcb029c1-d16a-45da-a3af-b4c187eb50eb/image.png" alt=""></p>
<p>압축파일은 java workspace에 압축 풀어준다.</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/fa0aa82a-a0e1-4c57-a5a7-6ccb32eadc0f/image.png" alt=""></p>
<p>File - Import - Gradle - Existing Gradle Project - Next - Project Root Directory 설정 - Override workspace setting 체크, Gradle wrapper 선택 - Finish</p>
<p>프로젝트 우클릭 후 Run as - Spring Boot App</p>
<p><del>Preferences - Server - Runtime Environments에서 Tomcat 10을 Add
프로젝트 우클릭 후 Properties - Projcet Facets - Dynamic Web Module 체크</del></p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/eddcfe3e-4b7e-4f72-8c39-216e53d39fd7/image.png" alt=""></p>
<p><del>프로젝트 우클릭 후 Run as - Run on server</del></p>
<p>웹브라우저에서 <a href="http://localhost:8080">http://localhost:8080</a> 에 접속한다.</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/3f85354f-5053-4913-9c10-b9432a03d0b4/image.png" alt=""></p>
<ul>
<li>🎈 Gradle: 빌드 자동화 툴. 컴파일, 라이브러리 다운로드, 패키징, 테스팅 등을 자동화 할 수 있다. 그루비라는 언어로 작성돼 있다.</li>
</ul>
<h2 id="🚨-the-import-orgspringframework-cannot-be-resolved">🚨 The import org.springframework cannot be resolved</h2>
<p>gradle에 <code>sourceCompatibility = &#39;17&#39;</code>와 <code>implementation &#39;org.springframework.boot:spring-boot-starter&#39;</code>이 있는지 확인한다.
나의 경우에는 없어서 에러가 떴다.</p>
<h2 id="🚨-프로젝트-실행이-안-돼요">🚨 프로젝트 실행이 안 돼요</h2>
<p>자세히 보니 구조가 이상하다
이클립스 마켓플레이스에서 STS4를 설치하자
그리고 프로젝트를 다시 생성해 보자</p>
<h2 id="필요-라이브러리-추가">필요 라이브러리 추가</h2>
<p>스프링 부트 프로젝트를 하다 보면 라이브러리를 추가하는 상황이 온다. 메이븐 리포지터리를 이용해 라이브러리를 추가하면 된다.</p>
<p><a href="https://mvnrepository.com">https://mvnrepository.com</a></p>
<p>MVN Repository에서 Guava를 검색 후 Usages가 많은 것을 선택하자.
30.1.1-jre를 선택할 것이다.
구글 <strong>guava</strong> 라이브러리를 이용하면 자바 Collection을 간편하게 활용할 수 있다.</p>
<p>이클립스에 <strong>lombok</strong> 라이브러리도 추가하면 좋다.
롬복은 getter, setter, builder, constructor를 자동으로 작성해 준다.
코드의 양을 줄이고 개발 시간을 단축할 수 있다.
롬복 jar 파일을 다운로드해서 롬복 jar 파일이 있는 해당 경로로 cmd 창을 열어준다.
<code>java -jar lombok.jar</code>
아마 자동으로 이클립스를 찾아줄 텐데, 찾지 못한다면 수동으로 이클립스 설치 폴더를 선택한 뒤 install/update 한다.
이클립스 재시작 후 Help - About Eclipse IDE를 선택하면 롬복이 설치된 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/afddf210-d074-479d-8825-f77d734b8bad/image.png" alt=""></p>
<h2 id="어노테이션-프로세서-설정">어노테이션 프로세서 설정</h2>
<p>프로젝트 우클릭 - Java Compiler - Annotation Processing - Apply and Close</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/7c37076c-b831-4ed3-8cb8-e265d21cfe02/image.png" alt=""></p>
<p>롬복의 동작 여부를 확인하기 위해 TodoModel.java를 생성한다.</p>
<pre><code class="language-java">import lombok.Builder;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@Builder
@RequiredArgsConstructor
public class TodoModel {
    @NonNull
    private String id;
}</code></pre>
<hr>
<h1 id="hello-world-출력하기">Hello World 출력하기</h1>
<ol>
<li>TestController.java 생성</li>
</ol>
<pre><code class="language-java">import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(&quot;/test&quot;)
public class TestController {

    @GetMapping
    public String testController() {
        return &quot;Hello World!&quot;;
    }
}</code></pre>
<ol start="2">
<li><p>프로젝트 실행</p>
</li>
<li><p>웹 브라우저에서 <code>localhost:8080/test</code>를 입력하여 확인하기</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/2bd42a80-ea45-4c3d-844f-15e1d0ce426a/image.png" alt=""></p>
<h2 id="postman으로-hello-world-확인하기">Postman으로 Hello World 확인하기</h2>
<p>포스트맨은 백엔드 개발을 할 때 request와 response를 확인할 수 있는 프로그램이다.</p>
<p><a href="https://www.postman.com/downloads/">https://www.postman.com/downloads/</a></p>
<p>포스트맨을 통해 GET request를 실행한다.</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/08b13016-7ba3-4048-8a83-a91367860827/image.png" alt=""></p>
<hr>
<h1 id="pathvariable-사용하기">@PathVariable 사용하기</h1>
<pre><code class="language-java">    @GetMapping(&quot;/{id}&quot;)
    public String testControllerWithPathVariables(@PathVariable(required = false) int id) {
        return &quot;Hello World! ID &quot; + id;
    }</code></pre>
<h2 id="🚨-javalangillegalargumentexception">🚨 java.lang.IllegalArgumentException</h2>
<p>Name for argument of type [int] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the &#39;-parameters&#39; flag.</p>
<p><a href="https://velog.io/@ghwns9991/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-3.2-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98-%EC%9D%B4%EB%A6%84-%EC%9D%B8%EC%8B%9D-%EB%AC%B8%EC%A0%9C">참고 - 스프링 부트 3.2 매개변수 이름 인식 문제</a>
<a href="https://www.inflearn.com/community/questions/1088283/pathvariable-%EB%B3%80%EC%88%98%EB%AA%85-%EA%B0%99%EC%9D%84%EB%95%8C-%EC%83%9D%EB%9E%B5%EC%8B%9C-%EC%98%A4%EB%A5%98-%EB%B9%8C%EB%93%9C-%EC%84%A4%EC%A0%95%EC%9D%84-gradle%EB%A1%9C-%ED%95%98%EB%A9%B4-%ED%95%B4%EA%B2%B0%EB%90%98%EB%8A%94-%EA%B2%83-%EA%B0%99%EC%8A%B5%EB%8B%88%EB%8B%A4">참고 - 인프런 @PathVariable 변수명 같을때 생략시 오류</a></p>
<p>PathVariable에 변수명을 명시해 주는 것이 권장하는 방법이다.</p>
<pre><code class="language-java">    @GetMapping(&quot;/{id}&quot;)
    public String testControllerWithPathVariables(@PathVariable(&quot;id&quot;) int id) {
        return &quot;Hello World! ID &quot; + id;
    }</code></pre>
<hr>
<h1 id="requestparam">@RequestParam</h1>
<p>@RequestParam을 이용하여 매개변수를 전달 받을 수 있다.
이때 request 요청 URI는 <code>localhost:8080/test/Param?id=123</code> 형태다.</p>
<pre><code class="language-java">    @GetMapping(&quot;/Param&quot;)
    public String testControllerRequestParam(@RequestParam(&quot;id&quot;) int id) {
        return &quot;Hello World! ID param &quot; + id;
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/868d3e88-9a6b-4608-8040-a7d7acc55f6e/image.png" alt=""></p>
<hr>
<h1 id="requestbody">@RequestBody</h1>
<p>클라이언트 요청을 JSON 형식으로 받아 스트링을 반환하도록 해본다</p>
<ol>
<li>TestRequestBodyDTO.java<pre><code class="language-java">package com.example.todo.dto;
</code></pre>
</li>
</ol>
<p>import lombok.Data;</p>
<p>@Data
public class TestRequestBodyDTO {
    private int id;
    private String message;
}</p>
<pre><code>
2. TestController.java
```java
    @GetMapping(&quot;/testRequestBody&quot;)
    public String testControllerRequstBody(@RequestBody TestRequestBodyDTO testRequestBodyDTO) {
        return &quot;Hello World! ID: &quot; + testRequestBodyDTO.getId() + &quot; / Message: &quot; + testRequestBodyDTO.getMessage();
    }</code></pre><p><img src="https://velog.velcdn.com/images/com_jinnn99/post/c0a226dd-c486-42a1-a4c1-371400d335a1/image.png" alt=""></p>
<hr>
<h1 id="responsebody">@ResponseBody</h1>
<p>@RestController 내부는 @Controller, @ResponseBody 어노테이션으로 구성된다.
@ResponseBody는 응답 객체를 만들어 클라이언트에게 반환한다.</p>
<ol>
<li>ResponseDTO.java<pre><code class="language-java">package com.example.todo.dto;
</code></pre>
</li>
</ol>
<p>import java.util.List;</p>
<p>import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;</p>
<p>@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ResponseDTO<T> {
    private String error;
    private List<T> data;
}</p>
<pre><code>
2. TestController.java
```java
    @GetMapping(&quot;/testResponseBody&quot;)
    public ResponseDTO&lt;String&gt; testControllerResponseBody() {
        List&lt;String&gt; list = new ArrayList&lt;&gt;();
        list.add(&quot;Hello world! I&#39;m ResponseDTO&quot;);
        list.add(&quot;See you!&quot;);
        ResponseDTO&lt;String&gt; response = ResponseDTO.&lt;String&gt;builder().data(list).build();
        return response;
    }</code></pre><p><img src="https://velog.velcdn.com/images/com_jinnn99/post/98a85168-7895-4e53-8a20-011856d18fb1/image.png" alt=""></p>
<hr>
<h1 id="responseentity로-http-status-조작">ResponseEntity로 HTTP Status 조작</h1>
<pre><code class="language-java">    @GetMapping(&quot;/testResponseEntityOk&quot;)
    public ResponseEntity&lt;?&gt; testControllerResponseEntityOk() {
        List&lt;String&gt; list = new ArrayList&lt;&gt;();
        list.add(&quot;Hello world! I&#39;m ResponseEntity. And you get 200!&quot;);
        list.add(&quot;See you&quot;);
        ResponseDTO&lt;String&gt; response = ResponseDTO.&lt;String&gt;builder().data(list).build();
        return ResponseEntity.ok().body(response);
    }

    @GetMapping(&quot;/testResponseEntityBad&quot;)
    public ResponseEntity&lt;?&gt; testControllerResponseEntityBad() {
        List&lt;String&gt; list = new ArrayList&lt;&gt;();
        list.add(&quot;Hello world! I&#39;m ResponseEntity. And you get 400!&quot;);
        list.add(&quot;See you&quot;);
        ResponseDTO&lt;String&gt; response = ResponseDTO.&lt;String&gt;builder().data(list).build();
        return ResponseEntity.badRequest().body(response);
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/3b9dd8da-213d-48aa-8fc2-e24807020b7f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/e67b32db-3858-4aca-bf24-407f8a18d5e1/image.png" alt=""></p>
<hr>
<h1 id="service">@Service</h1>
<p>레이어드 아키텍처</p>
<ol>
<li>프리젠테이션 레이어(Controller) - dto</li>
<li>비즈니스 레이어(Service) - model</li>
<li>퍼시스턴스 레이어(Persistence) - entity</li>
<li>데이터베이스 레이어(DB)</li>
</ol>
<p>@Service는 스테레오 타입 컴포넌트다.</p>
<ol>
<li>TodoService.java<pre><code class="language-java">import org.springframework.stereotype.Service;
</code></pre>
</li>
</ol>
<p>@Service
public class TodoService {
    public String testService() {
        return &quot;Test Service&quot;;
    }
}</p>
<pre><code>
2. TestController.java
```java
    @Autowired
    private TodoService todoService;

    @GetMapping(&quot;/service&quot;)
    public ResponseEntity&lt;?&gt; testService() {
        String str = todoService.testService();
        List&lt;String&gt; list = new ArrayList&lt;&gt;();
        list.add(str);
        ResponseDTO&lt;String&gt; response = ResponseDTO.&lt;String&gt;builder().data(list).build();
        return ResponseEntity.ok().body(response);
    }</code></pre><hr>
<h1 id="repository-사용하기">@Repository 사용하기</h1>
<p>스프링 부트로 웹 개발 시 패키지에 포함되는 일반적인 클래스는 크게 3가지다.</p>
<ol>
<li>@Controller(@RestController)<ul>
<li>프리젠테이션. Request, Response 기능 담당. JSON 형식 값을 전달하기 위해 @RestController를 사용한다.</li>
</ul>
</li>
<li>@Service<ul>
<li>비즈니스 로직 구현 클래스. request에서 넘어온 파라미터 또는 기능 요청에 대해 실제적인 기능을 수행하는 역할을 함. DB 테이블에 값을 저장하거나 값을 검색해 오는 역할 담당</li>
</ul>
</li>
<li>@Repository<ul>
<li>퍼시스턴스 레이어에 속하는 interface 클래스. JPARepository에서 상속받음. 관계형 DB의 CRUD를 상속받아 처리하므로 선언만 하고 내부 로직을 구현할 필요는 없음. 스프링 데이터 JPA가 AOP 인터페이스를 통해 메소드를 가로채 실행하기 때문 </li>
</ul>
</li>
</ol>
<ol>
<li>TodoEntity.java<pre><code class="language-java">package com.example.todo.model;
</code></pre>
</li>
</ol>
<p>import org.hibernate.annotations.GenericGenerator;</p>
<p>import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;</p>
<p>@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(name = &quot;Todo&quot;)
public class TodoEntity {
    @Id
    @GeneratedValue(generator = &quot;system-uuid&quot;)    // 자동으로 id 생성
    @GenericGenerator(name = &quot;system-uuid&quot;, strategy = &quot;uuid&quot;)
    private String id;</p>
<pre><code>private String usrId;
private String title;
private boolean done;</code></pre><p>}</p>
<pre><code>
2. TodoRepository.java
```java
package com.example.todo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.todo.model.TodoEntity;

@Repository
public interface TodoRepository extends JpaRepository&lt;TodoEntity, String&gt; {

}</code></pre><ol start="3">
<li><p>TodoService.java</p>
<pre><code class="language-java">@Service
public class TodoService {
 @Autowired
 private TodoRepository repository;

 public String testService() {
     // Todo entity 생성
     TodoEntity entity = TodoEntity.builder().title(&quot;my first todo item&quot;).build();
     // Todo entity 저장
     repository.save(entity);
     // Todo entity 검색
     TodoEntity savedEntity = repository.findById(entity.getId()).get();
     return savedEntity.getTitle();
 }
}</code></pre>
</li>
<li><p>TestController.java</p>
<pre><code class="language-java"> @GetMapping(&quot;/repository&quot;)
 public ResponseEntity&lt;?&gt; testRepository() {
     String str = todoService.testService();
     List&lt;String&gt; list = new ArrayList&lt;&gt;();
     list.add(str);
     ResponseDTO&lt;String&gt; response = ResponseDTO.&lt;String&gt;builder().data(list).build();
     return ResponseEntity.ok().body(response);
 }</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/7a58b729-8572-4216-b269-6cf037a8c8b4/image.png" alt=""></p>
<h2 id="repository에서-쿼리-추가하기">@Repository에서 쿼리 추가하기</h2>
<ol>
<li><p>TodoRepository.java</p>
<pre><code class="language-java">@Repository
public interface TodoRepository extends JpaRepository&lt;TodoEntity, String&gt; {
 List&lt;TodoEntity&gt; findByUsrId(String usrId);
}</code></pre>
</li>
<li><p>TodoService.java</p>
<pre><code class="language-java"> public String testService() {
     // Todo entity 생성
     TodoEntity entity = TodoEntity.builder().usrId(&quot;usrId01&quot;).title(&quot;my first todo item&quot;).build();
     // Todo entity 저장
     repository.save(entity);
     // Todo entity 검색
     TodoEntity savedEntity = repository.findByUsrId(entity.getUsrId()).get(0);
     return savedEntity.getUsrId();
 }</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/ac093673-8a4b-4aa4-a2b1-4a61b0797c35/image.png" alt=""></p>
<h2 id="namedquery-query-사용하기">@NamedQuery, @Query 사용하기</h2>
<p>JPA에서 제공하는 기본 쿼리 외에 SQL을 사용하여 사용자가 쿼리를 지정할 수 있다.
@NamedQuery를 사용한 쿼리는 Entity 클래스에서 정의하여 사용한다.</p>
<ol>
<li><p>TodoEntity.java</p>
<pre><code class="language-java">@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(name = &quot;Todo&quot;)
@NamedQuery(name = &quot;TodoRepository.searchByUsrId&quot;, query = &quot;select t from TodoEntity t where t.usrId = ?1&quot;)
public class TodoEntity {
 @Id
 @GeneratedValue(generator = &quot;system-uuid&quot;)    // 자동으로 id 생성
 @GenericGenerator(name = &quot;system-uuid&quot;, strategy = &quot;uuid&quot;)
 private String id;

 private String usrId;
 private String title;
 private boolean done;
}</code></pre>
</li>
<li><p>TodoRepository.java</p>
<pre><code class="language-java">@Repository
public interface TodoRepository extends JpaRepository&lt;TodoEntity, String&gt; {
 List&lt;TodoEntity&gt; searchByUsrId(String usrId);
}</code></pre>
</li>
<li><p>TodoService.java</p>
<pre><code class="language-java"> public String testService() {
     // Todo entity 생성
     TodoEntity entity = TodoEntity.builder().usrId(&quot;usrId01&quot;).title(&quot;my first todo item&quot;).build();
     // Todo entity 저장
     repository.save(entity);
     // Todo entity 검색
     TodoEntity savedEntity = repository.searchByUsrId(entity.getUsrId()).get(0);
     return savedEntity.getUsrId();
 }</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/ae266073-2da0-409e-bb06-bc342a147bec/image.png" alt=""></p>
<p>다음은 @Query를 사용하여 SQL을 질의하는 방법이다.
TodoRepository.java만 수정하면 되므로 비교적 간단하다.
@NamedQuery 구문은 삭제한다.</p>
<ol>
<li><p>TodoEntity.java</p>
<pre><code class="language-java">@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(name = &quot;Todo&quot;)
public class TodoEntity {
 @Id
 @GeneratedValue(generator = &quot;system-uuid&quot;)    // 자동으로 id 생성
 @GenericGenerator(name = &quot;system-uuid&quot;, strategy = &quot;uuid&quot;)
 private String id;

 private String usrId;
 private String title;
 private boolean done;
}</code></pre>
</li>
<li><p>TodoRepository.java</p>
<pre><code class="language-java">@Repository
public interface TodoRepository extends JpaRepository&lt;TodoEntity, String&gt; {
 @Query(&quot;select t from TodoEntity t where t.usrId = ?1&quot;)
 List&lt;TodoEntity&gt; searchByUsrId(String usrId);
}</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/4b7201e3-4593-43df-bcf9-c4f9e1170639/image.png" alt=""></p>
<hr>
<h1 id="todo-앱-만들기">Todo 앱 만들기</h1>
<h2 id="http-post를-이용한-create-rest-api-개발">HTTP POST를 이용한 Create REST API 개발</h2>
<ol>
<li>TodoRepository.java<pre><code class="language-java">@Repository
public interface TodoRepository extends JpaRepository&lt;TodoEntity, String&gt; {
</code></pre>
</li>
</ol>
<p>}</p>
<pre><code>
2. TodoEntity.java
```java
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(name = &quot;Todo&quot;)
public class TodoEntity {
    @Id
    @GeneratedValue(generator = &quot;system-uuid&quot;) // 자동으로 id 생성
    @GenericGenerator(name = &quot;system-uuid&quot;, strategy = &quot;uuid&quot;)
    private String id;

    private String usrId;
    private String title;
    private boolean done;
}</code></pre><ol start="3">
<li><p>TodoDTO.java</p>
<pre><code class="language-java">@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class TodoDTO {
 private String id;
 private String title;
 private boolean done;

 // entity to dto
 public TodoDTO(final TodoEntity entity) {
     this.id = entity.getId();
     this.title = entity.getTitle();
     this.done = entity.isDone();
 }

 // dto to entity
 public static TodoEntity toEntity(final TodoDTO dto) {
     return TodoEntity.builder()
             .id(dto.getId())
             .title(dto.getTitle())
             .done(dto.isDone())
             .build();
 }
}</code></pre>
</li>
<li><p>ResponseDTO.java</p>
<pre><code class="language-java">@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ResponseDTO&lt;T&gt; {
 private String error;
 private List&lt;T&gt; data;
}</code></pre>
</li>
<li><p>TodoService.java</p>
<pre><code class="language-java">@Slf4j
@Service
public class TodoService {

 @Autowired
 private TodoRepository repository;

 public Optional&lt;TodoEntity&gt; create(final TodoEntity entity) {
     // Validation
     validate(entity);
     repository.save(entity);
     return repository.findById(entity.getId());
 }

 public void validate(final TodoEntity entity) {
     if (entity == null) {
         log.warn(&quot;Entity cannot be null&quot;);
         throw new RuntimeException(&quot;Entity cannot be null&quot;);
     }

     if (entity.getUsrId() == null) {
         log.warn(&quot;Unknown user&quot;);
         throw new RuntimeException(&quot;Unknown user&quot;);
     }
 }
}</code></pre>
</li>
<li><p>TodoController.java</p>
<pre><code class="language-java">@Slf4j
@RestController
@RequestMapping(&quot;/todo&quot;)
public class TodoController {
 @Autowired
 private TodoService service;

 @PostMapping
 public ResponseEntity&lt;?&gt; createTodo(@RequestBody TodoDTO dto) {
     try {
         log.info(&quot;Log: createTodo entrance&quot;);
         TodoEntity entity = TodoDTO.toEntity(dto);

         log.info(&quot;Log:dto -&gt; entity ok&quot;);
         entity.setUsrId(&quot;temporary-usrId&quot;);

         Optional&lt;TodoEntity&gt; entities = service.create(entity);
         log.info(&quot;Log:service.create ok&quot;);

         List&lt;TodoDTO&gt; dtos = entities.stream().map(TodoDTO::new).collect(Collectors.toList());
         log.info(&quot;Log:entities -&gt; dtos ok&quot;);

         ResponseDTO&lt;TodoDTO&gt; response = ResponseDTO.&lt;TodoDTO&gt;builder().data(dtos).build();

         return ResponseEntity.ok().body(response);
     } catch (Exception e) {
         String error = e.getMessage();
         ResponseDTO&lt;TodoDTO&gt; response = ResponseDTO.&lt;TodoDTO&gt;builder().error(error).build();

         return ResponseEntity.badRequest().body(response);
     }
 }
}</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/a150fba1-3c03-4db5-906b-d785ac59bb3f/image.png" alt=""></p>
<h2 id="http-get을-이용한-retrieve-rest-api-개발">HTTP GET을 이용한 Retrieve REST API 개발</h2>
<ol>
<li><p>TodoRepository.java</p>
<pre><code class="language-java">@Repository
public interface TodoRepository extends JpaRepository&lt;TodoEntity, String&gt; {
 @Query(&quot;select t from TodoEntity t where t.usrId = ?1&quot;)
 List&lt;TodoEntity&gt; findByUsrId(String usrId);
}</code></pre>
</li>
<li><p>TodoService.java</p>
<pre><code class="language-java"> public List&lt;TodoEntity&gt; retrieve(final String usrId) {
     return repository.findByUsrId(usrId);
 }</code></pre>
</li>
<li><p>TodoController.java</p>
<pre><code class="language-java"> @GetMapping
 public ResponseEntity&lt;?&gt; retrieveTodoList() {
     String temporaryUsrId = &quot;temporary-usrId&quot;;
     List&lt;TodoEntity&gt; entities = service.retrieve(temporaryUsrId);
     List&lt;TodoDTO&gt; dtos = entities.stream().map(TodoDTO::new).collect(Collectors.toList());
     ResponseDTO&lt;TodoDTO&gt; response = ResponseDTO.&lt;TodoDTO&gt;builder().data(dtos).build();
     return ResponseEntity.ok().body(response);
 }</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/5e3c8153-72d3-42c3-a0c3-94c1f4b75900/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/6bd51c49-b7a7-4c44-92dd-4edc4230d064/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/8fb51681-07ae-4f80-8ca9-57b88851fd7d/image.png" alt=""></p>
<h2 id="http-put을-이용한-update-rest-api-개발">HTTP PUT을 이용한 Update REST API 개발</h2>
<ul>
<li>application.properties에 h2 database configuration 설정하고 h2 console을 설정한다.</li>
</ul>
<p>application.properties</p>
<pre><code>spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false</code></pre><ol>
<li><p>TodoService</p>
<pre><code class="language-java"> public Optional&lt;TodoEntity&gt; update(final TodoEntity entity){
     validate(entity);
     if (repository.existsById(entity.getId())) {
         repository.save(entity);
     } else {
         throw new RuntimeException(&quot;Unknown id&quot;);
     }

     return repository.findById(entity.getId());
 }

 public Optional&lt;TodoEntity&gt; updateTodo(final TodoEntity entity) {
     validate(entity);
     final Optional&lt;TodoEntity&gt; original = repository.findById(entity.getId());

     original.ifPresent(todo -&gt; {
         todo.setTitle(entity.getTitle());
         todo.setDone(entity.isDone());
         repository.save(todo);
     });

     return repository.findById(entity.getId());
 }</code></pre>
</li>
<li><p>TodoController</p>
<pre><code class="language-java"> @GetMapping(&quot;/update&quot;)
 public ResponseEntity&lt;?&gt; update(@RequestBody TodoDTO dto) {
     try {
         TodoEntity entity = TodoDTO.toEntity(dto);
         entity.setUsrId(&quot;temporary-usrId&quot;);
         Optional&lt;TodoEntity&gt; entities = service.update(entity);
         List&lt;TodoDTO&gt; dtos = entities.stream().map(TodoDTO::new).collect(Collectors.toList());
         ResponseDTO&lt;TodoDTO&gt; response = ResponseDTO.&lt;TodoDTO&gt;builder().data(dtos).build();
         return ResponseEntity.ok().body(response);
     } catch (Exception e) {
         String error = e.getMessage();
         ResponseDTO&lt;TodoDTO&gt; response = ResponseDTO.&lt;TodoDTO&gt;builder().error(error).build();

         return ResponseEntity.badRequest().body(response);
     }
 }

 @PutMapping
 public ResponseEntity&lt;?&gt; updateTodo(@RequestBody TodoDTO dto) {
     try {
         TodoEntity entity = TodoDTO.toEntity(dto);
         entity.setUsrId(&quot;temporary-usrId&quot;);
         Optional&lt;TodoEntity&gt; entities = service.updateTodo(entity);
         List&lt;TodoDTO&gt; dtos = entities.stream().map(TodoDTO::new).collect(Collectors.toList());
         ResponseDTO&lt;TodoDTO&gt; response = ResponseDTO.&lt;TodoDTO&gt;builder().data(dtos).build();
         return ResponseEntity.ok().body(response);
     } catch (Exception e) {
         String error = e.getMessage();
         ResponseDTO&lt;TodoDTO&gt; response = ResponseDTO.&lt;TodoDTO&gt;builder().error(error).build();

         return ResponseEntity.badRequest().body(response);
     }
 }</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/ff8eccff-a51f-4833-88e3-69337334ef0b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/9cfea397-f95f-4a57-860a-685165eb425c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/4b717a0d-8fc7-4c57-a409-72647364bb82/image.png" alt=""></p>
<p><a href="http://localhost:8080/h2-console">http://localhost:8080/h2-console</a>에 application.properties에 작성한 접속 정보로 접속 시 DB를 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/ce7e8f3c-4c77-4f1b-a116-304e7a165625/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[4일 차 4장 MSA Application 배포 실습]]></title>
            <link>https://velog.io/@com_jinnn99/4%EC%9D%BC-%EC%B0%A8-4%EC%9E%A5-MSA-Application-%EB%B0%B0%ED%8F%AC-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@com_jinnn99/4%EC%9D%BC-%EC%B0%A8-4%EC%9E%A5-MSA-Application-%EB%B0%B0%ED%8F%AC-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 13 Mar 2025 02:23:55 GMT</pubDate>
            <description><![CDATA[<h1 id="redis-배포-및-db-구축">Redis 배포 및 DB 구축</h1>
<p>Minikube 실행</p>
<ul>
<li>Podman 드라이버를 사용하여 Minikube를 실행</li>
</ul>
<pre><code>$ minikube start --driver=podman --container-runtime=cri-o</code></pre><p>Minikube 배포 확인</p>
<pre><code>$ kubectl get node
$ kubectl get pods -n kube-system</code></pre><p>CoreDNS 스크립트 적용</p>
<ul>
<li>쿠버네티스 내부 통신을 위한 스크립트 파일 적용</li>
</ul>
<pre><code>$ cd ~/edu-msa-file
$ chmod +x coredns-apply.sh
$ ./coredns-apply.sh</code></pre><p>CoreDNS 적용 확인</p>
<ul>
<li>CoreDNS YAML 파일에 &#39;localdomain&#39; 문구가 적용되었는지 확인</li>
</ul>
<pre><code>$ kubectl get cm coredns -n kube-system -o yaml</code></pre><p>Minikube IP 주소 조회</p>
<ul>
<li>서비스 포트의 외부 통신을 위한 Minikube IP 주소를 확인</li>
</ul>
<pre><code>$ minikube ip</code></pre><p>Redis 배포 준비</p>
<ul>
<li>redis-msa-ui.yaml 파일 수정</li>
</ul>
<pre><code>$ vim ~/edu-msa-file/Kubernetes/redis-msa-ui.yaml
${REDIS_MSA_UI}를 &quot;30801&quot;로 수정</code></pre><p>Redis 배포</p>
<ul>
<li>redis-msa-ui.yaml 배포</li>
</ul>
<pre><code>$ cd ~/edu-msa-file/Kubernetes/
$ kubectl apply -f redis-msa-ui.yaml</code></pre><p>MySql 배포 준비</p>
<pre><code>$ vim ~/edu-msa-file/Kubernetes/mysql-msa-board.yaml
${MYSQL_MSA_BOARD}을 &quot;30501&quot;로 수정

$ vim ~/edu-msa-file/Kubernetes/mysql-msa-comment.yaml
${MYSQL_MSA_COMMENT}을 &quot;30601&quot;로 수정

$ vim ~/edu-msa-file/Kubernetes/mysql-msa-user.yaml
${MYSQL_MSA_USER}을 &quot;30701&quot;로 수정</code></pre><p>MySql 배포 및 DB 구축 (board, comment, user)</p>
<pre><code>$ cd ~/edu-msa-file/Kubernetes/
$ kubectl apply -f mysql-msa-board.yaml -f mysql-msa-comment.yaml -f mysql-msa-user.yaml</code></pre><p>MySql 배포 확인</p>
<pre><code>$ kubectl get pods</code></pre><p>Board DB 접속, DB 구축</p>
<pre><code># kubectl exec -it ${POD_NAME} -- /bin/bash  
#조회한 파드의 이름을 아래와 같이 입력하시면 됩니다.
예시) $ kubectl exec -it mysql-msa-board-85bb55fb4f-gpj9f -- /bin/bash

# mysql -u root -p
Enter password: password

mysql&gt; CREATE DATABASE msa_board default CHARACTER SET UTF8;
mysql&gt; USE msa_board;
mysql&gt; CREATE TABLE `TB_BOARD` (
  `board_seq` int(11) NOT NULL AUTO_INCREMENT,
  `board_title` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `board_text` mediumtext COLLATE utf8_unicode_ci,
  `write_user_id` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `write_user_name` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `use_yn` varchar(1) COLLATE utf8_unicode_ci DEFAULT &#39;Y&#39;,
  `create_dt` datetime NOT NULL,
  `update_dt` datetime NOT NULL,
  PRIMARY KEY (`board_seq`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

&gt; exit
# exit</code></pre><p>Comment DB 접속, DB 구축</p>
<pre><code># kubectl exec -it ${POD_NAME} -- /bin/bash  
#조회한 파드의 이름을 아래와 같이 입력하시면 됩니다.
예시) $ kubectl exec -it mysql-msa-comment-694d68d4ff-562wk -- /bin/bash

# mysql -u root -p
Enter password: password

mysql&gt; CREATE DATABASE msa_comment default CHARACTER SET UTF8;
mysql&gt; USE msa_comment;
mysql&gt; CREATE TABLE `TB_COMMENT` (
  `comment_seq` int(11) NOT NULL AUTO_INCREMENT,
  `board_seq` int(11) NOT NULL,
  `comment` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `write_user_id` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `write_user_name` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `use_yn` varchar(1) COLLATE utf8_unicode_ci DEFAULT &#39;Y&#39;,
  `create_dt` datetime NOT NULL,
  `update_dt` datetime NOT NULL,
  PRIMARY KEY (`comment_seq`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

&gt; exit
# exit</code></pre><p>User DB 접속, DB 구축</p>
<pre><code># kubectl exec -it ${POD_NAME} -- /bin/bash  
#조회한 파드의 이름을 아래와 같이 입력하시면 됩니다.
예시) $ kubectl exec -it mysql-msa-user-6c5499bbb5-29skb -- /bin/bash

# mysql -u root -p
Enter password: password

mysql&gt; CREATE DATABASE msa_user default CHARACTER SET UTF8;
mysql&gt; USE msa_user;
mysql&gt; CREATE TABLE `TB_USER` (
  `user_seq` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `user_passwd` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
  `user_name` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `use_yn` varchar(1) COLLATE utf8_unicode_ci DEFAULT &#39;Y&#39;,
  `create_dt` datetime NOT NULL,
  `update_dt` datetime NOT NULL,
  PRIMARY KEY (`user_seq`),
  UNIQUE KEY `id_key` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

&gt; exit
# exit</code></pre><p>🚨 (참고) 실습 시 유의사항
VM 재시작 시 CoreDNS 스크립트 적용, Mysql DB 재구축 필요</p>
<ol>
<li>Minikube 실행</li>
<li>Pod, Service 정상 동작 확인, CoreDNS 스크립트 적용</li>
<li>파드 별 Mysql DB 재구축 필요</li>
<li>실습포털 접속하여 각 기능 정상 동작 확인</li>
</ol>
<h1 id="war-파일-생성">war 파일 생성</h1>
<p>JDK 8 다운로드</p>
<ul>
<li><a href="https://www.oracle.com/java/technologies/downloads/#java8-windows">jdk-8u***-window-x64.exe Download URL</a></li>
</ul>
<p>eGovFrameDev 다운로드</p>
<ul>
<li><a href="https://www.egovframe.go.kr/home/sub.do?menuNo=41">eGovFrameDev-3.9.0-64bit.exe Download URL</a></li>
<li>개발자용 개발환경 32&amp;64bit(Implementation Tool) Version 3.9.0</li>
</ul>
<p>API 소스 다운로드</p>
<ul>
<li><a href="https://github.com/K-PaaS?q=edu&amp;type=all&amp;language=&amp;sort=">Download URL</a></li>
<li>&#39;edu-msa&#39;로 검색하여 <code>edu-msa-ui, edu-msa-zuul, edu-msa-user, edu-msa-comment, edu-msa-board</code> API 소스 파일 다운로드</li>
</ul>
<p>프로젝트 추가 후 설정 파일 수정</p>
<ul>
<li>edu-msa-[board, comment, user] api.properties<ul>
<li>${NAMESPACE}: &quot;default&quot; 입력</li>
<li>${KUBERNETES_URL}: Minikube IP 주소 입력</li>
<li>${MYSQL_MSA_BOARD}: 30501</li>
<li>${MYSQL_MSA_COMMENT}: 30601</li>
<li>${MYSQL_MSA_USER}: 30701</li>
</ul>
</li>
<li>edu-msa-ui api.properties<ul>
<li>${NAMESPACE}: &quot;default&quot;</li>
<li>${KUBERNETES_URL}: Minikube IP 주소 입력</li>
<li>${REDIS_MSA_UI}: 30801</li>
</ul>
</li>
</ul>
<p>각 프로젝트(board, comment, ui, user, zuul) Maven build를 통해 war 파일 생성</p>
<p>각 war 파일을 서버의 <code>/edu-msa-file/Docker/</code> 폴더 안의 <code>edu-msa-*</code> 각 폴더로 이동</p>
<h1 id="이미지-생성-및-배포">이미지 생성 및 배포</h1>
<p>war 파일과 server.xml이 같은 폴더 안에 구성되어 있는지 확인 (board, comment, ui, user)</p>
<p>war 파일과 application.yml이 같은 폴더 안에 구성되어 있는지 확인 (zuul)</p>
<p>Docker hub</p>
<ul>
<li>도커에서 운영하는 도커 이미지 저장소 서비스</li>
</ul>
<p><a href="https://hub.docker.com/">Docker hub URL</a></p>
<p>계정 생성 후, Repositories 클릭하여 Repository Name 입력 후 이미지 저장소 생성 (board, comment, ui, user, zuul)</p>
<p>이미지 저장소 로그인</p>
<pre><code>$ podman login docker.io
Username: 아이디 입력
Password: 패스워드 입력</code></pre><p>이미지 빌드, 태그, 업로드 (board, comment, ui, user, zuul)</p>
<pre><code>$ cd ~/edu-msa-file/Docker/edu-msa-board/ 
$ podman build --tag edu-msa-board:latest .
$ podman tag edu-msa-board ${도커허브 ID}/edu-msa-board
$ podman push ${도커허브 ID}/edu-msa-board

$ cd ~/edu-msa-file/Docker/edu-msa-comment/ 
$ podman build --tag edu-msa-comment:latest .
$ podman tag edu-msa-comment ${도커허브 ID}/edu-msa-comment
$ podman push ${도커허브 ID}/edu-msa-comment

$ cd ~/edu-msa-file/Docker/edu-msa-ui/ 
$ podman build --tag edu-msa-ui:latest .
$ podman tag edu-msa-ui ${도커허브 ID}/edu-msa-ui
$ podman push ${도커허브 ID}/edu-msa-ui

$ cd ~/edu-msa-file/Docker/edu-msa-user/ 
$ podman build --tag edu-msa-user:latest .
$ podman tag edu-msa-user ${도커허브 ID}/edu-msa-user
$ podman push ${도커허브 ID}/edu-msa-user

$ cd ~/edu-msa-file/Docker/edu-msa-zuul/ 
$ podman build --tag edu-msa-zuul:latest .
$ podman tag edu-msa-zuul ${도커허브 ID}/edu-msa-zuul
$ podman push ${도커허브 ID}/edu-msa-zuul</code></pre><h1 id="msa-application-배포">MSA Application 배포</h1>
<p>edu-msa-[board, comment, ui, user, zuul] yaml 파일 수정</p>
<pre><code>$ vim ~/edu-msa-file/Kubernetes/edu-msa-board.yaml
${DOCKER_HUB_ID}을 &quot;도커허브계정&quot;로 입력
${EDU_MSA_BOARD}을 &quot;30201&quot;로 수정

$ vim ~/edu-msa-file/Kubernetes/edu-msa-comment.yaml
${DOCKER_HUB_ID}을 &quot;도커허브계정&quot;로 입력
${EDU_MSA_COMMENT}을 &quot;30301&quot;로 수정

$ vim ~/edu-msa-file/Kubernetes/edu-msa-ui.yaml
${DOCKER_HUB_ID}을 &quot;도커허브계정&quot;로 입력
${EDU_MSA_UI}을 &quot;30001&quot;로 수정

$ vim ~/edu-msa-file/Kubernetes/edu-msa-user.yaml
${DOCKER_HUB_ID}을 &quot;도커허브계정&quot;로 입력
${EDU_MSA_USER}을 &quot;30401&quot;로 수정

$ vim ~/edu-msa-file/Kubernetes/edu-msa-zuul.yaml
${DOCKER_HUB_ID}을 &quot;도커허브계정&quot;로 입력
${NAMESPACE}을 &quot;default&quot;로 입력 (총 3줄)
${EDU_MSA_ZULL}을 &quot;30101&quot;로 수정</code></pre><p>edu-msa-[board, comment, ui, user, zuul] yaml 파일 API 배포</p>
<pre><code>$ cd ~/edu-msa-file/Kubernetes/
$ kubectl apply -f edu-msa-board.yaml -f edu-msa-comment.yaml -f edu-msa-ui.yaml -f edu-msa-user.yaml -f edu-msa-zuul.yaml</code></pre><p>Pod 및 Service 배포 확인</p>
<pre><code>$ kubectl get pods
$ kubectl get service</code></pre><p>MSA Application 포털 접속</p>
<ul>
<li>VMware 화면 속 웹 브라우저에서 Minikube IP 주소와 edu-msa-ui의 노드 포트 번호를 통해 MSA 앱 접속을 실행하여 회원가입, 게시판, 댓글 기능이 정상 작동하는지 확인</li>
<li>ex. 192.168.49.2:30001/user/login</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[4일 차 3장 Minikube 및 Podman 설치 실습]]></title>
            <link>https://velog.io/@com_jinnn99/4%EC%9D%BC-%EC%B0%A8-3%EC%9E%A5-Minikube-%EB%B0%8F-Podman-%EC%84%A4%EC%B9%98-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@com_jinnn99/4%EC%9D%BC-%EC%B0%A8-3%EC%9E%A5-Minikube-%EB%B0%8F-Podman-%EC%84%A4%EC%B9%98-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Wed, 12 Mar 2025 08:06:18 GMT</pubDate>
            <description><![CDATA[<h1 id="minikube-설치">Minikube 설치</h1>
<p>Minikube</p>
<ul>
<li>로컬 환경에서 쿠버네티스 클러스터 환경을 단일 워커 노드로 구현하여 사용할 수 있는 도구</li>
</ul>
<p>Minikube 설치</p>
<pre><code>$ cd ~
$ wget https://github.com/kubernetes/minikube/releases/download/v1.32.0/minikube-linux-amd64
$ sudo cp minikube-linux-amd64 /usr/local/bin/minikube
$ sudo install minikube-linux-amd64 /usr/local/bin/minikube
$ minikube version</code></pre><p>kubectl 설치</p>
<ul>
<li>✅ Minikube 클러스터에 명령을 내리는 CLI</li>
</ul>
<pre><code>$ curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.29.2/bin/linux/amd64/kubectl
$ chmod +x ./kubectl
$ sudo mv ./kubectl /usr/local/bin/kubectl
$ kubectl version -o json  --client</code></pre><h1 id="podman-설치">Podman 설치</h1>
<p>Podman</p>
<ul>
<li>리눅스 시스템에서 컨테이너를 개발, 관리, 실행하기 위한 오픈소스 툴</li>
<li>데몬이 없는 포괄적인 아키텍처로 컨테이너를 더 안전하고 손쉽게 관리할 수 있고, 관련 툴과 기능을 통해 컨테이너 환경을 사용자 정의 방식으로 요구사항에 적합하게 설정할 수 있음</li>
</ul>
<p>Podman 설치 및 MSA 애플리케이션 배포 파일 다운로드</p>
<ul>
<li>✅ Podman 설치 및 MSA 애플리케이션 배포를 위한 배포 설정 파일을 다운로드한다.</li>
</ul>
<pre><code>$ cd ~
$ git clone https://github.com/K-PaaS/edu-msa-file.git</code></pre><p>Podman 설치</p>
<ul>
<li>컨테이너 및 컨테이너 이미지를 개발, 관리, 실행하기 위한 컨테이너 엔진 Podman을 설치</li>
<li><code>/etc/containers/</code> 경로에 있는 <code>registries.conf</code> 파일과 <code>policy.json</code> 파일을 수정한다.</li>
</ul>
<pre><code>$ cd ~/edu-msa-file
$ chmod +x podman-install.sh
$ sudo ./podman-install.sh
$ sudo rm -rf ~/.local/share/containers/

$ cd /etc/containers/
$ sudo rm registries.conf
$ sudo vim registries.conf

아래 내용 붙여넣기

[registries.search]
registries=[&quot;docker.io&quot;]

$ sudo rm policy.json
$ sudo vim policy.json

아래 내용 붙여넣기

{&quot;default&quot;:[{&quot;type&quot;:&quot;insecureAcceptAnything&quot;}]}

Podman 설정 적용

$ sudo podman system reset</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[4일 차 2장 가상머신 설치 실습]]></title>
            <link>https://velog.io/@com_jinnn99/4%EC%9D%BC-%EC%B0%A8-2%EC%9E%A5-%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0-%EC%84%A4%EC%B9%98-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@com_jinnn99/4%EC%9D%BC-%EC%B0%A8-2%EC%9E%A5-%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0-%EC%84%A4%EC%B9%98-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Wed, 12 Mar 2025 06:48:26 GMT</pubDate>
            <description><![CDATA[<h1 id="가상머신-생성-및-설정">가상머신 생성 및 설정</h1>
<ul>
<li>✅ VMware Workstation pro(Personal Use) 설치 가능한 개인용 PC (Windows 환경 사용 권장)<ul>
<li>CPU: 4개 이상</li>
<li>메모리(RAM): 16GB 이상</li>
<li>디스크: 30GB 이상 (Minikube 배포 최소 필요 공간)</li>
<li><a href="https://support.broadcom.com/group/ecx/productdownloads?subfamily=VMware%20Workstation%20Pro">VMware Workstation Pro Download URL</a></li>
</ul>
</li>
<li>✅ OS: Ubuntu 20.04<ul>
<li><a href="https://releases.ubuntu.com/20.04/">Ubuntu 20.04 Image Download URL</a></li>
</ul>
</li>
<li>✅ CPU: 4 core 이상</li>
<li>✅ 메모리: 12GB 이상 (16GB 이상 권장)</li>
<li>✅ 디스크: 30GB 이상의 여유 공간</li>
<li>✅ 기타: 인터넷 연결 가능한 환경, 외부 포트 개방 환경 (SFTP 기능 사용 시 필요)</li>
</ul>
<p>가상머신 실행 후 Openssh 서버 설치 및 IP 주소 확인
<code>sudo apt install -y openssh-server</code>
<code>ip a</code></p>
<h1 id="ssh-접속">SSH 접속</h1>
<p>MobaXterm</p>
<ul>
<li>Window 환경에서 SSH, FTP 등 다양한 원격접속을 할 수 있는 프로그램</li>
<li><a href="https://mobaxterm.mobatek.net/download-home-edition.html">MobaXterm Home Edition Download URL</a></li>
</ul>
<p>사전 패키지 업데이터 및 툴 설치</p>
<ul>
<li><code>sudo apt-get update</code></li>
<li><code>sudo apt-get install -y curl vim git</code></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[4일 차 1장 실습개요 및 아키텍처 이해]]></title>
            <link>https://velog.io/@com_jinnn99/4%EC%9D%BC-%EC%B0%A8-1%EC%9E%A5-%EC%8B%A4%EC%8A%B5%EA%B0%9C%EC%9A%94-%EB%B0%8F-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@com_jinnn99/4%EC%9D%BC-%EC%B0%A8-1%EC%9E%A5-%EC%8B%A4%EC%8A%B5%EA%B0%9C%EC%9A%94-%EB%B0%8F-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Wed, 12 Mar 2025 06:12:40 GMT</pubDate>
            <description><![CDATA[<p>💡 4일 차 학습목표</p>
<ol>
<li>K-PaaS 컨테이너 플랫폼과 유사한 Minikube 실습환경의 Architecture 및 배포 흐름을 이해할 수 있다.</li>
<li>가상머신, Minikube, Podman을 활용하여 실습환경을 구성할 수 있다.</li>
<li>구성한 실습환경에서 컨테이너 이미지 생성 및 컨테이너 배포를 통해 MSA Application을 구축할 수 있다.</li>
</ol>
<hr>
<h1 id="실습-개요">실습 개요</h1>
<p>📌 (실습 목표) 컨테이너 이미지 생성 및 컨테이너 배포를 통한 MSA 애플리케이션 구축</p>
<h1 id="architecture-구성-이해">Architecture 구성 이해</h1>
<p>배포 흐름도</p>
<ol>
<li>가상머신 설치
Ubuntu 20.04 다운로드</li>
<li>Minikube 설치
Kubectl 설치</li>
<li>Podman 설치</li>
<li>Redis, DB 환경 구축</li>
<li>war파일 생성
이미지 생성 및 배포</li>
<li>Minikube 클러스터 API yaml 배포</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[3일 차 4장 Container Platform 소스 컨트롤 구축]]></title>
            <link>https://velog.io/@com_jinnn99/3%EC%9D%BC-%EC%B0%A8-4%EC%9E%A5-Container-Platform-%EC%86%8C%EC%8A%A4-%EC%BB%A8%ED%8A%B8%EB%A1%A4-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@com_jinnn99/3%EC%9D%BC-%EC%B0%A8-4%EC%9E%A5-Container-Platform-%EC%86%8C%EC%8A%A4-%EC%BB%A8%ED%8A%B8%EB%A1%A4-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Wed, 12 Mar 2025 05:58:20 GMT</pubDate>
            <description><![CDATA[<h1 id="소스-컨트롤-구축">소스 컨트롤 구축</h1>
<p><a href="https://github.com/K-PaaS/Guide">K-PaaS 가이드 문서</a>
<a href="https://github.com/K-PaaS/container-platform/blob/master/install-guide/source-control/cp-source-control-standalone-guide.md">컨테이너 플랫폼 소스 컨트롤 가이드</a></p>
<p>소스 컨트롤</p>
<ul>
<li>시스템 형상 요소의 기능적 특성이나 물리적 특성을 문서화하고 그러한 특성의 변경을 관리하며, 변경의 과정이나 실현 상황을 기록 및 보고하여 지정된 요건이 충족되었다는 사실을 검증하는 것, 또는 그 과정</li>
<li>형상 관리는 프로젝트를 개발하는 동안 생산성과 안정성을 높여 좋은 품질의 소프트웨어를 생산하고 유지보수도 용이하게 하는데 목적이 있음</li>
</ul>
<p>소프트웨어 형상 관리(SCM, Software Configuration Management) 기능</p>
<ul>
<li>언제라도 특정 시간대에 가장 안정적인 버전의 소프트웨어를 유지할 수 있도록 소프트웨어 제품이 변경되어가는 상태에 대한 가시성을 확보</li>
<li>적절한 변경 관리를 통하여 무절제한 변경을 사전에 예방하고 변경에 따른 부작용을 최소화</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[3일 차 3장 Container Platform 파이프라인 구축]]></title>
            <link>https://velog.io/@com_jinnn99/3%EC%9D%BC-%EC%B0%A8-3%EC%9E%A5-Container-Platform-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@com_jinnn99/3%EC%9D%BC-%EC%B0%A8-3%EC%9E%A5-Container-Platform-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Wed, 12 Mar 2025 04:57:25 GMT</pubDate>
            <description><![CDATA[<h1 id="파이프라인-서비스-설치">파이프라인 서비스 설치</h1>
<p><a href="https://github.com/K-PaaS/Guide">K-PaaS 가이드 문서</a>
<a href="https://github.com/K-PaaS/container-platform/blob/master/install-guide/pipeline/cp-pipeline-standalone-guide.md">컨테이너 플랫폼 파이프라인 설치 가이드</a></p>
<p>Pipeline</p>
<ul>
<li>생산 라인 등과 같이 <strong>여러 공정별로 생산 라인이 나열되어 있고 동시에 공정별 프로세서를 가능</strong>하게 하는 것</li>
<li>시스템의 효율을 높이기 위해 명령문을 수행하면서 몇 가지의 특수한 작업들을 <strong>병렬 처리하도록 설계된 하드웨어 기법</strong></li>
</ul>
<p>파이프라인 기능 및 역할</p>
<ul>
<li>각 프로세스들을 저장한 파일 이름들 사이를 수직선으로 분리시키면 셀(cell)이 인식. 즉, 수직선 왼쪽 파일의 출력이 오른쪽 파일의 입력으로 연결됨</li>
<li>배포 파이프라인은 지속적 배포의 핵심 요소</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[3일 차 2장 Container Platform 포털 구축]]></title>
            <link>https://velog.io/@com_jinnn99/3%EC%9D%BC-%EC%B0%A8-2%EC%9E%A5-Container-Platform-%ED%8F%AC%ED%84%B8-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@com_jinnn99/3%EC%9D%BC-%EC%B0%A8-2%EC%9E%A5-Container-Platform-%ED%8F%AC%ED%84%B8-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Wed, 12 Mar 2025 02:45:35 GMT</pubDate>
            <description><![CDATA[<h1 id="포털-설치">포털 설치</h1>
<p><a href="https://github.com/K-PaaS/Guide">K-PaaS 가이드 문서</a>
<a href="https://github.com/K-PaaS/container-platform/blob/master/install-guide/portal/cp-portal-standalone-guide.md">컨테이너 플랫폼 포털 설치 가이드 문서</a></p>
<p><strong>포털 배포 파일 다운로드</strong></p>
<ul>
<li>컨테이너플랫폼포털배포(Deployment) 파일을 다운로드하여 해당경로에 위치</li>
<li>✅ 이 단계는 반드시 Kubernetes Control Plane(Master Node) 노드에서 진행해야 함</li>
</ul>
<ol>
<li><a href="https://nextcloud.k-paas.org/index.php/s/ZcFt4cpeXj8d4o4/download">컨테이너 플랫폼포털배포(Deployment) 파일 다운로드</a><pre><code># Deployment 파일 다운로드 경로 생성 
$ mkdir -p ~/workspace/container-platform 
$ cd ~/workspace/container-platform 
# Deployment 파일 다운로드 및 파일 경로확인 
$ wget --content-disposition https://nextcloud.k-paas.org/index.php/s/ZcFt4cpeXj8d4o4/download 
$ ls ~/workspace/container-platform 
cp-portal-deployment-v1.6.0.tar.gz 
# Deployment 파일 압축 해제 
$ tar -xvf cp-portal-deployment-v1.6.0.tar.gz</code></pre></li>
<li>배포(Deployment) 파일 디렉토리 구성</li>
</ol>
<p><strong>포털 변수 정의</strong></p>
<ul>
<li>컨테이너플랫폼포털배포 전변숫값정의가 필요하므로 배포에 필요한 정보를 확인하여 변수를 설정</li>
<li>Keycloak 기본배포프로토콜은 HTTP이며 인증서를 통한 HTTPS를 설정하고자 하는 경우 아래가이드를 참조하여 먼저 처리한다.</li>
</ul>
<ol>
<li>쉘파일 실행<pre><code>$ cd ~/workspace/container-platform/cp-portal-deployment/script 
$ vi cp-portal-vars.sh</code></pre></li>
<li>컨테이너 플랫폼 포털 변수 정의<pre><code>$ cd ~/workspace/container-platform/cp-portal-deployment/script 
$ vi cp-portal-vars.sh</code></pre></li>
</ol>
<p><strong>포털 배포</strong></p>
<ul>
<li>컨테이너 플랫폼 포털 배포 스크립트 실행 및 정상 배포 확인</li>
</ul>
<ol>
<li>쉘파일 실행<pre><code>$ chmod +x deploy-cp-portal.sh 
$ ./deploy-cp-portal.sh</code></pre></li>
<li>리소스별로 정상 배포되었는지 확인<pre><code>$ kubectl get all -n harbor 
$ kubectl get all –n mariadb 
$ kubectl get all –n keycloak 
$ kubectl get all –n cp-portal 
$ kubectl get all –n vault 
$ kubectl get all –n chartmuseum 
$ kubectl get all –n chaos-mesh</code></pre></li>
</ol>
<ul>
<li>✅ 리소스 Pod의 경우 Node에 바인딩 및 컨테이너생성 후 Running 상태로 전환되기까지 몇 초 소요됨</li>
<li>서비스접속 Host 조회 <code>$ kubectl get ingress –A</code></li>
</ul>
<p><strong>포털 리소스 정상 배포 확인</strong></p>
<ul>
<li>Harbor 리소스 조회 : <code>$ kubectl get all -n harbor</code>
✅ Harbor는 Kubespray를 통해 설치된 Kubernetes Cluster 환경에 포털이미지 및 Helm Chart를 관리</li>
<li>MariaDB 리소스 조회 : <code>$ kubectl get all -n mariadb</code>
✅ MariaDB(RDBMS)는 메타 데이터(다른 데이터를 기술하기 위해 사용하는 데이터)를관리</li>
<li>Keycloak 리소스 조회 : <code>$ kubectl get all -n keycloak</code>
✅ Keycloak 은 컨테이너 플랫폼 포털 사용자 인증을 관리</li>
<li>Keycloak 리소스 조회 : <code>$ kubectl get all -n cp-portal</code></li>
<li>Vault Pod 조회 : <code>$ kubectl get pods -n vault</code>
✅ Vault는 인증데이터를 관리</li>
<li>Vault Pod 조회 : <code>$ kubectl get pods -n chartmuseum</code></li>
<li>Vault Pod 조회 : <code>$ kubectl get pods -n chaos-mesh</code></li>
</ul>
<p><strong>포털 리소스 삭제</strong></p>
<ul>
<li>배포된 포털 리소스의 삭제를 원하는 경우 아래 스크립트를 실행</li>
<li>🚨 포털이 운영되는 상태에서 해당 스크립트 실행 시, 운영에 필요한 리소스가 모두 삭제되므로 주의 필요<pre><code>$ cd ~/workspace/container-platform/cp-portal-deployment/script 
$ chmod +x uninstall-cp-portal.sh 
$ ./uninstall-cp-portal.sh 
Are you sure you want to delete the container platform portal? &lt;y/n&gt; y</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[3일 차 1장 Container Platform 클러스터 구축]]></title>
            <link>https://velog.io/@com_jinnn99/3%EC%9D%BC-%EC%B0%A8-1%EC%9E%A5-Container-Platform-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@com_jinnn99/3%EC%9D%BC-%EC%B0%A8-1%EC%9E%A5-Container-Platform-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Wed, 12 Mar 2025 01:36:32 GMT</pubDate>
            <description><![CDATA[<p>💡 3일 차 학습목표</p>
<ol>
<li>K-PaaS 컨테이너 플랫폼 클러스터 배포과정을 이해한다.</li>
<li>K-PaaS 컨테이너 플랫폼 포털 서비스의 배포과정과 주요 기능을 이해한다.</li>
<li>K-PaaS 컨테이너 플랫폼 파이프라인 서비스, 소스 컨트롤 서비스의 배포과정과 주요 기능을 이해한다.</li>
</ol>
<hr>
<h1 id="container-platform-설치-개요">Container Platform 설치 개요</h1>
<p><a href="https://github.com/K-PaaS/Guide">K-PaaS 가이드 문서</a>
<a href="https://github.com/K-PaaS/container-platform/blob/master/install-guide/Readme.md">컨테이너 플랫폼(CP) 설치 가이드</a>
<a href="https://github.com/K-PaaS/container-platform/blob/master/use-guide/Readme.md">컨테이너 플랫폼(CP) 사용 가이드</a></p>
<h1 id="클러스터-설치-준비">클러스터 설치 준비</h1>
<table>
<thead>
<tr>
<th>인스턴스 종류</th>
<th>인스턴스 개수</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Install</strong></td>
<td>1개</td>
<td>Install 인스턴스 구성을 권장 / Control Plane 인스턴스로 대체 가능</td>
</tr>
<tr>
<td><strong>Control Plane</strong></td>
<td>1개 이상</td>
<td>테스트 환경에서는 1개, 실제 운영 환경에서는 3개 이상 권장</td>
</tr>
<tr>
<td><strong>Worker</strong></td>
<td>1~3개 이상</td>
<td>NFS 스토리지 사용 시 1개 이상, Rook-Ceph 스토리지 사용 시 3개 이상</td>
</tr>
<tr>
<td><strong>Storage</strong></td>
<td>1개</td>
<td>NFS 스토리지 사용 시 필요</td>
</tr>
<tr>
<td><strong>Load Balancer</strong></td>
<td>1~2개</td>
<td>HA Control Plane 구성 시 필요</td>
</tr>
</tbody></table>
<p>✅ 머신 당 deb / rpm 호환 Linux OS (Ubuntu 22.04)가 설치되어야 함
✅ 머신 당 16G 이상의 RAM (최소사양 8G)
✅ 머신 당 4개 이상의 CPU (최소사양 2개)
✅ 클러스터의 모든 시스템 간의 완전한 네트워크 연결 (인터넷 환경 양호)</p>
<p>방화벽 정보 (필수 설정)</p>
<p>Control Plane 노드</p>
<table>
<thead>
<tr>
<th>프로토콜</th>
<th>포트</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>TCP</td>
<td>111</td>
<td>NFS Portmapper</td>
</tr>
<tr>
<td>TCP</td>
<td>2049</td>
<td>NFS</td>
</tr>
<tr>
<td>TCP</td>
<td>2379-2380</td>
<td>etcd server client API</td>
</tr>
<tr>
<td>TCP</td>
<td>6443</td>
<td>Kubernetes API Server</td>
</tr>
<tr>
<td>TCP</td>
<td>10250</td>
<td>Kubelet API</td>
</tr>
<tr>
<td>TCP</td>
<td>10251</td>
<td>kube-scheduler</td>
</tr>
<tr>
<td>TCP</td>
<td>10252</td>
<td>kube-controller-manager</td>
</tr>
<tr>
<td>TCP</td>
<td>10255</td>
<td>Read-Only Kubelet API</td>
</tr>
<tr>
<td>TCP</td>
<td>4789</td>
<td>Calico networking VXLAN</td>
</tr>
</tbody></table>
<p>Worker 노드</p>
<table>
<thead>
<tr>
<th>프로토콜</th>
<th>포트</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>TCP</td>
<td>111</td>
<td>NFS Portmapper</td>
</tr>
<tr>
<td>TCP</td>
<td>2049</td>
<td>NFS</td>
</tr>
<tr>
<td>TCP</td>
<td>10250</td>
<td>Kubelet API</td>
</tr>
<tr>
<td>TCP</td>
<td>10255</td>
<td>Read-Only Kubelet API</td>
</tr>
<tr>
<td>TCP</td>
<td>30000-32767</td>
<td>NodePort Services</td>
</tr>
<tr>
<td>TCP</td>
<td>4789</td>
<td>Calico networking VXLAN</td>
</tr>
</tbody></table>
<p>K-PaaS 지원 스토리지 정보 (필수 설정)</p>
<ul>
<li>K-PaaS 컨테이너 플랫폼 클러스터 설치 전 스토리지를 선택<ul>
<li>✅ NFS<ul>
<li>NFS Server 설치 가이드 제공</li>
<li>클러스터 설치 전에 신규 인스턴스 생성하여 NFS Server 설치 필요</li>
</ul>
</li>
<li>✅ ceph<ul>
<li>ROOK을 통한 배포 단순화</li>
<li>기존 인스턴스에 볼륨 추가</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>쿠버네티스 서비스 External IP 설정 (필수 설정)</p>
<ul>
<li>✅ K-PaaS 컨테이너 플랫폼 서비스 구성을 위해 특정 서비스에 External IP 설정 필요</li>
</ul>
<p>로드밸런서 (HA Control Plane 구성 시 필수 설정)</p>
<ul>
<li>✅ HA Control Plane 구성이 아닐 경우 로드밸런서 구성 과정은 생략하고 다음 과정을 진행</li>
</ul>
<h1 id="클러스터-설치">클러스터 설치</h1>
<p>클러스터 설치 과정</p>
<ul>
<li>환경구성 &gt; NFS 서버설치 &gt; SSH key 생성 및 배포 &gt; Deployment 다운로드 &gt; 환경변수 입력 &gt; 쉘스크립트 배포 &gt; 설치 확인</li>
</ul>
<p>SSH 생성 및 배포</p>
<ul>
<li>클러스터 설치를 위해 인벤토리의 모든 서버들에 SSH Key 복사, 붙여넣기</li>
<li>✅ RSA 공개키를 이용하여 SSH 접속 설정</li>
<li>SSH Key 생성 및 배포 이후의 모든 설치과정은 Control Plane 노드에서 진행</li>
</ul>
<ol>
<li>Control Plane 노드에서 RSA 공개키 생성
<code>ssh-keygen -t rsa -m PEM -N &#39;&#39; -f $HOME/.ssh/id_rsa</code></li>
<li>RSA 공개키 출력 및 복사
<code>cat ~/.ssh/id_rsa.pub</code></li>
<li>Control Plane, Worker 노드의 authorized_keys 파일 본문의 마지막 줄 하단에 공개키 붙여넣기 (🔔 Control plane 수 + Worker 노드 수만큼 반복)
<code>vi .ssh/authorized_keys</code></li>
</ol>
<p>Deployment 다운로드</p>
<ul>
<li>클러스터 설치에 필요한 Deployment를 다운로드 후 설치 작업 경로로 이동하여 설치</li>
<li>✅ 이 단계부터는 Control Plane 노드에서만 진행</li>
</ul>
<ol>
<li>클러스터 Deployment를 다운로드하여 설치 작업경로에 위치
<a href="https://github.com/k-paas/cp-deployment">Kubespray Download URL</a></li>
<li>git clone 명령을 통해 다음 경로에서 클러스터 Deployment 다운로드 진행 가능
<code>git clone https://github.com/K-PaaS/cp-deployment.git 0b branch_v1.5.x</code></li>
</ol>
<p>환경변수 정의</p>
<ul>
<li>클러스터 설치에 필요한 환경변수를 사전 정의 후 쉘 스크립트를 통해 설치 진행</li>
</ul>
<ol>
<li>클러스터 설치경로로 이동
<code>cd ~/cp-deployment/single</code></li>
<li>클러스터 설치에 필요한 환경변수 정보를 입력
<code>vi cp-cluster-vars.sh</code></li>
</ol>
<p>쉘스크립트 배포</p>
<ul>
<li>쉘 스크립트를 통해 필요 패키지 설치, 클러스터 설치 환경변수 설정, Ansible playbook을 통한 컨테이너 플랫폼 클러스터 설치를 순차적으로 진행</li>
</ul>
<ol>
<li>쉘 스크립트를 통해 설치 진행
<code>source deploy-cp-cluster.sh</code></li>
</ol>
<p>클러스터 정상 설치 확인</p>
<ul>
<li>노드 및 kube-system 네임스페이스의 Pod를 확인하여 클러스터 설치 확인</li>
</ul>
<ol>
<li>Kubernetes Node 확인
<code>kubectl get nodes</code></li>
<li>kube-system Namespace의 Pod 확인
<code>kubectl get pods -n kube-system</code></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[2일 차 3장 Container Platform 주요 SW]]></title>
            <link>https://velog.io/@com_jinnn99/2%EC%9D%BC-%EC%B0%A8-3%EC%9E%A5-Container-Platform-%EC%A3%BC%EC%9A%94-SW</link>
            <guid>https://velog.io/@com_jinnn99/2%EC%9D%BC-%EC%B0%A8-3%EC%9E%A5-Container-Platform-%EC%A3%BC%EC%9A%94-SW</guid>
            <pubDate>Tue, 11 Mar 2025 02:47:19 GMT</pubDate>
            <description><![CDATA[<h1 id="클러스터-구성-소프트웨어">클러스터 구성 소프트웨어</h1>
<p>Kubespray</p>
<ul>
<li>kubernetes 설치를 지원하는 자동화 도구</li>
<li>ansible을 통해 구축하고자 하는 설정값만 맞게 변경하면 됨</li>
</ul>
<p>Kubernetes</p>
<ul>
<li>컨테이너화된 앱을 자동으로 배포, 스케일링 및 관리해 주는 오케스트레이션 도구</li>
</ul>
<p>CRI-O</p>
<ul>
<li>Kuberenetes를 위한 표준(OCI) 컨테이너 런타임 인터페이스(Container Runtime Interface)</li>
</ul>
<p>Calico</p>
<ul>
<li>Container, VM 환경에서 L3기반 Virtual Network 구축을 지원하는 도구</li>
</ul>
<p>Metal LB</p>
<ul>
<li>Kubernetes의 Service를 LoadBalancer 타입으로 서비스할 수 있도록 해주는 솔루션</li>
</ul>
<p>NGINX Ingress Controller</p>
<ul>
<li>Kubernetes 및 컨테이너화된 환경의 클라우드 네이티브 앱을 위한 트래픽 관리 솔루션</li>
</ul>
<p>Helm</p>
<ul>
<li>Kubernetes용으로 구축된 SW를 제공, 공유 및 사용할 수 있는 오픈소스 패키지 매니저</li>
</ul>
<p>Istio</p>
<ul>
<li>오픈소스 기반의 분산형 마이크로서비스 기반 앱을 어디서나 실행할 수 있도록 지원하는 서비스 메시</li>
</ul>
<p>Podman</p>
<ul>
<li>오픈소스 기반의 Linux 시스템에서 컨테이너를 개발, 관리, 실행하기 위한 도구</li>
</ul>
<p>OpenTofu</p>
<ul>
<li>클라우드 환경에서 인프라 관리를 단순화하는 오픈소스 코드 프로비저닝 도구</li>
</ul>
<p>NFS</p>
<ul>
<li>네트워크를 통해 다른 호스트에 있는 파일을 공유해 사용할 수 있도록한 분산 파일 시스템</li>
</ul>
<p>Rook Ceph</p>
<ul>
<li>높은 확장성과 다양한 스토리지 프로토콜 인터페이스 지원하는 오픈소스 분산 스토리지 시스템</li>
</ul>
<h1 id="포털-구성-소프트웨어">포털 구성 소프트웨어</h1>
<p>Vault</p>
<ul>
<li>신원 기반 시크릿 및 암호화 관리 시스템</li>
</ul>
<p>Harbor</p>
<ul>
<li>오픈소스 기반의 컨테이너 이미지를 관리하는 기능을 구현한 레지스트리</li>
</ul>
<p>MariaDB</p>
<ul>
<li>오픈소스 기반의 관계형 DB 관리 시스템(RDBMS)</li>
</ul>
<p>Keycloak</p>
<ul>
<li>오픈소스 기반의 IAM(Identity and Access Management) SW</li>
</ul>
<p>ChartMuseum</p>
<ul>
<li>Chart를 ChartMuseum에 저장해 놓고서 불러와서 사용할 수 있는 레파지토리</li>
</ul>
<p>Chaos Mesh</p>
<ul>
<li>클라우드 네이티브 환경을 위한 오픈소스 카오스 엔지니어링 플랫폼</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2일 차 2장 Container Podman, K8S 이해]]></title>
            <link>https://velog.io/@com_jinnn99/2%EC%9D%BC-%EC%B0%A8-2%EC%9E%A5-Container-Podman-K8S-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@com_jinnn99/2%EC%9D%BC-%EC%B0%A8-2%EC%9E%A5-Container-Podman-K8S-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Mon, 10 Mar 2025 07:40:49 GMT</pubDate>
            <description><![CDATA[<h1 id="container-이해">Container 이해</h1>
<p>Container</p>
<ul>
<li>가상화된 OS 위에서 애플리케이션의 독립적인 실행에 필요한 파일(소스코드, 라이브러리 등)을 모은 패키지</li>
<li>애플리케이션의 코드를 관련 구성 파일, 라이브러리 및 앱 실행에 필요한 종속성과 함께 번들로 제공하는 SW 패키지</li>
</ul>
<p>Hypervisor 기술과 Container 기술</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>Hypervisor (VM)</th>
<th>Container</th>
</tr>
</thead>
<tbody><tr>
<td><strong>가상화 방식</strong></td>
<td>하드웨어 가상화 (Guest OS 포함)</td>
<td>OS 가상화 (커널 공유)</td>
</tr>
<tr>
<td><strong>성능</strong></td>
<td>무거움, 부팅 속도 느림</td>
<td>✔ 가벼움, 빠른 실행</td>
</tr>
<tr>
<td><strong>격리 수준</strong></td>
<td>✔ 강한 격리 (보안성 높음)</td>
<td>약한 격리 (보안 고려 필요)</td>
</tr>
<tr>
<td><strong>리소스 사용량</strong></td>
<td>상대적으로 높음</td>
<td>✔ 상대적으로 낮음</td>
</tr>
<tr>
<td><strong>배포 속도</strong></td>
<td>느림 (OS 부팅 필요)</td>
<td>✔ 빠름 (이미지 실행)</td>
</tr>
<tr>
<td><strong>주요 활용 분야</strong></td>
<td>데이터센터, 서버 가상화</td>
<td>클라우드, DevOps, MSA</td>
</tr>
</tbody></table>
<ul>
<li>Hyperviosr 기술은 물리적인 서버 위에서 각각의 OS를 구성하여 그 위에 애플리케이션을 운영하는 구조</li>
<li>Container 기술은 OS 레벨에서 프로세스를 격리하는 가상화 기술</li>
<li><strong>안전성이 중요한 환경</strong>에서는 <strong>Hypervisor (VM)</strong></li>
<li><strong>빠른 배포와 경량 운영이 필요한 환경</strong>에서는 <strong>Container</strong></li>
<li>최근에는 <strong>하이브리드 환경</strong>(VM + Container)도 많이 사용됨 (예: Kubernetes on VMware)</li>
</ul>
<p>Container 특징</p>
<ul>
<li>개발과 운영의 관심사 분리</li>
<li>지속적인 개발, 통합 및 배포</li>
<li>기민한 앱 생성과 배포</li>
<li>가시성(observability)</li>
<li>개발, 테스팅 및 운영 환경에 걸친 일관성</li>
<li>클라우드 및 OS 배포판 간 이식성</li>
<li>앱 중심 관리</li>
</ul>
<p>Container Engine</p>
<ul>
<li>컨테이너를 관리하기 위한 API나 CLI 도구를 제공하는 SW</li>
<li>사용자 입력을 받고, 컨테이너 이미지를 꺼내고(pull), 컨테이너 실행 방법을 명시한 메타데이터를 만든 다음, 컨테이너 런타임에 이 정보들을 전달</li>
</ul>
<p>Container Runtime</p>
<ul>
<li>루트 파일시스템과 메타 데이터(spec file)를 받아 컨테이너를 실행하는 도구</li>
</ul>
<h1 id="podman-이해">Podman 이해</h1>
<p>Podman</p>
<ul>
<li>리눅스 시스템에서 컨테이너 및 컨테이너 이미지를 실행, 관리, 배포할 수 있도록 설계된 Daemonless 컨테이너 엔진</li>
</ul>
<p>Podman과 Docker의 차이</p>
<table>
<thead>
<tr>
<th>항목</th>
<th><strong>Podman</strong></th>
<th><strong>Docker</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>데몬 (Daemon)</strong></td>
<td>❌ 없음 (데몬리스)</td>
<td>✅ 있음 (<code>dockerd</code> 항상 실행)</td>
</tr>
<tr>
<td><strong>Rootless 모드</strong></td>
<td>✅ 기본 지원</td>
<td>⚠️ 제한적 (설정 필요)</td>
</tr>
<tr>
<td><strong>보안성</strong></td>
<td>🔒 더 안전함 (root 권한 불필요)</td>
<td>🔓 root 권한 필요 (보안 위험)</td>
</tr>
<tr>
<td><strong>OCI 호환성</strong></td>
<td>✅ 완전한 OCI 호환</td>
<td>✅ OCI 호환 (containerd 사용)</td>
</tr>
<tr>
<td><strong>Compose 지원</strong></td>
<td>⚠️ <code>podman-compose</code> 별도 설치 필요</td>
<td>✅ <code>docker-compose</code> 기본 지원</td>
</tr>
<tr>
<td><strong>Kubernetes 연동</strong></td>
<td>✅ <code>podman generate kube</code> 지원</td>
<td>✅ <code>docker stack</code> 사용 가능</td>
</tr>
<tr>
<td><strong>이미지 호환성</strong></td>
<td>✅ Docker 이미지 그대로 사용 가능</td>
<td>✅ Docker Hub 사용 가능</td>
</tr>
<tr>
<td><strong>컨테이너 관리</strong></td>
<td>✅ <code>podman ps</code>, <code>podman run</code></td>
<td>✅ <code>docker ps</code>, <code>docker run</code></td>
</tr>
</tbody></table>
<ol>
<li><strong>Podman은 데몬이 없고(Daemonless), Docker는 데몬(dockerd)이 항상 실행됨.</strong></li>
<li><strong>Podman은 rootless 모드를 기본 지원</strong>하여 보안성이 더 뛰어남.</li>
<li><strong>Docker는 <code>docker-compose</code> 기본 지원</strong>, Podman은 <code>podman-compose</code>를 추가 설치해야 함.</li>
<li><strong>Podman은 Kubernetes YAML을 쉽게 생성 가능</strong> (<code>podman generate kube</code>).</li>
<li><strong>Podman과 Docker는 거의 동일한 명령어를 사용</strong>하며, Docker 이미지를 그대로 사용할 수 있음.</li>
</ol>
<p>✅ <strong>Docker는 익숙한 환경, Podman은 보안성과 유연성이 중요한 환경에서 사용하면 좋음!</strong> 🚀</p>
<p>Podman CLI</p>
<ul>
<li>Podman 명령은 Docker-CLI와 유사한 CLI를 사용. 대부분 추가 권한 없이 일반 사용자로 실행 가능</li>
<li><code>podman version</code> Podman 버전 정보 표시</li>
<li><code>podman login(또는 logout)</code> 컨테이너 레지스트리에 로그인/로그아웃</li>
<li><code>podman build</code> 컨테이너 파일을 사용하여 컨테이너 이미지 빌드</li>
<li><code>podman tag</code> 로컬 이미지에 새로운 이름 추가</li>
<li><code>podman pull</code> 레지스트리에서 이미지 가져오기</li>
<li><code>podman push</code> 이미지, 매니페스트 목록 또는 이미지 인덱스를 로컬 스토리지에서 다른 곳으로 보내기</li>
<li><code>podman run</code> 컨테이너 이미지로 새 컨테이너 실행</li>
<li><code>podman exec</code> 실행 중인 컨테이너에서 명령 실행</li>
<li><code>podman stop</code> 실행 중인 컨테이너 중지</li>
<li><code>podman kill</code> 실행 중인 컨테이너 즉시 종료</li>
<li><code>podman ps</code> 컨테이너에 대한 정보 출력</li>
<li><code>podman rm</code> 컨테이너 삭제</li>
</ul>
<h1 id="kubernetes-이해">Kubernetes 이해</h1>
<p>쿠버네티스</p>
<ul>
<li>컨테이너화된 애플리케이션을 자동으로 배포, 스케일링 및 관리해 주는 오픈소스 시스템</li>
<li>컨테이너화된 워크로드와 서비스를 관리하기 위한 이식성이 있고, 확장가능한 오픈소스 플랫폼</li>
</ul>
<p>쿠버네티스 특징</p>
<ul>
<li>서비스 디스커버리와 로드 밸런싱</li>
<li>스토리지 오케스트레이션</li>
<li>자동화된 롤아웃과 롤백</li>
<li>자동화된 빈 패킹(bin packing)</li>
<li>자동화된 복구(self-healing)</li>
<li>시크릿과 구성 관리</li>
</ul>
<p>쿠버네티스 클러스터 구성</p>
<ul>
<li>Cluster
컨테이너화된 앱을 실행하기 위한 일련의 노드 머신의 집합</li>
<li>Master
클러스터 전체를 컨트롤하며 내부에 있는 모든 노드를 관리하는 가상 머신</li>
<li>Worker
마스터에 의해 명령을 받고 실제 컨테이너들이 생성되고 일을 하는 가상 머신</li>
</ul>
<p>Container Runtime Interface (CRI)</p>
<ul>
<li>컨테이너 런타임 인터페이스는 쿠버네티스에서 다양한 컨테이너 런타임을 사용할 수 있게 해주는 API</li>
</ul>
<p>Kubernetes 배포 도구</p>
<ul>
<li>kubeadm
일반적인 서버 클러스터 환경에서도 쿠버네티스를 쉽게 설치할 수 있게 해주는 관리 툴로서 클러스터를 빠르고 일관되게 설정할 수 있음</li>
<li>kops
자동화된 프로비저닝 시스템으로 AWS에 쉽고 빠르게 쿠버네티스 클러스터를 설치 가능</li>
<li>Kubespray
Ansible을 통해 쿠버네티스 클러스터를 유연하고 쉽게 배포 및 관리할 수 있는 강력한 오픈 소스 툴</li>
</ul>
<h1 id="kubernetes-리소스-개념">Kubernetes 리소스 개념</h1>
<p>Namespace</p>
<ul>
<li>클러스터를 논리적으로 분리하여 사용하는 것을 의미</li>
</ul>
<p>Deployment</p>
<ul>
<li>레플리카셋을 관리하면서 실행시켜야 할 파드의 배포 및 관리를 하는 리소스</li>
<li>상태가 없는 앱을 배포할 때 사용되는 가장 기본적인 컨트롤러</li>
<li>파드 개수 유지</li>
<li>배포 시 롤링 업데이트 발생</li>
</ul>
<p>ReplicaSet</p>
<ul>
<li>파드의 개수를 유지하고 관리하는 리소스</li>
<li>실행되는 파드를 안정적으로 유지</li>
<li>파드 개수에 대한 가용성 보장</li>
</ul>
<p>Pod</p>
<ul>
<li>실제로 컨테이너가 파드에서 실행되며 컨테이너를 돌리는 최소한의 단위</li>
<li>컨테이너가 직접 실행되는 장소</li>
<li>하나 이상의 컨테이너 그룹</li>
<li>컨테이너를 직접 관리하지 않고 파드 단위로 관리</li>
</ul>
<p>Service</p>
<ul>
<li>클러스터 외부에서 클러스터 내부 파드에 접근할 수 있도록 IP를 제공하는 리소스</li>
<li>고정된 IP주소 할당</li>
<li>파드 간의 로드 밸런싱 지원</li>
<li>하나 또는 여러 개의 포트 지원</li>
</ul>
<p>Volume</p>
<ul>
<li>파드의 일부분으로 동일한 파드 내의 컨테이너끼리는 볼륨 공유가 가능하며 컨테이너의 데이터를 보관하여 유지시키는 역할</li>
</ul>
<p>Ingress</p>
<ul>
<li>클러스터 외부로부터 클러스터 내부로 들어오는 네트워크 트래픽을 로드밸런싱</li>
<li>L7 로드밸런싱 기능을 수행</li>
<li>Service에 외부 URL을 제공</li>
<li>클러스터로 접근하는 URL 별로 다른 서비스에 트래픽을 분산</li>
<li>TLS/SSL 인증서 처리</li>
<li>도메인 기반의 Virtual hosting을 지정</li>
</ul>
<p>StatefulSet</p>
<ul>
<li>동일한 컨테이너 스펙을 가진 파드들을 관리하며 각 파드들은 각각의 식별자를 가지기 때문에 독자성을 유지</li>
<li>파드들의 순서 및 고유성을 보장</li>
<li>식별자를 가지고 있어 재스케줄링 간에도 지속적인 유지</li>
<li>내부 파드마다 고유한 pvc를 갖도록 설정이 가능하여, 스케일 확장 시 PV를 유지 가능</li>
</ul>
<p>Deployment vs StatefulSet</p>
<table>
<thead>
<tr>
<th>항목</th>
<th><strong>Deployment</strong></th>
<th><strong>StatefulSet</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>주요 목적</strong></td>
<td><strong>Stateless</strong> 애플리케이션 관리</td>
<td><strong>Stateful</strong> 애플리케이션 관리</td>
</tr>
<tr>
<td><strong>Pod 이름</strong></td>
<td>랜덤 생성 (<code>nginx-abcde</code>)</td>
<td>고정된 이름 (<code>web-0</code>, <code>web-1</code>)</td>
</tr>
<tr>
<td><strong>Pod 순서 보장</strong></td>
<td>❌ 순서 보장 안 됨</td>
<td>✅ 순서 보장 (<code>web-0</code> → <code>web-1</code>)</td>
</tr>
<tr>
<td><strong>네트워크 ID</strong></td>
<td>변경될 수 있음</td>
<td><strong>Stable Hostname</strong> 유지</td>
</tr>
<tr>
<td><strong>스토리지</strong></td>
<td>임시 볼륨 사용 가능</td>
<td><strong>Persistent Volume 유지</strong> (PVC 개별 할당)</td>
</tr>
<tr>
<td><strong>롤링 업데이트</strong></td>
<td>✅ 빠르게 수행</td>
<td>⚠️ 한 개씩 차례로 수행</td>
</tr>
<tr>
<td><strong>사용 사례</strong></td>
<td>웹 서버, API 서버, 백엔드 서비스 등</td>
<td>데이터베이스(MySQL, PostgreSQL), Kafka, Zookeeper</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th><strong>Deployment</strong> (무상태, Stateless)</th>
<th><strong>StatefulSet</strong> (유상태, Stateful)</th>
</tr>
</thead>
<tbody><tr>
<td>Stateless 방식의 Pod 배포 상태가 없는 앱 배포</td>
<td>Stateful한 방식의 Pod 관리 상태가 있는 앱 배포</td>
</tr>
<tr>
<td>Service를 통한 외부 노출</td>
<td>Headless Service를 통한 외부 노출</td>
</tr>
<tr>
<td>Service 요청 시 랜덤한 Pod 선택</td>
<td>요청 시 원하는 Pod 선택 가능 (단, Service 요청 불가능)</td>
</tr>
<tr>
<td>ReplicaSet을 가지고 있으며 rollback 가능</td>
<td>ReplicaSet이 없으며 rollback 불가능</td>
</tr>
<tr>
<td>모든 Pod가 1개의 PVC에 모두 연결</td>
<td>Pod마다 각각의 고유한 PVC를 생성하여 고유한 PV를 가짐</td>
</tr>
</tbody></table>
<p>Job</p>
<ul>
<li>여러 파드를 지정하여 지정된 파드를 성공적으로 실행하도록 하는 설정</li>
</ul>
<p>CronJob</p>
<ul>
<li>JOB을 실행하는데 스케줄러 역할</li>
</ul>
<p>DaemonSet</p>
<ul>
<li>클러스터 전체에 특정 파드를 실행할 때 사용하거나 특정 노드 또는 모든 노드에 항상 실행되어야 할 특정 파드들을 관리</li>
</ul>
<p>Persistent Volume</p>
<ul>
<li>특정 파드와 상관없이 별도의 생명주기를 가지는 독립적인 볼륨</li>
</ul>
<p>Persistent Volume Claim</p>
<ul>
<li>사용자가 PV(Persistent Volume)에 하는 요청</li>
<li>파드와 PV를 연결</li>
</ul>
<p>StorageClass</p>
<ul>
<li>PV로 확보한 스토리지 종류를 정의하는 리소스</li>
</ul>
<p>PV, PVC 생명주기</p>
<ol>
<li>생성 (Provisioning)</li>
<li>연결 (Binding)</li>
<li>사용 (Using)</li>
<li>삭제 (Reclaiming)</li>
</ol>
<p>💻 Kubectl</p>
<ul>
<li>쿠버네티스 API를 사용하여 쿠버네티스 클러스터의 컨트롤 플레인과 통신하기 위한 CLI</li>
<li><code>kubectl apply -f FILENAME</code> 파일이나 표준입력(stdin)으로부터 리소스에 구성 변경 사항을 적용</li>
<li><code>kubectl create -f FILENAME</code> 파일이나 표준입력에서 하나 이상의 리소스를 생성</li>
<li><code>kubectl get</code> 하나 이상의 리소스를 조회</li>
<li><code>kubectl describe -f FILENAME</code> 하나 이상의 리소스의 상세한 상태 조회</li>
<li><code>kubectl logs</code> 특정 이름을 가진 리소스의 로그 정보 출력</li>
<li><code>kubectl edit</code> 특정 이름을 가진 리소스 정보 수정</li>
</ul>
<h1 id="kubernetes-리소스-관리">Kubernetes 리소스 관리</h1>
<p>📝 Deployment yaml 파일 작성 방법</p>
<pre><code class="language-yaml">apiVersion: v1           # Kubernetes API 버전
kind: Pod                # 생성할 오브젝트 종류 (Pod)
metadata:                # 리소스에 이름을 부여하고 구분
  name: my-pod           # Pod의 이름
  labels:                # 키-값 쌍으로 구성
    app: my-app          # 라벨 (Pod 식별용)
spec:                              # 생성하고자 하는 리소스에 대한 내용 구체적 정의
  replicas: 3                      # 띄우고자 하는 파드 개수
  selector:                        # 어떤 파드를 컨트롤하고 감시해야 하는지 정의
    matchLabels:                   # metadata.labels와 동일한 설정으로 맵핑. 동일한 레이블 가진 파드의 컨테이너를 카운팅하여 현재 상태를 측정
      app: my-app                  # 레이블 셀렉터: app이 &#39;my-app&#39;인 Pod들만 선택
  template:                        # 어떤 파드를 실행할지에 대한 정보 설정
    metadata:                      # metadata.labels와 동일한 설정으로 맵핑. 동일한 레이블 가진 파드 실행
      labels:
        app: my-app                # Pod에 붙일 레이블
    spec:                          # 실행시킬 컨테이너에 대한 설정
      containers:                  # 실행시킬 컨테이너의 이름, 이미지, 포트 번호 등을 설정
      - name: my-container
        image: nginx:latest        # 컨테이너로 배포할 이미지 지정
        imagePullPolicy: Always    # 이미지 다운로드 정책 지정
        ports:
        - containerPort: 80        # 컨테이너에서 노출할 포트
      imagePullSecrets:            # 이미지 저장소에 접근하기 위한 인증정보
      - name: edu-msa-secret
      nodeSelector: kubernetes.io/hostname: paas-ta-worker-1 # 파드를 배포할 특정 노드 지정</code></pre>
<p>📝 서비스 yaml 파일 작성 방법</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app                    # 이 서비스가 연결할 Pod의 레이블
  ports:
    - nodePort: ${EDU_MSA_BOARD}   # 외부에서 접근 가능한 포트 번호 설정 (외부 포트)
      port: 80                     # 서비스가 클러스터 내에서 사용하는 포트 (내부 포트)
      protocol: TCP                # 프로토콜 방식 설정
      targetPort: 8080             # 접근하고자 하는 컨테이너 포트 번호를 설정
  type: NodePort                   # 외부에 노출하고자 하는 방식 설정 (NodePort는 포트 번호를 통해 외부에서 접근할 수 있는 방법)</code></pre>
<p>🔔 서로 연관된 여러 yaml 파일을 하나의 파일로 작성하기 위해서는 중간에 <code>---</code>로 구분 지어 작성</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2일 차 1장 Container Platform 이해]]></title>
            <link>https://velog.io/@com_jinnn99/2%EC%9D%BC-%EC%B0%A8-1%EC%9E%A5-Container-Platform-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@com_jinnn99/2%EC%9D%BC-%EC%B0%A8-1%EC%9E%A5-Container-Platform-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Mon, 10 Mar 2025 02:10:58 GMT</pubDate>
            <description><![CDATA[<p>💡 2일 차 학습목표</p>
<ol>
<li>K-PaaS에 대해 이해할 수 있다</li>
<li>표준모델인 컨테이너 플랫폼에 대해 이해할 수 있다</li>
<li>컨테이너 플랫폼을 구성하는 주요 요소를 이해할 수 있다</li>
<li>컨테이너 플랫폼을 관리하는 주요 도구를 이해할 수 있다</li>
</ol>
<hr>
<h1 id="container-platform-소개">Container Platform 소개</h1>
<p>K-PaaS</p>
<ul>
<li>클라우드 인프라 위에서 SW나 서비스를 개발·실행·운영·관리하는 기반 SW 환경 (미들웨어 성격)</li>
</ul>
<p>Container Platform</p>
<ul>
<li>쿠버네티스 기반의 단독 배포, 서비스형 배포 및 Edge 배포 기능을 구현하여 클라우드 기반 서비스 및 운영에 필요한 부가 서비스를 지원하는 오픈소스 PaaS 플랫폼</li>
<li>Kubernetes Cluster 및 운영에 필요한 Storage 서버로 구성하고, Description을 기반으로 컨테이너화 된 애플리케이션을 배포하는 방식으로 동작</li>
</ul>
<h1 id="container-platform-배포방식">Container Platform 배포방식</h1>
<p>단독형 배포</p>
<ul>
<li>컨테이너 플랫폼을 단독으로 배포하여 독립된 쿠버네티스 환경을 제공하는 배포 방식</li>
</ul>
<p>Edge 배포</p>
<ul>
<li>Edge Computing 기반 배포 방식으로 단독 배포와 같이 쿠버네티스 클러스터를 Edge 환경에 단독으로 배포하여 독립된 쿠버네티스를 제공하는 배포 방식</li>
</ul>
<p>서비스형 배포</p>
<ul>
<li>컨테이너 플랫폼을 애플리케이션 플랫폼에서 서비스 형태로 쿠버네티스 클러스터를 등록하여 사용하는 방식</li>
</ul>
<h1 id="container-platform-기능-특징">Container Platform 기능 특징</h1>
<p>2024 버전 특징</p>
<ul>
<li>멀티 클라우드 구성 지원<ul>
<li>서비스 메시(Istio)를 이용한 멀티 클라우드 환경 서비스 지원</li>
<li>서비스 메시(Linkerd)를 이용한 멀티 클라우드 환경 서비스 지원</li>
<li>클러스터 페더레이션을 이용한 멀티 클라우드 환경 서비스 지원</li>
<li>멀티 클라우드 패키징 추가, 다양한 환경에 맞는 Deployment 제공</li>
</ul>
</li>
<li>포털 서비스를 통한 클러스터 생성 및 멀티 클러스터 관리 지원</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[1일 차 4장 K-PaaS 소개]]></title>
            <link>https://velog.io/@com_jinnn99/1%EC%9D%BC%EC%B0%A8-4%EC%9E%A5-K-PaaS-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@com_jinnn99/1%EC%9D%BC%EC%B0%A8-4%EC%9E%A5-K-PaaS-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Mon, 10 Mar 2025 01:52:42 GMT</pubDate>
            <description><![CDATA[<h1 id="k-paas-개요">K-PaaS 개요</h1>
<p>K-PaaS = K-PaaS 표준모델 + K-PaaS 적합성 인증 PaaS/플랫폼 SW</p>
<p>K-PaaS 표준모델</p>
<ul>
<li>상호운용성 및 호환성을 보장하는 오픈 클라우드 플랫폼</li>
</ul>
<p>K-PaaS 적합성 인증 PaaS/플랫폼 SW</p>
<ul>
<li>K-PaaS 플랫폼을 지원하면서 추가 기능들로 확장하였음을 인증받은 제품이나 서비스 </li>
</ul>
<p>K-PaaS 기대효과</p>
<ul>
<li>HW자원의 효율적인 활용</li>
<li>SW자원의 신속한 제공</li>
<li>간편한 SW 유지보수</li>
<li>안정적인 시스템 운영</li>
<li>클라우드 이동성 확보</li>
</ul>
<h1 id="k-paas-표준모델">K-PaaS 표준모델</h1>
<p>지원 기능</p>
<ul>
<li>플랫폼</li>
<li>서비스</li>
<li>운영도구</li>
<li>개발도구</li>
</ul>
<p>K-PaaS 특징</p>
<ul>
<li>인프라 종속 탈피</li>
<li>국산 SW 서비스 지원</li>
<li>풍부한 개발운영 환경</li>
<li>CF-KBS 듀얼 통합 환경</li>
<li>보안성 강화</li>
<li>마켓 플레이스</li>
<li>통합 모니터링</li>
<li>자동·확장 및 장애복구</li>
</ul>
<h1 id="개방형-클라우드-플랫폼-센터-소개">개방형 클라우드 플랫폼 센터 소개</h1>
<p>센터 소개</p>
<ul>
<li>고도화 및 유지·관리</li>
<li>플랫폼 생태계 활성화 지원</li>
<li>적용·확산을 위한 전문기술지원</li>
<li>클라우드 플랫폼 전문가·기업 육성</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[1일 차 3장 Cloud Native Application]]></title>
            <link>https://velog.io/@com_jinnn99/1%EC%9D%BC%EC%B0%A8-3%EC%9E%A5-Cloud-Native-Application</link>
            <guid>https://velog.io/@com_jinnn99/1%EC%9D%BC%EC%B0%A8-3%EC%9E%A5-Cloud-Native-Application</guid>
            <pubDate>Fri, 07 Mar 2025 05:30:00 GMT</pubDate>
            <description><![CDATA[<h1 id="cloud-native-application-개념">Cloud Native Application 개념</h1>
<blockquote>
<p><strong>클라우드 네이티브</strong>
 클라우드의 이점을 최대로 활용할 수 있도록 애플리케이션을 구축하고 실행하는 방식</p>
</blockquote>
<p>클라우드 컴퓨팅 모델의 장점을 활용할 수 있도록 애플리케이션을 개발하고 실행하기 위한 접근 방식</p>
<p>핵심은 <strong>&#39;서비스&#39;</strong></p>
<ul>
<li>최소의 상태(stateful) 컴포넌트들이 격리된 상태의 마이크로서비스로 구성되며, 각각의 서비스는 <strong>분산</strong>되고, <strong>탄력적</strong>이며 <strong>수평적 확장성</strong> 있는 시스템으로 구성</li>
<li>애플리케이션과 각각의 독립적인 배포단위는 클라우드중심의 디자인패턴들과 셀프서비스가 가능한 <em>탄력적인 플랫폼*</em>에서 운영되도록 설계</li>
<li>애플리케이션을 서로 <strong>독립적</strong>인 기능을 하는 여러 개의 <strong>서비스</strong>로 구분</li>
<li><strong>서비스들을 어떻게 구성, 연결, 관리</strong>하는지가 핵심요소</li>
<li>‘서비스’들을 묶어 하나의 <strong>통합된‘(비즈니스) 서비스’</strong>로 만드는 다양한 기능과 기술필요</li>
</ul>
<p><strong>핵심요소</strong></p>
<ul>
<li>Microservices
안정성, 스케일링 용이성 개선</li>
<li>Container
IT 이식성, 유연성 확보</li>
<li>DevOps
APP 서비스 개선 속도 증가</li>
<li>CI/CD
개발-운영간 업무 속도 증가</li>
</ul>
<p>아키텍처
<img src="https://velog.velcdn.com/images/com_jinnn99/post/7120bf47-e7fa-4056-a005-86fb26242e09/image.png" alt=""></p>
<p>개발방식
<img src="https://velog.velcdn.com/images/com_jinnn99/post/36a9c393-511f-45b8-b2c1-65c4e8b4e2b5/image.png" alt=""></p>
<p>특징
<img src="https://velog.velcdn.com/images/com_jinnn99/post/71b32d51-45ee-442d-823c-68498f6e80e9/image.png" alt=""></p>
<p>예시
<img src="https://velog.velcdn.com/images/com_jinnn99/post/f036c332-8f08-41f2-a65d-e361a816f5e2/image.png" alt=""></p>
<p>효과
<img src="https://velog.velcdn.com/images/com_jinnn99/post/08b92908-7e46-4f63-bc49-b12ed4247fb3/image.png" alt=""></p>
<h1 id="microservice-architecture">MicroService Architecture</h1>
<blockquote>
<p><strong>MSA</strong>
 대규모 SW 개발에 적용하기 위한 것. 단독으로 실행 가능하고 독립적으로 배치될 수 있는 작은 단위(모듈)로 기능을 분해하여 서비스하는 아키텍처</p>
</blockquote>
<p>마이크로서비스 간 연결은 응용 프로그래밍 인터페이스(API)를 이용
마이크로서비스는 자원 표현이나 데이터 관리 등에 있어 기능적으로 완전해야 함</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/92c27071-c25b-442f-819e-d55698c90cb4/image.png" alt=""></p>
<p>REST API</p>
<ul>
<li>두 컴퓨터 시스템이 인터넷을 통해 정보를 안전하게 교환하기 위해 사용하는 인터페이스</li>
</ul>
<p>REST</p>
<ul>
<li>자원의 표현에 의한 상태 전달</li>
<li>자원(Resource) - URI</li>
<li>행위(Verb) - HTTP Method</li>
<li>표현(Representation) - Client가 자원의 상태(정보)에 대한 조작을 요청하면 Server는 이에 적절한 응답(Representation)을 보냄(JSON, XML, TEXT, RSS 등 여러 형태)</li>
</ul>
<p>MSA 특징</p>
<ul>
<li>One Thing
한 가지 기능을 수행하는데 초점</li>
<li>Small
가장 작은 단위의 서비스</li>
<li>API
다른 서비스와 연계</li>
<li>Autonomous
각자 자율 개발</li>
</ul>
<p>장점</p>
<ul>
<li>분산형 개발을 통해 개발자들이 동시 참여 가능하고, 유연하고 빠른 의사결정이 가능 =&gt; 개발 소요시간 단축</li>
<li>모놀리식 애플리케이션과는 달리, 한 부분에 장애가 발생하더라도 전체 애플리케이션이 중단되지 않음</li>
<li>빠르고 유연한 조직문화 기여</li>
<li>출시 시간 단축</li>
<li>유연한 확장</li>
<li>뛰어난 복구 능력</li>
<li>개발자 생산성 증가</li>
<li>높은 신기술 수용력</li>
</ul>
<p>단점</p>
<ul>
<li>모놀리식 아키텍처에 비해 구조가 복잡함. 서비스 간 통신에 대한 처리가 추가적 필요. 개발, 배포, 운영 측면에서 고려해야 할 사항 증가</li>
<li>개발, 운영 복잡성 증가</li>
<li>통합 테스트 어려움</li>
<li>배포, 운영을 위한 자동화 필요</li>
<li>코드 중복성</li>
<li>트랜잭션 처리 어려움</li>
<li>디버깅 어려움</li>
</ul>
<h1 id="container">Container</h1>
<p>컨테이너</p>
<ul>
<li>가상화된 OS 위에서 애플리케이션의 독립적인 실행에 필요한 파일(소스코드, 라이브러리 등)을 모은 패키지</li>
<li>Cloud Native 소프트웨어의 가장 작은 단위</li>
<li>서버에 바로 배포할 수 있을 뿐 아니라, 가상머신 위에도 배포 가능</li>
<li>OS를 통해 관리되며 리소스 및 기능에 제약을 가지는 구동 프로세스(running process)</li>
</ul>
<p>특징</p>
<ul>
<li>이식성을 바탕으로 작고, 가볍고, 손쉽게 애플리케이션 배포 가능</li>
</ul>
<h1 id="devops">DevOps</h1>
<p>DevOps</p>
<ul>
<li>기존의 개발업무와 운영업무로 나누어진 두 역할 사이의 커뮤니케이션, 협업, 통합</li>
<li>프로세스 자동화를 목표로 개발자와 운영자가 협업하여, 짧은 주기 내 신뢰성 있는 소프트웨어 생성, 테스트, 릴리즈 할 수 있는 문화와 환경을 의미</li>
<li>애플리케이션 개발-운영 간의 협업 프로세스를 자동화하여 개발과 개선 속도를 빠르게 함</li>
</ul>
<p>특징</p>
<ul>
<li>속도 및 신속한 제공
릴리즈 빈도, 속도를 개선</li>
<li>안전성
변경 사항이 제대로 안전하게 작동하는지 테스트 가능</li>
<li>확장 및 보안
규모에 따라 인프라와 개발 프로세스 운영, 관리 가능</li>
<li>협업강화</li>
</ul>
<p>DevOps 도입을 위한 필수요소</p>
<ul>
<li>애자일</li>
<li>CI/CD
지속적 통합, 배포</li>
</ul>
<h1 id="cicd">CI/CD</h1>
<p>CI/CD</p>
<ul>
<li>지속적인 통합, 서비스 제공, 배포의 자동화를 통해 더욱 짧은 주기로 고객에게 App 제공하는 방법</li>
<li>지속적인 통합 / 지속적인 배포
Continuous Integration / Continuous Delivery or Deployment</li>
</ul>
<p>CI</p>
<ul>
<li>앱 코드의 새 변경사항이 정기적으로 빌드 및 테스트를 거쳐 공유 리포지토리에 병합되는 것을 의미</li>
</ul>
<p>CD</p>
<ul>
<li>지속적인 서비스 제공(Delivery)은 개발자들이 앱에 적용한 변경 사항이 버그 테스트를 거쳐 리포지토리에 자동으로 업로드되는 것을 의미</li>
<li>지속적인 배포(Deployment)는 개발자가 변경한 사항을 리포지토리에서 고객이 사용 가능한 프로덕션 환경까지 자동으로 릴리스되는 것을 의미</li>
</ul>
<p>CI/CD와 DevOps 관계</p>
<ul>
<li>DevOps는 SW 개발 효율성 향상을 목표하는 <strong>문화이자 프로세스</strong></li>
<li>CI/CD는 DevOps를 실현하기 위해 필수적인 <strong>방법 요소</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[정상적인 경로로 접속하지 않은 경우 msg 페이지 보이기]]></title>
            <link>https://velog.io/@com_jinnn99/%EC%A0%95%EC%83%81%EC%A0%81%EC%9D%B8-%EA%B2%BD%EB%A1%9C%EB%A1%9C-%EC%A0%91%EC%86%8D%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EA%B2%BD%EC%9A%B0-msg-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%B3%B4%EC%9D%B4%EA%B8%B0</link>
            <guid>https://velog.io/@com_jinnn99/%EC%A0%95%EC%83%81%EC%A0%81%EC%9D%B8-%EA%B2%BD%EB%A1%9C%EB%A1%9C-%EC%A0%91%EC%86%8D%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EA%B2%BD%EC%9A%B0-msg-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%B3%B4%EC%9D%B4%EA%B8%B0</guid>
            <pubDate>Fri, 07 Mar 2025 04:36:18 GMT</pubDate>
            <description><![CDATA[<p>Controller에서 정상적인 경로로 해당 uri 접속한 게 아닌 경우</p>
<pre><code class="language-java">return CommUtil.doComplete(model, &quot;오류&quot;, &quot;정상적인 경로로 접속 해 주세요&quot;, &quot;history.back();&quot;);</code></pre>
<p>해당 코드로 처리한다.</p>
<pre><code class="language-java">    /**
     * 처리 완료 후 안내문구 및 스크립트 설정,
     * @param model ModelMap
     * @param title : 페이지 타이틀 제목 (보통 오류또는 안내)
     * @param msg : alert() 경고 문으로 보여줄 안내문, &quot;&quot; 값이면 경고창 없음
     * @param script : javascript 처리문장 (보통 location.href=&#39;~~~~&#39;)
     * @return /ncms/comm/message/message
     */
    public static String doComplete(ModelMap model, String title, String msg, String script) {
        HashMap&lt;String, String&gt; message = new HashMap&lt;String, String&gt;();
        message.put(&quot;title&quot;,title);
        message.put(&quot;msg&quot;,msg);
        message.put(&quot;script&quot;,script);
        message.put(&quot;type&quot;,&quot;alert&quot;);
        model.addAttribute(&quot;message&quot;, message);

        return &quot;/ncms/comm/message/message&quot;;
    }

    /**
     * 처리후 confirm 함수를 이용해 사용자 선택을 받아서 처리 함
     * @param model
     * @param title 제목
     * @param msg confirm 질문 내용
     * @param okUrl [확인]버튼 클릭시 스크립트
     * @param cancelUrl [아니오]버튼 클릭시 스크립트
     * @return
     */
    public static String doCompleteConfirm(ModelMap model, String title, String msg, String okUrl, String cancelUrl) {
        HashMap&lt;String, String&gt; message = new HashMap&lt;String, String&gt;();
        message.put(&quot;title&quot;,title);
        message.put(&quot;msg&quot;,msg);
        message.put(&quot;script&quot;,&quot;&quot;);
        message.put(&quot;okUrl&quot;,okUrl);
        message.put(&quot;cancelUrl&quot;,cancelUrl);
        message.put(&quot;type&quot;,&quot;confirm&quot;);
        model.addAttribute(&quot;message&quot;, message);
        return &quot;/ncms/comm/message/message&quot;;
    }</code></pre>
<p>return jsp는 아래와 같다.</p>
<pre><code class="language-js">&lt;%@ page contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ taglib prefix=&quot;form&quot; uri=&quot;http://www.springframework.org/tags/form&quot; %&gt;
&lt;%@ taglib prefix=&quot;ui&quot; uri=&quot;http://egovframework.gov/ctl/ui&quot;%&gt;
&lt;%@ taglib prefix=&quot;spring&quot; uri=&quot;http://www.springframework.org/tags&quot;%&gt;

&lt;style&gt;body{display:none;visibility:hidden;}&lt;/style&gt;
&lt;script&gt;
&lt;c:if test=&quot;${not empty message.title}&quot;&gt;
document.title = &quot;${message.title}&quot;;
&lt;/c:if&gt;
&lt;c:choose&gt;
    &lt;c:when test=&quot;${message.type eq &#39;alert&#39;}&quot;&gt;
        &lt;c:if test=&quot;${message.msg != &#39;&#39;}&quot;&gt;
        alert(&quot;&lt;c:out value=&quot;${message.msg}&quot;/&gt;&quot;);
        &lt;/c:if&gt;
        &lt;c:out value=&quot;${message.script}&quot; escapeXml=&quot;false&quot; /&gt;
    &lt;/c:when&gt;
    &lt;c:when test=&quot;${message.type eq &#39;confirm&#39;}&quot;&gt;
        &lt;c:if test=&quot;${message.msg != &#39;&#39;}&quot;&gt;
        if (confirm(&quot;&lt;c:out value=&quot;${message.msg}&quot;/&gt;&quot;)) {
            &lt;c:out value=&quot;${message.okUrl}&quot; escapeXml=&quot;false&quot; /&gt;
        } else {
            &lt;c:out value=&quot;${message.cancelUrl}&quot; escapeXml=&quot;false&quot; /&gt;
        }
        &lt;/c:if&gt;
        &lt;c:out value=&quot;${message.script}&quot; escapeXml=&quot;false&quot; /&gt;
    &lt;/c:when&gt;
    &lt;c:otherwise&gt;
        &lt;c:if test=&quot;${message.msg != &#39;&#39;}&quot;&gt;
        alert(&quot;&lt;c:out value=&quot;${message.msg}&quot;/&gt;&quot;);
        &lt;/c:if&gt;
        &lt;c:out value=&quot;${message.script}&quot; escapeXml=&quot;false&quot; /&gt;
    &lt;/c:otherwise&gt;
&lt;/c:choose&gt;
&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[1일 차 2장 Cloud Model 및 특징 이해]]></title>
            <link>https://velog.io/@com_jinnn99/1%EC%9D%BC%EC%B0%A8-2%EC%9E%A5-Cloud-Model-%EB%B0%8F-%ED%8A%B9%EC%A7%95-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@com_jinnn99/1%EC%9D%BC%EC%B0%A8-2%EC%9E%A5-Cloud-Model-%EB%B0%8F-%ED%8A%B9%EC%A7%95-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Thu, 06 Mar 2025 02:42:45 GMT</pubDate>
            <description><![CDATA[<h1 id="cloud-모델">Cloud 모델</h1>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/b0043007-066d-4fd4-85be-495ed7dd41ff/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/1dab7ddb-e3e0-4b89-a971-dc597b397a5d/image.png" alt=""></p>
<p>서비스 모델 구분 예시</p>
<ul>
<li>On-premises solution (개인 차 소유)</li>
<li>IaaS (렌트카 이용)</li>
<li>PaaS (택시 이용)</li>
<li>SaaS (버스 이용)</li>
</ul>
<p>Public Cloud (비안유확)</p>
<ul>
<li>서비스 제공업체가 구축한 서버, 스토리지 등의 IT 인프라를 기업들이 <strong>사용료를 내고 이용</strong>하는 방식</li>
<li><strong>비용</strong> 절감 (사용한 서비스에 대해서만 지불)</li>
<li><strong>유지관리</strong> 하지 않음 (서비스 공급자가 유지관리)</li>
<li>높은 <strong>안정성</strong> (광대한 서버 네트워크)</li>
<li>무제한에 가까운 <strong>확장성</strong> (주문형 리소스 사용 가능)</li>
</ul>
<p>Private Cloud (유보확)</p>
<ul>
<li>기업 자체적으로 <strong>데이터센터 안에 클라우드 환경을 구축</strong>해 사용하는 방식</li>
<li><strong>유연성</strong> 향상 (클라우드 환경을 사용자 지정 가능)</li>
<li><strong>보안</strong> 강화 (제한과 보안 수준 강화 가능)</li>
<li>높은 <strong>확장성</strong> (공용 클라우드의 확장성과 효율성 제공 가능)</li>
</ul>
<p>Hybrid Cloud (제유비용)(이기종)</p>
<ul>
<li>On-Premise 인프라 (또는 Private 클라우드)를 Public 클라우드와 <strong>결합</strong>하여 사용</li>
<li><strong>제어</strong> (직접 유지관리)</li>
<li><strong>유연성</strong> (추가 리소스 활용 가능)</li>
<li><strong>비용</strong> 효율성 (규모 조정 가능. 필요시에만 추가 컴퓨팅 기능에 대해 지불 가능)</li>
<li><strong>용이성</strong> (부담 없이 클라우드 전환 가능)</li>
</ul>
<p>Multi Cloud (동종)</p>
<ul>
<li>공급업체 종속으로 인한 <strong>리스크 대응</strong> 가능</li>
<li>최신기술 도입 적용을 통한 <strong>서비스 개선</strong> 가능</li>
<li>클라우드 서비스 조합을 통해 <strong>가격경쟁력</strong> 확보</li>
<li>기업 전략에 따른 <strong>여러 클라우드 서비스 이용</strong></li>
</ul>
<hr>
<h1 id="iaas">IaaS</h1>
<p>Infrastructure as a Service (서비스형 인프라)
<strong>물리적 리소스를 가상화</strong>하여 유연한 Infrastructure 제공</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/c11e938d-9ffa-4ac9-b9c4-f73eed9618f5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/79c9b517-1a34-421b-8696-debab801f65d/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>항목</th>
<th>하이퍼바이저(VM)</th>
<th>컨테이너(Container)</th>
</tr>
</thead>
<tbody><tr>
<td>격리 단위</td>
<td>OS 단위 (Guest OS 포함)</td>
<td>애플리케이션 단위</td>
</tr>
<tr>
<td>OS 포함 여부</td>
<td>각각의 VM에 OS 포함</td>
<td>호스트 OS 공유</td>
</tr>
<tr>
<td>속도</td>
<td>부팅 속도 느림</td>
<td>빠른 실행</td>
</tr>
<tr>
<td>리소스 사용량</td>
<td>많음 (중복된 OS 존재)</td>
<td>적음 (공유 자원 활용)</td>
</tr>
<tr>
<td>배포 방식</td>
<td>VM 이미지로 배포</td>
<td>컨테이너 이미지로 배포</td>
</tr>
<tr>
<td>보안성</td>
<td>강력한 격리, 보안 우수</td>
<td>커널 공유로 보안 취약 가능</td>
</tr>
<tr>
<td>운영 환경</td>
<td>클라우드, 엔터프라이즈</td>
<td>마이크로서비스, DevOps</td>
</tr>
</tbody></table>
<hr>
<h1 id="paas">PaaS</h1>
<p>Platform as a Service (서비스형 플랫폼)
PaaS는 <strong>미들웨어 성격</strong>을 띠며, <strong>컨테이너를 기반</strong>으로 SW 플랫폼 서비스를 제공
IaaS와 SaaS의 중간 수준의 서비스</p>
<p>특징</p>
<ul>
<li>IaaS 기반에서 SaaS 개발 시의 문제점을 해결 가능</li>
<li>표준화된 HW 및 SW의 설치구성을 자동화하여 <strong>신속한 개발·테스트</strong> 가능</li>
<li>애플리케이션을 개발, 실행, 관리할 수 있는 플랫폼을 제공하는 서비스</li>
<li>SaaS의 개념을 개발 플랫폼에 확장한 방식</li>
<li>개발을 위한 플랫폼 구축 필요 없이 웹에서 쉽게 빌려 쓸 수 있음</li>
<li><strong>개발자는 개발에만 집중, 애플리케이션이 동작하는 주변 환경은 가져다 쓰는 구조</strong></li>
<li>개발 to 배포까지 라이프사이클이 짧아 <strong>DevOps 문화를 적용하기 용이</strong>함</li>
</ul>
<hr>
<h1 id="saas">SaaS</h1>
<p>Software as a Service (서비스형 소프트웨어)
IaaS, PaaS 환경 위에서 SW기능을 서비스로 제공
<strong>필요한 만큼만 이용하고 요금을 지불</strong>하는 형태</p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/1d076275-dc84-4ab4-a0ce-03b8bb517f35/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/65518bca-9333-4a46-a0ff-3e0ec5af820f/image.png" alt=""></p>
<p>특징</p>
<ul>
<li>인터넷에만 접속하면 별도 설치 없이 <strong>바로 사용가능</strong></li>
<li><strong>구독형 서비스</strong></li>
<li>이용 규모·기간이 고정적이지 않아 <strong>단기간·소수 계정</strong>만으로도 사용 가능</li>
<li>데이터가 클라우드에 저장되어 <strong>보안성</strong>이 높고 <strong>접근</strong>이 자유로움</li>
<li>수시로 <strong>최신 버전</strong> 사용 가능</li>
<li>클라우드 공급자가 대신 관리하므로 유지관리에 소요되는 <strong>리소스 비용이 없음</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[1일 차 1장 Cloud 개요]]></title>
            <link>https://velog.io/@com_jinnn99/1%EC%9D%BC%EC%B0%A8-1%EC%9E%A5-Cloud-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@com_jinnn99/1%EC%9D%BC%EC%B0%A8-1%EC%9E%A5-Cloud-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Thu, 06 Mar 2025 01:26:27 GMT</pubDate>
            <description><![CDATA[<p>💡 1일차 학습 목표</p>
<ol>
<li>클라우드 컴퓨팅 기술의 기본속성, 발전과정 및 최신동향을 이해</li>
<li>클라우드 컴퓨팅 서비스를 구분하는 유형과 유형별 특징을 이해</li>
<li>클라우드 네이티브 애플리케이션의 개념과 MSA, 컨테이너, DevOps, CI/CD 등 주요 개념 이해</li>
<li>K-PaaS의 개발배경 및 아키텍처를 이해, 최신 버전의 특징 이해</li>
</ol>
<hr>
<h1 id="cloud-개요">Cloud 개요</h1>
<blockquote>
<p><strong>클라우드 컴퓨팅</strong>
 네트워크 기반 컴퓨팅 기술</p>
</blockquote>
<p>컴퓨팅 리소스를 데이터센터에 대량으로 집적 시킨 후,
개별 이용자가 요구하는만큼 가상으로 분리하여
정보통신망을 통해 제공하는 서비스</p>
<p>인터넷을 통해 서버, 스토리지, DB, 네트워킹, SW 등 컴퓨터 서비스 제공</p>
<p>특징 (주광리신측)</p>
<ul>
<li>주문형 셀프 서비스 (On-demand self-service)
사용자의 개별 관리화면을 통한 컴퓨팅 기능(서버 시간, 네트워크 스토리지 등) 설정</li>
<li>광범위한 네트워크 접속 (Broad network access)
다양한 디바이스를 통한 서비스 접속</li>
<li>리소스 공유 (Resource pooling)
여러 사용자의 요구에 따라 동적 할당 및 재할당 형태로 이용 가능</li>
<li>신속한 확장성 (Rapid elasticity)
스케일 업(처리능력을 높이는 것), 스케일 다운(처리능력을 낮추는 것) 가능</li>
<li>측정 가능한 서비스 (Measured service)
이용한 만큼 요금을 부과하는 종량제 서비스</li>
</ul>
<hr>
<h1 id="cloud-발전과정">Cloud 발전과정</h1>
<p>2000년 - 물리서버
2001년 - 가상화
2006년 - IaaS (ex. AWS)
2009년 - PaaS
2010년 - 오픈소스 IaaS (ex. openstack)
2011년 - 오픈소스 PaaS
2013년 - 컨테이너 (ex. docker)
2015년 - 클라우드 네이티브</p>
<p>Edge Computing</p>
<ul>
<li><strong>데이터가 발생한 현장 혹은 근거리에서 실시간 처리하는 방식</strong>으로 데이터 흐름 가속화를 지원하는 컴퓨팅 기술</li>
</ul>
<hr>
<h1 id="cloud-산업-동향">Cloud 산업 동향</h1>
<p>세계, 국내 클라우드 서비스 매출은 매년 꾸준히 성장
국내 클라우드 서비스는 IaaS 시장 주도, SaaS 시장 최근 빠르게 성장
글로벌 IT 기업이 세계 IaaS 시장의 75%, SaaS 시장의 35% 점유</p>
<hr>
<h1 id="cloud-트렌드">Cloud 트렌드</h1>
<p>23년 트렌드</p>
<ul>
<li>클라우드 보안 및 복원력 투가 증가</li>
<li>멀티클라우드 수요 증가</li>
<li>AI 및 머신러닝 기반 클라우드</li>
<li>로우코드 및 노코드 클라우드 서비스 (LCNC)</li>
<li>클라우드 게임</li>
</ul>
<p>24년 트렌드</p>
<ul>
<li>업무망 활용 등 공공분야 SaaS 도입 가속화</li>
<li>기술 중심 &#39;클라우드 네이티브&#39;로의 전환</li>
<li>클라우드 특화 보안 전략 채택</li>
<li>금융권 핵심업무시스템 클라우드 촉진</li>
<li>AI GPU 고수요 지속</li>
</ul>
<p>25년 트렌드</p>
<ul>
<li>AI가 클라우드 컴퓨팅의 두뇌가 되다</li>
<li>엣지 컴퓨팅과 클라우드의 융합</li>
<li>클라우드를 통한 양자 컴퓨팅의 대중화</li>
<li>새로운 클라우드의 표준, 하이브리드 &amp; 멀티</li>
<li>생성형 AI가 클라우드 개발을 혁신하다</li>
<li>슈퍼 클라우드가 궁극적인 데이터 패브릭으로 등장하다</li>
<li>지속 가능성이 클라우드의 필수가 되다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[개요]]></title>
            <link>https://velog.io/@com_jinnn99/%EA%B0%9C%EC%9A%94-l6fso0mb</link>
            <guid>https://velog.io/@com_jinnn99/%EA%B0%9C%EC%9A%94-l6fso0mb</guid>
            <pubDate>Thu, 06 Mar 2025 00:52:26 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/com_jinnn99/post/bfa0d860-a11f-4777-837b-0ee59a1585b7/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이클립스 확장]]></title>
            <link>https://velog.io/@com_jinnn99/%EC%9D%B4%ED%81%B4%EB%A6%BD%EC%8A%A4-%ED%99%95%EC%9E%A5</link>
            <guid>https://velog.io/@com_jinnn99/%EC%9D%B4%ED%81%B4%EB%A6%BD%EC%8A%A4-%ED%99%95%EC%9E%A5</guid>
            <pubDate>Fri, 24 May 2024 04:58:27 GMT</pubDate>
            <description><![CDATA[<h1 id="indent-guide">indent guide</h1>
<p>코드 가독성 향상</p>
<p>help - install new software</p>
<p><a href="http://kiritsuku.github.io/IndentGuide/update/">http://kiritsuku.github.io/IndentGuide/update/</a></p>
<p>window - preference - editors - text editors - indent guide</p>
<h1 id="edit-box">edit box</h1>
<p>코드 가독성 향상</p>
<p>help - install new software</p>
<p><a href="http://editbox.sourceforge.net/updates">http://editbox.sourceforge.net/updates</a></p>
<h1 id="more-clipboard">more clipboard</h1>
<p>복사한 것들을 여러 개 가지고 있을 수 있음
Ctrl + Shift + V</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JQuery ajax 사용 시 error 확인 방법]]></title>
            <link>https://velog.io/@com_jinnn99/JQuery-ajax-%EC%82%AC%EC%9A%A9-%EC%8B%9C-error-%ED%99%95%EC%9D%B8-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@com_jinnn99/JQuery-ajax-%EC%82%AC%EC%9A%A9-%EC%8B%9C-error-%ED%99%95%EC%9D%B8-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 21 May 2024 02:24:48 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="ajax-error-확인-방법">ajax error 확인 방법</h1>
<pre><code class="language-js">$.ajax({
    url: url,
    type: &#39;get&#39;,
    dataType: &#39;json&#39;, 
    contentType : &quot;application/json&quot;,
    success: function(data){
    }
    error: function(request, status, error){
          console.log(&quot;code:&quot;+request.status+&quot;\n&quot;+&quot;message:&quot;+request.responseText+&quot;\n&quot;+&quot;error:&quot;+error);
    }
});</code></pre>
<p>위와 같이 작성하면 ajax error 시 왜 에러가 발생했는지 알 수 있다.</p>
<p>또는,</p>
<pre><code class="language-js">$.ajax({
    url: url,
    type: &#39;get&#39;,
    dataType: &#39;json&#39;, 
    contentType : &quot;application/json&quot;,
    success: function(data){
    }
    error: function(request, status, error){
      var errorMsg = &#39;status(code): &#39; + request.status + &#39;\n&#39;;
      errorMsg += &#39;statusText: &#39; + request.statusText + &#39;\n&#39;;
      errorMsg += &#39;responseText: &#39; + request.responseText + &#39;\n&#39;;
      errorMsg += &#39;textStatus: &#39; + status + &#39;\n&#39;;
      errorMsg += &#39;error: &#39; + error;
      console.log(errorMsg);
    }
});</code></pre>
<hr>
<h1 id="참고">참고</h1>
<p><a href="https://shonm.tistory.com/454">[ JQUERY ] ajax 사용 시 error 발생 확인</a>
<a href="https://asufi.tistory.com/entry/ETC-ajax-error-%ED%99%95%EC%9D%B8">[Javascript] ajax error 확인</a></p>
]]></description>
        </item>
    </channel>
</rss>